diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/CREDITS linux/CREDITS
--- linux.vanilla/CREDITS	Sun Dec  6 00:14:35 1998
+++ linux/CREDITS	Fri Mar 26 16:56:23 1999
@@ -796,11 +796,11 @@
 D: for Menuconfig's lxdialog.
 
 N: Volker Lendecke
-E: lendecke@namu01.Num.Math.Uni-Goettingen.de
+E: vl@kki.org
 D: Kernel smbfs (to mount WfW, NT and OS/2 network drives.)
 D: NCP filesystem support (to mount NetWare volumes)
-S: Innersteweg 11
-S: 37081 Goettingen
+S: Von Ossietzky Str. 12
+S: 37085 Goettingen
 S: Germany
 
 N: Kevin Lentin
@@ -1245,6 +1245,11 @@
 S: B2240 Zandhoven
 S: Belgium
 
+N: Henning P. Schmiedehausen
+E: hps@tanstaafl.de
+D: added PCI support to the serial driver
+S: Buckenhof, Germany
+
 N: Martin Schulze
 E: joey@linux.de
 W: http://home.pages.de/~joey/
@@ -1641,6 +1646,7 @@
 E: lnz@dandelion.com
 W: http://www.dandelion.com/Linux/
 D: BusLogic SCSI driver
+D: Mylex DAC960 PCI RAID driver
 D: Miscellaneous kernel fixes
 S: 3078 Sulphur Spring Court
 S: San Jose, California 95148
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/Documentation/Configure.help linux/Documentation/Configure.help
--- linux.vanilla/Documentation/Configure.help	Sun Dec  6 00:14:35 1998
+++ linux/Documentation/Configure.help	Fri Mar 26 17:01:46 1999
@@ -79,6 +79,14 @@
   arch/i386/math-emu/README. If you are not sure, say Y; apart from
   resulting in a 45kB bigger kernel, it won't hurt.
 
+Max physical memory
+CONFIG_MAX_MEMSIZE
+  Linux/x86 can use up to ~3.5 gigabytes of physical memory. Default
+  is maximum 950 megabyte physical memory, this is enough for most
+  systems. (if you have more than 900MB RAM, see
+  Documentation/more-than-900MB-RAM.txt how to configure this option. Do
+  not change this value if you have less than 950MB RAM!)
+
 Normal floppy disk support
 CONFIG_BLK_DEV_FD
   If you want to use your floppy disk drive(s) under Linux, say Y.
@@ -165,6 +173,15 @@
   addresses.  Normally, just say N here; you will then use the new
   driver for all 4 interfaces.
 
+Use multi-mode by default
+CONFIG_IDEDISK_MULTI_MODE
+  If you get this error, try to enable this option.
+
+  hda: set_multmode: status=0x51 { DriveReady SeekComplete Error }
+  hda: set_multmode: error=0x04 { DriveStatusError }
+
+  If in doubt, say N.
+
 Include IDE/ATAPI CDROM support
 CONFIG_BLK_DEV_IDECD
   If you have a CDROM drive using the ATAPI protocol, say Y.  ATAPI is
@@ -320,6 +337,12 @@
   Documentation/modules.txt.
   It's pretty unlikely that you have one of these: say N.
 
+Mylex DAC960/DAC1100 PCI RAID Controller support
+CONFIG_BLK_DEV_DAC960
+  This driver adds support for the Mylex DAC960, AcceleRAID, and
+  eXtremeRAID PCI RAID controllers.  See README.DAC960 for further
+  information about this driver.
+
 Parallel port IDE device support
 CONFIG_PARIDE
   There are many external CD-ROM and disk devices that connect through
@@ -483,6 +506,16 @@
   be called fit3.o.  You must also have a high-level driver for the
   type of device that you want to support.
 
+Freecom IQ ASIC-2 protocol
+CONFIG_PARIDE_FRIQ
+  This option enables support for version 2 of the Freecom IQ parallel
+  port IDE adapter.  This adapter is used by the Maxell Superdisk 
+  drive.  If you chose to build PARIDE support into your kernel, you
+  may answer Y here to build in the protocol driver, otherwise you
+  should answer M to build it as a loadable module. The module will be
+  called friq.o. You must also have a high-level driver for the type
+  of device that you want to support. 
+
 FreeCom power protocol
 CONFIG_PARIDE_FRPW
   This option enables support for the Freecom power parallel port IDE
@@ -828,15 +861,33 @@
   certain BIOSes if your computer uses a PCI bus system. This is
   recommended; say Y.
 
-Intel 82371 PIIX (Triton I/II) DMA support
+Generic IDE (U)DMA support
 CONFIG_BLK_DEV_TRITON
-  If your PCI system uses an IDE hard drive (as opposed to SCSI, say)
-  and includes the Intel Triton I/II IDE interface chipset (i82371FB,
+  If your PCI system uses an EIDE hard disk (as opposed to SCSI, say)
+  and includes one of the Intel (U)DMA IDE Southbridge ICs (i82371FB,
   i82371SB or i82371AB), you will want to enable this option to allow
-  use of bus-mastering DMA data transfers. Read the comments at the
-  beginning of drivers/block/triton.c and Documentation/ide.txt.
+  use of bus-mastering DMA data transfers. This increases transfer
+  rates and reduces latencies and CPU utilization. Read the comments in
+  Documentation/ide.txt and Documentation/udma.txt.
   Check the file Documentation/Changes for location and latest version
-  of the hdparm utility. It is safe to say Y to this question.
+  of the hdparm utility. There are now several more chipsets added, to
+  include offboard PCI-IDE-UDMA cards and newer SiS and VIA chipsets.
+  It is safe to say Y to this question, as long as your PCI bus is
+  operating within specs (33MHz recommended).
+
+Boot off-board chipsets first support
+CONFIG_BLK_DEV_OFFBOARD
+  Normally, IDE controllers built into the motherboard (on-board
+  controllers) are assigned to ide0 and ide1 while those on add-in
+  PCI cards (off-board controllers) are relegated to ide2 and ide3.
+  Saying Y to here will reverse the situation, with off-board
+  controllers on ide0/1 and on-board controllers on ide2/3. This
+  can improve the usability of some boot managers such as LILO
+  when booting from a drive on an off-board controller.
+  Note that this will rearrange the order of the hd* devices and
+  may require modification of fstab and other files.
+  Check the file Documentation/udma.txt
+  If in doubt, say N.
 
 System V IPC
 CONFIG_SYSVIPC
@@ -1199,6 +1250,65 @@
   IP always defragment.
   If you want this, say Y.
 
+IP: MS PPTP masq support (EXPERIMENTAL)
+CONFIG_IP_MASQUERADE_PPTP
+  Support for masquerading of the GRE data channel portion of the PPTP
+  Virtual Private Network protocol.
+  If you are masquerading a PPTP client or server you need to enable
+  this in addition to regular IP Masquerade.
+  See http://www.wolfenet.com/~jhardin/ip_masq_pptp.html for more details.
+
+IP: MS PPTP Call ID masq support (EXPERIMENTAL)
+CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT
+  Enabling this adds code to masquerade PPTP Call IDs, which allows
+  more than one masqueraded client to access the same server.
+  This only needs to be enabled if you are masquerading more than one
+  client, and if those clients will try to access the same PPTP server
+  at the same time.
+  You do NOT need to enable this if you are masquerading a PPTP
+  server, regardless of how many clients will be accessing it.
+
+IP: MS PPTP masq debugging
+DEBUG_IP_MASQUERADE_PPTP
+  Enables PPTP Masquerade debugging messages. This should be disabled
+  for normal use once you have PPTP masq working, as it will cause
+  your system logs to quickly grow rather large. Enable verbose
+  debugging for more detailed information.
+
+IP: IPSEC ESP & ISAKMP masq support (EXPERIMENTAL)
+CONFIG_IP_MASQUERADE_IPSEC
+  Support for limited masquerading of the IPSEC ESP network encryption
+  and ISAKMP key-exchange protocols.
+  If you are masquerading an IPSEC client you need to enable this in
+  addition to regular IP Masquerade.
+  Note that this may not successfully masquerade all types of
+  IPSEC-based encryption, as some options in the protocol offer a
+  cryptographic checksum across the IP addresses, which prevents the
+  masqueraded packets from being accepted.
+
+IP: IPSEC masq table lifetime (minutes)
+CONFIG_IP_MASQUERADE_IPSEC_EXPIRE
+  After a period of inactivity IPSEC masq table entries expire and are
+  removed. When this happens inbound traffic can no longer be routed
+  to the masqueraded host until new outbound traffic creates a new
+  masq table entry.
+  For greatest reliability, your IPSEC rekey interval should be less
+  than the table entry lifetime. If your rekey interval is greater
+  than thirty minutes you will improve security by reducing it to
+  thirty minutes. If you don't want to do that, then increase the masq
+  table entry lifetime. Note that doing this will increase the clutter
+  in the IPSEC masq table, as old table entries will persist for this
+  many minutes after a rekey.
+  The minimum lifetime is 15 minutes. Decreasing the lifetime will
+  interfere with sessions that are idle for long periods of time.
+
+IP: IPSEC masq debugging
+DEBUG_IP_MASQUERADE_IPSEC
+  Enables IPSEC Masquerade debugging messages. This should be disabled
+  for normal use once you have IPSEC masq working, as it will cause
+  your system logs to quickly grow rather large. Enable verbose
+  debugging for more detailed information.
+
 IP: ipautofw masquerading (EXPERIMENTAL)
 CONFIG_IP_MASQUERADE_IPAUTOFW
   Richard Lynch's ipautofw allows masquerading to work with protocols
@@ -1622,66 +1732,102 @@
 Adaptec AIC7xxx chipset SCSI controller support
 CONFIG_SCSI_AIC7XXX
   This is support for the various aic7xxx based Adaptec SCSI
-  controllers.  These include the 274x EISA cards, 284x VLB cards,
-  294x PCI cards, 394x PCI cards, 3985 PCI card, and several versions
-  of the Adaptec built-in SCSI controllers on various PC motherboards.
+  controllers. These include the 274x EISA cards; 284x VLB cards; 2902,
+  2910, 293x, 294x, 394x, 3985 and several other PCI and motherboard based
+  SCSI controllers from Adaptec.  It does not support the AAA-13x RAID
+  controllers from Adaptec, nor will it likely ever support them.  It
+  does not support the 2920 cards from Adaptec that use the Future Domain
+  SCSI controller chip.  For those cards, you need the "Future Domain
+  16xx SCSI support" driver.
+
+  In general, if the controller is based on an Adaptec SCSI controller
+  chip from the aic777x series or the aic78xx series, it should work.  The
+  only exception is the 7810 which is specifically not supported (that's the
+  RAID controller chip on the AAA-13x cards).
+
   Information on the configuration options for this controller can be
-  found by checking the README.aic7xxx file, usually in
-  /usr/src/linux/drivers/scsi.
+  found by checking the help file for each of the available
+  configuration options. You should read drivers/scsi/README.aic7xxx
+  at a minimum before contacting the maintainer with any questions.  
+  The SCSI-HOWTO, available via FTP (user: anonymous) at
+  ftp://metalab.unc.edu/pub/Linux/docs/HOWTO can also be of great help.
 
-Override driver defaults for commands per LUN
-CONFIG_OVERRIDE_CMDS
-  Use this option to allow you to override the default maximum number
-  of commands that a single device on the aic7xxx controller is
-  allowed to have active at one time.  This option only effects tagged
-  queueing capable devices.  The driver uses a "failsafe" value of 8
-  by default.  This is much lower than many devices can handle, but
-  left in place for safety sake.
-  NOTE:  This does not actually enabled tagged queueing on any
-  particular device.  The driver has changed in this respect.  Please
-  see the file README.aic7xxx in /usr/src/linux/drivers/scsi for more
-  information on how to get particular devices to use tagged command
-  queueing.
-  Default: N
-
-Maximum number of commands per LUN
-CONFIG_AIC7XXX_CMDS_PER_LUN
-  Specify the maximum number of commands per lun you would like to
-  allocate per device.  Reasonable figures are in the range of 14 to
-  32 commands per device, but depending on hardware could be increased
-  or decreased from that figure.  If the number is too high for any
-  particular device, the driver will automatically compensate usually
-  after only 10 minutes of uptime and will issue a message to alert
-  you to the fact that the number of commands for that device has been
-  reduced.  It will not hinder performance if a portion of your
-  devices eventually have their commands per lun reduced, but is a
-  waste of memory if all of your devices end up reducing this number
-  down to a more reasonable figure.  Default: 24
+  If you want to compile this driver as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want),
+  say M here and read Documentation/modules.txt. The module will be
+  called aic7xxx.o.
+
+Enable or Disable Tagged Command Queueing by default
+CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT
+  This option causes the aic7xxx driver to attempt to use tagged command
+  queueing on any devices that claim to support it.  If this is set to yes,
+  you can still turn off TCQ on troublesome devices with the use of the
+  tag_info boot parameter.  See /usr/src/linux/drivers/scsi/README.aic7xxx
+  for more information on that and other aic7xxx setup commands.  If this
+  option is turned off, you may still enable TCQ on known good devices by
+  use of the tag_info boot parameter.
+  
+  If you are unsure about your devices then it is safest to say N here.
+  
+  However, TCQ can increase performance on some hard drives by as much
+  as 50% or more, so I would recommend that if you say N here, that you
+  at least read the README.aic7xxx file so you will know how to enable
+  this option manually should your drives prove to be safe in regards
+  to TCQ.
+
+  Conversely, certain drives are known to lock up or cause bus resets when
+  TCQ is enabled on them.  If you have a Western Digital Enterprise SCSI
+  drive for instance, then don't even bother to enable TCQ on it as the
+  drive will become unreliable, and it will actually reduce performance.
+
+Default number of TCQ commands per device
+CONFIG_AIC7XXX_CMDS_PER_DEVICE
+  Specify the number of commands you would like to allocate per SCSI
+  device when Tagged Command Queueing (TCQ) is enabled on that device.
+
+  Reasonable figures are in the range of 8 to 24 commands per device,
+  but depending on hardware could be increased or decreased from that
+  figure. If the number is too high for any particular device, the
+  driver will automatically compensate usually after only 10 minutes
+  of uptime. It will not hinder performance if some of your devices
+  eventually have their command depth reduced, but is a waste of memory
+  if all of your devices end up reducing this number down to a more
+  reasonable figure.
+  
+  NOTE: Certain very broken drives are known to lock up when given more
+  commands than they like to deal with.  Quantum Fireball drives are the
+  most common in this category.  For the Quantum Fireball drives I would
+  suggest no more than 8 commands per device.
+
+  Default: 8
 
 Collect statistics to report in /proc
 CONFIG_AIC7XXX_PROC_STATS
   This option tells the driver to keep track of how many commands have
   been sent to each particular device and report that information to
-  the user via the /proc/scsi/aic7xxx/x file, where x is the number
-  of the aic7xxx controller you want the information on.  This adds
-  a small amount of overhead to each and every SCSI command the
-  aic7xxx driver handles, so if you aren't really interested in this
-  information, it is best to leave it disabled.  Default: N
+  the user via the /proc/scsi/aic7xxx/n file, where n is the number of
+  the aic7xxx controller you want the information on. This adds a
+  small amount of overhead to each and every SCSI command the aic7xxx
+  driver handles, so if you aren't really interested in this
+  information, it is best to leave it disabled. This will only work if
+  you also say Y to "/proc filesystem support", below. 
+
+  If unsure, say N.
 
 Delay in seconds after SCSI bus reset
 CONFIG_AIC7XXX_RESET_DELAY
   This sets how long the driver will wait after resetting the SCSI bus
   before attempting to communicate with the devices on the SCSI bus
-  again.  This delay will be used during the reset phase at bootup
-  time as well as after any reset that might occur during normal
-  operation.  Reasonable numbers range anywhere from 5 to 15 seconds
-  depending on your devices. DAT tape drives are notorious for needing
-  more time after a bus reset to be ready for the next command, but
-  most hard drives and CD-ROM devices are ready in only a few seconds.
-  This option has a maximum upper limit of 20 seconds to avoid bad
-  interactions between the aic7xxx driver and the rest of the Linux
-  kernel.  The default value has been reduced.  If this doesn't work
-  with your hardware, try increasing this value.  Default: 5
+  again. This delay will be used during the reset phase at bootup time
+  as well as after any reset that might occur during normal operation.
+  Reasonable numbers range anywhere from 5 to 15 seconds depending on
+  your devices. DAT tape drives are notorious for needing more time
+  after a bus reset to be ready for the next command, but most hard
+  drives and CD-ROM devices are ready in only a few seconds. This
+  option has a maximum upper limit of 20 seconds to avoid bad
+  interactions between the aic7xxx driver and the rest of the linux
+  kernel. The default value has been reduced to 5 seconds. If this
+  doesn't work with your hardware, try increasing this value.
 
 BusLogic SCSI support
 CONFIG_SCSI_BUSLOGIC
@@ -1833,43 +1979,50 @@
 CONFIG_SCSI_NCR53C8XX
   This is the BSD ncr driver adapted to Linux for the NCR53C8XX family
   of PCI-SCSI controllers. This driver supports parity checking,
-  tagged command queuing, fast SCSI II transfer up to 10 MB/s with
-  narrow SCSI devices and 20 MB/s with wide SCSI devices.
-  Support of Ultra SCSI data transfers with NCR53C860 and NCR53C875
-  controllers has been recently added to the driver.
+  tagged command queuing and fast synchronous data transfers up to 80
+  MB/s with wide FAST-40 LVD devices and controllers.
+  The NCR53C860 and NCR53C875 support FAST-20 transfers. The NCR53C895
+  supports FAST-40 transfers with Ultra2 LVD devices.
+  If you have a SYM53C896 PCI-SCSI controller, you may want to use the new 
+  improved driver available at ftp://ftp.tux.org/pub/roudier/896/.
   Please read drivers/scsi/README.ncr53c8xx for more information.
-  Linux/i386 and Linux/Alpha are supported by this driver.
-
+  
 Synchronous data transfers frequency
 CONFIG_SCSI_NCR53C8XX_SYNC
-  SCSI-2 specifications allow SCSI devices to negotiate a synchronous
-  transfer period of 25 nano-seconds or more.
-  The transfer period value is 4 times the agreed transfer period.
-  So, data can be transferred at a 10 MHz frequency, allowing 10
-  MB/second throughput with 8 bits SCSI-2 devices and 20 MB/second
-  with wide16 devices.  This frequency can be used safely with
-  differential devices but may cause problems with singled-ended
-  devices.
-  Specify 0 if you want to only use asynchronous data transfers.
-  Otherwise, specify a value between 5 and 10.  Commercial O/Ses
-  generally use 5 Mhz frequency for synchronous transfers.  It is a
-  reasonable default value.
-  However, a flawless singled-ended SCSI bus supports 10 MHz data
-  transfers.  Regardless the value chosen in the Linux configuration,
-  the synchronous period can be changed after boot-up through the
-  /proc/scsi file system. The generic command is:
-      echo "setsync #target period" >/proc/scsi/ncr53c8xx/0
-  Use a 25 ns period for 10 Mhz synchronous data transfers.
-  If you don't know what to do now, go with the default.
+  The SCSI Parallel Interface-2 Standard defines 4 classes of transfer
+  rates: FAST-5, FAST-10, FAST-20 and FAST-40. The numbers are
+  respectively the maximum data transfer rates in mega-transfers per
+  second for each class. For example, a FAST-20 Wide 16 device is able
+  to transfer data at 20 million 16 bit packets per second for a total
+  rate of 40 MB/s.
+  You may specify 0 if you want to only use asynchronous data
+  transfers. This is the safest and slowest option. Otherwise, specify
+  a value between 5 and 40, depending on the capability of your SCSI
+  controller. The higher the number, the faster the data transfer.
+  Note that 40 should normally be ok since the driver decreases the
+  value automatically according to the controller's capabilities.
+  Your answer to this question is ignored for controllers with NVRAM,
+  since the driver will get this information from the user set-up. It
+  also can be overridden using a boot setup option, as follows
+  (example): 'ncr53c8xx=sync:12' will allow the driver to negotiate
+  for FAST-20 synchronous data transfer (20 mega-transfers per
+  second).
+  The normal answer therefore is not to go with the default but to
+  select the maximum value 40 allowing the driver to use the maximum
+  value supported by each controller. If this causes problems with
+  your SCSI devices, you should come back and decrease the value.
+  There is no safe option other than using good cabling, right
+  terminations and SCSI conformant devices.
 
 Use normal IO
 CONFIG_SCSI_NCR53C8XX_IOMAPPED
-  This option allows you to force the driver to use normal IO.
-  Memory mapped IO has less latency than normal IO and works for most
-  Intel-based hardware.
-  Under Linux/Alpha only normal IO is currently supported by the
-  driver and so, this option has no effect.
-  The normal answer therefore is N.
+  If you say Y here, the driver will use normal IO, as opposed to
+  memory mapped IO. Memory mapped IO has less latency than normal IO
+  and works for most Intel-based hardware. Under Linux/Alpha only
+  normal IO is currently supported by the driver and so, this option
+  has no effect on those systems. 
+  The normal answer therefore is N; try Y only if you encounter SCSI
+  related problems.
 
 Not allow targets to disconnect
 CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT
@@ -1877,70 +2030,61 @@
   device of yours to not support properly the target-disconnect
   feature. In that case, you would say Y here. In general however, to
   not allow targets to disconnect is not reasonable if there is more
-  than 1 device on a SCSI bus.  The normal answer therefore is N.
+  than 1 device on a SCSI bus. The normal answer therefore is N.
 
-Enable tagged command queuing
-CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE
-  This option allows you to enable tagged command queuing support at
-  Linux start-up.  Some SCSI devices do not properly support this
-  feature.  The suggested method is to say N here and to use the
-  "settags" control command after boot-up to enable this feature:
-      echo "settags 2 4" >/proc/scsi/ncr53c8xx/0
-  asks the driver to use up to 4 concurrent tagged commands for target
-  2 of controller 0.
-  See the file drivers/scsi/README.ncr53c8xx for more information.
-  WARNING! If you say Y here, then you have to say N to "not allow
-  targets to disconnect", above.
-  The safe answer therefore is N.
-  The normal answer therefore is Y.
+Default tagged command queue depth
+CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS
+  "Tagged command queuing" is a feature of SCSI-2 which improves
+  performance: the host adapter can send several SCSI commands to a
+  device's queue even if previous commands haven't finished yet. Some
+  SCSI devices don't implement this properly; if you want to disable
+  this feature, enter 0 or 1 here (it doesn't matter which).
+  The default value is 8 and should be supported by most hard disks.
+  This value can be overridden from the boot command line using the 
+  'tags' option as follows (example):
+  'ncr53c8xx=tags:4/t2t3q16/t0u2q10' will set default queue depth to
+  4, set queue depth to 16 for target 2 and target 3 on controller 0
+  and set queue depth to 10 for target 0 / lun 2 on controller 1.
+  The normal answer therefore is to go with the default 8 and to use 
+  a boot command line option for devices that need to use a different 
+  command queue depth.
+  There is no safe option other than using good SCSI devices.
 
 Maximum number of queued commands
 CONFIG_SCSI_NCR53C8XX_MAX_TAGS
   This option allows you to specify the maximum number of commands
-  that can be queued to a device, when tagged command queuing is
-  possible.  The default value is 4. Minimum is 2, maximum is 12.  The
-  normal answer therefore is the default one.
-
-Detect and read serial NVRAM
-CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT
-  Enable support for reading the serial NVRAM data on Symbios and
-  some Symbios compatible cards, and Tekram DC390W/U/F cards.  Useful
-  for systems with more than one Symbios compatible controller where
-  at least one has a serial NVRAM, or for a system with a mixture of
-  Symbios and Tekram cards.  Enables setting the boot order of host
-  adaptors to something other than the default order or "reverse
-  probe" order.  Also enables Symbios and Tekram cards to be
-  distinguished so CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT may be set in
-  a system with a mixture of Symbios and Tekram cards so the Symbios
-  cards can make use of the full range of Symbios features,
-  differential, led pin, without causing problems for the Tekram
-  card(s).
-      (added by Richard Waltham: dormouse@farsrobt.demon.co.uk)
-  Also enables setting host and targets SCSI features as defined in
-  the user setup for each host using a serial NVRAM (added by the
-  maintainer).
-  The default answer is N, the normal answer should be Y.
-  Read drivers/scsi/README.ncr53c8xx for more information.
+  that can be queued to any device, when tagged command queuing is
+  possible. The default value is 32. Minimum is 2, maximum is 64.
+  Modern hard disks are able to support 64 tags and even more, but 
+  donnot seem to be faster when more than 32 tags are being used.
+  So, the normal answer here is to go with the default value 32 unless
+  you are using very large hard disks with large cache (>= 1 MB) that
+  are able to take advantage of more than 32 tagged commands.
+  There is no safe option and the default answer is recommended.
 
 Assume boards are SYMBIOS compatible
 CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT
   This option allows you to enable some features depending on GPIO
-  wiring.  These General Purpose Input/Output pins can be used for
+  wiring. These General Purpose Input/Output pins can be used for
   vendor specific features or implementation of the standard SYMBIOS
-  features.  Genuine SYMBIOS boards use GPIO0 in output for controller
-  LED and GPIO3 bit as a flag indicating singled-ended/differential
-  interface.
-  If all the boards of your system are genuine SYMBIOS boards or use
-  BIOS and drivers from SYMBIOS, you would want to enable this option.
-  The driver behaves correctly on my system with this option enabled.
-  (SDMS 4.0 + Promise SCSI ULTRA 875 rev 0x3 + ASUS SC200 810A rev
-  0x12).  This option must be set to N if your system has at least one
-  53C8XX based SCSI board with a vendor-specific BIOS (example: Tekram
-  DC-390/U/W/F).  If unsure, say N.
-  However, if all your non Symbios compatible boards have NVRAM,
-  setting option CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT allows the driver
-  to distinguish Symbios compatible boards from other ones.  So,
-  you can answer Y if all non Symbios compatible boards have NVRAM.
+  features. Genuine SYMBIOS controllers use GPIO0 in output for
+  controller LED and GPIO3 bit as a flag indicating
+  singled-ended/differential interface. The Tekram DC-390U/F boards
+  uses a different GPIO wiring.
+  Your answer to this question is ignored if all your controllers have
+  NVRAM, since the driver is able to detect the board type from the
+  NVRAM format.
+  If all the controllers in your system are genuine SYMBIOS boards or
+  use BIOS and drivers from SYMBIOS, you would want to say Y here,
+  otherwise N. N is the safe answer.
+
+Enable profiling statistics gathering
+CONFIG_SCSI_NCR53C8XX_PROFILE
+  This option allows you to enable profiling information gathering.
+  These statistics are not very accurate due to the low frequency 
+  of the kernel clock (100 Hz on i386) and have performance impact 
+  on systems that use very fast devices.
+  The normal answer therefore is N.
 
 Always IN2000 SCSI support
 CONFIG_SCSI_IN2000
@@ -2055,21 +2199,59 @@
   and removed from the running kernel whenever you want), say M here
   and read Documentation/modules.txt.
 
-Tekram DC390(T) (AMD PCscsi) SCSI support
+Tekram DC390(T) and Am53/79C974 (PCscsi) SCSI support
 CONFIG_SCSI_DC390T
-  This driver supports the Tekram DC390(T) PCI SCSI Hostadapter with
-  the Am53C974A chip, and perhaps other cards using the same chip.
-  This driver does _not_ support the DC390W/U/F adaptor with the
-  NCR/Symbios chips.
+  This driver supports  PCI SCSI host adapters  based on the Am53C974A
+  chip,  e.g.  Tekram DC390(T),  DawiControl 2974  and  some   onboard
+  PCscsi/PCnet (Am53/79C974) solutions.
+  Documentation can be found in linux/drivers/scsi/README.tmscsim.
+  Note that this driver  does NOT support Tekram DC390W/U/F, which are
+  based on NCR/Symbios chips. Use the NCR53C8XX driver for those.
+  Also note, that there is another generic Am53C974 driver.
+  If you want to compile this driver as a module ( = code which can be
+  inserted in and removed from the running kernel  whenever you want),
+  say M here  and read Documentation/modules.txt.  The module  will be
+  called tmscsim.o.
+
+Skip support for other Am53/79C974 based SCSI adapters
+CONFIG_SCSI_DC390T_NOGENSUPP
+  Normally, the DC390(T) SCSI driver relies on the DC390 EEPROM to get
+  initial values  for its settings,  such as speed,  termination, etc.
+  If  it can't find  this EEPROM, it  will use  defaults  or  the user
+  supplied boot/module parameters. For details on driver configuration
+  see linux/drivers/scsi/README.tmscsim.
+  With this option set, if no EEPROM is found, the driver gives up and
+  thus only supports  Tekram DC390(T) adapters.  This can be useful if
+  you have a DC390(T)  and another Am53C974 based adapter,  which, for
+  some reason, you want to drive with the other AM53C974 driver.
+  If unsure, say N.
+
+Symbios Logic sym53c416 support
+CONFIG_SCSI_SYM53C416
+  This is support for the sym53c416 SCSI host adapter. This is the 
+  SCSI adapter that comes with some hp scanners. This driver requires that
+  the sym53c416 is configured first using some sort of pnp configuration
+  program (e.g. isapnp). After doing so it should be loaded as a module
+  using insmod. The parameters of the configured card(s) should be passed
+  to the driver. The format is:
+  
+    insmod sym53c416 sym53c416=<base>,<irq> [sym53c416_1=<base>,<irq>]
+
+  There is support for up to four adapters. If you want to compile this
+  driver as a module ( = code which can be inserted in and removed from
+  the running kernel whenever you want), say M here and read
+  Documentation/modules.txt.                                      
 
 AM53/79C974 PCI SCSI support
 CONFIG_SCSI_AM53C974
   This is support for the AM53/79C974 SCSI host adapters. Please read
   drivers/scsi/README.AM53C974 for details. Also, the SCSI-HOWTO,
   available via FTP (user: anonymous) at
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO, is for you.
-  Use the native DC390 driver if you've got a Tekram DC390(T) PCI-SCSI
-  host adapter.
+  ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO, is for you.
+  Note that there is another driver for AM53C974 based adapters: The
+  Tekram DC390(T) driver.
+  If you want to compile this driver as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want),
 
 GDT SCSI Disk Array Controller support
 CONFIG_SCSI_GDTH
@@ -2106,6 +2288,17 @@
        Winbond xxx837
        National Semiconductor PC87306 (early revisions)
 
+Initio INI-A100U2W SCSI support
+CONFIG_SCSI_INIA100
+  This is support for the Initio INI-A100U2W SCSI host adapter.
+  Please read the SCSI-HOWTO, available via FTP (user anonymous) at
+  ftp://metalab.unc.edu/pub/Linux/docs/HOWTO.
+
+  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 here and read Documenation/modules.txt. The module will be
+  called a100u2w.o
+
 Network device support?
 CONFIG_NETDEVICES
   You can say N here in case you don't intend to connect to any other
@@ -3840,15 +4033,61 @@
 NCP filesystem support (to mount NetWare volumes)
 CONFIG_NCP_FS
   NCP (NetWare Core Protocol) is a protocol that runs over IPX and is
-  used by Novel NetWare clients to talk to file servers. It is to IPX
-  what NFS is to TCP/IP, if that helps. Enabling this option allows
-  you to mount NetWare file server volumes and to access them just
-  like any other Unix directory. For details, please read the file
+  used by Novell NetWare clients to talk to file servers. It is to IPX
+  what NFS is to TCP/IP, if that helps. Saying Y here allows you to
+  mount NetWare file server volumes and to access them just like any
+  other Unix directory. For details, please read the file
   Documentation/filesystems/ncpfs.txt in the kernel source and the
-  IPX-HOWTO on sunsite.unc.edu:/pub/Linux/docs/howto.  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 here and
-  read Documentation/modules.txt.
+  IPX-HOWTO on ftp://sunsite.unc.edu:/pub/Linux/docs/howto.
+  You do not have to say Y here if you want your Linux box to act as a
+  file *server* for Novell NetWare clients.
+  General information about how to connect Linux, Windows machines and
+  Macs is on the WWW at http://www.eats.com/linux_mac_win.html (to
+  browse the WWW, you need to have access to a machine on the Internet
+  that has a program like lynx or netscape).
+  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 here and read Documentation/modules.txt. The module will be
+  called ncpfs.o. Say N unless you are connected to a Novell network.
+
+Packet signatures
+CONFIG_NCPFS_PACKET_SIGNING
+  NCP allows packets to be signed for stronger security. If you want
+  security, say Y. Normal users can leave it off. To be able to use
+  packet signing you must use ncpfs > 2.0.12.
+
+Proprietary file locking
+CONFIG_NCPFS_IOCTL_LOCKING
+  Allows locking of records on remote volumes. Say N unless you have
+  special applications which are able to utilize this locking scheme.
+
+Clear remove/delete inhibit when needed
+CONFIG_NCPFS_STRONG
+  Allows manipulation of files flagged as Delete or Rename Inhibit. To
+  use this feature you must mount volumes with the ncpmount parameter
+  "-s" (ncpfs-2.0.12 and newer). Say Y unless you are not mounting
+  volumes with -f 444.
+
+Use NFS namespace when available
+CONFIG_NCPFS_NFS_NS
+  Allows you to utilize NFS namespace on NetWare servers. It brings
+  you case sensitive filenames. Say Y. You can disable it at
+  mount-time with the `-N nfs' parameter of ncpmount.
+
+Use OS2/LONG namespace when available
+CONFIG_NCPFS_OS2_NS
+  Allows you to utilize OS2/LONG namespace on NetWare servers.
+  Filenames in this namespace are limited to 255 characters, they are
+  case insensitive, and case in names is preserved. Say Y. You can
+  disable it at mount time with the -N os2 parameter of ncpmount.
+
+Allow mounting of volume subdirectories
+CONFIG_NCPFS_MOUNT_SUBDIR
+  Allows you to mount not only whole servers or whole volumes, but
+  also subdirectories from a volume. It can be used to reexport data
+  and so on. There is no reason to say N, so Y is recommended unless
+  you count every byte.
+  To utilize this feature you must use ncpfs-2.0.12 or newer.
 
 Amiga FFS filesystem support (EXPERIMENTAL)
 CONFIG_AFFS_FS
@@ -4693,6 +4932,16 @@
   registers in the chips up correctly as the specification and Intel
   rules require. If you have a PPro or later SMP and one or more CPU's
   report a value of about 2-3 bogomips enable this.
+
+Nemory configuration
+CONFIG_MEM_STD
+  There are three memory configurations available. The standard
+  configuration allows use of just under 1GB of RAM with 3GB of
+  virtual space per process. The enterprise configuration allows
+  2Gigabytes of physical memory but limits the per process address
+  space to 2Gigabytes. The custom option allows you to specify the
+  split subject to kernel constraints. If you don't know how it works
+  don't pick it.
 
 # need an empty line after last entry, for sed script in Configure.
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/Documentation/README.DAC960 linux/Documentation/README.DAC960
--- linux.vanilla/Documentation/README.DAC960	Thu Jan  1 01:00:00 1970
+++ linux/Documentation/README.DAC960	Mon Nov 30 07:59:59 1998
@@ -0,0 +1,222 @@
+	       Mylex DAC960 PCI RAID Controller Driver for Linux
+
+			Version 2.0.0 for Linux 2.0.36
+			Version 2.1.0 for Linux 2.1.130
+
+			      BETA TEST RELEASE 3
+
+			       23 November 1998
+
+			       Leonard N. Zubkoff
+			       Dandelion Digital
+			       lnz@dandelion.com
+
+	   Copyright 1998 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+
+				 INTRODUCTION
+
+Mylex, Inc. designs and manufactures a variety of high performance PCI RAID
+controllers based on the Intel i960 processor.  Mylex Corporation is located at
+34551 Ardenwood Blvd., Fremont, California 94555, USA and can be reached at
+510/796-6100 or on the World Wide Web at http://www.mylex.com.  Mylex RAID
+Technical Support can be reached by electronic mail at support@mylex.com, by
+Voice at 510/608-2400, or by FAX at 510/745-7715.  Contact information for
+offices in Europe and Japan is available on the Web site.
+
+The latest information on Linux support for DAC960 PCI RAID Controllers, as
+well as the most recent release of this driver, 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
+driver at startup, along with any subsequent system messages relevant to the
+controller's operation, and a detailed description of your system's hardware
+configuration.
+
+Please consult the DAC960 documentation for detailed information regarding
+installation and configuration of the controllers.  This document primarily
+provides information specific to the Linux DAC960 support.
+
+
+				DRIVER FEATURES
+
+The DAC960 is supported solely as a high performance RAID controller, not as an
+interface to arbitrary SCSI devices.  The Linux DAC960 driver operates at the
+block device level, the same level as the SCSI and IDE drivers.  Unlike other
+RAID controllers currently supported on Linux, the DAC960 driver is not
+dependent on the SCSI subsystem, and hence avoids all the complexity and
+unnecessary code that would be associated with an implementation as a SCSI
+driver.  The DAC960 driver is designed for as high a performance as possible
+with no compromises or extra code for compatibility with lower performance
+devices.
+
+The DAC960 driver is architected to support up to 8 controllers per system.
+Each DAC960 controller can support up to 45 disk drives on 3 channels.  The
+drives installed on a controller are divided into one or more "Drive Groups",
+and then each Drive Group is subdivided further into 1 to 32 "Logical Drives".
+Each Logical Drive has a specific RAID Level and caching policy associated with
+it, and it appears to Linux as a single block device.  Logical Drives are
+further subdivided into up to 7 partitions through the normal Linux and PC disk
+partitioning schemes.  Logical Drives are also known as "System Drives", and
+Drive Groups are also called "Packs".  Both terms are in use in the Mylex
+documentation; I have chosen to standardize on the more generic "Logical Drive"
+and "Drive Group".
+
+DAC960 RAID disk devices are named in the style of the Device File System
+(DEVFS).  The device corresponding to Logical Drive D on Controller C is
+referred to as /dev/rd/cCdD, and the partitions are called /dev/rd/cCdDp1
+through /dev/rd/cCdDp7.  For example, partition 3 of Logical Drive 5 on
+Controller 2 is referred to as /dev/rd/c2d5p3.  Note that unlike with SCSI
+disks the device names will not change in the event of a disk drive failure.
+The DAC960 driver is assigned major numbers 48 - 55 with one major number per
+controller.  The 8 bits of minor number are divided into 5 bits for the Logical
+Drive and 3 bits for the partition.
+
+
+		     SUPPORTED DAC960 PCI RAID CONTROLLERS
+
+The following list comprises the supported DAC960 PCI RAID Controllers as of
+the date of this document.  It is recommended that anyone purchasing a Mylex
+PCI RAID Controller not in the following table contact the author beforehand to
+verify that it is or will be supported.
+
+AcceleRAID 250 (DAC960PTL-1)
+	    Uses onboard Symbios SCSI chips on certain motherboards
+	    Also includes one onboard Wide Ultra-2/LVD SCSI channel
+	    66MHz Intel i960RD RISC Processor
+	    4MB/8MB/16MB/32MB/64MB/128MB ECC EDO Memory
+
+AcceleRAID 200 (DAC960PTL-0)
+	    Uses onboard Symbios SCSI chips on certain motherboards
+	    Includes no onboard SCSI channels
+	    66MHz Intel i960RD RISC Processor
+	    4MB/8MB/16MB/32MB/64MB/128MB ECC EDO Memory
+
+DAC960PJ    1/2/3 Wide Ultra SCSI-3 Channels
+	    66MHz Intel i960RD RISC Processor
+	    4MB/8MB/16MB/32MB/64MB/128MB ECC EDO Memory
+
+DAC960PG    1/2/3 Wide Ultra SCSI-3 Channels
+	    33MHz Intel i960RP RISC Processor
+	    4MB/8MB ECC EDO Memory
+
+DAC960PU    1/2/3 Wide Ultra SCSI-3 Channels
+	    Intel i960CF RISC Processor
+	    2MB/4MB/8MB/16MB/32MB EDRAM or DRAM Memory (max 8MB EDRAM)
+
+DAC960PD    1/2/3 Wide Fast SCSI-2 Channels
+	    Intel i960CF RISC Processor
+	    2MB/4MB/8MB/16MB/32MB EDRAM or DRAM Memory (max 8MB EDRAM)
+
+DAC960PL    1/2/3 Wide Fast SCSI-2 Channels
+	    Intel i960 RISC Processor
+	    2MB/4MB/8MB/16MB/32MB DRAM Memory
+
+For the DAC960PJ and DAC960PG, firmware version 4.06-0-00 or above is required.
+This firmware version is available from http://www.dandelion.com/Linux/ and
+will eventually be available from http://www.mylex.com as well.  It has been
+released by Mylex and is provided with new controllers, but it has not yet
+appeared on their support web pages as of the date of this document.
+
+For the DAC960PU, DAC960PD, and DAC960PL, firmware version 3.51-0-04 or above
+required.  This firmware version is available from http://www.mylex.com.
+
+Note that earlier revisions of the DAC960PU, DAC960PD, and DAC960PL controllers
+were delivered with version 2.xx firmware.  Version 2.xx firmware is not
+supported by this driver and no support is envisioned.  Contact Mylex RAID
+Technical Support to inquire about upgrading controllers with version 2.xx
+firmware to version 3.51-0-04.  Upgrading to version 3.xx firmware requires
+installation of higher capacity Flash ROM chips, and not all DAC960PD and
+DAC960PL controllers can be upgraded.
+
+Please note that not all SCSI disk drives are suitable for use with DAC960
+controllers, and only particular firmware versions of any given model may
+actually function correctly.  Similarly, not all motherboards have a BIOS that
+properly initializes the AcceleRAID 250, AcceleRAID 200, DAC960PJ, and DAC960PG
+because the Intel i960RD/RP is a multi-function device.  If in doubt, contact
+Mylex RAID Technical Support (support@mylex.com) to verify compatibility.
+
+
+		CONTROLLER CONFIGURATION AND STATUS MONITORING
+
+The DAC960 Online Configuration Utilities are not yet available on Linux but
+will hopefully be supported in the future.  The AcceleRAID 250, AcceleRAID 200,
+DAC960PJ, and DAC960PG controllers can generally be configured using the DAC960
+Configuration Utility included in the controller's BIOS ROM and available via
+Alt-R during BIOS initialization.  Older DAC960 controllers required the DACCF
+utility that runs from a DOS boot disk.
+
+The status of each DAC960 controller is queried every 7 seconds by the Linux
+driver to verify that no problems have been detected, and any changes in status
+are reported through appropriate kernel messages.  The following log excerpt
+details the process of the controller automatically rebuilding onto a spare
+drive when a disk drive failed.  In this example, 4 drives in a SAF-TE
+enclosure were grouped into a Drive Group which was then divided into 3 Logical
+Drives configured as RAID-5, RAID-5, and RAID-6.  An additional identical drive
+was installed as a "Standby" or "Hot Spare" to provide for automatic
+rebuilding.  The first two messages are the result of the standby drive being
+removed, the third message is a result of it being reinstalled, and the
+remaining messages are the result of the first drive being removed to force an
+automatic rebuild.
+
+DAC960#0: Physical Drive 0:4 killed because it was removed 
+DAC960#0: Physical Drive 0:4 is now DEAD 
+DAC960#0: Physical Drive 0:4 is now STANDBY 
+DAC960#0: Logical Drive 0 (/dev/rd/c0d0) is now CRITICAL
+DAC960#0: Logical Drive 1 (/dev/rd/c0d1) is now CRITICAL
+DAC960#0: Logical Drive 2 (/dev/rd/c0d2) is now CRITICAL
+DAC960#0: Physical Drive 0:0 killed because of timeout on SCSI command 
+DAC960#0: Physical Drive 0:0 is now DEAD 
+DAC960#0: Physical Drive 0:0 killed because it was removed 
+DAC960#0: Physical Drive 0:4 is now WRITE-ONLY 
+DAC960#0: REBUILD IN PROGRESS: Logical Drive 0 (/dev/rd/c0d0) 9% completed
+DAC960#0: REBUILD IN PROGRESS: Logical Drive 0 (/dev/rd/c0d0) 45% completed
+DAC960#0: REBUILD IN PROGRESS: Logical Drive 0 (/dev/rd/c0d0) 90% completed
+DAC960#0: REBUILD IN PROGRESS: Logical Drive 1 (/dev/rd/c0d1) 28% completed
+DAC960#0: REBUILD IN PROGRESS: Logical Drive 1 (/dev/rd/c0d1) 66% completed
+DAC960#0: REBUILD IN PROGRESS: Logical Drive 2 (/dev/rd/c0d2) 62% completed
+DAC960#0: REBUILD IN PROGRESS: Logical Drive 2 (/dev/rd/c0d2) 82% completed
+DAC960#0: REBUILD COMPLETED SUCCESSFULLY
+DAC960#0: Logical Drive 0 (/dev/rd/c0d0) is now ONLINE
+DAC960#0: Logical Drive 1 (/dev/rd/c0d1) is now ONLINE
+DAC960#0: Logical Drive 2 (/dev/rd/c0d2) is now ONLINE 
+DAC960#0: Physical Drive 0:4 is now ONLINE 
+
+
+			      DRIVER INSTALLATION
+
+This distribution was prepared for Linux kernel version 2.0.36 or 2.1.130.
+
+To install the DAC960 RAID driver, you may use the following commands,
+replacing "/usr/src" with wherever you keep your Linux kernel source tree:
+
+  cd /usr/src
+  tar -xvzf DAC960-2.0.0-Beta3.tar.gz (or DAC960-2.1.0-Beta3.tar.gz)
+  mv README.DAC960 DAC960.[ch] linux/drivers/block
+  patch -p0 < DAC960.patch
+  cd linux
+  make config
+  make depend
+  make zImage (or bzImage)
+
+Then install "arch/i386/boot/zImage" or "arch/i386/boot/bzImage" as your
+standard kernel, run lilo if appropriate, and reboot.
+
+To create the necessary devices in /dev, the "make_rd" script included in
+"DAC960-Utilities.tar.gz" from http://www.dandelion.com/Linux/ may be used.
+Also included in this archive are patches to LILO 20 and FDISK v2.8 that add
+DAC960 support, along with statically linked executables of LILO and FDISK.
+This modified version of LILO will allow booting from a DAC960 controller
+and/or mounting the root file system from a DAC960.  Unfortunately, installing
+directly onto a DAC960 will be problematic until the various Linux distribution
+vendors update their installation utilities.
+
+
+		      DAC960 ANNOUNCEMENTS MAILING LIST
+
+The DAC960 Announcements Mailing List provides a forum for informing Linux
+users of new driver releases and other announcements regarding Linux support
+for DAC960 PCI RAID Controllers.  To join the mailing list, send a message to
+"dac960-announce-request@dandelion.com" with the line "subscribe" in the
+message body.
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/Documentation/ide.txt linux/Documentation/ide.txt
--- linux.vanilla/Documentation/ide.txt	Fri Jul 24 17:28:55 1998
+++ linux/Documentation/ide.txt	Sun Dec 27 21:19:47 1998
@@ -5,6 +5,9 @@
 	Gadi Oxman   <gadio@netvision.net.il>	-- tapes, disks, whatever
 	Scott Snyder <snyder@fnald0.fnal.gov>	-- cdroms, ATAPI, audio
 
+UDMA support was added for various chipsets, from kernel 2.0.35 on. Check
+the udma.txt file in this directory for details.
+
    +-----------------------------------------------------------------+
    |  The hdparm utility for controlling various IDE features is     |
    |  packaged separately.  Look for it on popular linux FTP sites.  |
@@ -483,3 +486,135 @@
 For really high end systems, go for fast/wide 7200rpm SCSI.  But it'll cost ya!
 
 mlord@pobox.com
+================================================================================
+
+DMA Bus Master transfer
+-----------------------
+The triton.c driver provides support for the DMA Bus Mastering functions of
+the Intel PCI Triton I/II chipsets (i82371FB or i82371SB).
+
+Pretty much the same code will work for the OPTi "Viper" chipset. Look for
+DMA support for this in linux kernel 2.1.xx, when it appears.
+
+DMA is currently supported only for hard disk drives (not cdroms).
+
+Support for cdroms will likely be added at a later date, after broader
+experience has been obtained with hard disks.
+
+Up to four drives may be enabled for DMA, and the motherboard chipset will
+(hopefully) arbitrate the PCI bus among them.  Note that the i82371 chip
+provides a single "line buffer" for the BM IDE function, so performance of
+multiple (two) drives doing DMA simultaneously will suffer somewhat, as they
+contest for that resource bottleneck.  This is handled transparently inside
+the i82371 chip.
+
+The SiS 5513 controller has two completely independent IDE controller units,
+each with a 64-byte line buffer (same size as the Intel); there is no
+bottleneck in simultaneous (U)DMA transfers for this resource. The 5513 is
+built-in the SiS 5571, 5598 and 5591 chipsets.
+
+The VIA chipsets like the Intel have a single 64 byte line buffer, but it
+can be split 1/2-1/2 or 1/4-3/4 between both channels.
+
+By default, DMA support is prepared for use, but is currently enabled only
+for drives which support multi-word DMA mode2 (mword2), or which are
+recognized as "good" (see table below).  Drives with only mode0 or mode1
+(single or multi) DMA should also work with this chipset/driver (eg.
+MC2112A) but are not enabled by default.  Use "hdparm -i" to view modes
+supported by a given drive.
+
+The hdparm-3.3 (patched) utility can be used to manually enable /disable DMA
+support, but must be (re-)compiled against this kernel version or later.
+
+Michel Aubry has produced a patch against hdparm-3.3 to support UDMA.
+
+To enable DMA, use "hdparm -d1 /dev/hd?" on a per-drive basis after booting.
+If problems arise, ide.c will disable DMA operation after a few retries.
+This error recovery mechanism works and has been extremely well exercised.
+
+IDE drives, depending on their vintage, may support several different modes
+of DMA operation.  The boot-time modes are indicated with a "*" in the
+"hdparm -I" listing, and can be changed with *knowledgeable* use of the
+"hdparm -X" feature (X32 for DMA 0, X33 for DMA 1, X34 for DMA 2, X64 for
+UDMA 0, X65 for UDMA 1 and X66 for UDMA 2).
+
+Testing was done with an ASUS P55TP4XE/100 system and the following drives:
+
+  Quantum Fireball 1080A (1Gig w/83kB buffer), DMA mode2, PIO mode4.
+ 	- DMA mode2 works well (7.4MB/sec), despite the tiny on-drive buffer.
+ 	- This drive also does PIO mode4, at about the same speed as DMA mode2.
+ 	  An awesome drive for the price!
+ 
+  Fujitsu M1606TA (1Gig w/256kB buffer), DMA mode2, PIO mode4.
+ 	- DMA mode2 gives horrible performance (1.6MB/sec), despite the good
+ 	  size of the on-drive buffer and a boasted 10ms average access time.
+ 	- PIO mode4 was better, but peaked at a mere 4.5MB/sec.
+ 
+  Micropolis MC2112A (1Gig w/508kB buffer), drive pre-dates EIDE and ATA2.
+ 	- DMA works fine (2.2MB/sec), probably due to the large on-drive buffer.
+ 	- This older drive can also be tweaked for fastPIO (3.7MB/sec) by using
+ 	  maximum clock settings (5,4) and setting all flags except prefetch.
+ 
+  Western Digital AC31000H (1Gig w/128kB buffer), DMA mode1, PIO mode3.
+ 	- DMA does not work reliably.  The drive appears to be somewhat tardy
+ 	  in deasserting DMARQ at the end of a sector.  This is evident in
+ 	  the observation that WRITEs work most of the time, depending on
+ 	  cache-buffer occupancy, but multi-sector reads seldom work.
+ 
+Testing was done with a Gigabyte GA-586 ATE system and the following drive:
+(Uwe Bonnes - bon@elektron.ikp.physik.th-darmstadt.de)
+ 
+  Western Digital AC31600H (1.6Gig w/128kB buffer), DMA mode2, PIO mode4.
+ 	- much better than its 1Gig cousin, this drive is reported to work
+ 	  very well with DMA (7.3MB/sec).
+ 
+Other drives:
+ 
+  Maxtor 7540AV (515Meg w/32kB buffer), DMA modes mword0/sword2, PIO mode3.
+ 	- a budget drive, with budget performance, around 3MB/sec.
+ 
+  Western Digital AC2850F (814Meg w/64kB buffer), DMA mode1, PIO mode3.
+ 	- another "caviar" drive, similar to the AC31000, except that this one
+ 	  worked with DMA in at least one system.  Throughput is about 3.8MB/sec
+ 	  for both DMA and PIO.
+ 
+  Conner CFS850A (812Meg w/64kB buffer), DMA mode2, PIO mode4.
+ 	- like most Conner models, this drive proves that even a fast interface
+ 	  cannot improve slow media.  Both DMA and PIO peak around 3.5MB/sec.
+ 
+  Maxtor 71260AT (1204Meg w/256kB buffer), DMA mword0/sword2, PIO mode3.
+ 	- works with DMA, on some systems (but not always on others, eg. Dell),
+ 	  giving 3-4MB/sec performance, about the same as mode3.
+ 
+  IBM DHEA 36480 (6197Meg w/476kB buffer), DMA mode2, PIO mode4, UDMA mode2
+	- works with DMA and UDMA on systems that support it. This drive and its
+	  larger 8.4GB cousin provide throughput of 9.8MB/sec under UDMA.
+ 
+If you have any drive models to add, email your results to:  mlord@pobox.com
+Keep an eye on /var/adm/messages for "DMA disabled" messages.
+ 
+Some people have reported trouble with Intel Zappa motherboards.
+This can be fixed by upgrading the AMI BIOS to version 1.00.04.BS0,
+available from ftp://ftp.intel.com/pub/bios/10004bs0.exe
+(thanks to Glen Morrell <glen@spin.Stanford.edu> for researching this).
+ 
+And, yes, Intel Zappa boards really *do* use the Triton IDE ports.
+ 
+Changes by Michel Aubry, Andre Hedrick and Andrew D. Balsa, June 1998:
+  a) Added support for non-Intel chipsets that support Bus Mastering DMA.
+  b) Added support for UDMA (33Mb/s) drives and controllers. Note that UDMA
+     support must be enabled in the BIOS, and that both the hard disk drive
+     _and_ the chipset must support UDMA.
+  On the IBM DHEA-36480 drive, transfer rates go from 7.76Mb/s to 9.76Mb/s,
+  a 25% improvement with zero cost (DMA mode2 to UDMA mode2).
+ 
+Extra UDMA PCI controller card support by Andre M. Hedrick, June 1998:
+  - PDC20246 Promise Ultra33 UDMA.
+  - AEC6210  Artop Electronics Corp. ACARD
+        sold under SIIG CN2449 UltraIDE Pro.
+  - HPT343   Triones Technologies (HighPoint Technologies) Inc.
+        future support -- nonbooting cards, need MNDA approval for
+        information release.
+        sold under Digital Research DRIDEUDMA.
+
+For more information on UDMA support, check /Documentation/udma.txt.
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/Documentation/isdn/README linux/Documentation/isdn/README
--- linux.vanilla/Documentation/isdn/README	Sun Dec  6 00:14:35 1998
+++ linux/Documentation/isdn/README	Tue Mar  2 00:39:21 1999
@@ -335,9 +335,9 @@
       "manual" is a dial mode created to prevent the unexpected dialouts.
         In this mode, the interface will never make any connections on its
         own. You must explicitly initiate a connection with "isdnctrl dial
-        isdn0". You _must_ also hangup the line explicitly as well! There
-        is NO timeout in this mode. Use "isdnctrl hangup isdn0" to end the
-        connection.
+        sdn0". However, after an idle time of no traffic as configured for 
+        the huptimeout value with isdnctrl, the connection _will_ be ended.
+        If you don't want any automatic hangup, set the huptimeout value to 0.
         "manual" is the default.
 
    j) Setup the interface with ifconfig as usual, and set a route to it.
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/Documentation/more-than-900MB-RAM.txt linux/Documentation/more-than-900MB-RAM.txt
--- linux.vanilla/Documentation/more-than-900MB-RAM.txt	Thu Jan  1 01:00:00 1970
+++ linux/Documentation/more-than-900MB-RAM.txt	Fri Dec 11 20:35:53 1998
@@ -0,0 +1,40 @@
+
+This document describes how to configure the Linux kernel to
+support more than 950MB physical RAM on x86 systems:
+
+you only have to change the 'Max physical memory in MB' kernel
+config option to get everything working. If you have less than
+900M RAM, dont touch the default setting, this option buys you
+nothing at all! The option is in 'General setup':
+
+          [ ] Kernel math emulation
+          (1800)  Max physical memory in MB
+          [*] Networking support
+          [ ] Limit memory to low 16MB
+
+the unit of CONFIG_MAX_MEMSIZE is 'megabytes', ie. a value of
+'1024' means '1024 MBytes'. Unless in 2.1 there is no restriction
+on the value of CONFIG_MAX_MEMSIZE!
+
+IMPORTANT: the value of CONFIG_MAX_MEMSIZE should be about 128M
+more than the amount of physical RAM (or 1024 if RAM is less than
+900M), because the kernel needs some space for it's own memory
+mappings. The kernel enforces this 128M window by clipping away
+from the end of phsyical memory if necessary. (in this case that
+chunk of physical memory is not used by Linux!) So configure this
+option carefully, and look at 'free' output and boot messages
+wether all RAM is correctly detected and configured.
+
+A system with 2G physical memory should use a value of ~2400, a
+system with 3.8G memory should use something like 3900. A bit of
+experimentation with the limit wont hurt, the kernel needs a ~128M
+window for vmalloc() plus PCI space uses up some memory too, thus
+physical addresses above FD000000 should rather be kept free.
+
+if the BIOS does not report correct memory size, use the mem= boot
+commandline option to override it.
+
+feel free to report any problems/suggestions to:
+
+         Ingo Molnar <mingo@redhat.com>
+
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/Documentation/paride.txt linux/Documentation/paride.txt
--- linux.vanilla/Documentation/paride.txt	Sun Dec  6 00:14:35 1998
+++ linux/Documentation/paride.txt	Sun Dec 27 21:08:29 1998
@@ -64,6 +64,7 @@
 	SyQuest EZ-135, EZ-230 & SparQ drives
 	Avatar Shark
 	Imation Superdisk LS-120
+	Maxell Superdisk LS-120
 	FreeCom Power CD 
 	Hewlett-Packard 5GB and 8GB tape drives
 	Hewlett-Packard 7100 and 7200 CD-RW drives
@@ -98,6 +99,7 @@
         epia    Shuttle EPIA                           (UK)
 	fit2    FIT TD-2000			       (US)
 	fit3    FIT TD-3000			       (US)
+	friq    Freecom IQ cable                       (DE)
         frpw    Freecom Power                          (DE)
         kbic    KingByte KBIC-951A and KBIC-971A       (TW)
 	ktti    KT Technology PHd adapter              (SG)
@@ -133,10 +135,12 @@
 	MicroSolutions          8000t tape      pt      bpck
 	SyQuest			EZ, SparQ	pd	epat
 	Imation			Superdisk	pf	epat
+	Maxell                  Superdisk       pf      friq
 	Avatar			Shark		pd	epat
 	FreeCom			CD-ROM		pcd	frpw
 	Hewlett-Packard		5GB Tape	pt	epat
-	Hewlett-Packard		7100/7200	pg	epat
+	Hewlett-Packard		7200e (CD)	pcd	epat
+	Hewlett-Packard		7200e (CD-R)	pg	epat
 
 2.1  Configuring built-in drivers
 
@@ -315,7 +319,6 @@
 partitioned.  Consequently, the pf driver does not support partitioned
 media.  This may be changed in a future version of the driver. 
 
-
 2.5  Using the pt driver
 
 The pt driver for parallel port ATAPI tape drives is a minimal driver.
@@ -323,27 +326,29 @@
 For best performance, a block size of 32KB should be used.  You will
 probably want to set the parallel port delay to 0, if you can.
 
-
 2.6  Using the pg driver
 
 The pg driver can be used in conjunction with the cdrecord program
-to create CD-ROMs.  Please get cdrecord version 1.6.1a3 or later
-from ftp://ftp.fokus.gmd.de/pub/unix/cdrecord/ (you may have to look
-in the alpha subdirectory).  To record CD-R media your parallel port
-should ideally be set to EPP mode, and the "port delay" should be
-set to 0.  With those settings it is possible to record at 2x speed
-without any buffer underruns.  If you cannot get the driver to work
+to create CD-ROMs.  Please get cdrecord version 1.6.1 or later
+from ftp://ftp.fokus.gmd.de/pub/unix/cdrecord/ .  To record CD-R media 
+your parallel port should ideally be set to EPP mode, and the "port delay" 
+should be set to 0.  With those settings it is possible to record at 2x 
+speed without any buffer underruns.  If you cannot get the driver to work
 in EPP mode, try to use "bidirectional" or "PS/2" mode and 1x speeds only.
 
 
 3. Troubleshooting
 
+3.1  Use EPP mode if you can
+
 The most common problems that people report with the PARIDE drivers
 concern the parallel port CMOS settings.  At this time, none of the
 PARIDE protocol modules support ECP mode, or any ECP combination modes.
 If you are able to do so, please set your parallel port into EPP mode
 using your CMOS setup procedure.
 
+3.2  Check the port delay
+
 Some parallel ports cannot reliably transfer data at full speed.  To
 offset the errors, the PARIDE protocol modules introduce a "port
 delay" between each access to the i/o ports.  Each protocol sets
@@ -357,6 +362,25 @@
 read the comments at the beginning of the driver source files in
 linux/drivers/block/paride.
 
+3.3  Some drives need a printer reset
+
+There appear to be a number of "noname" external drives on the market
+that do not always power up correctly.  We have noticed this with some
+drives based on OnSpec and older Freecom adapters.  In these rare cases,
+the adapter can often be reinitialised by issuing a "printer reset" on
+the parallel port.  As the reset operation is potentially disruptive in 
+multiple device environments, the PARIDE drivers will not do it 
+automatically.  You can however, force a printer reset by doing:
+
+	insmod lp
+	rmmod lp
+
+If you have one of these marginal cases, you should probably build
+your paride drivers as modules, and arrange to do the printer reset
+before loading the PARIDE drivers. 
+
+3.4  Use the verbose option and dmesg if you need help
+
 While a lot of testing has gone into these drivers to make them work
 as smoothly as possible, problems will arise.  If you do have problems,
 please check all the obvious things first:  does the drive work in
@@ -383,6 +407,8 @@
 of two ways.  Either send it directly to the author of the PARIDE suite,
 by e-mail to grant@torque.net, or join the linux-parport mailing list
 and post your report there.
+
+3.5  For more information or help
 
 You can join the linux-parport mailing list by sending a mail message
 to 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/Documentation/udma.txt linux/Documentation/udma.txt
--- linux.vanilla/Documentation/udma.txt	Thu Jan  1 01:00:00 1970
+++ linux/Documentation/udma.txt	Sun Dec 27 21:22:21 1998
@@ -0,0 +1,663 @@
+UDMA information for kernels 2.0.35+
+
+Version 0.4 - July 98
+by Andrew D. Balsa <andrebalsa@altern.org>
+
+If you are in a hurry, skip to the "How does one use UDMA support?" section.
+
+If you need troubleshooting advice, check the "Unreliable drive +
+motherboard + driver combination" section.
+
+Support for UDMA is based on previous work by Kim-Hoe Pang and Christian
+Brunner, posted on the Linux Kernel mailing list around September 1997.
+Additional code was provided by Michel Aubry (VIA support) and Andre Hedrick
+(support for various PCI UDMA controller cards). The code base is Mark
+Lord's triton.c driver.
+
+Check the Linux UDMA mini-HOWTO by Brion Vibber first! It is the only Linux
+specific document available on the subject.
+
+Technical references:
+a) The Intel 82371AB data sheet, available in PDF format.
+b) The SiS 5598 and 5591 data sheets, available in Microsoft Word format. :(
+c) The VIA 82C586, 82C586A and 82C586B data sheets, in PDF format.
+d) Small Form Factor document SFF 8038I v1.0. This is the original document
+   that describes the DMA mode 2 protocol. Available in PDF format.
+e) The ATA/ATAPI-4 Working Draft, revision 17. This is document
+   d1153r17.pdf (in PDF format), available from the main IDE technical
+   reference site, ftp://fission.dt.wdc.com/pub/standards. This draft
+   describes the Ultra DMA protocol and timings.
+
+A somewhat less technical, but still very informative document is the
+Enhanced IDE/Fast-ATA/ATA-2 FAQ, by John Wehman and Peter den Haan. Check
+the Web page at http://come.to/eide.  
+
+**************************************************************************
+
+Files changed
+-------------
+
+Here is the set of files from Linux kernels 2.0.32/2.0.34 modified to enable
+UDMA transfers on motherboard chipsets that support it. For each file, there
+is a small explanation of the changes.
+
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+The following changes do not affect performance or stability of the IDE
+driver in any way.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+/drivers/block/triton.c
+
+ - removed some Intel specific timing stuff. This should not affect
+driver operation or performance. This is the only file that is heavily
+modified; the Promise and Artop code is by Andre Hedrick, the VIA code
+by Michel Aubry.
+
+/drivers/block/ide.c
+
+ - added UDMA drive reporting during driver initialization, at the end
+of routine do_identify (single line mod).
+
+ - added data for SiS 5513 and VIA VP-1 chipset in routine probe_for_hwifs
+(single line mods). Each new UDMA capable chipset will have to be added to
+this list (a single line is needed each time). Notice that you don't even
+need the motherboard chipset's data sheets to find the needed information.
+You just have to identify the IDE controller. You can do this by checking
+/proc/pci, and then comparing the IDE controller signature with that
+available from the Linux kernel.
+
+As it stands in this patched version, routine probe_for_hwifs supports the
+following chipsets: Intel FX, HX, VX, TX, LX and SiS 5513 (which is
+integrated in the SiS 5571, 5598 and 5591 chips). The VIA-VP1
+chipset is supported for DMA mode 2 transfers, but compatibility has not
+been tested with this driver. The VIA MVP-3 is reported OK with UDMA.
+
+/drivers/block/ide.h
+
+ - added flag using_udma in struct ide_drive_s (single line mod). 
+
+Small changes to the tape and ide-floppy code, and additions to pci.h and
+pci.c for the extra PCI UDMA controller devices.
+
+
+Tested configurations
+---------------------
+
+UDMA support has been thoroughly tested on the following configurations:
+
+Intel TX motherboard, PCI bus at 33 and 37.5MHz. (ASUS TX-97E)
+
+SiS 5598 motherboards, PCI bus at 33 and 37.5MHz. (Chaintech P5-SDA; ASUS
+SP-97V at 33MHz only)
+
+IBM DeskStar 6.4Gb and 8.4Gb drives. Samsung UDMA hard disk proved
+unreliable under Linux _and_ Windows95 (so it was not a driver problem).
+Other UDMA drives not tested.
+
+libc5 and gcc2.7.2. Also tested under libc6 (RedHat 5.0).
+
+6x86MX processor running at 166MHz or 187.5MHz.
+
+DANGER: EIDE drives do not accept a PCI bus at 41.5MHz (83MHz / 2). Trying
+to run DMA Mode 2 or UDMA at these PCI bus clocks will result in crashes and
+loss of data. If your FSB runs at > 75MHz you MUST set the PCI bus for
+asynchronous 33MHz operation. YOU HAVE BEEN WARNED.
+
+Andre Hedrick Tests [IMPORTANT: those are SMP configurations]
+-------------------------------------------------------------
+
+Test I
+------
+
+Tyan Tomcat III bios v4.01 SMP Dual P5 200 w/ Artop AEC6210 w/ DMA2 drives
+
+Intel MultiProcessor Specification v1.4
+    Virtual Wire compatibility mode.
+OEM ID: OEM00000 Product ID: PROD00000000 APIC at: 0xFEE00000
+Processor #0 Pentium(tm) APIC version 17
+Processor #1 Pentium(tm) APIC version 17
+I/O APIC #2 Version 17 at 0xFEC00000.
+Processors: 2
+
+Linux version 2.0.34 (root@Zosma) (gcc version 2.8.1) #1 Mon Jun 8 16:40:25 CDT
+Booting processor 1 stack 00002000: Calibrating delay loop.. ok - 79.67
+BogoMIPSTotal of 2 processors activated (159.33 BogoMIPS).
+Starting kswapd v 1.4.2.2
+
+ide: DMA Bus Mastering IDE controller on PCI bus 0 function 57
+ide: ports are not enabled (BIOS)
+ide: AEC6210 ROM enabled but no address
+ide: UDMA Bus Mastering IDE controller on PCI bus 0 function 160
+ide: timings == 04010401
+    ide0: BM-DMA at 0x6700-0x6707
+    ide1: BM-DMA at 0x6708-0x670f
+hda: Maxtor 72004 AP, 1916MB w/128kB Cache, CHS=973/64/63, DMA
+hdb: Maxtor 71626 A, 1554MB w/64kB Cache, CHS=789/64/63, DMA
+hdc: IOMEGA ZIP 100 ATAPI, ATAPI FLOPPY drive
+hdd: HP COLORADO 5GB, ATAPI TAPE drive
+ide-tape: Sorry, DRQ types other than Accelerated DRQ
+ide-tape: are still not supported by the driver
+ide-tape: the tape is not supported by this version of the driver
+ide2: ports already in use, skipping probe
+ide0 at 0x6300-0x6307,0x6402 on irq 11
+ide1 at 0x6500-0x6507,0x6602 on irq 11 (shared with ide0)
+scsi0 : ncr53c8xx - revision 2.5f.1
+
+Test II
+-------
+
+SuperMicro P6DNF SMP Dual P6 233 w/ Artop AEC6210 and Promise Ultra33
+
+Intel MultiProcessor Specification v1.4
+    Virtual Wire compatibility mode.
+OEM ID: INTEL    Product ID: 440FX        APIC at: 0xFEE00000
+Processor #0 Pentium(tm) Pro APIC version 17
+Processor #1 Pentium(tm) Pro APIC version 17
+I/O APIC #2 Version 17 at 0xFEC00000.
+Processors: 2
+
+Linux version 2.0.34 (root@Orion) (gcc version 2.8.1) #1 Wed Jun 17 01:13:15 CDT 1998
+Booting processor 1 stack 00002000: Calibrating delay loop.. ok - 232.65 BogoMIPS
+Total of 2 processors activated (464.49 BogoMIPS).
+
+ide: Intel 82371 (single FIFO) DMA Bus Mastering IDE
+    Controller on PCI bus 0 function 57
+ide: ports are not enabled (BIOS)
+ide: AEC6210 ROM enabled at 0xfebf8000
+ide: PCI UDMA Bus Mastering IDE
+    Controller on PCI bus 0 function 160
+ide: timings == 04010401
+    ide0: BM-DMA at 0xef90-0xef97
+    ide1: BM-DMA at 0xef98-0xef9f
+hda: QUANTUM FIREBALL ST3.2A, 3079MB w/81kB Cache, CHS=782/128/63, UDMA
+hdb: QUANTUM FIREBALL ST6.4A, 6149MB w/81kB Cache, CHS=784/255/63, UDMA
+hdc: IOMEGA ZIP 100 ATAPI, ATAPI FLOPPY drive
+hdd: CD-ROM CDU611, ATAPI CDROM drive
+ide2: ports already in use, skipping probe
+ide0 at 0xeff0-0xeff7,0xefe6 on irq 10
+ide1 at 0xefa8-0xefaf,0xefe2 on irq 10 (shared with ide0)
+
+Test III
+--------
+
+Same kernel above but with Promise Ultra33
+
+ide: Intel 82371 (single FIFO) DMA Bus Mastering IDE
+    Controller on PCI bus 0 function 57
+ide: ports are not enabled (BIOS)
+ide: PDC20246 ROM enabled at 0xfebd0000
+ide: PCI UDMA Bus Mastering IDE
+    Controller on PCI bus 0 function 160
+ide: timings == 000003ee
+    ide0: BM-DMA at 0xef80-0xef87
+    ide1: BM-DMA at 0xef88-0xef8f
+hda: QUANTUM FIREBALL ST3.2A, 3079MB w/81kB Cache, CHS=782/128/63, UDMA
+hdb: QUANTUM FIREBALL ST6.4A, 6149MB w/81kB Cache, CHS=784/255/63, UDMA
+hdc: IOMEGA ZIP 100 ATAPI, ATAPI FLOPPY drive
+hdd: CD-ROM CDU611, ATAPI CDROM drive
+ide2: ports already in use, skipping probe
+ide0 at 0xeff0-0xeff7,0xefe6 on irq 10
+ide1 at 0xefa8-0xefaf,0xebe6 on irq 10 (shared with ide0)
+
+All tests cases yield this problem, IOMEGA ZIP 100 ATAPI FW 23.D
+I have a patch fix for 2.1.99->106 similar for FW 21.D drives.
+
+ide-floppy: hdc: I/O error, pc = 5a, key =  5, asc = 24, ascq =  0
+ide-floppy: Can't get drive capabilities
+
+Note that both AEC6210 and PDC20246 have onboard bios that auto-config.
+
+
+What UDMA support does
+----------------------
+
+        - It enables UDMA transfers on the Intel TX chipset.
+	- It enables DMA mode2 transfers on the SiS 5571 and VIA VP-1
+	  (82C586) chipsets.
+        - It enables DMA mode2 and UDMA mode2 transfers on the SiS 5598 and
+	  SiS 5591 chipsets, and the VIA VP3 and MVP-3.
+        - With single line mods for each new chipset, it will support any DMA
+	  mode2 and/or UDMA capable chipset compatible with document
+	  SFF 8038I v1.0.
+	- Supports a variety of PCI UDMA controller cards.
+
+
+Support for other chipsets
+--------------------------
+
+It is relatively easy to add support for other chipsets. Some chipsets are
+entirely integrated (e.g. the SiS 5598 chipset has various devices in a
+single chip), others are divided into a Northbridge (CPU to PCI circuitry,
+L2 cache control, etc) and Southbridge (PCI to IDE bus mastering interface,
+USB, etc). We are dealing here with the Southbridge, specifically with the
+IDE bus master PCI device. If the data sheet says the device is SFF 8038I
+v1.0 compatible, then the registers have a more or less standard layout and
+this driver should work with the below changes:
+
+1) Check that the chipset is correctly identified by /proc/pci. Search for
+the line that identifies a bus-mastering IDE controller device.
+
+2) If the chipset is not correctly identified (new chipsets are not in
+kernels up to 2.0.33), you will need to edit /include/linux/pci.h and
+/drivers/pci/pci.c. This is actually quite easy, requiring a single line in
+each of these files.
+
+3) Now add a single line to ide.c, in routine probe_for_hwifs.
+
+4) Test and report results; when troubleshooting, please check first that
+you have the latest BIOS for your motherboard. 
+
+
+HOW does UDMA mode2 work?
+-------------------------
+
+Well, actually, the excellent triton.c driver written by Mark Lord is a
+generic DMA transfer hard disk driver. It does not depend on any chipset
+feature or transfer mode (i.e. it will work with DMA mode 2, UDMA and other
+future DMA modes with little or no changes). BTW in late 2.1.x kernels the
+driver was renamed ide-dma.c, to indicate that it is independent of the
+chipset used.
+
+(Note: triton is the "old" name for the Intel FX chipset, for which Mark
+Lord wrote the driver initially.)
+
+The Intel chipset specific parts were slightly changed in the triton.c
+driver. These are only used to gather information for driver testing, and
+actually do not affect the operation or performance of the driver, so the
+changes are (well, should be) relatively inocuous.
+
+The real work involved in setting up the chips for DMA transfers is done
+mostly by the BIOS of each motherboard. Now of course one hopes that the
+BIOS has been correctly programmed...
+
+For example, the ASUS SP-97V motherboard with its original BIOS (Rev. 1.03)
+would malfunction with the modified Linux driver in both DMA mode 2 and UDMA
+modes; it would work well using PIO mode 4, or under Windows 95 in all
+modes. I downloaded the latest BIOS image (Rev. 1.06) from the ASUS Web site
+and flashed the BIOS EPROM with the latest BIOS revision. It has been
+working perfectly ever since (at 66 MHz bus speeds).
+
+What this tells us is that the BIOS sets up the DMA controller with specific
+timing parameters (active pulse and recovery clock cycles). My initial BIOS
+revision probably had bad timings. Since the Windows 95 driver sets up those
+timings by itself (i.e. it does not depend on the BIOS to setup the hard
+disk controller timing parameters), I initially had problems only with the
+Linux driver, while Windows 95 worked well.
+
+So, let me state this again: this Linux (U)DMA driver depends on the BIOS for
+correct (U)DMA controller setup. If you have problems, first check that you
+have the latest BIOS revision for your specific motherboard.
+
+OTOH Michel Aubry's code for the VIA Apollo chipset has complete support for
+setting up the timing parameters. Check the triton.c source code for
+details.
+
+New BIOS revisions can be downloaded from your motherboard manufacturer's
+Web site. Flashing a new BIOS image is a simple operation but one must
+strictly follow the steps explained on the motherboard manual.
+
+Late Award BIOS revisions seem stable with respect to UDMA. Anything with a
+date of 1998 should be fine.
+
+
+Features missing from the present UDMA support code
+---------------------------------------------------
+
+It does not set UDMA transfer parameters (the driver assumes the BIOS has
+correctly setup all timing parameters) in the various chipsets. This
+requires access to a complete set of data sheets for the various chipsets,
+and testing on a variety of configurations, so it's not exactly within the
+reach of a humble Linux hacker. IMHO this is best left to the guys at Award
+and AMI (the BIOS guys), and to the motherboard engineers.
+
+Basically, UDMA transfers depend on two timing parameters:
+	1) The pulse width of the active strobe signal for data transfers
+(usually described as the active pulse width).
+	2) The delay between the negation of the DMA READY signal to the
+assertion of STOP when the IDE controller wants to stop a read operation
+(usually described as the recovery time).
+
+Both timing parameters can be set individually for each hard disk (up to two
+hard disks per channel).
+
+Knowing which registers must hold this data and the appropriate values, one
+could program the Linux triton.c driver to setup the IDE controller device,
+without relying on BIOS settings. However, some chipsets allow setting other
+timing parameters, and the required code would quickly increase to a
+not-so-easy-to-manage size. Better keep it simple, IMHO.
+
+It seems Mark Lord has devised a neat way to do this in the ide-dma driver
+included in late kernels 2.1.x: each chipset has an entry in a table, with
+safe timing values. The chipset is also identified when the driver is
+loaded.
+
+
+How does one use UDMA support?
+------------------------------
+
+1) Backup your data or you will be sorry. Now do "hdparm -t -T
+/dev/hda". Write a small note with the transfer rates you see.
+
+2) Reboot. Press [Del] to launch the CMOS SETUP routine, go to the
+CHIPSET SPECIFIC or PERIPHERALS SETUP menus, and enable UDMA transfers
+for your hard disk drives which are UDMA capable, or leave the fields in
+the default "AUTO" value. Enable both IDE channels even if you have just
+one IDE drive (default setting).
+
+3) Boot Linux, compile the kernel with the TRITON support enabled. Install
+the new kernel (the lilo thingy). Reboot Linux.
+
+4) Watch for the drive parameters message when the kernel loads (or type
+"dmesg | more" after login). After the Cyl, Heads, Sect parameters you
+should see "DMA" or "UDMA" depending on your hard disk drive and chipset
+capabilities.
+
+Here is what I get with UDMA enabled in the BIOS of my SiS 5598 based
+configuration, with an IBM UDMA capable hard disk as hda:
+
+...
+ide: DMA Bus Mastering IDE controller on PCI bus 0 function 9
+    ide0: BM-DMA at 0x4000-0x4007
+    ide1: BM-DMA at 0x4008-0x400f
+hda: IBM-DHEA-36480, 6197MB w/476kB Cache, LBA, CHS=790/255/63, UDMA
+...
+
+If I disable UDMA in the BIOS, I get:
+
+...
+ide: DMA Bus Mastering IDE controller on PCI bus 0 function 9
+    ide0: BM-DMA at 0x4000-0x4007
+    ide1: BM-DMA at 0x4008-0x400f
+hda: IBM-DHEA-36480, 6197MB w/476kB Cache, LBA, CHS=790/255/63, DMA
+...
+
+5) Again, do "hdparm -t -T /dev/hda". Smile. Test your setup by copying
+a few large files around or doing some large compilation (e.g. the Linux
+kernel itself).
+
+
+Performance issues
+------------------
+
+1) Sustained transfer rates.
+
+Here is some data gathered after extensive testing, using the hdparm utility
+(also written by Mark Lord):
+
+PIO mode 4 transfer rates under Linux:   +/- 5.2MB/s
+
+DMA mode 2 transfer rates under Linux:   +/- 7.2MB/s
+
+UDMA mode 2 transfer rates under Linux:  +/- 9.8MB/s
+
+Data gathered on a Chaintech SiS 5598 motherboard, 6x86MX @ 187.5MHz, Linux
+2.0.32/2.0.33 with patched triton.c driver as explained above, IBM DeskStar
+6.4GB hard disk (IBM-DHEA-36480).
+
+The integrated video hardware in the SiS 5598 chip was disabled (a standard
+PCI video board was used); enabling the integrated SVGA controller will
+cause a 20% performance hit in processing performance, due to limited main
+memory bandwidth.
+
+The TX motherboard under the same test conditions will be slightly
+slower (0.1 - 0.2 MB/s slower).
+
+Burst (instantaneous) transfer rates are supposed to go from 16.6MB/s (PIO
+mode 4) to 16.6MB/s (DMA mode 2) up to 33MB/s (UDMA). In his patch against
+kernel 2.1.55, Kim-Hoe Pang actually checked the UDMA burst transfer rate
+with a logic analiser: 60ns/word, which translates into 33MB/s.
+
+Note that burst transfer rates only affect data transfers to/from the EIDE
+drive cache (476kB for the IBM 6.4GB drive), and IMHO are not particularly
+relevant for most Linux users.
+
+The Linux kernel uses as much RAM as possible to cache hard disk data
+accesses, and so if data is not in the kernel cache there is little chance
+that it will be in the (much smaller) hard disk cache.
+
+2) Processor utilization
+
+Unfortunately, it is very difficult to gather data about processor
+utilization during data transfers, but this is exactly the biggest advantage
+of DMA transfers over PIO transfers. My estimate is that CPU utilization
+during UDMA transfers will be as low as 3-4%, while being somewhere around
+30% for PIO transfers and 6-8% for DMA mode 2.
+
+3) UDMA vs SCSI
+
+The main advantage of DMA mode 2 and UDMA over SCSI is that the controller
+is already on your motherboard, so why not use it?
+
+Mark Lord's triton.c driver has a very small latency and so UDMA drives
+may beat their Ultra-Wide SCSI-2 counterparts in some cases (at equal
+spindle speeds) e.g. lots of small files (loaded news servers) being
+read/written at irregular intervals.
+
+Note however that SCSI drives are available at spindle speeds of 7,200,
+10,000 and even a recently announced 12,030 rpm. IBM is planning some 7,200
+rpm UDMA EIDE drives, but those are not yet available. Seagate has just
+released its EIDE 7,200 rpm drives, but they have special cooling
+requirements just like their SCSI counterparts. Expect this technology to
+become commonplace by the end of 98, though.
+
+The UDMA burst data transfer rates exceed maximum head transfer rates
+(maximum head transfer rates in the industry have reached 160 Mbits/s in
+1998) and so for large files neither Ultra-Wide SCSI-2 nor UDMA will have an
+advantage over the other technology.
+
+It used to be that high-capacity drives were only available with SCSI
+interfaces, but this isn't true anymore. Right now top capacity for an EIDE
+drive is Maxtor's 11.3Gb monster, which is quite affordable in fact. One can
+drive four of these with a standard motherboard: 45Gb for < $2k.
+
+SCSI drives can chain, overlap and re-order commands, EIDE drives cannot.
+However, Linux already has an intelligent "elevator" algorithm for hard disk
+accesses.
+
+At present, EIDE top speed is 33MB/s burst. Ultra-Wide II SCSI is 80MB/s
+burst. The cost of an Ultra-Wide II SCSI controller + 9Gb hard disk is > 4 x
+the cost of an 8GB UDMA drive. IMHO the price/performance ratio of UDMA
+beats SCSI.
+
+A new standard is emerging called ATA-66, which will double the burst
+transfer speed of EIDE drives to 66Mb/s. I don't have any technical info
+about it, unfortunately. The first ATA-66 drives will be shipped by Quantum
+in 1999, but VIA has already announced two ATA-66 capable chipsets (in fact
+based on the same Southbridge chip); as I write this, data sheets are not
+available to the general public. Probably Intel will come out with a chipset
+of its own with ATA-66 capabilities.
+
+4) What is the best UDMA chipset/hard disk?
+
+Intel designed the first DMA mode 2 capable chipset, the FX (Triton I) a few
+years ago. The Linux DMA mode 2 driver was initially written by Mark Lord
+for the original Intel FX chipset and appeared around kernel 1.3.20 if I
+remember well. The later HX and VX chipsets had exactly the same DMA mode 2
+capabilities and the triton.c driver was for a long time Intel-only. Mark
+planned to support the Opti Viper chipset but Opti went out of the
+motherboard chipset business so fast that Mark didn't even have the time to
+get his hands on an Opti motherboard, I guess.
+
+Intel later introduced a UDMA compatible motherboard chipset with its TX
+chipset. Kernel 2.0.31 was the first Linux kernel to support the TX chipset,
+however only DMA mode 2 (16.6MB/s) was supported.
+
+The TX chipset has a proven record of reliability. But DMA mode 2 and UDMA
+transfers on the TX suffer from a flaw common to previous Intel DMA mode 2
+only chipsets: a single data buffer is shared between the two IDE channels.
+This buffer (64 bytes deep) is used to hold data on its way from the PCI bus
+to/from the hard disk's small cache. A hardware arbitration mechanism
+prevents data loss when the OS tries to simultaneously use both IDE
+channels.
+
+VIA chips also have a single FIFO, with the same 64 bytes deep buffer.
+However, VIA chips can have the buffer split 1:1 or 3:1 between both IDE
+channels; an interesting feature, but difficult to use.
+
+How is this FIFO buffer used? Remember that the PCI bus can transfer data at
+a maximum rate of 132MB/s when clocked at 33MHz, 150MB/s when clocked at
+37.5MHz (maximum safe clock speed for PCI is 33MHz, after that well..). So the
+PCI bus mastering IDE controller will be transfering data from main memory
+DRAM to this FIFO buffer in small bursts of < 64 bytes, then from the buffer
+to the IDE disk drive cache (when writing; the other way around for reads).
+
+I recently managed to get hold of the SiS 5598 data sheet and studied the
+IDE controller part of this highly integrated chip, a device identified by
+the number 5513. The 5598 even includes an SVGA controller, which should be
+disabled if one wants to get decent performance from this chipset: it
+severely limits CPU/memory bandwidth. The SiS5597 is the same part with
+a different pinout.
+
+It appears the 5513 has two completely independent IDE channels, each with
+its own 64 bytes deep data buffer. On disk-to-disk or CD-to-disk transfers,
+the 5598 and 5591 chipsets will easily beat the Intel TX and VIA. On
+simultaneous (U)DMA transfers to two disks (for example, when the Linux md
+driver is used to create a RAID-0 array with data striping), the 5513 device
+will be faster than the TX Southbridge device since there will be no
+contention for the data buffer, assuming each drive is connected to a
+different IDE channel.  Other PCI bus related features will also improve its
+performance of the SiS devices. So, compared to the Intel TX and various VIA
+chipsets, the 5598 and 5591 win hands down in terms of UDMA implementation.
+
+Unfortunately, it is very difficult to get data sheets for the ALi Aladdin
+IV+ and Aladdin V chipsets. These newer chipsets support up to 1 MB of L2
+SRAM cache, the AGP bus (2X), 100 MHz CPU bus and of course, UDMA data
+transfers. The newest VIA chipset for Socket 7 motherboards beats them all
+in terms of features, as it sports ATA-66 compatibility.
+
+On the UDMA hard drive front, the present performance leaders are the IBM
+Deskstar drives. These drives have relatively large data caches (476kB
+available), a 5,400 rpm rotational speed and < 10ms random access times.
+They run very cool and although they can't be called silent, their noise
+level is acceptable. They are also reliable.
+
+Seagate has just begun shipping 7,200 rpm EIDE drives which will obviously
+benefit from the lower data latency. They are reported as particularly
+silent due to the use of Fluid Dynamic Bearing motors, but running quite
+hot. IMHO if one has to add a fan to cool them, this defeats any advantage
+these drives will have in terms of noise level. Another advantage of this
+technology is the lower vibration levels compared to ball bearings.
+
+IBM has pre-announced very large capacity (14GB), 7,200 rpm EIDE UDMA drives
+a month ago, but those are not shipping yet. They are based on a new head
+technology called Giant Magneto-Resistive Heads, which is supposed to
+increase the data density on the disks by a factor of 4 or more. More details
+when I get my hands on one. IBM licensed Western Digital to use this
+technology.
+
+Quantum has always shipped among the best and fastest EIDE drives, and they
+worked with Intel to create the UDMA standard. They used to have the fastest
+drives for Linux DMA mode 2 transfers (see the comments in
+/Documentation/ide.txt).
+
+Well, I just got an email from Denny de Jonge <denny@luna.nl> that proves
+Quantum drives will keep their reputation:
+
+"Andre,
+
+ After I applied the UDMA-patch for Linux 2.0.33 hdparm showed up with the
+ following benchmarks:
+
+ /dev/hda:
+
+ Timing buffer-cache reads:   64 MB in  1.02 seconds =62.75 MB/sec
+ Timing buffered disk reads:  32 MB in  3.02 seconds =10.60 MB/sec
+
+ Not bad, don't you think ?
+
+ These results have been obtained using the Intel 82371 Chipset and a
+ Quantum Fireball 4.3SE harddisk."
+
+I later asked what kind of processor Denny was using: it's a 266MHz PII.
+
+BTW I have been collecting hard disk/file subsystem benchmarking information
+based on bonnie, a popular benchmark available for Linux. I have come to the
+conclusion that bonnie is not a reliable benchmark when it comes to
+comparing different systems, basically because it depends so much on how
+much RAM one has installed and how much of it is free, as well as system
+load, CPU speed, etc. For this reason I will not quote bonnie results
+anymore. For comparative benchmarking between two hard disk drives on
+exactly the same hardware it may be acceptable, but otherwise it's too
+unreliable as an indicator of performance.
+
+
+Unreliable drive + motherboard + driver combination
+---------------------------------------------------
+
+Quoting Kim-Hoe Pang:
+
+"The UDMA mode of an UDMA drive would NOT be enabled on a non-UDMA capable
+chipset mobo. On power-up or after a hardware reset, the drive is in normal
+PIO/DMA mode. To enable the UDMA mode in the drive, the host, BIOS or OS,
+needs to send a SET FEATURE ("enable UDMA mode subcommand") AT command to
+the drive. A non-UDMA capable mobo will not send this command to the drive.
+
+UDMA mode is dis/enabled via BIOS setup. The patch does not attempt to
+override user's BIOS setting."
+
+There may be some combinations of drives, motherboards (BIOS) and Linux
+driver which may prove unreliable. Remember we are transfering data at
+33MB/s over unshielded ribbon cable in a very noisy (electromagnetically
+speaking) environment.
+
+In the future it would be nice if hard disk manufacturers would publish the
+timings required by their drives, and chipset manufacturers would follow a
+single standard for registers and controller architecture. Right now UDMA is
+extremely timing sensitive.
+
+A few recommendations for troubleshooting:
+
+1) Make sure you have the latest BIOS for your motherboard. Connect to the
+motherboard manufacturer's Web site and download the latest BIOS image file
+and EEPROM flashing utilities. Check your BIOS version, and only flash your
+EEPROM if needed.
+
+2) Keep the IDE cable going from the motherboard to the drive short, and do
+not loop it around another cable. I recommend < 30 cm (12") total cable
+length.
+
+3) If you have just a single UDMA hard disk drive per channel (which I
+recommend), use the connectors at both ends of the cable to connect
+motherboard and drive, do _not_ use the middle connector. If you have a UDMA
+hard disk drive and a CD-ROM drive on the same cable, plug the hard disk
+drive at the end of the cable (use the middle connector for the CD-ROM
+drive). Also the hard disk must be the master EIDE device, the CD-ROM drive
+the slave EIDE device, never the other way around (this is not determined by
+cable position, but by small jumpers on the drive and at the back of the
+CD-ROM). The same rules apply to CD-RW, ZIP and tape EIDE drives.
+
+4) If you have (shudder) Windows 95 installed in your system, and have been
+able to use UDMA, you should be able to use UDMA with Linux.
+
+5) DON'T OVERCLOCK the PCI bus. 33MHz is the maximum supported speed for
+the PCI bus. Some (supposedly compatible) UDMA drives will not even take
+37.5MHz, but should be OK at 33.3MHz.
+
+In any case, NEVER, NEVER set the PCI bus to 41.5MHz.
+
+The RECOMMENDED safe setting is 33MHz.
+
+Adequate testing is needed in each case. The golden rule here, as always:
+backup, backup, backup.
+
+
+Aknowledgments
+--------------
+
+Mark Lord for his excellent, reliable and very readable triton.c driver code
+and all his (E)IDE Linux programming.
+
+Kim-Hoe Pang for the first UDMA patch against kernel 2.1.55.
+
+Christian Brunner for his patch converting triton.c into a generic DMA mode
+2 EIDE driver. 
+
+Brion Vibber for his neat Linux UDMA mini-HOWTO, for his help and
+contributions to this package, and for bearing with my various documentation
+changes and suggestions.
+
+Michel Aubry for his complete VIA support and neat diagnostics code, as well
+as the patch to hdparm to support UDMA.
+
+Andre Hedrick for his great code for the various PCI UDMA controller cards.
+
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/MAINTAINERS linux/MAINTAINERS
--- linux.vanilla/MAINTAINERS	Sun Dec  6 00:14:35 1998
+++ linux/MAINTAINERS	Tue Mar  2 00:39:21 1999
@@ -165,11 +165,23 @@
 S:	Maintained
 
 CYCLADES ASYNC MUX DRIVER
-P:	Marcio Saito
-M:	Marcio Saito <marcio@cyclades.com>
+P:	Ivan Passos
+M:	Ivan Passos <ivan@cyclades.com>
 W:	http://www.cyclades.com/
 S:	Supported
 
+DC390/AM53C974 SCSI driver
+P:	Kurt Garloff
+M:	kurt@garloff.de
+W:	http://www.garloff.de/kurt/linux/dc390/
+S:	Maintained
+
+DAC960 RAID DRIVER
+P:	Leonard N. Zubkoff
+M:	Leonard N. Zubkoff <lnz@dandelion.com>
+L:	linux-raid@vger.rutgers.edu
+S:	Maintained
+
 EATA ISA/EISA/PCI SCSI DRIVER
 P:	Dario Ballabio
 M:	dario@milano.europe.dg.com
@@ -279,9 +291,9 @@
 
 NCP FILESYSTEM:
 P:	Volker Lendecke
-M:	lendecke@namu01.Num.Math.Uni-Goettingen.de
+M:	vl@kki.org
 L:	linware@sh.cvut.cz
-S:	Maintained
+S:	Odd Fixes
 
 NETROM NETWORK LAYER
 P:	Jon Naylor
@@ -315,7 +327,7 @@
 
 SMB FILESYSTEM:
 P:	Volker Lendecke
-M:	lendecke@namu01.Num.Math.Uni-Goettingen.de
+M:	vl@kki.org
 L:	samba@listproc.anu.edu.au
 S:	Odd Fixes
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/Makefile linux/Makefile
--- linux.vanilla/Makefile	Sun Dec  6 00:14:35 1998
+++ linux/Makefile	Mon Mar 22 00:06:36 1999
@@ -1,8 +1,8 @@
 VERSION = 2
 PATCHLEVEL = 0
-SUBLEVEL = 36
+SUBLEVEL = 37
 
-ARCH = i386
+ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
 
 #
 # For SMP kernels, set this. We don't want to have this in the config file
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/README linux/README
--- linux.vanilla/README	Sun Jun 21 18:41:03 1998
+++ linux/README	Fri Mar 26 16:57:24 1999
@@ -76,15 +76,6 @@
    the current directory, but an alternative directory can be specified
    as the second argument.
 
- - make sure your /usr/include/asm, /usr/include/linux, and /usr/include/scsi
-   directories are just symlinks to the kernel sources:
-
-		cd /usr/include
-		rm -rf asm linux scsi
-		ln -s /usr/src/linux/include/asm-i386 asm
-		ln -s /usr/src/linux/include/linux linux
-		ln -s /usr/src/linux/include/scsi scsi
-
  - make sure you have no stale .o files and dependencies lying around:
 
 		cd /usr/src/linux
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/alpha/Makefile linux/arch/alpha/Makefile
--- linux.vanilla/arch/alpha/Makefile	Sun Jun 21 18:41:25 1998
+++ linux/arch/alpha/Makefile	Mon Mar 22 00:06:48 1999
@@ -27,9 +27,9 @@
 CFLAGS := $(CFLAGS) -mno-fp-regs
 
 # determine if we can use the BWX instructions with GAS
-$(shell rm -f /tmp/GAS_VER)
+dummy=$(shell rm -f /tmp/GAS_VER)
 #$(shell $(AS) --version >& /tmp/GAS_VER)
-$(shell $(AS) --version > /tmp/GAS_VER 2>&1)
+dummy=$(shell $(AS) --version > /tmp/GAS_VER 2>&1)
 OLD_GAS := $(shell if cat /tmp/GAS_VER | grep 'version 2.7' > /dev/null; then echo yes; else echo no; fi)
 
 ifneq ($(OLD_GAS),yes)
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/i386/boot/setup.S linux/arch/i386/boot/setup.S
--- linux.vanilla/arch/i386/boot/setup.S	Sun Dec  6 00:14:35 1998
+++ linux/arch/i386/boot/setup.S	Sun Dec  6 03:30:21 1998
@@ -482,6 +482,29 @@
 	out	#0x60,al
 	call	empty_8042
 
+! wait until a20 really *is* enabled; it can take a fair amount of
+! time on certain systems; Toshiba Tecras are known to have this
+! problem.  The memory location used here is the int 0x1f vector,
+! which should be safe to use; any *unused* memory location < 0xfff0
+! should work here.  
+
+#define	TEST_ADDR 0x7c
+
+	push	ds
+	xor	ax,ax			! segment 0x0000
+	mov	ds,ax
+	dec	ax			! segment 0xffff (HMA)
+	mov	gs,ax
+	mov	bx,[TEST_ADDR]		! we want to restore the value later
+a20_wait:
+	inc	ax
+	mov	[TEST_ADDR],ax
+	seg	gs
+	cmp	ax,[TEST_ADDR+0x10]
+	je	a20_wait		! loop until no longer aliased
+	mov	[TEST_ADDR],bx		! restore original value
+	pop	ds
+
 ! make sure any possible coprocessor is properly reset..
 
 	xor	ax,ax
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/i386/config.in linux/arch/i386/config.in
--- linux.vanilla/arch/i386/config.in	Sun Dec  6 00:14:35 1998
+++ linux/arch/i386/config.in	Sun Jan 31 23:35:34 1999
@@ -22,6 +22,20 @@
 comment 'General setup'
 
 bool 'Kernel math emulation' CONFIG_MATH_EMULATION
+choice 'Memory configuration' \
+	"Standard	CONFIG_MEM_STD	\
+	 Enterprise	CONFIG_MEM_ENT	\
+	 Custom		CONFIG_MEM_SPECIAL" Standard
+
+if [ "$CONFIG_MEM_SPECIAL" = "y" ]; then
+	int ' Max physical memory in MB' CONFIG_MAX_MEMSIZE 1024
+fi
+if [ "$CONFIG_MEM_ENT" = "y" ]; then
+	define_int CONFIG_MAX_MEMSIZE 2048
+fi
+if [ "$CONFIG_MEM_STD" = "y" ]; then
+	define_int CONFIG_MAX_MEMSIZE 1024
+fi
 bool 'Networking support' CONFIG_NET
 bool 'Limit memory to low 16MB' CONFIG_MAX_16M
 bool 'PCI bios support' CONFIG_PCI
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/i386/defconfig linux/arch/i386/defconfig
--- linux.vanilla/arch/i386/defconfig	Fri Jul 24 17:28:56 1998
+++ linux/arch/i386/defconfig	Fri Dec 11 20:35:53 1998
@@ -55,7 +55,12 @@
 # Additional Block Devices
 #
 # CONFIG_BLK_DEV_LOOP is not set
-# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_MD=y
+CONFIG_AUTODETECT_RAID=y
+CONFIG_MD_LINEAR=y
+CONFIG_MD_STRIPED=y
+CONFIG_MD_MIRRORING=y
+CONFIG_MD_RAID5=y
 # CONFIG_BLK_DEV_RAM is not set
 # CONFIG_BLK_DEV_XD is not set
 # CONFIG_PARIDE is not set
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/i386/kernel/bios32.c linux/arch/i386/kernel/bios32.c
--- linux.vanilla/arch/i386/kernel/bios32.c	Sun Jun 21 18:41:24 1998
+++ linux/arch/i386/kernel/bios32.c	Mon Mar 22 00:08:21 1999
@@ -730,7 +730,7 @@
     outb (0x00, 0xCFB);
     outb (0x00, 0xCF8);
     outb (0x00, 0xCFA);
-    if (inb (0xCF8) == 0x00 && inb (0xCFB) == 0x00) {
+    if (inb (0xCF8) == 0x00 && inb (0xCFA) == 0x00) {
 	restore_flags(flags);
 	printk("pcibios_init: Using configuration type 2\n");
 	return &pci_direct_conf2;
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S
--- linux.vanilla/arch/i386/kernel/head.S	Sun Dec  6 00:14:35 1998
+++ linux/arch/i386/kernel/head.S	Mon Dec 14 18:07:26 1998
@@ -9,10 +9,14 @@
  */
 
 .text
+#include <linux/config.h>
 #include <linux/tasks.h>
 #include <linux/linkage.h>
 #include <asm/segment.h>
 #include <linux/config.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
 
 #define CL_MAGIC_ADDR	0x90020
 #define CL_MAGIC	0xA33F
@@ -329,7 +333,7 @@
  *  sets up a idt with 256 entries pointing to
  *  ignore_int, interrupt gates. It doesn't actually load
  *  idt - that can be done only after paging has been enabled
- *  and the kernel moved to 0xC0000000. Interrupts
+ *  and the kernel moved to PAGE_OFFSET. Interrupts
  *  are enabled elsewhere, when we can be relatively
  *  sure everything is ok.
  */
@@ -370,9 +374,9 @@
 /* Identity-map the kernel in low 4MB memory for ease of transition */
 /* set present bit/user r/w */
 	movl $ SYMBOL_NAME(pg0)+7,SYMBOL_NAME(swapper_pg_dir)
-/* But the real place is at 0xC0000000 */
+/* But the real place is at PAGE_OFFSET */
 /* set present bit/user r/w */
-	movl $ SYMBOL_NAME(pg0)+7,SYMBOL_NAME(swapper_pg_dir)+3072
+	movl $ SYMBOL_NAME(pg0)+7,SYMBOL_NAME(swapper_pg_dir)+__USER_PGD_PTRS*4
 	movl $ SYMBOL_NAME(pg0)+4092,%edi
 	movl $0x03ff007,%eax		/*  4Mb - 4096 + 7 (r/w user,p) */
 	std
@@ -460,7 +464,7 @@
 .word 0
 idt_descr:
 	.word 256*8-1		# idt contains 256 entries
-	.long 0xc0000000+SYMBOL_NAME(idt)
+	.long __PAGE_OFFSET+SYMBOL_NAME(idt)
 
 	ALIGN
 .word 0
@@ -470,19 +474,45 @@
 #else
 	.word (8+2*NR_TASKS)*8-1
 #endif
-	.long 0xc0000000+SYMBOL_NAME(gdt)
+	.long __PAGE_OFFSET+SYMBOL_NAME(gdt)
 
 /*
  * This gdt setup gives the kernel a 1GB address space at virtual
- * address 0xC0000000 - space enough for expansion, I hope.
+ * address PAGE_OFFSET - space enough for expansion, I hope.
  */
+
+#define upper_seg(type,dpl,base,limit) \
+        ((base) & 0xff000000) | \
+        (((base) & 0x00ff0000)>>16) | \
+        (((limit)>>12) & 0xf0000) | \
+        ((dpl)<<13) | \
+        (0x00c09000) | \
+        ((type)<<8)
+
+#define lower_seg(type,dpl,base,limit) \
+        (((base) & 0x0000ffff)<<16) | \
+        ((limit) & 0x0ffff)
+
+#define x86_seg(type,dpl,base,limit) \
+	.long lower_seg(type,dpl,base,limit); \
+	.long upper_seg(type,dpl,base,limit)
+
 ENTRY(gdt)
 	.quad 0x0000000000000000	/* NULL descriptor */
 	.quad 0x0000000000000000	/* not used */
-	.quad 0xc0c39a000000ffff	/* 0x10 kernel 1GB code at 0xC0000000 */
-	.quad 0xc0c392000000ffff	/* 0x18 kernel 1GB data at 0xC0000000 */
-	.quad 0x00cbfa000000ffff	/* 0x23 user   3GB code at 0x00000000 */
-	.quad 0x00cbf2000000ffff	/* 0x2b user   3GB data at 0x00000000 */
+
+	/* 0x10 kernel 1GB code at 0xC0000000: */
+	x86_seg(0xa,0,__PAGE_OFFSET,0xffffffff-__PAGE_OFFSET)
+
+	/* 0x18 kernel 1GB data at 0xC0000000: */
+	x86_seg(0x2,0,__PAGE_OFFSET,0xffffffff-__PAGE_OFFSET)
+
+	/* 0x23 user 3GB code at 0x00000000: */
+        x86_seg(0xa,3,0,__PAGE_OFFSET-1)
+
+	/* 0x2b user 3GB data at 0x00000000: */
+        x86_seg(0x2,3,0,__PAGE_OFFSET-1)
+
 	.quad 0x0000000000000000	/* not used */
 	.quad 0x0000000000000000	/* not used */
 	.fill 2*NR_TASKS,8,0		/* space for LDT's and TSS's etc */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/i386/kernel/hexify.c linux/arch/i386/kernel/hexify.c
--- linux.vanilla/arch/i386/kernel/hexify.c	Sun Jun 21 18:41:24 1998
+++ linux/arch/i386/kernel/hexify.c	Mon Dec 14 18:32:08 1998
@@ -1,7 +1,7 @@
 #include <stdio.h>
 
 
-void main()
+int main()
 {
 	int c;
 	int comma=0;
@@ -25,7 +25,5 @@
 	}
 	if(count)
 		printf("\n");
-	exit(0);
+	return 0;
 }
-
-		
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- linux.vanilla/arch/i386/kernel/process.c	Sun Jun 21 18:41:24 1998
+++ linux/arch/i386/kernel/process.c	Fri Dec 11 20:35:53 1998
@@ -314,10 +314,10 @@
 
 	/* Remap the kernel at virtual address zero, as well as offset zero
 	   from the kernel segment.  This assumes the kernel segment starts at
-	   virtual address 0xc0000000. */
+	   virtual address PAGE_OFFSET. */
 
-	memcpy (swapper_pg_dir, swapper_pg_dir + 768,
-		sizeof (swapper_pg_dir [0]) * 256);
+	memcpy (swapper_pg_dir, swapper_pg_dir + USER_PGD_PTRS,
+		sizeof (swapper_pg_dir [0]) * KERNEL_PGD_PTRS);
 
 	/* Make sure the first page is mapped to the start of physical memory.
 	   It is normally not mapped, to trap kernel NULL pointer dereferences. */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- linux.vanilla/arch/i386/kernel/setup.c	Sun Dec  6 00:14:36 1998
+++ linux/arch/i386/kernel/setup.c	Tue Mar  2 14:30:36 1999
@@ -167,13 +167,14 @@
 #endif
 
 	/*
-	 *	The 1Gig sanity checker.
+	 *	The CONFIG_MAX_MEMSIZE sanity checker.
 	 */
 	 
-	if (memory_end > 980*1024*1024)
+	if (memory_end > (CONFIG_MAX_MEMSIZE-128)*1024*1024)
 	{
-		printk(KERN_WARNING "Warning only 980Mb will be used.\n");
-		memory_end = 980 * 1024 * 1024;
+		memory_end = (CONFIG_MAX_MEMSIZE-128)*1024*1024;
+		printk(KERN_WARNING "ONLY %dMB RAM will be used, see Documentation/more-than-900MB-RAM.txt!.\n", CONFIG_MAX_MEMSIZE-128);
+		udelay(3*1000*1000);
 	}
 	
 	if (!MOUNT_ROOT_RDONLY)
@@ -462,7 +463,7 @@
 	static const char *x86_ext_cap_flags[] = {
 		   "fpu","vme", "de",   "pse", "tsc", "msr",  "6",   "mce",
 		   "cx8",  "9", "10", "syscr",  "12", "pge", "14",  "cmov",
-		"fpcmov", "17", "18",    "19",  "20",  "21", "22",   "mmx",
+		"fpcmov", "17", "psn",    "19",  "20",  "21", "22",   "mmx",
 		  "emmx", "25", "26",    "27",  "28",  "29", "30", "3dnow"
 	};
          
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/i386/mm/init.c linux/arch/i386/mm/init.c
--- linux.vanilla/arch/i386/mm/init.c	Sun Dec  6 00:14:36 1998
+++ linux/arch/i386/mm/init.c	Thu Mar  4 12:21:17 1999
@@ -139,18 +139,22 @@
 		 *	the error...
 		 */
 		if (!smp_scan_config(639*0x400,0x400))	/* Scan the top 1K of base RAM */
-			smp_scan_config(0xF0000,0x10000);	/* Scan the 64K of bios */
-		/*
-		 * If it is an SMP machine we should know now, unless the
-		 * configuration is in an EISA/MCA bus machine with an
-		 * extended bios data area. 
-		 *
-		 * there is a real-mode segmented pointer pointing to the
-		 * 4K EBDA area at 0x40E, calculate and scan it here:
-		 */
-		address = *(unsigned short *)phys_to_virt(0x40E);
-		address<<=4;
-		smp_scan_config(address, 0x1000);
+		{
+			if(!smp_scan_config(0xF0000,0x10000)) /* Scan the 64K of bios */
+			{
+				/*
+				 * If it is an SMP machine we should know now, unless the
+				 * configuration is in an EISA/MCA bus machine with an
+				 * extended bios data area. 
+				 *
+				 * there is a real-mode segmented pointer pointing to the
+				 * 4K EBDA area at 0x40E, calculate and scan it here:
+				 */
+				address = *(unsigned short *)phys_to_virt(0x40E);
+				address<<=4;
+				smp_scan_config(address, 0x1000);
+			}
+		}
 	}
 	/*
 	 *	If it is an SMP machine we should know now, unless the configuration
@@ -188,14 +192,14 @@
 #endif
 			wp_works_ok = 1;
 			pgd_val(pg_dir[0]) = _PAGE_TABLE | _PAGE_4M | address;
-			pgd_val(pg_dir[768]) = _PAGE_TABLE | _PAGE_4M | address;
+			pgd_val(pg_dir[USER_PGD_PTRS]) = _PAGE_TABLE | _PAGE_4M | address;
 			pg_dir++;
 			address += 4*1024*1024;
 			continue;
 		}
 #endif
-		/* map the memory at virtual addr 0xC0000000 */
-		pg_table = (pte_t *) (PAGE_MASK & pgd_val(pg_dir[768]));
+		/* map the memory at virtual addr PAGE_OFFSET */
+		pg_table = (pte_t *) (PAGE_MASK & pgd_val(pg_dir[USER_PGD_PTRS]));
 		if (!pg_table) {
 			pg_table = (pte_t *) start_mem;
 			start_mem += PAGE_SIZE;
@@ -203,7 +207,7 @@
 
 		/* also map it temporarily at 0x0000000 for init */
 		pgd_val(pg_dir[0])   = _PAGE_TABLE | (unsigned long) pg_table;
-		pgd_val(pg_dir[768]) = _PAGE_TABLE | (unsigned long) pg_table;
+		pgd_val(pg_dir[USER_PGD_PTRS]) = _PAGE_TABLE | (unsigned long) pg_table;
 		pg_dir++;
 		for (tmp = 0 ; tmp < PTRS_PER_PTE ; tmp++,pg_table++) {
 			if (address < end_mem)
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/Config.in linux/drivers/block/Config.in
--- linux.vanilla/drivers/block/Config.in	Fri Jul 24 17:28:56 1998
+++ linux/drivers/block/Config.in	Tue Mar  2 00:39:42 1999
@@ -11,6 +11,9 @@
   bool 'Old harddisk (MFM/RLL/IDE) driver' CONFIG_BLK_DEV_HD_ONLY
 else
   bool '   Use old disk-only driver on primary interface' CONFIG_BLK_DEV_HD_IDE
+  if [ "$CONFIG_BLK_DEV_HD_IDE" != "n" ]; then
+    bool '     Use multi-mode by default' CONFIG_IDEDISK_MULTI_MODE
+  fi
   bool '   Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD
   bool '   Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE
   bool '   Include IDE/ATAPI FLOPPY support (new)' CONFIG_BLK_DEV_IDEFLOPPY
@@ -23,6 +26,9 @@
   if [ "$CONFIG_PCI" = "y" ]; then
     bool '   RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000
     bool '   Intel 82371 PIIX (Triton I/II) DMA support' CONFIG_BLK_DEV_TRITON
+    if [ "$CONFIG_BLK_DEV_TRITON" = "y" ]; then
+      bool '     Boot off-board chipsets first support' CONFIG_BLK_DEV_OFFBOARD
+    fi
   fi
   bool '   Other IDE chipset support' CONFIG_IDE_CHIPSETS
   if [ "$CONFIG_IDE_CHIPSETS" = "y" ]; then
@@ -53,6 +59,15 @@
   bool '   Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD
 fi
 tristate 'XT harddisk support' CONFIG_BLK_DEV_XD
+if [ "$CONFIG_PCI" = "y" ]; then
+  bool 'Mylex DAC960 PCI RAID Controller support' CONFIG_BLK_DEV_DAC960
+fi
+
+tristate 'Compaq SMART2 support' CONFIG_BLK_CPQ_DA
+if [ "$CONFIG_BLK_CPQ_DA" = "y" -o "$CONFIG_BLK_CPQ_DA" = "m" ]; then
+  bool '   Support for PCI SMART-2 Controllers' CONFIG_BLK_CPQ_DA_PCI
+  bool '   Support for EISA SMART-2 Controllers' CONFIG_BLK_CPQ_DA_EISA
+fi
 
 tristate 'Parallel port IDE device support' CONFIG_PARIDE 
 if [ "$CONFIG_PARIDE" = "y" -o "$CONFIG_PARIDE" = "m" ]; then
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/DAC960.c linux/drivers/block/DAC960.c
--- linux.vanilla/drivers/block/DAC960.c	Thu Jan  1 01:00:00 1970
+++ linux/drivers/block/DAC960.c	Fri Jan 29 14:27:56 1999
@@ -0,0 +1,1980 @@
+/*
+
+  Linux Driver for Mylex DAC960 PCI RAID Controllers
+
+  Copyright 1998 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+  This program is free software; you may redistribute and/or modify it under
+  the terms of the GNU General Public License Version 2 as published by the
+  Free Software Foundation.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
+  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+  for complete details.
+
+  The author respectfully requests that any modifications to this software be
+  sent directly to him for evaluation and testing.
+
+*/
+
+
+#define DAC960_DriverVersion		"2.0.0 Beta3"
+#define DAC960_DriverDate		"29 November 1998"
+
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/hdreg.h>
+#include <linux/ioport.h>
+#include <linux/locks.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/timer.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/segment.h>
+#include "DAC960.h"
+
+
+/*
+  DAC960_ControllerCount is the number of DAC960 Controllers.
+*/
+
+static int
+  DAC960_ControllerCount =			0;
+
+
+/*
+  DAC960_Controllers is an array of pointers to the DAC960 Controller
+  structures.
+*/
+
+static DAC960_Controller_T
+  *DAC960_Controllers[DAC960_MaxControllers] =	{ NULL };
+
+
+/*
+  DAC960_FileOperations is the File Operations structure for DAC960 Logical
+  Disk Devices.
+*/
+
+static FileOperations_T
+  DAC960_FileOperations =
+    { lseek:		    NULL,
+      read:		    block_read,
+      write:		    block_write,
+      readdir:		    NULL,
+      select:		    NULL,
+      ioctl:		    DAC960_Ioctl,
+      mmap:		    NULL,
+      open:		    DAC960_Open,
+      release:		    DAC960_Release,
+      fsync:		    block_fsync,
+      fasync:		    NULL,
+      check_media_change:   NULL,
+      revalidate:	    NULL };
+
+
+/*
+  DAC960_AnnounceDriver announces the Driver Version and Date, Author's Name,
+  Copyright Notice, and Electronic Mail Address.
+*/
+
+static void DAC960_AnnounceDriver(DAC960_Controller_T *Controller)
+{
+  DAC960_Announce("***** DAC960 RAID Driver Version "
+		  DAC960_DriverVersion " of "
+		  DAC960_DriverDate " *****\n", Controller);
+  DAC960_Announce("Copyright 1998 by Leonard N. Zubkoff "
+		  "<lnz@dandelion.com>\n", Controller);
+}
+
+
+/*
+  DAC960_Failure prints a standardized error message, and then returns false.
+*/
+
+static boolean DAC960_Failure(DAC960_Controller_T *Controller,
+			      char *ErrorMessage)
+{
+  DAC960_Error("While configuring DAC960 PCI RAID Controller at\n",
+	       Controller);
+  if (Controller->IO_Address == 0)
+    DAC960_Error("PCI Bus %d Device %d Function %d I/O Address N/A "
+		 "PCI Address 0x%X\n", Controller,
+		 Controller->Bus, Controller->Device,
+		 Controller->Function, Controller->PCI_Address);
+  else DAC960_Error("PCI Bus %d Device %d Function %d I/O Address "
+		    "0x%X PCI Address 0x%X\n", Controller,
+		    Controller->Bus, Controller->Device,
+		    Controller->Function, Controller->IO_Address,
+		    Controller->PCI_Address);
+  DAC960_Error("%s FAILED - DETACHING\n", Controller, ErrorMessage);
+  return false;
+}
+
+
+/*
+  DAC960_ClearCommand clears critical fields of Command.
+*/
+
+static inline void DAC960_ClearCommand(DAC960_Command_T *Command)
+{
+  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+  CommandMailbox->Words[0] = 0;
+  CommandMailbox->Words[1] = 0;
+  CommandMailbox->Words[2] = 0;
+  CommandMailbox->Words[3] = 0;
+  Command->CommandStatus = 0;
+}
+
+
+/*
+  DAC960_AllocateCommand allocates a Command structure from Controller's
+  free list.
+*/
+
+static inline DAC960_Command_T *DAC960_AllocateCommand(DAC960_Controller_T
+						       *Controller)
+{
+  DAC960_Command_T *Command = Controller->FreeCommands;
+  if (Command == NULL) return NULL;
+  Controller->FreeCommands = Command->Next;
+  Command->Next = NULL;
+  return Command;
+}
+
+
+/*
+  DAC960_DeallocateCommand deallocates Command, returning it to Controller's
+  free list.
+*/
+
+static inline void DAC960_DeallocateCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  Command->Next = Controller->FreeCommands;
+  Controller->FreeCommands = Command;
+}
+
+
+/*
+  DAC960_QueueCommand queues Command.
+*/
+
+static void DAC960_QueueCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  void *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_V4_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+  DAC960_V4_CommandMailbox_T *NextCommandMailbox;
+  CommandMailbox->Common.CommandIdentifier = Command - Controller->Commands;
+  switch (Controller->ControllerType)
+    {
+    case DAC960_V4_Controller:
+      NextCommandMailbox = Controller->NextCommandMailbox;
+      DAC960_V4_WriteCommandMailbox(NextCommandMailbox, CommandMailbox);
+      if (Controller->PreviousCommandMailbox->Words[0] == 0)
+	DAC960_V4_NewCommand(ControllerBaseAddress);
+      Controller->PreviousCommandMailbox = NextCommandMailbox;
+      if (++NextCommandMailbox > Controller->LastCommandMailbox)
+	NextCommandMailbox = Controller->FirstCommandMailbox;
+      Controller->NextCommandMailbox = NextCommandMailbox;
+      break;
+    case DAC960_V3_Controller:
+      while (DAC960_V3_MailboxFullP(ControllerBaseAddress))
+	udelay(1);
+      DAC960_V3_WriteCommandMailbox(ControllerBaseAddress, CommandMailbox);
+      DAC960_V3_NewCommand(ControllerBaseAddress);
+      break;
+    }
+}
+
+
+/*
+  DAC960_ExecuteCommand executes Command and waits for completion.  It
+  returns true on success and false on failure.
+*/
+
+static boolean DAC960_ExecuteCommand(DAC960_Command_T *Command)
+{
+  Semaphore_T Semaphore = MUTEX_LOCKED;
+  Command->Semaphore = &Semaphore;
+  DAC960_QueueCommand(Command);
+  down(&Semaphore);
+  return Command->CommandStatus == DAC960_NormalCompletion;
+}
+
+
+/*
+  DAC960_ExecuteType3 executes a DAC960 Type 3 Command and waits for
+  completion.  It returns true on success and false on failure.
+*/
+
+static boolean DAC960_ExecuteType3(DAC960_Controller_T *Controller,
+				   DAC960_CommandOpcode_T CommandOpcode,
+				   void *DataPointer)
+{
+  DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
+  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+  boolean Result;
+  DAC960_ClearCommand(Command);
+  Command->CommandType = DAC960_ImmediateCommand;
+  CommandMailbox->Type3.CommandOpcode = CommandOpcode;
+  CommandMailbox->Type3.BusAddress = Virtual_to_Bus(DataPointer);
+  Result = DAC960_ExecuteCommand(Command);
+  DAC960_DeallocateCommand(Command);
+  return Result;
+}
+
+
+/*
+  DAC960_ExecuteType3D executes a DAC960 Type 3D Command and waits for
+  completion.  It returns true on success and false on failure.
+*/
+
+static boolean DAC960_ExecuteType3D(DAC960_Controller_T *Controller,
+				    DAC960_CommandOpcode_T CommandOpcode,
+				    unsigned char Channel,
+				    unsigned char TargetID,
+				    void *DataPointer)
+{
+  DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
+  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+  boolean Result;
+  DAC960_ClearCommand(Command);
+  Command->CommandType = DAC960_ImmediateCommand;
+  CommandMailbox->Type3D.CommandOpcode = CommandOpcode;
+  CommandMailbox->Type3D.Channel = Channel;
+  CommandMailbox->Type3D.TargetID = TargetID;
+  CommandMailbox->Type3D.BusAddress = Virtual_to_Bus(DataPointer);
+  Result = DAC960_ExecuteCommand(Command);
+  DAC960_DeallocateCommand(Command);
+  return Result;
+}
+
+
+/*
+  DAC960_V4_EnableMemoryMailboxInterface enables the V4 Memory Mailbox
+  Interface.
+*/
+
+static boolean DAC960_V4_EnableMemoryMailboxInterface(DAC960_Controller_T
+						      *Controller)
+{
+  void *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_V4_CommandMailbox_T *CommandMailboxesMemory;
+  DAC960_V4_StatusMailbox_T *StatusMailboxesMemory;
+  DAC960_CommandMailbox_T CommandMailbox;
+  DAC960_CommandStatus_T CommandStatus;
+  CommandMailboxesMemory =
+    (DAC960_V4_CommandMailbox_T *) __get_free_pages(GFP_KERNEL, 1, 0);
+  memset(CommandMailboxesMemory, 0, PAGE_SIZE << 1);
+  Controller->FirstCommandMailbox = CommandMailboxesMemory;
+  CommandMailboxesMemory += DAC960_CommandMailboxCount - 1;
+  Controller->LastCommandMailbox = CommandMailboxesMemory;
+  Controller->NextCommandMailbox = Controller->FirstCommandMailbox;
+  Controller->PreviousCommandMailbox = Controller->LastCommandMailbox;
+  StatusMailboxesMemory =
+    (DAC960_V4_StatusMailbox_T *) (CommandMailboxesMemory + 1);
+  Controller->FirstStatusMailbox = StatusMailboxesMemory;
+  StatusMailboxesMemory += DAC960_StatusMailboxCount - 1;
+  Controller->LastStatusMailbox = StatusMailboxesMemory;
+  Controller->NextStatusMailbox = Controller->FirstStatusMailbox;
+  /* Enable the Memory Mailbox Interface. */
+  CommandMailbox.TypeX.CommandOpcode = 0x2B;
+  CommandMailbox.TypeX.CommandIdentifier = 0;
+  CommandMailbox.TypeX.CommandOpcode2 = 0x10;
+  CommandMailbox.TypeX.CommandMailboxesBusAddress =
+    Virtual_to_Bus(Controller->FirstCommandMailbox);
+  CommandMailbox.TypeX.StatusMailboxesBusAddress =
+    Virtual_to_Bus(Controller->FirstStatusMailbox);
+  while (DAC960_V4_MailboxFullP(ControllerBaseAddress))
+    udelay(1);
+  DAC960_V4_WriteLegacyCommand(ControllerBaseAddress, &CommandMailbox);
+  DAC960_V4_NewCommand(ControllerBaseAddress);
+  while (!DAC960_V4_StatusAvailableP(ControllerBaseAddress))
+    udelay(1);
+  CommandStatus = DAC960_V4_ReadStatusRegister(ControllerBaseAddress);
+  DAC960_V4_AcknowledgeInterrupt(ControllerBaseAddress);
+  DAC960_V4_AcknowledgeStatus(ControllerBaseAddress);
+  return CommandStatus == DAC960_NormalCompletion;
+}
+
+
+/*
+  DAC960_DetectControllers detects DAC960 PCI RAID Controllers by interrogating
+  the PCI Configuration Space for DeviceID.
+*/
+
+static void DAC960_DetectControllers(unsigned short DeviceID)
+{
+  unsigned char Bus, DeviceFunction, IRQ_Channel;
+  unsigned int BaseAddress0, BaseAddress1;
+  unsigned int MemoryWindowSize = 0;
+  unsigned short Index = 0;
+  while (pcibios_find_device(PCI_VENDOR_ID_MYLEX, DeviceID,
+			     Index++, &Bus, &DeviceFunction) == 0)
+    {
+      DAC960_Controller_T *Controller = (DAC960_Controller_T *)
+	kmalloc(sizeof(DAC960_Controller_T), GFP_ATOMIC);
+      DAC960_ControllerType_T ControllerType = 0;
+      DAC960_IO_Address_T IO_Address = 0;
+      DAC960_PCI_Address_T PCI_Address = 0;
+      unsigned char Device = DeviceFunction >> 3;
+      unsigned char Function = DeviceFunction & 0x7;
+      pcibios_read_config_dword(Bus, DeviceFunction,
+				PCI_BASE_ADDRESS_0, &BaseAddress0);
+      pcibios_read_config_dword(Bus, DeviceFunction,
+				PCI_BASE_ADDRESS_1, &BaseAddress1);
+      pcibios_read_config_byte(Bus, DeviceFunction,
+			       PCI_INTERRUPT_LINE, &IRQ_Channel);
+      switch (DeviceID)
+	{
+	case PCI_DEVICE_ID_MYLEX_DAC960P_V4:
+	  ControllerType = DAC960_V4_Controller;
+	  PCI_Address = BaseAddress0 & PCI_BASE_ADDRESS_MEM_MASK;
+	  MemoryWindowSize = DAC960_V4_RegisterWindowSize;
+	  break;
+	case PCI_DEVICE_ID_MYLEX_DAC960P_V3:
+	  ControllerType = DAC960_V3_Controller;
+	  IO_Address = BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK;
+	  PCI_Address = BaseAddress1 & PCI_BASE_ADDRESS_MEM_MASK;
+	  MemoryWindowSize = DAC960_V3_RegisterWindowSize;
+	  break;
+	}
+      if (DAC960_ControllerCount == DAC960_MaxControllers)
+	{
+	  DAC960_Error("More than %d DAC960 Controllers detected - "
+		       "ignoring from Controller at\n",
+		       NULL, DAC960_MaxControllers);
+	  goto Failure;
+	}
+      if (Controller == NULL)
+	{
+	  DAC960_Error("Unable to allocate Controller structure for "
+		       "Controller at\n", NULL);
+	  goto Failure;
+	}
+      memset(Controller, 0, sizeof(DAC960_Controller_T));
+      Controller->ControllerNumber = DAC960_ControllerCount;
+      DAC960_Controllers[DAC960_ControllerCount++] = Controller;
+      if (IRQ_Channel == 0 || IRQ_Channel >= NR_IRQS)
+	{
+	  DAC960_Error("IRQ Channel %d illegal for Controller at\n",
+		       NULL, IRQ_Channel);
+	  goto Failure;
+	}
+      Controller->ControllerType = ControllerType;
+      Controller->IO_Address = IO_Address;
+      Controller->PCI_Address = PCI_Address;
+      Controller->Bus = Bus;
+      Controller->Device = Device;
+      Controller->Function = Function;
+      /*
+	Acquire shared access to the IRQ Channel.
+      */
+      strcpy(Controller->FullModelName, "DAC960");
+      if (request_irq(IRQ_Channel, DAC960_InterruptHandler,
+		      SA_INTERRUPT | SA_SHIRQ, Controller->FullModelName,
+		      Controller) < 0)
+	{
+	  DAC960_Error("Unable to acquire IRQ Channel %d for Controller at\n",
+		       NULL, IRQ_Channel);
+	  goto Failure;
+	}
+      Controller->IRQ_Channel = IRQ_Channel;
+      /*
+	Map the Controller Register Window.
+      */
+      if (MemoryWindowSize < PAGE_SIZE)
+	MemoryWindowSize = PAGE_SIZE;
+      Controller->MemoryMappedAddress =
+	ioremap_nocache(PCI_Address & PAGE_MASK, MemoryWindowSize);
+      Controller->BaseAddress =
+	Controller->MemoryMappedAddress + (PCI_Address & ~PAGE_MASK);
+      if (Controller->MemoryMappedAddress == NULL)
+	{
+	  DAC960_Error("Unable to map Controller Register Window for "
+		       "Controller at\n", NULL);
+	  goto Failure;
+	}
+      switch (DeviceID)
+	{
+	case PCI_DEVICE_ID_MYLEX_DAC960P_V4:
+	  DAC960_V4_DisableInterrupts(Controller->BaseAddress);
+	  if (!DAC960_V4_EnableMemoryMailboxInterface(Controller))
+	    {
+	      DAC960_Error("Unable to Enable V4 Memory Mailbox Interface "
+			   "for Controller at\n", NULL);
+	      goto Failure;
+	    }
+	  DAC960_V4_EnableInterrupts(Controller->BaseAddress);
+	  break;
+	case PCI_DEVICE_ID_MYLEX_DAC960P_V3:
+	  request_region(Controller->IO_Address, 0x80,
+			 Controller->FullModelName);
+	  DAC960_V3_EnableInterrupts(Controller->BaseAddress);
+	  break;
+	}
+      Controller->Commands[0].Controller = Controller;
+      Controller->Commands[0].Next = NULL;
+      Controller->FreeCommands = &Controller->Commands[0];
+      continue;
+    Failure:
+      if (IO_Address == 0)
+	DAC960_Error("PCI Bus %d Device %d Function %d I/O Address N/A "
+		     "PCI Address 0x%X\n", NULL,
+		     Bus, Device, Function, PCI_Address);
+      else DAC960_Error("PCI Bus %d Device %d Function %d I/O Address "
+			"0x%X PCI Address 0x%X\n", NULL,
+			Bus, Device, Function, IO_Address, PCI_Address);
+      if (Controller == NULL) break;
+      if (Controller->IRQ_Channel > 0)
+	free_irq(IRQ_Channel, Controller);
+      if (Controller->MemoryMappedAddress != NULL)
+	iounmap(Controller->MemoryMappedAddress);
+      kfree(Controller);
+      break;
+    }
+}
+
+
+/*
+  DAC960_ReadControllerConfiguration reads the Configuration Information
+  from Controller and initializes the Controller structure.
+*/
+
+static boolean DAC960_ReadControllerConfiguration(DAC960_Controller_T
+						  *Controller)
+{
+  DAC960_Enquiry2_T Enquiry2;
+  DAC960_Config2_T Config2;
+  int Channel, TargetID;
+  if (!DAC960_ExecuteType3(Controller, DAC960_Enquiry,
+			   &Controller->Enquiry[0]))
+    return DAC960_Failure(Controller, "ENQUIRY");
+  if (!DAC960_ExecuteType3(Controller, DAC960_Enquiry2, &Enquiry2))
+    return DAC960_Failure(Controller, "ENQUIRY2");
+  if (!DAC960_ExecuteType3(Controller, DAC960_ReadConfig2, &Config2))
+    return DAC960_Failure(Controller, "READ CONFIG2");
+  if (!DAC960_ExecuteType3(Controller, DAC960_GetLogicalDriveInformation,
+			   &Controller->LogicalDriveInformation[0]))
+    return DAC960_Failure(Controller, "GET LOGICAL DRIVE INFORMATION");
+  for (Channel = 0; Channel < Enquiry2.ActualChannels; Channel++)
+    for (TargetID = 0; TargetID < DAC960_MaxTargets; TargetID++)
+      if (!DAC960_ExecuteType3D(Controller, DAC960_GetDeviceState,
+				Channel, TargetID,
+				&Controller->DeviceState[0][Channel][TargetID]))
+	  return DAC960_Failure(Controller, "GET DEVICE STATE");
+  /*
+    Initialize the Controller Model Name and Full Model Name fields.
+  */
+  switch (Enquiry2.HardwareID.SubModel)
+    {
+    case DAC960_P_PD_PU:
+      if (Enquiry2.SCSICapability.BusSpeed == DAC960_Ultra)
+	strcpy(Controller->ModelName, "DAC960PU");
+      else strcpy(Controller->ModelName, "DAC960PD");
+      break;
+    case DAC960_PL:
+      strcpy(Controller->ModelName, "DAC960PL");
+      break;
+    case DAC960_PG:
+      strcpy(Controller->ModelName, "DAC960PG");
+      break;
+    case DAC960_PJ:
+      strcpy(Controller->ModelName, "DAC960PJ");
+      break;
+    case DAC960_PTL_0:
+      strcpy(Controller->ModelName, "DAC960PTL-0");
+      break;
+    case DAC960_PTL_1:
+      strcpy(Controller->ModelName, "DAC960PTL-1");
+      break;
+    default:
+      return DAC960_Failure(Controller, "MODEL VERIFICATION");
+    }
+  strcpy(Controller->FullModelName, "Mylex ");
+  strcat(Controller->FullModelName, Controller->ModelName);
+  /*
+    Initialize the Controller Firmware Version field and verify that it
+    is a supported firmware version.  The supported firmware versions are:
+
+    DAC960PTL/PJ/PG	4.06 and above
+    DAC960PU/PD/PL	3.51 and above
+  */
+  sprintf(Controller->FirmwareVersion, "%d.%02d-%c-%02d",
+	  Enquiry2.FirmwareID.MajorVersion, Enquiry2.FirmwareID.MinorVersion,
+	  Enquiry2.FirmwareID.FirmwareType, Enquiry2.FirmwareID.TurnID);
+  if (!((Controller->FirmwareVersion[0] == '4' &&
+	 strcmp(Controller->FirmwareVersion, "4.06") >= 0) ||
+	(Controller->FirmwareVersion[0] == '3' &&
+	 strcmp(Controller->FirmwareVersion, "3.51") >= 0)))
+    {
+      DAC960_Failure(Controller, "FIRMWARE VERSION VERIFICATION");
+      DAC960_Error("Firmware Version = '%s'\n", Controller,
+		   Controller->FirmwareVersion);
+      return false;
+    }
+  /*
+    Initialize the Controller Channels, Memory Size, and SAF-TE Fault
+    Management Enabled fields.
+  */
+  Controller->Channels = Enquiry2.ActualChannels;
+  Controller->MemorySize = Enquiry2.MemorySize >> 20;
+  Controller->SAFTE_FaultManagementEnabled =
+    Enquiry2.FaultManagementType == DAC960_SAFTE;
+  /*
+    Initialize the Controller Queue Depth, Driver Queue Depth, Logical Drive
+    Count, Maximum Blocks per Command, and Maximum Scatter/Gather Segments.
+    The Driver Queue Depth must be at most one less than the Controller Queue
+    Depth to allow for an automatic drive rebuild operation.
+  */
+  Controller->ControllerQueueDepth = Controller->Enquiry[0].MaxCommands;
+  Controller->DriverQueueDepth = Controller->ControllerQueueDepth - 1;
+  Controller->LogicalDriveCount = Controller->Enquiry[0].NumberOfLogicalDrives;
+  Controller->MaxBlocksPerCommand = Enquiry2.MaxBlocksPerCommand;
+  Controller->MaxScatterGatherSegments = Enquiry2.MaxScatterGatherEntries;
+  /*
+    Initialize the Stripe Size, Segment Size, and Geometry Translation.
+  */
+  Controller->StripeSize = Config2.BlocksPerStripe * Config2.BlockFactor
+			   >> (10 - DAC960_BlockSizeBits);
+  Controller->SegmentSize = Config2.BlocksPerCacheLine * Config2.BlockFactor
+			    >> (10 - DAC960_BlockSizeBits);
+  switch (Config2.DriveGeometry)
+    {
+    case DAC960_Geometry_128_32:
+      Controller->GeometryTranslationHeads = 128;
+      Controller->GeometryTranslationSectors = 32;
+      break;
+    case DAC960_Geometry_255_63:
+      Controller->GeometryTranslationHeads = 255;
+      Controller->GeometryTranslationSectors = 63;
+      break;
+    default:
+      return DAC960_Failure(Controller, "CONFIG2 DRIVE GEOMETRY");
+    }
+  return true;
+}
+
+
+/*
+  DAC960_ReportControllerConfiguration reports the configuration of
+  Controller.
+*/
+
+static boolean DAC960_ReportControllerConfiguration(DAC960_Controller_T
+						    *Controller)
+{
+  int LogicalDriveNumber, Channel, TargetID;
+  DAC960_Info("Configuring Mylex %s PCI RAID Controller\n",
+	      Controller, Controller->ModelName);
+  DAC960_Info("  Firmware Version: %s, Channels: %d, Memory Size: %dMB\n",
+	      Controller, Controller->FirmwareVersion,
+	      Controller->Channels, Controller->MemorySize);
+  DAC960_Info("  PCI Bus: %d, Device: %d, Function: %d, I/O Address: ",
+	      Controller, Controller->Bus,
+	      Controller->Device, Controller->Function);
+  if (Controller->IO_Address == 0)
+    DAC960_Info("Unassigned\n", Controller);
+  else DAC960_Info("0x%X\n", Controller, Controller->IO_Address);
+  DAC960_Info("  PCI Address: 0x%X mapped at 0x%lX, IRQ Channel: %d\n",
+	      Controller, Controller->PCI_Address,
+	      (unsigned long) Controller->BaseAddress,
+	      Controller->IRQ_Channel);
+  DAC960_Info("  Controller Queue Depth: %d, "
+	      "Maximum Blocks per Command: %d\n",
+	      Controller, Controller->ControllerQueueDepth,
+	      Controller->MaxBlocksPerCommand);
+  DAC960_Info("  Driver Queue Depth: %d, "
+	      "Maximum Scatter/Gather Segments: %d\n",
+	      Controller, Controller->DriverQueueDepth,
+	      Controller->MaxScatterGatherSegments);
+  DAC960_Info("  Stripe Size: %dKB, Segment Size: %dKB, "
+	      "BIOS Geometry: %d/%d\n", Controller,
+	      Controller->StripeSize,
+	      Controller->SegmentSize,
+	      Controller->GeometryTranslationHeads,
+	      Controller->GeometryTranslationSectors);
+  if (Controller->SAFTE_FaultManagementEnabled)
+    DAC960_Info("  SAF-TE Fault Management Enabled\n", Controller);
+  DAC960_Info("  Physical Devices:\n", Controller);
+  for (Channel = 0; Channel < Controller->Channels; Channel++)
+    for (TargetID = 0; TargetID < DAC960_MaxTargets; TargetID++)
+      {
+	DAC960_DeviceState_T *DeviceState =
+	  &Controller->DeviceState[0][Channel][TargetID];
+	if (!DeviceState->Present) continue;
+	switch (DeviceState->DeviceType)
+	  {
+	  case DAC960_OtherType:
+	    DAC960_Info("    %d:%d - Other\n", Controller, Channel, TargetID);
+	    break;
+	  case DAC960_DiskType:
+	    DAC960_Info("    %d:%d - Disk: %s, %d blocks\n", Controller,
+			Channel, TargetID,
+			(DeviceState->DeviceState == DAC960_Device_Dead
+			 ? "Dead"
+			 : DeviceState->DeviceState == DAC960_Device_WriteOnly
+			   ? "Write-Only"
+			   : DeviceState->DeviceState == DAC960_Device_Online
+			     ? "Online" : "Standby"),
+			DeviceState->DiskSize);
+	    break;
+	  case DAC960_SequentialType:
+	    DAC960_Info("    %d:%d - Sequential\n", Controller,
+			Channel, TargetID);
+	    break;
+	  case DAC960_CDROM_or_WORM_Type:
+	    DAC960_Info("    %d:%d - CD-ROM or WORM\n", Controller,
+			Channel, TargetID);
+	    break;
+	  }
+
+      }
+  DAC960_Info("  Logical Drives:\n", Controller);
+  for (LogicalDriveNumber = 0;
+       LogicalDriveNumber < Controller->LogicalDriveCount;
+       LogicalDriveNumber++)
+    {
+      DAC960_LogicalDriveInformation_T *LogicalDriveInformation =
+	&Controller->LogicalDriveInformation[0][LogicalDriveNumber];
+      DAC960_Info("    /dev/rd/c%dd%d: RAID-%d, %s, %d blocks, %s\n",
+		  Controller, Controller->ControllerNumber, LogicalDriveNumber,
+		  LogicalDriveInformation->RAIDLevel,
+		  (LogicalDriveInformation->LogicalDriveState ==
+		     DAC960_LogicalDrive_Online
+		   ? "Online"
+		   : LogicalDriveInformation->LogicalDriveState ==
+		     DAC960_LogicalDrive_Critical
+		     ? "Critical" : "Offline"),
+		  LogicalDriveInformation->LogicalDriveSize,
+		  (LogicalDriveInformation->WriteBack
+		   ? "Write Back" : "Write Thru"));
+    }
+  DAC960_Info("\n", Controller);
+  return true;
+}
+
+
+/*
+  DAC960_RegisterBlockDevice registers the Block Device structures
+  associated with Controller.
+*/
+
+static boolean DAC960_RegisterBlockDevice(DAC960_Controller_T *Controller)
+{
+  static void (*RequestFunctions[DAC960_MaxControllers])(void) =
+    { DAC960_RequestFunction0, DAC960_RequestFunction1,
+      DAC960_RequestFunction2, DAC960_RequestFunction3,
+      DAC960_RequestFunction4, DAC960_RequestFunction5,
+      DAC960_RequestFunction6, DAC960_RequestFunction7 };
+  int MajorNumber = DAC960_MAJOR + Controller->ControllerNumber;
+  GenericDiskInfo_T *GenericDiskInfo;
+  int MinorNumber;
+  /*
+    Register the Block Device Major Number for this DAC960 Controller.
+  */
+  if (register_blkdev(MajorNumber, "rd", &DAC960_FileOperations) < 0)
+    {
+      DAC960_Error("UNABLE TO ACQUIRE MAJOR NUMBER %d - DETACHING\n",
+		   Controller, MajorNumber);
+      return false;
+    }
+  /*
+    Initialize the I/O Request Function.
+  */
+  blk_dev[MajorNumber].request_fn =
+    RequestFunctions[Controller->ControllerNumber];
+  /*
+    Initialize the Disk Partitions array, Partition Sizes array, Block Sizes
+    array, Max Sectors per Request array, and Max Segments per Request array.
+  */
+  for (MinorNumber = 0; MinorNumber < DAC960_MinorCount; MinorNumber++)
+    {
+      Controller->BlockSizes[MinorNumber] = BLOCK_SIZE;
+      Controller->MaxSectorsPerRequest[MinorNumber] =
+	Controller->MaxBlocksPerCommand;
+      Controller->MaxSegmentsPerRequest[MinorNumber] =
+	Controller->MaxScatterGatherSegments;
+    }
+  Controller->GenericDiskInfo.part = Controller->DiskPartitions;
+  Controller->GenericDiskInfo.sizes = Controller->PartitionSizes;
+  blksize_size[MajorNumber] = Controller->BlockSizes;
+  max_sectors[MajorNumber] = Controller->MaxSectorsPerRequest;
+  max_segments[MajorNumber] = Controller->MaxSegmentsPerRequest;
+  /*
+    Initialize Read Ahead to 128 sectors.
+  */
+  read_ahead[MajorNumber] = 128;
+  /*
+    Complete initialization of the Generic Disk Information structure.
+  */
+  Controller->GenericDiskInfo.major = MajorNumber;
+  Controller->GenericDiskInfo.major_name = "rd";
+  Controller->GenericDiskInfo.minor_shift = DAC960_MaxPartitionsBits;
+  Controller->GenericDiskInfo.max_p = DAC960_MaxPartitions;
+  Controller->GenericDiskInfo.max_nr = DAC960_MaxLogicalDrives;
+  Controller->GenericDiskInfo.init = DAC960_InitializeGenericDiskInfo;
+  Controller->GenericDiskInfo.nr_real = Controller->LogicalDriveCount;
+  Controller->GenericDiskInfo.real_devices = Controller;
+  Controller->GenericDiskInfo.next = NULL;
+  /*
+    Install the Generic Disk Information structure at the end of the list.
+  */
+  if ((GenericDiskInfo = gendisk_head) != NULL)
+    {
+      while (GenericDiskInfo->next != NULL)
+	GenericDiskInfo = GenericDiskInfo->next;
+      GenericDiskInfo->next = &Controller->GenericDiskInfo;
+    }
+  else gendisk_head = &Controller->GenericDiskInfo;
+  /*
+    Indicate the Block Device Registration completed successfully,
+  */
+  return true;
+}
+
+
+/*
+  DAC960_UnregisterBlockDevice unregisters the Block Device structures
+  associated with Controller.
+*/
+
+static void DAC960_UnregisterBlockDevice(DAC960_Controller_T *Controller)
+{
+  int MajorNumber = DAC960_MAJOR + Controller->ControllerNumber;
+  /*
+    Unregister the Block Device Major Number for this DAC960 Controller.
+  */
+  unregister_blkdev(MajorNumber, "rd");
+  /*
+    Remove the I/O Request Function.
+  */
+  blk_dev[MajorNumber].request_fn = NULL;
+  /*
+    Remove the Disk Partitions array, Partition Sizes array, Block Sizes
+    array, Max Sectors per Request array, and Max Segments per Request array.
+  */
+  Controller->GenericDiskInfo.part = NULL;
+  Controller->GenericDiskInfo.sizes = NULL;
+  blk_size[MajorNumber] = NULL;
+  blksize_size[MajorNumber] = NULL;
+  max_sectors[MajorNumber] = NULL;
+  max_segments[MajorNumber] = NULL;
+  /*
+    Remove the Generic Disk Information structure from the list.
+  */
+  if (gendisk_head != &Controller->GenericDiskInfo)
+    {
+      GenericDiskInfo_T *GenericDiskInfo = gendisk_head;
+      while (GenericDiskInfo != NULL &&
+	     GenericDiskInfo->next != &Controller->GenericDiskInfo)
+	GenericDiskInfo = GenericDiskInfo->next;
+      if (GenericDiskInfo != NULL)
+	GenericDiskInfo->next = GenericDiskInfo->next->next;
+    }
+  else gendisk_head = Controller->GenericDiskInfo.next;
+}
+
+
+/*
+  DAC960_InitializeController initializes Controller.
+*/
+
+static void DAC960_InitializeController(DAC960_Controller_T *Controller)
+{
+  DAC960_AnnounceDriver(Controller);
+  if (DAC960_ReadControllerConfiguration(Controller) &&
+      DAC960_ReportControllerConfiguration(Controller) &&
+      DAC960_RegisterBlockDevice(Controller))
+    {
+      /*
+	Initialize the Command structures.
+      */
+      DAC960_Command_T *Commands = Controller->Commands;
+      int CommandIdentifier;
+      Controller->FreeCommands = NULL;
+      for (CommandIdentifier = 0;
+	   CommandIdentifier < Controller->DriverQueueDepth;
+	   CommandIdentifier++)
+	{
+	  Commands[CommandIdentifier].Controller = Controller;
+	  Commands[CommandIdentifier].Next = Controller->FreeCommands;
+	  Controller->FreeCommands = &Commands[CommandIdentifier];
+	}
+      /*
+	Initialize the Monitoring Timer.
+      */
+      init_timer(&Controller->MonitoringTimer);
+      Controller->MonitoringTimer.expires =
+	jiffies + DAC960_MonitoringTimerInterval;
+      Controller->MonitoringTimer.data = (unsigned long) Controller;
+      Controller->MonitoringTimer.function = DAC960_MonitoringTimerFunction;
+      add_timer(&Controller->MonitoringTimer);
+    }
+  else
+    {
+      free_irq(Controller->IRQ_Channel, Controller);
+      iounmap(Controller->MemoryMappedAddress);
+      DAC960_UnregisterBlockDevice(Controller);
+      DAC960_Controllers[Controller->ControllerNumber] = NULL;
+      kfree(Controller);
+    }
+}
+
+
+/*
+  DAC960_Initialize initializes the DAC960 Driver.
+*/
+
+void DAC960_Initialize(void)
+{
+  int ControllerNumber;
+  DAC960_DetectControllers(PCI_DEVICE_ID_MYLEX_DAC960P_V4);
+  DAC960_DetectControllers(PCI_DEVICE_ID_MYLEX_DAC960P_V3);
+  for (ControllerNumber = 0;
+       ControllerNumber < DAC960_ControllerCount;
+       ControllerNumber++)
+    if (DAC960_Controllers[ControllerNumber] != NULL)
+      DAC960_InitializeController(DAC960_Controllers[ControllerNumber]);
+}
+
+
+/*
+  DAC960_Finalize flushes all DAC960 caches before the system halts.
+*/
+
+void DAC960_Finalize(void)
+{
+  int ControllerNumber;
+  for (ControllerNumber = 0;
+       ControllerNumber < DAC960_ControllerCount;
+       ControllerNumber++)
+    {
+      DAC960_Controller_T *Controller = DAC960_Controllers[ControllerNumber];
+      if (Controller == NULL) continue;
+      DAC960_Notice("Flushing Cache...", Controller);
+      DAC960_ExecuteType3(Controller, DAC960_Flush, NULL);
+      DAC960_Notice("done\n", Controller);
+    }
+}
+
+
+/*
+  DAC960_ProcessRequest attempts to remove one I/O Request from Controller's
+  I/O Request Queue and queues it to the Controller.  Command is either a
+  previously allocated Command to be reused, or NULL if a new Command is to
+  be allocated for this I/O Request.  It returns true if an I/O Request was
+  queued and false otherwise.
+*/
+
+static boolean DAC960_ProcessRequest(DAC960_Controller_T *Controller,
+				     DAC960_Command_T *Command)
+{
+  int MajorNumber = DAC960_MAJOR + Controller->ControllerNumber;
+  IO_Request_T *Request = blk_dev[MajorNumber].current_request;
+  DAC960_CommandMailbox_T *CommandMailbox;
+  char *RequestBuffer;
+  if (Request == NULL || Request->rq_status == RQ_INACTIVE) return false;
+  if (Command == NULL)
+    Command = DAC960_AllocateCommand(Controller);
+  if (Command == NULL) return false;
+  DAC960_ClearCommand(Command);
+  if (Request->cmd == READ)
+    Command->CommandType = DAC960_ReadCommand;
+  else Command->CommandType = DAC960_WriteCommand;
+  Command->Semaphore = Request->sem;
+  Command->LogicalDriveNumber = DAC960_LogicalDriveNumber(Request->rq_dev);
+  Command->BlockNumber =
+    Request->sector
+    + Controller->GenericDiskInfo.part[MINOR(Request->rq_dev)].start_sect;
+  Command->BlockCount = Request->nr_sectors;
+  Command->SegmentCount = Request->nr_segments;
+  Command->BufferHeader = Request->bh;
+  RequestBuffer = Request->buffer;
+  Request->rq_status = RQ_INACTIVE;
+  blk_dev[MajorNumber].current_request = Request->next;
+  wake_up(&wait_for_request);
+  CommandMailbox = &Command->CommandMailbox;
+  if (Command->SegmentCount == 1)
+    {
+      if (Command->CommandType == DAC960_ReadCommand)
+	CommandMailbox->Type5.CommandOpcode = DAC960_Read;
+      else CommandMailbox->Type5.CommandOpcode = DAC960_Write;
+      CommandMailbox->Type5.LD.TransferLength = Command->BlockCount;
+      CommandMailbox->Type5.LD.LogicalDriveNumber = Command->LogicalDriveNumber;
+      CommandMailbox->Type5.LogicalBlockAddress = Command->BlockNumber;
+      CommandMailbox->Type5.BusAddress = Virtual_to_Bus(RequestBuffer);
+    }
+  else
+    {
+      DAC960_ScatterGatherSegment_T
+	*ScatterGatherList = Command->ScatterGatherList;
+      BufferHeader_T *BufferHeader = Command->BufferHeader;
+      char *LastDataEndPointer = NULL;
+      int SegmentNumber = 0;
+      if (Command->CommandType == DAC960_ReadCommand)
+	CommandMailbox->Type5.CommandOpcode = DAC960_ReadWithOldScatterGather;
+      else
+	CommandMailbox->Type5.CommandOpcode = DAC960_WriteWithOldScatterGather;
+      CommandMailbox->Type5.LD.TransferLength = Command->BlockCount;
+      CommandMailbox->Type5.LD.LogicalDriveNumber = Command->LogicalDriveNumber;
+      CommandMailbox->Type5.LogicalBlockAddress = Command->BlockNumber;
+      CommandMailbox->Type5.BusAddress = Virtual_to_Bus(ScatterGatherList);
+      CommandMailbox->Type5.ScatterGatherCount = Command->SegmentCount;
+      while (BufferHeader != NULL)
+	{
+	  if (BufferHeader->b_data == LastDataEndPointer)
+	    {
+	      ScatterGatherList[SegmentNumber-1].SegmentByteCount +=
+		BufferHeader->b_size;
+	      LastDataEndPointer += BufferHeader->b_size;
+	    }
+	  else
+	    {
+	      ScatterGatherList[SegmentNumber].SegmentDataPointer =
+		Virtual_to_Bus(BufferHeader->b_data);
+	      ScatterGatherList[SegmentNumber].SegmentByteCount =
+		BufferHeader->b_size;
+	      LastDataEndPointer = BufferHeader->b_data + BufferHeader->b_size;
+	      if (SegmentNumber++ > Controller->MaxScatterGatherSegments)
+		panic("DAC960: Scatter/Gather Segment Overflow\n");
+	    }
+	  BufferHeader = BufferHeader->b_reqnext;
+	}
+      if (SegmentNumber != Command->SegmentCount)
+	panic("DAC960: SegmentNumber != SegmentCount\n");
+    }
+  DAC960_QueueCommand(Command);
+  return true;
+}
+
+
+/*
+  DAC960_ProcessRequests attempts to remove as many I/O Requests as possible
+  from Controller's I/O Request Queue and queue them to the Controller.
+*/
+
+static inline void DAC960_ProcessRequests(DAC960_Controller_T *Controller)
+{
+  while (Controller->FreeCommands != NULL)
+    if (!DAC960_ProcessRequest(Controller, NULL)) break;
+}
+
+
+/*
+  DAC960_RequestFunction0 is the I/O Request Function for DAC960 Controller 0.
+*/
+
+static void DAC960_RequestFunction0(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[0];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+   */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction1 is the I/O Request Function for DAC960 Controller 1.
+*/
+
+static void DAC960_RequestFunction1(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[1];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+   */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction2 is the I/O Request Function for DAC960 Controller 2.
+*/
+
+static void DAC960_RequestFunction2(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[2];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+   */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction3 is the I/O Request Function for DAC960 Controller 3.
+*/
+
+static void DAC960_RequestFunction3(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[3];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+   */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction4 is the I/O Request Function for DAC960 Controller 4.
+*/
+
+static void DAC960_RequestFunction4(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[4];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+   */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction5 is the I/O Request Function for DAC960 Controller 5.
+*/
+
+static void DAC960_RequestFunction5(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[5];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+   */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction6 is the I/O Request Function for DAC960 Controller 6.
+*/
+
+static void DAC960_RequestFunction6(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[6];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+   */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction7 is the I/O Request Function for DAC960 Controller 7.
+*/
+
+static void DAC960_RequestFunction7(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[7];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+   */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_ReadWriteError prints an appropriate error message for Command when
+  an error occurs on a read or write operation.
+*/
+
+static void DAC960_ReadWriteError(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  char *CommandName = "UNKNOWN";
+  switch (Command->CommandType)
+    {
+    case DAC960_ReadCommand:
+    case DAC960_ReadRetryCommand:
+      CommandName = "READ";
+      break;
+    case DAC960_WriteCommand:
+    case DAC960_WriteRetryCommand:
+      CommandName = "WRITE";
+      break;
+    case DAC960_MonitoringCommand:
+    case DAC960_ImmediateCommand:
+      break;
+    }
+  switch (Command->CommandStatus)
+    {
+    case DAC960_IrrecoverableDataError:
+      DAC960_Error("Irrecoverable Data Error on %s:\n",
+		   Controller, CommandName);
+      break;
+    case DAC960_LogicalDriveNonexistentOrOffline:
+      break;
+    case DAC960_AccessBeyondEndOfLogicalDrive:
+      DAC960_Error("Attempt to Access Beyond End of Logical Drive "
+		   "on %s:\n", Controller, CommandName);
+      break;
+    case DAC960_BadDataEncountered:
+      DAC960_Error("Bad Data Encountered on %s:\n", Controller, CommandName);
+      break;
+    default:
+      DAC960_Error("Unexpected Error Status %04X on %s:\n",
+		   Controller, Command->CommandStatus, CommandName);
+      break;
+    }
+  DAC960_Error("  /dev/rd/c%dd%d:   absolute blocks %d..%d\n",
+	       Controller, Controller->ControllerNumber,
+	       Command->LogicalDriveNumber, Command->BlockNumber,
+	       Command->BlockNumber + Command->BlockCount - 1);
+  if (DAC960_PartitionNumber(Command->BufferHeader->b_rdev) > 0)
+    DAC960_Error("  /dev/rd/c%dd%dp%d: relative blocks %d..%d\n",
+		 Controller, Controller->ControllerNumber,
+		 Command->LogicalDriveNumber,
+		 DAC960_PartitionNumber(Command->BufferHeader->b_rdev),
+		 Command->BufferHeader->b_rsector,
+		 Command->BufferHeader->b_rsector + Command->BlockCount - 1);
+}
+
+
+/*
+  DAC960_ProcessCompletedBuffer performs completion processing for an
+  individual Buffer.
+*/
+
+static inline void DAC960_ProcessCompletedBuffer(BufferHeader_T *BufferHeader,
+						 boolean SuccessfulIO)
+{
+  mark_buffer_uptodate(BufferHeader, SuccessfulIO);
+  unlock_buffer(BufferHeader);
+}
+
+
+/*
+  DAC960_ProcessCompletedCommand performs completion processing for Command.
+*/
+
+static void DAC960_ProcessCompletedCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  DAC960_CommandType_T CommandType = Command->CommandType;
+  DAC960_CommandStatus_T CommandStatus = Command->CommandStatus;
+  BufferHeader_T *BufferHeader = Command->BufferHeader;
+  if (CommandType == DAC960_ReadCommand ||
+      CommandType == DAC960_WriteCommand)
+    {
+      if (CommandStatus == DAC960_NormalCompletion)
+	{
+	  /*
+	    Perform completion processing for all buffers in this I/O Request.
+	  */
+	  while (BufferHeader != NULL)
+	    {
+	      BufferHeader_T *NextBufferHeader = BufferHeader->b_reqnext;
+	      BufferHeader->b_reqnext = NULL;
+	      DAC960_ProcessCompletedBuffer(BufferHeader, true);
+	      BufferHeader = NextBufferHeader;
+	    }
+	  /*
+	    Wake up requestor for swap file paging requests.
+	  */
+	  if (Command->Semaphore != NULL)
+	    {
+	      up(Command->Semaphore);
+	      Command->Semaphore = NULL;
+	    }
+	}
+      else if ((CommandStatus == DAC960_IrrecoverableDataError ||
+		CommandStatus == DAC960_BadDataEncountered) &&
+	       BufferHeader != NULL &&
+	       BufferHeader->b_reqnext != NULL)
+	{
+	  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+	  if (CommandType == DAC960_ReadCommand)
+	    {
+	      Command->CommandType = DAC960_ReadRetryCommand;
+	      CommandMailbox->Type5.CommandOpcode = DAC960_Read;
+	    }
+	  else
+	    {
+	      Command->CommandType = DAC960_WriteRetryCommand;
+	      CommandMailbox->Type5.CommandOpcode = DAC960_Write;
+	    }
+	  Command->BlockCount = BufferHeader->b_size >> DAC960_BlockSizeBits;
+	  CommandMailbox->Type5.LD.TransferLength = Command->BlockCount;
+	  CommandMailbox->Type5.BusAddress =
+	    Virtual_to_Bus(BufferHeader->b_data);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      else
+	{
+	  DAC960_ReadWriteError(Command);
+	  /*
+	    Perform completion processing for all buffers in this I/O Request.
+	  */
+	  while (BufferHeader != NULL)
+	    {
+	      BufferHeader_T *NextBufferHeader = BufferHeader->b_reqnext;
+	      BufferHeader->b_reqnext = NULL;
+	      DAC960_ProcessCompletedBuffer(BufferHeader, false);
+	      BufferHeader = NextBufferHeader;
+	    }
+	  /*
+	    Wake up requestor for swap file paging requests.
+	  */
+	  if (Command->Semaphore != NULL)
+	    {
+	      up(Command->Semaphore);
+	      Command->Semaphore = NULL;
+	    }
+	}
+    }
+  else if (CommandType == DAC960_ReadRetryCommand ||
+	   CommandType == DAC960_WriteRetryCommand)
+    {
+      BufferHeader_T *NextBufferHeader = BufferHeader->b_reqnext;
+      BufferHeader->b_reqnext = NULL;
+      /*
+	Perform completion processing for this single buffer.
+      */
+      if (CommandStatus == DAC960_NormalCompletion)
+	DAC960_ProcessCompletedBuffer(BufferHeader, true);
+      else
+	{
+	  DAC960_ReadWriteError(Command);
+	  DAC960_ProcessCompletedBuffer(BufferHeader, false);
+	}
+      if (NextBufferHeader != NULL)
+	{
+	  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+	  Command->BlockNumber +=
+	    BufferHeader->b_size >> DAC960_BlockSizeBits;
+	  Command->BlockCount =
+	    NextBufferHeader->b_size >> DAC960_BlockSizeBits;
+	  Command->BufferHeader = NextBufferHeader;
+	  CommandMailbox->Type5.LD.TransferLength = Command->BlockCount;
+	  CommandMailbox->Type5.LogicalBlockAddress = Command->BlockNumber;
+	  CommandMailbox->Type5.BusAddress =
+	    Virtual_to_Bus(NextBufferHeader->b_data);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+    }
+  else if (CommandType == DAC960_MonitoringCommand)
+    {
+      DAC960_CommandOpcode_T CommandOpcode =
+	Command->CommandMailbox.Common.CommandOpcode;
+      unsigned int OldCriticalLogicalDriveCount = 0;
+      unsigned int NewCriticalLogicalDriveCount = 0;
+      if (CommandOpcode == DAC960_Enquiry)
+	{
+	  DAC960_Enquiry_T *OldEnquiry =
+	    &Controller->Enquiry[Controller->EnquiryIndex];
+	  DAC960_Enquiry_T *NewEnquiry =
+	    &Controller->Enquiry[Controller->EnquiryIndex ^= 1];
+	  OldCriticalLogicalDriveCount = OldEnquiry->CriticalLogicalDriveCount;
+	  NewCriticalLogicalDriveCount = NewEnquiry->CriticalLogicalDriveCount;
+	  if (NewEnquiry->StatusFlags.DeferredWriteError !=
+	      OldEnquiry->StatusFlags.DeferredWriteError)
+	    DAC960_Critical("Deferred Write Error Flag is now %s\n", Controller,
+			    (NewEnquiry->StatusFlags.DeferredWriteError
+			     ? "TRUE" : "FALSE"));
+	  if ((NewCriticalLogicalDriveCount > 0 ||
+	       NewCriticalLogicalDriveCount != OldCriticalLogicalDriveCount) ||
+	      (NewEnquiry->OfflineLogicalDriveCount > 0 ||
+	       NewEnquiry->OfflineLogicalDriveCount !=
+	       OldEnquiry->OfflineLogicalDriveCount) ||
+	      (NewEnquiry->DeadDriveCount > 0 ||
+	       NewEnquiry->DeadDriveCount !=
+	       OldEnquiry->DeadDriveCount) ||
+	      (NewEnquiry->EventLogSequenceNumber !=
+	       OldEnquiry->EventLogSequenceNumber) ||
+	      (jiffies - Controller->SecondaryMonitoringTime
+		 >= DAC960_SecondaryMonitoringInterval))
+	    {
+	      Controller->NeedLogicalDriveInformation = true;
+	      Controller->NewEventLogSequenceNumber =
+		NewEnquiry->EventLogSequenceNumber;
+	      Controller->NeedDeviceStateInformation = true;
+	      Controller->DeviceStateChannel = 0;
+	      Controller->DeviceStateTargetID = 0;
+	      Controller->SecondaryMonitoringTime = jiffies;
+	    }
+	  if ((NewEnquiry->RebuildCount > 0 &&
+	      jiffies - Controller->RebuildLastReportTime
+		>= DAC960_RebuildStatusReportingInterval) ||
+	      NewEnquiry->RebuildCount != OldEnquiry->RebuildCount)
+	    Controller->NeedRebuildProgress = true;
+	}
+      else if (CommandOpcode == DAC960_GetLogicalDriveInformation)
+	{
+	  int LogicalDriveNumber;
+	  for (LogicalDriveNumber = 0;
+	       LogicalDriveNumber < Controller->LogicalDriveCount;
+	       LogicalDriveNumber++)
+	    {
+	      DAC960_LogicalDriveInformation_T *OldLogicalDriveInformation =
+		&Controller->LogicalDriveInformation
+			     [Controller->LogicalDriveInformationIndex]
+			     [LogicalDriveNumber];
+	      DAC960_LogicalDriveInformation_T *NewLogicalDriveInformation =
+		&Controller->LogicalDriveInformation
+			     [Controller->LogicalDriveInformationIndex ^ 1]
+			     [LogicalDriveNumber];
+	      if (NewLogicalDriveInformation->LogicalDriveState !=
+		  OldLogicalDriveInformation->LogicalDriveState)
+		DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) "
+				"is now %s\n", Controller,
+				LogicalDriveNumber,
+				Controller->ControllerNumber,
+				LogicalDriveNumber,
+				(NewLogicalDriveInformation->LogicalDriveState
+				 == DAC960_LogicalDrive_Online
+				 ? "ONLINE"
+				 : NewLogicalDriveInformation->LogicalDriveState
+				 == DAC960_LogicalDrive_Critical
+				 ? "CRITICAL" : "OFFLINE"));
+	      if (NewLogicalDriveInformation->WriteBack !=
+		  OldLogicalDriveInformation->WriteBack)
+		DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) "
+				"is now %s\n", Controller,
+				LogicalDriveNumber,
+				Controller->ControllerNumber,
+				LogicalDriveNumber,
+				(NewLogicalDriveInformation->WriteBack
+				 ? "WRITE BACK" : "WRITE THRU"));
+	    }
+	  Controller->LogicalDriveInformationIndex ^= 1;
+	}
+      else if (CommandOpcode == DAC960_PerformEventLogOperation)
+	{
+	  DAC960_EventLogEntry_T *EventLogEntry = &Controller->EventLogEntry;
+	  if (EventLogEntry->SequenceNumber ==
+	      Controller->OldEventLogSequenceNumber)
+	    {
+	      unsigned char SenseKey = EventLogEntry->SenseKey;
+	      unsigned char AdditionalSenseCode =
+		EventLogEntry->AdditionalSenseCode;
+	      unsigned char AdditionalSenseCodeQualifier =
+		EventLogEntry->AdditionalSenseCodeQualifier;
+	      if (SenseKey == 9 &&
+		  AdditionalSenseCode == 0x80 &&
+		  AdditionalSenseCodeQualifier < DAC960_EventMessagesCount)
+		DAC960_Critical("Physical Drive %d:%d %s\n", Controller,
+				EventLogEntry->Channel,
+				EventLogEntry->TargetID,
+				DAC960_EventMessages[
+				  AdditionalSenseCodeQualifier]);
+	      else if (!((SenseKey == 2 &&
+			  AdditionalSenseCode == 0x04 &&
+			  (AdditionalSenseCodeQualifier == 0x01 ||
+			   AdditionalSenseCodeQualifier == 0x02)) ||
+			 (SenseKey == 6 && AdditionalSenseCode == 0x29)))
+		DAC960_Critical("Physical Drive %d:%d Error Log: "
+				"Sense Key = %d, ASC = %02X, ASCQ = %02X\n",
+				Controller,
+				EventLogEntry->Channel,
+				EventLogEntry->TargetID,
+				SenseKey,
+				AdditionalSenseCode,
+				AdditionalSenseCodeQualifier);
+	    }
+	  Controller->OldEventLogSequenceNumber++;
+	}
+      else if (CommandOpcode == DAC960_GetDeviceState)
+	{
+	  DAC960_DeviceState_T *OldDeviceState =
+	    &Controller->DeviceState[Controller->DeviceStateIndex]
+				    [Controller->DeviceStateChannel]
+				    [Controller->DeviceStateTargetID];
+	  DAC960_DeviceState_T *NewDeviceState =
+	    &Controller->DeviceState[Controller->DeviceStateIndex ^ 1]
+				    [Controller->DeviceStateChannel]
+				    [Controller->DeviceStateTargetID];
+	  if (NewDeviceState->DeviceState != OldDeviceState->DeviceState)
+	    DAC960_Critical("Physical Drive %d:%d is now %s\n", Controller,
+			    Controller->DeviceStateChannel,
+			    Controller->DeviceStateTargetID,
+			    (NewDeviceState->DeviceState == DAC960_Device_Dead
+			     ? "DEAD"
+			     : NewDeviceState->DeviceState
+			       == DAC960_Device_WriteOnly
+			       ? "WRITE-ONLY"
+			       : NewDeviceState->DeviceState
+				 == DAC960_Device_Online
+				 ? "ONLINE" : "STANDBY"));
+	  if (++Controller->DeviceStateTargetID == DAC960_MaxTargets)
+	    {
+	      Controller->DeviceStateChannel++;
+	      Controller->DeviceStateTargetID = 0;
+	    }
+	}
+      else if (CommandOpcode == DAC960_GetRebuildProgress)
+	{
+	  unsigned int LogicalDriveNumber =
+	    Controller->RebuildProgress.LogicalDriveNumber;
+	  unsigned int LogicalDriveSize =
+	    Controller->RebuildProgress.LogicalDriveSize;
+	  unsigned int BlocksCompleted =
+	    LogicalDriveSize - Controller->RebuildProgress.RemainingBlocks;
+	  switch (CommandStatus)
+	    {
+	    case DAC960_NormalCompletion:
+	      DAC960_Critical("REBUILD IN PROGRESS: "
+			      "Logical Drive %d (/dev/rd/c%dd%d) "
+			      "%d%% completed\n",
+			      Controller, LogicalDriveNumber,
+			      Controller->ControllerNumber,
+			      LogicalDriveNumber,
+			      (100 * (BlocksCompleted >> 7))
+			      / (LogicalDriveSize >> 7));
+	      break;
+	    case DAC960_RebuildFailed_LogicalDriveFailure:
+	      DAC960_Critical("REBUILD FAILED due to "
+			      "LOGICAL DRIVE FAILURE\n", Controller);
+	      break;
+	    case DAC960_RebuildFailed_BadBlocksOnOther:
+	      DAC960_Critical("REBUILD FAILED due to "
+			      "BAD BLOCKS ON OTHER DRIVES\n", Controller);
+	      break;
+	    case DAC960_RebuildFailed_NewDriveFailed:
+	      DAC960_Critical("REBUILD FAILED due to "
+			      "FAILURE OF DRIVE BEING REBUILT\n", Controller);
+	      break;
+	    case DAC960_RebuildSuccessful:
+	      DAC960_Critical("REBUILD COMPLETED SUCCESSFULLY\n", Controller);
+	      break;
+	    case DAC960_NoRebuildOrCheckInProgress:
+	      break;
+	    }
+	  Controller->RebuildLastReportTime = jiffies;
+	}
+      if (Controller->NeedLogicalDriveInformation &&
+	  NewCriticalLogicalDriveCount >= OldCriticalLogicalDriveCount)
+	{
+	  Controller->NeedLogicalDriveInformation = false;
+	  Command->CommandMailbox.Type3.CommandOpcode =
+	    DAC960_GetLogicalDriveInformation;
+	  Command->CommandMailbox.Type3.BusAddress =
+	    Virtual_to_Bus(
+	      &Controller->LogicalDriveInformation
+			   [Controller->LogicalDriveInformationIndex ^ 1]);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      if (Controller->NewEventLogSequenceNumber
+	  - Controller->OldEventLogSequenceNumber > 0)
+	{
+	  Command->CommandMailbox.Type3E.CommandOpcode =
+	    DAC960_PerformEventLogOperation;
+	  Command->CommandMailbox.Type3E.OperationType =
+	    DAC960_GetEventLogEntry;
+	  Command->CommandMailbox.Type3E.OperationQualifier = 1;
+	  Command->CommandMailbox.Type3E.SequenceNumber =
+	    Controller->OldEventLogSequenceNumber;
+	  Command->CommandMailbox.Type3E.BusAddress =
+	    Virtual_to_Bus(&Controller->EventLogEntry);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      if (Controller->NeedDeviceStateInformation)
+	{
+	  while (Controller->DeviceStateChannel < Controller->Channels)
+	    {
+	      DAC960_DeviceState_T *OldDeviceState =
+		&Controller->DeviceState[Controller->DeviceStateIndex]
+					[Controller->DeviceStateChannel]
+					[Controller->DeviceStateTargetID];
+	      if (OldDeviceState->Present &&
+		  OldDeviceState->DeviceType == DAC960_DiskType)
+		{
+		  Command->CommandMailbox.Type3D.CommandOpcode =
+		    DAC960_GetDeviceState;
+		  Command->CommandMailbox.Type3D.Channel =
+		    Controller->DeviceStateChannel;
+		  Command->CommandMailbox.Type3D.TargetID =
+		    Controller->DeviceStateTargetID;
+		  Command->CommandMailbox.Type3D.BusAddress =
+		    Virtual_to_Bus(&Controller->DeviceState
+				      [Controller->DeviceStateIndex ^ 1]
+				      [Controller->DeviceStateChannel]
+				      [Controller->DeviceStateTargetID]);
+		  DAC960_QueueCommand(Command);
+		  return;
+		}
+	      if (++Controller->DeviceStateTargetID == DAC960_MaxTargets)
+		{
+		  Controller->DeviceStateChannel++;
+		  Controller->DeviceStateTargetID = 0;
+		}
+	    }
+	  Controller->NeedDeviceStateInformation = false;
+	  Controller->DeviceStateIndex ^= 1;
+	}
+      if (Controller->NeedRebuildProgress)
+	{
+	  Controller->NeedRebuildProgress = false;
+	  Command->CommandMailbox.Type3.CommandOpcode =
+	    DAC960_GetRebuildProgress;
+	  Command->CommandMailbox.Type3.BusAddress =
+	    Virtual_to_Bus(&Controller->RebuildProgress);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      if (Controller->NeedLogicalDriveInformation &&
+	  NewCriticalLogicalDriveCount < OldCriticalLogicalDriveCount)
+	{
+	  Controller->NeedLogicalDriveInformation = false;
+	  Command->CommandMailbox.Type3.CommandOpcode =
+	    DAC960_GetLogicalDriveInformation;
+	  Command->CommandMailbox.Type3.BusAddress =
+	    Virtual_to_Bus(
+	      &Controller->LogicalDriveInformation
+			   [Controller->LogicalDriveInformationIndex ^ 1]);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      Controller->MonitoringTimer.expires =
+	jiffies + DAC960_MonitoringTimerInterval;
+      add_timer(&Controller->MonitoringTimer);
+    }
+  else if (CommandType == DAC960_ImmediateCommand)
+    {
+      up(Command->Semaphore);
+      Command->Semaphore = NULL;
+      return;
+    }
+  else panic("DAC960: Unknown Command Type %d\n", CommandType);
+  /*
+    Queue a Monitoring Command to the Controller using the just completed
+    Command if one was deferred previously due to lack of a free Command when
+    the Monitoring Timer Function was called.
+  */
+  if (Controller->MonitoringCommandDeferred)
+    {
+      Controller->MonitoringCommandDeferred = false;
+      DAC960_QueueMonitoringCommand(Command);
+      return;
+    }
+  /*
+    Attempt to remove a new I/O Request from the Controller's I/O Request
+    Queue and queue it to the Controller using the just completed Command.
+    If there is no I/O Request to be queued, deallocate the Command.
+  */
+  if (!DAC960_ProcessRequest(Controller, Command))
+    DAC960_DeallocateCommand(Command);
+}
+
+
+/*
+  DAC960_InterruptHandler handles hardware interrupts from DAC960 Controllers.
+*/
+
+static void DAC960_InterruptHandler(int IRQ_Channel,
+				    void *DeviceIdentifier,
+				    Registers_T *InterruptRegisters)
+{
+  DAC960_Controller_T *Controller = (DAC960_Controller_T *) DeviceIdentifier;
+  void *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_V4_StatusMailbox_T *NextStatusMailbox;
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockIH(Controller, &ProcessorFlags);
+  /*
+    Process Hardware Interrupts for Controller.
+  */
+  switch (Controller->ControllerType)
+    {
+    case DAC960_V4_Controller:
+      DAC960_V4_AcknowledgeInterrupt(ControllerBaseAddress);
+      NextStatusMailbox = Controller->NextStatusMailbox;
+      while (NextStatusMailbox->Fields.Valid)
+	{
+	  DAC960_CommandIdentifier_T CommandIdentifier =
+	    NextStatusMailbox->Fields.CommandIdentifier;
+	  DAC960_Command_T *Command = &Controller->Commands[CommandIdentifier];
+	  Command->CommandStatus = NextStatusMailbox->Fields.CommandStatus;
+	  NextStatusMailbox->Word = 0;
+	  if (++NextStatusMailbox > Controller->LastStatusMailbox)
+	    NextStatusMailbox = Controller->FirstStatusMailbox;
+	  DAC960_ProcessCompletedCommand(Command);
+	}
+      Controller->NextStatusMailbox = NextStatusMailbox;
+      break;
+    case DAC960_V3_Controller:
+      while (DAC960_V3_StatusAvailableP(ControllerBaseAddress))
+	{
+	  DAC960_CommandIdentifier_T CommandIdentifier =
+	    DAC960_V3_ReadStatusCommandIdentifier(ControllerBaseAddress);
+	  DAC960_Command_T *Command = &Controller->Commands[CommandIdentifier];
+	  Command->CommandStatus =
+	    DAC960_V3_ReadStatusRegister(ControllerBaseAddress);
+	  DAC960_V3_AcknowledgeInterrupt(ControllerBaseAddress);
+	  DAC960_V3_AcknowledgeStatus(ControllerBaseAddress);
+	  DAC960_ProcessCompletedCommand(Command);
+	}
+      break;
+    }
+  /*
+    Attempt to remove additional I/O Requests from the Controller's
+    I/O Request Queue and queue them to the Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockIH(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_QueueMonitoringCommand queues a Monitoring Command to Controller.
+*/
+
+static void DAC960_QueueMonitoringCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+  DAC960_ClearCommand(Command);
+  Command->CommandType = DAC960_MonitoringCommand;
+  CommandMailbox->Type3.CommandOpcode = DAC960_Enquiry;
+  CommandMailbox->Type3.BusAddress =
+    Virtual_to_Bus(&Controller->Enquiry[Controller->EnquiryIndex ^ 1]);
+  DAC960_QueueCommand(Command);
+}
+
+
+/*
+  DAC960_MonitoringTimerFunction is the timer function for monitoring
+  the status of DAC960 Controllers.
+*/
+
+static void DAC960_MonitoringTimerFunction(unsigned long TimerData)
+{
+  DAC960_Controller_T *Controller = (DAC960_Controller_T *) TimerData;
+  DAC960_Command_T *Command;
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
+  /*
+    Queue a Status Monitoring Command for Controller;
+  */
+  Command = DAC960_AllocateCommand(Controller);
+  if (Command != NULL)
+    DAC960_QueueMonitoringCommand(Command);
+  else Controller->MonitoringCommandDeferred = true;
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_Open is the Device Open Function for the DAC960 Driver.
+*/
+
+static int DAC960_Open(Inode_T *Inode, File_T *File)
+{
+  int ControllerNumber = DAC960_ControllerNumber(Inode->i_rdev);
+  int LogicalDriveNumber = DAC960_LogicalDriveNumber(Inode->i_rdev);
+  DAC960_Controller_T *Controller;
+  if (ControllerNumber < 0 || ControllerNumber > DAC960_ControllerCount - 1)
+    return -ENXIO;
+  Controller = DAC960_Controllers[ControllerNumber];
+  if (Controller == NULL ||
+      LogicalDriveNumber > Controller->LogicalDriveCount - 1)
+    return -ENXIO;
+  if (Controller->LogicalDriveInformation
+		  [Controller->LogicalDriveInformationIndex]
+		  [LogicalDriveNumber] .LogicalDriveState
+      == DAC960_LogicalDrive_Offline)
+    return -ENXIO;
+  if (Controller->GenericDiskInfo.sizes[MINOR(Inode->i_rdev)] == 0)
+    return -ENXIO;
+  /*
+    Increment Controller and Logical Drive Usage Counts.
+  */
+  Controller->ControllerUsageCount++;
+  Controller->LogicalDriveUsageCount[LogicalDriveNumber]++;
+  return 0;
+}
+
+
+/*
+  DAC960_Release is the Device Release Function for the DAC960 Driver.
+*/
+
+static void DAC960_Release(Inode_T *Inode, File_T *File)
+{
+  int ControllerNumber = DAC960_ControllerNumber(Inode->i_rdev);
+  int LogicalDriveNumber = DAC960_LogicalDriveNumber(Inode->i_rdev);
+  DAC960_Controller_T *Controller = DAC960_Controllers[ControllerNumber];
+  /*
+    Force any buffered data to be written.
+  */
+  fsync_dev(Inode->i_rdev);
+  /*
+    Decrement the Logical Drive and Controller Usage Counts.
+  */
+  Controller->LogicalDriveUsageCount[LogicalDriveNumber]--;
+  Controller->ControllerUsageCount--;
+}
+
+
+/*
+  DAC960_Ioctl is the Device Ioctl Function for the DAC960 Driver.
+*/
+
+static int DAC960_Ioctl(Inode_T *Inode, File_T *File,
+			unsigned int Request, unsigned long Argument)
+{
+  int ControllerNumber = DAC960_ControllerNumber(Inode->i_rdev);
+  int LogicalDriveNumber = DAC960_LogicalDriveNumber(Inode->i_rdev);
+  int PartitionNumber, ErrorCode;
+  unsigned short Cylinders;
+  DiskGeometry_T *Geometry;
+  DAC960_Controller_T *Controller;
+  if (ControllerNumber < 0 || ControllerNumber > DAC960_ControllerCount - 1)
+    return -ENXIO;
+  Controller = DAC960_Controllers[ControllerNumber];
+  if (Controller == NULL ||
+      LogicalDriveNumber > Controller->LogicalDriveCount - 1)
+    return -ENXIO;
+  switch (Request)
+    {
+    case HDIO_GETGEO:
+      /* Get BIOS Disk Geometry. */
+      Geometry = (DiskGeometry_T *) Argument;
+      if (Geometry == NULL) return -EINVAL;
+      ErrorCode = verify_area(VERIFY_WRITE, Geometry, sizeof(DiskGeometry_T));
+      if (ErrorCode != 0) return ErrorCode;
+      Cylinders =
+	Controller->LogicalDriveInformation
+		    [Controller->LogicalDriveInformationIndex]
+		    [LogicalDriveNumber].LogicalDriveSize
+	/ (Controller->GeometryTranslationHeads *
+	   Controller->GeometryTranslationSectors);
+      put_user(Controller->GeometryTranslationHeads, &Geometry->heads);
+      put_user(Controller->GeometryTranslationSectors, &Geometry->sectors);
+      put_user(Cylinders, &Geometry->cylinders);
+      put_user(Controller->GenericDiskInfo.part[MINOR(Inode->i_rdev)]
+					  .start_sect, &Geometry->start);
+      return 0;
+    case BLKGETSIZE:
+      /* Get Device Size. */
+      if ((long *) Argument == NULL) return -EINVAL;
+      ErrorCode = verify_area(VERIFY_WRITE, (long *) Argument, sizeof(long));
+      if (ErrorCode != 0) return ErrorCode;
+      put_user(Controller->GenericDiskInfo.part[MINOR(Inode->i_rdev)].nr_sects,
+	       (long *) Argument);
+      return 0;
+    case BLKRAGET:
+      /* Get Read-Ahead. */
+      if ((int *) Argument == NULL) return -EINVAL;
+      ErrorCode = verify_area(VERIFY_WRITE, (int *) Argument, sizeof(int));
+      if (ErrorCode != 0) return ErrorCode;
+      put_user(read_ahead[MAJOR(Inode->i_rdev)], (int *) Argument);
+      return 0;
+    case BLKRASET:
+      /* Set Read-Ahead. */
+      if (!suser()) return -EACCES;
+      if (Argument > 256) return -EINVAL;
+      read_ahead[MAJOR(Inode->i_rdev)] = Argument;
+      return 0;
+    case BLKFLSBUF:
+      /* Flush Buffers. */
+      if (!suser()) return -EACCES;
+      fsync_dev(Inode->i_rdev);
+      invalidate_buffers(Inode->i_rdev);
+      return 0;
+    case BLKRRPART:
+      /* Re-Read Partition Table. */
+      if (!suser()) return -EACCES;
+      if (Controller->LogicalDriveUsageCount[LogicalDriveNumber] > 1)
+	return -EBUSY;
+      for (PartitionNumber = 0;
+	   PartitionNumber < DAC960_MaxPartitions;
+	   PartitionNumber++)
+	{
+	  KernelDevice_T Device = DAC960_KernelDevice(ControllerNumber,
+						      LogicalDriveNumber,
+						      PartitionNumber);
+	  int MinorNumber = DAC960_MinorNumber(LogicalDriveNumber,
+					       PartitionNumber);
+	  if (Controller->GenericDiskInfo.part[MinorNumber].nr_sects == 0)
+	    continue;
+	  /*
+	    Flush all changes and invalidate buffered state.
+	  */
+	  sync_dev(Device);
+	  invalidate_inodes(Device);
+	  invalidate_buffers(Device);
+	  /*
+	    Clear existing partition sizes.
+	  */
+	  if (PartitionNumber > 0)
+	    {
+	      Controller->GenericDiskInfo.part[MinorNumber].start_sect = 0;
+	      Controller->GenericDiskInfo.part[MinorNumber].nr_sects = 0;
+	    }
+	  /*
+	    Reset the Block Size so that the partition table can be read.
+	  */
+	  set_blocksize(Device, BLOCK_SIZE);
+	}
+      resetup_one_dev(&Controller->GenericDiskInfo, LogicalDriveNumber);
+      return 0;
+    }
+  return -EINVAL;
+}
+
+
+/*
+  DAC960_GenericDiskInit is the Generic Disk Information Initialization
+  Function for the DAC960 Driver.
+*/
+
+static void DAC960_InitializeGenericDiskInfo(GenericDiskInfo_T *GenericDiskInfo)
+{
+  DAC960_Controller_T *Controller =
+    (DAC960_Controller_T *) GenericDiskInfo->real_devices;
+  DAC960_LogicalDriveInformation_T *LogicalDriveInformation =
+    Controller->LogicalDriveInformation
+		[Controller->LogicalDriveInformationIndex];
+  int LogicalDriveNumber;
+  for (LogicalDriveNumber = 0;
+       LogicalDriveNumber < Controller->LogicalDriveCount;
+       LogicalDriveNumber++)
+    GenericDiskInfo->part[DAC960_MinorNumber(LogicalDriveNumber, 0)].nr_sects =
+      LogicalDriveInformation[LogicalDriveNumber].LogicalDriveSize;
+}
+
+
+/*
+  DAC960_Message prints Driver Messages.
+*/
+
+static void DAC960_Message(DAC960_MessageLevel_T MessageLevel,
+			   char *Format,
+			   DAC960_Controller_T *Controller,
+			   ...)
+{
+  static char Buffer[DAC960_LineBufferSize];
+  static boolean BeginningOfLine = true;
+  va_list Arguments;
+  int Length = 0;
+  va_start(Arguments, Controller);
+  Length = vsprintf(Buffer, Format, Arguments);
+  va_end(Arguments);
+  if (MessageLevel == DAC960_AnnounceLevel)
+    {
+      static int AnnouncementLines = 0;
+      strcpy(&Controller->MessageBuffer[Controller->MessageBufferLength],
+	     Buffer);
+      Controller->MessageBufferLength += Length;
+      if (++AnnouncementLines <= 2)
+	printk("%sDAC960: %s", DAC960_MessageLevelMap[MessageLevel], Buffer);
+    }
+  else if (MessageLevel == DAC960_InfoLevel)
+    {
+      strcpy(&Controller->MessageBuffer[Controller->MessageBufferLength],
+	     Buffer);
+      Controller->MessageBufferLength += Length;
+      if (BeginningOfLine)
+	{
+	  if (Buffer[0] != '\n' || Length > 1)
+	    printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel],
+		   Controller->ControllerNumber, Buffer);
+	}
+      else printk("%s", Buffer);
+    }
+  else
+    {
+      if (BeginningOfLine)
+	printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel],
+	       Controller->ControllerNumber, Buffer);
+      else printk("%s", Buffer);
+    }
+  BeginningOfLine = (Buffer[Length-1] == '\n');
+}
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/DAC960.h linux/drivers/block/DAC960.h
--- linux.vanilla/drivers/block/DAC960.h	Thu Jan  1 01:00:00 1970
+++ linux/drivers/block/DAC960.h	Mon Nov 30 07:59:59 1998
@@ -0,0 +1,1565 @@
+/*
+
+  Linux Driver for Mylex DAC960 PCI RAID Controllers
+
+  Copyright 1998 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+  This program is free software; you may redistribute and/or modify it under
+  the terms of the GNU General Public License Version 2 as published by the
+  Free Software Foundation.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
+  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+  for complete details.
+
+  The author respectfully requests that any modifications to this software be
+  sent directly to him for evaluation and testing.
+
+*/
+
+
+/*
+  DAC960_DriverVersion protects the private portion of this file.
+*/
+
+#ifdef DAC960_DriverVersion
+
+
+/*
+  Define the maximum number of DAC960 Controllers supported by this driver.
+*/
+
+#define DAC960_MaxControllers			8
+
+
+/*
+  Define the maximum number of Controller Channels supported by this driver.
+*/
+
+#define DAC960_MaxChannels			3
+
+
+/*
+  Define the maximum number of Targets per Channel supported by this driver.
+*/
+
+#define DAC960_MaxTargets			16
+
+
+/*
+  Define the maximum number of Logical Drives supported by any DAC960 model.
+*/
+
+#define DAC960_MaxLogicalDrives			32
+
+
+/*
+  Define the maximum number of Partitions allowed for each Logical Drive.
+*/
+
+#define DAC960_MaxPartitions			8
+#define DAC960_MaxPartitionsBits		3
+
+
+/*
+  Define the maximum Driver Queue Depth and Controller Queue Depth supported
+  by any DAC960 model.
+*/
+
+#define DAC960_MaxDriverQueueDepth		127
+#define DAC960_MaxControllerQueueDepth		128
+
+
+/*
+  Define the maximum number of Scatter/Gather Segments supported by any
+  DAC960 model.
+*/
+
+#define DAC960_MaxScatterGatherSegments		33
+
+
+/*
+  Define the DAC960 Controller Monitoring Timer Interval.
+*/
+
+#define DAC960_MonitoringTimerInterval		(7 * HZ)
+
+
+/*
+  Define the DAC960 Controller Secondary Monitoring Interval.
+*/
+
+#define DAC960_SecondaryMonitoringInterval	(60 * HZ)
+
+
+/*
+  Define the DAC960 Controller Rebuild Status Reporting Interval.
+*/
+
+#define DAC960_RebuildStatusReportingInterval	(60 * HZ)
+
+
+/*
+  Define the number of Command Mailboxes and Status Mailboxes used by the
+  V4 Memory Mailbox Interface.
+*/
+
+#define DAC960_CommandMailboxCount		256
+#define DAC960_StatusMailboxCount		1024
+
+
+/*
+  Define macros to extract the Controller Number, Logical Drive Number, and
+  Partition Number from a Kernel Device, and to construct a Major Number, Minor
+  Number, and Kernel Device from the Controller Number, Logical Drive Number,
+  and Partition Number.  There is one Major Number assigned to each Controller.
+  The associated Minor Number is divided into the Logical Drive Number and
+  Partition Number.
+*/
+
+#define DAC960_ControllerNumber(Device) \
+  (MAJOR(Device) - DAC960_MAJOR)
+
+#define DAC960_LogicalDriveNumber(Device) \
+  (MINOR(Device) >> DAC960_MaxPartitionsBits)
+
+#define DAC960_PartitionNumber(Device) \
+  (MINOR(Device) & (DAC960_MaxPartitions - 1))
+
+#define DAC960_MajorNumber(ControllerNumber) \
+  (DAC960_MAJOR + (ControllerNumber))
+
+#define DAC960_MinorNumber(LogicalDriveNumber, PartitionNumber) \
+   (((LogicalDriveNumber) << DAC960_MaxPartitionsBits) | (PartitionNumber))
+
+#define DAC960_MinorCount			(DAC960_MaxLogicalDrives \
+						 * DAC960_MaxPartitions)
+
+#define DAC960_KernelDevice(ControllerNumber,				       \
+			    LogicalDriveNumber,				       \
+			    PartitionNumber)				       \
+   MKDEV(DAC960_MajorNumber(ControllerNumber),				       \
+	 DAC960_MinorNumber(LogicalDriveNumber, PartitionNumber))
+
+
+/*
+  Define the DAC960 Controller fixed Block Size and Block Size Bits.
+*/
+
+#define DAC960_BlockSize			512
+#define DAC960_BlockSizeBits			9
+
+
+/*
+  Define the Controller Line and Message Buffer Sizes.
+*/
+
+#define DAC960_LineBufferSize			100
+#define DAC960_MessageBufferSize		2048
+
+
+/*
+  Define the Driver Message Levels.
+*/
+
+typedef enum DAC960_MessageLevel
+{
+  DAC960_AnnounceLevel =			0,
+  DAC960_InfoLevel =				1,
+  DAC960_NoticeLevel =				2,
+  DAC960_WarningLevel =				3,
+  DAC960_ErrorLevel =				4,
+  DAC960_CriticalLevel =			5
+}
+DAC960_MessageLevel_T;
+
+static char
+  *DAC960_MessageLevelMap[] =
+    { KERN_NOTICE, KERN_NOTICE, KERN_NOTICE,
+      KERN_WARNING, KERN_ERR, KERN_CRIT };
+
+
+/*
+  Define Driver Message macros.
+*/
+
+#define DAC960_Announce(Format, Arguments...) \
+  DAC960_Message(DAC960_AnnounceLevel, Format, ##Arguments)
+
+#define DAC960_Info(Format, Arguments...) \
+  DAC960_Message(DAC960_InfoLevel, Format, ##Arguments)
+
+#define DAC960_Notice(Format, Arguments...) \
+  DAC960_Message(DAC960_NoticeLevel, Format, ##Arguments)
+
+#define DAC960_Warning(Format, Arguments...) \
+  DAC960_Message(DAC960_WarningLevel, Format, ##Arguments)
+
+#define DAC960_Error(Format, Arguments...) \
+  DAC960_Message(DAC960_ErrorLevel, Format, ##Arguments)
+
+#define DAC960_Critical(Format, Arguments...) \
+  DAC960_Message(DAC960_CriticalLevel, Format, ##Arguments)
+
+
+/*
+  Define the types of DAC960 Controllers that are supported.
+*/
+
+typedef enum
+{
+  DAC960_V4_Controller =			1,	/* DAC960PTL/PJ/PG */
+  DAC960_V3_Controller =			2	/* DAC960PU/PD/PL */
+}
+DAC960_ControllerType_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 DAC960_IO_Address_T;
+
+
+/*
+  Define a 32 bit PCI Bus Address data type.
+*/
+
+typedef unsigned int DAC960_PCI_Address_T;
+
+
+/*
+  Define a 32 bit Bus Address data type.
+*/
+
+typedef unsigned int DAC960_BusAddress_T;
+
+
+/*
+  Define a 32 bit Byte Count data type.
+*/
+
+typedef unsigned int DAC960_ByteCount_T;
+
+
+/*
+  Define types for some of the structures that interface with the rest
+  of the Linux Kernel and I/O Subsystem.
+*/
+
+typedef struct buffer_head BufferHeader_T;
+typedef struct file File_T;
+typedef struct file_operations FileOperations_T;
+typedef struct gendisk GenericDiskInfo_T;
+typedef struct hd_geometry DiskGeometry_T;
+typedef struct hd_struct DiskPartition_T;
+typedef struct inode Inode_T;
+typedef kdev_t KernelDevice_T;
+typedef unsigned long ProcessorFlags_T;
+typedef struct pt_regs Registers_T;
+typedef struct request IO_Request_T;
+typedef struct semaphore Semaphore_T;
+typedef struct timer_list Timer_T;
+
+
+/*
+  Define the DAC960 V4 Controller Interface Register Offsets.
+*/
+
+#define DAC960_V4_RegisterWindowSize		0x2000
+
+typedef enum
+{
+  DAC960_V4_InboundDoorBellRegisterOffset =	0x0020,
+  DAC960_V4_OutboundDoorBellRegisterOffset =	0x002C,
+  DAC960_V4_InterruptMaskRegisterOffset =	0x0034,
+  DAC960_V4_CommandOpcodeRegisterOffset =	0x1000,
+  DAC960_V4_CommandIdentifierRegisterOffset =	0x1001,
+  DAC960_V4_MailboxRegister2Offset =		0x1002,
+  DAC960_V4_MailboxRegister3Offset =		0x1003,
+  DAC960_V4_MailboxRegister4Offset =		0x1004,
+  DAC960_V4_MailboxRegister5Offset =		0x1005,
+  DAC960_V4_MailboxRegister6Offset =		0x1006,
+  DAC960_V4_MailboxRegister7Offset =		0x1007,
+  DAC960_V4_MailboxRegister8Offset =		0x1008,
+  DAC960_V4_MailboxRegister9Offset =		0x1009,
+  DAC960_V4_MailboxRegister10Offset =		0x100A,
+  DAC960_V4_MailboxRegister11Offset =		0x100B,
+  DAC960_V4_MailboxRegister12Offset =		0x100C,
+  DAC960_V4_StatusCommandIdentifierRegOffset =	0x1018,
+  DAC960_V4_StatusRegisterOffset =		0x101A
+}
+DAC960_V4_RegisterOffsets_T;
+
+
+/*
+  Define the structure of the DAC960 V4 Inbound Door Bell Register.
+*/
+
+typedef union DAC960_V4_InboundDoorBellRegister
+{
+  unsigned int All;
+  struct {
+    boolean NewCommand:1;				/* Bit 0 */
+    boolean AcknowledgeStatus:1;			/* Bit 1 */
+    boolean SoftReset:1;				/* Bit 2 */
+    unsigned int :29;					/* Bits 3-31 */
+  } Write;
+  struct {
+    boolean MailboxFull:1;				/* Bit 0 */
+    unsigned int :31;					/* Bits 1-31 */
+  } Read;
+}
+DAC960_V4_InboundDoorBellRegister_T;
+
+
+/*
+  Define the structure of the DAC960 V4 Outbound Door Bell Register.
+*/
+
+typedef union DAC960_V4_OutboundDoorBellRegister
+{
+  unsigned int All;
+  struct {
+    boolean AcknowledgeInterrupt:1;			/* Bit 0 */
+    unsigned int :31;					/* Bits 1-31 */
+  } Write;
+  struct {
+    boolean StatusAvailable:1;				/* Bit 0 */
+    unsigned int :31;					/* Bits 1-31 */
+  } Read;
+}
+DAC960_V4_OutboundDoorBellRegister_T;
+
+
+/*
+  Define the structure of the DAC960 V4 Interrupt Mask Register.
+*/
+
+typedef union DAC960_V4_InterruptMaskRegister
+{
+  unsigned int All;
+  struct {
+    unsigned int MessageUnitInterruptMask1:2;		/* Bits 0-1 */
+    boolean DisableInterrupts:1;			/* Bit 2 */
+    unsigned int MessageUnitInterruptMask2:5;		/* Bits 3-7 */
+    unsigned int Reserved0:24;				/* Bits 8-31 */
+  } Bits;
+}
+DAC960_V4_InterruptMaskRegister_T;
+
+
+/*
+  Define the DAC960 V3 Controller Interface Register Offsets.
+*/
+
+#define DAC960_V3_RegisterWindowSize		0x80
+
+typedef enum
+{
+  DAC960_V3_CommandOpcodeRegisterOffset =	0x00,
+  DAC960_V3_CommandIdentifierRegisterOffset =	0x01,
+  DAC960_V3_MailboxRegister2Offset =		0x02,
+  DAC960_V3_MailboxRegister3Offset =		0x03,
+  DAC960_V3_MailboxRegister4Offset =		0x04,
+  DAC960_V3_MailboxRegister5Offset =		0x05,
+  DAC960_V3_MailboxRegister6Offset =		0x06,
+  DAC960_V3_MailboxRegister7Offset =		0x07,
+  DAC960_V3_MailboxRegister8Offset =		0x08,
+  DAC960_V3_MailboxRegister9Offset =		0x09,
+  DAC960_V3_MailboxRegister10Offset =		0x0A,
+  DAC960_V3_MailboxRegister11Offset =		0x0B,
+  DAC960_V3_MailboxRegister12Offset =		0x0C,
+  DAC960_V3_StatusCommandIdentifierRegOffset =	0x0D,
+  DAC960_V3_StatusRegisterOffset =		0x0E,
+  DAC960_V3_InboundDoorBellRegisterOffset =	0x40,
+  DAC960_V3_OutboundDoorBellRegisterOffset =	0x41,
+  DAC960_V3_InterruptEnableRegisterOffset =	0x43
+}
+DAC960_V3_RegisterOffsets_T;
+
+
+/*
+  Define the structure of the DAC960 V3 Inbound Door Bell Register.
+*/
+
+typedef union DAC960_V3_InboundDoorBellRegister
+{
+  unsigned char All;
+  struct {
+    boolean NewCommand:1;				/* Bit 0 */
+    boolean AcknowledgeStatus:1;			/* Bit 1 */
+    unsigned char :1;					/* Bit 2 */
+    boolean SoftReset:1;				/* Bit 3 */
+    unsigned char :4;					/* Bits 4-7 */
+  } Write;
+  struct {
+    boolean MailboxFull:1;				/* Bit 0 */
+    unsigned char :7;					/* Bits 1-7 */
+  } Read;
+}
+DAC960_V3_InboundDoorBellRegister_T;
+
+
+/*
+  Define the structure of the DAC960 V3 Outbound Door Bell Register.
+*/
+
+typedef union DAC960_V3_OutboundDoorBellRegister
+{
+  unsigned char All;
+  struct {
+    boolean AcknowledgeInterrupt:1;			/* Bit 0 */
+    unsigned char :7;					/* Bits 1-7 */
+  } Write;
+  struct {
+    boolean StatusAvailable:1;				/* Bit 0 */
+    unsigned char :7;					/* Bits 1-7 */
+  } Read;
+}
+DAC960_V3_OutboundDoorBellRegister_T;
+
+
+/*
+  Define the structure of the DAC960 V3 Interrupt Enable Register.
+*/
+
+typedef union DAC960_V3_InterruptEnableRegister
+{
+  unsigned char All;
+  struct {
+    boolean EnableInterrupts:1;				/* Bit 0 */
+    unsigned char :7;					/* Bits 1-7 */
+  } Bits;
+}
+DAC960_V3_InterruptEnableRegister_T;
+
+
+/*
+  Define the DAC960 Command Identifier type.
+*/
+
+typedef unsigned char DAC960_CommandIdentifier_T;
+
+
+/*
+  Define the DAC960 Command Opcodes.
+*/
+
+typedef enum
+{
+  /* I/O Commands */
+  DAC960_ReadExtended =				0x33,
+  DAC960_WriteExtended =			0x34,
+  DAC960_ReadAheadExtended =			0x35,
+  DAC960_ReadExtendedWithScatterGather =	0xB3,
+  DAC960_WriteExtendedWithScatterGather =	0xB4,
+  DAC960_Read =					0x36,
+  DAC960_ReadWithOldScatterGather =		0xB6,
+  DAC960_Write =				0x37,
+  DAC960_WriteWithOldScatterGather =		0xB7,
+  DAC960_DCDB =					0x04,
+  DAC960_DCDBWithScatterGather =		0x84,
+  DAC960_Flush =				0x0A,
+  /* Controller Status Related Commands */
+  DAC960_Enquiry =				0x53,
+  DAC960_Enquiry2 =				0x1C,
+  DAC960_GetLogicalDriveElement =		0x55,
+  DAC960_GetLogicalDriveInformation =		0x19,
+  DAC960_IOPortRead =				0x39,
+  DAC960_IOPortWrite =				0x3A,
+  DAC960_GetSDStats =				0x3E,
+  DAC960_GetPDStats =				0x3F,
+  DAC960_PerformEventLogOperation =		0x72,
+  /* Device Related Commands */
+  DAC960_StartDevice =				0x10,
+  DAC960_GetDeviceState =			0x50,
+  DAC960_StopChannel =				0x13,
+  DAC960_StartChannel =				0x12,
+  DAC960_ResetChannel =				0x1A,
+  /* Commands Associated with Data Consistency and Errors */
+  DAC960_Rebuild =				0x09,
+  DAC960_RebuildAsync =				0x16,
+  DAC960_CheckConsistency =			0x0F,
+  DAC960_CheckConsistencyAsync =		0x1E,
+  DAC960_RebuildStat =				0x0C,
+  DAC960_GetRebuildProgress =			0x27,
+  DAC960_RebuildControl =			0x1F,
+  DAC960_ReadBadBlockTable =			0x0B,
+  DAC960_ReadBadDataTable =			0x25,
+  DAC960_ClearBadDataTable =			0x26,
+  DAC960_GetErrorTable =			0x17,
+  DAC960_AddCapacityAsync =			0x2A,
+  /* Configuration Related Commands */
+  DAC960_ReadConfig2 =				0x3D,
+  DAC960_WriteConfig2 =				0x3C,
+  DAC960_ReadConfigurationOnDisk =		0x4A,
+  DAC960_WriteConfigurationOnDisk =		0x4B,
+  DAC960_ReadConfiguration =			0x4E,
+  DAC960_ReadBackupConfiguration =		0x4D,
+  DAC960_WriteConfiguration =			0x4F,
+  DAC960_AddConfiguration =			0x4C,
+  DAC960_ReadConfigurationLabel =		0x48,
+  DAC960_WriteConfigurationLabel =		0x49,
+  /* Firmware Upgrade Related Commands */
+  DAC960_LoadImage =				0x20,
+  DAC960_StoreImage =				0x21,
+  DAC960_ProgramImage =				0x22,
+  /* Diagnostic Commands */
+  DAC960_SetDiagnosticMode =			0x31,
+  DAC960_RunDiagnostic =			0x32,
+  /* Subsystem Service Commands */
+  DAC960_GetSubsystemData =			0x70,
+  DAC960_SetSubsystemParameters =		0x71
+}
+__attribute__ ((packed))
+DAC960_CommandOpcode_T;
+
+
+/*
+  Define the DAC960 Command Status Codes.
+*/
+
+#define DAC960_NormalCompletion			0x0000	/* Common */
+#define DAC960_CheckConditionReceived		0x0002	/* Common */
+#define DAC960_NoDeviceAtAddress		0x0102	/* Common */
+#define DAC960_InvalidDeviceAddress		0x0105	/* Common */
+#define DAC960_InvalidParameter			0x0105	/* Common */
+#define DAC960_IrrecoverableDataError		0x0001	/* I/O */
+#define DAC960_LogicalDriveNonexistentOrOffline	0x0002	/* I/O */
+#define DAC960_AccessBeyondEndOfLogicalDrive	0x0105	/* I/O */
+#define DAC960_BadDataEncountered		0x010C	/* I/O */
+#define DAC960_DeviceBusy			0x0008	/* DCDB */
+#define DAC960_DeviceNonresponsive		0x000E	/* DCDB */
+#define DAC960_CommandTerminatedAbnormally	0x000F	/* DCDB */
+#define DAC960_UnableToStartDevice		0x0002	/* Device */
+#define DAC960_InvalidChannelOrTarget		0x0105	/* Device */
+#define DAC960_ChannelBusy			0x0106	/* Device */
+#define DAC960_ChannelNotStopped		0x0002	/* Device */
+#define DAC960_AttemptToRebuildOnlineDrive	0x0002	/* Consistency */
+#define DAC960_RebuildBadBlocksEncountered	0x0003	/* Consistency */
+#define DAC960_NewDiskFailedDuringRebuild	0x0004	/* Consistency */
+#define DAC960_RebuildOrCheckAlreadyInProgress	0x0106	/* Consistency */
+#define DAC960_DependentDiskIsDead		0x0002	/* Consistency */
+#define DAC960_InconsistentBlocksFound		0x0003	/* Consistency */
+#define DAC960_InvalidOrNonredundantLogicalDrive 0x0105	/* Consistency */
+#define DAC960_NoRebuildOrCheckInProgress	0x0105	/* Consistency */
+#define DAC960_RebuildInProgress_DataValid	0x0000	/* Consistency */
+#define DAC960_RebuildFailed_LogicalDriveFailure 0x0002	/* Consistency */
+#define DAC960_RebuildFailed_BadBlocksOnOther	0x0003	/* Consistency */
+#define DAC960_RebuildFailed_NewDriveFailed	0x0004	/* Consistency */
+#define DAC960_RebuildSuccessful		0x0100	/* Consistency */
+#define DAC960_AddCapacityInProgress		0x0004	/* Consistency */
+#define DAC960_AddCapacityFailedOrSuspended	0x00F4	/* Consistency */
+#define DAC960_Config2ChecksumError		0x0002	/* Configuration */
+#define DAC960_ConfigurationSuspended		0x0106	/* Configuration */
+#define DAC960_FailedToConfigureNVRAM		0x0105	/* Configuration */
+#define DAC960_ConfigurationNotSavedStateChange	0x0106	/* Configuration */
+#define DAC960_SubsystemNotInstalled		0x0001	/* Subsystem */
+#define DAC960_SubsystemFailed			0x0002	/* Subsystem */
+#define DAC960_SubsystemBusy			0x0106	/* Subsystem */
+
+typedef unsigned short DAC960_CommandStatus_T;
+
+
+/*
+  Define the Enquiry reply structure.
+*/
+
+typedef struct DAC960_Enquiry
+{
+  unsigned char NumberOfLogicalDrives;			/* Byte 0 */
+  unsigned int :24;					/* Bytes 1-3 */
+  unsigned int LogicalDriveSizes[32];			/* Bytes 4-131 */
+  unsigned short FlashAge;				/* Bytes 132-133 */
+  struct {
+    boolean DeferredWriteError:1;			/* Byte 134 Bit 0 */
+    boolean BatteryLow:1;				/* Byte 134 Bit 1 */
+    unsigned char :6;					/* Byte 134 Bits 2-7 */
+  } StatusFlags;
+  unsigned char :8;					/* Byte 135 */
+  unsigned char MinorFirmwareVersion;			/* Byte 136 */
+  unsigned char MajorFirmwareVersion;			/* Byte 137 */
+  enum {
+    DAC960_NoStandbyRebuildOrCheckInProgress =			0x00,
+    DAC960_StandbyRebuildInProgress =				0x01,
+    DAC960_BackgroundRebuildInProgress =			0x02,
+    DAC960_BackgroundCheckInProgress =				0x03,
+    DAC960_StandbyRebuildCOmpletedWithError =			0xFF,
+    DAC960_BackgroundRebuildOrCheckFailed_DriveFailed =		0xF0,
+    DAC960_BackgroundRebuildOrCheckFailed_LogicalDriveFailed =	0xF1,
+    DAC960_BackgroundRebuildOrCheckFailed_OtherCauses =		0xF2,
+    DAC960_BackgroundRebuildOrCheckSuccessfullyTerminated =	0xF3
+  } __attribute__ ((packed)) RebuildFlag;		/* Byte 138 */
+  unsigned char MaxCommands;				/* Byte 139 */
+  unsigned char OfflineLogicalDriveCount;		/* Byte 140 */
+  unsigned char :8;					/* Byte 141 */
+  unsigned short EventLogSequenceNumber;		/* Bytes 142-143 */
+  unsigned char CriticalLogicalDriveCount;		/* Byte 144 */
+  unsigned int :24;					/* Bytes 145-147 */
+  unsigned char DeadDriveCount;				/* Byte 148 */
+  unsigned char :8;					/* Byte 149 */
+  unsigned char RebuildCount;				/* Byte 150 */
+  struct {
+    unsigned char :3;					/* Byte 151 Bits 0-2 */
+    boolean BatteryBackupUnitPresent:1;			/* Byte 151 Bit 3 */
+    unsigned char :3;					/* Byte 151 Bits 4-6 */
+    unsigned char :1;					/* Byte 151 Bit 7 */
+  } MiscFlags;
+  struct {
+    unsigned char TargetID;
+    unsigned char Channel;
+  } DeadDrives[21];					/* Bytes 152-194 */
+  unsigned char Reserved[62];				/* Bytes 195-255 */
+}
+__attribute__ ((packed))
+DAC960_Enquiry_T;
+
+
+/*
+  Define the Enquiry2 reply structure.
+*/
+
+typedef struct DAC960_Enquiry2
+{
+  struct {
+    enum {
+      DAC960_P_PD_PU =				0x01,
+      DAC960_PL =				0x02,
+      DAC960_PG =				0x10,
+      DAC960_PJ =				0x11,
+      DAC960_PTL_0 =				0x14,
+      DAC960_PTL_1 =				0x16
+    } __attribute__ ((packed)) SubModel;		/* Byte 0 */
+    unsigned char ActualChannels;			/* Byte 1 */
+    enum {
+      DAC960_FiveChannelBoard =			0x01,
+      DAC960_ThreeChannelBoard =		0x02,
+      DAC960_TwoChannelBoard =			0x03,
+      DAC960_ThreeChannelASIC_DAC =		0x04
+    } __attribute__ ((packed)) Model;			/* Byte 2 */
+    enum {
+      DAC960_EISA_Controller =			0x01,
+      DAC960_MicroChannel_Controller =		0x02,
+      DAC960_PCI_Controller =			0x03,
+      DAC960_SCSItoSCSI_Controller =		0x08
+    } __attribute__ ((packed)) ProductFamily;		/* Byte 3 */
+  } HardwareID;						/* Bytes 0-3 */
+  /* MajorVersion.MinorVersion-FirmwareType-TurnID */
+  struct {
+    unsigned char MajorVersion;				/* Byte 4 */
+    unsigned char MinorVersion;				/* Byte 5 */
+    unsigned char TurnID;				/* Byte 6 */
+    char FirmwareType;					/* Byte 7 */
+  } FirmwareID;						/* Bytes 4-7 */
+  unsigned char :8;					/* Byte 8 */
+  unsigned int :24;					/* Bytes 9-11 */
+  unsigned char ConfiguredChannels;			/* Byte 12 */
+  unsigned char ActualChannels;				/* Byte 13 */
+  unsigned char MaxTargets;				/* Byte 14 */
+  unsigned char MaxTags;				/* Byte 15 */
+  unsigned char MaxLogicalDrives;			/* Byte 16 */
+  unsigned char MaxArms;				/* Byte 17 */
+  unsigned char MaxSpans;				/* Byte 18 */
+  unsigned char :8;					/* Byte 19 */
+  unsigned int :32;					/* Bytes 20-23 */
+  unsigned int MemorySize;				/* Bytes 24-27 */
+  unsigned int CacheSize;				/* Bytes 28-31 */
+  unsigned int FlashMemorySize;				/* Bytes 32-35 */
+  unsigned int NonVolatileMemorySize;			/* Bytes 36-39 */
+  struct {
+    enum {
+      DAC960_DRAM =				0x00,
+      DAC960_EDO =				0x01
+    } __attribute__ ((packed)) RamType:3;		/* Byte 40 Bits 0-2 */
+    enum {
+      DAC960_None =				0x00,
+      DAC960_Parity =				0x01,
+      DAC960_ECC =				0x02
+    } __attribute__ ((packed)) ErrorCorrection:3;	/* Byte 40 Bits 3-5 */
+    boolean FastPageMode:1;				/* Byte 40 Bit 6 */
+    boolean LowPowerMemory:1;				/* Byte 40 Bit 7 */
+    unsigned char :8;					/* Bytes 41 */
+  } MemoryType;
+  unsigned short ClockSpeed;				/* Bytes 42-43 */
+  unsigned short MemorySpeed;				/* Bytes 44-45 */
+  unsigned short HardwareSpeed;				/* Bytes 46-47 */
+  unsigned int :32;					/* Bytes 48-51 */
+  unsigned int :32;					/* Bytes 52-55 */
+  unsigned char :8;					/* Byte 56 */
+  unsigned char :8;					/* Byte 57 */
+  unsigned short :16;					/* Bytes 58-59 */
+  unsigned short MaxCommands;				/* Bytes 60-61 */
+  unsigned short MaxScatterGatherEntries;		/* Bytes 62-63 */
+  unsigned short MaxDriveCommands;			/* Bytes 64-65 */
+  unsigned short MaxIODescriptors;			/* Bytes 66-67 */
+  unsigned short MaxCombinedSectors;			/* Bytes 68-69 */
+  unsigned char Latency;				/* Byte 70 */
+  unsigned char :8;					/* Byte 71 */
+  unsigned char SCSITimeout;				/* Byte 72 */
+  unsigned char :8;					/* Byte 73 */
+  unsigned short MinFreeLines;				/* Bytes 74-75 */
+  unsigned int :32;					/* Bytes 76-79 */
+  unsigned int :32;					/* Bytes 80-83 */
+  unsigned char RebuildRateConstant;			/* Byte 84 */
+  unsigned char :8;					/* Byte 85 */
+  unsigned char :8;					/* Byte 86 */
+  unsigned char :8;					/* Byte 87 */
+  unsigned int :32;					/* Bytes 88-91 */
+  unsigned int :32;					/* Bytes 92-95 */
+  unsigned short PhysicalDriveBlockSize;		/* Bytes 96-97 */
+  unsigned short LogicalDriveBlockSize;			/* Bytes 98-99 */
+  unsigned short MaxBlocksPerCommand;			/* Bytes 100-101 */
+  unsigned short BlockFactor;				/* Bytes 102-103 */
+  unsigned short CacheLineSize;				/* Bytes 104-105 */
+  struct {
+    enum {
+      DAC960_Narrow_8bit =			0x00,
+      DAC960_Wide_16bit =			0x01,
+      DAC960_Wide_32bit =			0x02
+    } __attribute__ ((packed)) BusWidth:2;		/* Byte 106 Bits 0-1 */
+    enum {
+      DAC960_Fast =				0x00,
+      DAC960_Ultra =				0x01,
+    } __attribute__ ((packed)) BusSpeed:2;		/* Byte 106 Bits 2-3 */
+    boolean Differential:1;				/* Byte 106 Bit 4 */
+    unsigned char :3;					/* Byte 106 Bits 5-7 */
+  } SCSICapability;
+  unsigned char :8;					/* Byte 107 */
+  unsigned int :32;					/* Bytes 108-111 */
+  unsigned short FirmwareBuildNumber;			/* Bytes 112-113 */
+  enum {
+    DAC960_AEMI =				0x01,
+    DAC960_OEM1 =				0x02,
+    DAC960_OEM2 =				0x04,
+    DAC960_OEM3 =				0x08,
+    DAC960_Conner =				0x10,
+    DAC960_SAFTE =				0x20
+  } __attribute__ ((packed)) FaultManagementType;	/* Byte 114 */
+  unsigned char :8;					/* Byte 115 */
+  struct {
+    boolean Clustering:1;				/* Byte 116 Bit 0 */
+    boolean MylexOnlineRAIDExpansion:1;			/* Byte 116 Bit 1 */
+    unsigned int :30;					/* Bytes 116-119 */
+  } FirmwareFeatures;
+  unsigned int :32;					/* Bytes 120-123 */
+  unsigned int :32;					/* Bytes 124-127 */
+}
+DAC960_Enquiry2_T;
+
+
+/*
+  Define the Get Logical Drive Information reply structure.
+*/
+
+typedef struct DAC960_LogicalDriveInformation
+{
+  unsigned int LogicalDriveSize;			/* Bytes 0-3 */
+  enum {
+    DAC960_LogicalDrive_Online =		0x03,
+    DAC960_LogicalDrive_Critical =		0x04,
+    DAC960_LogicalDrive_Offline =		0xFF
+  } __attribute__ ((packed)) LogicalDriveState;		/* Byte 4 */
+  unsigned char RAIDLevel:7;				/* Byte 5 Bits 0-6 */
+  boolean WriteBack:1;					/* Byte 5 Bit 7 */
+  unsigned int :16;					/* Bytes 6-7 */
+}
+DAC960_LogicalDriveInformation_T;
+
+
+/*
+  Define the Perform Event Log Operation Types.
+*/
+
+typedef enum
+{
+  DAC960_GetEventLogEntry =				0x00
+}
+__attribute__ ((packed))
+DAC960_PerformEventLogOpType_T;
+
+
+/*
+  Define the Get Event Log Entry reply structure.
+*/
+
+typedef struct DAC960_EventLogEntry
+{
+  unsigned char MessageType;				/* Byte 0 */
+  unsigned char MessageLength;				/* Byte 1 */
+  unsigned char TargetID:5;				/* Byte 2 Bits 0-4 */
+  unsigned char Channel:3;				/* Byte 2 Bits 5-7 */
+  unsigned char LogicalUnit:6;				/* Byte 3 Bits 0-5 */
+  unsigned char :2;					/* Byte 3 Bits 6-7 */
+  unsigned short SequenceNumber;			/* Bytes 4-5 */
+  unsigned char ErrorCode:7;				/* Byte 6 Bits 0-6 */
+  boolean Valid:1;					/* Byte 6 Bit 7 */
+  unsigned char SegmentNumber;				/* Byte 7 */
+  unsigned char SenseKey:4;				/* Byte 8 Bits 0-3 */
+  unsigned char :1;					/* Byte 8 Bit 4 */
+  boolean ILI:1;					/* Byte 8 Bit 5 */
+  boolean EOM:1;					/* Byte 8 Bit 6 */
+  boolean Filemark:1;					/* Byte 8 Bit 7 */
+  unsigned char Information[4];				/* Bytes 9-12 */
+  unsigned char AdditionalSenseLength;			/* Byte 13 */
+  unsigned char CommandSpecificInformation[4];		/* Bytes 14-17 */
+  unsigned char AdditionalSenseCode;			/* Byte 18 */
+  unsigned char AdditionalSenseCodeQualifier;		/* Byte 19 */
+  unsigned char Dummy[12];				/* Bytes 20-31 */
+}
+DAC960_EventLogEntry_T;
+
+#define DAC960_EventMessagesCount		13
+
+static char
+  *DAC960_EventMessages[DAC960_EventMessagesCount] =
+     { "killed because write recovery failed",
+       "killed because of SCSI bus reset failure",
+       "killed because of double check condition",
+       "killed because it was removed",
+       "killed because of gross error on SCSI chip",
+       "killed because of bad tag returned from drive",
+       "killed because of timeout on SCSI command",
+       "killed because of reset SCSI command issued from system",
+       "killed because busy or parity error count exceeded limit",
+       "killed because of 'kill drive' command from system",
+       "killed because of selection timeout",
+       "killed due to SCSI phase sequence error",
+       "killed due to unknown status" };
+
+
+/*
+  Define the Get Device State reply structure.
+*/
+
+typedef struct DAC960_DeviceState
+{
+  boolean Present:1;					/* Byte 0 Bit 0 */
+  unsigned char :7;					/* Byte 0 Bits 1-7 */
+  enum {
+    DAC960_OtherType =				0x00,
+    DAC960_DiskType =				0x01,
+    DAC960_SequentialType =			0x02,
+    DAC960_CDROM_or_WORM_Type =			0x03
+    } __attribute__ ((packed)) DeviceType:2;		/* Byte 1 Bits 0-1 */
+  boolean :1;						/* Byte 1 Bit 2 */
+  boolean Fast20:1;					/* Byte 1 Bit 3 */
+  boolean Sync:1;					/* Byte 1 Bit 4 */
+  boolean Fast:1;					/* Byte 1 Bit 5 */
+  boolean Wide:1;					/* Byte 1 Bit 6 */
+  boolean TaggedQueuingSupported:1;			/* Byte 1 Bit 7 */
+  enum {
+    DAC960_Device_Dead =			0x00,
+    DAC960_Device_WriteOnly =			0x02,
+    DAC960_Device_Online =			0x03,
+    DAC960_Device_Standby =			0x10
+  } __attribute__ ((packed)) DeviceState;		/* Byte 2 */
+  unsigned char :8;					/* Byte 3 */
+  unsigned char SynchronousMultiplier;			/* Byte 4 */
+  unsigned char SynchronousOffset:5;			/* Byte 5 Bits 0-4 */
+  unsigned char :3;					/* Byte 5 Bits 5-7 */
+  unsigned long DiskSize __attribute__ ((packed));	/* Bytes 6-9 */
+}
+DAC960_DeviceState_T;
+
+
+/*
+  Define the Get Rebuild Progress reply structure.
+*/
+
+typedef struct DAC960_RebuildProgress
+{
+  unsigned int LogicalDriveNumber;			/* Bytes 0-3 */
+  unsigned int LogicalDriveSize;			/* Bytes 4-7 */
+  unsigned int RemainingBlocks;				/* Bytes 8-11 */
+}
+DAC960_RebuildProgress_T;
+
+
+/*
+  Define the Config2 reply structure.
+*/
+
+typedef struct DAC960_Config2
+{
+  unsigned char :1;					/* Byte 0 Bit 0 */
+  boolean ActiveNegationEnabled:1;			/* Byte 0 Bit 1 */
+  unsigned char :5;					/* Byte 0 Bits 2-6 */
+  boolean NoRescanIfResetReceivedDuringScan:1;		/* Byte 0 Bit 7 */
+  boolean StorageWorksSupportEnabled:1;			/* Byte 1 Bit 0 */
+  boolean HewlettPackardSupportEnabled:1;		/* Byte 1 Bit 1 */
+  boolean NoDisconnectOnFirstCommand:1;			/* Byte 1 Bit 2 */
+  unsigned char :2;					/* Byte 1 Bits 3-4 */
+  boolean AEMI_ARM:1;					/* Byte 1 Bit 5 */
+  boolean AEMI_OFM:1;					/* Byte 1 Bit 6 */
+  unsigned char :1;					/* Byte 1 Bit 7 */
+  enum {
+    DAC960_OEMID_Mylex =			0x00,
+    DAC960_OEMID_IBM =				0x08,
+    DAC960_OEMID_HP =				0x0A,
+    DAC960_OEMID_DEC =				0x0C,
+    DAC960_OEMID_Siemens =			0x10,
+    DAC960_OEMID_Intel =			0x12
+  } __attribute__ ((packed)) OEMID;			/* Byte 2 */
+  unsigned char OEMModelNumber;				/* Byte 3 */
+  unsigned char PhysicalSector;				/* Byte 4 */
+  unsigned char LogicalSector;				/* Byte 5 */
+  unsigned char BlockFactor;				/* Byte 6 */
+  boolean ReadAheadEnabled:1;				/* Byte 7 Bit 0 */
+  boolean LowBIOSDelay:1;				/* Byte 7 Bit 1 */
+  unsigned char :2;					/* Byte 7 Bits 2-3 */
+  boolean ReassignRestrictedToOneSector:1;		/* Byte 7 Bit 4 */
+  unsigned char :1;					/* Byte 7 Bit 5 */
+  boolean ForceUnitAccessDuringWriteRecovery:1;		/* Byte 7 Bit 6 */
+  boolean EnableLeftSymmetricRAID5Algorithm:1;		/* Byte 7 Bit 7 */
+  unsigned char DefaultRebuildRate;			/* Byte 8 */
+  unsigned char :8;					/* Byte 9 */
+  unsigned char BlocksPerCacheLine;			/* Byte 10 */
+  unsigned char BlocksPerStripe;			/* Byte 11 */
+  struct {
+    enum {
+      DAC960_Async =				0x00,
+      DAC960_Sync_8MHz =			0x01,
+      DAC960_Sync_5MHz =			0x02,
+      DAC960_Sync_10or20MHz =			0x03	/* Bits 0-1 */
+    } __attribute__ ((packed)) Speed:2;
+    boolean Force8Bit:1;				/* Bit 2 */
+    boolean DisableFast20:1;				/* Bit 3 */
+    unsigned char :3;					/* Bits 4-6 */
+    boolean EnableTaggedQueuing:1;			/* Bit 7 */
+  } __attribute__ ((packed)) ChannelParameters[6];	/* Bytes 12-17 */
+  unsigned char SCSIInitiatorID;			/* Byte 18 */
+  unsigned char :8;					/* Byte 19 */
+  enum {
+    DAC960_StartupMode_ControllerSpinUp =	0x00,
+    DAC960_StartupMode_PowerOnSpinUp =		0x01
+  } __attribute__ ((packed)) StartupMode;		/* Byte 20 */
+  unsigned char SimultaneousDeviceSpinUpCount;		/* Byte 21 */
+  unsigned char SecondsDelayBetweenSpinUps;		/* Byte 22 */
+  unsigned char Reserved1[29];				/* Bytes 23-51 */
+  boolean BIOSDisabled:1;				/* Byte 52 Bit 0 */
+  boolean CDROMBootEnabled:1;				/* Byte 52 Bit 1 */
+  unsigned char :3;					/* Byte 52 Bits 2-4 */
+  enum {
+    DAC960_Geometry_128_32 =			0x00,
+    DAC960_Geometry_255_63 =			0x01,
+    DAC960_Geometry_Reserved1 =			0x02,
+    DAC960_Geometry_Reserved2 =			0x03
+  } __attribute__ ((packed)) DriveGeometry:2;		/* Byte 52 Bits 5-6 */
+  unsigned char :1;					/* Byte 52 Bit 7 */
+  unsigned char Reserved2[9];				/* Bytes 53-61 */
+  unsigned short Checksum;				/* Bytes 62-63 */
+}
+DAC960_Config2_T;
+
+
+/*
+  Define the Scatter/Gather List Type 1 32 Bit Address 32 Bit Byte Count
+  structure.
+*/
+
+typedef struct DAC960_ScatterGatherSegment
+{
+  DAC960_BusAddress_T SegmentDataPointer;		/* Bytes 0-3 */
+  DAC960_ByteCount_T SegmentByteCount;			/* Bytes 4-7 */
+}
+DAC960_ScatterGatherSegment_T;
+
+
+/*
+  Define the 13 Byte DAC960 Command Mailbox structure.  Bytes 13-15 are
+  not used.  The Command Mailbox structure is padded to 16 bytes for
+  efficient access.
+*/
+
+typedef union DAC960_CommandMailbox
+{
+  unsigned int Words[4];				/* Words 0-3 */
+  unsigned char Bytes[16];				/* Bytes 0-15 */
+  struct {
+    DAC960_CommandOpcode_T CommandOpcode;		/* Byte 0 */
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 1 */
+    unsigned char Dummy[14];				/* Bytes 2-15 */
+  } __attribute__ ((packed)) Common;
+  struct {
+    DAC960_CommandOpcode_T CommandOpcode;		/* Byte 0 */
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 1 */
+    unsigned char Dummy1[6];				/* Bytes 2-7 */
+    DAC960_BusAddress_T BusAddress;			/* Bytes 8-11 */
+    unsigned char Dummy2[4];				/* Bytes 12-15 */
+  } __attribute__ ((packed)) Type3;
+  struct {
+    DAC960_CommandOpcode_T CommandOpcode;		/* Byte 0 */
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 1 */
+    unsigned char Channel;				/* Byte 2 */
+    unsigned char TargetID;				/* Byte 3 */
+    unsigned char Dummy1[4];				/* Bytes 4-7 */
+    DAC960_BusAddress_T BusAddress;			/* Bytes 8-11 */
+    unsigned char Dummy2[4];				/* Bytes 12-15 */
+  } __attribute__ ((packed)) Type3D;
+  struct {
+    DAC960_CommandOpcode_T CommandOpcode;		/* Byte 0 */
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 1 */
+    DAC960_PerformEventLogOpType_T OperationType;	/* Byte 2 */
+    unsigned char OperationQualifier;			/* Byte 3 */
+    unsigned short SequenceNumber;			/* Bytes 4-5 */
+    unsigned char Dummy1[2];				/* Bytes 6-7 */
+    DAC960_BusAddress_T BusAddress;			/* Bytes 8-11 */
+    unsigned char Dummy2[4];				/* Bytes 12-15 */
+  } __attribute__ ((packed)) Type3E;
+  struct {
+    DAC960_CommandOpcode_T CommandOpcode;		/* Byte 0 */
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 1 */
+    struct {
+      unsigned short TransferLength:11;			/* Bytes 2-3 */
+      unsigned char LogicalDriveNumber:5;		/* Byte 3 Bits 3-7 */
+    } __attribute__ ((packed)) LD;
+    unsigned int LogicalBlockAddress;			/* Bytes 4-7 */
+    DAC960_BusAddress_T BusAddress;			/* Bytes 8-11 */
+    unsigned char ScatterGatherCount:6;			/* Byte 12 Bits 0-5 */
+    enum {
+      DAC960_ScatterGather_32BitAddress_32BitByteCount =	0x0,
+      DAC960_ScatterGather_32BitAddress_16BitByteCount =	0x1,
+      DAC960_ScatterGather_32BitByteCount_32BitAddress =	0x2,
+      DAC960_ScatterGather_16BitByteCount_32BitAddress =	0x3
+    } __attribute__ ((packed)) ScatterGatherType:2;	/* Byte 12 Bits 6-7 */
+    unsigned char Dummy[3];				/* Bytes 13-15 */
+  } __attribute__ ((packed)) Type5;
+  struct {
+    DAC960_CommandOpcode_T CommandOpcode;		/* Byte 0 */
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 1 */
+    unsigned char CommandOpcode2;			/* Byte 2 */
+    unsigned char :8;					/* Byte 3 */
+    DAC960_BusAddress_T CommandMailboxesBusAddress;	/* Bytes 4-7 */
+    DAC960_BusAddress_T StatusMailboxesBusAddress;	/* Bytes 8-11 */
+    unsigned char Dummy[4];				/* Bytes 12-15 */
+  } __attribute__ ((packed)) TypeX;
+}
+DAC960_CommandMailbox_T;
+
+
+/*
+  Define the DAC960 V4 Controller Command Mailbox structure.
+*/
+
+typedef DAC960_CommandMailbox_T DAC960_V4_CommandMailbox_T;
+
+
+/*
+  Define the DAC960 V4 Controller Status Mailbox structure.
+*/
+
+typedef union DAC960_V4_StatusMailbox
+{
+  unsigned int Word;					/* Bytes 0-3 */
+  struct {
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 0 */
+    unsigned char :7;					/* Byte 1 Bits 0-6 */
+    boolean Valid:1;					/* Byte 1 Bit 7 */
+    DAC960_CommandStatus_T CommandStatus;		/* Bytes 2-3 */
+  } Fields;
+}
+DAC960_V4_StatusMailbox_T;
+
+
+/*
+  Define the DAC960 Driver Command Types.
+*/
+
+typedef enum
+{
+  DAC960_ReadCommand =				1,
+  DAC960_WriteCommand =				2,
+  DAC960_ReadRetryCommand =			3,
+  DAC960_WriteRetryCommand =			4,
+  DAC960_MonitoringCommand =			5,
+  DAC960_ImmediateCommand =			6
+}
+DAC960_CommandType_T;
+
+
+/*
+  Define the DAC960 Driver Command structure.
+*/
+
+typedef struct DAC960_Command
+{
+  DAC960_CommandType_T CommandType;
+  DAC960_CommandMailbox_T CommandMailbox;
+  DAC960_CommandStatus_T CommandStatus;
+  struct DAC960_Controller *Controller;
+  struct DAC960_Command *Next;
+  Semaphore_T *Semaphore;
+  unsigned int LogicalDriveNumber;
+  unsigned int BlockNumber;
+  unsigned int BlockCount;
+  unsigned int SegmentCount;
+  BufferHeader_T *BufferHeader;
+  DAC960_ScatterGatherSegment_T
+    ScatterGatherList[DAC960_MaxScatterGatherSegments];
+}
+DAC960_Command_T;
+
+
+/*
+  Define the DAC960 Driver Controller structure.
+*/
+
+typedef struct DAC960_Controller
+{
+  void *BaseAddress;
+  void *MemoryMappedAddress;
+  DAC960_ControllerType_T ControllerType;
+  DAC960_IO_Address_T IO_Address;
+  DAC960_PCI_Address_T PCI_Address;
+  unsigned char ControllerNumber;
+  unsigned char ModelName[12];
+  unsigned char FullModelName[18];
+  unsigned char FirmwareVersion[14];
+  unsigned char Bus;
+  unsigned char Device;
+  unsigned char Function;
+  unsigned char IRQ_Channel;
+  unsigned char Channels;
+  unsigned char MemorySize;
+  unsigned char LogicalDriveCount;
+  unsigned char GeometryTranslationHeads;
+  unsigned char GeometryTranslationSectors;
+  unsigned short ControllerQueueDepth;
+  unsigned short DriverQueueDepth;
+  unsigned short MaxBlocksPerCommand;
+  unsigned short MaxScatterGatherSegments;
+  unsigned short StripeSize;
+  unsigned short SegmentSize;
+  unsigned short NewEventLogSequenceNumber;
+  unsigned short OldEventLogSequenceNumber;
+  unsigned short MessageBufferLength;
+  unsigned int ControllerUsageCount;
+  unsigned int EnquiryIndex;
+  unsigned int LogicalDriveInformationIndex;
+  unsigned int DeviceStateIndex;
+  unsigned int DeviceStateChannel;
+  unsigned int DeviceStateTargetID;
+  unsigned long SecondaryMonitoringTime;
+  unsigned long RebuildLastReportTime;
+  boolean SAFTE_FaultManagementEnabled;
+  boolean MonitoringCommandDeferred;
+  boolean NeedLogicalDriveInformation;
+  boolean NeedDeviceStateInformation;
+  boolean NeedRebuildProgress;
+  GenericDiskInfo_T GenericDiskInfo;
+  Timer_T MonitoringTimer;
+  DAC960_Command_T *FreeCommands;
+  DAC960_V4_CommandMailbox_T *FirstCommandMailbox;
+  DAC960_V4_CommandMailbox_T *LastCommandMailbox;
+  DAC960_V4_CommandMailbox_T *NextCommandMailbox;
+  DAC960_V4_CommandMailbox_T *PreviousCommandMailbox;
+  DAC960_V4_StatusMailbox_T *FirstStatusMailbox;
+  DAC960_V4_StatusMailbox_T *LastStatusMailbox;
+  DAC960_V4_StatusMailbox_T *NextStatusMailbox;
+  DAC960_Enquiry_T Enquiry[2];
+  DAC960_LogicalDriveInformation_T
+    LogicalDriveInformation[2][DAC960_MaxLogicalDrives];
+  DAC960_DeviceState_T DeviceState[2][DAC960_MaxChannels][DAC960_MaxTargets];
+  DAC960_EventLogEntry_T EventLogEntry;
+  DAC960_RebuildProgress_T RebuildProgress;
+  DAC960_Command_T Commands[DAC960_MaxDriverQueueDepth];
+  DiskPartition_T DiskPartitions[DAC960_MinorCount];
+  int LogicalDriveUsageCount[DAC960_MaxLogicalDrives];
+  int PartitionSizes[DAC960_MinorCount];
+  int BlockSizes[DAC960_MinorCount];
+  int MaxSectorsPerRequest[DAC960_MinorCount];
+  int MaxSegmentsPerRequest[DAC960_MinorCount];
+  char MessageBuffer[DAC960_MessageBufferSize];
+}
+DAC960_Controller_T;
+
+
+/*
+  DAC960_AcquireControllerLock acquires exclusive access to Controller.
+*/
+
+static inline
+void DAC960_AcquireControllerLock(DAC960_Controller_T *Controller,
+				  ProcessorFlags_T *ProcessorFlags)
+{
+  save_flags(*ProcessorFlags);
+  cli();
+}
+
+
+/*
+  DAC960_ReleaseControllerLock releases exclusive access to Controller.
+*/
+
+static inline
+void DAC960_ReleaseControllerLock(DAC960_Controller_T *Controller,
+				  ProcessorFlags_T *ProcessorFlags)
+{
+  restore_flags(*ProcessorFlags);
+}
+
+
+/*
+  DAC960_AcquireControllerLockRF acquires exclusive access to Controller,
+  but is only called from the request function when interrupts are disabled.
+*/
+
+static inline
+void DAC960_AcquireControllerLockRF(DAC960_Controller_T *Controller,
+				    ProcessorFlags_T *ProcessorFlags)
+{
+}
+
+
+/*
+  DAC960_ReleaseControllerLockRF releases exclusive access to Controller,
+  but is only called from the request function when interrupts are disabled.
+*/
+
+static inline
+void DAC960_ReleaseControllerLockRF(DAC960_Controller_T *Controller,
+				    ProcessorFlags_T *ProcessorFlags)
+{
+}
+
+
+/*
+  DAC960_AcquireControllerLockIH acquires exclusive access to Controller,
+  but is only called from the interrupt handler when interrupts are disabled.
+*/
+
+static inline
+void DAC960_AcquireControllerLockIH(DAC960_Controller_T *Controller,
+				    ProcessorFlags_T *ProcessorFlags)
+{
+}
+
+
+/*
+  DAC960_ReleaseControllerLockIH releases exclusive access to Controller,
+  but is only called from the interrupt handler when interrupts are disabled.
+*/
+
+static inline
+void DAC960_ReleaseControllerLockIH(DAC960_Controller_T *Controller,
+				    ProcessorFlags_T *ProcessorFlags)
+{
+}
+
+
+/*
+  Define inline functions to provide an abstraction for reading and writing the
+  DAC960 V4 Controller Interface Registers.
+*/
+
+static inline
+void DAC960_V4_NewCommand(void *ControllerBaseAddress)
+{
+  DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.NewCommand = true;
+  writel(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V4_AcknowledgeStatus(void *ControllerBaseAddress)
+{
+  DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.AcknowledgeStatus = true;
+  writel(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V4_SoftReset(void *ControllerBaseAddress)
+{
+  DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.SoftReset = true;
+  writel(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset);
+}
+
+static inline
+boolean DAC960_V4_MailboxFullP(void *ControllerBaseAddress)
+{
+  DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All =
+    readl(ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset);
+  return InboundDoorBellRegister.Read.MailboxFull;
+}
+
+static inline
+void DAC960_V4_AcknowledgeInterrupt(void *ControllerBaseAddress)
+{
+  DAC960_V4_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All = 0;
+  OutboundDoorBellRegister.Write.AcknowledgeInterrupt = true;
+  writel(OutboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V4_OutboundDoorBellRegisterOffset);
+}
+
+static inline
+boolean DAC960_V4_StatusAvailableP(void *ControllerBaseAddress)
+{
+  DAC960_V4_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All =
+    readl(ControllerBaseAddress + DAC960_V4_OutboundDoorBellRegisterOffset);
+  return OutboundDoorBellRegister.Read.StatusAvailable;
+}
+
+static inline
+void DAC960_V4_EnableInterrupts(void *ControllerBaseAddress)
+{
+  DAC960_V4_InterruptMaskRegister_T InterruptMaskRegister;
+  InterruptMaskRegister.All = 0;
+  InterruptMaskRegister.Bits.MessageUnitInterruptMask1 = 0x3;
+  InterruptMaskRegister.Bits.DisableInterrupts = false;
+  InterruptMaskRegister.Bits.MessageUnitInterruptMask2 = 0x1F;
+  writel(InterruptMaskRegister.All,
+	 ControllerBaseAddress + DAC960_V4_InterruptMaskRegisterOffset);
+}
+
+static inline
+void DAC960_V4_DisableInterrupts(void *ControllerBaseAddress)
+{
+  DAC960_V4_InterruptMaskRegister_T InterruptMaskRegister;
+  InterruptMaskRegister.All = 0;
+  InterruptMaskRegister.Bits.MessageUnitInterruptMask1 = 0x3;
+  InterruptMaskRegister.Bits.DisableInterrupts = true;
+  InterruptMaskRegister.Bits.MessageUnitInterruptMask2 = 0x1F;
+  writel(InterruptMaskRegister.All,
+	 ControllerBaseAddress + DAC960_V4_InterruptMaskRegisterOffset);
+}
+
+static inline
+boolean DAC960_V4_InterruptsEnabledP(void *ControllerBaseAddress)
+{
+  DAC960_V4_InterruptMaskRegister_T InterruptMaskRegister;
+  InterruptMaskRegister.All =
+    readl(ControllerBaseAddress + DAC960_V4_InterruptMaskRegisterOffset);
+  return !InterruptMaskRegister.Bits.DisableInterrupts;
+}
+
+static inline
+void DAC960_V4_WriteCommandMailbox(DAC960_CommandMailbox_T *NextCommandMailbox,
+				   DAC960_CommandMailbox_T *CommandMailbox)
+{
+  NextCommandMailbox->Words[1] = CommandMailbox->Words[1];
+  NextCommandMailbox->Words[2] = CommandMailbox->Words[2];
+  NextCommandMailbox->Words[3] = CommandMailbox->Words[3];
+  NextCommandMailbox->Words[0] = CommandMailbox->Words[0];
+}
+
+static inline
+void DAC960_V4_WriteLegacyCommand(void *ControllerBaseAddress,
+				  DAC960_CommandMailbox_T *CommandMailbox)
+{
+  writel(CommandMailbox->Words[0],
+	 ControllerBaseAddress + DAC960_V4_CommandOpcodeRegisterOffset);
+  writel(CommandMailbox->Words[1],
+	 ControllerBaseAddress + DAC960_V4_MailboxRegister4Offset);
+  writel(CommandMailbox->Words[2],
+	 ControllerBaseAddress + DAC960_V4_MailboxRegister8Offset);
+  writeb(CommandMailbox->Bytes[12],
+	 ControllerBaseAddress + DAC960_V4_MailboxRegister12Offset);
+}
+
+static inline DAC960_CommandIdentifier_T
+DAC960_V4_ReadStatusCommandIdentifier(void *ControllerBaseAddress)
+{
+  return readb(ControllerBaseAddress
+	       + DAC960_V4_StatusCommandIdentifierRegOffset);
+}
+
+static inline DAC960_CommandStatus_T
+DAC960_V4_ReadStatusRegister(void *ControllerBaseAddress)
+{
+  return readw(ControllerBaseAddress + DAC960_V4_StatusRegisterOffset);
+}
+
+
+/*
+  Define inline functions to provide an abstraction for reading and writing the
+  DAC960 V3 Controller Interface Registers.
+*/
+
+static inline
+void DAC960_V3_NewCommand(void *ControllerBaseAddress)
+{
+  DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.NewCommand = true;
+  writeb(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V3_AcknowledgeStatus(void *ControllerBaseAddress)
+{
+  DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.AcknowledgeStatus = true;
+  writeb(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V3_SoftReset(void *ControllerBaseAddress)
+{
+  DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.SoftReset = true;
+  writeb(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset);
+}
+
+static inline
+boolean DAC960_V3_MailboxFullP(void *ControllerBaseAddress)
+{
+  DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All =
+    readb(ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset);
+  return InboundDoorBellRegister.Read.MailboxFull;
+}
+
+static inline
+void DAC960_V3_AcknowledgeInterrupt(void *ControllerBaseAddress)
+{
+  DAC960_V3_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All = 0;
+  OutboundDoorBellRegister.Write.AcknowledgeInterrupt = true;
+  writeb(OutboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V3_OutboundDoorBellRegisterOffset);
+}
+
+static inline
+boolean DAC960_V3_StatusAvailableP(void *ControllerBaseAddress)
+{
+  DAC960_V3_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All =
+    readb(ControllerBaseAddress + DAC960_V3_OutboundDoorBellRegisterOffset);
+  return OutboundDoorBellRegister.Read.StatusAvailable;
+}
+
+static inline
+void DAC960_V3_EnableInterrupts(void *ControllerBaseAddress)
+{
+  DAC960_V3_InterruptEnableRegister_T InterruptEnableRegister;
+  InterruptEnableRegister.All = 0;
+  InterruptEnableRegister.Bits.EnableInterrupts = true;
+  writeb(InterruptEnableRegister.All,
+	 ControllerBaseAddress + DAC960_V3_InterruptEnableRegisterOffset);
+}
+
+static inline
+void DAC960_V3_DisableInterrupts(void *ControllerBaseAddress)
+{
+  DAC960_V3_InterruptEnableRegister_T InterruptEnableRegister;
+  InterruptEnableRegister.All = 0;
+  InterruptEnableRegister.Bits.EnableInterrupts = false;
+  writeb(InterruptEnableRegister.All,
+	 ControllerBaseAddress + DAC960_V3_InterruptEnableRegisterOffset);
+}
+
+static inline
+boolean DAC960_V3_InterruptsEnabledP(void *ControllerBaseAddress)
+{
+  DAC960_V3_InterruptEnableRegister_T InterruptEnableRegister;
+  InterruptEnableRegister.All =
+    readb(ControllerBaseAddress + DAC960_V3_InterruptEnableRegisterOffset);
+  return InterruptEnableRegister.Bits.EnableInterrupts;
+}
+
+static inline
+void DAC960_V3_WriteCommandMailbox(void *ControllerBaseAddress,
+				   DAC960_CommandMailbox_T *CommandMailbox)
+{
+  writel(CommandMailbox->Words[0],
+	 ControllerBaseAddress + DAC960_V3_CommandOpcodeRegisterOffset);
+  writel(CommandMailbox->Words[1],
+	 ControllerBaseAddress + DAC960_V3_MailboxRegister4Offset);
+  writel(CommandMailbox->Words[2],
+	 ControllerBaseAddress + DAC960_V3_MailboxRegister8Offset);
+  writeb(CommandMailbox->Bytes[12],
+	 ControllerBaseAddress + DAC960_V3_MailboxRegister12Offset);
+}
+
+static inline DAC960_CommandIdentifier_T
+DAC960_V3_ReadStatusCommandIdentifier(void *ControllerBaseAddress)
+{
+  return readb(ControllerBaseAddress
+	       + DAC960_V3_StatusCommandIdentifierRegOffset);
+}
+
+static inline DAC960_CommandStatus_T
+DAC960_V3_ReadStatusRegister(void *ControllerBaseAddress)
+{
+  return readw(ControllerBaseAddress + DAC960_V3_StatusRegisterOffset);
+}
+
+
+/*
+  Virtual_to_Bus and Bus_to_Virtual map between Kernel Virtual Addresses
+  and PCI Bus Addresses.
+*/
+
+static inline DAC960_BusAddress_T Virtual_to_Bus(void *VirtualAddress)
+{
+  return (DAC960_BusAddress_T) virt_to_bus(VirtualAddress);
+}
+
+static inline void *Bus_to_Virtual(DAC960_BusAddress_T BusAddress)
+{
+  return (void *) bus_to_virt(BusAddress);
+}
+
+
+/*
+  Define compatibility macros between Linux 2.0 and Linux 2.1.
+*/
+
+#if LINUX_VERSION_CODE < 0x20100
+
+#define MODULE_PARM(Variable, Type)
+#define ioremap_nocache(Offset, Size)	vremap(Offset, Size)
+#define iounmap(Address)		vfree(Address)
+
+#endif
+
+
+/*
+  Define prototypes for the forward referenced DAC960 Driver Internal Functions.
+*/
+
+static void DAC960_RequestFunction0(void);
+static void DAC960_RequestFunction1(void);
+static void DAC960_RequestFunction2(void);
+static void DAC960_RequestFunction3(void);
+static void DAC960_RequestFunction4(void);
+static void DAC960_RequestFunction5(void);
+static void DAC960_RequestFunction6(void);
+static void DAC960_RequestFunction7(void);
+static void DAC960_InterruptHandler(int, void *, Registers_T *);
+static void DAC960_QueueMonitoringCommand(DAC960_Command_T *);
+static void DAC960_MonitoringTimerFunction(unsigned long);
+static int DAC960_Open(Inode_T *, File_T *);
+static void DAC960_Release(Inode_T *, File_T *);
+static int DAC960_Ioctl(Inode_T *, File_T *, unsigned int, unsigned long);
+static void DAC960_InitializeGenericDiskInfo(GenericDiskInfo_T *);
+static void DAC960_Message(DAC960_MessageLevel_T, char *,
+			   DAC960_Controller_T *, ...);
+
+
+#endif /* DAC960_DriverVersion */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/Makefile linux/drivers/block/Makefile
--- linux.vanilla/drivers/block/Makefile	Fri Jul 24 17:28:56 1998
+++ linux/drivers/block/Makefile	Fri Jan 29 14:21:03 1999
@@ -118,6 +118,10 @@
   endif
 endif
 
+ifeq ($(CONFIG_BLK_DEV_DAC960),y)
+L_OBJS += DAC960.o
+endif
+
 ifeq ($(CONFIG_BLK_DEV_MD),y)
 LX_OBJS += md.o
 
@@ -152,7 +156,15 @@
   M_OBJS += raid5.o
   endif
 endif
+endif
 
+ifeq ($(CONFIG_BLK_CPQ_DA),y)
+L_OBJS += cpqarray.o proc_array.o
+else 
+  ifeq ($(CONFIG_BLK_CPQ_DA),m)
+  M_OBJS += cpqarray.o
+  endif
 endif
+
 
 include $(TOPDIR)/Rules.make
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/README.smart2 linux/drivers/block/README.smart2
--- linux.vanilla/drivers/block/README.smart2	Thu Jan  1 01:00:00 1970
+++ linux/drivers/block/README.smart2	Fri Jan 29 14:21:22 1999
@@ -0,0 +1,88 @@
+This driver is for Compaq's SMART2 Intellegent Disk Array Controllers.
+
+WARNING:
+--------
+
+This is still development code.  It seems to work fine for me, but I haven't
+done any real hardcore testing against this driver.  Also, things are likely
+to change, such as the major number used by the device driver as well as the
+names of the /dev entries.
+
+Installing:
+-----------
+
+You need to build a new kernel to use this device, even if you want to
+use a loadable module.  This driver requires Leonard N. Zubkoff's excellent
+patches to ll_rw_blk.c (to controll the number of scatter/gather elements
+sent to lower disk drivers).  Visit http://www.dandelion.com/Linux/DAC960.html
+to get his patches.
+
+Apply the patch to a 2.0.36 kernel after applying Leonard's patch:
+
+# cd linux
+# patch -p1 <DAC960.patch
+# patch -p1 <smart2.patch
+
+Then build a new kernel and turn on Compaq SMART2 Disk Array support.
+Create device nodes for the diskarray device:
+
+# mkdev.ida [ctlrs]
+
+Where ctlrs is the number of controllers you have (defaults to 1 if not
+specified).
+
+EISA Controllers:
+-----------------
+
+If you want to use an EISA controller you'll have to turn on EISA support
+and supply some insmod/lilo paramaters.  If the driver is compiled into the
+kernel, must give it port/irq data at boot time.  For example, if you had
+two SMART-2/E controllers, in EISA slots 1 and 2, on irqs 15 and 5 respectively
+you'd give it a boot argument like this:
+
+	smart2=0x1000,0xf,0x2000,0x5
+
+If you were loading the driver as a module, you'd give load it like this:
+
+	insmod cpqarray.o eisa=0x1000,0xf,0x2000,0x5
+
+You can use EISA and PCI adapters at the same time.
+
+Booting:
+--------
+
+You'll need to use a modified lilo if you want to boot from a disk array.
+Its simply a version of lilo with some code added to tell it how to
+understand Compaq diskarray devices.
+
+Device Naming:
+--------------
+
+You need some entries in /dev for the ida device.  The mkdev.ida script
+can make device nodes for you automatically.  Currently the device setup
+is as follows:
+
+Major numbers:
+	72	ida0
+	73	ida1
+	74	ida2
+	etc...
+
+Minor numbers:
+        b7 b6 b5 b4 b3 b2 b1 b0
+        |----+----| |----+----|
+             |           |
+             |           +-------- Partition ID (0=wholedev, 1-15 partition)
+             |
+             +-------------------- Logical Volume number
+
+The suggested device naming scheme is:
+/dev/ida/c0d0		Controller 0, disk 0, whole device
+/dev/ida/c0d0p1		Controller 0, disk 0, partition 1
+/dev/ida/c0d0p2		Controller 0, disk 0, partition 2
+/dev/ida/c0d0p3		Controller 0, disk 0, partition 3
+
+/dev/ida/c1d1		Controller 1, disk 1, whole device
+/dev/ida/c1d1p1		Controller 1, disk 1, partition 1
+/dev/ida/c1d1p2		Controller 1, disk 1, partition 2
+/dev/ida/c1d1p3		Controller 1, disk 1, partition 3
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/TODO.smart2 linux/drivers/block/TODO.smart2
--- linux.vanilla/drivers/block/TODO.smart2	Thu Jan  1 01:00:00 1970
+++ linux/drivers/block/TODO.smart2	Fri Jan 29 14:21:41 1999
@@ -0,0 +1,20 @@
+Passthrough ioctl (needs testing)
+
+Missing #defines for error codes and bit definitions in arraycmd.h
+Missing structures for health and wellness stuff in arraycmd.h
+	
+Online Array config utility for userspace. 
+
+Array health monitor for userspace.  Perhaps add functionality to use
+the Event Notifier to send health events to a userspace daemon.
+
+Portability:  This driver probably doesn't work on Compaq's Alpha hardware.
+
+Performance, Performance, Performance:  There just have to be some bottlenecks
+in this driver.  I'd like to get some more profiling numbers on things, like
+how often we totally fill the controller's queue, how much stuff sits out on
+the completetion queue and timings on some critical sections of code (the
+intr. handler, the request function, cmd_alloc, etc).  The end result being
+that I'm absolutely positive that when the kernel has IO for me,  I am not
+letting the controller idle at all.
+
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/cpqarray.c linux/drivers/block/cpqarray.c
--- linux.vanilla/drivers/block/cpqarray.c	Thu Jan  1 01:00:00 1970
+++ linux/drivers/block/cpqarray.c	Tue Mar  2 00:39:42 1999
@@ -0,0 +1,1660 @@
+/*
+ *    Disk Array driver for Compaq SMART2 Controllers
+ *    Copyright 1998 Compaq Computer Corporation
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ *    NON INFRINGEMENT.  See the GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *    Questions/Comments/Bugfixes to arrays@compaq.com
+ *
+ *    If you want to make changes, improve or add functionality to this
+ *    driver, you'll probably need the Compaq Array Controller Interface
+ *    Specificiation (Document number ECG086/1198)
+ */
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/major.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/proc_fs.h>
+#include <linux/hdreg.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+#define DRIVER_NAME "Compaq SMART2 Driver (v 0.9.8)"
+
+#define MAJOR_NR COMPAQ_SMART2_MAJOR
+#include <linux/blk.h>
+#include <linux/blkdev.h>
+#include <linux/genhd.h>
+
+#include "cpqarray.h"
+#include "ida_cmd.h"
+#include "ida_ioctl.h"
+
+#define READ_AHEAD	128
+#define NR_CMDS	64
+
+#define MAX_CTLR	8
+#define CTLR_SHIFT	8
+
+static int nr_ctlr = 0;
+static ctlr_info_t *hba[MAX_CTLR] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+#ifdef CONFIG_BLK_CPQ_DA_EISA
+#ifndef MODULE
+static
+#endif
+int eisa[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
+static char *product_names[] = {
+	"Unknown",
+	"SMART-2/E",
+	"SMART-2/P", /* or SMART-2DH */
+	"SMART-2SL",
+	"SMART-3200",
+	"SMART-3100ES",
+};
+
+static struct hd_struct * ida;
+static int * ida_sizes;
+static int * ida_blocksizes;
+static int * ida_hardsizes;
+static int * ida_maxsectors;
+static int * ida_maxsegments;
+static struct gendisk ida_gendisk[MAX_CTLR];
+
+
+/* Debug... */
+#define DBG(s)	s
+/* Debug (general info)... */
+#define DBGINFO(s)
+/* Debug Paranoid... */
+#define DBGP(s) s
+/* Debug Extra Paranoid... */
+#define DBGPX(s)
+
+void cpqarray_init(void);
+#ifdef CONFIG_BLK_CPQ_DA_PCI
+static int cpqarray_pci_detect(void);
+static void cpqarray_pci_init(ctlr_info_t *c, unchar bus, unchar device_fn);
+static ulong remap_pci_mem(ulong base, ulong size);
+#endif
+#ifdef CONFIG_BLK_CPQ_DA_EISA
+static int cpqarray_eisa_detect(void);
+#endif
+static void flushcomplete(int ctlr);
+static int pollcomplete(int ctlr);
+static void getgeometry(int ctlr);
+
+static cmdlist_t * cmd_alloc(ctlr_info_t *h, int intr);
+static void cmd_free(ctlr_info_t *h, cmdlist_t *c);
+
+static int sendcmd(
+	__u8	cmd,
+	int	ctlr,
+	void	*buff,
+	size_t	size,
+	unsigned int blk,
+	unsigned int blkcnt,
+	unsigned int log_unit );
+
+static int ida_open(struct inode *inode, struct file *filep);
+static void ida_release(struct inode *inode, struct file *filep);
+static int ida_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg);
+static int ida_ctlr_ioctl(int ctlr, int dsk, ida_ioctl_t *io);
+
+static void do_ida_request(int i);
+/*
+ * This is a hack.  This driver eats a major number for each controller, and
+ * sets blkdev[xxx].request_fn to each one of these so the real request
+ * function knows what controller its working with.
+ */
+#define DO_IDA_REQUEST(x) { \
+	int flags; save_flags(flags); cli(); \
+	do_ida_request(x); restore_flags(flags); }
+
+static void do_ida_request0(void) DO_IDA_REQUEST(0);
+static void do_ida_request1(void) DO_IDA_REQUEST(1);
+static void do_ida_request2(void) DO_IDA_REQUEST(2);
+static void do_ida_request3(void) DO_IDA_REQUEST(3);
+static void do_ida_request4(void) DO_IDA_REQUEST(4);
+static void do_ida_request5(void) DO_IDA_REQUEST(5);
+static void do_ida_request6(void) DO_IDA_REQUEST(6);
+static void do_ida_request7(void) DO_IDA_REQUEST(7);
+
+static void start_io(ctlr_info_t *h);
+
+static inline cmdlist_t *removeQ(cmdlist_t **Qptr, cmdlist_t *c);
+static inline void addQ(cmdlist_t **Qptr, cmdlist_t *c);
+static inline void complete_buffers(struct buffer_head *bh, int ok);
+static inline void complete_command(cmdlist_t *cmd, int timeout);
+
+static void do_ida_intr(int irq, void *dev_id, struct pt_regs * regs);
+static void ida_timer(unsigned long tdata);
+static int frevalidate_logvol(kdev_t dev);
+static int revalidate_logvol(kdev_t dev, int maxusage);
+static int revalidate_allvol(kdev_t dev);
+
+static void ida_procinit(int i);
+static int ida_proc_get_info(char *buffer, char **start, off_t offset, int length, int dp);
+
+/*
+ * These macros control what happens when the driver tries to write to or
+ * read from a card.  If the driver is configured for EISA only or PCI only,
+ * the macros expand to inl/outl or readl/writel.  If the drive is configured
+ * for both EISA and PCI, the macro expands to a conditional which uses
+ * memory mapped IO if the card has it (PCI) or io ports if it doesn't (EISA).
+ */
+#ifdef CONFIG_BLK_CPQ_DA_PCI
+#	ifdef CONFIG_BLK_CPQ_DA_EISA
+#		warning "SMART2: EISA+PCI"
+#		define smart2_read(h, offset)  ( ((h)->vaddr) ? readl((h)->vaddr+(offset)) : inl((h)->ioaddr+(offset)) )
+#		define smart2_write(p, h, offset) ( ((h)->vaddr) ? writel((p), (h)->vaddr+(offset)) : outl((p), (h)->ioaddr+(offset)) )
+#	else
+#		warning "SMART2: PCI"
+#		define smart2_read(h, offset)  readl((h)->vaddr+(offset))
+#		define smart2_write(p, h, offset) writel((p), (h)->vaddr+(offset))
+#	endif
+#else
+#	ifdef CONFIG_BLK_CPQ_DA_EISA
+#		warning "SMART2: EISA"
+#		define smart2_read(h, offset)  inl((h)->vaddr+(offset))
+#		define smart2_write(p, h, offset) outl((p), (h)->vaddr+(offset))
+#	else
+#		error "You must enable either SMART2 PCI support or SMART2 EISA support or both!"
+#	endif
+#endif
+
+void ida_geninit(struct gendisk *g)
+{
+	int ctlr = g-ida_gendisk;
+	int i,j;
+	drv_info_t *drv;
+
+	for(i=0; i<NWD; i++) {
+		drv = &hba[ctlr]->drv[i];
+		if (!drv->nr_blks)
+			continue;
+		ida[(ctlr<<CTLR_SHIFT) + (i<<NWD_SHIFT)].nr_sects =
+		ida_sizes[(ctlr<<CTLR_SHIFT) + (i<<NWD_SHIFT)] =
+				drv->nr_blks;
+
+		for(j=0; j<16; j++) {
+			ida_blocksizes[(ctlr<<CTLR_SHIFT) + (i<<NWD_SHIFT)+j] =
+				1024;
+			ida_hardsizes[(ctlr<<CTLR_SHIFT) + (i<<NWD_SHIFT)+j] =
+				drv->blk_size;
+		}
+		ida_gendisk[ctlr].nr_real++;
+	}
+
+}
+
+struct file_operations ida_fops  = {
+	NULL,                        /* lseek - default */
+	block_read,                  /* read - general block-dev read */
+	block_write,                 /* write - general block-dev write */
+	NULL,                        /* readdir - bad */
+	NULL,                        /* select */
+	ida_ioctl,                  /* ioctl */
+	NULL,                        /* mmap */
+	ida_open,                     /* open code */
+	ida_release,                  /* release */
+	block_fsync,	              /* fsync */
+	NULL,                        /* fasync */
+	NULL,			/* Disk change */
+	frevalidate_logvol,	/* revalidate */
+};
+
+
+/*
+ * Place some files in /proc/array/* that contain some information about
+ * each controller.  There really isn't much useful in there now.
+ */
+extern struct inode_operations proc_diskarray_inode_operations;
+struct proc_dir_entry *proc_array = NULL;
+static void ida_procinit(int i)
+{
+	struct proc_dir_entry *pd;
+
+	if (proc_array == NULL) {
+		proc_array = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL);
+		if (!proc_array) return;
+		memset(proc_array, 0, sizeof(struct proc_dir_entry));
+		proc_array->namelen = 5;
+		proc_array->name = "array";
+		proc_array->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+		proc_array->nlink = 2;
+		proc_array->uid = 0;
+		proc_array->gid = 0;
+		proc_array->size = 0;
+		proc_array->ops = &proc_dir_inode_operations;
+		proc_register_dynamic(&proc_root, proc_array);
+	}
+
+	pd = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL);
+	if (!pd) return;
+	memset(pd, 0, sizeof(struct proc_dir_entry));
+	pd->namelen = 4;
+	pd->name = hba[i]->devname;
+	pd->mode = S_IFREG | S_IRUGO;
+	pd->nlink = 1;
+	pd->uid = 0;
+	pd->gid = 0;
+	pd->size = 0;
+	pd->ops = &proc_diskarray_inode_operations;
+	pd->get_info = ida_proc_get_info;
+	
+	hba[i]->proc = (int)pd;
+	proc_register_dynamic(proc_array, pd);
+}
+
+/*
+ * Report information about this controller.
+ */
+static int ida_proc_get_info(char *buffer, char **start, off_t offset, int length, int pd)
+{
+	off_t pos = 0;
+	off_t len = 0;
+	int size, i, ctlr;
+	ctlr_info_t *h;
+	drv_info_t *drv;
+#ifdef CPQ_PROC_PRINT_QUEUES
+	cmdlist_t *c;
+#endif
+
+	for(ctlr=0; ctlr<nr_ctlr; ctlr++) 
+		if (hba[ctlr] && hba[ctlr]->proc == pd) break;
+
+
+	if ((h = hba[ctlr]) == NULL)
+		return 0;
+
+	size = sprintf(buffer, "%s:  Compaq %s Disk Array Controller\n"
+		"       Board ID: %08lx\n"
+		"       Firmware Revision: %c%c%c%c\n"
+		"       Controller Sig: %08lx\n"
+		"       Memory Address: %08lx\n"
+		"       I/O Port: %04x\n"
+		"       IRQ: %x\n"
+		"       Logical drives: %d\n"
+		"       Physical drives: %d\n\n"
+		"       Current Q depth: %d\n"
+		"       Max Q depth since init: %d\n\n",
+		h->devname,
+		product_names[h->product],
+		(unsigned long)h->board_id,
+		h->firm_rev[0], h->firm_rev[1], h->firm_rev[2], h->firm_rev[3],
+		(unsigned long)h->ctlr_sig, (unsigned long)h->vaddr,
+		(unsigned int) h->ioaddr, (unsigned int)h->intr,
+		h->log_drives, h->phys_drives,
+		h->Qdepth, h->maxQsinceinit);
+
+	pos += size; len += size;
+	
+	size = sprintf(buffer+len, "Logical Drive Info:\n");
+	pos += size; len += size;
+
+	for(i=0; i<h->log_drives; i++) {
+		drv = &h->drv[i];
+		size = sprintf(buffer+len, "ida/c%dd%d: blksz=%d nr_blks=%d\n",
+				ctlr, i, drv->blk_size, drv->nr_blks);
+		pos += size; len += size;
+	}
+
+#ifdef CPQ_PROC_PRINT_QUEUES
+	size = sprintf(buffer+len, "\nCurrent Queues:\n");
+	pos += size; len += size;
+
+	c = h->reqQ;
+	size = sprintf(buffer+len, "reqQ = %p", c); pos += size; len += size;
+	if (c) c=c->next;
+	while(c && c != h->reqQ) {
+		size = sprintf(buffer+len, "->%p", c);
+		pos += size; len += size;
+		c=c->next;
+	}
+
+	c = h->cmpQ;
+	size = sprintf(buffer+len, "\ncmpQ = %p", c); pos += size; len += size;
+	if (c) c=c->next;
+	while(c && c != h->cmpQ) {
+		size = sprintf(buffer+len, "->%p", c);
+		pos += size; len += size;
+		c=c->next;
+	}
+
+	size = sprintf(buffer+len, "\n"); pos += size; len += size;
+#endif
+	size = sprintf(buffer+len,"nr_allocs = %d\nnr_frees = %d\n",
+			h->nr_allocs, h->nr_frees);
+	pos += size; len += size;
+
+	*start = buffer+offset;
+	len -= offset;
+	if (len>length)
+		len = length;
+	return len;
+}
+
+#ifdef MODULE
+/* This is a hack... */
+#include "proc_array.c"
+int init_module(void)
+{
+	int i, j;
+	cpqarray_init();
+	if (nr_ctlr == 0)
+		return -EIO;
+
+	for(i=0; i<nr_ctlr; i++) {
+		ida_geninit(&ida_gendisk[i]); 
+		for(j=0; j<NWD; j++)	
+			if (ida_sizes[(i<<CTLR_SHIFT) + (j<<NWD_SHIFT)])
+				resetup_one_dev(&ida_gendisk[i], j);
+	}
+	return 0;
+}
+void cleanup_module(void)
+{
+	int i;
+	struct gendisk *g;
+
+	for(i=0; i<nr_ctlr; i++) {
+		smart2_write(0, hba[i], INTR_MASK);
+		free_irq(hba[i]->intr, hba[i]);
+		vfree((void*)hba[i]->vaddr);
+		unregister_blkdev(MAJOR_NR+i, hba[i]->devname);
+		del_timer(&hba[i]->timer);
+		proc_unregister(proc_array,
+			((struct proc_dir_entry*)hba[i]->proc)->low_ino);
+		kfree(hba[i]->cmd_pool);
+		kfree(hba[i]->cmd_pool_bits);
+
+		if (gendisk_head == &ida_gendisk[i]) {
+			gendisk_head = ida_gendisk[i].next;
+		} else {
+			for(g=gendisk_head; g; g=g->next) {
+				if (g->next == &ida_gendisk[i]) {
+					g->next = ida_gendisk[i].next;
+					break;
+				}
+			}
+		}
+
+		blk_dev[MAJOR_NR+i].request_fn = NULL;
+		blksize_size[MAJOR_NR+i] = NULL;
+		hardsect_size[MAJOR_NR+i] = NULL;
+		max_sectors[MAJOR_NR+i] = NULL;
+		max_segments[MAJOR_NR+i] = NULL;
+	}
+	proc_unregister(&proc_root, proc_array->low_ino);
+	kfree(ida);
+	kfree(ida_sizes);
+	kfree(ida_hardsizes);
+	kfree(ida_blocksizes);
+
+	kfree(ida_maxsectors);
+	kfree(ida_maxsegments);
+
+}
+#endif /* MODULE */
+
+/*
+ *  This is it.  Find all the controllers and register them.  I really hate
+ *  stealing all these major device numbers.
+ */
+void cpqarray_init(void)
+{
+	void (*request_fns[MAX_CTLR])(void) = {
+		do_ida_request0, do_ida_request1,
+		do_ida_request2, do_ida_request3,
+		do_ida_request4, do_ida_request5,
+		do_ida_request6, do_ida_request7,
+	};
+	int i;
+
+	/* detect controllers */
+#ifdef CONFIG_BLK_CPQ_DA_PCI
+	cpqarray_pci_detect();
+#endif
+#ifdef CONFIG_BLK_CPQ_DA_EISA
+	cpqarray_eisa_detect();
+#endif
+
+	if (nr_ctlr == 0)
+		return;
+
+	printk(DRIVER_NAME "\n");
+	printk("Found %d controller(s)\n", nr_ctlr);
+
+	/* allocate space for disk structs */
+	ida = kmalloc(sizeof(struct hd_struct)*nr_ctlr*NWD*16, GFP_KERNEL);
+	ida_sizes =      kmalloc(sizeof(int)*nr_ctlr*NWD*16, GFP_KERNEL);
+	ida_blocksizes = kmalloc(sizeof(int)*nr_ctlr*NWD*16, GFP_KERNEL);
+	ida_hardsizes =  kmalloc(sizeof(int)*nr_ctlr*NWD*16, GFP_KERNEL);
+
+	ida_maxsegments =  kmalloc(sizeof(int)*nr_ctlr*NWD*16, GFP_KERNEL);
+	ida_maxsectors =  kmalloc(sizeof(int)*nr_ctlr*NWD*16, GFP_KERNEL);
+
+	memset(ida, 0, sizeof(struct hd_struct)*nr_ctlr*NWD*16);
+	memset(ida_sizes, 0, sizeof(int)*nr_ctlr*NWD*16);
+	memset(ida_blocksizes, 0, sizeof(int)*nr_ctlr*NWD*16);
+	memset(ida_hardsizes, 0, sizeof(int)*nr_ctlr*NWD*16);
+	memset(ida_maxsegments, 0, sizeof(int)*nr_ctlr*NWD*16);
+	memset(ida_maxsectors, 0, sizeof(int)*nr_ctlr*NWD*16);
+	memset(ida_gendisk, 0, sizeof(struct gendisk)*MAX_CTLR);
+
+	for(i=0; i<nr_ctlr*NWD*16; i++) {
+		ida_maxsegments[i] = SG_MAX;
+		ida_maxsectors[i] = 1024;
+	}
+	/* 
+	 * register block devices
+	 * Find disks and fill in structs
+	 * Get an interrupt, set the Q depth and get into /proc
+	 */
+	for(i=0; i< nr_ctlr; i++) {
+		smart2_write(0, hba[i], INTR_MASK);  /* No interrupts */
+		if (request_irq(hba[i]->intr, do_ida_intr,
+			SA_INTERRUPT | SA_SHIRQ, hba[i]->devname, hba[i])) {
+
+			printk("Unable to get irq %d for %s\n",
+				hba[i]->intr, hba[i]->devname);
+			continue;
+		}
+		if (register_blkdev(MAJOR_NR+i, hba[i]->devname, &ida_fops)) {
+			printk("Unable to get major number %d for %s\n",
+				MAJOR_NR+i, hba[i]->devname);
+			continue;
+		}
+
+		hba[i]->cmd_pool = (cmdlist_t*)kmalloc(
+				NR_CMDS*sizeof(cmdlist_t), GFP_KERNEL);
+		hba[i]->cmd_pool_bits = (__u32*)kmalloc(
+				((NR_CMDS+31)/32)*sizeof(__u32), GFP_KERNEL);
+		memset(hba[i]->cmd_pool, 0, NR_CMDS*sizeof(cmdlist_t));
+		memset(hba[i]->cmd_pool_bits, 0, ((NR_CMDS+31)/32)*sizeof(__u32));
+
+		printk("Finding drives on %s", hba[i]->devname);
+		getgeometry(i);
+
+		smart2_write(FIFO_NOT_EMPTY, hba[i], INTR_MASK);
+
+		ida_procinit(i);
+
+		ida_gendisk[i].major = MAJOR_NR + i;
+		ida_gendisk[i].major_name = "ida";
+		ida_gendisk[i].minor_shift = NWD_SHIFT;
+		ida_gendisk[i].max_p = 16;
+		ida_gendisk[i].max_nr = 16;
+		ida_gendisk[i].init = ida_geninit;
+		ida_gendisk[i].part = ida + (i*256);
+		ida_gendisk[i].sizes = ida_sizes + (i*256);
+		/* ida_gendisk[i].nr_real is handled by getgeometry */
+	
+		blk_dev[MAJOR_NR+i].request_fn = request_fns[i];
+		blksize_size[MAJOR_NR+i] = ida_blocksizes + (i*256);
+		hardsect_size[MAJOR_NR+i] = ida_hardsizes + (i*256);
+		read_ahead[MAJOR_NR+i] = READ_AHEAD;
+		max_sectors[MAJOR_NR+i] = ida_maxsectors + (i*256);
+		max_segments[MAJOR_NR+i] = ida_maxsegments + (i*256);
+
+		/* Get on the disk list */
+		ida_gendisk[i].next = gendisk_head;
+		gendisk_head = &ida_gendisk[i];
+
+		init_timer(&hba[i]->timer);
+		hba[i]->timer.expires = jiffies + IDA_TIMER;
+		hba[i]->timer.data = (unsigned long)hba[i];
+		hba[i]->timer.function = ida_timer;
+		add_timer(&hba[i]->timer);
+
+	}
+	/* done ! */
+}
+
+#ifdef CONFIG_BLK_CPQ_DA_PCI
+/*
+ * Find the controller and initialize it
+ */
+static int cpqarray_pci_detect(void)
+{
+	int index;
+	unchar bus=0, dev_fn=0;
+	
+	for(index=0; ; index++) {
+		if (pcibios_find_device(PCI_VENDOR_ID_COMPAQ,
+			PCI_DEVICE_ID_COMPAQ_SMART2P, index, &bus, &dev_fn))
+			break;
+
+		if (index == 1000000) break;
+		if (nr_ctlr == 8) {
+			printk("This driver supports a maximum of "
+				"8 controllers.\n");
+			break;
+		}
+		
+		hba[nr_ctlr] = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL);
+		memset(hba[nr_ctlr], 0, sizeof(ctlr_info_t));
+		cpqarray_pci_init(hba[nr_ctlr], bus, dev_fn);
+		sprintf(hba[nr_ctlr]->devname, "ida%d", nr_ctlr);
+		hba[nr_ctlr]->ctlr = nr_ctlr;
+		nr_ctlr++;
+	}
+
+	return nr_ctlr;
+}
+
+/*
+ * Find the IO address of the controller, its IRQ and so forth.  Fill
+ * in some basic stuff into the ctlr_info_t structure.
+ */
+static void cpqarray_pci_init(ctlr_info_t *c, unchar bus, unchar device_fn)
+{
+	ushort vendor_id, device_id, command;
+	unchar cache_line_size, latency_timer;
+	unchar irq, revision;
+	uint addr[6];
+
+	int i;
+
+	(void) pcibios_read_config_word(bus, device_fn,
+					PCI_VENDOR_ID, &vendor_id);
+	(void) pcibios_read_config_word(bus, device_fn,
+					PCI_DEVICE_ID, &device_id);
+	(void) pcibios_read_config_word(bus, device_fn,
+					PCI_COMMAND, &command);
+	for(i=0; i<6; i++)
+		(void) pcibios_read_config_dword(bus, device_fn,
+				PCI_BASE_ADDRESS_0 + i*4, addr+i);
+
+	(void) pcibios_read_config_byte(bus, device_fn,
+					PCI_CLASS_REVISION,&revision);
+	(void) pcibios_read_config_byte(bus, device_fn,
+					PCI_INTERRUPT_LINE, &irq);
+	(void) pcibios_read_config_byte(bus, device_fn,
+					PCI_CACHE_LINE_SIZE, &cache_line_size);
+	(void) pcibios_read_config_byte(bus, device_fn,
+					PCI_LATENCY_TIMER, &latency_timer);
+
+DBGINFO(
+	printk("vendor_id = %x\n", vendor_id);
+	printk("device_id = %x\n", device_id);
+	printk("command = %x\n", command);
+	for(i=0; i<6; i++)
+		printk("addr[%d] = %x\n", i, addr[i]);
+	printk("revision = %x\n", revision);
+	printk("irq = %x\n", irq);
+	printk("cache_line_size = %x\n", cache_line_size);
+	printk("latency_timer = %x\n", latency_timer);
+);
+
+	c->intr = irq;
+	c->ioaddr = addr[0] & ~0x1;
+
+	/*
+	 * Memory base addr is first addr with the first bit _not_ set
+	 */
+	for(i=0; i<6; i++)
+		if (!(addr[i] & 0x1)) {
+			c->paddr = addr[i];
+			break;
+		}
+	c->vaddr = remap_pci_mem(c->paddr, 128);
+}
+
+/*
+ * Map (physical) PCI mem into (virtual) kernel space
+ */
+static ulong remap_pci_mem(ulong base, ulong size)
+{
+        ulong page_base        = ((ulong) base) & PAGE_MASK;
+        ulong page_offs        = ((ulong) base) - page_base;
+        ulong page_remapped    = (ulong) vremap(page_base, page_offs+size);
+
+        return (ulong) (page_remapped ? (page_remapped + page_offs) : 0UL);
+}
+#endif /* CONFIG_BLK_CPQ_DA_PCI */
+
+#ifdef CONFIG_BLK_CPQ_DA_EISA
+/*
+ * Copy the contents of the ints[] array passed to us by init.
+ */
+void cpqarray_setup(char *str, int *ints)
+{
+	int i;
+	if (ints[0] & 1) {
+		printk( "SMART2 Parameter Usage:\n"
+			"     smart2=io,irq,io,irq,...\n");
+		return;
+	}
+	for(i=0; i<ints[0]; i++) {
+		eisa[i] = ints[i+1];
+	}
+}
+
+/*
+ * Find an EISA controller's signature.  Set up an hba if we find it.
+ */
+static int cpqarray_eisa_detect(void)
+{
+	int i=0;
+
+	while(i<16 && eisa[i]) {
+		if (inl(eisa[i]+0xC80) == 0x3040110e) {
+			if (nr_ctlr == 8) {
+				printk("This driver supports a maximum of "
+					"8 controllers.\n");
+				break;
+			}
+			hba[nr_ctlr] = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL);
+			memset(hba[nr_ctlr], 0, sizeof(ctlr_info_t));
+			hba[nr_ctlr]->ioaddr = eisa[i];
+			hba[nr_ctlr]->intr = eisa[i+1];
+			sprintf(hba[nr_ctlr]->devname, "ida%d", nr_ctlr);
+			hba[nr_ctlr]->ctlr = nr_ctlr;
+			nr_ctlr++;
+		} else {
+			printk("SMART2:  Could not find a controller at io=0x%04x irq=0x%x\n", eisa[i], eisa[i+1]);
+		}
+		i+=2;
+	}
+	return nr_ctlr;
+}
+#endif /* CONFIG_BLK_CPQ_DA_EISA */
+
+/*
+ * Open.  Make sure the device is really there.
+ */
+static int ida_open(struct inode *inode, struct file *filep)
+{
+	int ctlr = MAJOR(inode->i_rdev) - MAJOR_NR;
+	int dsk  = MINOR(inode->i_rdev) >> NWD_SHIFT;
+
+	DBGINFO(printk("ida_open %x (%x:%x)\n", inode->i_rdev, ctlr, dsk) );
+	if (ctlr > MAX_CTLR || hba[ctlr] == NULL)
+		return -ENXIO;
+
+	if (!suser() && ida_sizes[(ctlr << CTLR_SHIFT) +
+						MINOR(inode->i_rdev)] == 0)
+		return -ENXIO;
+
+	/*
+	 * Root is allowed to open raw volume zero even if its not configured
+	 * so array config can still work.  I don't think I really like this,
+	 * but I'm already using way to many device nodes to claim another one
+	 * for "raw controller".
+	 */
+	if (suser()
+		&& ida_sizes[(ctlr << CTLR_SHIFT) + MINOR(inode->i_rdev)] == 0 
+		&& MINOR(inode->i_rdev) != 0)
+		return -ENXIO;
+
+	hba[ctlr]->drv[dsk].usage_count++;
+	hba[ctlr]->usage_count++;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * Close.  Sync first.
+ */
+void ida_release(struct inode *inode, struct file *filep)
+{
+	int ctlr = MAJOR(inode->i_rdev) - MAJOR_NR;
+	int dsk  = MINOR(inode->i_rdev) >> NWD_SHIFT;
+
+	DBGINFO(printk("ida_release %x (%x:%x)\n", inode->i_rdev, ctlr, dsk) );
+	fsync_dev(inode->i_rdev);
+
+	hba[ctlr]->drv[dsk].usage_count--;
+	hba[ctlr]->usage_count--;
+	MOD_DEC_USE_COUNT;
+}
+
+/*
+ * Enqueuing and dequeuing functions for cmdlists.
+ */
+static inline void addQ(cmdlist_t **Qptr, cmdlist_t *c)
+{
+	if (*Qptr == NULL) {
+		*Qptr = c;
+		c->next = c->prev = c;
+	} else {
+		c->prev = (*Qptr)->prev;
+		c->next = (*Qptr);
+		(*Qptr)->prev->next = c;
+		(*Qptr)->prev = c;
+	}
+}
+
+static inline cmdlist_t *removeQ(cmdlist_t **Qptr, cmdlist_t *c)
+{
+	if (c && c->next != c) {
+		if (*Qptr == c) *Qptr = c->next;
+		c->prev->next = c->next;
+		c->next->prev = c->prev;
+	} else {
+		*Qptr = NULL;
+	}
+	return c;
+}
+
+/*
+ * Get a request and submit it to the controller.
+ * This routine needs to grab all the requests it possibly can from the
+ * req Q and submit them.  Interrupts are off (and need to be off) when you
+ * are in here (either via the dummy do_ida_request functions or by being
+ * called from the interrupt handler
+ */
+void do_ida_request(int ctlr)
+{
+	ctlr_info_t *h = hba[ctlr];
+	cmdlist_t *c;
+	int seg;
+	char *lastdataend;
+	struct buffer_head *bh;
+	struct request *creq;
+
+	creq = blk_dev[MAJOR_NR+ctlr].current_request;
+	if (creq == NULL || creq->rq_status == RQ_INACTIVE)
+		goto doreq_done;
+
+	if (ctlr != MAJOR(creq->rq_dev)-MAJOR_NR ||
+		ctlr > nr_ctlr || h == NULL) {
+		printk("doreq cmd for %d, %x at %p\n",
+				ctlr, creq->rq_dev, creq);
+		complete_buffers(creq->bh, 0);
+		goto doreq_done;
+	}
+
+	if ((c = cmd_alloc(h, 1)) == NULL)
+		goto doreq_done;
+
+	blk_dev[MAJOR_NR+ctlr].current_request = creq->next;
+	creq->rq_status = RQ_INACTIVE;
+
+	bh = creq->bh;
+
+	c->ctlr = ctlr;
+	c->hdr.unit = MINOR(creq->rq_dev) >> NWD_SHIFT;
+	c->hdr.prio = 0;
+	c->hdr.size = sizeof(rblk_t) >> 2;
+	c->size += sizeof(rblk_t);
+
+	c->req.hdr.next = 0;
+	c->req.hdr.rcode = 0;
+	c->req.bp = 0;
+	c->req.hdr.sg_cnt = creq->nr_segments;
+	c->req.hdr.reserved = 0;
+	c->req.hdr.blk = ida[(ctlr<<CTLR_SHIFT) + MINOR(creq->rq_dev)].start_sect + creq->sector;
+	c->req.hdr.blk_cnt = creq->nr_sectors;
+	c->bh = bh;
+	
+	seg = 0; lastdataend = NULL;
+	while(bh) {
+		if (bh->b_data == lastdataend) {
+			c->req.sg[seg-1].size += bh->b_size;
+			lastdataend += bh->b_size;
+		} else {
+			c->req.sg[seg].size = bh->b_size;
+			c->req.sg[seg].addr = (__u32) virt_to_bus(bh->b_data);
+			lastdataend = bh->b_data + bh->b_size;
+			if (seg++ > SG_MAX)
+				panic("SG list overflow\n");
+		}
+		bh = bh->b_reqnext;
+	}
+	if (seg != creq->nr_segments)
+		panic("seg != nr_segments\n");
+
+	c->req.hdr.cmd = (creq->cmd == READ) ? IDA_READ : IDA_WRITE;
+	c->type = CMD_RWREQ;
+
+	/* Put the request on the tail of the request queue */
+	addQ(&h->reqQ, c);
+	h->Qdepth++;
+	if (h->Qdepth > h->maxQsinceinit) h->maxQsinceinit = h->Qdepth;
+
+	wake_up(&wait_for_request);
+doreq_done:
+	start_io(h);
+}
+
+/* 
+ * start_io submits everything on a controller's request queue
+ * and moves it to the completion queue.
+ *
+ * Interrupts had better be off if you're in here
+ */
+static void start_io(ctlr_info_t *h)
+{
+	cmdlist_t *c;
+
+	while((c = h->reqQ) != NULL) {
+		/* Can't do anything if we're busy */
+		if (smart2_read(h, COMMAND_FIFO) == 0)
+			return;
+
+		/* Get the first entry from the request Q */
+		removeQ(&h->reqQ, c);
+		h->Qdepth--;
+	
+		/* Tell the controller to do our bidding */
+		smart2_write(c->busaddr, h, COMMAND_FIFO);
+
+		/* Get onto the completion Q */
+		addQ(&h->cmpQ, c);
+	}
+}
+
+
+static inline void complete_buffers(struct buffer_head *bh, int ok)
+{
+	struct buffer_head *xbh;
+	while(bh) {
+		xbh = bh->b_reqnext;
+		bh->b_reqnext = NULL;
+		mark_buffer_uptodate(bh, ok);
+		unlock_buffer(bh);
+		bh = xbh;
+	}
+}
+
+/*
+ * Mark all buffers that cmd was responsible for
+ */
+static inline void complete_command(cmdlist_t *cmd, int timeout)
+{
+	char buf[80];
+	int ok=1;
+
+	if (cmd->req.hdr.rcode & RCODE_NONFATAL &&
+		(hba[cmd->ctlr]->misc_tflags & MISC_NONFATAL_WARN) == 0) {
+			sprintf(buf, "Non Fatal error on ida/c%dd%d\n",
+					cmd->ctlr, cmd->hdr.unit);
+			console_print(buf);
+			hba[cmd->ctlr]->misc_tflags |= MISC_NONFATAL_WARN;
+	}
+	if (cmd->req.hdr.rcode & RCODE_FATAL) {
+		sprintf(buf, "Fatal error on ida/c%dd%d\n",
+				cmd->ctlr, cmd->hdr.unit);
+		console_print(buf);
+		ok = 0;
+	}
+	if (cmd->req.hdr.rcode & RCODE_INVREQ) {
+sprintf(buf, "Invalid request on ida/c%dd%d = (cmd=%x sect=%d cnt=%d sg=%d ret=%x)\n",
+				cmd->ctlr, cmd->hdr.unit, cmd->req.hdr.cmd,
+				cmd->req.hdr.blk, cmd->req.hdr.blk_cnt,
+				cmd->req.hdr.sg_cnt, cmd->req.hdr.rcode);
+		console_print(buf);
+		ok = 0;	
+	}
+	if (timeout) {
+		sprintf(buf, "Request timeout on ida/c%dd%d\n",
+				cmd->ctlr, cmd->hdr.unit);
+		console_print(buf);
+		ok = 0;
+	}
+	complete_buffers(cmd->bh, ok);
+}
+
+/*
+ *  The controller will interrupt us upon completion of commands.
+ *  Find the command on the completion queue, remove it, tell the OS and
+ *  try to queue up more IO
+ */
+void do_ida_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	ctlr_info_t *h = dev_id;
+	cmdlist_t *c;
+	unsigned long istat;
+	__u32 a,a1;
+
+	istat = smart2_read(h, INTR_PENDING);
+	/* Is this interrupt for us? */
+	if (istat == 0)
+		return;
+
+	/*
+	 * If there are completed commands in the completion queue,
+	 * we had better do something about it.
+	 */
+	if (istat & FIFO_NOT_EMPTY) {
+		while((a = smart2_read(h, COMMAND_COMPLETE_FIFO))) {
+			a1 = a; a &= ~3;
+			if ((c = h->cmpQ) == NULL) goto bad_completion;
+			while(c->busaddr != a) {
+				c = c->next;
+				if (c == h->cmpQ) break;
+			}
+			/*
+			 * If we've found the command, take it off the
+			 * completion Q and free it
+			 */
+			if (c->busaddr == a) {
+				removeQ(&h->cmpQ, c);
+				if (c->type == CMD_RWREQ) {
+					complete_command(c, 0);
+					cmd_free(h, c);
+				} else if (c->type == CMD_IOCTL_PEND) {
+					c->type = CMD_IOCTL_DONE;
+				}
+				continue;
+			}
+bad_completion:
+			printk("Completion of %08lx ignored\n", (unsigned long)a1);
+		}
+	}
+
+	/*
+	 * See if we can queue up some more IO (Is this safe?)
+	 */
+	do_ida_request(h->ctlr);
+}
+
+/*
+ * This timer is for timing out requests that haven't happened after
+ * IDA_TIMEOUT, or rather it _WAS_ for timing out "dead" requests.
+ * That didn't work quite like I expected and would cause crashes
+ * and other nonsense.
+ */
+static void ida_timer(unsigned long tdata)
+{
+	ctlr_info_t *h = (ctlr_info_t*)tdata;
+
+	h->timer.expires = jiffies + IDA_TIMER;
+	add_timer(&h->timer);
+	h->misc_tflags = 0;
+}
+
+/*
+ *  ida_ioctl does some miscellaneous stuff like reporting drive geometry,
+ *  setting readahead and submitting commands from userspace to the controller.
+ */
+int ida_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg)
+{
+	int ctlr = MAJOR(inode->i_rdev) - MAJOR_NR;
+	int dsk  = MINOR(inode->i_rdev) >> NWD_SHIFT;
+	int error;
+	int diskinfo[4];
+	struct hd_geometry *geo = (struct hd_geometry *)arg;
+	ida_ioctl_t *io = (ida_ioctl_t*)arg;
+	ida_ioctl_t my_io;
+
+	DBGINFO(printk("ida_ioctl %x %x %x\n", inode->i_rdev, cmd, arg));
+	switch(cmd) {
+	case HDIO_GETGEO:
+		error = verify_area(VERIFY_WRITE, geo, sizeof(*geo));
+		if (error) return error;
+ 		if (hba[ctlr]->drv[dsk].cylinders) {
+ 			diskinfo[0] = hba[ctlr]->drv[dsk].heads;
+ 			diskinfo[1] = hba[ctlr]->drv[dsk].sectors;
+ 			diskinfo[2] = hba[ctlr]->drv[dsk].cylinders;
+ 		} else {
+ 			diskinfo[0] = 0xff;
+ 			diskinfo[1] = 0x3f;
+ 			diskinfo[2] = hba[ctlr]->drv[dsk].nr_blks / (0xff*0x3f);
+ 		}
+		put_user(diskinfo[0], &geo->heads);
+		put_user(diskinfo[1], &geo->sectors);
+		put_user(diskinfo[2], &geo->cylinders);
+		put_user(ida[(ctlr<<CTLR_SHIFT)+MINOR(inode->i_rdev)].start_sect, &geo->start);
+		return 0;
+	case IDAGETDRVINFO:
+		error = verify_area(VERIFY_WRITE, io, sizeof(*io));
+		if (error) return error;
+		memcpy_tofs(&io->c.drv,&hba[ctlr]->drv[dsk],sizeof(drv_info_t));
+		return 0;
+	case BLKGETSIZE:
+		if (!arg) return -EINVAL;
+		error = verify_area(VERIFY_WRITE, (long*)arg, sizeof(long));
+		if (error) return error;
+		put_user(ida[(ctlr<<CTLR_SHIFT)+MINOR(inode->i_rdev)].nr_sects, (long*)arg);
+		return 0;
+	case BLKRASET:
+		if (!suser()) return -EACCES;
+		if (!(inode->i_rdev)) return -EINVAL;
+		if (arg>0xff) return -EINVAL;
+		read_ahead[MAJOR(inode->i_rdev)] = arg;
+		return 0;
+	case BLKRAGET:
+		if (!arg) return -EINVAL;
+		error=verify_area(VERIFY_WRITE, (int*)arg, sizeof(int));
+		if (error) return error;
+		put_user(read_ahead[MAJOR(inode->i_rdev)], (int*)arg);
+		return 0;
+	case BLKRRPART:
+		return revalidate_logvol(inode->i_rdev, 1);
+	case IDAPASSTHRU:
+		if (!suser()) return -EPERM;
+		error = verify_area(VERIFY_READ|VERIFY_WRITE, io, sizeof(*io));
+		if (error) return error;
+		memcpy_fromfs(&my_io, io, sizeof(my_io));
+		error = ida_ctlr_ioctl(ctlr, dsk, &my_io);
+		if (error) return error;
+		memcpy_tofs(io, &my_io, sizeof(my_io));
+		return 0;
+	case IDAGETCTLRSIG:
+		if (!arg) return -EINVAL;
+		error=verify_area(VERIFY_WRITE, (int*)arg, sizeof(int));
+		if (error) return error;
+		put_user(hba[ctlr]->ctlr_sig, (int*)arg);
+		return 0;
+	case IDAREVALIDATEVOLS:
+		return revalidate_allvol(inode->i_rdev);
+
+	RO_IOCTLS(inode->i_rdev, arg);
+
+	default:
+		return -EBADRQC;
+	}
+		
+}
+/*
+ * ida_ctlr_ioctl is for passing commands to the controller from userspace.
+ * The command block (io) has already been copied to kernel space for us,
+ * however, any elements in the sglist need to be copied to kernel space
+ * or copied back to userspace.
+ *
+ * Only root may perform a controller passthru command, however I'm not doing
+ * any serious sanity checking on the arguments.  Doing an IDA_WRITE_MEDIA and
+ * putting a 64M buffer in the sglist is probably a *bad* idea.
+ */
+int ida_ctlr_ioctl(int ctlr, int dsk, ida_ioctl_t *io)
+{
+	ctlr_info_t *h = hba[ctlr];
+	cmdlist_t *c;
+	void *p = NULL;
+	unsigned long flags;
+	int error;
+
+	DBGINFO(printk("ida_ctlr_ioctl %d %x %p\n", ctlr, dsk, io));
+	if ((c = cmd_alloc(h, 0)) == NULL)
+		return -ENOMEM;
+	c->ctlr = ctlr;
+	c->hdr.unit = (io->unit & UNITVALID) ? io->unit &0x7f : dsk;
+	c->hdr.prio = 0;
+	c->hdr.size = sizeof(rblk_t) >> 2;
+	c->size += sizeof(rblk_t);
+
+	c->req.hdr.next = 0;
+	c->req.hdr.rcode = 0;
+	c->req.bp = 0;
+	c->req.hdr.reserved = 0;
+
+	c->req.hdr.blk = 0;
+	c->req.hdr.blk_cnt = 0;
+	c->req.hdr.cmd = io->cmd;
+	c->type = CMD_IOCTL_PEND;
+
+	/* Pre submit processing */
+	switch(io->cmd) {
+	case PASSTHRU_A:
+		error = verify_area(VERIFY_READ|VERIFY_WRITE,
+				(void*)io->sg[0].addr, io->sg[0].size);
+		if (error) goto ioctl_err_exit;
+
+		p = kmalloc(io->sg[0].size, GFP_KERNEL);
+		if (!p) { error = -ENOMEM; goto ioctl_err_exit; }
+		memcpy_fromfs(p, (void*)io->sg[0].addr, io->sg[0].size);
+		c->req.bp = virt_to_bus(&(io->c));
+		c->req.sg[0].size = io->sg[0].size;
+		c->req.sg[0].addr = virt_to_bus(p);
+		c->req.hdr.sg_cnt = 1;
+		break;
+	case IDA_READ:
+		error = verify_area(VERIFY_WRITE,
+				(void*)io->sg[0].addr, io->sg[0].size);
+		if (error) goto ioctl_err_exit;
+		p = kmalloc(io->sg[0].size, GFP_KERNEL);
+		if (!p) { error = -ENOMEM; goto ioctl_err_exit; }
+		c->req.sg[0].size = io->sg[0].size;
+		c->req.sg[0].addr = virt_to_bus(p);
+		c->req.hdr.sg_cnt = 1;
+		break;
+	case IDA_WRITE:
+	case IDA_WRITE_MEDIA:
+		error = verify_area(VERIFY_READ,
+				(void*)io->sg[0].addr, io->sg[0].size);
+		if (error) goto ioctl_err_exit;
+		p = kmalloc(io->sg[0].size, GFP_KERNEL);
+		if (!p) { error = -ENOMEM; goto ioctl_err_exit; }
+		memcpy_fromfs(p, (void*)io->sg[0].addr, io->sg[0].size);
+		c->req.sg[0].size = io->sg[0].size;
+		c->req.sg[0].addr = virt_to_bus(p);
+		c->req.hdr.sg_cnt = 1;
+		break;
+	default:
+		c->req.sg[0].size = sizeof(io->c);
+		c->req.sg[0].addr = virt_to_bus(&io->c);
+		c->req.hdr.sg_cnt = 1;
+	}
+
+	/* Put the request on the tail of the request queue */
+	save_flags(flags);
+	cli();
+	addQ(&h->reqQ, c);
+	h->Qdepth++;
+	start_io(h);
+	restore_flags(flags);
+
+	/* Wait for completion */
+	while(c->type != CMD_IOCTL_DONE)
+		schedule();
+
+	/* Post submit processing */
+	switch(io->cmd) {
+	case PASSTHRU_A:
+	case IDA_READ:
+		memcpy_tofs((void*)io->sg[0].addr, p, io->sg[0].size);
+		/* fall through and free p */
+	case IDA_WRITE:
+	case IDA_WRITE_MEDIA:
+		kfree(p);
+		break;
+	default:
+		/* Nothing to do */
+	}
+
+	io->rcode = c->req.hdr.rcode;
+	error = 0;
+ioctl_err_exit:
+	cmd_free(h, c);
+	return error;
+}
+
+/*
+ * Sooner or later we'll want to maintain our own cache of
+ * commands.  For now, just use kmalloc to get them
+ */
+cmdlist_t * cmd_alloc(ctlr_info_t *h, int intr)
+{
+	cmdlist_t * c;
+	int i;
+
+	do {
+		i = find_first_zero_bit(h->cmd_pool_bits, NR_CMDS);
+		if (i == NR_CMDS)
+			return NULL;
+	} while(set_bit(i%32, h->cmd_pool_bits+(i/32)) != 0);
+
+	c = h->cmd_pool + i;
+	memset(c, 0, sizeof(cmdlist_t));
+	c->busaddr = virt_to_bus(c);
+	h->nr_allocs++;
+	return c;
+}
+
+void cmd_free(ctlr_info_t *h, cmdlist_t *c)
+{
+	int i = c - h->cmd_pool;
+	clear_bit(i%32, h->cmd_pool_bits+(i/32));
+	h->nr_frees++;
+}
+
+/***********************************************************************
+    name:        sendcmd
+    Send a command to an IDA using the memory mapped FIFO interface
+    and wait for it to complete.  
+    This routine should only be called at init time.
+***********************************************************************/
+int sendcmd(
+	__u8	cmd,
+	int	ctlr,
+	void	*buff,
+	size_t	size,
+	unsigned int blk,
+	unsigned int blkcnt,
+	unsigned int log_unit )
+{
+	cmdlist_t *c;
+	int complete;
+	__u32 base_ptr;
+	unsigned long temp;
+	unsigned long i;
+	ctlr_info_t *info_p = hba[ctlr];
+
+	c = cmd_alloc(info_p, 0);
+	c->ctlr = ctlr;
+	c->hdr.unit = log_unit;
+	c->hdr.prio = 0;
+	c->hdr.size = sizeof(rblk_t) >> 2;
+	c->size += sizeof(rblk_t);
+
+	/* The request information. */
+	c->req.hdr.next = 0;
+	c->req.hdr.rcode = 0;
+	c->req.bp = 0;
+	c->req.hdr.sg_cnt = 1;
+	c->req.hdr.reserved = 0;
+	
+	if (size == 0)
+		c->req.sg[0].size = 512;
+	else
+		c->req.sg[0].size = size;
+
+	c->req.hdr.blk = blk;
+	c->req.hdr.blk_cnt = blkcnt;
+	c->req.hdr.cmd = (unsigned char) cmd;
+	c->req.sg[0].addr = (__u32) virt_to_bus(buff);
+	flushcomplete(ctlr);
+	/*
+	 * Disable interrupt
+	 */
+	base_ptr = info_p->vaddr;
+	smart2_write(0, info_p, INTR_MASK);
+	/* Make sure there is room in the command FIFO */
+	/* Actually it should be completely empty at this time. */
+	for (i = 200000; i > 0; i--) {
+		temp = smart2_read(info_p, COMMAND_FIFO);
+		if (temp != 0) {
+			break;
+		}
+		udelay(10);
+DBG(
+		printk("ida%d: idaSendPciCmd FIFO full, waiting!\n",
+		       ctlr);
+);
+	}
+	/*
+	 * Send the cmd
+	 */
+	smart2_write(c->busaddr, info_p, COMMAND_FIFO);
+	complete = pollcomplete(ctlr);
+	if (complete != 1) {
+		if (complete != c->busaddr) {
+			printk(
+			"ida%d: idaSendPciCmd "
+		      "Invalid command list address returned! (%08lx)\n",
+				ctlr, (unsigned long)complete);
+			cmd_free(info_p, c);
+			return (IO_ERROR);
+		}
+	} else {
+		printk(
+			"ida%d: idaSendPciCmd Timeout out, "
+			"No command list address returned!\n",
+			ctlr);
+		cmd_free(info_p, c);
+		return (IO_ERROR);
+	}
+
+	if (c->req.hdr.rcode & 0x00FE) {
+		if (!(c->req.hdr.rcode & BIG_PROBLEM)) {
+			printk(
+			"ida%d: idaSendPciCmd, error: Controller failed "
+				"at init time "
+				"cmd: 0x%x, return code = 0x%x\n",
+				ctlr, c->req.hdr.cmd, c->req.hdr.rcode);
+
+			cmd_free(info_p, c);
+			return (IO_ERROR);
+		}
+	}
+	cmd_free(info_p, c);
+	return (IO_OK);
+}
+
+int frevalidate_logvol(kdev_t dev)
+{
+	return revalidate_logvol(dev, 0);
+}
+
+/*
+ * revalidate_allvol is for online array config utilities.  After a
+ * utility reconfigures the drives in the array, it can use this function
+ * (through an ioctl) to make the driver zap any previous disk structs for
+ * that controller and get new ones.
+ *
+ * Right now I'm using the getgeometry() function to do this, but this
+ * function should probably be finer grained and allow you to revalidate one
+ * particualar logical volume (instead of all of them on a particular
+ * controller).
+ */
+static int revalidate_allvol(kdev_t dev)
+{
+	int ctlr, i;
+	unsigned long flags;
+
+	ctlr = MAJOR(dev) - MAJOR_NR;
+	if (MINOR(dev) != 0)
+		return -ENXIO;
+
+	save_flags(flags);
+	cli();
+	if (hba[ctlr]->usage_count > 1) {
+		restore_flags(flags);
+		printk("Device busy for volume revalidation (usage=%d)\n",
+					hba[ctlr]->usage_count);
+		return -EBUSY;
+	}
+
+	hba[ctlr]->usage_count++;
+	restore_flags(flags);
+
+	/*
+	 * Set the partition and block size structures for all volumes
+	 * on this controller to zero.  We will reread all of this data
+	 */
+	memset(ida+(ctlr*256),            0, sizeof(struct hd_struct)*NWD*16);
+	memset(ida_sizes+(ctlr*256),      0, sizeof(int)*NWD*16);
+	memset(ida_blocksizes+(ctlr*256), 0, sizeof(int)*NWD*16);
+	memset(ida_hardsizes+(ctlr*256),  0, sizeof(int)*NWD*16);
+	ida_gendisk[ctlr].nr_real = 0;
+
+	/*
+	 * Tell the array controller not to give us any interupts while
+	 * we check the new geometry.  Then turn interrupts back on when
+	 * we're done.
+	 */
+	smart2_write(0, hba[ctlr], INTR_MASK);
+	getgeometry(ctlr);
+	smart2_write(FIFO_NOT_EMPTY, hba[ctlr], INTR_MASK);
+
+	ida_geninit(&ida_gendisk[ctlr]);
+	for(i=0; i<NWD; i++)
+		if (ida_sizes[(ctlr<<CTLR_SHIFT) + (i<<NWD_SHIFT)])
+			revalidate_logvol(dev+(i<<NWD_SHIFT), 2);
+
+	hba[ctlr]->usage_count--;
+	return 0;
+}
+
+/* Borrowed and adapted from sd.c */
+int revalidate_logvol(kdev_t dev, int maxusage)
+{
+	int ctlr, target;
+	struct gendisk *gdev;
+	unsigned long flags;
+	int max_p;
+	int start;
+	int i;
+
+	target = DEVICE_NR(dev);
+	ctlr = MAJOR(dev) - MAJOR_NR;
+	gdev = &ida_gendisk[ctlr];
+	
+	save_flags(flags);
+	cli();
+	if (hba[ctlr]->drv[target].usage_count > maxusage) {
+		restore_flags(flags);
+		printk("Device busy for revalidation (usage=%d)\n",
+					hba[ctlr]->drv[target].usage_count);
+		return -EBUSY;
+	}
+
+	hba[ctlr]->drv[target].usage_count++;
+	restore_flags(flags);
+
+	max_p = gdev->max_p;
+	start = target << gdev->minor_shift;
+
+	for(i=max_p; i>=0; i--) {
+		int minor = start+i;
+		kdev_t devi = MKDEV(MAJOR_NR + ctlr, minor);
+		sync_dev(devi);
+		invalidate_inodes(devi);
+		invalidate_buffers(devi);
+		gdev->part[minor].start_sect = 0;	
+		gdev->part[minor].nr_sects = 0;	
+
+		/* reset the blocksize so we can read the partition table */
+		blksize_size[MAJOR_NR+ctlr][minor] = 1024;
+	}
+
+	gdev->part[start].nr_sects =  hba[ctlr]->drv[target].nr_blks;
+	resetup_one_dev(gdev, target);
+	hba[ctlr]->drv[target].usage_count--;
+	return 0;
+}
+
+
+/********************************************************************
+    name: pollcomplete
+    Wait polling for a command to complete.
+    The memory mapped FIFO is polled for the completion.
+    Used only at init time, interrupts disabled.
+ ********************************************************************/
+int pollcomplete(int ctlr)
+{
+	int done;
+	int i;
+
+	/* Wait (up to 2 seconds) for a command to complete */
+
+	for (i = 200000; i > 0; i--) {
+		done = smart2_read(hba[ctlr], COMMAND_COMPLETE_FIFO);
+		if (done == 0) {
+			udelay(10);	/* a short fixed delay */
+		} else
+			return (done);
+	}
+	/* Invalid address to tell caller we ran out of time */
+	return 1;
+}
+
+/*
+ * Clear the complete FIFO
+
+ Polling routine.
+ This should only be used at init time.
+ Any commands unexpectedly found in the completed command fifo
+ will be discarded.  There should be none.
+ Called in only one place.
+ Note this reads and discards any completed commands but does not
+ wait for any uncompleted commands.
+ This is kinda goofy.
+
+ */
+void flushcomplete(int ctlr)
+{
+	unsigned long ret_addr;
+	unsigned int i;
+
+	for (i = 200000; i > 0; i--) {
+		ret_addr = smart2_read(hba[ctlr], COMMAND_COMPLETE_FIFO);
+		if (ret_addr == 0) {
+			break;
+		}
+		udelay(10);
+DBG(
+		printk("ida%d: flushcomplete "
+		       "Discarding completion %x!\n",
+		       ctlr, (unsigned int)ret_addr);
+);
+	}
+}
+
+
+
+/*****************************************************************
+    idaGetGeometry
+    Get ida logical volume geometry from the controller 
+    This is a large bit of code which once existed in two flavors,
+    for EISA and PCI.  It is used only at init time.
+****************************************************************
+*/
+void getgeometry(int ctlr)
+{				
+	id_log_drv_t *id_ldrive;
+	id_ctlr_t *id_ctlr_buf;
+	sense_log_drv_stat_t *id_lstatus_buf;
+	config_t *sense_config_buf;
+	unsigned int log_unit, log_index;
+	int ret_code, size;
+	drv_info_t *drv;
+	ctlr_info_t *info_p = hba[ctlr];
+
+	id_ldrive = (id_log_drv_t *)kmalloc(sizeof(id_log_drv_t), GFP_KERNEL);
+	id_ctlr_buf = (id_ctlr_t *)kmalloc(sizeof(id_ctlr_t), GFP_KERNEL);
+	id_lstatus_buf = (sense_log_drv_stat_t *)kmalloc(sizeof(sense_log_drv_stat_t), GFP_KERNEL);
+	sense_config_buf = (config_t *)kmalloc(sizeof(config_t), GFP_KERNEL);
+
+	memset(id_ldrive, 0, sizeof(id_log_drv_t));
+	memset(id_ctlr_buf, 0, sizeof(id_ctlr_t));
+	memset(id_lstatus_buf, 0, sizeof(sense_log_drv_stat_t));
+	memset(sense_config_buf, 0, sizeof(config_t));
+
+	info_p->phys_drives = 0;
+	info_p->log_drv_map = 0;
+	info_p->drv_assign_map = 0;
+	info_p->drv_spare_map = 0;
+	info_p->mp_failed_drv_map = 0;	/* only initialized here */
+	/* Get controllers info for this logical drive */
+	ret_code = sendcmd(ID_CTLR, ctlr, id_ctlr_buf, 0, 0, 0, 0);
+	if (ret_code == IO_ERROR) {
+		/*
+		 * If can't get controller info, set the logical drive map to 0,
+		 * so the idastubopen will fail on all logical drives
+		 * on the controller.
+		 */
+		goto geo_ret;	/* release the buf and return */
+	}
+	info_p->log_drives = id_ctlr_buf->nr_drvs;;
+	*(__u32*)(info_p->firm_rev) = *(__u32*)(id_ctlr_buf->firm_rev);
+	info_p->ctlr_sig = id_ctlr_buf->cfg_sig;
+	info_p->board_id = id_ctlr_buf->board_id;
+
+	switch(info_p->board_id) {
+		case 0x0E114030: /* SMART-2/E */
+			info_p->product = 1;
+			break;
+		case 0x40300E11: /* SMART-2/P or SMART-2DH */
+			info_p->product = 2;
+			break;
+		case 0x40310E11: /* SMART-2SL */
+			info_p->product = 3;
+			break;
+		case 0x40320E11: /* SMART-3200 */
+			info_p->product = 4;
+			break;
+		case 0x40330E11: /* SMART-3100ES */
+			info_p->product = 5;
+			break;
+		default:  
+			/*
+			 * Well, its a SMART-2 or better, don't know which
+			 * kind.
+ 			 */
+			info_p->product = 0;
+	}
+	printk(" (%s)\n", product_names[info_p->product]);
+	/*
+	 * Initialize logical drive map to zero
+	 */
+#ifdef REDUNDANT
+	info_p->log_drive_map = 0;
+#endif				/* #ifdef REDUNDANT */
+	log_index = 0;
+	/*
+	 * Get drive geometry for all logical drives
+	 */
+	if (id_ctlr_buf->nr_drvs > 16)
+		printk("ida%d:  This driver supports 16 logical drives "
+			"per controller.  Additional drives will not be "
+			"detected.\n", ctlr);
+
+	for (log_unit = 0;
+	     (log_index < id_ctlr_buf->nr_drvs)
+	     && (log_unit < NWD);
+	     log_unit++) {
+
+		size = sizeof(sense_log_drv_stat_t);
+
+		/*
+		   Send "Identify logical drive status" cmd
+		 */
+		ret_code = sendcmd(SENSE_LOG_DRV_STAT,
+			     ctlr, id_lstatus_buf, size, 0, 0, log_unit);
+		if (ret_code == IO_ERROR) {
+			/*
+			   If can't get logical drive status, set
+			   the logical drive map to 0, so the
+			   idastubopen will fail for all logical drives
+			   on the controller. 
+			 */
+			info_p->log_drv_map = 0;	
+			printk(
+			     "ida%d: idaGetGeometry - Controller failed "
+				"to report status of logical drive %d\n"
+			 "Access to this controller has been disabled\n",
+				ctlr, log_unit);
+			goto geo_ret;	/* release the buf and return */
+
+		}
+		/*
+		   Make sure the logical drive is configured
+		 */
+		if (id_lstatus_buf->status != LOG_NOT_CONF) {
+			ret_code = sendcmd(ID_LOG_DRV, ctlr, id_ldrive,
+			       sizeof(id_log_drv_t), 0, 0, log_unit);
+			/*
+			   If error, the bit for this
+			   logical drive won't be set and
+			   idastubopen will return error. 
+			 */
+			if (ret_code != IO_ERROR) {
+				drv = &info_p->drv[log_unit];
+				drv->blk_size = id_ldrive->blk_size;
+				drv->nr_blks = id_ldrive->nr_blks;
+ 				drv->cylinders = id_ldrive->drv.cyl;
+ 				drv->heads = id_ldrive->drv.heads;
+ 				drv->sectors = id_ldrive->drv.sect_per_track;
+				info_p->log_drv_map |=	(1 << log_unit);
+
+				printk("ida/c%dd%d: blksz=%d nr_blks=%d\n",
+						ctlr, log_unit, drv->blk_size,
+						drv->nr_blks);
+				ret_code = sendcmd(SENSE_CONFIG,
+						  ctlr, sense_config_buf,
+				 sizeof(config_t), 0, 0, log_unit);
+				if (ret_code == IO_ERROR) {
+					info_p->log_drv_map = 0;
+					goto geo_ret;	/* release the buf and return */
+				}
+				info_p->phys_drives =
+				    sense_config_buf->ctlr_phys_drv;
+				info_p->drv_assign_map
+				    |= sense_config_buf->drv_asgn_map;
+				info_p->drv_assign_map
+				    |= sense_config_buf->spare_asgn_map;
+				info_p->drv_spare_map
+				    |= sense_config_buf->spare_asgn_map;
+			}	/* end of if no error on id_ldrive */
+			log_index = log_index + 1;
+		}		/* end of if logical drive configured */
+	}			/* end of for log_unit */
+      geo_ret:
+	kfree(id_ctlr_buf);
+	kfree(id_ldrive);
+	kfree(id_lstatus_buf);
+	kfree(sense_config_buf);
+}
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/cpqarray.h linux/drivers/block/cpqarray.h
--- linux.vanilla/drivers/block/cpqarray.h	Thu Jan  1 01:00:00 1970
+++ linux/drivers/block/cpqarray.h	Tue Mar  2 00:39:51 1999
@@ -0,0 +1,96 @@
+/*
+ *    Disk Array driver for Compaq SMART2 Controllers
+ *    Copyright 1998 Compaq Computer Corporation
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ *    NON INFRINGEMENT.  See the GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *    Questions/Comments/Bugfixes to arrays@compaq.com
+ *
+ *    If you want to make changes, improve or add functionality to this
+ *    driver, you'll probably need the Compaq Array Controller Interface
+ *    Specificiation (Document number ECG086/1198)
+ */
+#ifndef CPQARRAY_H
+#define CPQARRAY_H
+
+#ifdef __KERNEL__
+#include <linux/blkdev.h>
+#include <linux/locks.h>
+#include <linux/malloc.h>
+#include <linux/config.h>
+#include <linux/md.h>
+#include <linux/timer.h>
+#endif
+
+#include "ida_cmd.h"
+
+#define IO_OK		0
+#define IO_ERROR	1
+#define NWD		16
+#define NWD_SHIFT	4
+
+#define IDA_TIMER	(5*HZ)
+#define IDA_TIMEOUT	(10*HZ)
+
+#define MISC_NONFATAL_WARN	0x01
+
+typedef struct {
+	unsigned blk_size;
+	unsigned nr_blks;
+	unsigned cylinders;
+	unsigned heads;
+	unsigned sectors;
+	int usage_count;
+} drv_info_t;
+
+typedef struct {
+	int	ctlr;
+	char	devname[8];
+	__u32	log_drv_map;
+	__u32	drv_assign_map;
+	__u32	drv_spare_map;
+	__u32	mp_failed_drv_map;
+
+	char	firm_rev[4];
+	int	product;
+	int	ctlr_sig;
+
+	int	log_drives;
+	int	phys_drives;
+
+	__u32	board_id;
+	__u32	vaddr;
+	__u32	paddr;
+	__u32	ioaddr;
+	int	intr;
+	int	usage_count;
+	drv_info_t	drv[NWD];
+	int	proc;
+
+	cmdlist_t *reqQ;
+	cmdlist_t *cmpQ;
+	cmdlist_t *cmd_pool;
+	__u32	*cmd_pool_bits;
+	unsigned int Qdepth;
+	unsigned int maxQsinceinit;
+
+	unsigned int nr_requests;
+	unsigned int nr_allocs;
+	unsigned int nr_frees;
+	struct timer_list timer;
+	unsigned int misc_tflags;
+} ctlr_info_t;
+
+#endif /* CPQARRAY_H */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/floppy.c linux/drivers/block/floppy.c
--- linux.vanilla/drivers/block/floppy.c	Sun Dec  6 00:14:36 1998
+++ linux/drivers/block/floppy.c	Mon Mar  1 21:24:52 1999
@@ -96,6 +96,11 @@
  * features to asm/floppy.h.
  */
 
+/*
+ * 1999/02/23 -- Paul Slootman -- floppy stopped working on Alpha after 24
+ * days, 6 hours, 32 minutes and 32 seconds (i.e. MAXINT jiffies; ints were
+ * being used to store jiffies, which are unsigned longs).
+ */
 
 #define FLOPPY_SANITY_CHECK
 #undef  FLOPPY_SILENT_DCL_CLEAR
@@ -521,6 +526,8 @@
 static unsigned char current_drive = 0;
 static long current_count_sectors = 0;
 static unsigned char sector_t; /* sector in track */
+static unsigned char in_sector_offset; /* offset within physical sector,
+                                        * expressed in units of 512 bytes */
 
 #ifndef fd_eject
 #define fd_eject(x) -EINVAL
@@ -571,10 +578,10 @@
 #define OLOGSIZE 20
 
 static void (*lasthandler)(void) = NULL;
-static int interruptjiffies=0;
-static int resultjiffies=0;
+static unsigned long interruptjiffies=0;
+static unsigned long resultjiffies=0;
 static int resultsize=0;
-static int lastredo=0;
+static unsigned long lastredo=0;
 
 static struct output_log {
 	unsigned char data;
@@ -677,7 +684,7 @@
 #ifdef DCL_DEBUG
 	if (UDP->flags & FD_DEBUG){
 		DPRINT("checking disk change line for drive %d\n",drive);
-		DPRINT("jiffies=%ld\n", jiffies);
+		DPRINT("jiffies=%lu\n", jiffies);
 		DPRINT("disk change line=%x\n",fd_inb(FD_DIR)&0x80);
 		DPRINT("flags=%x\n",UDRS->flags);
 	}
@@ -1224,7 +1231,7 @@
 static void fdc_specify(void)
 {
 	unsigned char spec1, spec2;
-	int srt, hlt, hut;
+	unsigned long srt, hlt, hut;
 	unsigned long dtr = NOMINAL_DTR;
 	unsigned long scale_dtr = NOMINAL_DTR;
 	int hlt_max_code = 0x7f;
@@ -1407,7 +1414,8 @@
  */
 static void setup_rw_floppy(void)
 {
-	int i,ready_date,r, flags,dflags;
+	int i,r, flags,dflags;
+	unsigned long ready_date;
 	timeout_fn function;
 
 	flags = raw_cmd->flags;
@@ -1480,7 +1488,7 @@
 #ifdef DCL_DEBUG
 		if (DP->flags & FD_DEBUG){
 			DPRINT("clearing NEWCHANGE flag because of effective seek\n");
-			DPRINT("jiffies=%ld\n", jiffies);
+			DPRINT("jiffies=%lu\n", jiffies);
 		}
 #endif
 		CLEARF(FD_DISK_NEWCHANGE); /* effective seek */
@@ -1760,20 +1768,20 @@
 	printk("\n");
 	printk("floppy driver state\n");
 	printk("-------------------\n");
-	printk("now=%ld last interrupt=%d last called handler=%p\n",
-	       jiffies, interruptjiffies, lasthandler);
+	printk("now=%lu last interrupt=%lu diff=%lu last called handler=%p\n",
+	       jiffies, interruptjiffies, jiffies-interruptjiffies, lasthandler);
 
 
 #ifdef FLOPPY_SANITY_CHECK
 	printk("timeout_message=%s\n", timeout_message);
 	printk("last output bytes:\n");
 	for (i=0; i < OLOGSIZE; i++)
-		printk("%2x %2x %ld\n",
+		printk("%2x %2x %lu\n",
 		       output_log[(i+output_log_pos) % OLOGSIZE].data,
 		       output_log[(i+output_log_pos) % OLOGSIZE].status,
 		       output_log[(i+output_log_pos) % OLOGSIZE].jiffies);
-	printk("last result at %d\n", resultjiffies);
-	printk("last redo_fd_request at %d\n", lastredo);
+	printk("last result at %lu\n", resultjiffies);
+	printk("last redo_fd_request at %lu\n", lastredo);
 	for (i=0; i<resultsize; i++){
 		printk("%2x ", reply_buffer[i]);
 	}
@@ -1790,8 +1798,8 @@
 		printk("fd_timer.function=%p\n", fd_timer.function);
 	if (fd_timeout.prev){
 		printk("timer_table=%p\n",fd_timeout.function);
-		printk("expires=%ld\n",fd_timeout.expires-jiffies);
-		printk("now=%ld\n",jiffies);
+		printk("expires=%lu\n",fd_timeout.expires-jiffies);
+		printk("now=%lu\n",jiffies);
 	}
 	printk("cont=%p\n", cont);
 	printk("CURRENT=%p\n", CURRENT);
@@ -2056,7 +2064,7 @@
 
 #define CODE2SIZE (ssize = ((1 << SIZECODE) + 3) >> 2)
 #define FM_MODE(x,y) ((y) & ~(((x)->rate & 0x80) >>1))
-#define CT(x) ((x) | 0x40)
+#define CT(x) ((x) | 0xc0)
 static void setup_format_params(int track)
 {
 	struct fparm {
@@ -2224,7 +2232,7 @@
 /* Interrupt handler evaluating the result of the r/w operation */
 static void rw_interrupt(void)
 {
-	int nr_sectors, ssize, eoc;
+	int nr_sectors, ssize, eoc, heads;
 
 	if (!DRS->first_read_date)
 		DRS->first_read_date = jiffies;
@@ -2236,23 +2244,32 @@
 		eoc = 1;
 	else
 		eoc = 0;
-	nr_sectors = ((R_TRACK-TRACK)*_floppy->head+R_HEAD-HEAD) *
-		_floppy->sect + ((R_SECTOR-SECTOR+eoc) <<  SIZECODE >> 2) -
-		(sector_t % _floppy->sect) % ssize;
+
+	if(COMMAND & 0x80)
+		heads = 2;
+	else
+		heads = 1;
+
+	nr_sectors = (((R_TRACK-TRACK) * heads + 
+				   R_HEAD-HEAD) * SECT_PER_TRACK +
+				  R_SECTOR-SECTOR + eoc) << SIZECODE >> 2;
 
 #ifdef FLOPPY_SANITY_CHECK
-	if (nr_sectors > current_count_sectors + ssize -
-	     (current_count_sectors + sector_t) % ssize +
-	     sector_t % ssize){
+	if (nr_sectors / ssize > 
+		(in_sector_offset + current_count_sectors + ssize - 1)/ssize) {
 		DPRINT("long rw: %x instead of %lx\n",
 			nr_sectors, current_count_sectors);
 		printk("rs=%d s=%d\n", R_SECTOR, SECTOR);
 		printk("rh=%d h=%d\n", R_HEAD, HEAD);
 		printk("rt=%d t=%d\n", R_TRACK, TRACK);
-		printk("spt=%d st=%d ss=%d\n", SECT_PER_TRACK,
-		       sector_t, ssize);
+		printk("heads=%d eoc=%d\n", heads, eoc);
+		printk("spt=%d st=%d ss=%d\n", SECT_PER_TRACK, 
+			   sector_t, ssize);
+		printk("in_sector_offset=%d\n", in_sector_offset);
 	}
 #endif
+
+	nr_sectors -= in_sector_offset;
 	INFBOUND(nr_sectors,0);
 	SUPBOUND(current_count_sectors, nr_sectors);
 
@@ -2424,6 +2441,32 @@
 #endif
 }
 
+/* work around a bug in pseudo DMA
+ * (on some FDCs) pseudo DMA does not stop when the CPU stops
+ * sending data.  Hence we need a different way to signal the
+ * transfer length:  We use SECT_PER_TRACK.  Unfortunately, this
+ * does not work with MT, hence we can only transfer one head at
+ * a time
+ */
+static void virtualdmabug_workaround(void) {
+	int hard_sectors, end_sector;
+	if(CT(COMMAND) == FD_WRITE) {
+		COMMAND &= ~0x80; /* switch off multiple track mode */
+	   
+		hard_sectors = raw_cmd->length >> (7 + SIZECODE);
+		end_sector = SECTOR + hard_sectors - 1;
+#ifdef FLOPPY_SANITY_CHECK
+		if(end_sector > SECT_PER_TRACK) {
+			printk("too many sectors %d > %d\n", 
+				   end_sector, SECT_PER_TRACK);
+			return;
+		}
+#endif
+		SECT_PER_TRACK = end_sector; /* make sure SECT_PER_TRACK points
+									  * to end of transfer */
+	}
+}
+
 /*
  * Formulate a read/write request.
  * this routine decides where to load the data (directly to buffer, or to
@@ -2495,11 +2538,17 @@
 	CODE2SIZE;
 	SECT_PER_TRACK = _floppy->sect << 2 >> SIZECODE;
 	SECTOR = ((sector_t % _floppy->sect) << 2 >> SIZECODE) + 1;
+
+	/* tracksize describes the size which can be filled up with sectors
+	 * of size ssize.
+	 */
 	tracksize = _floppy->sect - _floppy->sect % ssize;
 	if (tracksize < _floppy->sect){
 		SECT_PER_TRACK ++;
 		if (tracksize <= sector_t % _floppy->sect)
 			SECTOR--;
+
+		/* if we are beyond tracksize, fill up using smaller sectors */
 		while (tracksize <= sector_t % _floppy->sect){
 			while(tracksize + ssize > _floppy->sect){
 				SIZECODE--;
@@ -2509,10 +2558,15 @@
 			tracksize += ssize;
 		}
 		max_sector = HEAD * _floppy->sect + tracksize;
-	} else if (!TRACK && !HEAD && !(_floppy->rate & FD_2M) && probing)
+	} else if (!TRACK && !HEAD && !(_floppy->rate & FD_2M) && probing) {
+		max_sector = _floppy->sect;
+	} else if (!HEAD && CT(COMMAND) == FD_WRITE) {
+		/* for virtual DMA bug workaround */
 		max_sector = _floppy->sect;
+	}
 
-	aligned_sector_t = sector_t - (sector_t % _floppy->sect) % ssize;
+	in_sector_offset = (sector_t % _floppy->sect) % ssize;
+	aligned_sector_t = sector_t - in_sector_offset;
 	max_size = CURRENT->nr_sectors;
 	if ((raw_cmd->track == buffer_track) && 
 	    (current_drive == buffer_drive) &&
@@ -2522,7 +2576,7 @@
 			copy_buffer(1, max_sector, buffer_max);
 			return 1;
 		}
-	} else if (aligned_sector_t != sector_t || CURRENT->nr_sectors < ssize){
+	} else if (in_sector_offset || CURRENT->nr_sectors < ssize){
 		if (CT(COMMAND) == FD_WRITE){
 			if (sector_t + CURRENT->nr_sectors > ssize &&
 			    sector_t + CURRENT->nr_sectors < ssize + ssize)
@@ -2575,6 +2629,7 @@
 					indirect, direct, sector_t);
 				return 0;
 			}
+			virtualdmabug_workaround();
 			return 2;
 		}
 	}
@@ -2588,7 +2643,7 @@
 	    sector_t > buffer_max ||
 	    sector_t < buffer_min ||
 	    ((CT(COMMAND) == FD_READ ||
-	      (aligned_sector_t == sector_t && CURRENT->nr_sectors >= ssize))&&
+	      (!in_sector_offset && CURRENT->nr_sectors >= ssize))&&
 	     max_sector > 2 * max_buffer_sectors + buffer_min &&
 	     max_size + sector_t > 2 * max_buffer_sectors + buffer_min)
 	    /* not enough space */){
@@ -2605,7 +2660,7 @@
 		 * is either aligned or the data already in the buffer
 		 * (buffer will be overwritten) */
 #ifdef FLOPPY_SANITY_CHECK
-		if (sector_t != aligned_sector_t && buffer_track == -1)
+		if (in_sector_offset && buffer_track == -1)
 			DPRINT("internal error offset !=0 on write\n");
 #endif
 		buffer_track = raw_cmd->track;
@@ -2616,7 +2671,7 @@
 			      2*max_buffer_sectors+buffer_min-aligned_sector_t);
 
 	/* round up current_count_sectors to get dma xfer size */
-	raw_cmd->length = sector_t+current_count_sectors-aligned_sector_t;
+	raw_cmd->length = in_sector_offset+current_count_sectors;
 	raw_cmd->length = ((raw_cmd->length -1)|(ssize-1))+1;
 	raw_cmd->length <<= 9;
 #ifdef FLOPPY_SANITY_CHECK
@@ -2678,6 +2733,8 @@
 		return 0;
 	}
 #endif
+
+	virtualdmabug_workaround();
 	return 2;
 }
 
@@ -3663,7 +3720,7 @@
 	if (UTESTF(FD_DISK_CHANGED) || UTESTF(FD_VERIFY))
 		return 1;
 
-	if (UDP->checkfreq < jiffies - UDRS->last_checked){
+	if (UDP->checkfreq < (int)(jiffies - UDRS->last_checked)) {
 		lock_fdc(drive,0);
 		poll_drive(0,0);
 		process_fd_request();
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/genhd.c linux/drivers/block/genhd.c
--- linux.vanilla/drivers/block/genhd.c	Sun Jun 21 18:41:09 1998
+++ linux/drivers/block/genhd.c	Fri Mar 26 16:56:23 1999
@@ -51,6 +51,9 @@
 
 extern int chr_dev_init(void);
 extern int blk_dev_init(void);
+#ifdef CONFIG_BLK_DEV_DAC960
+extern void DAC960_Initialize(void);
+#endif
 extern int scsi_dev_init(void);
 extern int net_dev_init(void);
 
@@ -83,6 +86,29 @@
 			maj = "hd";
 	}
 #endif
+	if (hd->major >= DAC960_MAJOR+0 && hd->major <= DAC960_MAJOR+7)
+	  {
+	    int controller = hd->major - DAC960_MAJOR;
+	    int partition = minor & ((1 << hd->minor_shift) - 1);
+	    if (partition == 0)
+	      sprintf(buf, "%s/c%dd%d",
+		      maj, controller, minor >> hd->minor_shift);
+	    else sprintf(buf, "%s/c%dd%dp%d",
+			 maj, controller, minor >> hd->minor_shift, partition);
+	    return buf;
+	  }
+#if defined(CONFIG_BLK_CPQ_DA) || defined(CONFIG_BLK_CPQ_DA_MODULE)
+	if (hd->major >= COMPAQ_SMART2_MAJOR && hd->major <= COMPAQ_SMART2_MAJOR+7) {
+		int ctlr = hd->major - COMPAQ_SMART2_MAJOR;
+		int disk = minor >> hd->minor_shift;
+		int part = minor & (( 1 << hd->minor_shift) - 1);
+		if (part == 0)
+			sprintf(buf, "%s/c%dd%d", maj, ctlr, disk);
+		else
+			sprintf(buf, "%s/c%dd%dp%d", maj, ctlr, disk, part);
+		return buf;
+	}
+#endif
 	part = minor & ((1 << hd->minor_shift) - 1);
 	if (part)
 		sprintf(buf, "%s%c%d", maj, unit, part);
@@ -93,10 +119,18 @@
 
 static void add_partition (struct gendisk *hd, int minor, int start, int size)
 {
-	char buf[8];
+	char buf[40];
 	hd->part[minor].start_sect = start;
 	hd->part[minor].nr_sects   = size;
-	printk(" %s", disk_name(hd, minor, buf));
+	if (hd->major >= DAC960_MAJOR+0 && hd->major <= DAC960_MAJOR+7)
+		printk(" p%d", (minor & ((1 << hd->minor_shift) - 1)));
+	else
+#if defined(CONFIG_BLK_CPQ_DA) || defined(CONFIG_BLK_CPQ_DA_MODULE)
+	if (hd->major >= COMPAQ_SMART2_MAJOR && hd->major <= COMPAQ_SMART2_MAJOR+7) 
+		printk(" p%d", (minor & ((1 << hd->minor_shift) - 1)));
+	else
+#endif
+		printk(" %s", disk_name(hd, minor, buf));
 }
 
 static inline int is_extended_partition(struct partition *p)
@@ -332,7 +366,9 @@
 				   && (q->sector & 63) == 1
 				   && (q->end_sector & 63) == 63) {
 					unsigned int heads = q->end_head + 1;
-					if (heads == 32 || heads == 64 || heads == 128 || heads == 255) {
+					if (heads == 32 || heads == 64 ||
+					    heads == 128 || heads == 240 ||
+					    heads == 255) {
 
 						(void) ide_xlate_1024(dev, heads, " [PTBL]");
 						break;
@@ -641,7 +677,7 @@
 {
 	static int first_time = 1;
 	unsigned long first_sector;
-	char buf[8];
+	char buf[40];
 
 	if (first_time)
 		printk("Partition check:\n");
@@ -733,14 +769,21 @@
 void device_setup(void)
 {
 	extern void console_map_init(void);
+	extern void cpqarray_init(void);
 	struct gendisk *p;
 	int nr=0;
 
 	chr_dev_init();
 	blk_dev_init();
 	sti();
+#ifdef CONFIG_BLK_DEV_DAC960
+	DAC960_Initialize();
+#endif
 #ifdef CONFIG_SCSI
 	scsi_dev_init();
+#endif
+#ifdef CONFIG_BLK_CPQ_DA
+	cpqarray_init();
 #endif
 #ifdef CONFIG_INET
 	net_dev_init();
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/hd.c linux/drivers/block/hd.c
--- linux.vanilla/drivers/block/hd.c	Fri Jul 24 17:28:57 1998
+++ linux/drivers/block/hd.c	Sun Dec 27 21:19:47 1998
@@ -871,6 +871,7 @@
 			restore_flags(flags);
 			return err;
 
+		case HDIO_OBSOLETE_IDENTITY:
 		case HDIO_GET_IDENTITY:
 			if (!arg)  return -EINVAL;
 			if (MINOR(inode->i_rdev) & 0x3F) return -EINVAL;
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/ida_cmd.h linux/drivers/block/ida_cmd.h
--- linux.vanilla/drivers/block/ida_cmd.h	Thu Jan  1 01:00:00 1970
+++ linux/drivers/block/ida_cmd.h	Tue Mar  2 00:40:03 1999
@@ -0,0 +1,338 @@
+/*
+ *    Disk Array driver for Compaq SMART2 Controllers
+ *    Copyright 1998 Compaq Computer Corporation
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ *    NON INFRINGEMENT.  See the GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *    Questions/Comments/Bugfixes to arrays@compaq.com
+ *
+ *    If you want to make changes, improve or add functionality to this
+ *    driver, you'll probably need the Compaq Array Controller Interface
+ *    Specificiation (Document number ECG086/1198)
+ */
+#ifndef ARRAYCMD_H
+#define ARRAYCMD_H
+
+#include <asm/types.h>
+#if 0
+#include <linux/blkdev.h>
+#endif
+
+
+#define COMMAND_FIFO		0x04
+#define COMMAND_COMPLETE_FIFO	0x08
+#define INTR_MASK		0x0C
+#define INTR_STATUS		0x10
+#define INTR_PENDING		0x14
+
+#define FIFO_NOT_EMPTY		0x01
+#define FIFO_NOT_FULL		0x02
+
+#define BIG_PROBLEM		0x40
+#define LOG_NOT_CONF		2
+
+#pragma pack(1)
+typedef struct {
+	__u32	size;
+	__u32	addr;
+} sg_t;
+
+#define RCODE_NONFATAL	0x02
+#define RCODE_FATAL	0x04
+#define RCODE_INVREQ	0x10
+typedef struct {
+	__u16	next;
+	__u8	cmd;
+	__u8	rcode;
+	__u32	blk;
+	__u16	blk_cnt;
+	__u8	sg_cnt;
+	__u8	reserved;
+} rhdr_t;
+
+#define SG_MAX			32
+typedef struct {
+	rhdr_t	hdr;
+	sg_t	sg[SG_MAX];
+	__u32	bp;
+} rblk_t;
+
+typedef struct {
+	__u8	unit;
+	__u8	prio;
+	__u16	size;
+} chdr_t;
+
+#define CMD_RWREQ	0x00
+#define CMD_IOCTL_PEND	0x01
+#define CMD_IOCTL_DONE	0x02
+
+typedef struct cmdlist {
+	chdr_t	hdr;
+	rblk_t	req;
+	__u32	size;
+	int	retry_cnt;
+	__u32	busaddr;
+	int	ctlr;
+	struct cmdlist *prev;
+	struct cmdlist *next;
+	struct buffer_head *bh;
+	int type;
+} cmdlist_t;
+	
+#define ID_CTLR		0x11
+typedef struct {
+	__u8	nr_drvs;
+	__u32	cfg_sig;
+	__u8	firm_rev[4];
+	__u8	rom_rev[4];
+	__u8	hw_rev;
+	__u32	bb_rev;
+	__u32	drv_present_map;
+	__u32	ext_drv_map;
+	__u32	board_id;
+	__u8	cfg_error;
+	__u32	non_disk_bits;
+	__u8	bad_ram_addr;
+	__u8	cpu_rev;
+	__u8	pdpi_rev;
+	__u8	epic_rev;
+	__u8	wcxc_rev;
+	__u8	marketing_rev;
+	__u8	ctlr_flags;
+	__u8	host_flags;
+	__u8	expand_dis;
+	__u8	scsi_chips;
+	__u32	max_req_blocks;
+	__u32	ctlr_clock;
+	__u8	drvs_per_bus;
+	__u16	big_drv_present_map[8];
+	__u16	big_ext_drv_map[8];
+	__u16	big_non_disk_map[8];
+	__u16	task_flags;
+	__u8	icl_bus;
+	__u8	red_modes;
+	__u8	cur_red_mode;
+	__u8	red_ctlr_stat;
+	__u8	red_fail_reason;
+	__u8	reserved[403];
+} id_ctlr_t;
+
+typedef struct {
+	__u16	cyl;
+	__u8	heads;
+	__u8	xsig;
+	__u8	psectors;
+	__u16	wpre;
+	__u8	maxecc;
+	__u8	drv_ctrl;
+	__u16	pcyls;
+	__u8	pheads;
+	__u16	landz;
+	__u8	sect_per_track;
+	__u8	cksum;
+} drv_param_t;
+
+#define ID_LOG_DRV	0x10
+typedef struct {
+	__u16	blk_size;
+	__u32	nr_blks;
+	drv_param_t drv;
+	__u8	fault_tol;
+	__u8	reserved;
+	__u8	bios_disable;
+} id_log_drv_t;
+
+#define ID_LOG_DRV_EXT	0x18
+typedef struct {
+	__u32	log_drv_id;
+	__u8	log_drv_label[64];
+	__u8	reserved[418];
+} id_log_drv_ext_t;
+
+#define SENSE_LOG_DRV_STAT	0x12
+typedef struct {
+	__u8	status;
+	__u32	fail_map;
+	__u16	read_err[32];
+	__u16	write_err[32];
+	__u8	drv_err_data[256];
+	__u8	drq_timeout[32];
+	__u32	blks_to_recover;
+	__u8	drv_recovering;
+	__u16	remap_cnt[32];
+	__u32	replace_drv_map;
+	__u32	act_spare_map;
+	__u8	spare_stat;
+	__u8	spare_repl_map[32];
+	__u32	repl_ok_map;
+	__u8	media_exch;
+	__u8	cache_fail;
+	__u8	expn_fail;
+	__u8	unit_flags;
+	__u16	big_fail_map[8];
+	__u16	big_remap_map[8];
+	__u16	big_repl_map[8];
+	__u16	big_act_spare_map[8];
+	__u8	big_spar_repl_map[128];
+	__u16	big_repl_ok_map[8];
+	__u8	big_drv_rebuild;
+	__u8	reserved[36];
+} sense_log_drv_stat_t;
+
+#define START_RECOVER		0x13
+
+#define ID_PHYS_DRV		0x15
+typedef struct {
+	__u8	scsi_bus;
+	__u8	scsi_id;
+	__u16	blk_size;
+	__u32	nr_blks;
+	__u32	rsvd_blks;
+	__u8	drv_model[40];
+	__u8	drv_sn[40];
+	__u8	drv_fw[8];
+	__u8	scsi_iq_bits;
+	__u8	compaq_drv_stmp;
+	__u8	last_fail;
+	__u8	phys_drv_flags;
+	__u8	phys_drv_flags1;
+	__u8	scsi_lun;
+	__u8	phys_drv_flags2;
+	__u8	reserved;
+	__u32	spi_speed_rules;
+	__u8	phys_connector[2];
+	__u8	phys_box_on_bus;
+	__u8	phys_bay_in_box;
+} id_phys_drv_t;
+
+#define BLINK_DRV_LEDS		0x16
+typedef struct {
+	__u32	blink_duration;
+	__u32	reserved;
+	__u8	blink[256];
+	__u8	reserved1[248];
+} blink_drv_leds_t;
+
+#define SENSE_BLINK_LEDS	0x17
+typedef struct {
+	__u32	blink_duration;
+	__u32	btime_elap;
+	__u8	blink[256];
+	__u8	reserved1[248];
+} sense_blink_leds_t;
+
+#define IDA_READ		0x20
+#define IDA_WRITE		0x30
+#define IDA_WRITE_MEDIA		0x31
+#define RESET_TO_DIAG		0x40
+#define DIAG_PASS_THRU		0x41
+
+#define SENSE_CONFIG		0x50
+#define SET_CONFIG		0x51
+typedef struct {
+	__u32	cfg_sig;
+	__u16	compat_port;
+	__u8	data_dist_mode;
+	__u8	surf_an_ctrl;
+	__u16	ctlr_phys_drv;
+	__u16	log_unit_phys_drv;
+	__u16	fault_tol_mode;
+	__u8	phys_drv_param[16];
+	drv_param_t drv;
+	__u32	drv_asgn_map;
+	__u16	dist_factor;
+	__u32	spare_asgn_map;
+	__u8	reserved[6];
+	__u16	os;
+	__u8	ctlr_order;
+	__u8	extra_info;
+	__u32	data_offs;
+	__u8	parity_backedout_write_drvs;
+	__u8	parity_dist_mode;
+	__u8	parity_shift_fact;
+	__u8	bios_disable_flag;
+	__u32	blks_on_vol;
+	__u32	blks_per_drv;
+	__u8	scratch[16];
+	__u16	big_drv_map[8];
+	__u16	big_spare_map[8];
+	__u8	ss_source_vol;
+	__u8	mix_drv_cap_range;
+	struct {
+		__u16	big_drv_map[8];
+		__u32	blks_per_drv;
+		__u16	fault_tol_mode;
+		__u16	dist_factor;
+	} MDC_range[4];
+	__u8	reserved1[248];
+} config_t;
+
+#define BYPASS_VOL_STATE	0x52
+#define SS_CREATE_VOL		0x53
+#define CHANGE_CONFIG		0x54
+#define SENSE_ORIG_CONF		0x55
+#define REORDER_LOG_DRV		0x56
+typedef struct {
+	__u8	old_units[32];
+} reorder_log_drv_t;
+
+#define LABEL_LOG_DRV		0x57
+typedef struct {
+	__u8	log_drv_label[64];
+} label_log_drv_t;
+
+#define SS_TO_VOL		0x58
+	
+#define SET_SURF_DELAY		0x60
+typedef struct {
+	__u16	delay;
+	__u8	reserved[510];
+} surf_delay_t;
+
+#define SET_OVERHEAT_DELAY	0x61
+typedef struct {
+	__u16	delay;
+} overhead_delay_t;
+ 
+#define SET_MP_DELAY
+typedef struct {
+	__u16	delay;
+	__u8	reserved[510];
+} mp_delay_t;
+
+#define PASSTHRU_A	0x91
+typedef struct {
+	__u8	target;
+	__u8	bus;
+	__u8	lun;
+	__u32	timeout;
+	__u32	flags;
+	__u8	status;
+	__u8	error;
+	__u8	cdb_len;
+	__u8	sense_error;
+	__u8	sense_key;
+	__u32	sense_info;
+	__u8	sense_code;
+	__u8	sense_qual;
+	__u8	residual;
+	__u8	reserved[4];
+	__u8	cdb[12];	
+} scsi_param_t;
+
+#pragma pack()	
+
+#endif /* ARRAYCMD_H */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/ida_ioctl.h linux/drivers/block/ida_ioctl.h
--- linux.vanilla/drivers/block/ida_ioctl.h	Thu Jan  1 01:00:00 1970
+++ linux/drivers/block/ida_ioctl.h	Fri Jan 29 15:25:34 1999
@@ -0,0 +1,80 @@
+/*
+ *    Disk Array driver for Compaq SMART2 Controllers
+ *    Copyright 1998 Compaq Computer Corporation
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ *    NON INFRINGEMENT.  See the GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *    Questions/Comments/Bugfixes to arrays@compaq.com
+ *
+ *    If you want to make changes, improve or add functionality to this
+ *    driver, you'll probably need the Compaq Array Controller Interface
+ *    Specificiation (Document number ECG086/1198)
+ */
+#ifndef IDA_IOCTL_H
+#define IDA_IOCTL_H
+
+#include "ida_cmd.h"
+#include "cpqarray.h"
+
+#define IDAGETDRVINFO		0x27272828
+#define IDAPASSTHRU		0x28282929
+#define IDAGETCTLRSIG		0x29293030
+#define IDAREVALIDATEVOLS	0x30303131
+
+/*
+ * Normally, the ioctl determines the logical unit for this command by
+ * the major,minor number of the fd passed to ioctl.  If you need to send
+ * a command to a different/nonexistant unit (such as during config), you
+ * can override the normal behavior by setting the unit valid bit. (Normally,
+ * it should be zero) The controller to which the command is sent is still
+ * determined by the major number of the open device.
+ */
+
+#define UNITVALID	0x80
+typedef struct {
+	__u8	cmd;
+	__u8	rcode;
+	__u8	unit;
+
+/* currently, sg_cnt is assumed to be 1: only the 0th element of sg is used */
+	struct {
+		void	*addr;
+		size_t	size;
+	} sg[SG_MAX];
+	int	sg_cnt;
+
+	union ctlr_cmds {
+		drv_info_t		drv;
+		unsigned char		buf[512];
+
+		id_ctlr_t		id_ctlr;
+		drv_param_t		drv_param;
+		id_log_drv_t		id_log_drv;
+		id_log_drv_ext_t	id_log_drv_ext;
+		sense_log_drv_stat_t	sense_log_drv_stat;
+		id_phys_drv_t		id_phys_drv;
+		blink_drv_leds_t	blink_drv_leds;
+		sense_blink_leds_t	sense_blink_leds;
+		config_t		config;
+		reorder_log_drv_t	reorder_log_drv;
+		label_log_drv_t		label_log_drv;
+		surf_delay_t		surf_delay;
+		overhead_delay_t	overhead_delay;
+		mp_delay_t		mp_delay;
+		scsi_param_t		scsi_param;
+	} c;
+} ida_ioctl_t;
+
+#endif /* IDA_IOCTL_H */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/ide.c linux/drivers/block/ide.c
--- linux.vanilla/drivers/block/ide.c	Sun Dec  6 00:14:36 1998
+++ linux/drivers/block/ide.c	Tue Mar  2 00:40:03 1999
@@ -277,6 +277,12 @@
  *			fix MC_ERR handling
  *			fix mis-detection of NEC cdrom as floppy
  *			issue ATAPI reset and re-probe after "no response"
+ * Version 5.53.3	changes by Andrew D. Balsa to enable DMA mode 2 and
+ *			UDMA on SiS and TX chipsets.
+ * Version 5.53.4	moved Promise/33 auto-detection and DMA support
+ *			to trition.c and added UDMA to current DMA support.
+ *			update Promise Ultra33 and added AEC6210U/UF UDMA cards.
+ *			add configuration flag to allow booting of either card.
  *
  *  Some additional driver compile-time options are in ide.h
  *
@@ -607,8 +613,12 @@
 	unsigned long chs_sects   = id->cyls * id->heads * id->sectors;
 	unsigned long _10_percent = chs_sects / 10;
 
-	/* very large drives (8GB+) may lie about the number of cylinders */
-	if (id->cyls == 16383 && id->heads == 16 && id->sectors == 63 && lba_sects > chs_sects) {
+	/*
+	 * very large drives (8GB+) may lie about the number of cylinders
+	 * This is a split test for drives 8 Gig and Bigger only.
+	 */
+	if ((id->lba_capacity >= 16514064) && (id->cyls == 0x3fff) &&
+	    (id->heads == 16) && (id->sectors == 63)) {
 		id->cyls = lba_sects / (16 * 63); /* correct cyls */
 		return 1;	/* lba_capacity is our only option */
 	}
@@ -649,6 +659,15 @@
 			drive->cyl = id->lba_capacity / (drive->head * drive->sect);
 			capacity = id->lba_capacity;
 			drive->select.b.lba = 1;
+#if 0
+			/*
+			 * This is the correct place to perform this task;
+			 * however, we do this later for reporting.
+			 */
+			if (*(int *)&id->cur_capacity0 != id->lba_capacity) {
+				*(int *)&id->cur_capacity0 = id->lba_capacity;
+			}
+#endif
 		}
 	}
 	return (capacity - drive->sect0);
@@ -2192,17 +2211,18 @@
 		case HDIO_GET_MULTCOUNT:
 			return write_fs_long(arg, drive->mult_count);
 
+		case HDIO_OBSOLETE_IDENTITY:
 		case HDIO_GET_IDENTITY:
 			if (!arg || (MINOR(inode->i_rdev) & PARTN_MASK))
 				return -EINVAL;
 			if (drive->id == NULL)
 				return -ENOMSG;
-			err = verify_area(VERIFY_WRITE, (char *)arg, sizeof(*drive->id));
+			err = verify_area(VERIFY_WRITE, (char *)arg, (cmd == HDIO_GET_IDENTITY) ? sizeof(*drive->id) : 142);
 			if (!err)
-				memcpy_tofs((char *)arg, (char *)drive->id, sizeof(*drive->id));
+				memcpy_tofs((char *)arg, (char *)drive->id, (cmd == HDIO_GET_IDENTITY) ? sizeof(*drive->id) : 142);
 			return err;
 
-			case HDIO_GET_NOWERR:
+		case HDIO_GET_NOWERR:
 			return write_fs_long(arg, drive->bad_wstat == BAD_R_STAT);
 
 		case HDIO_SET_DMA:
@@ -2545,9 +2565,8 @@
 		drive->sect    = drive->bios_sect = id->sectors;
 	}
 	/* Handle logical geometry translation by the drive */
-	if ((id->field_valid & 1) && id->cur_cyls && id->cur_heads
-	 && (id->cur_heads <= 16) && id->cur_sectors)
-	{
+	if ((id->field_valid & 1) && id->cur_cyls &&
+		id->cur_heads && (id->cur_heads <= 16) && id->cur_sectors) {
 		/*
 		 * Extract the physical drive geometry for our use.
 		 * Note that we purposely do *not* update the bios info.
@@ -2572,19 +2591,38 @@
 		}
 	}
 	/* Use physical geometry if what we have still makes no sense */
-	if ((!drive->head || drive->head > 16) && id->heads && id->heads <= 16) {
-		drive->cyl  = id->cyls;
-		drive->head = id->heads;
-		drive->sect = id->sectors;
+	if ((!drive->head || drive->head > 16) &&
+	    id->heads && id->heads <= 16) {
+		if ((id->lba_capacity > 16514064) || (id->cyls == 0x3fff)) {
+			id->cyls = ((int)(id->lba_capacity/(id->heads * id->sectors)));
+		}
+		drive->cyl  = id->cur_cyls    = id->cyls;
+		drive->head = id->cur_heads   = id->heads;
+		drive->sect = id->cur_sectors = id->sectors;
 	}
 
 	/* calculate drive capacity, and select LBA if possible */
-	(void) current_capacity (drive);
+	capacity = current_capacity (drive);
 
-	/* Correct the number of cyls if the bios value is too small */
-	if (drive->sect == drive->bios_sect && drive->head == drive->bios_head) {
-		if (drive->cyl > drive->bios_cyl)
-			drive->bios_cyl = drive->cyl;
+	/*
+	 * if possible, give fdisk access to more of the drive,
+	 * by correcting bios_cyls:
+	 */
+	if ((capacity >= (id->cyls * id->heads * id->sectors)) &&
+	    (!drive->forced_geom) && drive->bios_sect && drive->bios_head) {
+		drive->bios_cyl = (capacity / drive->bios_sect) / drive->bios_head;
+#ifdef DEBUG
+		printk("FDISK Fixing Geometry :: CHS=%d/%d/%d to CHS=%d/%d/%d\n",
+			drive->id->cur_cyls,
+			drive->id->cur_heads,
+			drive->id->cur_sectors,
+			drive->bios_cyl,
+			drive->bios_head,
+			drive->bios_sect);
+#endif
+		drive->id->cur_cyls    = drive->bios_cyl;
+		drive->id->cur_heads   = drive->bios_head;
+		drive->id->cur_sectors = drive->bios_sect;
 	}
 
 	if (!strncmp(id->model, "BMI ", 4) &&
@@ -2592,27 +2630,54 @@
 	    drive->select.b.lba)
 		drive->no_geom = 1;
 
-	printk ("%s: %.40s, %ldMB w/%dkB Cache, CHS=%d/%d/%d",
-	 drive->name, id->model, current_capacity(drive)/2048L, id->buf_size/2,
-	 drive->bios_cyl, drive->bios_head, drive->bios_sect);
-
 	drive->mult_count = 0;
 	if (id->max_multsect) {
+#ifdef CONFIG_IDEDISK_MULTI_MODE
+		id->multsect = ((id->max_multsect/2) > 1) ? id->max_multsect : 0;
+		id->multsect_valid = id->multsect ? 1 : 0;
+		drive->mult_req = id->multsect_valid ? id->max_multsect : INITIAL_MULT_COUNT;
+		drive->special.b.set_multmode = drive->mult_req ? 1 : 0;
+#else	/* original, pre IDE-NFG, per request of AC */
 		drive->mult_req = INITIAL_MULT_COUNT;
 		if (drive->mult_req > id->max_multsect)
 			drive->mult_req = id->max_multsect;
 		if (drive->mult_req || ((id->multsect_valid & 1) && id->multsect))
 			drive->special.b.set_multmode = 1;
+#endif
 	}
+
+	drive->no_io_32bit = id->dword_io ? 1 : 0;
+
 	if (drive->autotune != 2 && HWIF(drive)->dmaproc != NULL) {
-		if (!(HWIF(drive)->dmaproc(ide_dma_check, drive))) {
-			if ((id->field_valid & 4) && (id->dma_ultra & (id->dma_ultra >> 8) & 7))
-				printk(", UDMA");
-			else
-				printk(", DMA");
+		(void) HWIF(drive)->dmaproc(ide_dma_check, drive);
+	}
+
+	printk ("%s: %.40s, %ldMB w/%dkB Cache, CHS=%d/%d/%d",
+		drive->name, id->model,
+		capacity/2048L, id->buf_size/2,
+		drive->bios_cyl, drive->bios_head, drive->bios_sect);
+	if (drive->using_dma) {
+		if ((id->field_valid & 4) && (id->dma_ultra & (id->dma_ultra >> 8) & 7)) {
+			printk(", UDMA");	/* UDMA BIOS-enabled! */
+		} else if (id->field_valid & 4) {
+			printk(", (U)DMA");	/* Can be BIOS-enabled! */
+		} else {
+			printk(", DMA");
 		}
 	}
 	printk("\n");
+	if (drive->select.b.lba) {
+		if (*(int *)&id->cur_capacity0 != id->lba_capacity) {
+#ifdef DEBUG
+			printk("     CurSects=%d, LBASects=%d, ",
+				*(int *)&id->cur_capacity0, id->lba_capacity);
+#endif
+			*(int *)&id->cur_capacity0 = id->lba_capacity;
+#ifdef DEBUG
+			printk( "Fixed CurSects=%d\n", *(int *)&id->cur_capacity0);
+#endif
+		}
+	}
 }
 
 /*
@@ -3314,6 +3379,13 @@
  * an IDE disk drive, or if a geometry was "forced" on the commandline.
  * Returns 1 if the geometry translation was successful.
  */
+
+/*
+ * We update the values if the current CHS as determined by the kernel.
+ * This now allows for the hdparm tool to read the actual settings of the
+ * being used by the kernel.  This removes the need for doing guess
+ * translations based on the raw values of the drive.
+ */
 int ide_xlate_1024 (kdev_t i_rdev, int xparm, const char *msg)
 {
 	ide_drive_t *drive;
@@ -3321,14 +3393,42 @@
 	const byte *heads = head_vals;
 	unsigned long tracks;
 
-	if ((drive = get_info_ptr(i_rdev)) == NULL || drive->forced_geom)
+	drive = get_info_ptr(i_rdev);
+	if (!drive)
 		return 0;
 
-	if (xparm > 1 && xparm <= drive->bios_head && drive->bios_sect == 63)
+	if (drive->forced_geom) {
+		/*
+		 * Update the current 3D drive values.
+		 */
+		drive->id->cur_cyls	= drive->bios_cyl;
+		drive->id->cur_heads	= drive->bios_head;
+		drive->id->cur_sectors	= drive->bios_sect;
+		return 0;
+	}
+
+	if (xparm > 1 && xparm <= drive->bios_head && drive->bios_sect == 63) {
+		/*
+		 * Update the current 3D drive values.
+		 */
+		drive->id->cur_cyls	= drive->bios_cyl;
+		drive->id->cur_heads	= drive->bios_head;
+		drive->id->cur_sectors	= drive->bios_sect;
 		return 0;		/* we already have a translation */
+	}
 
 	printk("%s ", msg);
 
+	if (xparm < 0 && (drive->bios_cyl * drive->bios_head * drive->bios_sect) < (1024 * 16 * 63)) {
+		/*
+		 * Update the current 3D drive values.
+		 */
+		drive->id->cur_cyls	= drive->bios_cyl;
+		drive->id->cur_heads	= drive->bios_head;
+		drive->id->cur_sectors	= drive->bios_sect;
+		return 0;		/* small disk: no translation needed */
+	}
+
 	if (drive->id) {
 		drive->cyl  = drive->id->cyls;
 		drive->head = drive->id->heads;
@@ -3366,6 +3466,12 @@
 	}
 	drive->part[0].nr_sects = current_capacity(drive);
 	printk("[%d/%d/%d]", drive->bios_cyl, drive->bios_head, drive->bios_sect);
+	/*
+	 * Update the current 3D drive values.
+	 */
+	drive->id->cur_cyls	= drive->bios_cyl;
+	drive->id->cur_heads	= drive->bios_head;
+	drive->id->cur_sectors	= drive->bios_sect;
 	return 1;
 }
 
@@ -3535,43 +3641,6 @@
 }
 
 #endif /* defined(CONFIG_BLK_DEV_RZ1000) || defined(CONFIG_BLK_DEV_TRITON) */
-
-static void ide_probe_promise_20246(void)
-{
-	byte fn, bus;
-	unsigned short io[6], count = 0;
-	unsigned int reg, tmp, i;
-	ide_hwif_t *hwif;
-
-	memset(io, 0, 6 * sizeof(unsigned short));
-	if (pcibios_find_device(PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246, 0, &bus, &fn))
-		return;
-	printk("ide: Promise Technology IDE Ultra-DMA 33 on PCI bus %d function %d\n", bus, fn);
-	for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) {
-		pcibios_read_config_dword(bus, fn, reg, &tmp);
-		if (tmp & PCI_BASE_ADDRESS_SPACE_IO)
-			io[count++] = tmp & PCI_BASE_ADDRESS_IO_MASK;
-	}
-	for (i = 2; i < 4; i++) {
-		hwif = ide_hwifs + i;
-		if (hwif->chipset == ide_generic) {
-			printk("ide%d: overridden with command line parameter\n", i);
-			return;
-		}
-		tmp = (i - 2) * 2;
-		if (!io[tmp] || !io[tmp + 1]) {
-			printk("ide%d: invalid port address %x, %x -- aborting\n", i, io[tmp], io[tmp + 1]);
-			return;
-		}
-		hwif->io_base = io[tmp];
-		hwif->ctl_port = io[tmp + 1] + 2;
-		hwif->noprobe = 0;
-	}
-#ifdef CONFIG_BLK_DEV_TRITON
-	ide_init_promise (bus, fn, &ide_hwifs[2], &ide_hwifs[3], io[4]);
-#endif /* CONFIG_BLK_DEV_TRITON */
-}
-
 #endif /* CONFIG_PCI */
 
 /*
@@ -3599,11 +3668,20 @@
 		 * So instead, we search for PCI_DEVICE_ID_INTEL_82371_0,
 		 * and then add 1.
 		 */
+#ifdef CONFIG_BLK_DEV_OFFBOARD
+		ide_probe_pci (PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246, &ide_init_triton, 0);
+		ide_probe_pci (PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP850UF, &ide_init_triton, 0);
+#endif /* CONFIG_BLK_DEV_OFFBOARD */
 		ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371_0, &ide_init_triton, 1);
 		ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, &ide_init_triton, 0);
 		ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, &ide_init_triton, 0);
+		ide_probe_pci (PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5513, &ide_init_triton, 0);
+		ide_probe_pci (PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1, &ide_init_triton, 0);
+#ifndef CONFIG_BLK_DEV_OFFBOARD
+		ide_probe_pci (PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246, &ide_init_triton, 0);
+		ide_probe_pci (PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP850UF, &ide_init_triton, 0);
+#endif /* CONFIG_BLK_DEV_OFFBOARD */
 #endif /* CONFIG_BLK_DEV_TRITON */
-		ide_probe_promise_20246();
 	}
 #endif /* CONFIG_PCI */
 #ifdef CONFIG_BLK_DEV_CMD640
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/ide.h linux/drivers/block/ide.h
--- linux.vanilla/drivers/block/ide.h	Sun Dec  6 00:14:36 1998
+++ linux/drivers/block/ide.h	Fri Mar 26 17:14:16 1999
@@ -429,7 +429,7 @@
 typedef enum {	ide_unknown,	ide_generic,	ide_triton,
 		ide_cmd640,	ide_dtc2278,	ide_ali14xx,
 		ide_qd6580,	ide_umc8672,	ide_ht6560b,
-		ide_promise,	ide_promise_udma }
+		ide_promise,	ide_udma }
 	hwif_chipset_t;
 
 typedef struct hwif_s {
@@ -743,5 +743,4 @@
 
 #ifdef CONFIG_BLK_DEV_TRITON
 void ide_init_triton (byte, byte);
-void ide_init_promise (byte bus, byte fn, ide_hwif_t *hwif0, ide_hwif_t *hwif1, unsigned short dma);
 #endif /* CONFIG_BLK_DEV_TRITON */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- linux.vanilla/drivers/block/ll_rw_blk.c	Fri Jul 24 17:28:57 1998
+++ linux/drivers/block/ll_rw_blk.c	Fri Mar 26 17:00:22 1999
@@ -82,6 +82,30 @@
 int * hardsect_size[MAX_BLKDEV] = { NULL, NULL, };
 
 /*
+ * Max number of sectors per request
+ */
+int * max_sectors[MAX_BLKDEV] = { NULL, NULL, };
+
+/*
+ * Max number of segments per request
+ */
+int * max_segments[MAX_BLKDEV] = { NULL, NULL, };
+
+static inline int get_max_sectors(kdev_t dev)
+{
+	if (!max_sectors[MAJOR(dev)])
+		return MAX_SECTORS;
+	return max_sectors[MAJOR(dev)][MINOR(dev)];
+}
+
+static inline int get_max_segments(kdev_t dev)
+{
+	if (!max_segments[MAJOR(dev)])
+		return MAX_SEGMENTS;
+	return max_segments[MAJOR(dev)][MINOR(dev)];
+}
+
+/*
  * remove the plug and let it rip..
  */
 void unplug_device(void * data)
@@ -233,22 +257,29 @@
 
 void add_request(struct blk_dev_struct * dev, struct request * req)
 {
+	int major = MAJOR(req->rq_dev);
+	int minor = MINOR(req->rq_dev);
 	struct request * tmp;
 	short		 disk_index;
 
-	switch (MAJOR(req->rq_dev)) {
+	switch (major) {
+		case DAC960_MAJOR+0:
+			disk_index = (minor & 0x00f8) >> 3;
+			if (disk_index < 4)
+				drive_stat_acct(req->cmd, req->nr_sectors, disk_index);
+			break;
 		case SCSI_DISK_MAJOR:
-			disk_index = (MINOR(req->rq_dev) & 0x0070) >> 4;
+			disk_index = (minor & 0x0070) >> 4;
 			if (disk_index < 4)
 				drive_stat_acct(req->cmd, req->nr_sectors, disk_index);
 			break;
 		case IDE0_MAJOR:	/* same as HD_MAJOR */
 		case XT_DISK_MAJOR:
-			disk_index = (MINOR(req->rq_dev) & 0x0040) >> 6;
+			disk_index = (minor & 0x0040) >> 6;
 			drive_stat_acct(req->cmd, req->nr_sectors, disk_index);
 			break;
 		case IDE1_MAJOR:
-			disk_index = ((MINOR(req->rq_dev) & 0x0040) >> 6) + 2;
+			disk_index = ((minor & 0x0040) >> 6) + 2;
 			drive_stat_acct(req->cmd, req->nr_sectors, disk_index);
 		default:
 			break;
@@ -274,30 +305,39 @@
 	tmp->next = req;
 
 /* for SCSI devices, call request_fn unconditionally */
-	if (scsi_blk_major(MAJOR(req->rq_dev)))
+	if (scsi_blk_major(major))
 		(dev->request_fn)();
 
+	if ( (major >= DAC960_MAJOR+0 && major <= DAC960_MAJOR+7) ||
+	     (major >= COMPAQ_SMART2_MAJOR+0 && major <= COMPAQ_SMART2_MAJOR+7))
+	  (dev->request_fn)();
+
 	sti();
 }
 
-#define MAX_SECTORS 244
-
-static inline void attempt_merge (struct request *req)
+static inline void attempt_merge (struct request *req,
+				  int max_sectors,
+				  int max_segments)
 {
 	struct request *next = req->next;
+	int total_segments;
 
 	if (!next)
 		return;
 	if (req->sector + req->nr_sectors != next->sector)
 		return;
-	if (next->sem || req->cmd != next->cmd || req->rq_dev != next->rq_dev || req->nr_sectors + next->nr_sectors >= MAX_SECTORS)
+	if (next->sem || req->cmd != next->cmd || req->rq_dev != next->rq_dev ||
+	    req->nr_sectors + next->nr_sectors > max_sectors)
+		return;
+	total_segments = req->nr_segments + next->nr_segments;
+	if (req->bhtail->b_data + req->bhtail->b_size == next->bh->b_data)
+		total_segments--;
+	if (total_segments > max_segments)
 		return;
-#if 0
-	printk ("%s: merge %ld, %ld + %ld == %ld\n", kdevname(req->rq_dev), req->sector, req->nr_sectors, next->nr_sectors, req->nr_sectors + next->nr_sectors);
-#endif	
 	req->bhtail->b_reqnext = next->bh;
 	req->bhtail = next->bhtail;
 	req->nr_sectors += next->nr_sectors;
+	req->nr_segments = total_segments;
 	next->rq_status = RQ_INACTIVE;
 	req->next = next->next;
 	wake_up (&wait_for_request);
@@ -307,7 +347,7 @@
 {
 	unsigned int sector, count;
 	struct request * req;
-	int rw_ahead, max_req;
+	int rw_ahead, max_req, max_sectors, max_segments;
 
 	count = bh->b_size >> 9;
 	sector = bh->b_rsector;
@@ -324,7 +364,7 @@
 	lock_buffer(bh);
 
 	if (blk_size[major])
-		if (blk_size[major][MINOR(bh->b_rdev)] < (sector + count)>>1) {
+               if (blk_size[major][MINOR(bh->b_rdev)] < (sector + count)>>1) {
 			bh->b_state &= (1 << BH_Lock) | (1 << BH_FreeOnIO);
                         /* This may well happen - the kernel calls bread()
                            without checking the size of the device, e.g.,
@@ -390,6 +430,9 @@
 	/*
 	 * Try to coalesce the new request with old requests
 	 */
+	max_sectors = get_max_sectors(bh->b_rdev);
+	max_segments = get_max_segments(bh->b_rdev);
+
 	cli();
 	req = blk_dev[major].current_request;
 	if (!req) {
@@ -418,25 +461,52 @@
 
 	     case SCSI_DISK_MAJOR:
 	     case SCSI_CDROM_MAJOR:
-
+	     case DAC960_MAJOR+0:
+	     case DAC960_MAJOR+1:
+	     case DAC960_MAJOR+2:
+	     case DAC960_MAJOR+3:
+	     case DAC960_MAJOR+4:
+	     case DAC960_MAJOR+5:
+	     case DAC960_MAJOR+6:
+	     case DAC960_MAJOR+7:
+	     case COMPAQ_SMART2_MAJOR+0:
+	     case COMPAQ_SMART2_MAJOR+1:
+	     case COMPAQ_SMART2_MAJOR+2:
+	     case COMPAQ_SMART2_MAJOR+3:
+	     case COMPAQ_SMART2_MAJOR+4:
+	     case COMPAQ_SMART2_MAJOR+5:
+	     case COMPAQ_SMART2_MAJOR+6:
+	     case COMPAQ_SMART2_MAJOR+7:
 		do {
 			if (req->sem)
 				continue;
 			if (req->cmd != rw)
 				continue;
-			if (req->nr_sectors >= MAX_SECTORS)
+			if (req->nr_sectors + count > max_sectors)
 				continue;
 			if (req->rq_dev != bh->b_rdev)
 				continue;
 			/* Can we add it to the end of this request? */
 			if (req->sector + req->nr_sectors == sector) {
+				if (req->bhtail->b_data + req->bhtail->b_size
+				    != bh->b_data) {
+					if (req->nr_segments < max_segments)
+						req->nr_segments++;
+					else continue;
+				}
 				req->bhtail->b_reqnext = bh;
 				req->bhtail = bh;
 			    	req->nr_sectors += count;
 				/* Can we now merge this req with the next? */
-				attempt_merge(req);
+				attempt_merge(req, max_sectors, max_segments);
 			/* or to the beginning? */
 			} else if (req->sector - count == sector) {
+				if (bh->b_data + bh->b_size
+				    != req->bh->b_data) {
+					if (req->nr_segments < max_segments)
+						req->nr_segments++;
+					else continue;
+				}
 			    	bh->b_reqnext = req->bh;
 			    	req->bh = bh;
 			    	req->buffer = bh->b_data;
@@ -470,6 +540,7 @@
 	req->errors = 0;
 	req->sector = sector;
 	req->nr_sectors = count;
+	req->nr_segments = 1;
 	req->current_nr_sectors = count;
 	req->buffer = bh->b_data;
 	req->sem = NULL;
@@ -634,6 +705,7 @@
 			req[j]->errors = 0;
 			req[j]->sector = rsector;
 			req[j]->nr_sectors = buffersize >> 9;
+			req[j]->nr_segments = 1;
 			req[j]->current_nr_sectors = buffersize >> 9;
 			req[j]->buffer = buf;
 			req[j]->sem = &sem;
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/paride/Config.in linux/drivers/block/paride/Config.in
--- linux.vanilla/drivers/block/paride/Config.in	Fri Jul 24 17:28:57 1998
+++ linux/drivers/block/paride/Config.in	Sun Dec 27 21:08:29 1998
@@ -16,6 +16,7 @@
 dep_tristate '    FIT TD-3000 protocol' CONFIG_PARIDE_FIT3 $CONFIG_PARIDE
 dep_tristate '    Shuttle EPAT/EPEZ protocol' CONFIG_PARIDE_EPAT $CONFIG_PARIDE
 dep_tristate '    Shuttle EPIA protocol' CONFIG_PARIDE_EPIA $CONFIG_PARIDE
+dep_tristate '    Freecom IQ ASIC-2 protocol' CONFIG_PARIDE_FRIQ $CONFIG_PARIDE
 dep_tristate '    FreeCom power protocol' CONFIG_PARIDE_FRPW $CONFIG_PARIDE
 dep_tristate '    KingByte KBIC-951A/971A protocols' CONFIG_PARIDE_KBIC $CONFIG_PARIDE
 dep_tristate '    KT PHd protocol' CONFIG_PARIDE_KTTI $CONFIG_PARIDE
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/paride/Makefile linux/drivers/block/paride/Makefile
--- linux.vanilla/drivers/block/paride/Makefile	Sun Dec  6 00:14:37 1998
+++ linux/drivers/block/paride/Makefile	Sun Dec 27 21:08:29 1998
@@ -147,6 +147,15 @@
   endif
 endif
 
+
+ifeq ($(CONFIG_PARIDE_FRIQ),y)
+  LX_OBJS += friq.o
+else
+  ifeq ($(CONFIG_PARIDE_FRIQ),m)
+    M_OBJS += friq.o
+  endif
+endif
+
 ifeq ($(CONFIG_PARIDE_ON20),y)
   LX_OBJS += on20.o
 else
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/paride/friq.c linux/drivers/block/paride/friq.c
--- linux.vanilla/drivers/block/paride/friq.c	Thu Jan  1 01:00:00 1970
+++ linux/drivers/block/paride/friq.c	Fri Jan 29 14:13:09 1999
@@ -0,0 +1,282 @@
+/* 
+	friq.c	(c) 1998    Grant R. Guenther <grant@torque.net>
+		            Under the terms of the GNU public license
+
+	friq.c is a low-level protocol driver for the Freecom "IQ"
+	parallel port IDE adapter.   Early versions of this adapter
+	use the 'frpw' protocol.
+	
+	Freecom uses this adapter in a battery powered external 
+	CD-ROM drive.  It is also used in LS-120 drives by
+	Maxell and Panasonic, and other devices.
+
+	The battery powered drive requires software support to
+	control the power to the drive.  This module enables the
+	drive power when the high level driver (pcd) is loaded
+	and disables it when the module is unloaded.  Note, if
+	the friq module is built in to the kernel, the power
+	will never be switched off, so other means should be
+	used to conserve battery power.
+
+*/
+
+/* Changes:
+
+	1.01	GRG 1998.12.20	 Added support for soft power switch
+*/
+
+#define	FRIQ_VERSION	"1.01" 
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+#include "paride.h"
+
+#define CMD(x)		w2(4);w0(0xff);w0(0xff);w0(0x73);w0(0x73);\
+			w0(0xc9);w0(0xc9);w0(0x26);w0(0x26);w0(x);w0(x);
+
+#define j44(l,h)	(((l>>4)&0x0f)|(h&0xf0))
+
+/* cont = 0 - access the IDE register file 
+   cont = 1 - access the IDE command set 
+*/
+
+static int  cont_map[2] = { 0x08, 0x10 };
+
+static int friq_read_regr( PIA *pi, int cont, int regr )
+
+{	int	h,l,r;
+
+	r = regr + cont_map[cont];
+
+	CMD(r);
+	w2(6); l = r1();
+	w2(4); h = r1();
+	w2(4); 
+
+	return j44(l,h);
+
+}
+
+static void friq_write_regr( PIA *pi, int cont, int regr, int val)
+
+{	int r;
+
+        r = regr + cont_map[cont];
+
+	CMD(r);
+	w0(val);
+	w2(5);w2(7);w2(5);w2(4);
+}
+
+static void friq_read_block_int( PIA *pi, char * buf, int count, int regr )
+
+{       int     h, l, k, ph;
+
+        switch(pi->mode) {
+
+        case 0: CMD(regr); 
+                for (k=0;k<count;k++) {
+                        w2(6); l = r1();
+                        w2(4); h = r1();
+                        buf[k] = j44(l,h);
+                }
+                w2(4);
+                break;
+
+        case 1: ph = 2;
+                CMD(regr+0xc0); 
+                w0(0xff);
+                for (k=0;k<count;k++) {
+                        w2(0xa4 + ph); 
+                        buf[k] = r0();
+                        ph = 2 - ph;
+                } 
+                w2(0xac); w2(0xa4); w2(4);
+                break;
+
+	case 2: CMD(regr+0x80);
+		for (k=0;k<count-2;k++) buf[k] = r4();
+		w2(0xac); w2(0xa4);
+		buf[count-2] = r4();
+		buf[count-1] = r4();
+		w2(4);
+		break;
+
+	case 3: CMD(regr+0x80);
+                for (k=0;k<(count/2)-1;k++) ((u16 *)buf)[k] = r4w();
+                w2(0xac); w2(0xa4);
+                buf[count-2] = r4();
+                buf[count-1] = r4();
+                w2(4);
+                break;
+
+	case 4: CMD(regr+0x80);
+                for (k=0;k<(count/4)-1;k++) ((u32 *)buf)[k] = r4l();
+                buf[count-4] = r4();
+                buf[count-3] = r4();
+                w2(0xac); w2(0xa4);
+                buf[count-2] = r4();
+                buf[count-1] = r4();
+                w2(4);
+                break;
+
+        }
+}
+
+static void friq_read_block( PIA *pi, char * buf, int count)
+
+{	friq_read_block_int(pi,buf,count,0x08);
+}
+
+static void friq_write_block( PIA *pi, char * buf, int count )
+ 
+{	int	k;
+
+	switch(pi->mode) {
+
+	case 0:
+	case 1: CMD(8); w2(5);
+        	for (k=0;k<count;k++) {
+			w0(buf[k]);
+			w2(7);w2(5);
+		}
+		w2(4);
+		break;
+
+	case 2: CMD(0xc8); w2(5);
+		for (k=0;k<count;k++) w4(buf[k]);
+		w2(4);
+		break;
+
+        case 3: CMD(0xc8); w2(5);
+                for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
+                w2(4);
+                break;
+
+        case 4: CMD(0xc8); w2(5);
+                for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
+                w2(4);
+                break;
+	}
+}
+
+static void friq_connect ( PIA *pi  )
+
+{       pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+	w2(4);
+}
+
+static void friq_disconnect ( PIA *pi )
+
+{       CMD(0x20);
+	w0(pi->saved_r0);
+        w2(pi->saved_r2);
+} 
+
+static int friq_test_proto( PIA *pi, char * scratch, int verbose )
+
+{       int     j, k, r;
+	int	e[2] = {0,0};
+
+	pi->saved_r0 = r0();	
+	w0(0xff); udelay(20); CMD(0x3d); /* turn the power on */
+	udelay(500);
+	w0(pi->saved_r0);
+
+	friq_connect(pi);
+	for (j=0;j<2;j++) {
+                friq_write_regr(pi,0,6,0xa0+j*0x10);
+                for (k=0;k<256;k++) {
+                        friq_write_regr(pi,0,2,k^0xaa);
+                        friq_write_regr(pi,0,3,k^0x55);
+                        if (friq_read_regr(pi,0,2) != (k^0xaa)) e[j]++;
+                        }
+                }
+	friq_disconnect(pi);
+
+	friq_connect(pi);
+        friq_read_block_int(pi,scratch,512,0x10);
+        r = 0;
+        for (k=0;k<128;k++) if (scratch[k] != k) r++;
+	friq_disconnect(pi);
+
+        if (verbose)  {
+            printk("%s: friq: port 0x%x, mode %d, test=(%d,%d,%d)\n",
+                   pi->device,pi->port,pi->mode,e[0],e[1],r);
+        }
+
+        return (r || (e[0] && e[1]));
+}
+
+
+static void friq_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{       char    *mode_string[6] = {"4-bit","8-bit",
+				   "EPP-8","EPP-16","EPP-32"};
+
+        printk("%s: friq %s, Freecom IQ ASIC-2 adapter at 0x%x, ", pi->device,
+		FRIQ_VERSION,pi->port);
+        printk("mode %d (%s), delay %d\n",pi->mode,
+		mode_string[pi->mode],pi->delay);
+
+	pi->private = 1;
+	friq_connect(pi);
+	CMD(0x9e);  		/* disable sleep timer */
+	friq_disconnect(pi);
+
+}
+
+static void friq_init_proto( PIA *pi)
+
+{       MOD_INC_USE_COUNT;
+	pi->private = 0;
+}
+
+static void friq_release_proto( PIA *pi)
+
+{       if (pi->private) {		/* turn off the power */
+		friq_connect(pi);
+		CMD(0x1d); CMD(0x1e);
+		friq_disconnect(pi);
+		pi->private = 0;
+	}
+
+	MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol friq = {"friq",0,5,2,1,1,
+                           friq_write_regr,
+                           friq_read_regr,
+                           friq_write_block,
+                           friq_read_block,
+                           friq_connect,
+                           friq_disconnect,
+                           0,
+                           0,
+                           friq_test_proto,
+                           friq_log_adapter,
+                           friq_init_proto,
+                           friq_release_proto
+                          };
+
+
+#ifdef MODULE
+
+int     init_module(void)
+
+{       return pi_register( &friq ) - 1;
+}
+
+void    cleanup_module(void)
+
+{       pi_unregister( &friq );
+}
+
+#endif
+
+/* end of friq.c */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/paride/frpw.c linux/drivers/block/paride/frpw.c
--- linux.vanilla/drivers/block/paride/frpw.c	Sun Dec  6 00:14:37 1998
+++ linux/drivers/block/paride/frpw.c	Sun Dec 27 21:08:29 1998
@@ -5,6 +5,12 @@
 	frpw.c is a low-level protocol driver for the Freecom "Power"
 	parallel port IDE adapter.
 	
+	Some applications of this adapter may require a "printer" reset
+	prior to loading the driver.  This can be done by loading and
+	unloading the "lp" driver, or it can be done by this driver
+	if you define FRPW_HARD_RESET.  The latter is not recommended
+	as it may upset devices on other ports.
+
 */
 
 /* Changes:
@@ -13,10 +19,11 @@
 			       fix chip detect
 			       added EPP-16 and EPP-32
 	1.02    GRG 1998.09.23 added hard reset to initialisation process
+	1.03    GRG 1998.12.14 made hard reset conditional
 
 */
 
-#define	FRPW_VERSION	"1.02" 
+#define	FRPW_VERSION	"1.03" 
 
 #include <linux/module.h>
 #include <linux/delay.h>
@@ -185,8 +192,10 @@
 
 {	int olddelay, a, b;
 
+#ifdef FRPW_HARD_RESET
         w0(0); w2(8); udelay(50); w2(0xc);   /* parallel bus reset */
         udelay(1500000);
+#endif
 
 	olddelay = pi->delay;
 	pi->delay = 10;
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/paride/jumbo linux/drivers/block/paride/jumbo
--- linux.vanilla/drivers/block/paride/jumbo	Sun Dec  6 00:14:37 1998
+++ linux/drivers/block/paride/jumbo	Sun Dec 27 21:08:29 1998
@@ -53,11 +53,11 @@
 FK="-D__KERNEL__ -I ../../../include"
 FLCH=-D_LINUX_CONFIG_H
 #
-echo cc $FK $FSMP $FLCH $FPARP $FPROTO -Wall -O2 -o Jb.o -c paride.c
-cc $FK $FSMP $FLCH $FPARP $FPROTO -Wall -O2 -o Jb.o -c paride.c
+echo cc $FK $FSMP $FLCH $FPARP $FPROTO $FMODV -Wall -O2 -o Jb.o -c paride.c
+cc $FK $FSMP $FLCH $FPARP $FPROTO $FMODV -Wall -O2 -o Jb.o -c paride.c
 #
-echo cc $FK $FSMP -Wall -O2 -o Jp.o -c $PROTO.c
-cc $FK $FSMP -Wall -O2 -o Jp.o -c $PROTO.c
+echo cc $FK $FSMP $FMODV -Wall -O2 -o Jp.o -c $PROTO.c
+cc $FK $FSMP $FMODV -Wall -O2 -o Jp.o -c $PROTO.c
 #
 echo cc $FK $FSMP $FMODV -DMODULE -DPARIDE_JUMBO -Wall -O2 -o Jd.o -c $HLD.c
 cc $FK $FSMP $FMODV -DMODULE -DPARIDE_JUMBO -Wall -O2 -o Jd.o -c $HLD.c
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/paride/on26.c linux/drivers/block/paride/on26.c
--- linux.vanilla/drivers/block/paride/on26.c	Sun Dec  6 00:14:37 1998
+++ linux/drivers/block/paride/on26.c	Fri Jan 29 14:13:09 1999
@@ -11,10 +11,12 @@
 
         1.01    GRG 1998.05.06 init_proto, release_proto
 	1.02    GRG 1998.09.23 updates for the -E rev chip
+	1.03    GRG 1998.12.14 fix for slave drives
+	1.04    GRG 1998.12.20 yet another bug fix
 
 */
 
-#define ON26_VERSION      "1.02"
+#define ON26_VERSION      "1.04"
 
 #include <linux/module.h>
 #include <linux/delay.h>
@@ -118,9 +120,11 @@
         w2(pi->saved_r2);
 } 
 
+#define	RESET_WAIT  200
+
 static int on26_test_port( PIA *pi)  /* hard reset */
 
-{       int     i, m, d;
+{       int     i, m, d, x, y;
 
         pi->saved_r0 = r0();
         pi->saved_r2 = r2();
@@ -151,11 +155,18 @@
             
             on26_write_regr(pi,0,6,0xa0);
 
-            for (i=0;i<100;i++) {
-                if (!(on26_read_regr(pi,0,7) & 0x80)) break;
+            for (i=0;i<RESET_WAIT;i++) {
+                on26_write_regr(pi,0,6,0xa0);
+                x = on26_read_regr(pi,0,7);
+                on26_write_regr(pi,0,6,0xb0);
+                y = on26_read_regr(pi,0,7);
+                if (!((x&0x80)||(y&0x80))) break;
                 udelay(100000);
             }
 
+	    if (i == RESET_WAIT) 
+		printk("on26: Device reset failed (%x,%x)\n",x,y);
+
             w0(4); P1; w0(4); P1;
         }
 
@@ -189,7 +200,7 @@
         case 1: w0(1); P1; w0(1); P2; w0(2); P1; w0(0x19); P2; w0(0); P1;
 		udelay(10);
                 for (k=0;k<count/2;k++) {
-                        w2(0x26); buf[2*k] = r0(); 
+                        w2(0x26); buf[2*k] = r0();  
 			w2(0x24); buf[2*k+1] = r0();
                 }
                 w0(2); P1; w0(9); P2;
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/paride/paride.c linux/drivers/block/paride/paride.c
--- linux.vanilla/drivers/block/paride/paride.c	Fri Jul 24 17:28:57 1998
+++ linux/drivers/block/paride/paride.c	Sun Dec 27 21:08:29 1998
@@ -11,10 +11,12 @@
 
 	1.01	GRG 1998.05.03	Use spinlocks
 	1.02	GRG 1998.05.05  init_proto, release_proto, ktti
+	1.03    GRG 1998.08.15  eliminate compiler warning
+	1.04    GRG 1998.11.28  added support for FRIQ
 
 */
 
-#define PI_VERSION      "1.02"
+#define PI_VERSION      "1.04"
 
 #include <linux/module.h>
 #include <linux/config.h>
@@ -449,6 +451,11 @@
           pi_register(&frpw);
         };
 #endif
+#ifdef CONFIG_PARIDE_FRIQ
+        { extern struct pi_protocol friq;
+          pi_register(&friq);
+        };
+#endif 
 #ifdef CONFIG_PARIDE_FIT2
         { extern struct pi_protocol fit2;
           pi_register(&fit2);
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/paride/pseudo.h linux/drivers/block/paride/pseudo.h
--- linux.vanilla/drivers/block/paride/pseudo.h	Fri Jul 24 17:28:57 1998
+++ linux/drivers/block/paride/pseudo.h	Sun Dec 27 21:08:29 1998
@@ -16,19 +16,21 @@
 	when either it returns true, or timeout jiffies have passed,
 	continuation() will be invoked.
 
-	If nice is true, the test will done approximately once a
+	If nice is 1, the test will done approximately once a
 	jiffy.  If nice is 0, the test will also be done whenever
-	the scheduler runs (by adding it to a task queue).
+	the scheduler runs (by adding it to a task queue).  If
+	nice is greater than 1, the test will be done once every
+	(nice-1) jiffies. 
 
 */
 
 /* Changes:
 
 	1.01	1998.05.03	Switched from cli()/sti() to spinlocks
-
+	1.02    1998.12.14      Added support for nice > 1
 */
 	
-#define PS_VERSION	"1.01"
+#define PS_VERSION	"1.02"
 
 #include <linux/sched.h>
 #include <linux/timer.h>
@@ -37,13 +39,13 @@
 static void ps_timer_int( unsigned long data);
 static void ps_tq_int( void *data);
 
-static int ps_use_tq = 1;
 static void (* ps_continuation)(void);
 static int (* ps_ready)(void);
 static int ps_then;
 static int ps_timeout;
 static int ps_timer_active = 0;
 static int ps_tq_active = 0;
+static int ps_nice = 0;
 
 /* static spinlock_t ps_spinlock = SPIN_LOCK_UNLOCKED; */
 
@@ -62,9 +64,9 @@
 	ps_ready = ready;
         ps_then = jiffies;
 	ps_timeout = jiffies + timeout;
-	ps_use_tq = !nice;
+	ps_nice = nice;
 
-        if (ps_use_tq && !ps_tq_active) {
+        if (!ps_nice && !ps_tq_active) {
 #ifdef HAVE_DISABLE_HLT
                 disable_hlt();
 #endif
@@ -74,7 +76,7 @@
 
         if (!ps_timer_active) {
 		ps_timer_active = 1;
-                ps_timer.expires = jiffies;
+                ps_timer.expires = jiffies + (ps_nice>0)?(ps_nice-1):0;
                 add_timer(&ps_timer);
         }
 
@@ -136,7 +138,7 @@
 		return;
 		}
 	ps_timer_active = 1;
-        ps_timer.expires = jiffies;
+        ps_timer.expires = jiffies + (ps_nice>0)?(ps_nice-1):0;
         add_timer(&ps_timer);
         spin_unlock_irqrestore(&ps_spinlock,flags);
 }
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/paride/pt.c linux/drivers/block/paride/pt.c
--- linux.vanilla/drivers/block/paride/pt.c	Sun Dec  6 00:14:37 1998
+++ linux/drivers/block/paride/pt.c	Sun Dec 27 21:08:29 1998
@@ -464,7 +464,7 @@
 
 {	int	k, e, s;
 
-	k = 0;
+	k = 0; e = 0; s = 0;
 	while (k < tmo) {
 		pt_sleep(pause);
 		k++;
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/proc_array.c linux/drivers/block/proc_array.c
--- linux.vanilla/drivers/block/proc_array.c	Thu Jan  1 01:00:00 1970
+++ linux/drivers/block/proc_array.c	Fri Jan 29 14:22:46 1999
@@ -0,0 +1,119 @@
+/*
+ *  Taken from linux/fs/proc/net.c
+ *
+ *  Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ *  gjh 3/'93 heim@peanuts.informatik.uni-tuebingen.de (Gerald J. Heim)
+ *            most of this file is stolen from base.c
+ *            it works, but you shouldn't use it as a guideline
+ *            for new proc-fs entries. once i'll make it better.
+ * fvk 3/'93  waltje@uwalt.nl.mugnet.org (Fred N. van Kempen)
+ *	      cleaned up the whole thing, moved "net" specific code to
+ *	      the NET kernel layer (where it belonged in the first place).
+ * Michael K. Johnson (johnsonm@stolaf.edu) 3/93
+ *            Added support from my previous inet.c.  Cleaned things up
+ *            quite a bit, modularized the code.
+ * fvk 4/'93  waltje@uwalt.nl.mugnet.org (Fred N. van Kempen)
+ *	      Renamed "route_get_info()" to "rt_get_info()" for consistency.
+ * Alan Cox (gw4pts@gw4pts.ampr.org) 4/94
+ *	      Dusted off the code and added IPX. Fixed the 4K limit.
+ * Erik Schoenfelder (schoenfr@ibr.cs.tu-bs.de)
+ *	      /proc/net/snmp.
+ * Alan Cox (gw4pts@gw4pts.ampr.org) 1/95
+ *	      Added Appletalk slots
+ *
+ *  proc diskarray directory handling functions
+ */
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+
+#include <asm/segment.h>
+
+#define PROC_BLOCK_SIZE	(3*1024)		/* 4K page size but our output routines use some slack for overruns */
+
+static int proc_readdiskarray(struct inode * inode, struct file * file,
+			char * buf, int count)
+{
+	char * page;
+	int bytes=count;
+	int copied=0;
+	char *start;
+	struct proc_dir_entry * dp;
+
+	if (count < 0)
+		return -EINVAL;
+	dp = (struct proc_dir_entry *) inode->u.generic_ip;
+	if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+		return -ENOMEM;
+
+	while (bytes>0)
+	{
+		int length, thistime=bytes;
+		if (bytes > PROC_BLOCK_SIZE)
+			thistime=PROC_BLOCK_SIZE;
+
+		length = dp->get_info(page, &start,
+				      file->f_pos,
+				      thistime, (int)dp);
+
+		/*
+ 		 *	We have been given a non page aligned block of
+		 *	the data we asked for + a bit. We have been given
+ 		 *	the start pointer and we know the length.. 
+		 */
+
+		if (length <= 0)
+			break;
+		/*
+ 		 *	Copy the bytes
+		 */
+		memcpy_tofs(buf+copied, start, length);
+		file->f_pos += length;	/* Move down the file */
+		bytes  -= length;
+		copied += length;
+		if (length<thistime)
+			break;	/* End of file */
+	}
+	free_page((unsigned long) page);
+	return copied;
+}
+
+static struct file_operations proc_diskarray_operations = {
+	NULL,			/* lseek - default */
+	proc_readdiskarray,		/* read - bad */
+	NULL,			/* write - bad */
+	NULL,			/* readdir */
+	NULL,			/* select - default */
+	NULL,			/* ioctl - default */
+	NULL,			/* mmap */
+	NULL,			/* no special open code */
+	NULL,			/* no special release code */
+	NULL			/* can't fsync */
+};
+
+/*
+ * proc directories can do almost nothing..
+ */
+struct inode_operations proc_diskarray_inode_operations = {
+	&proc_diskarray_operations,	/* default diskarray file-ops */
+	NULL,			/* create */
+	NULL,			/* lookup */
+	NULL,			/* link */
+	NULL,			/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	NULL			/* permission */
+};
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/triton.c linux/drivers/block/triton.c
--- linux.vanilla/drivers/block/triton.c	Fri Jul 24 17:28:58 1998
+++ linux/drivers/block/triton.c	Sun Dec 27 21:31:37 1998
@@ -1,107 +1,18 @@
 /*
  *  linux/drivers/block/triton.c	Version 1.13  Aug 12, 1996
+ *					Version 1.13a June 1998 - new chipsets
+ *					Version 1.13b July 1998 - DMA blacklist
  *
  *  Copyright (c) 1995-1996  Mark Lord
  *  May be copied or modified under the terms of the GNU General Public License
  */
 
 /*
- * This module provides support for the Bus Master IDE DMA function
- * of the Intel PCI Triton I/II chipsets (i82371FB or i82371SB).
- *
- * Pretty much the same code will work for the OPTi "Viper" chipset.
- * Look for DMA support for this in linux kernel 2.1.xx, when it appears.
- *
- * DMA is currently supported only for hard disk drives (not cdroms).
- *
- * Support for cdroms will likely be added at a later date,
- * after broader experience has been obtained with hard disks.
- *
- * Up to four drives may be enabled for DMA, and the Triton chipset will
- * (hopefully) arbitrate the PCI bus among them.  Note that the i82371 chip
- * provides a single "line buffer" for the BM IDE function, so performance of
- * multiple (two) drives doing DMA simultaneously will suffer somewhat,
- * as they contest for that resource bottleneck.  This is handled transparently
- * inside the i82371 chip.
- *
- * By default, DMA support is prepared for use, but is currently enabled only
- * for drives which support multi-word DMA mode2 (mword2), or which are
- * recognized as "good" (see table below).  Drives with only mode0 or mode1
- * (single or multi) DMA should also work with this chipset/driver (eg. MC2112A)
- * but are not enabled by default.  Use "hdparm -i" to view modes supported
- * by a given drive.
- *
- * The hdparm-2.4 (or later) utility can be used for manually enabling/disabling
- * DMA support, but must be (re-)compiled against this kernel version or later.
- *
- * To enable DMA, use "hdparm -d1 /dev/hd?" on a per-drive basis after booting.
- * If problems arise, ide.c will disable DMA operation after a few retries.
- * This error recovery mechanism works and has been extremely well exercised.
- *
- * IDE drives, depending on their vintage, may support several different modes
- * of DMA operation.  The boot-time modes are indicated with a "*" in
- * the "hdparm -i" listing, and can be changed with *knowledgeable* use of
- * the "hdparm -X" feature.  There is seldom a need to do this, as drives
- * normally power-up with their "best" PIO/DMA modes enabled.
- *
- * Testing was done with an ASUS P55TP4XE/100 system and the following drives:
- *
- *   Quantum Fireball 1080A (1Gig w/83kB buffer), DMA mode2, PIO mode4.
- *	- DMA mode2 works well (7.4MB/sec), despite the tiny on-drive buffer.
- *	- This drive also does PIO mode4, at about the same speed as DMA mode2.
- *	  An awesome drive for the price!
- *
- *   Fujitsu M1606TA (1Gig w/256kB buffer), DMA mode2, PIO mode4.
- *	- DMA mode2 gives horrible performance (1.6MB/sec), despite the good
- *	  size of the on-drive buffer and a boasted 10ms average access time.
- *	- PIO mode4 was better, but peaked at a mere 4.5MB/sec.
- *
- *   Micropolis MC2112A (1Gig w/508kB buffer), drive pre-dates EIDE and ATA2.
- *	- DMA works fine (2.2MB/sec), probably due to the large on-drive buffer.
- *	- This older drive can also be tweaked for fastPIO (3.7MB/sec) by using
- *	  maximum clock settings (5,4) and setting all flags except prefetch.
- *
- *   Western Digital AC31000H (1Gig w/128kB buffer), DMA mode1, PIO mode3.
- *	- DMA does not work reliably.  The drive appears to be somewhat tardy
- *	  in deasserting DMARQ at the end of a sector.  This is evident in
- *	  the observation that WRITEs work most of the time, depending on
- *	  cache-buffer occupancy, but multi-sector reads seldom work.
- *
- * Testing was done with a Gigabyte GA-586 ATE system and the following drive:
- * (Uwe Bonnes - bon@elektron.ikp.physik.th-darmstadt.de)
- *
- *   Western Digital AC31600H (1.6Gig w/128kB buffer), DMA mode2, PIO mode4.
- *	- much better than its 1Gig cousin, this drive is reported to work
- *	  very well with DMA (7.3MB/sec).
- *
- * Other drives:
- *
- *   Maxtor 7540AV (515Meg w/32kB buffer), DMA modes mword0/sword2, PIO mode3.
- *	- a budget drive, with budget performance, around 3MB/sec.
- *
- *   Western Digital AC2850F (814Meg w/64kB buffer), DMA mode1, PIO mode3.
- *	- another "caviar" drive, similar to the AC31000, except that this one
- *	  worked with DMA in at least one system.  Throughput is about 3.8MB/sec
- *	  for both DMA and PIO.
- *
- *   Conner CFS850A (812Meg w/64kB buffer), DMA mode2, PIO mode4.
- *	- like most Conner models, this drive proves that even a fast interface
- *	  cannot improve slow media.  Both DMA and PIO peak around 3.5MB/sec.
- *
- *   Maxtor 71260AT (1204Meg w/256kB buffer), DMA mword0/sword2, PIO mode3.
- *	- works with DMA, on some systems (but not always on others, eg. Dell),
- *	giving 3-4MB/sec performance, about the same as mode3.
- *
- * If you have any drive models to add, email your results to:  mlord@pobox.com
- * Keep an eye on /var/adm/messages for "DMA disabled" messages.
- *
- * Some people have reported trouble with Intel Zappa motherboards.
- * This can be fixed by upgrading the AMI BIOS to version 1.00.04.BS0,
- * available from ftp://ftp.intel.com/pub/bios/10004bs0.exe
- * (thanks to Glen Morrell <glen@spin.Stanford.edu> for researching this).
- *
- * And, yes, Intel Zappa boards really *do* use the Triton IDE ports.
+ * This module provides support for Bus Master IDE DMA functions in various
+ * motherboard chipsets and PCI controller cards.
+ * Please check /Documentation/ide.txt and /Documentation/udma.txt for details.
  */
+
 #include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
@@ -120,6 +31,13 @@
 #include "ide.h"
 
 #undef DISPLAY_TRITON_TIMINGS	/* define this to display timings */
+#undef DISPLAY_APOLLO_TIMINGS	/* define this for extensive debugging information */
+
+#if defined(CONFIG_PROC_FS) && defined(DISPLAY_APOLLO_TIMINGS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+#include <linux/via_ide_dma.h>
+#endif
 
 /*
  * good_dma_drives() lists the model names (from "hdparm -i")
@@ -132,6 +50,27 @@
 				 NULL};
 
 /*
+ * bad_dma_drives() lists the model names (from "hdparm -i")
+ * of drives which supposedly support (U)DMA but which are
+ * known to corrupt data with this interface under Linux.
+ *
+ * Note: the list was generated by statistical analysis of problem
+ * reports. It's not clear if there are problems with the drives,
+ * or with some combination of drive/controller or what. 
+ *
+ * You can forcibly override this if you wish. This is the kernel
+ * 'Tread carefully' list.
+ *
+ * Finally see http://www.wdc.com/quality/err-rec.html if you have
+ * one of the listed drives. 
+ */
+const char *bad_dma_drives[] = {"WDC AC11000H",
+				"WDC AC22100H",
+				"WDC AC32500H",
+				"WDC AC33100H",
+				 NULL};
+
+/*
  * Our Physical Region Descriptor (PRD) table should be large enough
  * to handle the biggest I/O request we are likely to see.  Since requests
  * can have no more than 256 sectors, and since the typical blocksize is
@@ -150,6 +89,7 @@
 #define PRD_BYTES	8
 #define PRD_ENTRIES	(PAGE_SIZE / (2 * PRD_BYTES))
 #define DEFAULT_BMIBA	0xe800	/* in case BIOS did not init it */
+#define DEFAULT_BMCRBA  0xcc00  /* VIA's default value */
 
 /*
  * dma_intr() is the handler for disk read/write DMA interrupts
@@ -161,8 +101,8 @@
 	struct request *rq = HWGROUP(drive)->rq;
 	unsigned short dma_base = HWIF(drive)->dma_base;
 
-	dma_stat = inb(dma_base+2);		/* get DMA status */
 	outb(inb(dma_base)&~1, dma_base);	/* stop DMA operation */
+	dma_stat = inb(dma_base+2);		/* get DMA status */
 	stat = GET_STAT();			/* get drive status */
 	if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
 		if ((dma_stat & 7) == 4) {	/* verify good DMA status */
@@ -244,23 +184,37 @@
 	return 1;	/* let the PIO routines handle this weirdness */
 }
 
+/*
+ * We will only enable drives with multi-word (mode2) (U)DMA capabilities,
+ * and ignore the very rare cases of drives that can only do single-word
+ * (modes 0 & 1) (U)DMA transfers. We also discard "blacklisted" hard disks.
+ */
 static int config_drive_for_dma (ide_drive_t *drive)
 {
 	const char **list;
 	struct hd_driveid *id = drive->id;
 
 	if (id && (id->capability & 1)) {
-		/* Enable DMA on any drive that has UltraDMA (mode 0/1/2) enabled */
+		/* Consult the list of known "bad" drives */
+		list = bad_dma_drives;
+		while (*list) {
+			if (!strcmp(*list++,id->model)) {
+				drive->using_dma = 0;   /* no DMA */
+				printk("ide: Disabling DMA modes on %s drive (%s).\n", drive->name, id->model);
+				return 1;	/* DMA disabled */
+			}
+		}
+		/* Enable DMA on any drive that has mode 2 UltraDMA enabled */
 		if (id->field_valid & 4)	/* UltraDMA */
-			if  ((id->dma_ultra & (id->dma_ultra >> 8) & 7)) {
+			if  ((id->dma_ultra & 0x404) == 0x404) {
 				drive->using_dma = 1;
-				return 0;	/* dma enabled */
+				return 0;	/* DMA enabled */
 			}
-		/* Enable DMA on any drive that has mode2 DMA (multi or single) enabled */
+		/* Enable DMA on any drive that has mode2 DMA enabled */
 		if (id->field_valid & 2)	/* regular DMA */
-			if  ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404) {
+			if  ((id->dma_mword & 0x404) == 0x404) {
 				drive->using_dma = 1;
-				return 0;	/* dma enabled */
+				return 0;	/* DMA enabled */
 			}
 		/* Consult the list of known "good" drives */
 		list = good_dma_drives;
@@ -387,22 +341,135 @@
 }
 
 /*
+ *  Set VIA Chipset Timings for (U)DMA modes enabled.
+ */
+static int set_via_timings (byte bus, byte fn, byte post, byte flush)
+{
+	byte via_config = 0;
+	int rc = 0;
+
+	/* setting IDE read prefetch buffer and IDE post write buffer */
+	if ((rc = pcibios_read_config_byte(bus, fn, 0x41, &via_config)))
+		return (1);
+	if ((rc = pcibios_write_config_byte(bus, fn, 0x41, via_config | post)))
+		return (1);
+
+	/* setting Channel read and End-of-sector FIFO flush: */
+	if ((rc = pcibios_read_config_byte(bus, fn, 0x46, &via_config)))
+		return (1);
+	if ((rc = pcibios_write_config_byte(bus, fn, 0x46, via_config | flush)))
+		return (1);
+
+	return (0);
+}
+
+/*
  * ide_init_triton() prepares the IDE driver for DMA operation.
  * This routine is called once, from ide.c during driver initialization,
- * for each triton chipset which is found (unlikely to be more than one).
+ * for each BM-DMA chipset which is found (rarely more than one).
  */
 void ide_init_triton (byte bus, byte fn)
 {
 	int rc = 0, h;
 	int dma_enabled = 0;
-	unsigned short pcicmd;
-	unsigned int bmiba, timings;
+	unsigned short io[6], count = 0, step_count = 0;
+	unsigned short pcicmd, vendor, device, class;
+	unsigned int bmiba, timings, reg, tmp;
+	unsigned int addressbios = 0;
+
+#ifdef DISPLAY_APOLLO_TIMINGS
+	bmide_bus = bus;
+	bmide_fn = fn;
+#endif /* DISPLAY_APOLLO_TIMINGS */
+
+/*
+ *  We pick up the vendor, device, and class info for selecting the correct
+ *  controller that is supported.  Since we can access this routine more than
+ *  once with the use of onboard and off-board EIDE controllers, a method
+ *  of determining "who is who for what" is needed.
+ */
+
+	pcibios_read_config_word (bus, fn, PCI_VENDOR_ID, &vendor);
+	pcibios_read_config_word (bus, fn, PCI_DEVICE_ID, &device);
+	pcibios_read_config_word (bus, fn, PCI_CLASS_DEVICE, &class);
+
+	switch(vendor) {
+		case PCI_VENDOR_ID_INTEL:
+                   printk("ide: Intel 82371 (single FIFO) DMA Bus Mastering IDE ");
+                   break;
+		case PCI_VENDOR_ID_SI:
+			printk("ide: SiS 5513 (dual FIFO) DMA Bus Mastering IDE ");
+			break;
+                case PCI_VENDOR_ID_VIA:
+                   printk("ide: VIA VT82C586B (split FIFO) UDMA Bus Mastering IDE ");
+                   break;
+		case PCI_VENDOR_ID_PROMISE:
+			/*	PCI_CLASS_STORAGE_RAID == class	*/
+			/*
+			 *  I have been able to make my Promise Ultra33 UDMA card change class.
+			 *  It has reported as both PCI_CLASS_STORAGE_RAID and PCI_CLASS_STORAGE_IDE.
+			 *  Since the PCI_CLASS_STORAGE_RAID mode should automatically mirror the
+			 *  two halves of the PCI_CONFIG register data, but sometimes it forgets.
+			 *  Thus we guarantee that they are identical, with a quick check and
+			 *  correction if needed.
+			 *  PDC20246 (primary) PDC20247 (secondary) IDE hwif's.
+			 *
+			 *  Note that Promise "stories,fibs,..." about this device not being
+			 *  capable of ATAPI and AT devices.
+			 */
+			if (PCI_CLASS_STORAGE_RAID == class) {
+				unsigned char irq1 = 0, irq2 = 0;
+				pcibios_read_config_byte (bus, fn, PCI_INTERRUPT_LINE, &irq1);
+				pcibios_read_config_byte (bus, fn, (PCI_INTERRUPT_LINE)|0x80, &irq2);
+				if (irq1 != irq2) {
+					pcibios_write_config_byte(bus, fn, (PCI_INTERRUPT_LINE)|0x80, irq1);
+				}
+			}
+		case PCI_VENDOR_ID_ARTOP:
+			/*	PCI_CLASS_STORAGE_SCSI == class	*/
+			/*
+			 *  I have found that by stroking rom_enable_bit on both the AEC6210U/UF and
+			 *  PDC20246 controller cards, the features desired are almost guaranteed
+			 *  to be enabled and compatible.  This ROM may not be registered in the
+			 *  config data, but it can be turned on.  Registration failure has only
+			 *  been observed if and only if Linux sets up the pci_io_address in the
+			 *  0x6000 range.  If they are setup in the 0xef00 range it is reported.
+			 *  WHY??? got me.........
+			 */
+			printk("ide: %s UDMA Bus Mastering ",
+				(vendor == PCI_VENDOR_ID_ARTOP) ? "AEC6210" : "PDC20246");
+			pcibios_read_config_dword(bus, fn, PCI_ROM_ADDRESS, &addressbios);
+			if (addressbios) {
+				pcibios_write_config_byte(bus, fn, PCI_ROM_ADDRESS, addressbios | PCI_ROM_ADDRESS_ENABLE);
+				printk("with ROM enabled at 0x%08x", addressbios);
+			}
+			/*
+			 *  This was stripped out of 2.1.XXX kernel code and parts from a patch called
+			 *  promise_update.  This finds the PCI_BASE_ADDRESS spaces and makes them
+			 *  available for configuration later.
+			 *  PCI_BASE_ADDRESS_0  hwif0->io_base
+			 *  PCI_BASE_ADDRESS_1  hwif0->ctl_port
+			 *  PCI_BASE_ADDRESS_2  hwif1->io_base
+			 *  PCI_BASE_ADDRESS_3  hwif1->ctl_port
+			 *  PCI_BASE_ADDRESS_4  bmiba
+			 */
+			memset(io, 0, 6 * sizeof(unsigned short));
+			for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) {
+				pcibios_read_config_dword(bus, fn, reg, &tmp);
+				if (tmp & PCI_BASE_ADDRESS_SPACE_IO)
+					io[count++] = tmp & PCI_BASE_ADDRESS_IO_MASK;
+			}
+			break;
+		default:
+			return;
+	}
+
+	printk("\n    Controller on PCI bus %d function %d\n", bus, fn);
 
-	printk("ide: i82371 PIIX (Triton) on PCI bus %d function %d\n", bus, fn);
 	/*
 	 * See if IDE and BM-DMA features are enabled:
 	 */
-	if ((rc = pcibios_read_config_word(bus, fn, 0x04, &pcicmd)))
+	if ((rc = pcibios_read_config_word(bus, fn, PCI_COMMAND, &pcicmd)))
 		goto quit;
 	if ((pcicmd & 1) == 0)  {
 		printk("ide: ports are not enabled (BIOS)\n");
@@ -416,21 +483,21 @@
 		 */
 		int try_again = 1;
 		do {
-			if ((rc = pcibios_read_config_dword(bus, fn, 0x20, &bmiba)))
+			if ((rc = pcibios_read_config_dword(bus, fn, PCI_BASE_ADDRESS_4, &bmiba)))
 				goto quit;
 			bmiba &= 0xfff0;	/* extract port base address */
 			if (bmiba) {
 				dma_enabled = 1;
 				break;
 			} else {
-				printk("ide: BM-DMA base register is invalid (0x%04x, PnP BIOS problem)\n", bmiba);
-				if (inb(DEFAULT_BMIBA) != 0xff || !try_again)
+                                printk("ide: BM-DMA base register is invalid (0x%04x, PnP BIOS problem)\n", bmiba);
+                                if (inb(((vendor == PCI_VENDOR_ID_VIA) ? DEFAULT_BMCRBA : DEFAULT_BMIBA)) != 0xff || !try_again)
 					break;
-				printk("ide: setting BM-DMA base register to 0x%04x\n", DEFAULT_BMIBA);
-				if ((rc = pcibios_write_config_word(bus, fn, 0x04, pcicmd&~1)))
+				printk("ide: setting BM-DMA base register to 0x%04x\n", ((vendor == PCI_VENDOR_ID_VIA) ? DEFAULT_BMCRBA : DEFAULT_BMIBA));
+				if ((rc = pcibios_write_config_word(bus, fn, PCI_COMMAND, pcicmd&~1)))
 					goto quit;
-				rc = pcibios_write_config_dword(bus, fn, 0x20, DEFAULT_BMIBA|1);
-				if (pcibios_write_config_word(bus, fn, 0x04, pcicmd|5) || rc)
+				rc = pcibios_write_config_dword(bus, fn, 0x20, ((vendor == PCI_VENDOR_ID_VIA) ? DEFAULT_BMCRBA : DEFAULT_BMIBA)|1);
+				if (pcibios_write_config_word(bus, fn, PCI_COMMAND, pcicmd|5) || rc)
 					goto quit;
 			}
 		} while (try_again--);
@@ -439,89 +506,218 @@
 	/*
 	 * See if ide port(s) are enabled
 	 */
-	if ((rc = pcibios_read_config_dword(bus, fn, 0x40, &timings)))
-		goto quit;
-	if (!(timings & 0x80008000)) {
-		printk("ide: neither port is enabled\n");
+	if ((rc = pcibios_read_config_dword(bus, fn,
+		(vendor == PCI_VENDOR_ID_PROMISE) ? 0x50 : 
+		(vendor == PCI_VENDOR_ID_ARTOP) ? 0x54 :
+		0x40, &timings)))
 		goto quit;
-	}
+	/*
+	 * We do a vendor check since the Ultra33 and AEC6210
+	 * holds their timings in a different location.
+	 */
+	printk("ide: timings == %08x\n", timings);
+
+	/*
+	 *  The switch preserves some stuff that was original.
+	 */
+	switch(vendor) {
+		case PCI_VENDOR_ID_INTEL:
+			if (!(timings & 0x80008000)) {
+				printk("ide: INTEL: neither port is enabled\n");
+				goto quit;
+			}
+			break;
+		case PCI_VENDOR_ID_VIA:
+			if(!(timings & 0x03)) {
+				printk("ide: VIA: neither port is enabled\n");
+				goto quit;
+			}
+			break;
+		case PCI_VENDOR_ID_SI:
+		case PCI_VENDOR_ID_PROMISE:
+		case PCI_VENDOR_ID_ARTOP:
+                default:
+                        break;
+        }
 
 	/*
 	 * Save the dma_base port addr for each interface
 	 */
 	for (h = 0; h < MAX_HWIFS; ++h) {
+		ide_hwif_t *hwif = &ide_hwifs[h];
+
+		/*
+		 *  This prevents the first contoller from accidentally
+		 *  initalizing the hwif's that it does not use and block
+		 *  an off-board ide-pci from getting in the game.
+		 */
+		if (step_count >= 2) {
+			goto quit;
+		}
+#ifdef CONFIG_BLK_DEV_OFFBOARD
+		/*
+		 *  This is a forced override for the onboard ide controller
+		 *  to be enabled, if one chooses to have an offboard ide-pci
+		 *  card as the primary booting device.  This beasty is
+		 *  for offboard UDMA upgrades with hard disks, but saving
+		 *  the onboard DMA2 controllers for CDROMS, TAPES, ZIPS, etc...
+		 */
+		if ((vendor == PCI_VENDOR_ID_INTEL) ||
+		    (vendor == PCI_VENDOR_ID_SI) ||
+		    (vendor == PCI_VENDOR_ID_VIA)) {
+			if (h == 2) {
+				hwif->io_base = 0x1f0;
+				hwif->ctl_port = 0x3f6;
+				hwif->irq = 14;
+				hwif->noprobe = 0;
+			}
+			if (h == 3) {
+				hwif->io_base = 0x170;
+				hwif->ctl_port = 0x376;
+				hwif->irq = 15;
+				hwif->noprobe = 0;
+			}
+		}
+#endif /* CONFIG_BLK_DEV_OFFBOARD */
+		/*
+		 *  If the chipset is listed as "ide_unknown", lets get a
+		 *  hwif while they last.  This does the first check on
+		 *  the current availability of the ide_hwifs[h] in question.
+		 */
+		if (hwif->chipset != ide_unknown) {
+			continue;
+		} else if (vendor == PCI_VENDOR_ID_INTEL) {
+			unsigned short time;
 #ifdef DISPLAY_TRITON_TIMINGS
-		byte s_clks, r_clks;
-		unsigned short devid;
+			byte s_clks, r_clks;
+			unsigned short devid;
 #endif /* DISPLAY_TRITON_TIMINGS */
-		ide_hwif_t *hwif = &ide_hwifs[h];
-		unsigned short time;
-		if (hwif->io_base == 0x1f0) {
-			time = timings & 0xffff;
-			if ((time & 0x8000) == 0)	/* interface enabled? */
-				continue;
-			hwif->chipset = ide_triton;
-			if (dma_enabled)
-				init_triton_dma(hwif, bmiba);
-		} else if (hwif->io_base == 0x170) {
-			time = timings >> 16;
-			if ((time & 0x8000) == 0)	/* interface enabled? */
+			if (hwif->io_base == 0x1f0) {
+				time = timings & 0xffff;
+				if ((time & 0x8000) == 0)	/* interface enabled? */
+					continue;
+				hwif->chipset = ide_triton;
+				if (dma_enabled)
+					init_triton_dma(hwif, bmiba);
+				step_count++;
+			} else if (hwif->io_base == 0x170) {
+				time = timings >> 16;
+				if ((time & 0x8000) == 0)	/* interface enabled? */
+					continue;
+				hwif->chipset = ide_triton;
+				if (dma_enabled)
+					init_triton_dma(hwif, bmiba + 8);
+				step_count++;
+			} else {
 				continue;
-			hwif->chipset = ide_triton;
-			if (dma_enabled)
-				init_triton_dma(hwif, bmiba + 8);
-		} else
-			continue;
+			}
 #ifdef DISPLAY_TRITON_TIMINGS
-		s_clks = ((~time >> 12) & 3) + 2;
-		r_clks = ((~time >>  8) & 3) + 1;
-		printk("    %s timing: (0x%04x) sample_CLKs=%d, recovery_CLKs=%d\n",
-		 hwif->name, time, s_clks, r_clks);
-		if ((time & 0x40) && !pcibios_read_config_word(bus, fn, 0x02, &devid)
-		 && devid == PCI_DEVICE_ID_INTEL_82371SB_1)
-		{
-			byte stime;
-			if (pcibios_read_config_byte(bus, fn, 0x44, &stime)) {
-				if (hwif->io_base == 0x1f0) {
-					s_clks = ~stime >> 6;
-					r_clks = ~stime >> 4;
+			s_clks = ((~time >> 12) & 3) + 2;
+			r_clks = ((~time >>  8) & 3) + 1;
+			printk("    %s timing: (0x%04x) sample_CLKs=%d, recovery_CLKs=%d\n",
+				hwif->name, time, s_clks, r_clks);
+			if ((time & 0x40) && !pcibios_read_config_word(bus, fn, PCI_DEVICE_ID, &devid)
+				&& devid == PCI_DEVICE_ID_INTEL_82371SB_1) {
+				byte stime;
+				if (pcibios_read_config_byte(bus, fn, 0x44, &stime)) {
+					if (hwif->io_base == 0x1f0) {
+						s_clks = ~stime >> 6;
+						r_clks = ~stime >> 4;
+					} else {
+						s_clks = ~stime >> 2;
+						r_clks = ~stime;
+					}
+					s_clks = (s_clks & 3) + 2;
+					r_clks = (r_clks & 3) + 1;
+					printk("                   slave: sample_CLKs=%d, recovery_CLKs=%d\n",
+						s_clks, r_clks);
+				}
+			}
+			print_triton_drive_flags (0, time & 0xf);
+			print_triton_drive_flags (1, (time >> 4) & 0xf);
+#endif /* DISPLAY_TRITON_TIMINGS */
+		} else if (vendor == PCI_VENDOR_ID_SI) {
+			if (hwif->io_base == 0x1f0) {
+				hwif->chipset = ide_triton;
+				if (dma_enabled)
+					init_triton_dma(hwif, bmiba);
+				step_count++;
+			} else if (hwif->io_base == 0x170) {
+				hwif->chipset = ide_triton;
+				if (dma_enabled)
+					init_triton_dma(hwif, bmiba + 8);
+				step_count++;
+			} else {
+				continue;
+			}
+		} else if(vendor == PCI_VENDOR_ID_VIA) {
+			if (hwif->io_base == 0x1f0) {
+				if((timings & 0x02) == 0)
+					continue;
+				hwif->chipset = ide_triton;
+				if (dma_enabled)
+					init_triton_dma(hwif, bmiba);
+				if (set_via_timings(bus, fn, 0xc0, 0xa0))
+					goto quit;
+#ifdef DISPLAY_APOLLO_TIMINGS
+				proc_register_dynamic(&proc_root, &via_proc_entry);
+#endif /* DISPLAY_APOLLO_TIMINGS */
+				step_count++;
+			} else if (hwif->io_base == 0x170) {
+				if((timings & 0x01) == 0)
+					continue;
+				hwif->chipset = ide_triton;
+				if (dma_enabled)
+					init_triton_dma(hwif, bmiba + 8);
+				if (set_via_timings(bus, fn, 0x30, 0x50))
+					goto quit;
+				step_count++;
+			} else {
+				continue;
+			}
+		} else if ((vendor == PCI_VENDOR_ID_PROMISE) ||
+			   (vendor == PCI_VENDOR_ID_ARTOP)) {
+	/*
+	 *  This silly tmp = h routine allows an off-board ide-pci card to
+	 *  be booted as primary hwifgroup, provided that the onboard
+	 *  controllers are disabled.  If they are active, then we wait our
+	 *  turn for hwif assignment.
+	 */
+			unsigned char irq = 0;
+			pcibios_read_config_byte (bus, fn, PCI_INTERRUPT_LINE, &irq);
+			if ((h == 0) || (h == 1)) {
+				tmp = h * 2;
+			} else {
+				tmp = (h - 2) * 2;
+			}
+			hwif->io_base = io[tmp];
+			hwif->ctl_port = io[tmp + 1] + 2;
+			hwif->irq = irq;
+			hwif->noprobe = 0;
+
+			if (vendor == PCI_VENDOR_ID_ARTOP) {
+				hwif->serialized = 1;
+			}
+
+			if (dma_enabled) {
+				if (!check_region(bmiba, 8)) {
+					hwif->chipset = ide_udma;
+					init_triton_dma(hwif, bmiba);
+					step_count++;
+				} else if (!check_region((bmiba + 0x08), 8)) {
+					if ((vendor == PCI_VENDOR_ID_PROMISE) &&
+					    (!check_region(bmiba+16, 16))) {
+						request_region(bmiba+16, 16, "PDC20246");
+					}
+					hwif->chipset = ide_udma;
+					init_triton_dma(hwif, bmiba + 8);
+					step_count++;
 				} else {
-					s_clks = ~stime >> 2;
-					r_clks = ~stime;
+					continue;
 				}
-				s_clks = (s_clks & 3) + 2;
-				r_clks = (r_clks & 3) + 1;
-				printk("                   slave: sample_CLKs=%d, recovery_CLKs=%d\n",
-		 			s_clks, r_clks);
 			}
 		}
-		print_triton_drive_flags (0, time & 0xf);
-		print_triton_drive_flags (1, (time >> 4) & 0xf);
-#endif /* DISPLAY_TRITON_TIMINGS */
 	}
 
-quit: if (rc) printk("ide: pcibios access failed - %s\n", pcibios_strerror(rc));
-}
-
-void ide_init_promise (byte bus, byte fn, ide_hwif_t *hwif0, ide_hwif_t *hwif1, unsigned short dma)
-{
-	int rc;
-	unsigned short pcicmd;
-	unsigned int bmiba = 0;
-
-	printk("ide: Enabling DMA for Promise Technology IDE Ultra-DMA 33 on PCI bus %d function %d, port 0x%04x\n", bus, fn, dma);
-	if ((rc = pcibios_read_config_word(bus, fn, 0x04, &pcicmd)) || (pcicmd & 1) == 0 || (pcicmd & 4) == 0)
-		goto abort;
-	if ((rc = pcibios_read_config_dword(bus, fn, 0x20, &bmiba)))
-		goto abort;
-	bmiba &= 0xfff0;	/* extract port base address */
-	if (bmiba != dma || !bmiba)
-		goto abort;
-	hwif0->chipset = ide_promise_udma;
-	hwif1->chipset = ide_promise_udma;
-	init_triton_dma(hwif0, bmiba);
-	init_triton_dma(hwif1, bmiba + 0x08);
-	return;
-abort:
-	printk(KERN_WARNING "ide: Promise/33 not configured correctly (BIOS)\n");
+	quit: if (rc) printk("ide: pcibios access failed - %s\n", pcibios_strerror(rc));
 }
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/cdrom/sbpcd.c linux/drivers/cdrom/sbpcd.c
--- linux.vanilla/drivers/cdrom/sbpcd.c	Sun Jun 21 18:41:20 1998
+++ linux/drivers/cdrom/sbpcd.c	Mon Dec 14 18:32:36 1998
@@ -4911,14 +4911,16 @@
 		msg(DBG_000, "sbp_data: beginning to read.\n");
 		p = D_S[d].sbp_buf + frame *  CD_FRAMESIZE;
 		if (sbpro_type==1) OUT(CDo_sel_i_d,1);
-		if (cmd_type==READ_M2)
+		if (cmd_type==READ_M2) {
                         if (do_16bit) insw(CDi_data, xa_head_buf, CD_XA_HEAD>>1);
                         else insb(CDi_data, xa_head_buf, CD_XA_HEAD);
+		}
 		if (do_16bit) insw(CDi_data, p, CD_FRAMESIZE>>1);
 		else insb(CDi_data, p, CD_FRAMESIZE);
-		if (cmd_type==READ_M2)
+		if (cmd_type==READ_M2) {
                         if (do_16bit) insw(CDi_data, xa_tail_buf, CD_XA_TAIL>>1);
                         else insb(CDi_data, xa_tail_buf, CD_XA_TAIL);
+		}
 		D_S[d].sbp_current++;
 		if (sbpro_type==1) OUT(CDo_sel_i_d,0);
 		if (cmd_type==READ_M2)
@@ -5502,9 +5504,10 @@
 		cc_ReadStatus();
 		i=ResponseStatus();  /* returns orig. status or p_busy_new */
 		if (famT_drive) i=ResponseStatus();  /* returns orig. status or p_busy_new */
-		if (i<0)
+		if (i<0) {
 			if (i!=-402)
 				msg(DBG_INF,"init: ResponseStatus returns %d.\n",i);
+		}
 		else
 		{
 			if (st_check)
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/char/Config.in linux/drivers/char/Config.in
--- linux.vanilla/drivers/char/Config.in	Sun Dec  6 00:14:37 1998
+++ linux/drivers/char/Config.in	Sun Dec  6 03:14:36 1998
@@ -5,6 +5,11 @@
 comment 'Character devices'
 
 tristate 'Standard/generic serial support' CONFIG_SERIAL
+if [ "$CONFIG_SERIAL" != "n" ]; then
+	if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+		bool '  PCI serial support' CONFIG_SERIAL_PCI
+	fi
+fi
 bool 'Digiboard PC/Xx Support' CONFIG_DIGI
 tristate 'Cyclades async mux support' CONFIG_CYCLADES
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/char/apm_bios.c linux/drivers/char/apm_bios.c
--- linux.vanilla/drivers/char/apm_bios.c	Sun Dec  6 00:14:37 1998
+++ linux/drivers/char/apm_bios.c	Fri Dec 11 20:35:53 1998
@@ -1167,11 +1167,11 @@
 	apm_bios_entry.offset = apm_bios_info.offset;
 	apm_bios_entry.segment = APM_CS;
 	set_base(gdt[APM_CS >> 3],
-		 0xc0000000 + ((unsigned long)apm_bios_info.cseg << 4));
+		 __PAGE_OFFSET + ((unsigned long)apm_bios_info.cseg << 4));
 	set_base(gdt[APM_CS_16 >> 3],
-		 0xc0000000 + ((unsigned long)apm_bios_info.cseg_16 << 4));
+		 __PAGE_OFFSET + ((unsigned long)apm_bios_info.cseg_16 << 4));
 	set_base(gdt[APM_DS >> 3],
-		 0xc0000000 + ((unsigned long)apm_bios_info.dseg << 4));
+		 __PAGE_OFFSET + ((unsigned long)apm_bios_info.dseg << 4));
 	if (apm_bios_info.version == 0x100) {
 		set_limit(gdt[APM_CS >> 3], 64 * 1024);
 		set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/char/cyclades.c linux/drivers/char/cyclades.c
--- linux.vanilla/drivers/char/cyclades.c	Sun Dec  6 00:14:37 1998
+++ linux/drivers/char/cyclades.c	Fri Jan 29 14:26:15 1999
@@ -1,7 +1,7 @@
 #define BLOCKMOVE
 #define	Z_WAKE
 static char rcsid[] =
-"$Revision: 2.1.1.10 $$Date: 1998/11/12 16:08:23 $";
+"$Revision: 2.1.1.11 $$Date: 1998/12/30 18:58:47 $";
 
 /*
  *  linux/drivers/char/cyclades.c
@@ -31,7 +31,11 @@
  *   void cleanup_module(void);
  *
  * $Log: cyclades.c,v $
- * Revision: 2.1.1.10  1998/11/12 16:08:23 ivan
+ * Revision 2.1.1.11  1998/12/30 18:58:47 ivan
+ * Changed access to PLX PCI bridge registers from I/O to MMIO, in
+ * order to make PLX9050-based boards work with certain motherboards.
+ *
+ * Revision 2.1.1.10  1998/11/12 16:08:23 ivan
  * cy_close function now resets (correctly) the tty->closing flag;
  * JIFFIES_DIFF macro fixed.
  *
@@ -4707,7 +4711,6 @@
 {
 #ifdef CONFIG_PCI
   unsigned char         cyy_bus, cyy_dev_fn, cyy_rev_id;
-  unsigned long         pci_intr_ctrl;
   unsigned char         cy_pci_irq;
   uclong                cy_pci_addr0, cy_pci_addr1, cy_pci_addr2;
   unsigned short        i,j,cy_pci_nchan, plx_ver;
@@ -4761,7 +4764,7 @@
             printk("Cyclom-Y/PCI:found  winaddr=0x%lx ioaddr=0x%lx\n",
 		(ulong)cy_pci_addr2, (ulong)cy_pci_addr1);
 #endif
-                cy_pci_addr1  &= PCI_BASE_ADDRESS_IO_MASK;
+                cy_pci_addr0  &= PCI_BASE_ADDRESS_MEM_MASK;
                 cy_pci_addr2  &= PCI_BASE_ADDRESS_MEM_MASK;
 
 #if defined(__alpha__)
@@ -4778,6 +4781,7 @@
 	            continue;
                 }
 #else
+		cy_pci_addr0  = (ulong) ioremap(cy_pci_addr0, CyPCI_Yctl);
                 if ((ulong)cy_pci_addr2 >= 0x100000)  /* above 1M? */
                     cy_pci_addr2 = (ulong) ioremap(cy_pci_addr2, CyPCI_Ywin);
 #endif
@@ -4827,7 +4831,7 @@
 
                 /* set cy_card */
                 cy_card[j].base_addr = (ulong)cy_pci_addr2;
-                cy_card[j].ctl_addr = 0;
+		cy_card[j].ctl_addr = (ulong)cy_pci_addr0;
                 cy_card[j].irq = (int) cy_pci_irq;
                 cy_card[j].bus_index = 1;
                 cy_card[j].first_line = cy_next_channel;
@@ -4839,20 +4843,16 @@
                 switch (plx_ver) {
 		    case PLX_9050:
 
-		    outw(inw(cy_pci_addr1+0x4c)|0x0040,cy_pci_addr1+0x4c);
-		    pci_intr_ctrl = (unsigned long)
-				(inw(cy_pci_addr1+0x4c)
-				| inw(cy_pci_addr1+0x4e)<<16);
+		    cy_writew(cy_pci_addr0+0x4c,
+			cy_readw(cy_pci_addr0+0x4c)|0x0040);
 		    break;
 
 		    case PLX_9060:
 		    case PLX_9080:
 		    default: /* Old boards, use PLX_9060 */
 
-		    outw(inw(cy_pci_addr1+0x68)|0x0900,cy_pci_addr1+0x68);
-		    pci_intr_ctrl = (unsigned long)
-				(inw(cy_pci_addr1+0x68)
-				| inw(cy_pci_addr1+0x6a)<<16);
+		    cy_writew(cy_pci_addr0+0x68,
+			cy_readw(cy_pci_addr0+0x68)|0x0900);
 		    break;
                 }
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/char/keyboard.c linux/drivers/char/keyboard.c
--- linux.vanilla/drivers/char/keyboard.c	Sun Jun 21 18:41:10 1998
+++ linux/drivers/char/keyboard.c	Sun Dec  6 03:37:59 1998
@@ -374,8 +374,13 @@
 	mark_bh(CONSOLE_BH);
 	add_keyboard_randomness(scancode);
 
-	tty = ttytab[fg_console];
- 	kbd = kbd_table + fg_console;
+	tty = ttytab? ttytab[fg_console]: NULL;
+	if (tty && (!tty->driver_data)) {
+		/* This is to workaround ugly bug in tty_io.c, which
+                   does not do locking when it should */
+		tty = NULL;
+	}
+	kbd = kbd_table + fg_console;
 	if ((raw_mode = (kbd->kbdmode == VC_RAW))) {
  		put_queue(scancode);
 		/* we do not return yet, because we want to maintain
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/char/random.c linux/drivers/char/random.c
--- linux.vanilla/drivers/char/random.c	Sun Dec  6 00:14:37 1998
+++ linux/drivers/char/random.c	Tue Dec 29 15:06:38 1998
@@ -1004,7 +1004,17 @@
 		buf += i;
 		add_timer_randomness(r, &extract_timer_state, nbytes);
 		if (to_user && need_resched)
+		{
+			if(current->signal & ~current->blocked)
+			{
+				if(nbytes==0)
+					ret = -ERESTARTSYS;
+				else
+					ret -= nbytes;
+				break;
+			}
 			schedule();
+		}
 	}
 
 	/* Wipe data from memory */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/char/serial.c linux/drivers/char/serial.c
--- linux.vanilla/drivers/char/serial.c	Sun Jun 21 18:41:10 1998
+++ linux/drivers/char/serial.c	Sun Dec 27 20:46:14 1998
@@ -19,6 +19,9 @@
  *      flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK.
  *                                            Bernd Anhäupl 05/17/96.
  * 
+ * Added Support for PCI serial boards which contain 16x50 Chips
+ * 31.10.1998 Henning P. Schmiedehausen <hps@tanstaafl.de>
+ * 
  * This module exports the following rs232 io functions:
  *
  *	int rs_init(void);
@@ -43,13 +46,18 @@
 #include <linux/ioport.h>
 #include <linux/mm.h>
 
+#ifdef CONFIG_SERIAL_PCI
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#endif
+
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/segment.h>
 #include <asm/bitops.h>
 
 static char *serial_name = "Serial driver";
-static char *serial_version = "4.13";
+static char *serial_version = "4.13p1";
 
 DECLARE_TASK_QUEUE(tq_serial);
 
@@ -83,6 +91,10 @@
 #undef SERIAL_DEBUG_OPEN
 #undef SERIAL_DEBUG_FLOW
 
+#ifdef CONFIG_SERIAL_PCI
+# undef SERIAL_DEBUG_PCI
+#endif
+
 #define RS_STROBE_TIME (10*HZ)
 #define RS_ISR_PASS_LIMIT 256
 
@@ -121,6 +133,14 @@
  */
 #define BASE_BAUD ( 1843200 / 16 )
 
+/*
+ * Well, it is not a 24,756 MHz clock but it is at least a start. 
+ * This PCI board here has a 14,7456 MHz crystal oscillator which is 
+ * eight times as fast as the standard serial clock...
+ */
+
+#define PCI_BAUD  ( 14745600 / 16 )
+
 /* Standard COM flags (except for COM4, because of the 8514 problem) */
 #define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST )
 #define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF
@@ -130,6 +150,29 @@
 #define BOCA_FLAGS 0
 #define HUB6_FLAGS 0
 	
+#ifdef CONFIG_SERIAL_PCI
+
+#define PCI_FLAGS (ASYNC_PCI|ASYNC_BOOT_AUTOCONF)
+
+#ifndef PCI_DEVICE_ID_PLX_SPCOM200
+#define PCI_DEVICE_ID_PLX_SPCOM200 0x1103
+#endif
+
+/*
+ * The chips we know about
+ */
+
+#define PCISER_PLX9050  0    /* PLX 9050 local bus bridge as serial card */
+#define PCISER_PCCOM4   1    /* "PC COM PCI Bus 4 port serial Adapter" -- from Alvin Sim <alvin@alloycp.com.au> */
+
+struct pci_serial_boards pci_serial_tbl[] = {
+  { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200, "SPCom 200", PCISER_PLX9050, pci_space_0|pci_space_1, 1, 0, 128, PCI_BAUD },
+  { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM4,   "PC COM 4",  PCISER_PCCOM4,  pci_space_0,             4, 8, 128, BASE_BAUD },
+  {	0, 0, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+#endif
+	
 /*
  * The following define the access methods for the HUB6 card. All
  * access is through two ports for all 24 possible chips. The card is
@@ -202,10 +245,42 @@
 	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) },	/* ttyS42 */
 	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) },	/* ttyS43 */
 #endif
+
+#ifdef CONFIG_SERIAL_PCI
+		{ 0, BASE_BAUD, 0x0, 0, 0 },	/* ttyS32 or bigger... */
+		{ 0, BASE_BAUD, 0x0, 0, 0 },	/* ttyS33 */
+		{ 0, BASE_BAUD, 0x0, 0, 0 },	/* ttyS34 */
+		{ 0, BASE_BAUD, 0x0, 0, 0 },	/* ttyS35 */
+		{ 0, BASE_BAUD, 0x0, 0, 0 },	/* ttyS36 */
+		{ 0, BASE_BAUD, 0x0, 0, 0 },	/* ttyS37 */
+		{ 0, BASE_BAUD, 0x0, 0, 0 },	/* ttyS38 */
+		{ 0, BASE_BAUD, 0x0, 0, 0 },	/* ttyS39 */
+#endif
 };
 
 #define NR_PORTS	(sizeof(rs_table)/sizeof(struct async_struct))
 
+#ifdef CONFIG_SERIAL_PCI
+
+/*
+ * currently you can have up to four PCI serial boards in your
+ * system. Increase the size of this structure to have more
+ */
+
+struct pci_struct pci_rs_chips[] = {                 
+  {0, 0,},
+  {0, 0,},
+  {0, 0,},
+  {0, 0,},
+};
+
+#define PCI_NR_BOARDS	(sizeof(pci_rs_chips)/sizeof(struct pci_struct))
+#define PCI_NR_PORTS	8
+
+#define PCI_PORT_START (NR_PORTS - PCI_NR_PORTS)
+
+#endif
+
 static struct tty_struct *serial_table[NR_PORTS];
 static struct termios *serial_termios[NR_PORTS];
 static struct termios *serial_termios_locked[NR_PORTS];
@@ -1731,6 +1806,26 @@
 
 
 /*
+ * rs_break() --- routine which turns the break handling on or off
+ * adapted from 2.1.124
+ */
+static void rs_break(struct async_struct * info, int break_state)
+{
+        unsigned long flags;
+        
+        if (!info->port)
+                return;
+        save_flags(flags);cli();
+        if (break_state == -1)
+                serial_out(info, UART_LCR,
+                           serial_inp(info, UART_LCR) | UART_LCR_SBC);
+        else
+                serial_out(info, UART_LCR,
+                           serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
+        restore_flags(flags);
+}
+
+/*
  * This routine sends a break character out the serial port.
  */
 static void send_break(	struct async_struct * info, int duration)
@@ -1922,6 +2017,20 @@
 	}
 	
 	switch (cmd) {
+                case TIOCSBRK:  /* Turn break on, unconditionally */
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			rs_break(info,-1);
+			return 0;
+                case TIOCCBRK:  /* Turn break off, unconditionally */
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			rs_break(info,0);
+			return 0;
 		case TCSBRK:	/* SVID version: non-zero arg --> no break */
 			retval = tty_check_change(tty);
 			if (retval)
@@ -2478,6 +2587,10 @@
 static void show_serial_version(void)
 {
  	printk(KERN_INFO "%s version %s with", serial_name, serial_version);
+#ifdef CONFIG_SERIAL_PCI
+  printk(" PCI");
+#define SERIAL_OPT
+#endif
 #ifdef CONFIG_HUB6
 	printk(" HUB-6");
 #define SERIAL_OPT
@@ -2712,6 +2825,73 @@
 	restore_flags(flags);
 }
 
+void display_uart_type(int type)
+{
+  switch (type) {
+	case PORT_8250:
+	  printk(" is a 8250\n");
+	  break;
+	case PORT_16450:
+	  printk(" is a 16450\n");
+	  break;
+	case PORT_16550:
+	  printk(" is a 16550\n");
+	  break;
+	case PORT_16550A:
+	  printk(" is a 16550A\n");
+	  break;
+	case PORT_16650:
+	  printk(" is a 16650\n");
+	  break;
+	default:
+	  printk("\n");
+	  break;
+  }
+}
+
+void init_port(struct async_struct *info, int num)
+{
+  info->magic = SERIAL_MAGIC;
+  info->line = num;
+  info->tty = 0;
+  info->type = PORT_UNKNOWN;
+  info->custom_divisor = 0;
+  info->close_delay = 5*HZ/10;
+  info->closing_wait = 30*HZ;
+  info->x_char = 0;
+  info->event = 0;
+  info->count = 0;
+  info->blocked_open = 0;
+  info->tqueue.routine = do_softint;
+  info->tqueue.data = info;
+  info->tqueue_hangup.routine = do_serial_hangup;
+  info->tqueue_hangup.data = info;
+  info->callout_termios =callout_driver.init_termios;
+  info->normal_termios = serial_driver.init_termios;
+  info->open_wait = 0;
+  info->close_wait = 0;
+  info->delta_msr_wait = 0;
+  info->icount.cts = info->icount.dsr = 
+	info->icount.rng = info->icount.dcd = 0;
+  info->next_port = 0;
+  info->prev_port = 0;
+  if (info->irq == 2)
+	info->irq = 9;
+  
+  if (info->type == PORT_UNKNOWN) {
+	if (!(info->flags & ASYNC_BOOT_AUTOCONF))
+	  return;
+	autoconfig(info);
+	if (info->type == PORT_UNKNOWN)
+	  return;
+  }
+  printk(KERN_INFO "ttyS%02d%s%s at 0x%04x (irq = %d)", info->line, 
+		 (info->flags & ASYNC_FOURPORT) ? " FourPort" : "",
+		 (info->flags & ASYNC_PCI)      ? " PCI" : "",
+		 info->port, info->irq);
+  display_uart_type(info->type);
+}
+
 int register_serial(struct serial_struct *req);
 void unregister_serial(int line);
 
@@ -2722,9 +2902,212 @@
 #include <linux/symtab_end.h>
 };
 
+#ifdef CONFIG_SERIAL_PCI
+
+/*
+ * Query PCI space for known serial boards
+ * If found, add them to the PCI device space in rs_table[]
+ *
+ * Accept a maximum of eight boards
+ *
+ */
+
+static void probe_serial_pci(void) 
+{
+  u16 vendor, device;
+  static int pci_index  = 0;
+  unsigned char pci_bus, pci_device_fn;
+  struct async_struct *pci_boards = &rs_table[PCI_PORT_START];
+  unsigned int port_num = 0;
+  unsigned int card_num = 0;
+
+  u32 device_ioaddr;
+  u8  device_irq;
+  
+  enum pci_spc pci_space        = pci_space_0;
+  unsigned int pci_space_offset = 0;
+
+
+#ifdef SERIAL_DEBUG_PCI
+  printk(KERN_DEBUG "Entered probe_serial_pci()\n");
+#endif
+  
+  if (! pcibios_present()) {
+#ifdef SERIAL_DEBUG_PCI
+	printk(KERN_DEBUG "Leaving probe_serial_pci() (no pcibios)\n");
+#endif
+	return;
+  }
+
+/*
+ * Start scanning the PCI bus for serial controllers ...
+ *
+ */
+
+  for (;pci_index < 0xff; pci_index++) {
+	int i = 0;
+	
+	if (pcibios_find_class(PCI_CLASS_COMMUNICATION_SERIAL << 8,
+						   pci_index,
+						   &pci_bus, 
+						   &pci_device_fn) != PCIBIOS_SUCCESSFUL)
+	  break; /* for (; pci_index ... */
+	
+	pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, &vendor);
+	pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, &device);
+	
+	for (i = 0; pci_serial_tbl[i].board_name; i++) {
+	  if (vendor == pci_serial_tbl[i].vendor_id  &&
+		  device == pci_serial_tbl[i].device_id)
+		break; /* for(i=0... */
+	}
+	
+	if (pci_serial_tbl[i].board_name == 0) {
+#ifdef SERIAL_DEBUG_PCI
+	  printk(KERN_DEBUG "Found Board (%x/%x) (not one of us)\n", vendor, device);
+#endif
+	  continue;        /* Found a serial communication controller but not one we know */
+	}
+
+/*
+ * At this point we found a serial board which we know
+ */
+
+	if(card_num >= PCI_NR_BOARDS) {
+	  printk(KERN_ERR "Already %d boards configured, skipping\n", PCI_NR_BOARDS);
+	  continue; /* for (;pci_index < 0xff */
+	}
+
+	pcibios_read_config_byte(pci_bus, pci_device_fn, 
+							 PCI_INTERRUPT_LINE, &device_irq);
+	pcibios_read_config_dword(pci_bus, pci_device_fn, 
+							  PCI_BASE_ADDRESS_1, &device_ioaddr);
+
+#ifdef SERIAL_DEBUG_PCI
+		printk(KERN_DEBUG "Device %s at #%x found\n", pci_serial_tbl[i].board_name, device_ioaddr);
+#endif
+
+	if (check_region(device_ioaddr, pci_serial_tbl[i].io_size)) {
+	  printk(KERN_ERR "Could not reserve %d bytes of I/O Space at %x\n", pci_serial_tbl[i].io_size, device_ioaddr);
+	  continue; /* for (;pci_index < 0xff */
+	}
+
+/*
+ * Every PCI device brings 128 bytes (at least) of IO-Space with it
+ * reserve a region for it. It is not exactly necessary as PCI will
+ * ensure that no other device will be mapped onto this space (LOL)
+ * but we do it nevertheless so it will show up nicely on
+ * /proc/ioports -- hps
+ */
+	
+	  if((device_ioaddr & 1) == 0) {
+#ifdef SERIAL_DEBUG_PCI
+		device_ioaddr &= ~0x7f;
+		printk(KERN_DEBUG "%s has its config registers memory-mapped at #%x (ignoring)\n", 
+			   pci_serial_tbl[i].board_name, device_ioaddr);
+#endif
+		continue; /* for (;pci_index < 0xff */
+	  }
+
+	device_ioaddr &= ~0x7f;         /* Mask out the flag bits 
+									 * from this register. At least on the PLX9050
+									 * they're always 0 but this is here nevertheless
+									 * for sanity's sake 
+									 */
+
+	request_region(device_ioaddr, pci_serial_tbl[i].io_size, "serial (PCI Controller)");
+		
+	pci_rs_chips[card_num].start = device_ioaddr;
+	pci_rs_chips[card_num].type  = &pci_serial_tbl[i];
+		
+
+/*
+ * Every PCI device can bring up to four PCI memory or IO spaces (at
+ * least according to the documentation I have. So we will now check
+ * with our config whether this device has one of these spaces and we
+ * should configure UARTs inside -- hps
+ */
+
+	for(; pci_space <= pci_space_3; pci_space <<= 1, pci_space_offset+= 4) {
+	  u32 uart_chip_base;
+	  u32 uart_chip_count;
+	  
+	  if((pci_serial_tbl[i].pci_space & pci_space) == 0)
+		continue; /* for(;pci_space... */
+	  
+	  pcibios_read_config_dword(pci_bus, pci_device_fn, 
+								PCI_BASE_ADDRESS_2+pci_space_offset, &uart_chip_base);
+	  
+	  if((uart_chip_base & 1) == 0) {
+#ifdef SERIAL_DEBUG_PCI
+		chip_base &= ~0x0f;
+		printk(KERN_DEBUG "%s has a memory-mapped IO Chip at #%x (ignoring)\n", 
+			   pci_serial_tbl[i].board_name, chip_base);
+#endif
+		continue; /* for(;pci_space... */
+	  }
+
+	  uart_chip_base &= ~0x0f;
+
+/*
+ * uart_chip_base now points to the IO-Space. 
+ *
+ * Alvin Sim <alvin@alloycp.com.au> told me the following thing:
+ *
+ * UARTS can be "setserial"d by kernel 2.0.35, but ports needed to be
+ * manually specified. 4 ports start at 0x6100, in increments of 8
+ * addresses.
+ * 
+ * so there is at least one board out there which can do more than one
+ * UART in a single PCI config space. My trustworthy SPCom 200 PCI has
+ * just one UART in one config space. So I added a check for more than
+ * one chip in a config space -- hps
+ *
+ */
+
+	  for(uart_chip_count=0;uart_chip_count < pci_serial_tbl[i].dev_per_space; uart_chip_count++) {
+#ifdef SERIAL_DEBUG_PCI
+		printk(KERN_DEBUG "%s has an IO Chip at #%x\n", 
+			   pci_serial_tbl[i].board_name, uart_chip_base);
+#endif
+		
+		if(port_num >= PCI_NR_PORTS) {
+		  printk(KERN_ERR "Already %d ports configured, skipping\n", PCI_NR_PORTS);
+		  break; /* for(;uart_chip_count... */
+		}
+		
+		if (check_region(uart_chip_base, 8)) {
+		  printk(KERN_ERR "Could not reserve %d bytes of I/O Space at %x\n", 8, uart_chip_base);
+		  break; /* for(;uart_chip_count... */
+		}
+		  
+		request_region(uart_chip_base, 8, "serial (PCI)");
+		pci_boards[port_num].port  = uart_chip_base;
+		pci_boards[port_num].irq   = device_irq;
+		pci_boards[port_num].flags = PCI_FLAGS;
+		pci_boards[port_num].baud_base = pci_serial_tbl[i].baud_base;
+		
+		port_num++;
+		uart_chip_base += pci_serial_tbl[i].dev_spacing;
+
+	  }  /* for(uart_chip_count... */
+	} /* for(pci_space ... */
+	
+	card_num++;
+  }  /* for */
+
+#ifdef SERIAL_DEBUG_PCI
+  printk(KERN_DEBUG "Leaving probe_serial_pci() (probe finished)\n");
+#endif
+  return;
+}
+
+#endif /* CONFIG_SERIAL_PCI */
+
 /*
  * The serial driver boot-time initialization code!
  */
+
 int rs_init(void)
 {
 	int i;
@@ -2744,6 +3127,9 @@
 	}
 	
 	show_serial_version();
+#ifdef CONFIG_SERIAL_PCI
+		probe_serial_pci();
+#endif	
 
 	/* Initialize the tty_driver structure */
 	
@@ -2795,67 +3181,15 @@
 		panic("Couldn't register callout driver\n");
 	
 	for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
-		info->magic = SERIAL_MAGIC;
-		info->line = i;
-		info->tty = 0;
-		info->type = PORT_UNKNOWN;
-		info->custom_divisor = 0;
-		info->close_delay = 5*HZ/10;
-		info->closing_wait = 30*HZ;
-		info->x_char = 0;
-		info->event = 0;
-		info->count = 0;
-		info->blocked_open = 0;
-		info->tqueue.routine = do_softint;
-		info->tqueue.data = info;
-		info->tqueue_hangup.routine = do_serial_hangup;
-		info->tqueue_hangup.data = info;
-		info->callout_termios =callout_driver.init_termios;
-		info->normal_termios = serial_driver.init_termios;
-		info->open_wait = 0;
-		info->close_wait = 0;
-		info->delta_msr_wait = 0;
-		info->icount.cts = info->icount.dsr = 
-			info->icount.rng = info->icount.dcd = 0;
-		info->next_port = 0;
-		info->prev_port = 0;
-		if (info->irq == 2)
-			info->irq = 9;
-		if (info->type == PORT_UNKNOWN) {
-			if (!(info->flags & ASYNC_BOOT_AUTOCONF))
-				continue;
-			autoconfig(info);
-			if (info->type == PORT_UNKNOWN)
-				continue;
-		}
-		printk(KERN_INFO "tty%02d%s at 0x%04x (irq = %d)", info->line, 
-		       (info->flags & ASYNC_FOURPORT) ? " FourPort" : "",
-		       info->port, info->irq);
-		switch (info->type) {
-			case PORT_8250:
-				printk(" is a 8250\n");
-				break;
-			case PORT_16450:
-				printk(" is a 16450\n");
-				break;
-			case PORT_16550:
-				printk(" is a 16550\n");
-				break;
-			case PORT_16550A:
-				printk(" is a 16550A\n");
-				break;
-			case PORT_16650:
-				printk(" is a 16650\n");
-				break;
-			default:
-				printk("\n");
-				break;
-		}
-	}
+				init_port(info, i);
+		};
+
 	register_symtab(&serial_syms);
 	return 0;
 }
 
+
+
 /*
  * register_serial and unregister_serial allows for serial ports to be
  * configured at run-time, to support PCMCIA modems.
@@ -2898,20 +3232,9 @@
 		printk("register_serial(): autoconfig failed\n");
 		return -1;
 	}
-	printk(KERN_INFO "tty%02d at 0x%04x (irq = %d)", info->line, 
+		printk(KERN_INFO "ttyS%02d at 0x%04x (irq = %d)", info->line, 
 	       info->port, info->irq);
-	switch (info->type) {
-	case PORT_8250:
-		printk(" is a 8250\n"); break;
-	case PORT_16450:
-		printk(" is a 16450\n"); break;
-	case PORT_16550:
-		printk(" is a 16550\n"); break;
-	case PORT_16550A:
-		printk(" is a 16550A\n"); break;
-	default:
-		printk("\n"); break;
-	}
+		display_uart_type(info->type);
 	restore_flags(flags);
 	return info->line;
 }
@@ -2926,7 +3249,7 @@
 	if (info->tty)
 		tty_hangup(info->tty);
 	info->type = PORT_UNKNOWN;
-	printk(KERN_INFO "tty%02d unloaded\n", info->line);
+		printk(KERN_INFO "ttyS%02d unloaded\n", info->line);
 	restore_flags(flags);
 }
 
@@ -2960,6 +3283,18 @@
 		if (rs_table[i].type != PORT_UNKNOWN)
 			release_region(rs_table[i].port, 8);
 	}
+
+#ifdef CONFIG_SERIAL_PCI
+		for (i = 0; i < PCI_NR_BOARDS; i++) {
+		  if (pci_rs_chips[i].start != 0x0) {
+#ifdef SERIAL_DEBUG_PCI
+			printk(KERN_DEBUG "Releasing %d Bytes at #%x\n", pci_rs_chips[i].type->io_size, pci_rs_chips[i].start);
+#endif
+			release_region(pci_rs_chips[i].start, pci_rs_chips[i].type->io_size);
+		  }
+		}
+#endif
+
 	if (tmp_buf) {
 		free_page((unsigned long) tmp_buf);
 		tmp_buf = NULL;
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- linux.vanilla/drivers/char/tty_io.c	Sun Dec  6 00:14:37 1998
+++ linux/drivers/char/tty_io.c	Sun Dec  6 03:37:07 1998
@@ -964,7 +964,13 @@
 	 * Failures after this point use release_mem to clean up, so 
 	 * there's no need to null out the local pointers.
 	 */
-	driver->table[idx] = tty;
+	driver->table[idx] = tty;	/* FIXME: this is broken and
+	probably causes ^D bug. tty->private_date does not (yet) point
+	to a console, if keypress comes now, await armagedon. 
+
+	also, driver->table is accessed from interrupt for vt case,
+	and this does not look like atomic access at all. */
+	
 	if (!*tp_loc)
 		*tp_loc = tp;
 	if (!*ltp_loc)
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/isdn/isdn_net.c linux/drivers/isdn/isdn_net.c
--- linux.vanilla/drivers/isdn/isdn_net.c	Sun Dec  6 00:14:39 1998
+++ linux/drivers/isdn/isdn_net.c	Tue Mar  2 00:40:09 1999
@@ -1,4 +1,4 @@
-/* $Id: isdn_net.c,v 1.48.2.27 1998/11/05 22:11:53 fritz Exp $
+/* $Id: isdn_net.c,v 1.48.2.28 1998/11/27 15:38:12 paul Exp $
 
  * Linux ISDN subsystem, network interfaces and related functions (linklevel).
  *
@@ -21,6 +21,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_net.c,v $
+ * Revision 1.48.2.28  1998/11/27 15:38:12  paul
+ * Also huptimeout with dialmode == manual
+ *
  * Revision 1.48.2.27  1998/11/05 22:11:53  fritz
  * Changed mail-address.
  *
@@ -350,11 +353,11 @@
 static int isdn_net_xmit(struct device *, isdn_net_local *, struct sk_buff *);
 static void dev_purge_queues(struct device *dev);	/* move this to net/core/dev.c */
 
-char *isdn_net_revision = "$Revision: 1.48.2.27 $";
+char *isdn_net_revision = "$Revision: 1.48.2.28 $";
 
- /*
-  * Code for raw-networking over ISDN
-  */
+/*
+ * Code for raw-networking over ISDN
+ */
 
 static void
 isdn_net_unreachable(struct device *dev, struct sk_buff *skb, char *reason)
@@ -362,7 +365,6 @@
 	int i;
 
 	if(skb != NULL) {
-	
 		u_short proto = ntohs(skb->protocol);
 
 		printk(KERN_DEBUG "isdn_net: %s: %s, send ICMP %s\n",
@@ -538,13 +540,14 @@
 		if ((l->flags & ISDN_NET_CONNECTED) && (!l->dialstate)) {
 			anymore = 1;
 			l->huptimer++;
- 			/*
- 			 * only do timeout-hangup
- 			 * if interface is configured as AUTO
- 			 */
-  			if ((ISDN_NET_DIALMODE(*l) == ISDN_NET_DM_AUTO) &&
- 			    (l->onhtime) &&
- 			    (l->huptimer > l->onhtime))
+			/*
+			 * if there is some dialmode where timeout-hangup
+			 * should _not_ be done, check for that here and
+			 * 35 lines below (ifdef CONFIG_ISDN_BUDGET)
+			 * eg: (ISDN_NET_DIALMODE(*l) != ISDN_NET_DM_NOTIMEOUT)
+			 */
+			if ((l->onhtime) &&
+			    (l->huptimer > l->onhtime))
 				if (l->hupflags & ISDN_MANCHARGE &&
 				    l->hupflags & ISDN_CHARGEHUP) {
 					while (jiffies - l->chargetime > l->chargeint)
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/isdn/isdn_ppp.c linux/drivers/isdn/isdn_ppp.c
--- linux.vanilla/drivers/isdn/isdn_ppp.c	Sun Dec  6 00:14:39 1998
+++ linux/drivers/isdn/isdn_ppp.c	Tue Mar  2 00:40:09 1999
@@ -1,4 +1,4 @@
-/* $Id: isdn_ppp.c,v 1.28.2.2 1998/11/03 14:31:23 fritz Exp $
+/* $Id: isdn_ppp.c,v 1.28.2.3 1998/12/30 17:49:00 paul Exp $
  *
  * Linux ISDN subsystem, functions for synchronous PPP (linklevel).
  *
@@ -19,6 +19,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_ppp.c,v $
+ * Revision 1.28.2.3  1998/12/30 17:49:00  paul
+ * fixed syncPPP callback out
+ *
  * Revision 1.28.2.2  1998/11/03 14:31:23  fritz
  * Reduced stack usage in various functions.
  * Adapted statemachine to work with certified HiSax.
@@ -181,7 +184,7 @@
 static void isdn_ppp_free_mpqueue(isdn_net_dev *);
 #endif
 
-char *isdn_ppp_revision = "$Revision: 1.28.2.2 $";
+char *isdn_ppp_revision = "$Revision: 1.28.2.3 $";
 
 static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
 static struct isdn_ppp_compressor *ipc_head = NULL;
@@ -303,7 +306,8 @@
 		}
 	} else {
 		for (i = 0; i < ISDN_MAX_CHANNELS; i++)
-			if (ippp_table[i]->minor == lp->pppbind && ippp_table[i]->state == IPPP_OPEN)
+			if (ippp_table[i]->minor == lp->pppbind &&
+			    (ippp_table[i]->state & IPPP_OPEN) == IPPP_OPEN)
 				break;
 	}
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/3c59x.c linux/drivers/net/3c59x.c
--- linux.vanilla/drivers/net/3c59x.c	Sun Dec  6 00:14:39 1998
+++ linux/drivers/net/3c59x.c	Sun Dec  6 19:28:57 1998
@@ -15,18 +15,19 @@
 */
 
 static char *version =
-"3c59x.c:v0.99E 5/12/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n";
+"3c59x.c:v0.99H 11/17/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n";
 
 /* "Knobs" that adjust features and parameters. */
 /* Set the copy breakpoint for the copy-only-tiny-frames scheme.
    Setting to > 1512 effectively disables this feature. */
-static const rx_copybreak = 200;
+static const int rx_copybreak = 200;
 /* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */
-static const mtu = 1500;
+static const int mtu = 1500;
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
 static int max_interrupt_work = 20;
 
 /* Put out somewhat more debugging messages. (0: no msg, 1 minimal .. 6). */
+#define vortex_debug debug
 #ifdef VORTEX_DEBUG
 static int vortex_debug = VORTEX_DEBUG;
 #else
@@ -37,15 +38,6 @@
    debugging. */
 static int rx_nocopy = 0, rx_copy = 0, queued_packet = 0, rx_csumhits;
 
-/* Enable the automatic media selection code -- usually set. */
-#define AUTOMEDIA 1
-
-/* Allow the use of fragment bus master transfers instead of only
-   programmed-I/O for Vortex cards.  Full-bus-master transfers are always
-   enabled by default on Boomerang cards.  If VORTEX_BUS_MASTER is defined,
-   the feature may be turned on using 'options'. */
-#define VORTEX_BUS_MASTER
-
 /* A few values that may be tweaked. */
 /* Time in jiffies before concluding the transmitter is hung. */
 #define TX_TIMEOUT  ((400*HZ)/1000)
@@ -56,12 +48,12 @@
 #define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
 
 #include <linux/config.h>
+#include <linux/version.h>
 #ifdef MODULE
 #ifdef MODVERSIONS
 #include <linux/modversions.h>
 #endif
 #include <linux/module.h>
-#include <linux/version.h>
 #else
 #define MOD_INC_USE_COUNT
 #define MOD_DEC_USE_COUNT
@@ -70,68 +62,61 @@
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/string.h>
-#include <linux/ptrace.h>
+#include <linux/timer.h>
 #include <linux/errno.h>
 #include <linux/in.h>
 #include <linux/ioport.h>
 #include <linux/malloc.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#if LINUX_VERSION_CODE < 0x20155  ||  defined(CARDBUS)
 #include <linux/bios32.h>
-#include <linux/timer.h>
+#endif
 #include <asm/irq.h>			/* For NR_IRQS only. */
 #include <asm/bitops.h>
 #include <asm/io.h>
 
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-
-/* Kernel compatibility defines, common to David Hind's PCMCIA package.
+/* Kernel compatibility defines, some common to David Hinds' PCMCIA package.
    This is only in the support-all-kernels source code. */
-#ifndef LINUX_VERSION_CODE
-#include <linux/version.h>		/* Redundant above, here for easy clean-up. */
-#endif
-#if LINUX_VERSION_CODE < 0x10300
-#define RUN_AT(x) (x)			/* What to put in timer->expires.  */
-#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC)
-#if defined(__alpha)
-#error "The Alpha architecture is only support with kernel version 2.0."
-#endif
-#define virt_to_bus(addr)  ((unsigned long)addr)
-#define bus_to_virt(addr) ((void*)addr)
-#define NR_IRQS 16
-#else  /* 1.3.0 and later */
-#define RUN_AT(x) (jiffies + (x))
-#define DEV_ALLOC_SKB(len) dev_alloc_skb(len)
-#endif
-#if LINUX_VERSION_CODE < 0x20159
-#define DEV_FREE_SKB(skb) dev_kfree_skb (skb, FREE_WRITE);
-#else  /* Grrr, unneeded incompatible change. */
-#define DEV_FREE_SKB(skb) dev_kfree_skb(skb);
-#endif
 
-#ifdef SA_SHIRQ
-#define FREE_IRQ(irqnum, dev) free_irq(irqnum, dev)
-#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n, instance)
-#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs)
-#else
-#define FREE_IRQ(irqnum, dev) free_irq(irqnum)
-#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n)
-#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs)
-#endif
+#define RUN_AT(x) (jiffies + (x))
 
-#if (LINUX_VERSION_CODE >= 0x10344)
-#define NEW_MULTICAST
 #include <linux/delay.h>
+
+#if (LINUX_VERSION_CODE >= 0x20100)
+char kernel_version[] = UTS_RELEASE;
 #else
-#define udelay(microsec)	do { int _i = 4*microsec; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
+#ifndef __alpha__
+#define ioremap(a,b) \
+	(((a)<0x100000) ? (void *)((u_long)(a)) : vremap(a,b))
+#define iounmap(v) \
+	do { if ((u_long)(v) > 0x100000) vfree(v); } while (0)
+#endif
+#endif
+#if LINUX_VERSION_CODE <= 0x20139
+#define	net_device_stats enet_statistics
+#define NETSTATS_VER2
 #endif
-
 #if LINUX_VERSION_CODE < 0x20138
 #define test_and_set_bit(val, addr) set_bit(val, addr)
+#define le32_to_cpu(val) (val)
+#define cpu_to_le32(val) (val)
+#endif
+#if LINUX_VERSION_CODE < 0x20155
+#define PCI_SUPPORT_VER1
+#else
+#define PCI_SUPPORT_VER2
+#endif
+#if LINUX_VERSION_CODE < 0x20159
+#define DEV_FREE_SKB(skb) dev_kfree_skb (skb, FREE_WRITE);
+#else  /* Grrr, unneeded incompatible change. */
+#define DEV_FREE_SKB(skb) dev_kfree_skb(skb);
 #endif
-#if defined(MODULE) && (LINUX_VERSION_CODE >= 0x20115)
+
+#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115
 MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
 MODULE_DESCRIPTION("3Com 3c590/3c900 series Vortex/Boomerang driver");
 MODULE_PARM(debug, "i");
@@ -141,7 +126,7 @@
 MODULE_PARM(max_interrupt_work, "i");
 MODULE_PARM(compaq_ioaddr, "i");
 MODULE_PARM(compaq_irq, "i");
-MODULE_PARM(compaq_prod_id, "i");
+MODULE_PARM(compaq_device_id, "i");
 #endif
 
 /* Operational parameter that usually are not changed. */
@@ -153,35 +138,11 @@
 #define VORTEX_TOTAL_SIZE 0x20
 #define BOOMERANG_TOTAL_SIZE 0x40
 
-#ifdef HAVE_DEVLIST
-struct netdev_entry tc59x_drv =
-{"Vortex", vortex_pci_probe, VORTEX_TOTAL_SIZE, NULL};
-#endif
-
 /* Set iff a MII transceiver on any interface requires mdio preamble.
    This only set with the original DP83840 on older 3c905 boards, so the extra
    code size of a per-interface flag is not worthwhile. */
 static char mii_preamble_required = 0;
 
-/* Caution!  These entries must be consistent. */
-static const int product_ids[] = {
-	0x5900, 0x5920, 0x5970, 0x5950, 0x5951, 0x5952, 0x9000, 0x9001,
-	0x9050, 0x9051, 0x9055,	0x5057, 0 };
-static const char *product_names[] = {
-	"3c590 Vortex 10Mbps",
-	"3c592 EISA 10mbps Demon/Vortex",
-	"3c597 EISA Fast Demon/Vortex",
-	"3c595 Vortex 100baseTX",
-	"3c595 Vortex 100baseT4",
-	"3c595 Vortex 100base-MII",
-	"3c900 Boomerang 10baseT",
-	"3c900 Boomerang 10Mbps/Combo",
-	"3c905 Boomerang 100baseTx",
-	"3c905 Boomerang 100baseT4",
-	"3c905B Cyclone 100baseTx",
-	"3c575",						/* Cardbus Boomerang */
-};
-
 /*
 				Theory of Operation
 
@@ -192,17 +153,19 @@
 versions of the FastEtherLink cards.  The supported product IDs are
   3c590, 3c592, 3c595, 3c597, 3c900, 3c905
 
-The ISA 3c515 is supported with a separate driver, 3c515.c, included with
-the kernel source or available from
+The related ISA 3c515 is supported with a separate driver, 3c515.c, included
+with the kernel source or available from
     cesdis.gsfc.nasa.gov:/pub/linux/drivers/3c515.html
 
 II. Board-specific settings
 
 PCI bus devices are configured by the system at boot time, so no jumpers
 need to be set on the board.  The system BIOS should be set to assign the
-PCI INTA signal to an otherwise unused system IRQ line.  While it's
-physically possible to shared PCI interrupt lines, the 1.2.0 kernel doesn't
-support it.
+PCI INTA signal to an otherwise unused system IRQ line.
+
+The EEPROM settings for media type and forced-full-duplex are observed.
+The EEPROM media type should be left at the default "autoselect" unless using
+10base2 or AUI connections which cannot be reliably detected.
 
 III. Driver operation
 
@@ -213,7 +176,7 @@
 The 3c900 "Boomerang" series uses a full-bus-master interface with separate
 lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet,
 DEC Tulip and Intel Speedo3.  The first chip version retains a compatible
-programmed-I/O interface that will be removed in the 'B' and subsequent
+programmed-I/O interface that has been removed in 'B' and subsequent board
 revisions.
 
 One extension that is advertised in a very large font is that the adapters
@@ -231,7 +194,7 @@
 single frame.
 
 With full-bus-master support, this driver uses a "RX_COPYBREAK" scheme.
-Tather than a fixed intermediate receive buffer, this scheme allocates
+Rather than a fixed intermediate receive buffer, this scheme allocates
 full-sized skbuffs as receive buffers.  The value RX_COPYBREAK is used as
 the copying breakpoint: it is chosen to trade-off the memory wasted by
 passing the full-sized skbuff to the queue layer for all frames vs. the
@@ -257,7 +220,66 @@
 limit of 4K.
 */
 
-#define TCOM_VENDOR_ID	0x10B7		/* 3Com's manufacturer's ID. */
+/* This table drives the PCI probe routines.  It's mostly boilerplate in all
+   of the drivers, and will likely be provided by some future kernel.
+*/
+enum pci_flags_bit {
+	PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
+	PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3,
+};
+struct pci_id_info {
+	const char *name;
+	u16	vendor_id, device_id, device_id_mask, flags;
+	int drv_flags, io_size;
+	struct device *(*probe1)(int pci_bus, int pci_devfn, struct device *dev,
+							 long ioaddr, int irq, int chip_idx, int fnd_cnt);
+};
+
+enum { IS_VORTEX=1, IS_BOOMERANG=2, IS_CYCLONE=4,
+	   HAS_PWR_CTRL=0x10, HAS_MII=0x20, HAS_NWAY=0x40, HAS_CB_FNS=0x80, };
+static struct device *vortex_probe1(int pci_bus, int pci_devfn,
+									struct device *dev, long ioaddr,
+									int irq, int dev_id, int card_idx);
+static struct pci_id_info pci_tbl[] = {
+	{"3c590 Vortex 10Mbps",			0x10B7, 0x5900, 0xffff,
+	 PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, vortex_probe1},
+	{"3c595 Vortex 100baseTx",		0x10B7, 0x5950, 0xffff,
+	 PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, vortex_probe1},
+	{"3c595 Vortex 100baseT4",		0x10B7, 0x5951, 0xffff,
+	 PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, vortex_probe1},
+	{"3c595 Vortex 100base-MII",	0x10B7, 0x5952, 0xffff,
+	 PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, vortex_probe1},
+	{"3Com Vortex",					0x10B7, 0x5900, 0xff00,
+	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, vortex_probe1},
+	{"3c900 Boomerang 10baseT",		0x10B7, 0x9000, 0xffff,
+	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, vortex_probe1},
+	{"3c900 Boomerang 10Mbps Combo", 0x10B7, 0x9001, 0xffff,
+	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, vortex_probe1},
+	{"3c900 Cyclone 10Mbps Combo", 0x10B7, 0x9005, 0xffff,
+	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1},
+	{"3c900B-FL Cyclone 10base-FL",	0x10B7, 0x900A, 0xffff,
+	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1},
+	{"3c905 Boomerang 100baseTx",	0x10B7, 0x9050, 0xffff,
+	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, vortex_probe1},
+	{"3c905 Boomerang 100baseT4",	0x10B7, 0x9051, 0xffff,
+	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, vortex_probe1},
+	{"3c905B Cyclone 100baseTx",	0x10B7, 0x9055, 0xffff,
+	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY, 128, vortex_probe1},
+	{"3c905B-FX Cyclone 100baseFx",	0x10B7, 0x905A, 0xffff,
+	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1},
+	{"3c980 Cyclone",	0x10B7, 0x9800, 0xfff0,
+	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1},
+	{"3c575 Boomerang CardBus",		0x10B7, 0x5057, 0xffff,
+	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, vortex_probe1},
+	{"3CCFE575 Cyclone CardBus",	0x10B7, 0x5157, 0xffff,
+	 PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS,
+	 128, vortex_probe1},
+	{"3c575 series CardBus (unknown version)", 0x10B7, 0x5057, 0xf0ff,
+	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, vortex_probe1},
+	{"3Com Boomerang (unknown version)",	0x10B7, 0x9000, 0xff00,
+	 PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, vortex_probe1},
+	{0,},						/* 0 terminated list. */
+};
 
 /* Operational definitions.
    These are not used by other compilation units and thus are not
@@ -286,7 +308,7 @@
 	SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
 	SetTxThreshold = 18<<11, SetTxStart = 19<<11,
 	StartDMAUp = 20<<11, StartDMADown = (20<<11)+1, StatsEnable = 21<<11,
-	StatsDisable = 22<<11, StopCoax = 23<<11,};
+	StatsDisable = 22<<11, StopCoax = 23<<11, SetFilterBit = 25<<11,};
 
 /* The SetRxFilter command accepts the following classes: */
 enum RxFilter {
@@ -326,6 +348,9 @@
 	NodeAddr01=10, NodeAddr23=11, NodeAddr45=12,
 	DriverTune=13, Checksum=15};
 
+enum Window2 {			/* Window 2. */
+	Wn2_ResetOptions=12,
+};
 enum Window3 {			/* Window 3: MAC/config bits. */
 	Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8,
 };
@@ -393,37 +418,37 @@
 enum ChipCaps { CapBusMaster=0x20 };
 
 struct vortex_private {
-	char devname[8];			/* "ethN" string, also for kernel debug. */
-	const char *product_name;
-	struct device *next_module;
-	/* The Rx and Tx rings are here to keep them quad-word-aligned. */
+	/* The Rx and Tx rings should be quad-word-aligned. */
 	struct boom_rx_desc rx_ring[RX_RING_SIZE];
 	struct boom_tx_desc tx_ring[TX_RING_SIZE];
 	/* The addresses of transmit- and receive-in-place skbuffs. */
 	struct sk_buff* rx_skbuff[RX_RING_SIZE];
 	struct sk_buff* tx_skbuff[TX_RING_SIZE];
+	struct device *next_module;
+	void *priv_addr;
 	unsigned int cur_rx, cur_tx;		/* The next free ring entry */
 	unsigned int dirty_rx, dirty_tx;	/* The ring entries to be free()ed. */
-	struct enet_statistics stats;
+	struct net_device_stats stats;
 	struct sk_buff *tx_skb;		/* Packet being eaten by bus master ctrl.  */
 
 	/* PCI configuration space information. */
-	u8 pci_bus, pci_dev_fn;		/* PCI bus location, for power management. */
-	u16 pci_device_id;
+	u8 pci_bus, pci_devfn;		/* PCI bus location, for power management. */
+	char *cb_fn_base;			/* CardBus function status addr space. */
+	int chip_id;
 
 	/* The remainder are related to chip state, mostly media selection. */
-	int in_interrupt;
+	unsigned long in_interrupt;
 	struct timer_list timer;	/* Media selection timer. */
 	int options;				/* User-settable misc. driver options. */
-	unsigned int
-	  media_override:3, 			/* Passed-in media type. */
-	  default_media:3,				/* Read from the EEPROM/Wn3_Config. */
-	  full_duplex:1, autoselect:1,
-	  bus_master:1,				/* Vortex can only do a fragment bus-m. */
-	  full_bus_master_tx:1, full_bus_master_rx:2, /* Boomerang  */
-	  hw_csums:1,				/* Has hardware checksums. */
-      tx_full:1;
+	unsigned int media_override:3, 			/* Passed-in media type. */
+		default_media:4,				/* Read from the EEPROM/Wn3_Config. */
+		full_duplex:1, force_fd:1, autoselect:1,
+		bus_master:1,				/* Vortex can only do a fragment bus-m. */
+		full_bus_master_tx:1, full_bus_master_rx:2, /* Boomerang  */
+		hw_csums:1,				/* Has hardware checksums. */
+		tx_full:1;
 	u16 status_enable;
+	u16 intr_enable;
 	u16 available_media;				/* From Wn3_Options. */
 	u16 capabilities, info1, info2;		/* Various, from EEPROM. */
 	u16 advertising;					/* NWay media advertisement */
@@ -458,61 +483,39 @@
   { "Default",	 0,			0xFF, XCVR_10baseT, 10000},
 };
 
-static int vortex_scan(struct device *dev);
-static struct device *vortex_found_device(struct device *dev, int ioaddr,
-										  int irq, int device_id,
-										  int options, int card_idx);
-static int vortex_probe1(struct device *dev);
-static int vortex_open(struct device *dev);
-static void mdio_sync(int ioaddr, int bits);
-static int mdio_read(int ioaddr, int phy_id, int location);
-#ifdef HAVE_PRIVATE_IOCTL
-static void mdio_write(int ioaddr, int phy_id, int location, int value);
+#ifndef CARDBUS
+static int vortex_scan(struct device *dev, struct pci_id_info pci_tbl[]);
 #endif
+static int vortex_open(struct device *dev);
+static void mdio_sync(long ioaddr, int bits);
+static int mdio_read(long ioaddr, int phy_id, int location);
+static void mdio_write(long ioaddr, int phy_id, int location, int value);
 static void vortex_timer(unsigned long arg);
 static int vortex_start_xmit(struct sk_buff *skb, struct device *dev);
 static int boomerang_start_xmit(struct sk_buff *skb, struct device *dev);
 static int vortex_rx(struct device *dev);
 static int boomerang_rx(struct device *dev);
-static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs);
+static void vortex_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 static int vortex_close(struct device *dev);
-static void update_stats(int addr, struct device *dev);
-static struct enet_statistics *vortex_get_stats(struct device *dev);
+static void update_stats(long ioaddr, struct device *dev);
+static struct net_device_stats *vortex_get_stats(struct device *dev);
 static void set_rx_mode(struct device *dev);
-#ifdef HAVE_PRIVATE_IOCTL
 static int vortex_ioctl(struct device *dev, struct ifreq *rq, int cmd);
-#endif
-#ifndef NEW_MULTICAST
-static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
-#endif
 
 
-/* Unlike the other PCI cards the 59x cards don't need a large contiguous
-   memory region, so making the driver a loadable module is feasible.
-
-   Unfortunately maximizing the shared code between the integrated and
-   module version of the driver results in a complicated set of initialization
-   procedures.
-   init_module() -- modules /  tc59x_init()  -- built-in
-		The wrappers for vortex_scan()
-   vortex_scan()  		 The common routine that scans for PCI and EISA cards
-   vortex_found_device() Allocate a device structure when we find a card.
-					Different versions exist for modules and built-in.
-   vortex_probe1()		Fill in the device structure -- this is separated
-					so that the modules code can put it in dev->init.
-*/
 /* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
-/* Note: this is the only limit on the number of cards supported!! */
-static int options[8] = { -1, -1, -1, -1, -1, -1, -1, -1,};
-static int full_duplex[8] = {-1, -1, -1, -1, -1, -1, -1, -1};
+/* Option count limit only -- unlimited interfaces are supported. */
+#define MAX_UNITS 8
+static int options[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1,};
+static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
 /* A list of all installed Vortex devices, for removing the driver module. */
 static struct device *root_vortex_dev = NULL;
 
 #ifdef MODULE
+#ifndef CARDBUS
 /* Variables to work-around the Compaq PCI BIOS32 problem. */
 static int compaq_ioaddr = 0, compaq_irq = 0, compaq_device_id = 0x5900;
-
-static int debug = -1;
+#endif
 
 #ifdef CARDBUS
 
@@ -520,19 +523,38 @@
 
 static dev_node_t *vortex_attach(dev_locator_t *loc)
 {
-	u16 dev_id;
+	u16 dev_id, vendor_id;
 	u32 io;
 	u8 bus, devfn, irq;
 	struct device *dev;
+	int chip_idx;
 
 	if (loc->bus != LOC_PCI) return NULL;
 	bus = loc->b.pci.bus; devfn = loc->b.pci.devfn;
-	printk(KERN_INFO "vortex_attach(bus %d, function %d)\n", bus, devfn);
 	pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &io);
 	pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq);
+	pcibios_read_config_word(bus, devfn, PCI_VENDOR_ID, &vendor_id);
 	pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &dev_id);
+	printk(KERN_INFO "vortex_attach(bus %d, function %d, device %4.4x)\n",
+		   bus, devfn, dev_id);
 	io &= ~3;
-	dev = vortex_found_device(NULL, io, irq, dev_id, 0, -1);
+	if (io == 0 || irq == 0) {
+		printk(KERN_ERR "The 3Com CardBus Ethernet interface was not "
+			   "assigned an %s.\n" KERN_ERR "  It will not be activated.\n",
+			   io == 0 ? "I/O address" : "IRQ");
+		return NULL;
+	}
+	for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++)
+		if (vendor_id == pci_tbl[chip_idx].vendor_id
+			&& (dev_id & pci_tbl[chip_idx].device_id_mask) ==
+			pci_tbl[chip_idx].device_id)
+			break;
+	if (pci_tbl[chip_idx].vendor_id == 0) { 		/* Compiled out! */
+		printk(KERN_INFO "Unable to match chip type %4.4x %4.4x in "
+			   "vortex_attach().\n", vendor_id, dev_id);
+		return NULL;
+	}
+	dev = vortex_probe1(bus, devfn, NULL, io, irq, chip_idx, MAX_UNITS+1);
 	if (dev) {
 		dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL);
 		strcpy(node->dev_name, dev->name);
@@ -554,61 +576,49 @@
 	}
 	if (*devp) {
 		struct device *dev = *devp;
+		struct vortex_private *vp = dev->priv;
 		if (dev->flags & IFF_UP)
 			vortex_close(dev);
 		dev->flags &= ~(IFF_UP|IFF_RUNNING);
 		unregister_netdev(dev);
+		if (vp->cb_fn_base) iounmap(vp->cb_fn_base);
 		kfree(dev);
 		*devp = *next;
+		kfree(vp);
 		kfree(node);
 		MOD_DEC_USE_COUNT;
 	}
 }
 
 struct driver_operations vortex_ops = {
-	"3c59x_cb", vortex_attach, NULL, NULL, vortex_detach
+	"3c575_cb", vortex_attach, NULL, NULL, vortex_detach
 };
 
 #endif  /* Cardbus support */
 
 
-int
-init_module(void)
+int init_module(void)
 {
-	if (debug >= 0)
-		vortex_debug = debug;
 	if (vortex_debug)
-		printk(version);
-
-	root_vortex_dev = NULL;
+		printk(KERN_INFO "%s", version);
 #ifdef CARDBUS
 	register_driver(&vortex_ops);
 	return 0;
 #else
-	{
-		int cards_found = vortex_scan(0);
-		if (cards_found == 0)
-			printk("No 3Com Vortex/Boomerang cards found.\n");
-		return cards_found ? 0 : -ENODEV;
-	}
+	return vortex_scan(0, pci_tbl);
 #endif
 }
 
 #else
 int tc59x_probe(struct device *dev)
 {
-	int cards_found = 0;
-
-	cards_found = vortex_scan(dev);
-
-	if (vortex_debug > 0  &&  cards_found)
-		printk(version);
-
-	return cards_found ? 0 : -ENODEV;
+	printk(KERN_INFO "%s", version);
+	return vortex_scan(dev, pci_tbl);
 }
 #endif  /* not MODULE */
 
-static int vortex_scan(struct device *dev)
+#ifndef CARDBUS
+static int vortex_scan(struct device *dev, struct pci_id_info pci_tbl[])
 {
 	int cards_found = 0;
 
@@ -623,25 +633,30 @@
 		unsigned char pci_bus, pci_device_fn;
 
 		for (;pci_index < 0xff; pci_index++) {
-			u8 pci_latency;
-			u16 pci_command, new_command, vendor, device;
-			int irq;
+			u16 vendor, device, pci_command, new_command, pwr_cmd;
+			int chip_idx, irq;
 			long ioaddr;
 
-			if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8,
-									pci_index, &pci_bus, &pci_device_fn)
+			if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, pci_index,
+									&pci_bus, &pci_device_fn)
 				!= PCIBIOS_SUCCESSFUL)
 				break;
 			pcibios_read_config_word(pci_bus, pci_device_fn,
 									 PCI_VENDOR_ID, &vendor);
 			pcibios_read_config_word(pci_bus, pci_device_fn,
 									 PCI_DEVICE_ID, &device);
-			pcibios_read_config_word(pci_bus, pci_device_fn,
-									 PCI_COMMAND, &pci_command);
+			for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++)
+				if (vendor == pci_tbl[chip_idx].vendor_id
+					&& (device & pci_tbl[chip_idx].device_id_mask) ==
+					pci_tbl[chip_idx].device_id)
+					break;
+			if (pci_tbl[chip_idx].vendor_id == 0) 		/* Compiled out! */
+				continue;
+
 			{
 #if LINUX_VERSION_CODE >= 0x20155
 				struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn);
-				ioaddr = pdev->base_address[0];
+				ioaddr = pdev->base_address[0] & ~3;
 				irq = pdev->irq;
 #else
 				u32 pci_ioaddr;
@@ -650,15 +665,28 @@
 										 PCI_INTERRUPT_LINE, &pci_irq_line);
 				pcibios_read_config_dword(pci_bus, pci_device_fn,
 										  PCI_BASE_ADDRESS_0, &pci_ioaddr);
-				ioaddr = pci_ioaddr;
+				ioaddr = pci_ioaddr & ~3;;
 				irq = pci_irq_line;
 #endif
 			}
-			/* Remove I/O space marker in bit 0. */
-			ioaddr &= ~3;
 
-			if (vendor != TCOM_VENDOR_ID)
-				continue;
+			/* Power-up the card. */
+			pcibios_read_config_word(pci_bus, pci_device_fn,
+										 0xe0, &pwr_cmd);
+			if (pwr_cmd & 0x3) {
+				/* Save the ioaddr and IRQ info! */
+				printk(KERN_INFO "  A 3Com network adapter is powered down!"
+					   "  Setting the power state %4.4x->%4.4x.\n",
+					   pwr_cmd, pwr_cmd & ~3);
+				pcibios_write_config_word(pci_bus, pci_device_fn,
+										  0xe0, pwr_cmd & ~3);
+				printk(KERN_INFO "  Setting the IRQ to %d, IOADDR to %#lx.\n",
+					   irq, ioaddr);
+				pcibios_write_config_byte(pci_bus, pci_device_fn,
+										 PCI_INTERRUPT_LINE, irq);
+				pcibios_write_config_dword(pci_bus, pci_device_fn,
+										  PCI_BASE_ADDRESS_0, ioaddr);
+			}
 
 			if (ioaddr == 0) {
 				printk(KERN_WARNING "  A 3Com network adapter has been found, "
@@ -668,34 +696,31 @@
 				continue;
 			}
 
-			if (check_region(ioaddr, VORTEX_TOTAL_SIZE))
+			if (check_region(ioaddr, pci_tbl[chip_idx].io_size))
 				continue;
 
 			/* Activate the card. */
+			pcibios_read_config_word(pci_bus, pci_device_fn,
+									 PCI_COMMAND, &pci_command);
 			new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO;
 			if (pci_command != new_command) {
-				printk(KERN_INFO "  The PCI BIOS has not enabled this"
-					   " device!  Updating PCI command %4.4x->%4.4x.\n",
-					   pci_command, new_command);
+				printk(KERN_INFO "  The PCI BIOS has not enabled the device "
+					   "at %d/%d. Updating PCI command %4.4x->%4.4x.\n",
+					   pci_bus, pci_device_fn, pci_command, new_command);
 				pcibios_write_config_word(pci_bus, pci_device_fn,
 										  PCI_COMMAND, new_command);
 			}
 
-			dev = vortex_found_device(dev, ioaddr, irq,
-									  device, dev && dev->mem_start
-									  ? dev->mem_start : options[cards_found],
-									  cards_found);
+			dev = vortex_probe1(pci_bus, pci_device_fn, dev, ioaddr, irq,
+								chip_idx, cards_found);
 
 			if (dev) {
-				struct vortex_private *vp = (struct vortex_private *)dev->priv;
 				/* Get and check the latency values.  On the 3c590 series
 				   the latency timer must be set to the maximum value to avoid
 				   data corruption that occurs when the timer expires during
 				   a transfer -- a bug in the Vortex chip only. */
-				u8 new_latency = (device&0xff00) == 0x5900 ? 248 : 32;
-				vp->pci_bus = pci_bus;
-				vp->pci_dev_fn = pci_device_fn;
-				vp->pci_device_id = device;
+				u8 pci_latency;
+				u8 new_latency = (device & 0xff00) == 0x5900 ? 248 : 32;
 				
 				pcibios_read_config_byte(pci_bus, pci_device_fn,
 										 PCI_LATENCY_TIMER, &pci_latency);
@@ -715,7 +740,7 @@
 
 	/* Now check all slots of the EISA bus. */
 	if (EISA_bus) {
-		static int ioaddr = 0x1000;
+		static long ioaddr = 0x1000;
 		for ( ; ioaddr < 0x9000; ioaddr += 0x1000) {
 			int device_id;
 			if (check_region(ioaddr, VORTEX_TOTAL_SIZE))
@@ -727,10 +752,8 @@
 			device_id = (inb(ioaddr + 0xC82)<<8) + inb(ioaddr + 0xC83);
 			if ((device_id & 0xFF00) != 0x5900)
 				continue;
-			vortex_found_device(dev, ioaddr, inw(ioaddr + 0xC88) >> 12,
-								device_id,  dev && dev->mem_start
-								? dev->mem_start : options[cards_found],
-								cards_found);
+			vortex_probe1(0, 0, dev, ioaddr, inw(ioaddr + 0xC88) >> 12,
+						  4, cards_found);
 			dev = 0;
 			cards_found++;
 		}
@@ -739,97 +762,58 @@
 #ifdef MODULE
 	/* Special code to work-around the Compaq PCI BIOS32 problem. */
 	if (compaq_ioaddr) {
-		vortex_found_device(dev, compaq_ioaddr, compaq_irq, compaq_device_id,
-							dev && dev->mem_start ? dev->mem_start
-							: options[cards_found], cards_found);
-		cards_found++;
+		vortex_probe1(0, 0, dev, compaq_ioaddr, compaq_irq,
+					  compaq_device_id, cards_found++);
 		dev = 0;
 	}
 #endif
 
-	/* 3c515 cards are now supported by the 3c515.c driver. */
-
-	return cards_found;
+	return cards_found ? 0 : -ENODEV;
 }
+#endif  /* ! Cardbus */
 
-static struct device *
-vortex_found_device(struct device *dev, int ioaddr, int irq,
-					int device_id, int option, int card_idx)
+static struct device *vortex_probe1(int pci_bus, int pci_devfn,
+									struct device *dev, long ioaddr,
+									int irq, int chip_idx, int card_idx)
 {
 	struct vortex_private *vp;
-	const char *product_name;
-	int board_index = 0;
+	int option;
+	unsigned int eeprom[0x40], checksum = 0;		/* EEPROM contents */
+	int i;
 
-	for (board_index = 0; product_ids[board_index]; board_index++) {
-		if (device_id == product_ids[board_index])
-			break;
-	}
-	/* Handle products we don't recognize, but might still work with. */
-	if (product_ids[board_index])
-		product_name = product_names[board_index];
-	else if ((device_id & 0xff00) == 0x5900)
-		product_name = "3c590 Vortex";
-	else if ((device_id & 0xfff0) == 0x9000)
-		product_name = "3c900";
-	else if ((device_id & 0xfff0) == 0x9050)
-		product_name = "3c905";
-	else {
-		printk(KERN_WARNING "Unknown 3Com PCI ethernet adapter type %4.4x detected:"
-			   " not configured.\n", device_id);
-		return 0;
-	}
+	dev = init_etherdev(dev, 0);
 
-#ifdef MODULE
-	/* Allocate and fill new device structure. */
-	{
-		int dev_size = sizeof(struct device) +
-			sizeof(struct vortex_private) + 15;		/* Pad for alignment */
+	printk(KERN_INFO "%s: 3Com %s at 0x%lx, ",
+		   dev->name, pci_tbl[chip_idx].name, ioaddr);
 
-		dev = (struct device *) kmalloc(dev_size, GFP_KERNEL);
-		memset(dev, 0, dev_size);
-	}
-	/* Align the Rx and Tx ring entries.  */
-	dev->priv = (void *)(((long)dev + sizeof(struct device) + 15) & ~15);
-	vp = (struct vortex_private *)dev->priv;
-	dev->name = vp->devname; /* An empty string. */
 	dev->base_addr = ioaddr;
 	dev->irq = irq;
-	dev->init = vortex_probe1;
-	vp->product_name = product_name;
-	vp->options = option;
-	if (card_idx >= 0) {
-		if (full_duplex[card_idx] >= 0)
-			vp->full_duplex = full_duplex[card_idx];
-	} else
-		vp->full_duplex = (option > 0 && (option & 0x10) ? 1 : 0);
+	dev->mtu = mtu;
 
-	if (option > 0) {
-		vp->media_override = ((option & 7) == XCVR_10baseTOnly) ?
-			XCVR_10baseT  :  option & 7;
-		vp->bus_master = (option & 16) ? 1 : 0;
-	} else {
-		vp->media_override = 7;
-		vp->bus_master = 0;
+	/* Make certain the descriptor lists are aligned. */
+	{
+		void *mem = kmalloc(sizeof(*vp) + 15, GFP_KERNEL);
+		vp =  (void *)(((long)mem + 15) & ~15);
+		vp->priv_addr = mem;
 	}
-	ether_setup(dev);
+	memset(vp, 0, sizeof(*vp));
+	dev->priv = vp;
+
 	vp->next_module = root_vortex_dev;
 	root_vortex_dev = dev;
-	if (register_netdev(dev) != 0)
-		return 0;
-#else  /* not a MODULE */
-	if (dev) {
-		/* Caution: quad-word alignment required for rings! */
-		dev->priv = kmalloc(sizeof (struct vortex_private), GFP_KERNEL);
-		memset(dev->priv, 0, sizeof (struct vortex_private));
-	}
-	dev = init_etherdev(dev, sizeof(struct vortex_private));
-	dev->base_addr = ioaddr;
-	dev->irq = irq;
-	dev->mtu = mtu;
 
-	vp  = (struct vortex_private *)dev->priv;
-	vp->product_name = product_name;
-	vp->options = option;
+	vp->chip_id = chip_idx;
+	vp->pci_bus = pci_bus;
+	vp->pci_devfn = pci_devfn;
+
+	/* The lower four bits are the media type. */
+	if (dev->mem_start)
+		option = dev->mem_start;
+	else if (card_idx < MAX_UNITS)
+		option = options[card_idx];
+	else
+		option = -1;
+
 	if (option >= 0) {
 		vp->media_override = ((option & 7) == 2)  ?  0  :  option & 7;
 		vp->full_duplex = (option & 8) ? 1 : 0;
@@ -839,22 +823,11 @@
 		vp->full_duplex = 0;
 		vp->bus_master = 0;
 	}
+	if (card_idx < MAX_UNITS  &&  full_duplex[card_idx] > 0)
+		vp->full_duplex = 1;
 
-	vortex_probe1(dev);
-#endif /* MODULE */
-	return dev;
-}
-
-static int vortex_probe1(struct device *dev)
-{
-	int ioaddr = dev->base_addr;
-	struct vortex_private *vp = (struct vortex_private *)dev->priv;
-	u16 *ether_addr = (u16 *)dev->dev_addr;
-	unsigned int eeprom[0x40], checksum = 0;		/* EEPROM contents */
-	int i;
-
-	printk(KERN_INFO "%s: 3Com %s at %#3x,",
-		   dev->name, vp->product_name, ioaddr);
+	vp->force_fd = vp->full_duplex;
+	vp->options = option;
 
 	/* Read the station address from the EEPROM. */
 	EL3WINDOW(0);
@@ -885,14 +858,30 @@
 		printk(" ***INVALID CHECKSUM %4.4x*** ", checksum);
 
 	for (i = 0; i < 3; i++)
-		ether_addr[i] = htons(eeprom[i + 10]);
+		((u16 *)dev->dev_addr)[i] = htons(eeprom[i + 10]);
 	for (i = 0; i < 6; i++)
 		printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i]);
+#ifdef __sparc__
+	printk(", IRQ %s\n", __irq_itoa(dev->irq));
+#else
 	printk(", IRQ %d\n", dev->irq);
 	/* Tell them about an invalid IRQ. */
 	if (vortex_debug && (dev->irq <= 0 || dev->irq >= NR_IRQS))
 		printk(KERN_WARNING " *** Warning: IRQ %d is unlikely to work! ***\n",
 			   dev->irq);
+#endif
+
+	if (pci_tbl[vp->chip_id].drv_flags & HAS_CB_FNS) {
+		u32 fn_st_addr;			/* Cardbus function status space */
+		pcibios_read_config_dword(pci_bus, pci_devfn, PCI_BASE_ADDRESS_2,
+								  &fn_st_addr);
+		if (fn_st_addr)
+			vp->cb_fn_base = ioremap(fn_st_addr & ~3, 128);
+		printk("%s: CardBus functions mapped %8.8x->%p (PCMCIA committee"
+			   " brain-damage).\n", dev->name, fn_st_addr, vp->cb_fn_base);
+		EL3WINDOW(2);
+		outw(0x10 | inw(ioaddr + Wn2_ResetOptions), ioaddr + Wn2_ResetOptions);
+	}
 
 	/* Extract our information from the EEPROM data. */
 	vp->info1 = eeprom[13];
@@ -918,7 +907,7 @@
 			   config.u.ram_width ? "word" : "byte",
 			   ram_split[config.u.ram_split],
 			   config.u.autoselect ? "autoselect/" : "",
-			   config.u.xcvr ? "NWay Autonegotiation" :
+			   config.u.xcvr > XCVR_ExtMII ? "<invalid transceiver>" :
 			   media_tbl[config.u.xcvr].name);
 		vp->default_media = config.u.xcvr;
 		vp->autoselect = config.u.autoselect;
@@ -931,22 +920,24 @@
 	} else
 		dev->if_port = vp->default_media;
 
-	if (dev->if_port == XCVR_MII) {
+	if (dev->if_port == XCVR_MII || dev->if_port == XCVR_NWAY) {
 		int phy, phy_idx = 0;
 		EL3WINDOW(4);
-		for (phy = 0; phy < 32 && phy_idx < sizeof(vp->phys); phy++) {
-			int mii_status;
-			mdio_sync(ioaddr, 32);
-			mii_status = mdio_read(ioaddr, phy, 1);
+		mii_preamble_required++;
+		mii_preamble_required++;
+		mdio_read(ioaddr, 24, 1);
+		for (phy = 1; phy <= 32 && phy_idx < sizeof(vp->phys); phy++) {
+			int mii_status, phyx = phy & 0x1f;
+			mii_status = mdio_read(ioaddr, phyx, 1);
 			if (mii_status  &&  mii_status != 0xffff) {
-				vp->phys[phy_idx++] = phy;
-				printk(KERN_INFO "  MII transceiver found at address %d, status %4x.\n",
-					   phy, mii_status);
-				mdio_sync(ioaddr, 32);
-				if ((mdio_read(ioaddr, phy, 1) & 0x0040) == 0)
-					mii_preamble_required = 1;
+				vp->phys[phy_idx++] = phyx;
+				printk(KERN_INFO "  MII transceiver found at address %d,"
+					   " status %4x.\n", phyx, mii_status);
+				if ((mii_status & 0x0040) == 0)
+					mii_preamble_required++;
 			}
 		}
+		mii_preamble_required--;
 		if (phy_idx == 0) {
 			printk(KERN_WARNING"  ***WARNING*** No MII transceivers found!\n");
 			vp->phys[0] = 24;
@@ -954,7 +945,7 @@
 			vp->advertising = mdio_read(ioaddr, vp->phys[0], 4);
 			if (vp->full_duplex) {
 				/* Only advertise the FD media types. */
-				vp->advertising &= 0x015F;
+				vp->advertising &= ~0x02A0;
 				mdio_write(ioaddr, vp->phys[0], 4, vp->advertising);
 			}
 		}
@@ -968,30 +959,24 @@
 	}
 
 	/* We do a request_region() to register /proc/ioports info. */
-	request_region(ioaddr, VORTEX_TOTAL_SIZE, vp->product_name);
+	request_region(ioaddr, pci_tbl[chip_idx].io_size, dev->name);
 
 	/* The 3c59x-specific entries in the device structure. */
 	dev->open = &vortex_open;
 	dev->hard_start_xmit = &vortex_start_xmit;
 	dev->stop = &vortex_close;
 	dev->get_stats = &vortex_get_stats;
-#ifdef HAVE_PRIVATE_IOCTL
 	dev->do_ioctl = &vortex_ioctl;
-#endif
-#ifdef NEW_MULTICAST
 	dev->set_multicast_list = &set_rx_mode;
-#else
-	dev->set_multicast_list = &set_multicast_list;
-#endif
 
-	return 0;
+	return dev;
 }
 
 
 static int
 vortex_open(struct device *dev)
 {
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
 	union wn3_config config;
 	int i;
@@ -1006,28 +991,31 @@
 				   dev->name, vp->media_override,
 				   media_tbl[vp->media_override].name);
 		dev->if_port = vp->media_override;
+	} else if (vp->autoselect && pci_tbl[vp->chip_id].drv_flags & HAS_NWAY) {
+			dev->if_port = XCVR_NWAY;
 	} else if (vp->autoselect) {
 		/* Find first available media type, starting with 100baseTx. */
 		dev->if_port = XCVR_100baseTx;
 		while (! (vp->available_media & media_tbl[dev->if_port].mask))
 			dev->if_port = media_tbl[dev->if_port].next;
-
-		if (vortex_debug > 1)
-			printk(KERN_DEBUG "%s: Initial media type %s.\n",
-				   dev->name, media_tbl[dev->if_port].name);
-
-		init_timer(&vp->timer);
-		vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait);
-		vp->timer.data = (unsigned long)dev;
-		vp->timer.function = &vortex_timer;    /* timer handler */
-		add_timer(&vp->timer);
 	} else
 		dev->if_port = vp->default_media;
 
+	init_timer(&vp->timer);
+	vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait);
+	vp->timer.data = (unsigned long)dev;
+	vp->timer.function = &vortex_timer;		/* timer handler */
+	add_timer(&vp->timer);
+
+	if (vortex_debug > 1)
+		printk(KERN_DEBUG "%s: Initial media type %s.\n",
+			   dev->name, media_tbl[dev->if_port].name);
+
+	vp->full_duplex = vp->force_fd;
 	config.u.xcvr = dev->if_port;
 	outl(config.i, ioaddr + Wn3_Config);
 
-	if (dev->if_port == XCVR_MII) {
+	if (dev->if_port == XCVR_MII || dev->if_port == XCVR_NWAY) {
 		int mii_reg1, mii_reg5;
 		EL3WINDOW(4);
 		/* Read BMSR (reg1) only to clear old status. */
@@ -1067,20 +1055,10 @@
 
 	outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
 
-#ifdef SA_SHIRQ
 	/* Use the now-standard shared IRQ implementation. */
 	if (request_irq(dev->irq, &vortex_interrupt, SA_SHIRQ, dev->name, dev)) {
 		return -EAGAIN;
 	}
-#else
-	if (dev->irq == 0  ||  irq2dev_map[dev->irq] != NULL)
-		return -EAGAIN;
-	irq2dev_map[dev->irq] = dev;
-	if (request_irq(dev->irq, &vortex_interrupt, 0, vp->product_name)) {
-		irq2dev_map[dev->irq] = NULL;
-		return -EAGAIN;
-	}
-#endif
 
 	if (vortex_debug > 1) {
 		EL3WINDOW(4);
@@ -1098,9 +1076,11 @@
 	if (dev->if_port == XCVR_10base2)
 		/* Start the thinnet transceiver. We should really wait 50ms...*/
 		outw(StartCoax, ioaddr + EL3_CMD);
-	EL3WINDOW(4);
-	outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP|Media_SQE)) |
-		 media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media);
+	if (dev->if_port != XCVR_NWAY) {
+		EL3WINDOW(4);
+		outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP|Media_SQE)) |
+			 media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media);
+	}
 
 	/* Switch to the stats window, and clear all stats by reading. */
 	outw(StatsDisable, ioaddr + EL3_CMD);
@@ -1127,22 +1107,23 @@
 			printk(KERN_DEBUG "%s:  Filling in the Rx ring.\n", dev->name);
 		for (i = 0; i < RX_RING_SIZE; i++) {
 			struct sk_buff *skb;
-			vp->rx_ring[i].next = virt_to_bus(&vp->rx_ring[i+1]);
+			vp->rx_ring[i].next = cpu_to_le32(virt_to_bus(&vp->rx_ring[i+1]));
 			vp->rx_ring[i].status = 0;	/* Clear complete bit. */
-			vp->rx_ring[i].length = PKT_BUF_SZ | LAST_FRAG;
-			skb = DEV_ALLOC_SKB(PKT_BUF_SZ);
+			vp->rx_ring[i].length = cpu_to_le32(PKT_BUF_SZ | LAST_FRAG);
+			skb = dev_alloc_skb(PKT_BUF_SZ);
 			vp->rx_skbuff[i] = skb;
 			if (skb == NULL)
 				break;			/* Bad news!  */
 			skb->dev = dev;			/* Mark as being used by this device. */
 #if LINUX_VERSION_CODE >= 0x10300
 			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
-			vp->rx_ring[i].addr = virt_to_bus(skb->tail);
+			vp->rx_ring[i].addr = cpu_to_le32(virt_to_bus(skb->tail));
 #else
 			vp->rx_ring[i].addr = virt_to_bus(skb->data);
 #endif
 		}
-		vp->rx_ring[i-1].next = virt_to_bus(&vp->rx_ring[0]); /* Wrap the ring. */
+		/* Wrap the ring. */
+		vp->rx_ring[i-1].next = cpu_to_le32(virt_to_bus(&vp->rx_ring[0]));
 		outl(virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr);
 	}
 	if (vp->full_bus_master_tx) { 		/* Boomerang bus master Tx. */
@@ -1170,14 +1151,16 @@
 		(vp->full_bus_master_tx ? DownComplete : TxAvailable) |
 		(vp->full_bus_master_rx ? UpComplete : RxComplete) |
 		(vp->bus_master ? DMADone : 0);
+	vp->intr_enable = SetIntrEnb | IntLatch | TxAvailable | RxComplete |
+		StatsFull | HostError | TxComplete | IntReq
+		| (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete;
 	outw(vp->status_enable, ioaddr + EL3_CMD);
 	/* Ack all pending events, and set active indicator mask. */
 	outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
 		 ioaddr + EL3_CMD);
-	outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
-		 | HostError | TxComplete
-		 | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete,
-			ioaddr + EL3_CMD);
+	outw(vp->intr_enable, ioaddr + EL3_CMD);
+	if (vp->cb_fn_base)			/* The PCMCIA people are idiots.  */
+		writel(0x8000, vp->cb_fn_base + 4);
 
 	MOD_INC_USE_COUNT;
 
@@ -1186,24 +1169,23 @@
 
 static void vortex_timer(unsigned long data)
 {
-#ifdef AUTOMEDIA
 	struct device *dev = (struct device *)data;
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
-	int ioaddr = dev->base_addr;
-	unsigned long flags;
+	long ioaddr = dev->base_addr;
+	int next_tick = 0;
 	int ok = 0;
+	int media_status, mii_status, old_window;
 
 	if (vortex_debug > 1)
 		printk(KERN_DEBUG "%s: Media selection timer tick happened, %s.\n",
 			   dev->name, media_tbl[dev->if_port].name);
 
-	save_flags(flags);	cli(); {
-	  int old_window = inw(ioaddr + EL3_CMD) >> 13;
-	  int media_status;
-	  EL3WINDOW(4);
-	  media_status = inw(ioaddr + Wn4_Media);
-	  switch (dev->if_port) {
-	  case XCVR_10baseT:  case XCVR_100baseTx:  case XCVR_100baseFx:
+	disable_irq(dev->irq);
+	old_window = inw(ioaddr + EL3_CMD) >> 13;
+	EL3WINDOW(4);
+	media_status = inw(ioaddr + Wn4_Media);
+	switch (dev->if_port) {
+	case XCVR_10baseT:  case XCVR_100baseTx:  case XCVR_100baseFx:
 		if (media_status & Media_LnkBeat) {
 		  ok = 1;
 		  if (vortex_debug > 1)
@@ -1212,27 +1194,40 @@
 		} else if (vortex_debug > 1)
 		  printk(KERN_DEBUG "%s: Media %s is has no link beat, %x.\n",
 				   dev->name, media_tbl[dev->if_port].name, media_status);
-
 		break;
-	  case XCVR_MII:
-		  {
-			  int mii_reg1 = mdio_read(ioaddr, vp->phys[0], 1);
+	  case XCVR_MII: case XCVR_NWAY:
+		  mii_status = mdio_read(ioaddr, vp->phys[0], 1);
+		  ok = 1;
+		  if (debug > 1)
+			  printk(KERN_DEBUG "%s: MII transceiver has status %4.4x.\n",
+					 dev->name, mii_status);
+		  if (mii_status & 0x0004) {
 			  int mii_reg5 = mdio_read(ioaddr, vp->phys[0], 5);
-			  if (vortex_debug > 1)
-				  printk(KERN_DEBUG "%s: MII #%d status register is %4.4x, "
-						 "link partner capability %4.4x.\n",
-						 dev->name, vp->phys[0], mii_reg1, mii_reg5);
-			  if (mii_reg1 & 0x0004)
-				  ok = 1;
-			  break;
+			  if (! vp->force_fd  &&  mii_reg5 != 0xffff) {
+				  int duplex = (mii_reg5&0x0100) ||
+					  (mii_reg5 & 0x01C0) == 0x0040;
+				  if (vp->full_duplex != duplex) {
+					  vp->full_duplex = duplex;
+					  printk(KERN_INFO "%s: Setting %s-duplex based on MII "
+							 "#%d link partner capability of %4.4x.\n",
+							 dev->name, vp->full_duplex ? "full" : "half",
+							 vp->phys[0], mii_reg5);
+					  /* Set the full-duplex bit. */
+					  outb((vp->full_duplex ? 0x20 : 0) |
+						   (dev->mtu > 1500 ? 0x40 : 0),
+						   ioaddr + Wn3_MAC_Ctrl);
+				  }
+				  next_tick = 60*HZ;
+			  }
 		  }
+		  break;
 	  default:					/* Other media types handled by Tx timeouts. */
 		if (vortex_debug > 1)
 		  printk(KERN_DEBUG "%s: Media %s is has no indication, %x.\n",
 				 dev->name, media_tbl[dev->if_port].name, media_status);
 		ok = 1;
-	  }
-	  if ( ! ok) {
+	}
+	if ( ! ok) {
 		union wn3_config config;
 
 		do {
@@ -1249,8 +1244,7 @@
 			printk(KERN_DEBUG "%s: Media selection failed, now trying "
 				   "%s port.\n",
 				   dev->name, media_tbl[dev->if_port].name);
-		  vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait);
-		  add_timer(&vp->timer);
+		  next_tick = RUN_AT(media_tbl[dev->if_port].wait);
 		}
 		outw((media_status & ~(Media_10TP|Media_SQE)) |
 			 media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media);
@@ -1262,21 +1256,25 @@
 
 		outw(dev->if_port == XCVR_10base2 ? StartCoax : StopCoax,
 			 ioaddr + EL3_CMD);
-	  }
-	  EL3WINDOW(old_window);
-	}   restore_flags(flags);
-	if (vortex_debug > 1)
+	}
+	EL3WINDOW(old_window);
+	enable_irq(dev->irq);
+
+	if (vortex_debug > 2)
 	  printk(KERN_DEBUG "%s: Media selection timer finished, %s.\n",
 			 dev->name, media_tbl[dev->if_port].name);
 
-#endif /* AUTOMEDIA*/
+	if (next_tick) {
+		vp->timer.expires = RUN_AT(next_tick);
+		add_timer(&vp->timer);
+	}
 	return;
 }
 
 static void vortex_tx_timeout(struct device *dev)
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	int j;
 
 	printk(KERN_ERR "%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
@@ -1290,7 +1288,7 @@
 		printk(KERN_ERR "%s: Interrupt posted but not delivered --"
 			   " IRQ blocked by another device?\n", dev->name);
 		/* Bad idea here.. but we might as well handle a few events. */
-		vortex_interrupt IRQ(dev->irq, dev, 0);
+		vortex_interrupt(dev->irq, dev, 0);
 	}
 	outw(TxReset, ioaddr + EL3_CMD);
 	for (j = 200; j >= 0 ; j--)
@@ -1309,8 +1307,8 @@
 		for (i = 0; i < TX_RING_SIZE; i++) {
 			printk(KERN_DEBUG "  %d: @%p  length %8.8x status %8.8x\n", i,
 				   &vp->tx_ring[i],
-				   vp->tx_ring[i].length,
-				   vp->tx_ring[i].status);
+				   le32_to_cpu(vp->tx_ring[i].length),
+				   le32_to_cpu(vp->tx_ring[i].status));
 		}
 	}
 #endif
@@ -1347,7 +1345,7 @@
 vortex_error(struct device *dev, int status)
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	int do_tx_reset = 0;
 	int i;
 
@@ -1387,8 +1385,10 @@
 			DoneDidThat++;
 		}
 	}
-	if (status & IntReq)		/* Restore all interrupt sources.  */
-		outw(ioaddr + EL3_CMD, vp->status_enable);
+	if (status & IntReq) {		/* Restore all interrupt sources.  */
+		outw(vp->status_enable, ioaddr + EL3_CMD);
+		outw(vp->intr_enable, ioaddr + EL3_CMD);
+	}
 	if (status & HostError) {
 		u16 fifo_diag;
 		EL3WINDOW(4);
@@ -1434,7 +1434,7 @@
 vortex_start_xmit(struct sk_buff *skb, struct device *dev)
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 
 	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
 		if (jiffies - dev->trans_start >= TX_TIMEOUT)
@@ -1444,7 +1444,6 @@
 
 	/* Put out the doubleword header... */
 	outl(skb->len, ioaddr + TX_FIFO);
-#ifdef VORTEX_BUS_MASTER
 	if (vp->bus_master) {
 		/* Set the bus-master controller to transfer the packet. */
 		outl(virt_to_bus(skb->data), ioaddr + Wn7_MasterAddr);
@@ -1462,16 +1461,6 @@
 			/* Interrupt us when the FIFO has room for max-sized packet. */
 			outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
 	}
-#else
-	/* ... and the packet rounded to a doubleword. */
-	outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
-	DEV_FREE_SKB(skb);
-	if (inw(ioaddr + TxFree) > 1536) {
-		clear_bit(0, (void*)&dev->tbusy);
-	} else
-		/* Interrupt us when the FIFO has room for max-sized packet. */
-		outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
-#endif  /* bus master */
 
 	dev->trans_start = jiffies;
 
@@ -1506,7 +1495,7 @@
 boomerang_start_xmit(struct sk_buff *skb, struct device *dev)
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 
 	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
 		if (jiffies - dev->trans_start >= TX_TIMEOUT)
@@ -1528,13 +1517,12 @@
 				printk(KERN_WARNING "%s: Tx Ring full, refusing to send buffer.\n",
 					   dev->name);
 			return 1;
-		} 
-		/* end change 06/25/97 M. Sievers */	
+		}
 		vp->tx_skbuff[entry] = skb;
 		vp->tx_ring[entry].next = 0;
-		vp->tx_ring[entry].addr = virt_to_bus(skb->data);
-		vp->tx_ring[entry].length = skb->len | LAST_FRAG;
-		vp->tx_ring[entry].status = skb->len | TxIntrUploaded;
+		vp->tx_ring[entry].addr = cpu_to_le32(virt_to_bus(skb->data));
+		vp->tx_ring[entry].length = cpu_to_le32(skb->len | LAST_FRAG);
+		vp->tx_ring[entry].status = cpu_to_le32(skb->len | TxIntrUploaded);
 
 		save_flags(flags);
 		cli();
@@ -1543,7 +1531,7 @@
 		for (i = 600; i >= 0 ; i--)
 			if ( (inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0)
 				break;
-		prev_entry->next = virt_to_bus(&vp->tx_ring[entry]);
+		prev_entry->next = cpu_to_le32(virt_to_bus(&vp->tx_ring[entry]));
 		if (inl(ioaddr + DownListPtr) == 0) {
 			outl(virt_to_bus(&vp->tx_ring[entry]), ioaddr + DownListPtr);
 			queued_packet++;
@@ -1555,7 +1543,7 @@
 		if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1)
 			vp->tx_full = 1;
 		else {					/* Clear previous interrupt enable. */
-			prev_entry->status &= ~TxIntrUploaded;
+			prev_entry->status &= cpu_to_le32(~TxIntrUploaded);
 			clear_bit(0, (void*)&dev->tbusy);
 		}
 		dev->trans_start = jiffies;
@@ -1565,28 +1553,33 @@
 
 /* The interrupt handler does all of the Rx thread work and cleans up
    after the Tx thread. */
-static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs)
+static void vortex_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-#ifdef SA_SHIRQ		/* Use the now-standard shared IRQ implementation. */
 	struct device *dev = dev_id;
-#else
-	struct device *dev = (struct device *)(irq2dev_map[irq]);
-#endif
-	struct vortex_private *vp;
-	int ioaddr, status;
-	int latency;
+	struct vortex_private *vp = (struct vortex_private *)dev->priv;
+	long ioaddr;
+	int latency, status;
 	int work_done = max_interrupt_work;
 
-	vp = (struct vortex_private *)dev->priv;
-	if (test_and_set_bit(0, (void*)&vp->in_interrupt)) {
+#if defined(__i386__)
+	/* A lock to prevent simultaneous entry bug on Intel SMP machines. */
+	if (test_and_set_bit(0, (void*)&dev->interrupt)) {
+		printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n",
+			   dev->name);
+		dev->interrupt = 0;	/* Avoid halting machine. */
+		return;
+	}
+#else
+	if (dev->interrupt) {
 		printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
 		return;
 	}
+	dev->interrupt = 1;
+#endif
 
 	dev->interrupt = 1;
 	ioaddr = dev->base_addr;
 	latency = inb(ioaddr + Timer);
-
 	status = inw(ioaddr + EL3_STATUS);
 
 	if (vortex_debug > 4)
@@ -1635,17 +1628,23 @@
 				mark_bh(NET_BH);
 			}
 		}
-#ifdef VORTEX_BUS_MASTER
 		if (status & DMADone) {
-			outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */
-			clear_bit(0, (void*)&dev->tbusy);
-			DEV_FREE_SKB(vp->tx_skb); /* Release the transfered buffer */
-			mark_bh(NET_BH);
+			if (inw(ioaddr + Wn7_MasterStatus) & 0x1000) {
+				outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */
+				DEV_FREE_SKB(vp->tx_skb); /* Release the transfered buffer */
+				if (inw(ioaddr + TxFree) > 1536) {
+					clear_bit(0, (void*)&dev->tbusy);
+					mark_bh(NET_BH);
+				} else /* Interrupt when FIFO has room for max-sized packet. */
+					outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
+			}
 		}
-#endif
 		/* Check for all uncommon interrupts at once. */
-		if (status & (HostError | RxEarly | StatsFull | TxComplete | IntReq))
+		if (status & (HostError | RxEarly | StatsFull | TxComplete | IntReq)) {
+			if (status == 0xffff)
+				break;
 			vortex_error(dev, status);
+		}
 
 		if (--work_done < 0) {
 			if ((status & (0x7fe - (UpComplete | DownComplete))) == 0) {
@@ -1658,13 +1657,14 @@
 				/* Disable all pending interrupts. */
 				outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD);
 				outw(AckIntr | 0x7FF, ioaddr + EL3_CMD);
-				/* Set a timer to reenable interrupts. */
-				
+				/* The timer will reenable interrupts. */
 				break;
 			}
 		}
 		/* Acknowledge the IRQ. */
 		outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
+		if (vp->cb_fn_base)			/* The PCMCIA people are idiots.  */
+			writel(0x8000, vp->cb_fn_base + 4);
 
 	} while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete));
 
@@ -1672,16 +1672,18 @@
 		printk(KERN_DEBUG "%s: exiting interrupt, status %4.4x.\n",
 			   dev->name, status);
 
+#if defined(__i386__)
+	clear_bit(0, (void*)&dev->interrupt);
+#else
 	dev->interrupt = 0;
-	clear_bit(0, (void*)&vp->in_interrupt);
+#endif
 	return;
 }
 
-static int
-vortex_rx(struct device *dev)
+static int vortex_rx(struct device *dev)
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	int i;
 	short rx_status;
 
@@ -1704,25 +1706,28 @@
 			int pkt_len = rx_status & 0x1fff;
 			struct sk_buff *skb;
 
-			skb = DEV_ALLOC_SKB(pkt_len + 5);
+			skb = dev_alloc_skb(pkt_len + 5);
 			if (vortex_debug > 4)
 				printk(KERN_DEBUG "Receiving packet size %d status %4.4x.\n",
 					   pkt_len, rx_status);
 			if (skb != NULL) {
 				skb->dev = dev;
-#if LINUX_VERSION_CODE >= 0x10300
 				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
 				/* 'skb_put()' points to the start of sk_buff data area. */
-				insl(ioaddr + RX_FIFO, skb_put(skb, pkt_len),
-					 (pkt_len + 3) >> 2);
+				if (vp->bus_master &&
+					! (inw(ioaddr + Wn7_MasterStatus) & 0x8000)) {
+					outl(virt_to_bus(skb_put(skb, pkt_len)),
+						 ioaddr + Wn7_MasterAddr);
+					outw((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen);
+					outw(StartDMAUp, ioaddr + EL3_CMD);
+					while (inw(ioaddr + Wn7_MasterStatus) & 0x8000)
+						;
+				} else {
+					insl(ioaddr + RX_FIFO, skb_put(skb, pkt_len),
+						 (pkt_len + 3) >> 2);
+				}
 				outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
 				skb->protocol = eth_type_trans(skb, dev);
-#else
-				skb->len = pkt_len;
-				/* 'skb->data' points to the start of sk_buff data area. */
-				insl(ioaddr + RX_FIFO, skb->data, (pkt_len + 3) >> 2);
-				outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
-#endif  /* KERNEL_1_3_0 */
 				netif_rx(skb);
 				dev->last_rx = jiffies;
 				vp->stats.rx_packets++;
@@ -1751,7 +1756,7 @@
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
 	int entry = vp->cur_rx % RX_RING_SIZE;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	int rx_status;
 	int rx_work_limit = vp->dirty_rx + RX_RING_SIZE - vp->cur_rx;
 
@@ -1759,8 +1764,9 @@
 		printk(KERN_DEBUG "  In boomerang_rx(), status %4.4x, rx_status "
 			   "%4.4x.\n",
 			   inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus));
-	while ((--rx_work_limit >= 0) &&
-			((rx_status = vp->rx_ring[entry].status) & RxDComplete)) {
+	while ((rx_status = le32_to_cpu(vp->rx_ring[entry].status)) & RxDComplete){
+		if (--rx_work_limit < 0)
+			break;
 		if (rx_status & RxDError) { /* Error, update stats. */
 			unsigned char rx_error = rx_status >> 16;
 			if (vortex_debug > 2)
@@ -1783,41 +1789,28 @@
 			/* Check if the packet is long enough to just accept without
 			   copying to a properly sized skbuff. */
 			if (pkt_len < rx_copybreak
-				&& (skb = DEV_ALLOC_SKB(pkt_len + 2)) != 0) {
+				&& (skb = dev_alloc_skb(pkt_len + 2)) != 0) {
 				skb->dev = dev;
-#if LINUX_VERSION_CODE >= 0x10300
 				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
 				/* 'skb_put()' points to the start of sk_buff data area. */
 				memcpy(skb_put(skb, pkt_len),
-					   bus_to_virt(vp->rx_ring[entry].addr),
+					   bus_to_virt(le32_to_cpu(vp->rx_ring[entry].addr)),
 					   pkt_len);
-#else
-				memcpy(skb->data, bus_to_virt(vp->rx_ring[entry].addr), pkt_len);
-				skb->len = pkt_len;
-#endif
 				rx_copy++;
-			} else{
+			} else {
 				void *temp;
 				/* Pass up the skbuff already on the Rx ring. */
 				skb = vp->rx_skbuff[entry];
-				if (skb == NULL) {
-					printk(KERN_WARNING "%s: in boomerang_rx -- attempt to use NULL skb caught\n", dev->name);
-					break;
-				}
 				vp->rx_skbuff[entry] = NULL;
-#if LINUX_VERSION_CODE >= 0x10300
 				temp = skb_put(skb, pkt_len);
-#else
-				temp = skb->data;
-#endif
 				/* Remove this checking code for final release. */
-				if (bus_to_virt(vp->rx_ring[entry].addr) != temp)
+				if (bus_to_virt(le32_to_cpu(vp->rx_ring[entry].addr)) != temp)
 					printk(KERN_ERR "%s: Warning -- the skbuff addresses do not match"
 						   " in boomerang_rx: %p vs. %p.\n", dev->name,
-						   bus_to_virt(vp->rx_ring[entry].addr), temp);
+						   bus_to_virt(le32_to_cpu(vp->rx_ring[entry].addr)),
+						   temp);
 				rx_nocopy++;
 			}
-#if LINUX_VERSION_CODE > 0x10300
 			skb->protocol = eth_type_trans(skb, dev);
 			{					/* Use hardware checksum info. */
 				int csum_bits = rx_status & 0xee000000;
@@ -1828,9 +1821,6 @@
 					rx_csumhits++;
 				}
 			}
-#else
-			skb->len = pkt_len;
-#endif
 			netif_rx(skb);
 			dev->last_rx = jiffies;
 			vp->stats.rx_packets++;
@@ -1842,29 +1832,17 @@
 		struct sk_buff *skb;
 		entry = vp->dirty_rx % RX_RING_SIZE;
 		if (vp->rx_skbuff[entry] == NULL) {
-			skb = DEV_ALLOC_SKB(PKT_BUF_SZ);
-			if (skb == NULL) {
-				printk(KERN_DEBUG "%s: in boomerang_rx -- could not allocate skbuff\n", dev->name);
+			skb = dev_alloc_skb(PKT_BUF_SZ);
+			if (skb == NULL)
 				break;			/* Bad news!  */
-			}
 			skb->dev = dev;			/* Mark as being used by this device. */
-#if LINUX_VERSION_CODE > 0x10300
 			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
-			vp->rx_ring[entry].addr = virt_to_bus(skb->tail);
-#else
-			vp->rx_ring[entry].addr = virt_to_bus(skb->data);
-#endif
+			vp->rx_ring[entry].addr = cpu_to_le32(virt_to_bus(skb->tail));
 			vp->rx_skbuff[entry] = skb;
 		}
 		vp->rx_ring[entry].status = 0;	/* Clear complete bit. */
 		outw(UpUnstall, ioaddr + EL3_CMD);
 	}
-
-	if (vp->dirty_rx >= RX_RING_SIZE ) {
-		vp->cur_rx -= RX_RING_SIZE;
-		vp->dirty_rx -= RX_RING_SIZE;
-	}
-
 	return 0;
 }
 
@@ -1872,7 +1850,7 @@
 vortex_close(struct device *dev)
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	int i;
 
 	dev->start = 0;
@@ -1899,12 +1877,7 @@
 		/* Turn off thinnet power.  Green! */
 		outw(StopCoax, ioaddr + EL3_CMD);
 
-#ifdef SA_SHIRQ
 	free_irq(dev->irq, dev);
-#else
-	free_irq(dev->irq);
-	irq2dev_map[dev->irq] = 0;
-#endif
 
 	outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD);
 
@@ -1934,8 +1907,7 @@
 	return 0;
 }
 
-static struct enet_statistics *
-vortex_get_stats(struct device *dev)
+static struct net_device_stats *vortex_get_stats(struct device *dev)
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
 	unsigned long flags;
@@ -1956,7 +1928,7 @@
 	table.  This is done by checking that the ASM (!) code generated uses
 	atomic updates with '+='.
 	*/
-static void update_stats(int ioaddr, struct device *dev)
+static void update_stats(long ioaddr, struct device *dev)
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
 
@@ -1987,20 +1959,14 @@
 	return;
 }
 
-#ifdef HAVE_PRIVATE_IOCTL
 static int vortex_ioctl(struct device *dev, struct ifreq *rq, int cmd)
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	u16 *data = (u16 *)&rq->ifr_data;
 	int phy = vp->phys[0] & 0x1f;
 
-	if (vortex_debug > 2)
-		printk(KERN_DEBUG "%s: In ioct(%-.6s, %#4.4x) %4.4x %4.4x %4.4x %4.4x.\n",
-			   dev->name, rq->ifr_ifrn.ifrn_name, cmd,
-			   data[0], data[1], data[2], data[3]);
-
-    switch(cmd) {
+	switch(cmd) {
 	case SIOCDEVPRIVATE:		/* Get the address of the PHY in use. */
 		data[0] = phy;
 	case SIOCDEVPRIVATE+1:		/* Read the specified MII register. */
@@ -2010,22 +1976,20 @@
 	case SIOCDEVPRIVATE+2:		/* Write the specified MII register */
 		if (!suser())
 			return -EPERM;
+		EL3WINDOW(4);
 		mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]);
 		return 0;
 	default:
 		return -EOPNOTSUPP;
 	}
 }
-#endif  /* HAVE_PRIVATE_IOCTL */
 
-/* This new version of set_rx_mode() supports v1.4 kernels.
-   The Vortex chip has no documented multicast filter, so the only
+/* Pre-Cyclone chips have no documented multicast filter, so the only
    multicast setting is to receive all multicast frames.  At least
    the chip has a very clean way to set the mode, unlike many others. */
-static void
-set_rx_mode(struct device *dev)
+static void set_rx_mode(struct device *dev)
 {
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	int new_mode;
 
 	if (dev->flags & IFF_PROMISC) {
@@ -2039,14 +2003,6 @@
 
 	outw(new_mode, ioaddr + EL3_CMD);
 }
-#ifndef NEW_MULTICAST
-/* The old interface to set the Rx mode. */
-static void
-set_multicast_list(struct device *dev, int num_addrs, void *addrs)
-{
-	set_rx_mode(dev);
-}
-#endif
 
 
 /* MII transceiver control section.
@@ -2057,7 +2013,7 @@
 /* The maximum data clock rate is 2.5 Mhz.  The minimum timing is usually
    met by back-to-back PCI I/O cycles, but we insert a delay to avoid
    "overclocking" issues. */
-#define mdio_delay() udelay(1)
+#define mdio_delay() inl(mdio_addr)
 
 #define MDIO_SHIFT_CLK	0x01
 #define MDIO_DIR_WRITE	0x04
@@ -2068,11 +2024,11 @@
 
 /* Generate the preamble required for initial synchronization and
    a few older transceivers. */
-static void mdio_sync(int ioaddr, int bits)
+static void mdio_sync(long ioaddr, int bits)
 {
-	int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+	long mdio_addr = ioaddr + Wn4_PhysicalMgmt;
 
-	/* Establish sync by sending at least 32 logic ones. */ 
+	/* Establish sync by sending at least 32 logic ones. */
 	while (-- bits >= 0) {
 		outw(MDIO_DATA_WRITE1, mdio_addr);
 		mdio_delay();
@@ -2081,12 +2037,12 @@
 	}
 }
 
-static int mdio_read(int ioaddr, int phy_id, int location)
+static int mdio_read(long ioaddr, int phy_id, int location)
 {
 	int i;
 	int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
 	unsigned int retval = 0;
-	int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+	long mdio_addr = ioaddr + Wn4_PhysicalMgmt;
 
 	if (mii_preamble_required)
 		mdio_sync(ioaddr, 32);
@@ -2107,13 +2063,17 @@
 		outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
 		mdio_delay();
 	}
-	return retval>>1 & 0xffff;
+#if 0
+	return (retval>>1) & 0x1ffff;
+#else
+	return retval & 0x20000 ? 0xffff : retval>>1 & 0xffff;
+#endif
 }
 
-static void mdio_write(int ioaddr, int phy_id, int location, int value)
+static void mdio_write(long ioaddr, int phy_id, int location, int value)
 {
 	int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value;
-	int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+	long mdio_addr = ioaddr + Wn4_PhysicalMgmt;
 	int i;
 
 	if (mii_preamble_required)
@@ -2140,8 +2100,7 @@
 
 
 #ifdef MODULE
-void
-cleanup_module(void)
+void cleanup_module(void)
 {
 	struct device *next_dev;
 
@@ -2151,11 +2110,14 @@
 
 	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
 	while (root_vortex_dev) {
-		next_dev = ((struct vortex_private *)root_vortex_dev->priv)->next_module;
+		struct vortex_private *vp=(void *)(root_vortex_dev->priv);
+		next_dev = vp->next_module;
 		unregister_netdev(root_vortex_dev);
 		outw(TotalReset, root_vortex_dev->base_addr + EL3_CMD);
-		release_region(root_vortex_dev->base_addr, VORTEX_TOTAL_SIZE);
+		release_region(root_vortex_dev->base_addr,
+					   pci_tbl[vp->chip_id].io_size);
 		kfree(root_vortex_dev);
+		kfree(vp->priv_addr);
 		root_vortex_dev = next_dev;
 	}
 }
@@ -2166,7 +2128,7 @@
  * Local variables:
  *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
  *  SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c"
- *  compile-command-alt1: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c -o 3c59x_cb.o"
+ *  cardbus-compile-command: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c -o 3c575_cb.o -I/usr/src/pcmcia-cs-3.0.5/include/"
  *  c-indent-level: 4
  *  c-basic-offset: 4
  *  tab-width: 4
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/Makefile linux/drivers/net/Makefile
--- linux.vanilla/drivers/net/Makefile	Sun Dec  6 00:14:39 1998
+++ linux/drivers/net/Makefile	Fri Jan 29 15:38:50 1999
@@ -682,8 +682,8 @@
 clean:
 	rm -f core *.o *.a *.s
 
-rcpci.o: rcpci45.o rcmtl.o
-	$(LD) -r -o rcpci.o rcpci45.o rcmtl.o
+rcpci.o: rcpci45.o rclanmtl.o
+	$(LD) -r -o rcpci.o rcpci45.o rclanmtl.o
 
 wd.o:	wd.c CONFIG
 	$(CC) $(CPPFLAGS) $(CFLAGS) $(WD_OPTS) -c $<
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/README.rcpci linux/drivers/net/README.rcpci
--- linux.vanilla/drivers/net/README.rcpci	Thu Jan  1 01:00:00 1970
+++ linux/drivers/net/README.rcpci	Fri Jan 29 14:04:32 1999
@@ -0,0 +1,79 @@
+
+Application Information
+
+
+The included application, called "rcc" (for RedCreek Control), is an 
+example of a user-space application (i.e., not running within kernel
+space).  It issues ioctl commands to communicate with the PCI driver.  
+It can currently report any of the following information:
+
+   - PCI driver information ("getinfo")
+   - card statistics ("getstats")
+   - card's ip address & netmask ("getipnmask")
+   - card's mac address ("getmac")
+   - current speed ("getspeed")
+   - firmware version string ("getfirmware")
+   - status of the link (up or down) ("getstatus")
+
+Also, it can "set" the following parameters:
+
+   - IP and mask
+   - mac address
+   - link speed
+   - promiscuous mode
+
+Example:  rcc eth1 setipnmask="192.168.254.254 255.255.255.0"
+
+Note: rcc's command line parser is very basic. If you type the
+command incorrectly, it might result in a core dump.
+
+This program needs to run as root, to avoid encountering permission 
+problems.  An alternative is to change the permission and ownership
+so that it runs as a setuid root process (for example, "chown 
+root.root rcc; chmod u+s rcc").
+
+
+
+Quick PCI driver background
+
+
+The adapter has its own IP and mac addresses which you have to
+assign using the RedCreek manager (assuming the adapter is
+running 3.X firmware).  Your linux box will not know anything 
+about the adapter's IP address -- ie, the adapter will show up 
+as a regular nic.  You will assign the linux box IP address using 
+the "ifconfig" command, as mentioned below.
+
+
+To compile the driver, simply type "make".
+This, of course, assumes that you have GNU compiler environment
+already setup on a linux box.  The .c and .h files were copied 
+to a dos filesystem (the floppy), so you may have to use "dos2unix" to 
+convert it back to a unix text file.  Keep in mind that the driver
+currently works with kernels 2.0.X only.  Furthermore, it was only
+tested with kernel 2.0.34.
+
+To load the driver:
+
+"insmod rcpci"
+
+The adapter will show up as a regular nic.  Thus, if you have only
+one nic (the pci card) in your box, you would at this point configure
+it with the following commands:
+
+mandatory:
+"ifconfig eth0 <your linux box IP address (NOT the IP address of the 
+                adapter!>"
+"route add -net <your network address> eth0"
+
+optional (if you want to be able to access other networks):
+"route add default gw <your default gateway IP address> eth0"
+
+Done.  Type "ifconfig" to see "eth0" and the packet count, as well
+as the IP address, net mask, etc.
+
+To unload the driver, you first have to shutdown the interface:
+
+"ifconfig eth0 down"
+
+Then you unload the driver with "rmmod rcpci".
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/Space.c linux/drivers/net/Space.c
--- linux.vanilla/drivers/net/Space.c	Sun Dec  6 00:14:39 1998
+++ linux/drivers/net/Space.c	Mon Dec 14 18:32:50 1998
@@ -52,6 +52,7 @@
 extern int express_probe(struct device *);
 extern int eepro_probe(struct device *);
 extern int el3_probe(struct device *);
+extern int tc515_probe(struct device *);
 extern int at1500_probe(struct device *);
 extern int at1700_probe(struct device *);
 extern int fmv18x_probe(struct device *);
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/at1700.c linux/drivers/net/at1700.c
--- linux.vanilla/drivers/net/at1700.c	Sun Jun 21 18:41:04 1998
+++ linux/drivers/net/at1700.c	Mon Dec 14 18:33:02 1998
@@ -595,7 +595,6 @@
 /* The inverse routine to net_open(). */
 static int net_close(struct device *dev)
 {
-	struct net_local *lp = (struct net_local *)dev->priv;
 	int ioaddr = dev->base_addr;
 
 	dev->tbusy = 1;
@@ -605,14 +604,6 @@
 	outb(0xda, ioaddr + CONFIG_0);
 
 	/* No statistic counters on the chip to update. */
-
-#if 0
-	/* Disable the IRQ on boards where it is feasible. */
-	if (lp->jumpered) {
-		outb(0x00, ioaddr + IOCONFIG1);
-		free_irq(dev->irq, dev);
-	}
-#endif
 
 	/* Power-down the chip.  Green, green, green! */
 	outb(0x00, ioaddr + CONFIG_1);
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/de4x5.c linux/drivers/net/de4x5.c
--- linux.vanilla/drivers/net/de4x5.c	Sun Dec  6 00:14:39 1998
+++ linux/drivers/net/de4x5.c	Fri Jan 29 14:17:37 1999
@@ -404,11 +404,12 @@
 			   alignment for Alpha's and avoid their unaligned
 			   access traps. This flag is merely for log messages:
 			   should do something more definitive though...
+      0.5352 30-Dec-98    Fix driver recognition of the newer DECchips.
 
     =========================================================================
 */
 
-static const char *version = "de4x5.c:V0.5351 1998/10/4 davies@maniac.ultranet.com\n";
+static const char *version = "de4x5.c:V0.5352 1998/12/30 davies@maniac.ultranet.com\n";
 
 #include <linux/module.h>
 
@@ -769,7 +770,7 @@
     int tx_new, tx_old;                     /* TX descriptor ring pointers  */
     char setup_frame[SETUP_FRAME_LEN];      /* Holds MCA and PA info.       */
     char frame[64];                         /* Min sized packet for loopback*/
-    struct net_device_stats stats;           /* Public stats                 */
+    struct net_device_stats stats;          /* Public stats                 */
     struct {
 	u_int bins[DE4X5_PKT_STAT_SZ];      /* Private stats counters       */
 	u_int unicast;
@@ -1356,7 +1357,6 @@
     ** Re-initialize the DE4X5... 
     */
     status = de4x5_init(dev);
-    
     lp->state = OPEN;
     de4x5_dbg_open(dev);
     
@@ -1599,7 +1599,7 @@
     DISABLE_IRQs;                        /* Ensure non re-entrancy */
 
     if (test_and_set_bit(MASK_INTERRUPTS, (void*) &lp->interrupt))
-	printk("%s: Re-entering the interrupt handler.\n", dev->name);
+        printk("%s: Re-entering the interrupt handler.\n", dev->name);
 
 #if	LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8))
     synchronize_irq();
@@ -2075,7 +2075,9 @@
 	    irq = inb(EISA_REG0);
 	    irq = de4x5_irq[(irq >> 1) & 0x03];
 
-	    if (is_DC2114x) device |= (cfrv & CFRV_RN);
+	    if (is_DC2114x) {
+		device = ((cfrv & CFRV_RN) < DC2114x_BRK ? DC21142 : DC21143);
+	    }
 	    lp->chipset = device;
 
 	    /* Write the PCI Configuration Registers */
@@ -2180,7 +2182,9 @@
 	    lp->bus_num = pb;
 	    
 	    /* Set the chipset information */
-	    if (is_DC2114x) device |= (cfrv & CFRV_RN);
+	    if (is_DC2114x) {
+		device = ((cfrv & CFRV_RN) < DC2114x_BRK ? DC21142 : DC21143);
+	    }
 	    lp->chipset = device;
 
 	    /* Get the board I/O address (64 bits on sparc64) */
@@ -2291,7 +2295,9 @@
 	lp->bus_num = pb;
 	    
 	/* Set the chipset information */
-	if (is_DC2114x) device |= (cfrv & CFRV_RN);
+	if (is_DC2114x) {
+	    device = ((cfrv & CFRV_RN) < DC2114x_BRK ? DC21142 : DC21143);
+	}
 	lp->chipset = device;
 
 	/* Get the board I/O address (64 bits on sparc64) */
@@ -5657,7 +5663,7 @@
 	cli();
 	copy_to_user(ioc->data, &lp->pktStats, ioc->len); 
 	sti();
-	
+
 	break;
     case DE4X5_CLR_STATS:            /* Zero out the driver statistics */
 	if (suser()) {
@@ -5830,6 +5836,12 @@
 	if (!mdev) mdev = p;
 
 	if (register_netdev(p) != 0) {
+	    struct de4x5_private *lp = (struct de4x5_private *)p->priv;
+	    if (lp) {
+		release_region(p->base_addr, (lp->bus == PCI ? 
+					      DE4X5_PCI_TOTAL_SIZE :
+					      DE4X5_EISA_TOTAL_SIZE));
+	    }
 	    kfree(p);
 	} else {
 	    status = 0;                 /* At least one adapter will work */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/de4x5.h linux/drivers/net/de4x5.h
--- linux.vanilla/drivers/net/de4x5.h	Sun Jun 21 18:41:05 1998
+++ linux/drivers/net/de4x5.h	Fri Jan 29 14:17:37 1999
@@ -121,6 +121,7 @@
 #define DC2114x     DC2114x_DID
 #define DC21142     (DC2114x_DID | 0x0010)
 #define DC21143     (DC2114x_DID | 0x0030)
+#define DC2114x_BRK 0x0020           /* CFRV break between DC21142 & DC21143 */
 
 #define is_DC21040 ((vendor == DC21040_VID) && (device == DC21040_DID))
 #define is_DC21041 ((vendor == DC21041_VID) && (device == DC21041_DID))
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/depca.c linux/drivers/net/depca.c
--- linux.vanilla/drivers/net/depca.c	Sun Jun 21 18:41:04 1998
+++ linux/drivers/net/depca.c	Sun Dec  6 03:16:27 1998
@@ -154,7 +154,20 @@
     the 'memory autoprobe' picking the wrong shared memory  (for the case of
     2 depca's in a PC).
 
+    ************************************************************************
+    Support for MCA EtherWORKS cards added 11-3-98.
+    Verified to work with up to 2 DE212 cards in a system (although not
+      fully stress-tested).  
+
+    Currently known bugs/limitations:
+
+    Note:  with the MCA stuff as a module, it trusts the MCA configuration,
+           not the command line for IRQ and memory address.  You can
+           specify them if you want, but it will throw your values out.
+           You still have to pass the IO address it was configured as
+           though.
 
+    ************************************************************************
     TO DO:
     ------
 
@@ -202,11 +215,16 @@
       0.422   29-Apr-96	  Fix depca_hw_init() bug <jari@markkus2.fimr.fi>
       0.423    7-Jun-96   Fix module load bug <kmg@barco.be>
       0.43    16-Aug-96   Update alloc_device() to conform to de4x5.c
+      0.44     1-Sep-97   Fix *_probe() to test check_region() first - bug
+                           reported by <mmogilvi@elbert.uccs.edu>
+      0.45     3-Nov-98   Added support for MCA EtherWORKS (DE210/DE212) cards
+                           by <tymm@computer.org> 
+      0.451    5-Nov-98   Fixed mca stuff cuz I'm a dummy. <tymm@computer.org>
 
     =========================================================================
 */
 
-static const char *version = "depca.c:v0.43 96/8/16 davies@maniac.ultranet.com\n";
+static const char *version = "depca.c:v0.451 1998/11/14 davies@maniac.ultranet.com\n";
 
 #include <linux/module.h>
 
@@ -233,6 +251,10 @@
 #include <linux/unistd.h>
 #include <linux/ctype.h>
 
+#ifdef CONFIG_MCA
+#include <linux/mca.h>
+#endif
+
 #include "depca.h"
 
 #ifdef DEPCA_DEBUG
@@ -280,15 +302,20 @@
 static short mem_chkd = 0;
 
 /*
+** Adapter ID for the MCA EtherWORKS DE210/212 adapter
+*/
+#define DE212_ID 0x6def
+
+/*
 ** Name <-> Adapter mapping
 */
 #define DEPCA_SIGNATURE {"DEPCA",\
 			 "DE100","DE101",\
                          "DE200","DE201","DE202",\
-			 "DE210",\
+			 "DE210","DE212",\
                          "DE422",\
                          ""}
-static enum {DEPCA, de100, de101, de200, de201, de202, de210, de422, unknown} adapter;
+static enum {DEPCA, de100, de101, de200, de201, de202, de210, de212, de422, unknown} adapter;
 
 /*
 ** Miscellaneous info...
@@ -342,7 +369,7 @@
     char devname[DEPCA_STRLEN];    /* Device Product String                  */
     char adapter_name[DEPCA_STRLEN];/* /proc/ioports string                  */
     char adapter;                  /* Adapter type                           */
-    struct depca_rx_desc *rx_ring; /* Pointer to start of RX descriptor ring */
+    char mca_slot;                 /* MCA slot, if MCA else -1               */    struct depca_rx_desc *rx_ring; /* Pointer to start of RX descriptor ring */
     struct depca_tx_desc *tx_ring; /* Pointer to start of TX descriptor ring */
     struct depca_init	init_block;/* Shadow Initialization block            */
     char *rx_memcpy[NUM_RX_DESC];  /* CPU virt address of sh'd memory buffs  */
@@ -393,7 +420,7 @@
 /*
 ** Private functions
 */
-static int    depca_hw_init(struct device *dev, u_long ioaddr);
+static int    depca_hw_init(struct device *dev, u_long ioaddr, int mca_slot);
 static void   depca_init_ring(struct device *dev);
 static int    depca_rx(struct device *dev);
 static int    depca_tx(struct device *dev);
@@ -407,6 +434,9 @@
 static void   SetMulticastFilter(struct device *dev);
 static void   isa_probe(struct device *dev, u_long iobase);
 static void   eisa_probe(struct device *dev, u_long iobase);
+#ifdef CONFIG_MCA      
+static void   mca_probe(struct device *dev, u_long iobase);
+#endif
 static struct device *alloc_device(struct device *dev, u_long iobase);
 static int    depca_dev_index(char *s);
 static struct device *insert_device(struct device *dev, u_long iobase, int (*init)(struct device *));
@@ -441,7 +471,8 @@
 
 
 
-int depca_probe(struct device *dev)
+int
+depca_probe(struct device *dev)
 {
   int tmp = num_depcas, status = -ENODEV;
   u_long iobase = dev->base_addr;
@@ -450,6 +481,9 @@
     printk("Autoprobing is not supported when loading a module based driver.\n");
     status = -EIO;
   } else {
+#ifdef CONFIG_MCA      
+    mca_probe(dev, iobase);
+#endif
     isa_probe(dev, iobase);
     eisa_probe(dev, iobase);
 
@@ -472,7 +506,7 @@
 }
 
 static int
-depca_hw_init(struct device *dev, u_long ioaddr)
+depca_hw_init(struct device *dev, u_long ioaddr, int mca_slot)
 {
   struct depca_private *lp;
   int i, j, offset, netRAM, mem_len, status=0;
@@ -495,7 +529,10 @@
     if ((adapter != unknown) && mem_start) {        /* found a DEPCA device */
       dev->base_addr = ioaddr;
 
-      if ((ioaddr&0x0fff)==DEPCA_EISA_IO_PORTS) {/* EISA slot address */
+      if (mca_slot != -1) {
+	printk("%s: %s at 0x%04lx (MCA slot %d)", dev->name, name, 
+	                                                   ioaddr, mca_slot);
+      } else if ((ioaddr & 0x0fff) == DEPCA_EISA_IO_PORTS) { /* EISA slot address */
 	printk("%s: %s at 0x%04lx (EISA slot %d)", 
 	                    dev->name, name, ioaddr, (int)((ioaddr>>12)&0x0f));
       } else {                             /* ISA port address */
@@ -541,7 +578,8 @@
 	  lp = (struct depca_private *)dev->priv;
 	  memset((char *)dev->priv, 0, sizeof(struct depca_private));
 	  lp->adapter = adapter;
-	  sprintf(lp->adapter_name,"%s (%s)", name, dev->name);
+	  lp->mca_slot = mca_slot;
+ 	  sprintf(lp->adapter_name,"%s (%s)", name, dev->name);
 	  request_region(ioaddr, DEPCA_TOTAL_SIZE, lp->adapter_name);
 
 	  /* Initialisation Block */
@@ -603,6 +641,7 @@
 	    case de201:
 	    case de202:
 	    case de210:
+	    case de212:
 	      depca_irq = de2xx_irq;
 	      break;
 	    case de422:
@@ -1205,10 +1244,161 @@
   return;
 }
 
+#ifdef CONFIG_MCA
+/*
+** Microchannel bus I/O device probe
+*/
+static void
+mca_probe(struct device *dev, u_long ioaddr)
+{
+    unsigned char pos[2];
+    unsigned char where;
+    unsigned long iobase;
+    int irq;
+    int slot = 0;
+
+    /*
+    ** See if we've been here before.
+    */
+    if ((!ioaddr && autoprobed) || (ioaddr && !loading_module)) return;   
+
+    if (MCA_bus) {
+	/*
+	** Search for the adapter.  If an address has been given, search 
+	** specifically for the card at that address.  Otherwise find the
+	** first card in the system.
+	*/
+	while ((dev!=NULL) && 
+	       ((slot=mca_find_adapter(DE212_ID, slot)) != MCA_NOTFOUND)) {
+	    pos[0] = mca_read_stored_pos(slot, 2);
+	    pos[1] = mca_read_stored_pos(slot, 3);
+
+	    /*
+	    ** IO of card is handled by bits 1 and 2 of pos0.    
+	    **
+	    **    bit2 bit1    IO
+	    **       0    0    0x2c00
+	    **       0    1    0x2c10
+	    **       1    0    0x2c20
+	    **       1    1    0x2c30
+	    */
+	    where = (pos[0] & 6) >> 1;
+	    iobase = 0x2c00 + (0x10 * where);
+	       
+	    if ((ioaddr) && (ioaddr != iobase)) {
+		/*
+		** Card was found, but not at the right IO location. Continue 
+		** scanning from the next MCA slot up for another card.
+		*/
+		slot++;
+		continue;
+	    }
+
+	    /*
+	    ** Found the adapter we were looking for. Now start setting it up.
+	    ** 
+	    ** First work on decoding the IRQ.  It's stored in the lower 4 bits
+	    ** of pos1.  Bits are as follows (from the ADF file):
+	    **
+	    **      Bits           
+	    **   3   2   1   0    IRQ 
+	    **   --------------------
+	    **   0   0   1   0     5
+	    **   0   0   0   1     9
+	    **   0   1   0   0    10
+	    **   1   0   0   0    11
+	    **/
+	    where = pos[1] & 0x0f;
+	    switch(where) {       
+	    case 1:
+		irq = 9;
+		break;
+	    case 2:
+		irq = 5;
+		break;
+	    case 4:
+		irq = 10;
+		break;
+	    case 8:
+		irq = 11;
+		break;
+	    default:
+		printk("%s: mca_probe IRQ error.  You should never get here (%d).\n", dev->name, where);
+		return;
+	    }  
+ 
+	    /*
+	    ** Shared memory address of adapter is stored in bits 3-5 of pos0.
+	    ** They are mapped as follows:
+	    **
+	    **    Bit
+	    **   5  4  3       Memory Addresses
+	    **   0  0  0       C0000-CFFFF (64K)
+	    **   1  0  0       C8000-CFFFF (32K)
+	    **   0  0  1       D0000-DFFFF (64K)
+	    **   1  0  1       D8000-DFFFF (32K)
+	    **   0  1  0       E0000-EFFFF (64K)
+	    **   1  1  0       E8000-EFFFF (32K)
+	    */ 
+	    where = (pos[0] & 0x18) >> 3; 
+	    mem = 0xc0000 + (where * 0x10000);
+	    if (pos[0] & 0x20) {
+		mem += 0x8000;
+	    }
+       
+	    /*
+	    ** Get everything allocated and initialized...  (almost just
+	    ** like the ISA and EISA probes)
+	    */
+	    if (DevicePresent(iobase) != 0) {
+		/*
+		** If the MCA configuration says the card should be here,
+		** it really should be here.
+		*/
+		printk(KERN_ERR "%s: MCA reports card at 0x%lx but it is not
+responding.\n", dev->name, iobase);
+	    }
+       
+	    if (check_region(iobase, DEPCA_TOTAL_SIZE) == 0) {
+		if ((dev = alloc_device(dev, iobase)) != NULL) {
+		    dev->irq = irq;
+		    if (depca_hw_init(dev, iobase, slot) == 0) {       
+			/*
+			** Adapter initialized correctly:  Name it in
+			** /proc/mca.
+			*/
+			mca_set_adapter_name(slot, "DE210/212 Ethernet Adapter");
+			mca_mark_as_used(slot);
+			num_depcas++;
+		    }
+		    num_eth++;
+		}
+	    } else if (autoprobed) {
+		printk(KERN_WARNING "%s: region already allocated at 0x%04lx.\n", dev->name, iobase);
+	    }
+       
+	    /*
+	    ** If this is a probe by a module, return after setting up the
+	    ** given card.
+	    */
+	    if (ioaddr) return;
+       
+	    /*
+	    ** Set up to check the next slot and loop.
+	    */
+	    slot++;
+	}
+    }
+
+    return;
+}
+#endif
+
 /*
 ** ISA bus I/O device probe
 */
-static void isa_probe(struct device *dev, u_long ioaddr)
+static void
+isa_probe(struct device *dev, u_long ioaddr)
 {
   int i = num_depcas, maxSlots;
   s32 ports[] = DEPCA_IO_PORTS;
@@ -1225,17 +1415,17 @@
   }
 
   for (; (i<maxSlots) && (dev!=NULL) && ports[i]; i++) {
-    if (DevicePresent(ports[i]) == 0) { 
-      if (check_region(ports[i], DEPCA_TOTAL_SIZE) == 0) {
+    if (check_region(ports[i], DEPCA_TOTAL_SIZE) == 0) {
+      if (DevicePresent(ports[i]) == 0) { 
 	if ((dev = alloc_device(dev, ports[i])) != NULL) {
-	  if (depca_hw_init(dev, ports[i]) == 0) {
+	  if (depca_hw_init(dev, ports[i], -1) == 0) {
 	    num_depcas++;
 	  }
 	  num_eth++;
 	}
-      } else if (autoprobed) {
-	printk("%s: region already allocated at 0x%04x.\n", dev->name,ports[i]);
       }
+    } else if (autoprobed) {
+      printk("%s: region already allocated at 0x%04x.\n", dev->name,ports[i]);
     }
   }
 
@@ -1246,7 +1436,8 @@
 ** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually
 ** the motherboard. Upto 15 EISA devices are supported.
 */
-static void eisa_probe(struct device *dev, u_long ioaddr)
+static void
+eisa_probe(struct device *dev, u_long ioaddr)
 {
   int i, maxSlots;
   u_long iobase;
@@ -1267,19 +1458,19 @@
   if ((iobase & 0x0fff) == 0) iobase += DEPCA_EISA_IO_PORTS;
 
   for (; (i<maxSlots) && (dev!=NULL); i++, iobase+=EISA_SLOT_INC) {
-    if (EISA_signature(name, EISA_ID)) {
-      if (DevicePresent(iobase) == 0) { 
-	if (check_region(iobase, DEPCA_TOTAL_SIZE) == 0) {
+    if (check_region(iobase, DEPCA_TOTAL_SIZE) == 0) {
+      if (EISA_signature(name, EISA_ID)) {
+	if (DevicePresent(iobase) == 0) { 
 	  if ((dev = alloc_device(dev, iobase)) != NULL) {
-	    if (depca_hw_init(dev, iobase) == 0) {
+	    if (depca_hw_init(dev, iobase, -1) == 0) {
 	      num_depcas++;
 	    }
 	    num_eth++;
 	  }
-	} else if (autoprobed) {
-	  printk("%s: region already allocated at 0x%04lx.\n",dev->name,iobase);
 	}
       }
+    } else if (autoprobed) {
+      printk("%s: region already allocated at 0x%04lx.\n",dev->name,iobase);
     }
   }
 
@@ -1683,7 +1874,7 @@
 /*
 ** Perform IOCTL call functions here. Some are privileged operations and the
 ** effective uid is checked in those cases.
-** All MCA IOCTLs will not work here and are for testing purposes only.
+** All multicast IOCTLs will not work here and are for testing purposes only.
 */
 static int depca_ioctl(struct device *dev, struct ifreq *rq, int cmd)
 {
@@ -1703,126 +1894,103 @@
       tmp.addr[i] = dev->dev_addr[i];
     }
     ioc->len = ETH_ALEN;
-    if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) {
-      memcpy_tofs(ioc->data, tmp.addr, ioc->len);
-    }
-
+    if (verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len)) return -EFAULT;
+    memcpy_tofs(ioc->data, tmp.addr, ioc->len);
     break;
+
   case DEPCA_SET_HWADDR:             /* Set the hardware address */
-    if (suser()) {
-      if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN))) {
-	memcpy_fromfs(tmp.addr,ioc->data,ETH_ALEN);
-	for (i=0; i<ETH_ALEN; i++) {
-	  dev->dev_addr[i] = tmp.addr[i];
-	}
-	while(dev->tbusy);              /* Stop ring access */
-	set_bit(0, (void*)&dev->tbusy);
-	while(lp->tx_old != lp->tx_new);/* Wait for the ring to empty */
-
-	STOP_DEPCA;                     /* Temporarily stop the depca.  */
-	depca_init_ring(dev);           /* Initialize the descriptor rings */
-	LoadCSRs(dev);                  /* Reload CSR3 */
-	InitRestartDepca(dev);          /* Resume normal operation. */
-	dev->tbusy = 0;                 /* Unlock the TX ring */
-      }
-    } else {
-      status = -EPERM;
+    if (!suser()) return -EPERM;
+    if (verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN)) return -EFAULT;
+    memcpy_fromfs(tmp.addr,ioc->data,ETH_ALEN);
+    for (i=0; i<ETH_ALEN; i++) {
+      dev->dev_addr[i] = tmp.addr[i];
     }
+    while(dev->tbusy) barrier();        /* Stop ring access */
+    set_bit(0, (void*)&dev->tbusy);
+    while(lp->tx_old != lp->tx_new);    /* Wait for the ring to empty */
 
+    STOP_DEPCA;                         /* Temporarily stop the depca.  */
+    depca_init_ring(dev);               /* Initialize the descriptor rings */
+    LoadCSRs(dev);                      /* Reload CSR3 */
+    InitRestartDepca(dev);              /* Resume normal operation. */
+    dev->tbusy = 0;                     /* Unlock the TX ring */
     break;
+
   case DEPCA_SET_PROM:               /* Set Promiscuous Mode */
-    if (suser()) {
-      while(dev->tbusy);                /* Stop ring access */
-      set_bit(0, (void*)&dev->tbusy);
-      while(lp->tx_old != lp->tx_new);  /* Wait for the ring to empty */
-
-      STOP_DEPCA;                       /* Temporarily stop the depca.  */
-      depca_init_ring(dev);             /* Initialize the descriptor rings */
-      lp->init_block.mode |= PROM;      /* Set promiscuous mode */
-
-      LoadCSRs(dev);                    /* Reload CSR3 */
-      InitRestartDepca(dev);            /* Resume normal operation. */
-      dev->tbusy = 0;                   /* Unlock the TX ring */
-    } else {
-      status = -EPERM;
-    }
+    if (!suser()) return -EPERM;
+    while(dev->tbusy) barrier();        /* Stop ring access */
+    set_bit(0, (void*)&dev->tbusy);
+    while(lp->tx_old != lp->tx_new);    /* Wait for the ring to empty */
 
+    STOP_DEPCA;                         /* Temporarily stop the depca.  */
+    depca_init_ring(dev);               /* Initialize the descriptor rings */
+    lp->init_block.mode |= PROM;        /* Set promiscuous mode */
+
+    LoadCSRs(dev);                      /* Reload CSR3 */
+    InitRestartDepca(dev);              /* Resume normal operation. */
+    dev->tbusy = 0;                     /* Unlock the TX ring */
     break;
+
   case DEPCA_CLR_PROM:               /* Clear Promiscuous Mode */
-    if (suser()) {
-      while(dev->tbusy);                /* Stop ring access */
-      set_bit(0, (void*)&dev->tbusy);
-      while(lp->tx_old != lp->tx_new);  /* Wait for the ring to empty */
-
-      STOP_DEPCA;                       /* Temporarily stop the depca.  */
-      depca_init_ring(dev);             /* Initialize the descriptor rings */
-      lp->init_block.mode &= ~PROM;     /* Clear promiscuous mode */
-
-      LoadCSRs(dev);                    /* Reload CSR3 */
-      InitRestartDepca(dev);            /* Resume normal operation. */
-      dev->tbusy = 0;                   /* Unlock the TX ring */
-    } else {
-      status = -EPERM;
-    }
+    if (!suser()) return -EPERM;
+    while(dev->tbusy) barrier();        /* Stop ring access */
+    set_bit(0, (void*)&dev->tbusy);
+    while(lp->tx_old != lp->tx_new);    /* Wait for the ring to empty */
 
+    STOP_DEPCA;                         /* Temporarily stop the depca.  */
+    depca_init_ring(dev);               /* Initialize the descriptor rings */
+    lp->init_block.mode &= ~PROM;       /* Clear promiscuous mode */
+
+    LoadCSRs(dev);                      /* Reload CSR3 */
+    InitRestartDepca(dev);              /* Resume normal operation. */
+    dev->tbusy = 0;                     /* Unlock the TX ring */
     break;
+
   case DEPCA_SAY_BOO:                /* Say "Boo!" to the kernel log file */
     printk("%s: Boo!\n", dev->name);
-
     break;
+
   case DEPCA_GET_MCA:                /* Get the multicast address table */
     ioc->len = (HASH_TABLE_LEN >> 3);
-    if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
-      memcpy_tofs(ioc->data, lp->init_block.mcast_table, ioc->len); 
-    }
-
+    if (verify_area(VERIFY_WRITE, ioc->data, ioc->len)) return -EFAULT;
+    memcpy_tofs(ioc->data, lp->init_block.mcast_table, ioc->len); 
     break;
-  case DEPCA_SET_MCA:                /* Set a multicast address */
-    if (suser()) {
-      if (!(status=verify_area(VERIFY_READ, ioc->data, ETH_ALEN*ioc->len))) {
-	memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN * ioc->len);
-	set_multicast_list(dev);
-      }
-    } else {
-      status = -EPERM;
-    }
 
+  case DEPCA_SET_MCA:                /* Set a multicast address */
+    if (!suser()) return -EPERM;
+    if (verify_area(VERIFY_READ, ioc->data, ETH_ALEN*ioc->len)) return -EFAULT;
+    memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN * ioc->len);
+    set_multicast_list(dev);
     break;
-  case DEPCA_CLR_MCA:                /* Clear all multicast addresses */
-    if (suser()) {
-      set_multicast_list(dev);
-    } else {
-      status = -EPERM;
-    }
 
+  case DEPCA_CLR_MCA:                /* Clear all multicast addresses */
+    if (!suser()) return -EPERM;
+    set_multicast_list(dev);
     break;
+
   case DEPCA_MCA_EN:                 /* Enable pass all multicast addressing */
-    if (suser()) {
+    if (!suser()) return -EPERM;
       set_multicast_list(dev);
-    } else {
-      status = -EPERM;
-    }
-
     break;
+
   case DEPCA_GET_STATS:              /* Get the driver statistics */
     cli();
     ioc->len = sizeof(lp->pktStats);
-    if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
-      memcpy_tofs(ioc->data, &lp->pktStats, ioc->len); 
+    if (verify_area(VERIFY_WRITE, ioc->data, ioc->len)) {
+	status = -EFAULT;
+    } else {
+	memcpy_tofs(ioc->data, &lp->pktStats, ioc->len); 
     }
     sti();
-
     break;
-  case DEPCA_CLR_STATS:              /* Zero out the driver statistics */
-    if (suser()) {
-      cli();
-      memset(&lp->pktStats, 0, sizeof(lp->pktStats));
-      sti();
-    } else {
-      status = -EPERM;
-    }
 
+  case DEPCA_CLR_STATS:              /* Zero out the driver statistics */
+    if (!suser()) return -EPERM;
+    cli();
+    memset(&lp->pktStats, 0, sizeof(lp->pktStats));
+    sti();
     break;
+
   case DEPCA_GET_REG:                /* Get the DEPCA Registers */
     i=0;
     tmp.sval[i++] = inw(DEPCA_NICSR);
@@ -1830,25 +1998,25 @@
     tmp.sval[i++] = inw(DEPCA_DATA);
     memcpy(&tmp.sval[i], &lp->init_block, sizeof(struct depca_init));
     ioc->len = i+sizeof(struct depca_init);
-    if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
-      memcpy_tofs(ioc->data, tmp.addr, ioc->len);
-    }
-
+    if (verify_area(VERIFY_WRITE, ioc->data, ioc->len)) return -EFAULT;
+    memcpy_tofs(ioc->data, tmp.addr, ioc->len);
     break;
+
   default:
-    status = -EOPNOTSUPP;
+    return -EOPNOTSUPP;
   }
 
   return status;
 }
 
 #ifdef MODULE
-static char devicename[9] = { 0, };
+static char devicename[9] = {0,};
 static struct device thisDepca = {
   devicename,  /* device name is inserted by /linux/drivers/net/net_init.c */
   0, 0, 0, 0,
   0x200, 7,    /* I/O address, IRQ */
-  0, 0, 0, NULL, depca_probe };
+  0, 0, 0, NULL, depca_probe
+};
 
 static int irq=7;	/* EDIT THESE LINE FOR YOUR CONFIGURATION */
 static int io=0x200;    /* Or use the irq= io= options to insmod */
@@ -1869,8 +2037,13 @@
 void
 cleanup_module(void)
 {
-  if (thisDepca.priv) {
-    kfree(thisDepca.priv);
+  struct depca_private *lp = thisDepca.priv;
+  if (lp) {
+#ifdef CONFIG_MCA      
+    if(lp->mca_slot != -1)
+      mca_mark_as_unused(lp->mca_slot);
+#endif                 
+    kfree(lp);
     thisDepca.priv = NULL;
   }
   thisDepca.irq=0;
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/dgrs.c linux/drivers/net/dgrs.c
--- linux.vanilla/drivers/net/dgrs.c	Fri Jul 24 17:28:59 1998
+++ linux/drivers/net/dgrs.c	Mon Dec 14 18:33:16 1998
@@ -1254,10 +1254,11 @@
 )
 {
 	DGRS_PRIV	*priv;
-	int		i;
 
 	#ifdef MODULE
 	{
+		int		i;
+
 		/* Allocate and fill new device structure. */
 		int dev_size = sizeof(struct device) + sizeof(DGRS_PRIV);
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/dummy.c linux/drivers/net/dummy.c
--- linux.vanilla/drivers/net/dummy.c	Sun Jun 21 18:41:04 1998
+++ linux/drivers/net/dummy.c	Fri Jan 29 14:29:36 1999
@@ -70,6 +70,10 @@
 	return 0;
 }
 
+static int dummy_rebuild(void *eth, struct device *dev, unsigned long raddr, struct sk_buff *skb)
+{
+	return 0;
+}
 
 int dummy_init(struct device *dev)
 {
@@ -94,6 +98,7 @@
 	/* Fill in the fields of the device structure with ethernet-generic values. */
 	ether_setup(dev);
 	dev->flags |= IFF_NOARP;
+	dev->rebuild_header = dummy_rebuild;
 
 	return 0;
 }
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/eepro100.c linux/drivers/net/eepro100.c
--- linux.vanilla/drivers/net/eepro100.c	Sun Dec  6 00:14:40 1998
+++ linux/drivers/net/eepro100.c	Sun Dec  6 03:34:33 1998
@@ -7,7 +7,7 @@
 	of the GNU Public License, incorporated herein by reference.
 
 	This driver is for the Intel EtherExpress Pro 100B boards.
-	It should work with other i82557 boards (if any others exist).
+	It should work with other i82557 and i82558 boards.
 	To use a built-in driver, install as drivers/net/eepro100.c.
 	To use as a module, use the compile-command at the end of the file.
 
@@ -15,11 +15,13 @@
 	Center of Excellence in Space Data and Information Sciences
 	   Code 930.5, NASA Goddard Space Flight Center, Greenbelt MD 20771
 	For updates see
-	<base href="http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html">
+		http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html
+	There is also a mailing list based at
+		linux-eepro100@cesdis.gsfc.nasa.gov
 */
 
 static const char *version =
-"eepro100.c:v0.99B 4/7/98 Donald Becker linux-eepro100@cesdis.gsfc.nasa.gov\n";
+"eepro100.c:v1.05 10/16/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html\n";
 
 /* A few user-configurable values that apply to all boards.
    First set are undocumented and spelled per Intel recommendations. */
@@ -41,6 +43,7 @@
 /* Maximum number of multicast addresses to filter (vs. rx-all-multicast) */
 static int multicast_filter_limit = 64;
 
+#include <linux/config.h>
 #ifdef MODULE
 #ifdef MODVERSIONS
 #include <linux/modversions.h>
@@ -53,20 +56,18 @@
 
 #include <linux/version.h>
 #include <linux/kernel.h>
-#include <linux/sched.h>
 #include <linux/string.h>
 #include <linux/timer.h>
-#include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
 #include <linux/malloc.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
-#include <linux/bios32.h>
-#include <asm/processor.h>		/* Processor type for cache alignment. */
+#if LINUX_VERSION_CODE < 0x20155
+#include <linux/bios32.h>		/* Ignore the bogus warning in 2.1.100+ */
+#endif
 #include <asm/bitops.h>
 #include <asm/io.h>
-#include <asm/dma.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -74,9 +75,9 @@
 #include <linux/delay.h>
 
 /* Unused in the 2.0.* version, but retained for documentation. */
-#if LINUX_VERSION_CODE > 0x20118
+#if LINUX_VERSION_CODE > 0x20118  &&  defined(MODULE)
 MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
-MODULE_DESCRIPTION("Intel i82557/i82558 EtherExpressPro driver");
+MODULE_DESCRIPTION("Intel i82557/i82558 PCI EtherExpressPro driver");
 MODULE_PARM(debug, "i");
 MODULE_PARM(options, "1-" __MODULE_STRING(8) "i");
 MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i");
@@ -95,6 +96,11 @@
 #if (LINUX_VERSION_CODE < 0x20123)
 #define test_and_set_bit(val, addr) set_bit(val, addr)
 #endif
+#if LINUX_VERSION_CODE < 0x20159
+#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE);
+#else
+#define dev_free_skb(skb) dev_kfree_skb(skb);
+#endif
 
 /* The total I/O port extent of the board.
    The registers beyond 0x18 only exist on the i82558. */
@@ -132,7 +138,7 @@
 Despite the extra space overhead in each receive skbuff, the driver must use
 the simplified Rx buffer mode to assure that only a single data buffer is
 associated with each RxFD. The driver implements this by reserving space
-for the Rx descriptor at the head of each Rx skbuff
+for the Rx descriptor at the head of each Rx skbuff.
 
 The Speedo-3 has receive and command unit base addresses that are added to
 almost all descriptor pointers.  The driver sets these to zero, so that all
@@ -147,10 +153,13 @@
 
 The driver must use the complex Tx command+descriptor mode in order to
 have a indirect pointer to the skbuff data section.  Each Tx command block
-(TxCB) is associated with a single, immediately appended Tx buffer descriptor
+(TxCB) is associated with two immediately appended Tx Buffer Descriptor
 (TxBD).  A fixed ring of these TxCB+TxBD pairs are kept as part of the
 speedo_private data structure for each adapter instance.
 
+The newer i82558 explicitly supports this structure, and can read the two
+TxBDs in the same PCI burst as the TxCB.
+
 This ring structure is used for all normal transmit packets, but the
 transmit packet descriptors aren't long enough for most non-Tx commands such
 as CmdConfigure.  This is complicated by the possibility that the chip has
@@ -181,10 +190,10 @@
 	doing the CU_RESUME
 		the chip processes the next-yet-valid post-final-command.
 So blindly sending a CU_RESUME is only safe if we do it immediately after
-erasing the previous CmdSuspend, without the possibility of an intervening
-delay.  Thus the resume command is always within the interrupts-disabled
-region.  This is a timing dependence, but handling this condition in a
-timing-independent way would considerably complicate the code.
+after erasing the previous CmdSuspend, without the possibility of an
+intervening delay.  Thus the resume command is always within the
+interrupts-disabled region.  This is a timing dependence, but handling this
+condition in a timing-independent way would considerably complicate the code.
 
 Note: In previous generation Intel chips, restarting the command unit was a
 notoriously slow process.  This is presumably no longer true.
@@ -243,11 +252,11 @@
 
 /* How to wait for the command unit to accept a command.
    Typically this takes 0 ticks. */
-static inline void wait_for_cmd_done(int cmd_ioaddr)
+static inline void wait_for_cmd_done(long cmd_ioaddr)
 {
-  short wait = 100;
-  do   ;
-  while(inb(cmd_ioaddr) && --wait >= 0);
+	int wait = 100;
+	do   ;
+	while(inb(cmd_ioaddr) && --wait >= 0);
 }
 
 /* Operational parameter that usually are not changed. */
@@ -306,17 +315,24 @@
 	u16 size;
 };
 
-/* Elements of the RxFD.status word. */
-#define RX_COMPLETE 0x8000
+/* Selected elements of the Tx/RxFD.status word. */
+enum RxFD_bits {
+	RxComplete=0x8000, RxOK=0x2000,
+	RxErrCRC=0x0800, RxErrAlign=0x0400, RxErrTooBig=0x0200, RxErrSymbol=0x0010,
+	RxEth2Type=0x0020, RxNoMatch=0x0004, RxNoIAMatch=0x0002,
+	StatusComplete=0x8000,
+};
 
 struct TxFD {					/* Transmit frame descriptor set. */
 	s32 status;
 	u32 link;					/* void * */
 	u32 tx_desc_addr;			/* Always points to the tx_buf_addr element. */
 	s32 count;					/* # of TBD (=1), Tx start thresh., etc. */
-	/* This constitutes a single "TBD" entry -- we only use one. */
-	u32 tx_buf_addr;			/* void *, frame to be transmitted.  */
-	s32 tx_buf_size;			/* Length of Tx frame. */
+	/* This constitutes two "TBD" entries -- we only use one. */
+	u32 tx_buf_addr0;			/* void *, frame to be transmitted.  */
+	s32 tx_buf_size0;			/* Length of Tx frame. */
+	u32 tx_buf_addr1;			/* void *, frame to be transmitted.  */
+	s32 tx_buf_size1;			/* Length of Tx frame. */
 };
 
 /* Elements of the dump_statistics block. This block must be lword aligned. */
@@ -358,10 +374,9 @@
 	long last_rx_time;			/* Last Rx, in jiffies, to handle Rx hang. */
 	unsigned int cur_rx, cur_tx;		/* The next free ring entry */
 	unsigned int dirty_rx, dirty_tx;	/* The ring entries to be free()ed. */
-	struct descriptor config_cmd;	/* A configure command, with header... */
-	u8 config_cmd_data[22];			/* .. and setup parameters. */
 	int mc_setup_frm_len;			 	/* The length of an allocated.. */
 	struct descriptor *mc_setup_frm; 	/* ..multicast setup frame. */
+	int mc_setup_busy;					/* Avoid double-use of setup frame. */
 	int in_interrupt;					/* Word-aligned dev->interrupt */
 	char rx_mode;						/* Current PROMISC/ALLMULTI setting. */
 	unsigned int tx_full:1;				/* The Tx queue is full. */
@@ -376,11 +391,16 @@
 /* The parameters for a CmdConfigure operation.
    There are so many options that it would be difficult to document each bit.
    We mostly use the default or recommended settings. */
-const char basic_config_cmd[22] = {
+const char i82557_config_cmd[22] = {
 	22, 0x08, 0, 0,  0, 0x80, 0x32, 0x03,  1, /* 1=Use MII  0=Use AUI */
 	0, 0x2E, 0,  0x60, 0,
 	0xf2, 0x48,   0, 0x40, 0xf2, 0x80, 		/* 0x40=Force full-duplex */
 	0x3f, 0x05, };
+const char i82558_config_cmd[22] = {
+	22, 0x08, 0, 1,  0, 0x80, 0x22, 0x03,  1, /* 1=Use MII  0=Use AUI */
+	0, 0x2E, 0,  0x60, 0x08, 0x88,
+	0x68, 0, 0x40, 0xf2, 0xBD, 		/* 0xBD->0xFD=Force full-duplex */
+	0x31, 0x05, };
 
 /* PHY media interface chips. */
 static const char *phys[] = {
@@ -392,12 +412,12 @@
 					 S80C24, I82555, DP83840A=10, };
 static const char is_mii[] = { 0, 1, 1, 0, 1, 1, 0, 1 };
 
-static void speedo_found1(struct device *dev, int ioaddr, int irq,
+static void speedo_found1(struct device *dev, long ioaddr, int irq,
 						  int card_idx);
 
-static int read_eeprom(int ioaddr, int location);
-static int mdio_read(int ioaddr, int phy_id, int location);
-static int mdio_write(int ioaddr, int phy_id, int location, int value);
+static int read_eeprom(long ioaddr, int location, int addr_len);
+static int mdio_read(long ioaddr, int phy_id, int location);
+static int mdio_write(long ioaddr, int phy_id, int location, int value);
 static int speedo_open(struct device *dev);
 static void speedo_timer(unsigned long data);
 static void speedo_init_rx_ring(struct device *dev);
@@ -420,68 +440,91 @@
 static int debug = -1;			/* The debug level */
 #endif
 
+#ifdef honor_default_port
+/* Optional driver feature to allow forcing the transceiver setting.
+   Not recommended. */
+static int mii_ctrl[8] = { 0x3300, 0x3100, 0x0000, 0x0100,
+						   0x2000, 0x2100, 0x0400, 0x3100};
+#endif
+
 /* A list of all installed Speedo devices, for removing the driver module. */
 static struct device *root_speedo_dev = NULL;
 
 int eepro100_init(struct device *dev)
 {
 	int cards_found = 0;
+	static int pci_index = 0;
 
-	if (pcibios_present()) {
-		static int pci_index = 0;
-		for (; pci_index < 8; pci_index++) {
-			unsigned char pci_bus, pci_device_fn, pci_irq_line, pci_latency;
-			int pci_ioaddr;
-
-			unsigned short pci_command, new_command;
-
-			if (pcibios_find_device(PCI_VENDOR_ID_INTEL,
-									PCI_DEVICE_ID_INTEL_82557,
-									pci_index, &pci_bus,
-									&pci_device_fn))
-			  break;
+	if (! pcibios_present())
+		return cards_found;
+
+	for (; pci_index < 8; pci_index++) {
+		unsigned char pci_bus, pci_device_fn, pci_latency;
+		long ioaddr;
+		int irq;
+
+		u16 pci_command, new_command;
+
+		if (pcibios_find_device(PCI_VENDOR_ID_INTEL,
+								PCI_DEVICE_ID_INTEL_82557,
+								pci_index, &pci_bus,
+								&pci_device_fn))
+			break;
+#if LINUX_VERSION_CODE >= 0x20155  ||  PCI_SUPPORT_1
+		{
+			struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn);
+			ioaddr = pdev->base_address[1];		/* Use [0] to mem-map */
+			irq = pdev->irq;
+		}
+#else
+		{
+			u32 pci_ioaddr;
+			u8 pci_irq_line;
 			pcibios_read_config_byte(pci_bus, pci_device_fn,
 									 PCI_INTERRUPT_LINE, &pci_irq_line);
 			/* Note: BASE_ADDRESS_0 is for memory-mapping the registers. */
 			pcibios_read_config_dword(pci_bus, pci_device_fn,
 									  PCI_BASE_ADDRESS_1, &pci_ioaddr);
-			/* Remove I/O space marker in bit 0. */
-			pci_ioaddr &= ~3;
-			if (speedo_debug > 2)
-				printk("Found Intel i82557 PCI Speedo at I/O %#x, IRQ %d.\n",
-					   (int)pci_ioaddr, pci_irq_line);
-
-			/* Get and check the bus-master and latency values. */
-			pcibios_read_config_word(pci_bus, pci_device_fn,
-									 PCI_COMMAND, &pci_command);
-			new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO;
-			if (pci_command != new_command) {
-				printk(KERN_INFO "  The PCI BIOS has not enabled this"
-					   " device!  Updating PCI command %4.4x->%4.4x.\n",
-					   pci_command, new_command);
-				pcibios_write_config_word(pci_bus, pci_device_fn,
-										  PCI_COMMAND, new_command);
-			}
-			pcibios_read_config_byte(pci_bus, pci_device_fn,
-										 PCI_LATENCY_TIMER, &pci_latency);
-			if (pci_latency < 32) {
-				printk("  PCI latency timer (CFLT) is unreasonably low at %d."
-					   "  Setting to 32 clocks.\n", pci_latency);
-				pcibios_write_config_byte(pci_bus, pci_device_fn,
-										  PCI_LATENCY_TIMER, 32);
-			} else if (speedo_debug > 1)
-				printk("  PCI latency timer (CFLT) is %#x.\n", pci_latency);
-
-			speedo_found1(dev, pci_ioaddr, pci_irq_line, cards_found);
-			dev = NULL;
-			cards_found++;
+			ioaddr = pci_ioaddr;
+			irq = pci_irq_line;
+		}
+#endif
+		/* Remove I/O space marker in bit 0. */
+		ioaddr &= ~3;
+		if (speedo_debug > 2)
+			printk("Found Intel i82557 PCI Speedo at I/O %#lx, IRQ %d.\n",
+				   ioaddr, irq);
+
+		/* Get and check the bus-master and latency values. */
+		pcibios_read_config_word(pci_bus, pci_device_fn,
+								 PCI_COMMAND, &pci_command);
+		new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO;
+		if (pci_command != new_command) {
+			printk(KERN_INFO "  The PCI BIOS has not enabled this"
+				   " device!  Updating PCI command %4.4x->%4.4x.\n",
+				   pci_command, new_command);
+			pcibios_write_config_word(pci_bus, pci_device_fn,
+									  PCI_COMMAND, new_command);
 		}
+		pcibios_read_config_byte(pci_bus, pci_device_fn,
+								 PCI_LATENCY_TIMER, &pci_latency);
+		if (pci_latency < 32) {
+			printk("  PCI latency timer (CFLT) is unreasonably low at %d."
+				   "  Setting to 32 clocks.\n", pci_latency);
+			pcibios_write_config_byte(pci_bus, pci_device_fn,
+									  PCI_LATENCY_TIMER, 32);
+		} else if (speedo_debug > 1)
+			printk("  PCI latency timer (CFLT) is %#x.\n", pci_latency);
+
+		speedo_found1(dev, ioaddr, irq, cards_found);
+		dev = NULL;
+		cards_found++;
 	}
 
 	return cards_found;
 }
 
-static void speedo_found1(struct device *dev, int ioaddr, int irq,
+static void speedo_found1(struct device *dev, long ioaddr, int irq,
 						  int card_idx)
 {
 	static int did_version = 0;			/* Already printed version info. */
@@ -495,7 +538,7 @@
 
 	dev = init_etherdev(dev, sizeof(struct speedo_private));
 
-	if (dev->mem_start > 0) 
+	if (dev->mem_start > 0)
 		option = dev->mem_start;
 	else if (card_idx >= 0  &&  options[card_idx] >= 0)
 		option = options[card_idx];
@@ -508,8 +551,10 @@
 	{
 		u16 sum = 0;
 		int j;
+		int addr_len = read_eeprom(ioaddr, 0, 6) == 0xffff ? 8 : 6;
+
 		for (j = 0, i = 0; i < 0x40; i++) {
-			u16 value = read_eeprom(ioaddr, i);
+			u16 value = read_eeprom(ioaddr, i, addr_len);
 			eeprom[i] = value;
 			sum += value;
 			if (i < 3) {
@@ -535,7 +580,7 @@
 	else
 		product = "Intel EtherExpress Pro 10/100";
 
-	printk(KERN_INFO "%s: %s at %#3x, ", dev->name, product, ioaddr);
+	printk(KERN_INFO "%s: %s at %#3lx, ", dev->name, product, ioaddr);
 
 	for (i = 0; i < 5; i++)
 		printk("%2.2X:", dev->dev_addr[i]);
@@ -666,22 +711,22 @@
 #define eeprom_delay(nanosec)		udelay(1);
 
 /* The EEPROM commands include the alway-set leading bit. */
-#define EE_WRITE_CMD	(5 << 6)
-#define EE_READ_CMD		(6 << 6)
-#define EE_ERASE_CMD	(7 << 6)
+#define EE_WRITE_CMD	(5 << addr_len)
+#define EE_READ_CMD		(6 << addr_len)
+#define EE_ERASE_CMD	(7 << addr_len)
 
-static int read_eeprom(int ioaddr, int location)
+static int read_eeprom(long ioaddr, int location, int addr_len)
 {
-	int i;
 	unsigned short retval = 0;
 	int ee_addr = ioaddr + SCBeeprom;
 	int read_cmd = location | EE_READ_CMD;
+	int i;
 
 	outw(EE_ENB & ~EE_CS, ee_addr);
 	outw(EE_ENB, ee_addr);
 
 	/* Shift the read command bits out. */
-	for (i = 10; i >= 0; i--) {
+	for (i = 12; i >= 0; i--) {
 		short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
 		outw(EE_ENB | dataval, ee_addr);
 		eeprom_delay(100);
@@ -703,7 +748,7 @@
 	return retval;
 }
 
-static int mdio_read(int ioaddr, int phy_id, int location)
+static int mdio_read(long ioaddr, int phy_id, int location)
 {
 	int val, boguscnt = 64*10;		/* <64 usec. to complete, typ 27 ticks */
 	outl(0x08000000 | (location<<16) | (phy_id<<21), ioaddr + SCBCtrlMDI);
@@ -716,7 +761,7 @@
 	return val & 0xffff;
 }
 
-static int mdio_write(int ioaddr, int phy_id, int location, int value)
+static int mdio_write(long ioaddr, int phy_id, int location, int value)
 {
 	int val, boguscnt = 64*10;		/* <64 usec. to complete, typ 27 ticks */
 	outl(0x04000000 | (location<<16) | (phy_id<<21) | value,
@@ -735,7 +780,7 @@
 speedo_open(struct device *dev)
 {
 	struct speedo_private *sp = (struct speedo_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 
 #ifdef notdef
 	/* We could reset the chip, but should not need to. */
@@ -752,6 +797,22 @@
 
 	MOD_INC_USE_COUNT;
 
+	/* Retrigger negotiation to reset previous errors. */
+	if ((sp->phy[0] & 0x8000) == 0) {
+		int phy_addr = sp->phy[0] & 0x1f ;
+		/* Use 0x3300 for restarting NWay, other values to force xcvr:
+		   0x0000 10-HD
+		   0x0100 10-FD
+		   0x2000 100-HD
+		   0x2100 100-FD
+		*/
+#ifdef honor_default_port
+		mdio_write(ioaddr, phy_addr, 0, mii_ctrl[dev->default_port & 7]);
+#else
+		mdio_write(ioaddr, phy_addr, 0, 0x3300);
+#endif
+	}
+
 	/* Load the statistics block address. */
 	wait_for_cmd_done(ioaddr + SCBCmd);
 	outl(virt_to_bus(&sp->lstats), ioaddr + SCBPointer);
@@ -805,6 +866,7 @@
 	/* Setup the chip and configure the multicast list. */
 	sp->mc_setup_frm = NULL;
 	sp->mc_setup_frm_len = 0;
+	sp->mc_setup_busy = 0;
 	sp->rx_mode = -1;			/* Invalid -> always reset the mode. */
 	set_rx_mode(dev);
 
@@ -825,6 +887,9 @@
 
 	wait_for_cmd_done(ioaddr + SCBCmd);
 	outw(CU_DUMPSTATS, ioaddr + SCBCmd);
+	/* No need to wait for the command unit to accept here. */
+	if ((sp->phy[0] & 0x8000) == 0)
+		mdio_read(ioaddr, sp->phy[0] & 0x1f, 0);
 	return 0;
 }
 
@@ -833,24 +898,22 @@
 {
 	struct device *dev = (struct device *)data;
 	struct speedo_private *sp = (struct speedo_private *)dev->priv;
-	int tickssofar = jiffies - sp->last_rx_time;
 
 	if (speedo_debug > 3) {
-		int ioaddr = dev->base_addr;
-		printk(KERN_DEBUG "%s: Media selection tick, status %4.4x.\n",
+		long ioaddr = dev->base_addr;
+		printk(KERN_DEBUG "%s: Media control tick, status %4.4x.\n",
 			   dev->name, inw(ioaddr + SCBStatus));
 	}
-	if (sp->rx_bug) {
-		if (tickssofar > 2*HZ  || sp->rx_mode < 0) {
-			/* We haven't received a packet in a Long Time.  We might have been
-			   bitten by the receiver hang bug.  This can be cleared by sending
-			   a set multicast list command. */
-			set_rx_mode(dev);
-		}
-		/* We must continue to monitor the media. */
-		sp->timer.expires = RUN_AT(2*HZ); 			/* 2.0 sec. */
-		add_timer(&sp->timer);
+	if (sp->rx_mode < 0  ||
+		(sp->rx_bug  && jiffies - sp->last_rx_time > 2*HZ)) {
+		/* We haven't received a packet in a Long Time.  We might have been
+		   bitten by the receiver hang bug.  This can be cleared by sending
+		   a set multicast list command. */
+		set_rx_mode(dev);
 	}
+	/* We must continue to monitor the media. */
+	sp->timer.expires = RUN_AT(2*HZ); 			/* 2.0 sec. */
+	add_timer(&sp->timer);
 }
 
 /* Initialize the Rx and Tx rings, along with various 'dev' bits. */
@@ -862,30 +925,32 @@
 	int i;
 
 	sp->cur_rx = 0;
-	sp->dirty_rx = RX_RING_SIZE - 1;
 
 	for (i = 0; i < RX_RING_SIZE; i++) {
 		struct sk_buff *skb;
-		skb = alloc_skb(PKT_BUF_SZ, GFP_ATOMIC);
+		skb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD));
 		sp->rx_skbuff[i] = skb;
 		if (skb == NULL)
-			break;			/* Bad news!  */
+			break;			/* OK.  Just initially short of Rx bufs. */
 		skb->dev = dev;			/* Mark as being used by this device. */
-
 		rxf = (struct RxFD *)skb->tail;
-		skb_reserve(skb, sizeof(struct RxFD));
 		sp->rx_ringp[i] = rxf;
+		skb_reserve(skb, sizeof(struct RxFD));
 		if (last_rxf)
 			last_rxf->link = virt_to_bus(rxf);
 		last_rxf = rxf;
 		rxf->status = 0x00000001; 			/* '1' is flag value only. */
 		rxf->link = 0;						/* None yet. */
 		/* This field unused by i82557, we use it as a consistency check. */
+#ifdef final_version
+		rxf->rx_buf_addr = 0xffffffff;
+#else
 		rxf->rx_buf_addr = virt_to_bus(skb->tail);
-
+#endif
 		rxf->count = 0;
 		rxf->size = PKT_BUF_SZ;
 	}
+	sp->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
 	/* Mark the last entry as end-of-list. */
 	last_rxf->status = 0xC0000002; 			/* '2' is flag value only. */
 	sp->last_rxf = last_rxf;
@@ -894,20 +959,21 @@
 static void speedo_tx_timeout(struct device *dev)
 {
 	struct speedo_private *sp = (struct speedo_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 
 	printk(KERN_WARNING "%s: Transmit timed out: status %4.4x "
-		   "command %4.4x.\n",
-		   dev->name, inw(ioaddr + SCBStatus), inw(ioaddr + SCBCmd));
-
+		   " %4.4x at %d/%d command %8.8x.\n",
+		   dev->name, inw(ioaddr + SCBStatus), inw(ioaddr + SCBCmd),
+		   sp->dirty_tx, sp->cur_tx,
+		   sp->tx_ring[sp->dirty_tx % TX_RING_SIZE].status);
 	if ((inw(ioaddr + SCBStatus) & 0x00C0) != 0x0080) {
-	  printk(KERN_WARNING "%s: Trying to restart the transmitter...\n",
-			 dev->name);
-	  outl(virt_to_bus(&sp->tx_ring[sp->dirty_tx % TX_RING_SIZE]),
-		   ioaddr + SCBPointer);
-	  outw(CU_START, ioaddr + SCBCmd);
+		printk(KERN_WARNING "%s: Trying to restart the transmitter...\n",
+			   dev->name);
+		outl(virt_to_bus(&sp->tx_ring[sp->dirty_tx % TX_RING_SIZE]),
+			 ioaddr + SCBPointer);
+		outw(CU_START, ioaddr + SCBCmd);
 	} else {
-	  outw(DRVR_INT, ioaddr + SCBCmd);
+		outw(DRVR_INT, ioaddr + SCBCmd);
 	}
 	/* Reset the MII transceiver, suggested by Fred Young @ scalable.com. */
 	if ((sp->phy[0] & 0x8000) == 0) {
@@ -916,6 +982,9 @@
 		mdio_write(ioaddr, phy_addr, 1, 0x0000);
 		mdio_write(ioaddr, phy_addr, 4, 0x0000);
 		mdio_write(ioaddr, phy_addr, 0, 0x8000);
+#ifdef honor_default_port
+		mdio_write(ioaddr, phy_addr, 0, mii_ctrl[dev->default_port & 7]);
+#endif
 	}
 	sp->stats.tx_errors++;
 	dev->trans_start = jiffies;
@@ -926,7 +995,7 @@
 speedo_start_xmit(struct sk_buff *skb, struct device *dev)
 {
 	struct speedo_private *sp = (struct speedo_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	int entry;
 
 	/* Block a timer-based transmit from overlapping.  This could better be
@@ -963,22 +1032,22 @@
 		sp->tx_ring[entry].link =
 		  virt_to_bus(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]);
 		sp->tx_ring[entry].tx_desc_addr =
-		  virt_to_bus(&sp->tx_ring[entry].tx_buf_addr);
+		  virt_to_bus(&sp->tx_ring[entry].tx_buf_addr0);
 		/* The data region is always in one buffer descriptor, Tx FIFO
 		   threshold of 256. */
 		sp->tx_ring[entry].count = 0x01208000;
-		sp->tx_ring[entry].tx_buf_addr = virt_to_bus(skb->data);
-		sp->tx_ring[entry].tx_buf_size = skb->len;
+		sp->tx_ring[entry].tx_buf_addr0 = virt_to_bus(skb->data);
+		sp->tx_ring[entry].tx_buf_size0 = skb->len;
 		/* Todo: perhaps leave the interrupt bit set if the Tx queue is more
 		   than half full.  Argument against: we should be receiving packets
 		   and scavenging the queue.  Argument for: if so, it shouldn't
 		   matter. */
 		sp->last_cmd->command &= ~(CmdSuspend | CmdIntr);
 		sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
+		restore_flags(flags);
 		/* Trigger the command unit resume. */
 		wait_for_cmd_done(ioaddr + SCBCmd);
 		outw(CU_RESUME, ioaddr + SCBCmd);
-		restore_flags(flags);
 	}
 
 	/* Leave room for set_rx_mode() to fill two entries. */
@@ -998,7 +1067,7 @@
 {
 	struct device *dev = (struct device *)dev_instance;
 	struct speedo_private *sp;
-	int ioaddr, boguscnt = max_interrupt_work;
+	long ioaddr, boguscnt = max_interrupt_work;
 	unsigned short status;
 
 #ifndef final_version
@@ -1015,6 +1084,7 @@
 	if (test_and_set_bit(0, (void*)&sp->in_interrupt)) {
 		printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n",
 			   dev->name);
+		sp->in_interrupt = 0;	/* Avoid halting machine. */
 		return;
 	}
 	dev->interrupt = 1;
@@ -1058,14 +1128,15 @@
 				if (speedo_debug > 5)
 					printk(KERN_DEBUG " scavenge candidate %d status %4.4x.\n",
 						   entry, status);
-				if ((status & 0x8000) == 0)
+				if ((status & StatusComplete) == 0)
 					break;			/* It still hasn't been processed. */
 				/* Free the original skb. */
 				if (sp->tx_skbuff[entry]) {
 					sp->stats.tx_packets++;	/* Count only user packets. */
-					dev_kfree_skb(sp->tx_skbuff[entry], FREE_WRITE);
+					dev_free_skb(sp->tx_skbuff[entry]);
 					sp->tx_skbuff[entry] = 0;
-				}
+				} else if ((sp->tx_ring[entry].status&0x70000) == CmdNOp << 16)
+					sp->mc_setup_busy = 0;
 				dirty_tx++;
 			}
 
@@ -1113,108 +1184,104 @@
 	struct speedo_private *sp = (struct speedo_private *)dev->priv;
 	int entry = sp->cur_rx % RX_RING_SIZE;
 	int status;
+	int rx_work_limit = sp->dirty_rx + RX_RING_SIZE - sp->cur_rx;
 
 	if (speedo_debug > 4)
 		printk(KERN_DEBUG " In speedo_rx().\n");
 	/* If we own the next entry, it's a new packet. Send it up. */
-	while ((status = sp->rx_ringp[entry]->status) & RX_COMPLETE) {
+	while (sp->rx_ringp[entry] != NULL &&
+		   (status = sp->rx_ringp[entry]->status) & RxComplete) {
 
+		if (--rx_work_limit < 0)
+			break;
 		if (speedo_debug > 4)
 			printk(KERN_DEBUG "  speedo_rx() status %8.8x len %d.\n", status,
 				   sp->rx_ringp[entry]->count & 0x3fff);
-		if (status & 0x0200) {
-			printk(KERN_ERR "%s: Ethernet frame overran the Rx buffer, "
-				   "status %8.8x!\n", dev->name, status);
-		} else if ( ! (status & 0x2000)) {
-			/* There was a fatal error.  This *should* be impossible. */
-			sp->stats.rx_errors++;
-			printk(KERN_ERR "%s: Anomalous event in speedo_rx(), status %8.8x.\n",
-				   dev->name, status);
+		if ((status & (RxErrTooBig|RxOK)) != RxOK) {
+			if (status & RxErrTooBig)
+				printk(KERN_ERR "%s: Ethernet frame overran the Rx buffer, "
+					   "status %8.8x!\n", dev->name, status);
+			else if ( ! (status & 0x2000)) {
+				/* There was a fatal error.  This *should* be impossible. */
+				sp->stats.rx_errors++;
+				printk(KERN_ERR "%s: Anomalous event in speedo_rx(), "
+					   "status %8.8x.\n",
+					   dev->name, status);
+			}
 		} else {
-			/* Malloc up new buffer, compatible with net-2e. */
 			int pkt_len = sp->rx_ringp[entry]->count & 0x3fff;
 			struct sk_buff *skb;
-			int rx_in_place = 0;
 
 			/* Check if the packet is long enough to just accept without
 			   copying to a properly sized skbuff. */
-			if (pkt_len > rx_copybreak) {
-				struct sk_buff *newskb;
-				char *temp;
-
-				/* Pass up the skb already on the Rx ring. */
-				skb = sp->rx_skbuff[entry];
-				temp = skb_put(skb, pkt_len);
-				if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp)
-					printk(KERN_ERR "%s: Warning -- the skbuff addresses do not match"
-						   " in speedo_rx: %8.8x vs. %p / %p.\n", dev->name,
-						   sp->rx_ringp[entry]->rx_buf_addr, skb->head, temp);
-				/* Get a fresh skbuff to replace the filled one. */
-				newskb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD));
-
-				if (newskb) {
-					struct RxFD *rxf;
-					rx_in_place = 1;
-					sp->rx_skbuff[entry] = newskb;
-					newskb->dev = dev;
-					rxf = sp->rx_ringp[entry] = (struct RxFD *)newskb->tail;
-					skb_reserve(newskb, sizeof(struct RxFD));
-					/* Unused by i82557, consistency check only. */
-					rxf->rx_buf_addr = virt_to_bus(newskb->tail);
-					rxf->status = 0x00000001;
-				} else			/* No memory, drop the packet. */
-				  skb = 0;
-			} else
-				skb = dev_alloc_skb(pkt_len + 2);
-			if (skb == NULL) {
-				int i;
-				printk(KERN_ERR "%s: Memory squeeze, deferring packet.\n", dev->name);
-				/* Check that at least two ring entries are free.
-				   If not, free one and mark stats->rx_dropped++. */
-				/* ToDo: This is not correct!!!!  We should count the number
-				   of linked-in Rx buffer to very that we have at least two
-				   remaining. */
-				for (i = 0; i < RX_RING_SIZE; i++)
-					if (! ((sp->rx_ringp[(entry+i) % RX_RING_SIZE]->status)
-						   & RX_COMPLETE))
-						break;
-
-				if (i > RX_RING_SIZE -2) {
-					sp->stats.rx_dropped++;
-					sp->rx_ringp[entry]->status = 0;
-					sp->cur_rx++;
-				}
-				break;
-			}
-			skb->dev = dev;
-			if (! rx_in_place) {
-				skb_reserve(skb, 2);	/* 16 byte align the data fields */
-#if defined(__i386)   &&  notyet
+			if (pkt_len < rx_copybreak
+				&& (skb = dev_alloc_skb(pkt_len + 2)) != 0) {
+				skb->dev = dev;
+				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
+				/* 'skb_put()' points to the start of sk_buff data area. */
+#if 1 || USE_IP_CSUM
 				/* Packet is in one chunk -- we can copy + cksum. */
-				eth_io_copy_and_sum(skb, bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr),
-									pkt_len, 0);
+				eth_copy_and_sum(skb,
+								 bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr),
+								 pkt_len, 0);
+				skb_put(skb, pkt_len);
 #else
 				memcpy(skb_put(skb, pkt_len),
 					   bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), pkt_len);
 #endif
+			} else {
+				void *temp;
+				/* Pass up the already-filled skbuff. */
+				skb = sp->rx_skbuff[entry];
+				if (skb == NULL) {
+					printk(KERN_ERR "%s: Inconsistent Rx descriptor chain.\n",
+						   dev->name);
+					break;
+				}
+				sp->rx_skbuff[entry] = NULL;
+				temp = skb_put(skb, pkt_len);
+				if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp)
+					printk(KERN_ERR "%s: Rx consistency error -- the skbuff "
+						   "addresses do not match in speedo_rx: %p vs. %p "
+						   "/ %p.\n", dev->name,
+						   bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr),
+						   skb->head, temp);
+				sp->rx_ringp[entry] = NULL;
 			}
 			skb->protocol = eth_type_trans(skb, dev);
 			netif_rx(skb);
 			sp->stats.rx_packets++;
 		}
+		entry = (++sp->cur_rx) % RX_RING_SIZE;
+	}
 
-		/*	ToDo: This is better than before, but should be checked. */
-		{
-			struct RxFD *rxf = sp->rx_ringp[entry];
-			rxf->status = 0xC0000003; 		/* '3' for verification only */
-			rxf->link = 0;			/* None yet. */
-			rxf->count = 0;
-			rxf->size = PKT_BUF_SZ;
-			sp->last_rxf->link = virt_to_bus(rxf);
-			sp->last_rxf->status &= ~0xC0000000;
-			sp->last_rxf = rxf;
-			entry = (++sp->cur_rx) % RX_RING_SIZE;
+	/* Refill the Rx ring buffers. */
+	for (; sp->dirty_rx < sp->cur_rx; sp->dirty_rx++) {
+		struct RxFD *rxf;
+		entry = sp->dirty_rx % RX_RING_SIZE;
+		if (sp->rx_skbuff[entry] == NULL) {
+			struct sk_buff *skb;
+			/* Get a fresh skbuff to replace the consumed one. */
+			skb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD));
+			sp->rx_skbuff[entry] = skb;
+			if (skb == NULL) {
+				sp->rx_ringp[entry] = NULL;
+				break;			/* Better luck next time!  */
+			}
+			rxf = sp->rx_ringp[entry] = (struct RxFD *)skb->tail;
+			skb->dev = dev;
+			skb_reserve(skb, sizeof(struct RxFD));
+			rxf->rx_buf_addr = virt_to_bus(skb->tail);
+		} else {
+			rxf = sp->rx_ringp[entry];
 		}
+		rxf->status = 0xC0000001; 	/* '1' for driver use only. */
+		rxf->link = 0;			/* None yet. */
+		rxf->count = 0;
+		rxf->size = PKT_BUF_SZ;
+		sp->last_rxf->link = virt_to_bus(rxf);
+		sp->last_rxf->status &= ~0xC0000000;
+		sp->last_rxf = rxf;
 	}
 
 	sp->last_rx_time = jiffies;
@@ -1224,7 +1291,7 @@
 static int
 speedo_close(struct device *dev)
 {
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	struct speedo_private *sp = (struct speedo_private *)dev->priv;
 	int i;
 
@@ -1250,7 +1317,7 @@
 		sp->rx_skbuff[i] = 0;
 		/* Clear the Rx descriptors. */
 		if (skb)
-			dev_kfree_skb(skb, FREE_WRITE);
+			dev_free_skb(skb);
 	}
 
 	for (i = 0; i < TX_RING_SIZE; i++) {
@@ -1258,7 +1325,7 @@
 		sp->tx_skbuff[i] = 0;
 		/* Clear the Tx descriptors. */
 		if (skb)
-			dev_kfree_skb(skb, FREE_WRITE);
+			dev_free_skb(skb);
 	}
 	if (sp->mc_setup_frm) {
 		kfree(sp->mc_setup_frm);
@@ -1303,7 +1370,7 @@
 speedo_get_stats(struct device *dev)
 {
 	struct speedo_private *sp = (struct speedo_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 
 	if (sp->lstats.done_marker == 0xA007) {	/* Previous dump finished */
 		sp->stats.tx_aborted_errors += sp->lstats.tx_coll16_errs;
@@ -1329,7 +1396,7 @@
 static int speedo_ioctl(struct device *dev, struct ifreq *rq, int cmd)
 {
 	struct speedo_private *sp = (struct speedo_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	u16 *data = (u16 *)&rq->ifr_data;
 	int phy = sp->phy[0] & 0x1f;
 
@@ -1362,7 +1429,8 @@
 set_rx_mode(struct device *dev)
 {
 	struct speedo_private *sp = (struct speedo_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
+	struct descriptor *last_cmd;
 	char new_rx_mode;
 	unsigned long flags;
 	int entry, i;
@@ -1383,57 +1451,55 @@
 	}
 
 	if (new_rx_mode != sp->rx_mode) {
-		/* We must change the configuration. Construct a CmdConfig frame. */
-		memcpy(sp->config_cmd_data, basic_config_cmd,sizeof(basic_config_cmd));
-		sp->config_cmd_data[1] = (txfifo << 4) | rxfifo;
-		sp->config_cmd_data[4] = rxdmacount;
-		sp->config_cmd_data[5] = txdmacount + 0x80;
-		sp->config_cmd_data[15] = (new_rx_mode & 2) ? 0x49 : 0x48;
-		sp->config_cmd_data[19] = sp->full_duplex ? 0xC0 : 0x80;
-		sp->config_cmd_data[21] = (new_rx_mode & 1) ? 0x0D : 0x05;
-		if (sp->phy[0] & 0x8000) {			/* Use the AUI port instead. */
-		  sp->config_cmd_data[15] |= 0x80;
-		  sp->config_cmd_data[8] = 0;
-		}
-		save_flags(flags);
+		u8 *config_cmd_data;
+
+		save_flags(flags);		/* Lock to protect sp->cur_tx. */
 		cli();
-		/* Fill the "real" tx_ring frame with a no-op and point it to us. */
 		entry = sp->cur_tx++ % TX_RING_SIZE;
-		sp->tx_skbuff[entry] = 0;	/* Nothing to free. */
-		sp->tx_ring[entry].status = CmdNOp << 16;
-		sp->tx_ring[entry].link = virt_to_bus(&sp->config_cmd);
-		sp->config_cmd.status = 0;
-		sp->config_cmd.command = CmdSuspend | CmdConfigure;
-		sp->config_cmd.link =
-		  virt_to_bus(&(sp->tx_ring[sp->cur_tx % TX_RING_SIZE]));
-		sp->last_cmd->command &= ~CmdSuspend;
-		/* Immediately trigger the command unit resume. */
-		wait_for_cmd_done(ioaddr + SCBCmd);
-		outw(CU_RESUME, ioaddr + SCBCmd);
-		sp->last_cmd = &sp->config_cmd;
+		last_cmd = sp->last_cmd;
+		sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
 		restore_flags(flags);
-		if (speedo_debug > 5) {
-			int i;
-			printk(KERN_DEBUG " CmdConfig frame in entry %d.\n", entry);
-			for(i = 0; i < 32; i++)
-				printk(" %2.2x", ((unsigned char *)&sp->config_cmd)[i]);
-			printk(".\n");
+
+		sp->tx_skbuff[entry] = 0;			/* Redundant. */
+		sp->tx_ring[entry].status = (CmdSuspend | CmdConfigure) << 16;
+		sp->tx_ring[entry].link =
+			virt_to_bus(&sp->tx_ring[(entry + 1) % TX_RING_SIZE]);
+		config_cmd_data = (void *)&sp->tx_ring[entry].tx_desc_addr;
+		/* Construct a full CmdConfig frame. */
+		memcpy(config_cmd_data, i82558_config_cmd, sizeof(i82558_config_cmd));
+		config_cmd_data[1] = (txfifo << 4) | rxfifo;
+		config_cmd_data[4] = rxdmacount;
+		config_cmd_data[5] = txdmacount + 0x80;
+		config_cmd_data[15] |= (new_rx_mode & 2) ? 1 : 0;
+		config_cmd_data[19] |= sp->full_duplex ? 0x40 : 0;
+		config_cmd_data[21] = (new_rx_mode & 1) ? 0x0D : 0x05;
+		if (sp->phy[0] & 0x8000) {			/* Use the AUI port instead. */
+			config_cmd_data[15] |= 0x80;
+			config_cmd_data[8] = 0;
 		}
+		/* Trigger the command unit resume. */
+		last_cmd->command &= ~CmdSuspend;
+		wait_for_cmd_done(ioaddr + SCBCmd);
+		outw(CU_RESUME, ioaddr + SCBCmd);
 	}
 
-	if (new_rx_mode == 0  &&  dev->mc_count < 3) {
-		/* The simple case of 0-2 multicast list entries occurs often, and
+	if (new_rx_mode == 0  &&  dev->mc_count < 4) {
+		/* The simple case of 0-3 multicast list entries occurs often, and
 		   fits within one tx_ring[] entry. */
-		u16 *setup_params, *eaddrs;
 		struct dev_mc_list *mclist;
+		u16 *setup_params, *eaddrs;
 
-		save_flags(flags);
+		save_flags(flags);		/* Lock to protect sp->cur_tx. */
 		cli();
 		entry = sp->cur_tx++ % TX_RING_SIZE;
+		last_cmd = sp->last_cmd;
+		sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
+		restore_flags(flags);
+
 		sp->tx_skbuff[entry] = 0;
 		sp->tx_ring[entry].status = (CmdSuspend | CmdMulticastList) << 16;
 		sp->tx_ring[entry].link =
-		  virt_to_bus(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]);
+			virt_to_bus(&sp->tx_ring[(entry + 1) % TX_RING_SIZE]);
 		sp->tx_ring[entry].tx_desc_addr = 0; /* Really MC list count. */
 		setup_params = (u16 *)&sp->tx_ring[entry].tx_desc_addr;
 		*setup_params++ = dev->mc_count*6;
@@ -1446,36 +1512,39 @@
 			*setup_params++ = *eaddrs++;
 		}
 
-		sp->last_cmd->command &= ~CmdSuspend;
+		last_cmd->command &= ~CmdSuspend;
 		/* Immediately trigger the command unit resume. */
 		wait_for_cmd_done(ioaddr + SCBCmd);
 		outw(CU_RESUME, ioaddr + SCBCmd);
-		sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
-		restore_flags(flags);
 	} else if (new_rx_mode == 0) {
-		/* This does not work correctly, but why not? */
 		struct dev_mc_list *mclist;
-		u16 *eaddrs;
+		u16 *setup_params, *eaddrs;
 		struct descriptor *mc_setup_frm = sp->mc_setup_frm;
-		u16 *setup_params;
 		int i;
 
 		if (sp->mc_setup_frm_len < 10 + dev->mc_count*6
 			|| sp->mc_setup_frm == NULL) {
-			/* Allocate a new frame, 10bytes + addrs, with a few
-			   extra entries for growth. */
+			/* Allocate a full setup frame, 10bytes + <max addrs>. */
 			if (sp->mc_setup_frm)
 				kfree(sp->mc_setup_frm);
-			sp->mc_setup_frm_len = 10 + dev->mc_count*6 + 24;
+			sp->mc_setup_busy = 0;
+			sp->mc_setup_frm_len = 10 + multicast_filter_limit*6;
 			sp->mc_setup_frm = kmalloc(sp->mc_setup_frm_len, GFP_ATOMIC);
 			if (sp->mc_setup_frm == NULL) {
-			  printk(KERN_ERR "%s: Failed to allocate a setup frame.\n", dev->name);
+				printk(KERN_ERR "%s: Failed to allocate a setup frame.\n",
+					   dev->name);
 				sp->rx_mode = -1; /* We failed, try again. */
 				return;
 			}
 		}
+		/* If we are busy, someone might be quickly adding to the MC list.
+		   Try again later when the list changes stop. */
+		if (sp->mc_setup_busy) {
+			sp->rx_mode = -1;
+			return;
+		}
 		mc_setup_frm = sp->mc_setup_frm;
-		/* Construct the new setup frame. */
+		/* Fill the setup frame. */
 		if (speedo_debug > 1)
 			printk(KERN_DEBUG "%s: Constructing a setup frame at %p, "
 				   "%d bytes.\n",
@@ -1483,7 +1552,7 @@
 		mc_setup_frm->status = 0;
 		mc_setup_frm->command = CmdSuspend | CmdIntr | CmdMulticastList;
 		/* Link set below. */
-		setup_params = (u16 *)mc_setup_frm->params;
+		setup_params = (u16 *)&mc_setup_frm->params;
 		*setup_params++ = dev->mc_count*6;
 		/* Fill in the multicast addresses. */
 		for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
@@ -1498,10 +1567,10 @@
 		save_flags(flags);
 		cli();
 		entry = sp->cur_tx++ % TX_RING_SIZE;
-
-		if (speedo_debug > 5)
-			printk(" CmdMCSetup frame length %d in entry %d.\n",
-				   dev->mc_count, entry);
+		last_cmd = sp->last_cmd;
+		sp->last_cmd = mc_setup_frm;
+		sp->mc_setup_busy++;
+		restore_flags(flags);
 
 		/* Change the command to a NoOp, pointing to the CmdMulti command. */
 		sp->tx_skbuff[entry] = 0;
@@ -1510,17 +1579,15 @@
 
 		/* Set the link in the setup frame. */
 		mc_setup_frm->link =
-		  virt_to_bus(&(sp->tx_ring[sp->cur_tx % TX_RING_SIZE]));
+			virt_to_bus(&(sp->tx_ring[(entry+1) % TX_RING_SIZE]));
 
-		sp->last_cmd->command &= ~CmdSuspend;
+		last_cmd->command &= ~CmdSuspend;
 		/* Immediately trigger the command unit resume. */
 		wait_for_cmd_done(ioaddr + SCBCmd);
 		outw(CU_RESUME, ioaddr + SCBCmd);
-		sp->last_cmd = mc_setup_frm;
-		restore_flags(flags);
-		if (speedo_debug > 1)
-			printk(KERN_DEBUG "%s: Last command at %p is %4.4x.\n",
-				   dev->name, sp->last_cmd, sp->last_cmd->command);
+		if (speedo_debug > 5)
+			printk(" CmdMCSetup frame length %d in entry %d.\n",
+				   dev->mc_count, entry);
 	}
 
 	sp->rx_mode = new_rx_mode;
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/epic100.c linux/drivers/net/epic100.c
--- linux.vanilla/drivers/net/epic100.c	Sun Dec  6 00:14:40 1998
+++ linux/drivers/net/epic100.c	Sun Dec 27 20:48:40 1998
@@ -1,6 +1,6 @@
 /* epic100.c: A SMC 83c170 EPIC/100 Fast Ethernet driver for Linux. */
 /*
-	Written 1997-1998 by Donald Becker.
+	Written/copyright 1997-1998 by Donald Becker.
 
 	This software may be used and distributed according to the terms
 	of the GNU Public License, incorporated herein by reference.
@@ -10,15 +10,15 @@
 	SMC EtherPower II 9432 PCI adapter, and several CardBus cards.
 
 	The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
-	Center of Excellence in Space Data and Information Sciences
+	USRA Center of Excellence in Space Data and Information Sciences
 	   Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
 
-	Support and updates available at
+	Information and updates available at
 	http://cesdis.gsfc.nasa.gov/linux/drivers/epic100.html
 */
 
 static const char *version =
-"epic100.c:v1.03 8/7/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/epic100.html\n";
+"epic100.c:v1.04 8/23/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/epic100.html\n";
 
 /* A few user-configurable values. */
 
@@ -118,6 +118,7 @@
 /* The I/O extent. */
 #define EPIC_TOTAL_SIZE 0x100
 
+#define epic_debug debug
 static int epic_debug = 1;
 
 /*
@@ -155,7 +156,8 @@
 /* The rest of these values should never change. */
 
 static struct device *epic_probe1(int pci_bus, int pci_devfn,
-									 struct device *dev, int card_idx);
+								  struct device *dev, long ioaddr, int irq,
+								  int chip_id, int card_idx);
 
 enum pci_flags_bit {
 	PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
@@ -166,9 +168,11 @@
 	u16	vendor_id, device_id, device_id_mask, pci_flags;
 	int io_size, min_latency;
 	struct device *(*probe1)(int pci_bus, int pci_devfn, struct device *dev,
-							 int chip_idx);
+							 long ioaddr, int irq, int chip_idx, int fnd_cnt);
 } chip_tbl[] = {
-	{"SMSC EPIC/100", 0x10B8, 0x0005, 0x7fff,
+	{"SMSC EPIC/100 83c170", 0x10B8, 0x0005, 0x7fff,
+	 PCI_USES_IO|PCI_USES_MASTER|PCI_ADDR0, EPIC_TOTAL_SIZE, 32, epic_probe1},
+	{"SMSC EPIC/C 83c175", 0x10B8, 0x0006, 0x7fff,
 	 PCI_USES_IO|PCI_USES_MASTER|PCI_ADDR0, EPIC_TOTAL_SIZE, 32, epic_probe1},
 	{0,},
 };
@@ -276,7 +280,7 @@
 int epic100_probe(struct device *dev)
 {
 	int cards_found = 0;
-	int chip_idx;
+	int chip_idx, irq;
 	u16 pci_command, new_command;
 	unsigned char pci_bus, pci_device_fn;
 
@@ -298,6 +302,7 @@
 			continue;
 		pci_bus = pcidev->bus->number;
 		pci_device_fn = pcidev->devfn;
+		irq = pcidev->irq;
 #else
 	int pci_index;
 
@@ -305,6 +310,7 @@
 		return -ENODEV;
 
 	for (pci_index = 0; pci_index < 0xff; pci_index++) {
+		u8 pci_irq_line;
 		u16 vendor, device;
 		u32 pci_ioaddr;
 
@@ -327,8 +333,11 @@
 
 		pcibios_read_config_dword(pci_bus, pci_device_fn,
 								  PCI_BASE_ADDRESS_0, &pci_ioaddr);
+		pcibios_read_config_byte(pci_bus, pci_device_fn,
+								 PCI_INTERRUPT_LINE, &pci_irq_line);
 		/* Remove I/O space marker in bit 0. */
 		pci_ioaddr &= ~3;
+		irq = pci_irq_line;
 
 		if (check_region(pci_ioaddr, chip_tbl[chip_idx].io_size))
 			continue;
@@ -350,8 +359,8 @@
 									  PCI_COMMAND, new_command);
 		}
 
-		dev = chip_tbl[chip_idx].probe1(pci_bus, pci_device_fn,
-										dev, cards_found);
+		dev = chip_tbl[chip_idx].probe1(pci_bus, pci_device_fn, dev, pci_ioaddr,
+									   irq, chip_idx, cards_found);
 
 		/* Check the latency timer. */
 		if (dev) {
@@ -375,17 +384,12 @@
 }
 #endif  /* not CARDBUS */
 
-static struct device *epic_probe1(int bus, int devfn, struct device *dev,
-									 int card_idx)
+static struct device *epic_probe1(int pci_bus, int pci_devfn,
+								  struct device *dev, long ioaddr, int irq,
+								  int chip_idx, int card_idx)
 {
-	static int did_version = 0;			/* Already printed version info. */
 	struct epic_private *ep;
 	int i, option = 0, duplex = 0;
-	u16 chip_id;
-	u32 ioaddr;
-
-	if (epic_debug > 0  &&  did_version++ == 0)
-		printk(KERN_INFO "%s", version);
 
 	if (dev && dev->mem_start) {
 		option = dev->mem_start;
@@ -399,26 +403,10 @@
 
 	dev = init_etherdev(dev, 0);
 
-	{		/* Grrrr, badly consider interface change. */
-#if defined(PCI_SUPPORT_VER2)
-		struct pci_dev *pdev = pci_find_slot(bus, devfn);
-		ioaddr = pdev->base_address[0] & ~3;
-		dev->irq = pdev->irq;
-		chip_id = pdev->device;
-#else
-		u8 irq;
-		u32 ioaddr0;
-		pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &ioaddr0);
-		pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq);
-		pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &chip_id);
-		ioaddr = ioaddr0 & ~3;
-		dev->irq = irq;
-#endif
-	}
-
 	dev->base_addr = ioaddr;
-	printk(KERN_INFO "%s: SMC EPIC/100 (chip ID %4.4x) at %#3x, IRQ %d, ",
-		   dev->name, chip_id, ioaddr, dev->irq);
+	dev->irq = irq;
+	printk(KERN_INFO "%s: SMC EPIC/100 at %#lx, IRQ %d, ",
+		   dev->name, ioaddr, dev->irq);
 
 	/* Bring the chip out of low-power mode. */
 	outl(0x4200, ioaddr + GENCTL);
@@ -427,7 +415,7 @@
 
 	/* Turn on the MII transceiver. */
 	outl(0x12, ioaddr + MIICfg);
-	if (chip_id == 6)
+	if (chip_idx == 1)
 		outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);
 	outl(0x0200, ioaddr + GENCTL);
 
@@ -447,7 +435,7 @@
 	}
 
 	/* We do a request_region() to register /proc/ioports info. */
-	request_region(ioaddr, EPIC_TOTAL_SIZE, "SMC EPIC/100");
+	request_region(ioaddr, EPIC_TOTAL_SIZE, dev->name);
 
 	/* The data structures must be quadword aligned. */
 	ep = kmalloc(sizeof(*ep), GFP_KERNEL | GFP_DMA);
@@ -457,9 +445,13 @@
 	ep->next_module = root_epic_dev;
 	root_epic_dev = dev;
 
-	ep->pci_bus = bus;
-	ep->pci_dev_fn = devfn;
-	ep->chip_id = chip_id;
+	ep->pci_bus = pci_bus;
+	ep->pci_dev_fn = pci_devfn;
+#if defined(PCI_SUPPORT_VER2)
+	ep->chip_id = pci_find_slot(pci_bus, pci_devfn)->device;
+#else
+	ep->chip_id = chip_tbl[chip_idx].device_id;
+#endif
 
 	/* Find the connected MII xcvrs.
 	   Doing this in open() would allow detecting external xcvrs later, but
@@ -545,7 +537,6 @@
 	int read_cmd = location |
 		(inl(ee_addr) & 0x40) ? EE_READ64_CMD : EE_READ256_CMD;
 
-	printk("EEctrl is %x.\n", inl(ee_addr));
 	outl(EE_ENB & ~EE_CS, ee_addr);
 	outl(EE_ENB, ee_addr);
 
@@ -930,11 +921,9 @@
 static void epic_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
 {
 	struct device *dev = (struct device *)dev_instance;
-	struct epic_private *ep;
-	int status, ioaddr, boguscnt = max_interrupt_work;
-
-	ioaddr = dev->base_addr;
-	ep = (struct epic_private *)dev->priv;
+	struct epic_private *ep = (struct epic_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	int status, boguscnt = max_interrupt_work;
 
 #if defined(__i386__)
 	/* A lock to prevent simultaneous entry bug on Intel SMP machines. */
@@ -1340,12 +1329,18 @@
 
 	if (loc->bus != LOC_PCI) return NULL;
 	bus = loc->b.pci.bus; devfn = loc->b.pci.devfn;
-	printk(KERN_INFO "epic_attach(bus %d, function %d)\n", bus, devfn);
+	printk(KERN_DEBUG "epic_attach(bus %d, function %d)\n", bus, devfn);
 	pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &io);
 	pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq);
 	pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &dev_id);
 	io &= ~3;
-	dev = epic_probe1(bus, devfn, NULL, -1);
+	if (io == 0 || irq == 0) {
+		printk(KERN_ERR "The EPIC/C CardBus Ethernet interface was not "
+			   "assigned an %s.\n" KERN_ERR "  It will not be activated.\n",
+			   io == 0 ? "I/O address" : "IRQ");
+		return NULL;
+	}
+	dev = epic_probe1(bus, devfn, NULL, io, irq, 2, -1);
 	if (dev) {
 		dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL);
 		strcpy(node->dev_name, dev->name);
@@ -1410,14 +1405,10 @@
 
 #ifdef MODULE
 
-/* An additional parameter that may be passed in... */
-static int debug = -1;
-
-int
-init_module(void)
+int init_module(void)
 {
-	if (debug >= 0)
-		epic_debug = debug;
+	if (epic_debug)
+		printk(KERN_INFO "%s", version);
 
 #ifdef CARDBUS
 	register_driver(&epic_ops);
@@ -1427,8 +1418,7 @@
 #endif
 }
 
-void
-cleanup_module(void)
+void cleanup_module(void)
 {
 	struct device *next_dev;
 
@@ -1438,11 +1428,13 @@
 
 	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
 	while (root_epic_dev) {
-		next_dev = ((struct epic_private *)root_epic_dev->priv)->next_module;
+		struct epic_private *ep = (struct epic_private *)root_epic_dev->priv;
+		next_dev = ep->next_module;
 		unregister_netdev(root_epic_dev);
 		release_region(root_epic_dev->base_addr, EPIC_TOTAL_SIZE);
 		kfree(root_epic_dev);
 		root_epic_dev = next_dev;
+		kfree(ep);
 	}
 }
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/eth16i.c linux/drivers/net/eth16i.c
--- linux.vanilla/drivers/net/eth16i.c	Sun Dec  6 00:14:40 1998
+++ linux/drivers/net/eth16i.c	Mon Dec 14 18:33:22 1998
@@ -886,7 +886,7 @@
 	creg[0] &= 0x0F;      /* Mask collision cnr */
 	creg[2] &= 0x7F;      /* Mask DCLEN bit */
 
-#ifdef 0
+#if 0
 	/* 
 	   This was removed because the card was sometimes left to state
 	   from which it couldn't be find anymore. If there is need
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/lance.c linux/drivers/net/lance.c
--- linux.vanilla/drivers/net/lance.c	Sun Dec  6 00:14:40 1998
+++ linux/drivers/net/lance.c	Mon Dec 14 18:29:48 1998
@@ -334,14 +334,17 @@
 		dev->base_addr = io[this_dev];
 		dev->dma = dma[this_dev];
 		dev->init = lance_probe;
+#if NO_AUTOPROBE_MODULE
 		if (io[this_dev] == 0)  {
 			if (this_dev != 0) break; /* only complain once */
 			printk(KERN_NOTICE "lance.c: Module autoprobing not allowed. Append \"io=0xNNN\" value(s).\n");
 			return -EPERM;
 		}
+#endif
 		if (register_netdev(dev) != 0) {
-			printk(KERN_WARNING "lance.c: No PCnet/LANCE card found (i/o = 0x%x).\n", io[this_dev]);
-			if (found != 0) return 0;	/* Got at least one. */
+			if (found != 0)
+				return 0;	/* Got at least one. */
+			printk(KERN_WARNING "lance.c: No PCnet/LANCE card found\n");
 			return -ENXIO;
 		}
 		found++;
@@ -350,8 +353,7 @@
 	return 0;
 }
 
-void
-cleanup_module(void)
+void cleanup_module(void)
 {
 	int this_dev;
 
@@ -390,15 +392,21 @@
 			unsigned short pci_command;
 
 			if (pcibios_find_device (PCI_VENDOR_ID_AMD,
-									 PCI_DEVICE_ID_AMD_LANCE, pci_index,
-									 &pci_bus, &pci_device_fn) != 0)
+						 PCI_DEVICE_ID_AMD_LANCE, pci_index,
+						 &pci_bus, &pci_device_fn) != 0)
 				break;
 			pcibios_read_config_byte(pci_bus, pci_device_fn,
-									 PCI_INTERRUPT_LINE, &pci_irq_line);
+						 PCI_INTERRUPT_LINE, &pci_irq_line);
 			pcibios_read_config_dword(pci_bus, pci_device_fn,
-									  PCI_BASE_ADDRESS_0, &pci_ioaddr);
+						  PCI_BASE_ADDRESS_0, &pci_ioaddr);
 			/* Remove I/O space marker in bit 0. */
 			pci_ioaddr &= ~3;
+	    
+			/* Avoid already found cards from previous calls
+			 */
+			if (check_region(pci_ioaddr, LANCE_TOTAL_SIZE))
+				continue;
+	    
 			/* PCI Spec 2.1 states that it is either the driver or PCI card's
 	 		 * responsibility to set the PCI Master Enable Bit if needed.
 			 *	(From Mark Stockton <marks@schooner.sys.hou.compaq.com>)
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/ne.c linux/drivers/net/ne.c
--- linux.vanilla/drivers/net/ne.c	Sun Jun 21 18:41:04 1998
+++ linux/drivers/net/ne.c	Tue Mar  2 00:51:17 1999
@@ -97,6 +97,7 @@
     {"ET-100","ET-200", {0x00, 0x45, 0x54}}, /* YANG and YA clone */
     {"COMPEX","COMPEX16",{0x00,0x80,0x48}}, /* Broken ISA Compex cards */
     {"E-LAN100", "E-LAN200", {0x00, 0x00, 0x5d}}, /* Broken ne1000 clones */
+    {"PCM-4823", "PCM-4823", {0x00, 0xc0, 0x6c}}, /* Broken Advantech MoBo */
     {0,}
 };
 #endif
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/ni52.c linux/drivers/net/ni52.c
--- linux.vanilla/drivers/net/ni52.c	Fri Jul 24 17:28:59 1998
+++ linux/drivers/net/ni52.c	Mon Dec 14 18:33:43 1998
@@ -363,10 +363,11 @@
 #endif
   int base_addr = dev->base_addr;
 
-  if (base_addr > 0x1ff)		/* Check a single specified location. */
+  if (base_addr > 0x1ff) {		/* Check a single specified location. */
     if( (inb(base_addr+NI52_MAGIC1) == NI52_MAGICVAL1) &&
         (inb(base_addr+NI52_MAGIC2) == NI52_MAGICVAL2))
       return ni52_probe1(dev, base_addr);
+  }
   else if (base_addr > 0)		/* Don't probe at all. */
     return ENXIO;
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/pi2.c linux/drivers/net/pi2.c
--- linux.vanilla/drivers/net/pi2.c	Fri Jul 24 17:29:00 1998
+++ linux/drivers/net/pi2.c	Mon Dec 14 18:33:51 1998
@@ -1452,7 +1452,7 @@
 static int pi_open(struct device *dev)
 {
     unsigned long flags;
-    static first_time = 1;
+    static int first_time = 1;
 
     struct pi_local *lp = (struct pi_local *) dev->priv;
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/ppp.c linux/drivers/net/ppp.c
--- linux.vanilla/drivers/net/ppp.c	Sun Jun 21 18:41:04 1998
+++ linux/drivers/net/ppp.c	Mon Dec 14 18:41:59 1998
@@ -3155,7 +3155,7 @@
 	ppp_stats.tx_heartbeat_errors = 0;
 
 	if (ppp->flags & SC_DEBUG)
-		printk (KERN_INFO "ppp_dev_stats called");
+		printk (KERN_INFO "ppp_dev_stats called\n");
 	return &ppp_stats;
 }
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/pt.c linux/drivers/net/pt.c
--- linux.vanilla/drivers/net/pt.c	Fri Jul 24 17:29:00 1998
+++ linux/drivers/net/pt.c	Mon Dec 14 18:33:58 1998
@@ -909,7 +909,7 @@
 {
     unsigned long flags;
     struct pt_local *lp = dev->priv;
-    static first_time = 1;
+    static int first_time = 1;
     
     if (dev->base_addr & CHANA)
     {
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/rcif.h linux/drivers/net/rcif.h
--- linux.vanilla/drivers/net/rcif.h	Sun Dec  6 00:14:40 1998
+++ linux/drivers/net/rcif.h	Fri Jan 29 14:03:40 1999
@@ -8,7 +8,7 @@
 **  RedCreek InterFace include file.
 **
 **  ---------------------------------------------------------------------
-**  ---     Copyright (c) 1998, RedCreek Communications Inc.          ---
+**  ---     Copyright (c) 1998-1999, RedCreek Communications Inc.     ---
 **  ---                   All rights reserved.                        ---
 **  ---------------------------------------------------------------------
 **
@@ -38,7 +38,7 @@
 
 /* The following protocol revision # should be incremented every time
    a new protocol or new structures are used in this file. */
-int USER_PROTOCOL_REV = 1;     /* used to track different protocol revisions */
+int USER_PROTOCOL_REV = 2;     /* used to track different protocol revisions */
 
 /* define a single TCB & buffer */
 typedef struct                  /* a single buffer */
@@ -124,6 +124,31 @@
             U32 LinkSpeedCode;
         } RCgetspeed;                   /* <---- RCgetspeed */
   
+        /* SETSPEED structure */
+        struct RCsetspeed_tag {
+            U16 LinkSpeedCode;
+        } RCsetspeed;                   /* <---- RCsetspeed */
+
+        /* GETPROM structure */
+        struct  RCgetprom_tag {
+            U32 PromMode;
+        } RCgetprom;                   /* <---- RCgetprom */
+  
+        /* SETPROM structure */
+        struct RCsetprom_tag {
+            U16 PromMode;
+        } RCsetprom;                   /* <---- RCsetprom */
+
+        /* GETBROADCAST structure */
+        struct  RCgetbroadcast_tag {
+            U32 BroadcastMode;
+        } RCgetbroadcast;                   /* <---- RCgetbroadcast */
+  
+        /* SETBROADCAST structure */
+        struct RCsetbroadcast_tag {
+            U16 BroadcastMode;
+        } RCsetbroadcast;                   /* <---- RCsetbroadcast */
+
         /* GETFIRMWAREVER structure */
         #define FirmStringLen 80
         struct RCgetfwver_tag {
@@ -136,12 +161,23 @@
             U32 NetMask;
         } RCgetipandmask;               /* <---- RCgetipandmask */
 
+        /* SETIPANDMASK structure */
+        struct RCsetipnmask_tag {
+            U32 IpAddr;
+            U32 NetMask;
+        } RCsetipandmask;               /* <---- RCsetipandmask */
+
         /* GETMAC structure */
         #define MAC_SIZE 10
         struct RCgetmac_tag {
             U8 mac[MAC_SIZE];
         } RCgetmac;                     /* <---- RCgetmac */
 
+        /* SETMAC structure */
+        struct RCsetmac_tag {
+            U8 mac[MAC_SIZE];
+        } RCsetmac;                     /* <---- RCsetmac */
+
         /* GETLINKSTATUS structure */
         struct RCgetlnkstatus_tag {
             U32 ReturnStatus;
@@ -166,35 +202,56 @@
 union RC_user_data_tag {        /* structure tags used are taken from RC_user_tag structure above */
     struct RCgetinfo_tag      *getinfo;
     struct RCgetspeed_tag     *getspeed;
+    struct RCgetprom_tag *getprom;
+    struct RCgetbroadcast_tag   *getbroadcast;
     struct RCgetfwver_tag     *getfwver;
     struct RCgetipnmask_tag   *getipandmask;
     struct RCgetmac_tag       *getmac;
     struct RCgetlnkstatus_tag *getlinkstatus;
     struct RCgetlinkstats_tag *getlinkstatistics;
     struct RCdefault_tag      *rcdefault;
+    struct RCsetspeed_tag     *setspeed;
+    struct RCsetprom_tag     *setprom;
+    struct RCsetbroadcast_tag     *setbroadcast;
+    struct RCsetipnmask_tag   *setipandmask;
+    struct RCsetmac_tag       *setmac;
 } _RC_user_data;  /* declare as a global, so the defines below will work */
 
 /* 3) Structure short-cut entry */
 /* define structure short-cuts */   /* structure names are taken from RC_user_tag structure above */
 #define RCUS_GETINFO           data.RCgetinfo;
 #define RCUS_GETSPEED          data.RCgetspeed;
+#define RCUS_GETPROM           data.RCgetprom;
+#define RCUS_GETBROADCAST      data.RCgetbroadcast;
 #define RCUS_GETFWVER          data.RCgetfwver;
 #define RCUS_GETIPANDMASK      data.RCgetipandmask;
 #define RCUS_GETMAC            data.RCgetmac;
 #define RCUS_GETLINKSTATUS     data.RCgetlnkstatus;
 #define RCUS_GETLINKSTATISTICS data.RCgetlinkstats;
 #define RCUS_DEFAULT           data.RCdefault;
+#define RCUS_SETSPEED          data.RCsetspeed;
+#define RCUS_SETPROM           data.RCsetprom;
+#define RCUS_SETBROADCAST      data.RCsetbroadcast;
+#define RCUS_SETIPANDMASK      data.RCsetipandmask;
+#define RCUS_SETMAC            data.RCsetmac;
 
 /* 4) Data short-cut entry */
 /* define data short-cuts */    /* pointer names are from RC_user_data_tag union (just below RC_user_tag) */
 #define RCUD_GETINFO           _RC_user_data.getinfo
 #define RCUD_GETSPEED          _RC_user_data.getspeed
+#define RCUD_GETPROM           _RC_user_data.getprom
+#define RCUD_GETBROADCAST      _RC_user_data.getbroadcast
 #define RCUD_GETFWVER          _RC_user_data.getfwver
 #define RCUD_GETIPANDMASK      _RC_user_data.getipandmask
 #define RCUD_GETMAC            _RC_user_data.getmac
 #define RCUD_GETLINKSTATUS     _RC_user_data.getlinkstatus
 #define RCUD_GETLINKSTATISTICS _RC_user_data.getlinkstatistics
 #define RCUD_DEFAULT           _RC_user_data.rcdefault
+#define RCUD_SETSPEED          _RC_user_data.setspeed
+#define RCUD_SETPROM           _RC_user_data.setprom
+#define RCUD_SETBROADCAST      _RC_user_data.setbroadcast
+#define RCUD_SETIPANDMASK      _RC_user_data.setipandmask
+#define RCUD_SETMAC            _RC_user_data.setmac
 
 /* 5) Command identifier entry */
 /* define command identifiers */
@@ -205,7 +262,14 @@
 #define RCUC_GETMAC             0x05
 #define RCUC_GETLINKSTATUS      0x06
 #define RCUC_GETLINKSTATISTICS  0x07
+#define RCUC_GETPROM            0x14
+#define RCUC_GETBROADCAST       0x15
 #define RCUC_DEFAULT            0xff
+#define RCUC_SETSPEED           0x08
+#define RCUC_SETIPANDMASK       0x09
+#define RCUC_SETMAC             0x0a
+#define RCUC_SETPROM            0x16
+#define RCUC_SETBROADCAST       0x17
 
 /* define ioctl commands to use, when talking to RC 45/PCI driver */
 #define RCU_PROTOCOL_REV         SIOCDEVPRIVATE
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/rclanmtl.c linux/drivers/net/rclanmtl.c
--- linux.vanilla/drivers/net/rclanmtl.c	Thu Jan  1 01:00:00 1970
+++ linux/drivers/net/rclanmtl.c	Fri Jan 29 14:03:56 1999
@@ -0,0 +1,2307 @@
+/*
+** *************************************************************************
+**
+**
+**     R C L A N M T L . C             $Revision: 5 $
+**
+**
+**  RedCreek I2O LAN Message Transport Layer program module.
+**
+**  ---------------------------------------------------------------------
+**  ---     Copyright (c) 1997-1999, RedCreek Communications Inc.     ---
+**  ---                   All rights reserved.                        ---
+**  ---------------------------------------------------------------------
+**
+**  File Description:
+**
+**  Host side I2O (Intelligent I/O) LAN message transport layer.
+**
+**  This program is free software; you can redistribute it and/or modify
+**  it under the terms of the GNU General Public License as published by
+**  the Free Software Foundation; either version 2 of the License, or
+**  (at your option) any later version.
+
+**  This program is distributed in the hope that it will be useful,
+**  but WITHOUT ANY WARRANTY; without even the implied warranty of
+**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+**  GNU General Public License for more details.
+
+**  You should have received a copy of the GNU General Public License
+**  along with this program; if not, write to the Free Software
+**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+** *************************************************************************
+*/
+
+#undef DEBUG
+
+#define RC_LINUX_MODULE
+#include "rclanmtl.h"
+
+#define dprintf kprintf
+
+extern int printk(const char * fmt, ...);
+
+ /* RedCreek LAN device Target ID */
+#define RC_LAN_TARGET_ID  0x10 
+ /* RedCreek's OSM default LAN receive Initiator */
+#define DEFAULT_RECV_INIT_CONTEXT  0xA17  
+
+
+/*
+** I2O message structures
+*/
+
+#define    I2O_TID_SZ                                  12
+#define    I2O_FUNCTION_SZ                             8
+
+/* Transaction Reply Lists (TRL) Control Word structure */
+
+#define    I2O_TRL_FLAGS_SINGLE_FIXED_LENGTH           0x00
+#define    I2O_TRL_FLAGS_SINGLE_VARIABLE_LENGTH        0x40
+#define    I2O_TRL_FLAGS_MULTIPLE_FIXED_LENGTH         0x80
+
+/* LAN Class specific functions */
+
+#define    I2O_LAN_PACKET_SEND                         0x3B
+#define    I2O_LAN_SDU_SEND                            0x3D
+#define    I2O_LAN_RECEIVE_POST                        0x3E
+#define    I2O_LAN_RESET                               0x35
+#define    I2O_LAN_SHUTDOWN                            0x37
+
+/* Private Class specfic function */
+#define    I2O_PRIVATE                                 0xFF
+
+/*  I2O Executive Function Codes.  */
+
+#define    I2O_EXEC_ADAPTER_ASSIGN                     0xB3
+#define    I2O_EXEC_ADAPTER_READ                       0xB2
+#define    I2O_EXEC_ADAPTER_RELEASE                    0xB5
+#define    I2O_EXEC_BIOS_INFO_SET                      0xA5
+#define    I2O_EXEC_BOOT_DEVICE_SET                    0xA7
+#define    I2O_EXEC_CONFIG_VALIDATE                    0xBB
+#define    I2O_EXEC_CONN_SETUP                         0xCA
+#define    I2O_EXEC_DEVICE_ASSIGN                      0xB7
+#define    I2O_EXEC_DEVICE_RELEASE                     0xB9
+#define    I2O_EXEC_HRT_GET                            0xA8
+#define    I2O_EXEC_IOP_CLEAR                          0xBE
+#define    I2O_EXEC_IOP_CONNECT                        0xC9
+#define    I2O_EXEC_IOP_RESET                          0xBD
+#define    I2O_EXEC_LCT_NOTIFY                         0xA2
+#define    I2O_EXEC_OUTBOUND_INIT                      0xA1
+#define    I2O_EXEC_PATH_ENABLE                        0xD3
+#define    I2O_EXEC_PATH_QUIESCE                       0xC5
+#define    I2O_EXEC_PATH_RESET                         0xD7
+#define    I2O_EXEC_STATIC_MF_CREATE                   0xDD
+#define    I2O_EXEC_STATIC_MF_RELEASE                  0xDF
+#define    I2O_EXEC_STATUS_GET                         0xA0
+#define    I2O_EXEC_SW_DOWNLOAD                        0xA9
+#define    I2O_EXEC_SW_UPLOAD                          0xAB
+#define    I2O_EXEC_SW_REMOVE                          0xAD
+#define    I2O_EXEC_SYS_ENABLE                         0xD1
+#define    I2O_EXEC_SYS_MODIFY                         0xC1
+#define    I2O_EXEC_SYS_QUIESCE                        0xC3
+#define    I2O_EXEC_SYS_TAB_SET                        0xA3
+
+
+ /* Init Outbound Q status */
+#define    I2O_EXEC_OUTBOUND_INIT_IN_PROGRESS          0x01
+#define    I2O_EXEC_OUTBOUND_INIT_REJECTED             0x02
+#define    I2O_EXEC_OUTBOUND_INIT_FAILED               0x03
+#define    I2O_EXEC_OUTBOUND_INIT_COMPLETE             0x04
+
+
+#define    I2O_UTIL_NOP                                0x00
+
+
+/* I2O Get Status State values */
+
+#define    I2O_IOP_STATE_INITIALIZING                  0x01
+#define    I2O_IOP_STATE_RESET                         0x02
+#define    I2O_IOP_STATE_HOLD                          0x04
+#define    I2O_IOP_STATE_READY                         0x05
+#define    I2O_IOP_STATE_OPERATIONAL                   0x08
+#define    I2O_IOP_STATE_FAILED                        0x10
+#define    I2O_IOP_STATE_FAULTED                       0x11
+
+
+/* Defines for Request Status Codes:  Table 3-1 Reply Status Codes.  */
+
+#define    I2O_REPLY_STATUS_SUCCESS                    0x00
+#define    I2O_REPLY_STATUS_ABORT_DIRTY                0x01
+#define    I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER     0x02
+#define    I2O_REPLY_STATUS_ABORT_PARTIAL_TRANSFER     0x03
+#define    I2O_REPLY_STATUS_ERROR_DIRTY                0x04
+#define    I2O_REPLY_STATUS_ERROR_NO_DATA_TRANSFER     0x05
+#define    I2O_REPLY_STATUS_ERROR_PARTIAL_TRANSFER     0x06
+#define    I2O_REPLY_STATUS_PROCESS_ABORT_DIRTY        0x07
+#define    I2O_REPLY_STATUS_PROCESS_ABORT_NO_DATA_TRANSFER   0x08
+#define    I2O_REPLY_STATUS_PROCESS_ABORT_PARTIAL_TRANSFER   0x09
+#define    I2O_REPLY_STATUS_TRANSACTION_ERROR          0x0A
+#define    I2O_REPLY_STATUS_PROGRESS_REPORT            0x80
+
+
+/* DetailedStatusCode defines for ALL messages: Table 3-2 Detailed Status Codes.*/
+
+#define    I2O_DETAIL_STATUS_SUCCESS                        0x0000
+#define    I2O_DETAIL_STATUS_BAD_KEY                        0x0001
+#define    I2O_DETAIL_STATUS_CHAIN_BUFFER_TOO_LARGE         0x0002
+#define    I2O_DETAIL_STATUS_DEVICE_BUSY                    0x0003
+#define    I2O_DETAIL_STATUS_DEVICE_LOCKED                  0x0004
+#define    I2O_DETAIL_STATUS_DEVICE_NOT_AVAILABLE           0x0005
+#define    I2O_DETAIL_STATUS_DEVICE_RESET                   0x0006
+#define    I2O_DETAIL_STATUS_INAPPROPRIATE_FUNCTION         0x0007
+#define    I2O_DETAIL_STATUS_INSUFFICIENT_RESOURCE_HARD     0x0008
+#define    I2O_DETAIL_STATUS_INSUFFICIENT_RESOURCE_SOFT     0x0009
+#define    I2O_DETAIL_STATUS_INVALID_INITIATOR_ADDRESS      0x000A
+#define    I2O_DETAIL_STATUS_INVALID_MESSAGE_FLAGS          0x000B
+#define    I2O_DETAIL_STATUS_INVALID_OFFSET                 0x000C
+#define    I2O_DETAIL_STATUS_INVALID_PARAMETER              0x000D
+#define    I2O_DETAIL_STATUS_INVALID_REQUEST                0x000E
+#define    I2O_DETAIL_STATUS_INVALID_TARGET_ADDRESS         0x000F
+#define    I2O_DETAIL_STATUS_MESSAGE_TOO_LARGE              0x0010
+#define    I2O_DETAIL_STATUS_MESSAGE_TOO_SMALL              0x0011
+#define    I2O_DETAIL_STATUS_MISSING_PARAMETER              0x0012
+#define    I2O_DETAIL_STATUS_NO_SUCH_PAGE                   0x0013
+#define    I2O_DETAIL_STATUS_REPLY_BUFFER_FULL              0x0014
+#define    I2O_DETAIL_STATUS_TCL_ERROR                      0x0015
+#define    I2O_DETAIL_STATUS_TIMEOUT                        0x0016
+#define    I2O_DETAIL_STATUS_UNKNOWN_ERROR                  0x0017
+#define    I2O_DETAIL_STATUS_UNKNOWN_FUNCTION               0x0018
+#define    I2O_DETAIL_STATUS_UNSUPPORTED_FUNCTION           0x0019
+#define    I2O_DETAIL_STATUS_UNSUPPORTED_VERSION            0x001A
+
+ /* I2O msg header defines for VersionOffset */
+#define I2OMSGVER_1_5   0x0001
+#define SGL_OFFSET_0    I2OMSGVER_1_5
+#define SGL_OFFSET_4    (0x0040 | I2OMSGVER_1_5)
+#define TRL_OFFSET_5    (0x0050 | I2OMSGVER_1_5)
+#define TRL_OFFSET_6    (0x0060 | I2OMSGVER_1_5)
+
+ /* I2O msg header defines for MsgFlags */
+#define MSG_STATIC      0x0100
+#define MSG_64BIT_CNTXT 0x0200
+#define MSG_MULTI_TRANS 0x1000
+#define MSG_FAIL        0x2000
+#define MSG_LAST        0x4000
+#define MSG_REPLY       0x8000
+
+  /* normal LAN request message MsgFlags and VersionOffset (0x1041) */
+#define LAN_MSG_REQST  (MSG_MULTI_TRANS | SGL_OFFSET_4)
+
+ /* minimum size msg */
+#define THREE_WORD_MSG_SIZE 0x00030000
+#define FOUR_WORD_MSG_SIZE  0x00040000
+#define FIVE_WORD_MSG_SIZE  0x00050000
+#define SIX_WORD_MSG_SIZE   0x00060000
+#define SEVEN_WORD_MSG_SIZE 0x00070000
+#define EIGHT_WORD_MSG_SIZE 0x00080000
+#define NINE_WORD_MSG_SIZE  0x00090000
+
+/* Special TID Assignments */
+
+#define I2O_IOP_TID   0
+#define I2O_HOST_TID  1
+
+ /* RedCreek I2O private message codes */
+#define RC_PRIVATE_GET_MAC_ADDR     0x0001/**/ /* OBSOLETE */
+#define RC_PRIVATE_SET_MAC_ADDR     0x0002
+#define RC_PRIVATE_GET_NIC_STATS    0x0003
+#define RC_PRIVATE_GET_LINK_STATUS  0x0004
+#define RC_PRIVATE_SET_LINK_SPEED   0x0005
+#define RC_PRIVATE_SET_IP_AND_MASK  0x0006
+/* #define RC_PRIVATE_GET_IP_AND_MASK  0x0007 */ /* OBSOLETE */
+#define RC_PRIVATE_GET_LINK_SPEED   0x0008
+#define RC_PRIVATE_GET_FIRMWARE_REV 0x0009
+/* #define RC_PRIVATE_GET_MAC_ADDR     0x000A *//**/
+#define RC_PRIVATE_GET_IP_AND_MASK  0x000B /**/
+#define RC_PRIVATE_DEBUG_MSG        0x000C
+#define RC_PRIVATE_REPORT_DRIVER_CAPABILITY  0x000D
+#define RC_PRIVATE_SET_PROMISCUOUS_MODE  0x000e
+#define RC_PRIVATE_GET_PROMISCUOUS_MODE  0x000f
+#define RC_PRIVATE_SET_BROADCAST_MODE    0x0010
+#define RC_PRIVATE_GET_BROADCAST_MODE    0x0011
+
+#define RC_PRIVATE_REBOOT           0x00FF
+
+
+/* I2O message header */
+typedef struct _I2O_MESSAGE_FRAME 
+{
+    U8                          VersionOffset;
+    U8                          MsgFlags;
+    U16                         MessageSize;
+    BF                          TargetAddress:I2O_TID_SZ;
+    BF                          InitiatorAddress:I2O_TID_SZ;
+    BF                          Function:I2O_FUNCTION_SZ;
+    U32                         InitiatorContext;
+    /* SGL[] */ 
+}
+I2O_MESSAGE_FRAME, *PI2O_MESSAGE_FRAME;
+
+
+ /* assumed a 16K minus 256 byte space for outbound queue message frames */
+#define MSG_FRAME_SIZE  512
+#define NMBR_MSG_FRAMES 30
+
+/*
+**  Message Unit CSR definitions for RedCreek PCI45 board
+*/
+typedef struct tag_rcatu 
+{
+    volatile unsigned long APICRegSel;  /* APIC Register Select */
+    volatile unsigned long reserved0;
+    volatile unsigned long APICWinReg;  /* APIC Window Register */
+    volatile unsigned long reserved1;
+    volatile unsigned long InMsgReg0;   /* inbound message register 0 */
+    volatile unsigned long InMsgReg1;   /* inbound message register 1 */
+    volatile unsigned long OutMsgReg0;  /* outbound message register 0 */
+    volatile unsigned long OutMsgReg1;  /* outbound message register 1 */
+    volatile unsigned long InDoorReg;   /* inbound doorbell register */
+    volatile unsigned long InIntStat;   /* inbound interrupt status register */
+    volatile unsigned long InIntMask;   /* inbound interrupt mask register */
+    volatile unsigned long OutDoorReg;  /* outbound doorbell register */
+    volatile unsigned long OutIntStat;  /* outbound interrupt status register */
+    volatile unsigned long OutIntMask;  /* outbound interrupt mask register */
+    volatile unsigned long reserved2;
+    volatile unsigned long reserved3;
+    volatile unsigned long InQueue;     /* inbound queue port */
+    volatile unsigned long OutQueue;    /* outbound queue port */
+    volatile unsigned long reserved4;
+    volatile unsigned long reserver5;
+    /* RedCreek extension */
+    volatile unsigned long EtherMacLow;
+    volatile unsigned long EtherMacHi;
+    volatile unsigned long IPaddr;
+    volatile unsigned long IPmask;
+}
+ATU, *PATU;
+
+ /* 
+ ** typedef PAB
+ **
+ ** PCI Adapter Block - holds instance specific information and is located
+ ** in a reserved space at the start of the message buffer allocated by user.
+ */
+typedef struct
+{
+    PATU             p_atu;                /* ptr to  ATU register block */
+    PU8              pPci45LinBaseAddr;
+    PU8              pLinOutMsgBlock;
+    U32              outMsgBlockPhyAddr; 
+    PFNTXCALLBACK    pTransCallbackFunc;
+    PFNRXCALLBACK    pRecvCallbackFunc;
+    PFNCALLBACK      pRebootCallbackFunc;
+    PFNCALLBACK      pCallbackFunc;
+    U16              IOPState;
+    U16              InboundMFrameSize;
+}
+PAB, *PPAB;
+
+ /* 
+ ** in reserved space right after PAB in host memory is area for returning
+ ** values from card 
+ */
+
+ /* 
+ ** Array of pointers to PCI Adapter Blocks.
+ ** Indexed by a zero based (0-31) interface number.
+ */ 
+#define MAX_ADAPTERS 32
+static PPAB  PCIAdapterBlock[MAX_ADAPTERS] = 
+{
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+
+/*
+** typedef NICSTAT
+**
+** Data structure for NIC statistics retruned from PCI card.  Data copied from
+** here to user allocated RCLINKSTATS (see rclanmtl.h) structure.
+*/
+typedef struct tag_NicStat 
+{
+    unsigned long   TX_good; 
+    unsigned long   TX_maxcol;
+    unsigned long   TX_latecol;
+    unsigned long   TX_urun;
+    unsigned long   TX_crs;         /* lost carrier sense */
+    unsigned long   TX_def;         /* transmit deferred */
+    unsigned long   TX_singlecol;   /* single collisions */
+    unsigned long   TX_multcol;
+    unsigned long   TX_totcol;
+    unsigned long   Rcv_good;
+    unsigned long   Rcv_CRCerr;
+    unsigned long   Rcv_alignerr;
+    unsigned long   Rcv_reserr;     /* rnr'd pkts */
+    unsigned long   Rcv_orun;
+    unsigned long   Rcv_cdt;
+    unsigned long   Rcv_runt;
+    unsigned long   dump_status;    /* last field directly from the chip */
+} 
+NICSTAT, *P_NICSTAT;
+ 
+
+#define DUMP_DONE   0x0000A005      /* completed statistical dump */
+#define DUMP_CLEAR  0x0000A007      /* completed stat dump and clear counters */
+
+
+static volatile int msgFlag;
+
+
+/* local function prototypes */
+static void ProcessOutboundI2OMsg(PPAB pPab, U32 phyMsgAddr);
+static int FillI2OMsgSGLFromTCB(PU32 pMsg, PRCTCB pXmitCntrlBlock);
+static int GetI2OStatus(PPAB pPab);
+static int SendI2OOutboundQInitMsg(PPAB pPab);
+static int SendEnableSysMsg(PPAB pPab);
+
+
+/* 1st 100h bytes of message block is reserved for messenger instance */
+#define ADAPTER_BLOCK_RESERVED_SPACE 0x100
+
+/*
+** =========================================================================
+** RCInitI2OMsgLayer()
+**
+** Initialize the RedCreek I2O Module and adapter.
+**
+** Inputs:  AdapterID - interface number from 0 to 15
+**          pciBaseAddr - virual base address of PCI (set by BIOS)
+**          p_msgbuf - virual address to private message block (min. 16K)
+**          p_phymsgbuf - physical address of private message block
+**          TransmitCallbackFunction - address of transmit callback function
+**          ReceiveCallbackFunction  - address of receive  callback function
+**
+** private message block is allocated by user.  It must be in locked pages.
+** p_msgbuf and p_phymsgbuf point to the same location.  Must be contigous
+** memory block of a minimum of 16K byte and long word aligned.
+** =========================================================================
+*/
+RC_RETURN
+RCInitI2OMsgLayer(U16 AdapterID, U32 pciBaseAddr, 
+                  PU8 p_msgbuf,  PU8 p_phymsgbuf,
+                  PFNTXCALLBACK  TransmitCallbackFunction,
+                  PFNRXCALLBACK  ReceiveCallbackFunction,
+                  PFNCALLBACK    RebootCallbackFunction)
+{
+    int result;
+    PPAB pPab; 
+    
+#ifdef DEBUG
+    kprintf("InitI2O: Adapter:0x%04.4ux ATU:0x%08.8ulx msgbuf:0x%08.8ulx phymsgbuf:0x%08.8ulx\n"
+            "TransmitCallbackFunction:0x%08.8ulx  ReceiveCallbackFunction:0x%08.8ulx\n",
+            AdapterID, pciBaseAddr, p_msgbuf, p_phymsgbuf, TransmitCallbackFunction, ReceiveCallbackFunction);
+#endif /* DEBUG */
+
+    
+    /* Check if this interface already initialized - if so, shut it down */
+    if (PCIAdapterBlock[AdapterID] != NULL)
+    {
+        printk("PCIAdapterBlock[%d]!=NULL\n", AdapterID);
+//        RCResetLANCard(AdapterID, 0, (PU32)NULL, (PFNCALLBACK)NULL);
+        PCIAdapterBlock[AdapterID] = NULL;
+    }
+
+    /* 
+    ** store adapter instance values in adapter block.
+    ** Adapter block is at beginning of message buffer
+    */  
+    pPab = (PPAB)p_msgbuf;
+    
+    pPab->p_atu = (PATU)pciBaseAddr;
+    pPab->pPci45LinBaseAddr =  (PU8)pciBaseAddr;
+    
+    /* Set outbound message frame addr - skip over Adapter Block */
+    pPab->outMsgBlockPhyAddr = (U32)(p_phymsgbuf + ADAPTER_BLOCK_RESERVED_SPACE);
+    pPab->pLinOutMsgBlock    = (PU8)(p_msgbuf + ADAPTER_BLOCK_RESERVED_SPACE);
+
+    /* store callback function addresses */
+    pPab->pTransCallbackFunc = TransmitCallbackFunction;
+    pPab->pRecvCallbackFunc  = ReceiveCallbackFunction;
+    pPab->pRebootCallbackFunc  = RebootCallbackFunction;
+    pPab->pCallbackFunc  = (PFNCALLBACK)NULL;
+
+    /*
+    ** Initialize I2O IOP
+    */
+    result = GetI2OStatus(pPab);
+    
+    if (result != RC_RTN_NO_ERROR)
+        return result;
+
+    if (pPab->IOPState == I2O_IOP_STATE_OPERATIONAL)
+    {
+        printk("pPab->IOPState == op: resetting adapter\n");
+        RCResetLANCard(AdapterID, 0, (PU32)NULL, (PFNCALLBACK)NULL);
+    }
+        
+    result = SendI2OOutboundQInitMsg(pPab);
+    
+    if (result != RC_RTN_NO_ERROR)
+        return result;
+
+    result = SendEnableSysMsg(pPab);
+   
+    if (result != RC_RTN_NO_ERROR)
+        return result;
+   
+    PCIAdapterBlock[AdapterID] = pPab;
+    return RC_RTN_NO_ERROR;
+}
+
+/*
+** =========================================================================
+** Disable and Enable I2O interrupts.  I2O interrupts are enabled at Init time
+** but can be disabled and re-enabled through these two function calls.
+** Packets will still be put into any posted received buffers and packets will
+** be sent through RCI2OSendPacket() functions.  Disabling I2O interrupts
+** will prevent hardware interrupt to host even though the outbound I2O msg
+** queue is not emtpy.
+** =========================================================================
+*/
+#define i960_OUT_POST_Q_INT_BIT        0x0008 /* bit set masks interrupts */
+
+RC_RETURN RCDisableI2OInterrupts(U16 AdapterID)
+{
+    PPAB pPab;
+
+
+    pPab = PCIAdapterBlock[AdapterID];
+    
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+        
+    pPab->p_atu->OutIntMask |= i960_OUT_POST_Q_INT_BIT;
+
+    return RC_RTN_NO_ERROR;
+}
+
+RC_RETURN RCEnableI2OInterrupts(U16 AdapterID)
+{
+    PPAB pPab;
+
+    pPab = PCIAdapterBlock[AdapterID];
+    
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    pPab->p_atu->OutIntMask &= ~i960_OUT_POST_Q_INT_BIT;
+    
+    return RC_RTN_NO_ERROR;
+
+}
+
+
+/*
+** =========================================================================
+** RCI2OSendPacket()
+** =========================================================================
+*/
+RC_RETURN
+RCI2OSendPacket(U16 AdapterID, U32 InitiatorContext, PRCTCB pTransCtrlBlock)
+{
+    U32 msgOffset;
+    PU32 pMsg;
+    int size;
+    PPAB pPab;
+
+#ifdef DEBUG
+    kprintf("RCI2OSendPacket()...\n");
+#endif /* DEBUG */
+    
+    pPab = PCIAdapterBlock[AdapterID];
+
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    /* get Inbound free Q entry - reading from In Q gets free Q entry */
+    /* offset to Msg Frame in PCI msg block */
+
+    msgOffset = pPab->p_atu->InQueue; 
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+#ifdef DEBUG
+        kprintf("RCI2OSendPacket(): Inbound Free Q empty!\n");
+#endif /* DEBUG */
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+        
+    /* calc virual address of msg - virual already mapped to physical */    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+    size = FillI2OMsgSGLFromTCB(pMsg + 4, pTransCtrlBlock);
+
+    if (size == -1) /* error processing TCB - send NOP msg */
+    {
+#ifdef DEBUG
+        kprintf("RCI2OSendPacket(): Error Rrocess TCB!\n");
+#endif /* DEBUG */
+        pMsg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0;
+        pMsg[1] = I2O_UTIL_NOP << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+        return RC_RTN_TCB_ERROR;
+    }
+    else /* send over msg header */
+    {    
+        pMsg[0] = (size + 4) << 16 | LAN_MSG_REQST; /* send over message size and flags */
+        pMsg[1] = I2O_LAN_PACKET_SEND << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+        pMsg[2] = InitiatorContext;
+        pMsg[3] = 0;  /* batch reply */
+        /* post to Inbound Post Q */   
+        pPab->p_atu->InQueue = msgOffset;
+        return RC_RTN_NO_ERROR;
+    }
+}
+ 
+
+/*
+** =========================================================================
+** RCI2OPostRecvBuffer()
+**
+** inputs:  pBufrCntrlBlock - pointer to buffer control block
+**
+** returns TRUE if successful in sending message, else FALSE.
+** =========================================================================
+*/
+RC_RETURN
+RCPostRecvBuffers(U16 AdapterID, PRCTCB pTransCtrlBlock)
+{
+    U32 msgOffset;
+    PU32 pMsg;
+    int size;
+    PPAB pPab;
+
+#ifdef DEBUG
+    kprintf("RCPostRecvBuffers()...\n");
+#endif /* DEBUG */
+    
+    /* search for DeviceHandle */
+    pPab = PCIAdapterBlock[AdapterID];
+
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+
+    /* get Inbound free Q entry - reading from In Q gets free Q entry */
+    /* offset to Msg Frame in PCI msg block */
+    msgOffset = pPab->p_atu->InQueue; 
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+#ifdef DEBUG
+        kprintf("RCPostRecvBuffers(): Inbound Free Q empty!\n");
+#endif /* DEBUG */
+        return RC_RTN_FREE_Q_EMPTY;
+   
+    }
+    /* calc virual address of msg - virual already mapped to physical */    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+    size = FillI2OMsgSGLFromTCB(pMsg + 4, pTransCtrlBlock);
+
+    if (size == -1) /* error prcessing TCB - send 3 DWORD private msg == NOP */
+    {
+#ifdef DEBUG
+        kprintf("RCPostRecvBuffers(): Error Processing TCB! size = %d\n", size);
+#endif /* DEBUG */
+        pMsg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0;
+        pMsg[1] = I2O_UTIL_NOP << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+        /* post to Post Q */   
+        pPab->p_atu->InQueue = msgOffset;
+        return RC_RTN_TCB_ERROR;
+    }
+    else /* send over size msg header */
+    {    
+        pMsg[0] = (size + 4) << 16 | LAN_MSG_REQST; /* send over message size and flags */
+        pMsg[1] = I2O_LAN_RECEIVE_POST << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+        pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+        pMsg[3] = *(PU32)pTransCtrlBlock; /* number of packet buffers */
+        /* post to Post Q */   
+        pPab->p_atu->InQueue = msgOffset;
+        return RC_RTN_NO_ERROR;
+    }
+}
+
+
+/*
+** =========================================================================
+** RCProcI2OMsgQ()
+**
+** Process I2O outbound message queue until empty.
+** =========================================================================
+*/
+void 
+RCProcI2OMsgQ(U16 AdapterID)
+{
+    U32 phyAddrMsg;
+    PU8 p8Msg;
+    PU32 p32;
+    U16 count;
+    PPAB pPab;
+    unsigned char debug_msg[20];
+    
+    pPab = PCIAdapterBlock[AdapterID];
+    
+    if (pPab == NULL)
+        return;
+    
+    phyAddrMsg = pPab->p_atu->OutQueue;
+
+    while (phyAddrMsg != 0xFFFFFFFF)
+    {
+        p8Msg = pPab->pLinOutMsgBlock + (phyAddrMsg - pPab->outMsgBlockPhyAddr);
+        p32 = (PU32)p8Msg;
+        
+        //printk(" msg: 0x%x  0x%x \n", p8Msg[7], p32[5]);
+
+        /* 
+        ** Send Packet Reply Msg
+        */
+        if (I2O_LAN_PACKET_SEND == p8Msg[7])  /* function code byte */
+        {
+            count = *(PU16)(p8Msg+2);
+            count -= p8Msg[0] >> 4;
+            /* status, count, context[], adapter */
+            (*pPab->pTransCallbackFunc)(p8Msg[19], count, p32+5, AdapterID);
+        }             
+        /* 
+        ** Receive Packet Reply Msg */
+        else if (I2O_LAN_RECEIVE_POST == p8Msg[7])
+        {
+#ifdef DEBUG    
+            kprintf("I2O_RECV_REPLY pPab:0x%08.8ulx p8Msg:0x%08.8ulx p32:0x%08.8ulx\n", pPab, p8Msg, p32);
+            kprintf("msg: 0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
+                    p32[0], p32[1], p32[2], p32[3]);
+            kprintf("     0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
+                    p32[4], p32[5], p32[6], p32[7]);
+            kprintf("     0x%08.8ulx:0X%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
+                    p32[8], p32[9], p32[10], p32[11]);
+#endif
+            /*  status, count, buckets remaining, packetParmBlock, adapter */
+            (*pPab->pRecvCallbackFunc)(p8Msg[19], p8Msg[12], p32[5], p32+6, AdapterID);
+      
+      
+        }
+        else if (I2O_LAN_RESET == p8Msg[7] || I2O_LAN_SHUTDOWN == p8Msg[7])
+        {
+            if (pPab->pCallbackFunc)
+            {
+                (*pPab->pCallbackFunc)(p8Msg[19],0,0,AdapterID);
+            }
+            else
+            {
+                pPab->pCallbackFunc = (PFNCALLBACK) 1;
+            }
+            //PCIAdapterBlock[AdapterID] = 0;
+        }
+        else if (I2O_PRIVATE == p8Msg[7])
+        {
+            //printk("i2o private 0x%x, 0x%x \n", p8Msg[7], p32[5]);
+            switch (p32[5])
+            {
+            case RC_PRIVATE_DEBUG_MSG:
+                msgFlag = 1;
+                /*printk("Received I2O_PRIVATE msg\n");*/
+                debug_msg[15]  = (p32[6]&0xff000000) >> 24;
+                debug_msg[14]  = (p32[6]&0x00ff0000) >> 16;
+                debug_msg[13]  = (p32[6]&0x0000ff00) >> 8;
+                debug_msg[12]  = (p32[6]&0x000000ff);
+
+                debug_msg[11]  = (p32[7]&0xff000000) >> 24;
+                debug_msg[10]  = (p32[7]&0x00ff0000) >> 16;
+                debug_msg[ 9]  = (p32[7]&0x0000ff00) >> 8;
+                debug_msg[ 8]  = (p32[7]&0x000000ff);
+
+                debug_msg[ 7]  = (p32[8]&0xff000000) >> 24;
+                debug_msg[ 6]  = (p32[8]&0x00ff0000) >> 16;
+                debug_msg[ 5] = (p32[8]&0x0000ff00) >> 8;
+                debug_msg[ 4] = (p32[8]&0x000000ff);
+
+                debug_msg[ 3] = (p32[9]&0xff000000) >> 24;
+                debug_msg[ 2] = (p32[9]&0x00ff0000) >> 16;
+                debug_msg[ 1] = (p32[9]&0x0000ff00) >> 8;
+                debug_msg[ 0] = (p32[9]&0x000000ff);
+
+                debug_msg[16] = '\0';
+                printk (debug_msg);
+                break;
+            case RC_PRIVATE_REBOOT:
+                printk("Adapter reboot initiated...\n");
+                if (pPab->pRebootCallbackFunc)
+                {
+                    (*pPab->pRebootCallbackFunc)(0,0,0,AdapterID);
+                }
+                break;
+            default:
+                printk("Unknown private I2O msg received: 0x%x\n",
+                       p32[5]);
+                break;
+            }
+        }
+
+        /* 
+        ** Process other Msg's
+        */
+        else
+        {
+            ProcessOutboundI2OMsg(pPab, phyAddrMsg);
+        }
+        
+        /* return MFA to outbound free Q*/
+        pPab->p_atu->OutQueue = phyAddrMsg;
+    
+        /* any more msgs? */
+        phyAddrMsg = pPab->p_atu->OutQueue;
+    }
+}
+
+
+/*
+** =========================================================================
+**  Returns LAN interface statistical counters to space provided by caller at
+**  StatsReturnAddr.  Returns 0 if success, else RC_RETURN code.
+**  This function will call the WaitCallback function provided by
+**  user while waiting for card to respond.
+** =========================================================================
+*/
+RC_RETURN
+RCGetLinkStatistics(U16 AdapterID, 
+                    P_RCLINKSTATS StatsReturnAddr,
+                    PFNWAITCALLBACK WaitCallback)
+{
+    U32 msgOffset;
+    volatile U32 timeout;
+    volatile PU32 pMsg;
+    volatile PU32 p32, pReturnAddr;
+    P_NICSTAT pStats;
+    int i;
+    PPAB pPab;
+
+/*kprintf("Get82558Stats() StatsReturnAddr:0x%08.8ulx\n", StatsReturnAddr);*/
+
+    pPab = PCIAdapterBlock[AdapterID];
+
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    msgOffset = pPab->p_atu->InQueue;
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+#ifdef DEBUG
+        kprintf("Get8255XStats(): Inbound Free Q empty!\n");
+#endif
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+
+    /* calc virual address of msg - virual already mapped to physical */
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+/*dprintf("Get82558Stats - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n", pMsg, msgOffset);*/
+/*dprintf("Get82558Stats - pMsg = 0x%08X, InQ msgOffset = 0x%08X\n", pMsg, msgOffset);*/
+
+    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+    pMsg[3] = 0x112; /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_NIC_STATS;
+    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+
+    p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+
+    pStats = (P_NICSTAT)p32;
+    pStats->dump_status = 0xFFFFFFFF;
+
+    /* post to Inbound Post Q */
+    pPab->p_atu->InQueue = msgOffset;
+
+    timeout = 100000;
+    while (1)
+    {
+        if (WaitCallback)
+            (*WaitCallback)();
+        
+        for (i = 0; i < 1000; i++)
+            ;
+
+        if (pStats->dump_status != 0xFFFFFFFF)
+            break;
+
+        if (!timeout--)
+        {
+#ifdef DEBUG
+            kprintf("RCGet82558Stats() Timeout waiting for NIC statistics\n");
+#endif
+            return RC_RTN_MSG_REPLY_TIMEOUT;
+        }
+    }
+    
+    pReturnAddr = (PU32)StatsReturnAddr;
+    
+    /* copy Nic stats to user's structure */
+    for (i = 0; i < (int) sizeof(RCLINKSTATS) / 4; i++)
+        pReturnAddr[i] = p32[i];
+   
+    return RC_RTN_NO_ERROR;     
+}
+
+
+/*
+** =========================================================================
+** Get82558LinkStatus()
+** =========================================================================
+*/
+RC_RETURN
+RCGetLinkStatus(U16 AdapterID, PU32 ReturnAddr, PFNWAITCALLBACK WaitCallback)
+{
+    U32 msgOffset;
+    volatile U32 timeout;
+    volatile PU32 pMsg;
+    volatile PU32 p32;
+    PPAB pPab;
+
+/*kprintf("Get82558LinkStatus() ReturnPhysAddr:0x%08.8ulx\n", ReturnAddr);*/
+
+    pPab = PCIAdapterBlock[AdapterID];
+
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    msgOffset = pPab->p_atu->InQueue;
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+#ifdef DEBUG
+        dprintf("Get82558LinkStatus(): Inbound Free Q empty!\n");
+#endif
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+
+    /* calc virual address of msg - virual already mapped to physical */
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+/*dprintf("Get82558LinkStatus - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n", pMsg, msgOffset);*/
+/*dprintf("Get82558LinkStatus - pMsg = 0x%08X, InQ msgOffset = 0x%08X\n", pMsg, msgOffset);*/
+
+    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+    pMsg[3] = 0x112; /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_LINK_STATUS;
+    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+
+    p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    *p32 = 0xFFFFFFFF;
+    
+    /* post to Inbound Post Q */
+    pPab->p_atu->InQueue = msgOffset;
+
+    timeout = 100000;
+    while (1)
+    {
+        U32 i;
+
+        if (WaitCallback)
+            (*WaitCallback)();
+        
+        for (i = 0; i < 1000; i++)
+            ;
+
+        if (*p32 != 0xFFFFFFFF)
+            break;
+
+        if (!timeout--)
+        {
+#ifdef DEBUG
+            kprintf("Timeout waiting for link status\n");
+#endif    
+            return RC_RTN_MSG_REPLY_TIMEOUT;
+        }
+    }
+    
+    *ReturnAddr = *p32; /* 1 = up 0 = down */
+    
+    return RC_RTN_NO_ERROR;
+    
+}
+
+/*
+** =========================================================================
+** RCGetMAC()
+**
+** get the MAC address the adapter is listening for in non-promiscous mode.
+** MAC address is in media format.
+** =========================================================================
+*/
+RC_RETURN
+RCGetMAC(U16 AdapterID, PU8 mac, PFNWAITCALLBACK WaitCallback)
+{
+    unsigned i, timeout;
+    U32      off;
+    PU32     p;
+    U32      temp[2];
+    PPAB     pPab;
+    PATU     p_atu;
+    
+    pPab = PCIAdapterBlock[AdapterID];
+    
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    p_atu = pPab->p_atu;
+
+    p_atu->EtherMacLow = 0;     /* first zero return data */
+    p_atu->EtherMacHi = 0;
+    
+    off = p_atu->InQueue;   /* get addresss of message */
+ 
+    if (0xFFFFFFFF == off)
+        return RC_RTN_FREE_Q_EMPTY;
+
+    p = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+#ifdef RCDEBUG
+    printk("RCGetMAC: p_atu 0x%08x, off 0x%08x, p 0x%08x\n", 
+           (uint)p_atu, (uint)off, (uint)p);
+#endif /* RCDEBUG */
+    /* setup private message */
+    p[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0;
+    p[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    p[2] = 0;               /* initiator context */
+    p[3] = 0x218;           /* transaction context */
+    p[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_MAC_ADDR;
+
+
+    p_atu->InQueue = off;   /* send it to the I2O device */
+#ifdef RCDEBUG
+    printk("RCGetMAC: p_atu 0x%08x, off 0x%08x, p 0x%08x\n", 
+           (uint)p_atu, (uint)off, (uint)p);
+#endif /* RCDEBUG */
+
+    /* wait for the rcpci45 board to update the info */
+    timeout = 1000000;
+    while (0 == p_atu->EtherMacLow) 
+    {
+        if (WaitCallback)
+            (*WaitCallback)();
+    
+        for (i = 0; i < 1000; i++)
+            ;
+
+        if (!timeout--)
+        {
+            printk("rc_getmac: Timeout\n");
+            return RC_RTN_MSG_REPLY_TIMEOUT;
+        }
+    }
+    
+    /* read the mac address  */
+    temp[0] = p_atu->EtherMacLow;
+    temp[1] = p_atu->EtherMacHi;
+    memcpy((char *)mac, (char *)temp, 6);
+
+
+#ifdef RCDEBUG
+//    printk("rc_getmac: 0x%X\n", ptr);
+#endif /* RCDEBUG */
+
+    return RC_RTN_NO_ERROR;
+}
+
+ 
+/*
+** =========================================================================
+** RCSetMAC()
+**
+** set MAC address the adapter is listening for in non-promiscous mode.
+** MAC address is in media format.
+** =========================================================================
+*/
+RC_RETURN
+RCSetMAC(U16 AdapterID, PU8 mac)
+{
+    U32  off;
+    PU32 pMsg;
+    PPAB pPab;
+
+
+    pPab = PCIAdapterBlock[AdapterID];
+    
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    off = pPab->p_atu->InQueue; /* get addresss of message */
+    
+    if (0xFFFFFFFF == off)
+        return RC_RTN_FREE_Q_EMPTY;
+    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+    /* setup private message */
+    pMsg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = 0;                 /* initiator context */
+    pMsg[3] = 0x219;             /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_MAC_ADDR;
+    pMsg[5] = *(unsigned *)mac;  /* first four bytes */
+    pMsg[6] = *(unsigned *)(mac + 4); /* last two bytes */
+    
+    pPab->p_atu->InQueue = off;   /* send it to the I2O device */
+
+    return RC_RTN_NO_ERROR ;
+}
+
+
+/*
+** =========================================================================
+** RCSetLinkSpeed()
+**
+** set ethernet link speed. 
+** input: speedControl - determines action to take as follows
+**          0 = reset and auto-negotiate (NWay)
+**          1 = Full Duplex 100BaseT
+**          2 = Half duplex 100BaseT
+**          3 = Full Duplex  10BaseT
+**          4 = Half duplex  10BaseT
+**          all other values are ignore (do nothing)
+** =========================================================================
+*/
+RC_RETURN
+RCSetLinkSpeed(U16 AdapterID, U16 LinkSpeedCode)
+{
+    U32  off;
+    PU32 pMsg;
+    PPAB pPab;
+
+    
+    pPab =PCIAdapterBlock[AdapterID];
+     
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    off = pPab->p_atu->InQueue; /* get addresss of message */
+    
+    if (0xFFFFFFFF == off)
+        return RC_RTN_FREE_Q_EMPTY;
+    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+    /* setup private message */
+    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = 0;                 /* initiator context */
+    pMsg[3] = 0x219;             /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_LINK_SPEED;
+    pMsg[5] = LinkSpeedCode;     /* link speed code */
+
+    pPab->p_atu->InQueue = off;   /* send it to the I2O device */
+
+    return RC_RTN_NO_ERROR ;
+}
+/*
+** =========================================================================
+** RCSetPromiscuousMode()
+**
+** Defined values for Mode:
+**  0 - turn off promiscuous mode
+**  1 - turn on  promiscuous mode
+**
+** =========================================================================
+*/
+RC_RETURN
+RCSetPromiscuousMode(U16 AdapterID, U16 Mode)
+{
+    U32  off;
+    PU32 pMsg;
+    PPAB pPab;
+    
+    pPab =PCIAdapterBlock[AdapterID];
+     
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    off = pPab->p_atu->InQueue; /* get addresss of message */
+    
+    if (0xFFFFFFFF == off)
+        return RC_RTN_FREE_Q_EMPTY;
+    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+    /* setup private message */
+    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = 0;                 /* initiator context */
+    pMsg[3] = 0x219;             /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_PROMISCUOUS_MODE;
+    pMsg[5] = Mode;     /* promiscuous mode setting */
+
+    pPab->p_atu->InQueue = off;   /* send it to the device */
+
+    return RC_RTN_NO_ERROR ;
+}
+/*
+** =========================================================================
+** RCGetPromiscuousMode()
+**
+** get promiscuous mode setting
+**
+** Possible return values placed in pMode:
+**  0 = promisuous mode not set
+**  1 = promisuous mode is set
+**
+** =========================================================================
+*/
+RC_RETURN
+RCGetPromiscuousMode(U16 AdapterID, PU32 pMode, PFNWAITCALLBACK WaitCallback)
+{
+    U32 msgOffset, timeout;
+    PU32 pMsg;
+    volatile PU32 p32;
+    PPAB pPab;
+
+    pPab =PCIAdapterBlock[AdapterID];
+
+
+    msgOffset = pPab->p_atu->InQueue;
+
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+        kprintf("RCGetLinkSpeed(): Inbound Free Q empty!\n");
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+
+    /* calc virtual address of msg - virtual already mapped to physical */    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+    /* virtual pointer to return buffer - clear first two dwords */
+    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32[0] = 0xff;
+
+    /* setup private message */
+    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = 0;                 /* initiator context */
+    pMsg[3] = 0x219;             /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_PROMISCUOUS_MODE;
+    /* phys address to return status - area right after PAB */
+    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+   
+    /* post to Inbound Post Q */   
+
+    pPab->p_atu->InQueue = msgOffset;
+
+    /* wait for response */
+    timeout = 1000000;
+    while(1)
+    {
+        int i;
+        
+        if (WaitCallback)
+            (*WaitCallback)();
+
+        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
+            ;
+            
+        if (p32[0] != 0xff)
+            break;
+            
+        if (!timeout--)
+        {
+            kprintf("Timeout waiting for promiscuous mode from adapter\n");
+            kprintf("0x%08.8ulx\n", p32[0]);
+            return RC_RTN_NO_LINK_SPEED;
+        }
+    }
+
+    /* get mode */
+    *pMode = (U8)((volatile PU8)p32)[0] & 0x0f;
+
+    return RC_RTN_NO_ERROR;
+}
+/*
+** =========================================================================
+** RCSetBroadcastMode()
+**
+** Defined values for Mode:
+**  0 - turn off promiscuous mode
+**  1 - turn on  promiscuous mode
+**
+** =========================================================================
+*/
+RC_RETURN
+RCSetBroadcastMode(U16 AdapterID, U16 Mode)
+{
+    U32  off;
+    PU32 pMsg;
+    PPAB pPab;
+    
+    pPab =PCIAdapterBlock[AdapterID];
+     
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    off = pPab->p_atu->InQueue; /* get addresss of message */
+    
+    if (0xFFFFFFFF == off)
+        return RC_RTN_FREE_Q_EMPTY;
+    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+    /* setup private message */
+    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = 0;                 /* initiator context */
+    pMsg[3] = 0x219;             /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_BROADCAST_MODE;
+    pMsg[5] = Mode;     /* promiscuous mode setting */
+
+    pPab->p_atu->InQueue = off;   /* send it to the device */
+
+    return RC_RTN_NO_ERROR ;
+}
+/*
+** =========================================================================
+** RCGetBroadcastMode()
+**
+** get promiscuous mode setting
+**
+** Possible return values placed in pMode:
+**  0 = promisuous mode not set
+**  1 = promisuous mode is set
+**
+** =========================================================================
+*/
+RC_RETURN
+RCGetBroadcastMode(U16 AdapterID, PU32 pMode, PFNWAITCALLBACK WaitCallback)
+{
+    U32 msgOffset, timeout;
+    PU32 pMsg;
+    volatile PU32 p32;
+    PPAB pPab;
+
+    pPab =PCIAdapterBlock[AdapterID];
+
+
+    msgOffset = pPab->p_atu->InQueue;
+
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+        kprintf("RCGetLinkSpeed(): Inbound Free Q empty!\n");
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+
+    /* calc virtual address of msg - virtual already mapped to physical */    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+    /* virtual pointer to return buffer - clear first two dwords */
+    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32[0] = 0xff;
+
+    /* setup private message */
+    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = 0;                 /* initiator context */
+    pMsg[3] = 0x219;             /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_BROADCAST_MODE;
+    /* phys address to return status - area right after PAB */
+    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+   
+    /* post to Inbound Post Q */   
+
+    pPab->p_atu->InQueue = msgOffset;
+
+    /* wait for response */
+    timeout = 1000000;
+    while(1)
+    {
+        int i;
+        
+        if (WaitCallback)
+            (*WaitCallback)();
+
+        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
+            ;
+            
+        if (p32[0] != 0xff)
+            break;
+            
+        if (!timeout--)
+        {
+            kprintf("Timeout waiting for promiscuous mode from adapter\n");
+            kprintf("0x%08.8ulx\n", p32[0]);
+            return RC_RTN_NO_LINK_SPEED;
+        }
+    }
+
+    /* get mode */
+    *pMode = (U8)((volatile PU8)p32)[0] & 0x0f;
+
+    return RC_RTN_NO_ERROR;
+}
+
+/*
+** =========================================================================
+** RCGetLinkSpeed()
+**
+** get ethernet link speed. 
+**
+** 0 = Unknown
+** 1 = Full Duplex 100BaseT
+** 2 = Half duplex 100BaseT
+** 3 = Full Duplex  10BaseT
+** 4 = Half duplex  10BaseT
+**
+** =========================================================================
+*/
+RC_RETURN
+RCGetLinkSpeed(U16 AdapterID, PU32 pLinkSpeedCode, PFNWAITCALLBACK WaitCallback)
+{
+    U32 msgOffset, timeout;
+    PU32 pMsg;
+    volatile PU32 p32;
+    U8 IOPLinkSpeed;
+    PPAB pPab;
+
+    pPab =PCIAdapterBlock[AdapterID];
+
+
+    msgOffset = pPab->p_atu->InQueue;
+
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+        kprintf("RCGetLinkSpeed(): Inbound Free Q empty!\n");
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+
+    /* calc virtual address of msg - virtual already mapped to physical */    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+    /* virtual pointer to return buffer - clear first two dwords */
+    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32[0] = 0xff;
+
+    /* setup private message */
+    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = 0;                 /* initiator context */
+    pMsg[3] = 0x219;             /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_LINK_SPEED;
+    /* phys address to return status - area right after PAB */
+    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+   
+    /* post to Inbound Post Q */   
+
+    pPab->p_atu->InQueue = msgOffset;
+
+    /* wait for response */
+    timeout = 1000000;
+    while(1)
+    {
+        int i;
+        
+        if (WaitCallback)
+            (*WaitCallback)();
+
+        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
+            ;
+            
+        if (p32[0] != 0xff)
+            break;
+            
+        if (!timeout--)
+        {
+            kprintf("Timeout waiting for link speed from IOP\n");
+            kprintf("0x%08.8ulx\n", p32[0]);
+            return RC_RTN_NO_LINK_SPEED;
+        }
+    }
+
+    /* get Link speed */
+    IOPLinkSpeed = (U8)((volatile PU8)p32)[0] & 0x0f;
+
+    *pLinkSpeedCode= IOPLinkSpeed;
+
+    return RC_RTN_NO_ERROR;
+}
+
+/*
+** =========================================================================
+** RCReportDriverCapability(U16 AdapterID, U32 capability)
+**
+** Currently defined bits:
+** WARM_REBOOT_CAPABLE   0x01
+**
+** =========================================================================
+*/
+RC_RETURN
+RCReportDriverCapability(U16 AdapterID, U32 capability)
+{
+    U32  off;
+    PU32 pMsg;
+    PPAB pPab;
+
+    pPab =PCIAdapterBlock[AdapterID];
+     
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    off = pPab->p_atu->InQueue; /* get addresss of message */
+    
+    if (0xFFFFFFFF == off)
+        return RC_RTN_FREE_Q_EMPTY;
+    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+    /* setup private message */
+    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = 0;                 /* initiator context */
+    pMsg[3] = 0x219;             /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_REPORT_DRIVER_CAPABILITY;
+    pMsg[5] = capability;
+
+    pPab->p_atu->InQueue = off;   /* send it to the I2O device */
+
+    return RC_RTN_NO_ERROR ;
+}
+
+/*
+** =========================================================================
+** RCGetFirmwareVer()
+**
+** Return firmware version in the form "SoftwareVersion : Bt BootVersion"
+**
+** =========================================================================
+*/
+RC_RETURN
+RCGetFirmwareVer(U16 AdapterID, PU8 pFirmString, PFNWAITCALLBACK WaitCallback)
+{
+    U32 msgOffset, timeout;
+    PU32 pMsg;
+    volatile PU32 p32;
+    PPAB pPab;
+
+    pPab =PCIAdapterBlock[AdapterID];
+
+    msgOffset = pPab->p_atu->InQueue;
+
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+        kprintf("RCGetFirmwareVer(): Inbound Free Q empty!\n");
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+
+    /* calc virtual address of msg - virtual already mapped to physical */    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+    /* virtual pointer to return buffer - clear first two dwords */
+    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32[0] = 0xff;
+
+    /* setup private message */
+    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = 0;                 /* initiator context */
+    pMsg[3] = 0x219;             /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_FIRMWARE_REV;
+    /* phys address to return status - area right after PAB */
+    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+
+
+   
+    /* post to Inbound Post Q */   
+
+    pPab->p_atu->InQueue = msgOffset;
+
+    
+    /* wait for response */
+    timeout = 1000000;
+    while(1)
+    {
+        int i;
+        
+        if (WaitCallback)
+            (*WaitCallback)();
+            
+        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
+            ;
+
+        if (p32[0] != 0xff)
+            break;
+            
+        if (!timeout--)
+        {
+            kprintf("Timeout waiting for link speed from IOP\n");
+            return RC_RTN_NO_FIRM_VER;
+        }
+    }
+
+    strcpy(pFirmString, (PU8)p32);
+    return RC_RTN_NO_ERROR;
+}
+
+/*
+** =========================================================================
+** RCResetLANCard()
+**
+** ResourceFlags indicates whether to return buffer resource explicitly
+** to host or keep and reuse.
+** CallbackFunction (if not NULL) is the function to be called when 
+** reset is complete.
+** If CallbackFunction is NULL, ReturnAddr will have a 1 placed in it when
+** reset is done (if not NULL).
+**
+** =========================================================================
+*/
+RC_RETURN 
+RCResetLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction)
+{
+    unsigned long off;
+    unsigned long *pMsg;
+    PPAB pPab;
+    int i;
+    long timeout = 0;
+
+    
+    pPab =PCIAdapterBlock[AdapterID];
+     
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+
+    off = pPab->p_atu->InQueue; /* get addresss of message */
+    
+    if (0xFFFFFFFF == off)
+        return RC_RTN_FREE_Q_EMPTY;
+    
+    pPab->pCallbackFunc = CallbackFunction;
+
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+    /* setup message */
+    pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_LAN_RESET << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+    pMsg[3] = ResourceFlags << 16;   /* resource flags */
+
+    pPab->p_atu->InQueue = off;   /* send it to the I2O device */
+
+    if (CallbackFunction == (PFNCALLBACK)NULL)
+    {
+        /* call RCProcI2OMsgQ() until something in pPab->pCallbackFunc
+           or until timer goes off */
+        while (pPab->pCallbackFunc == (PFNCALLBACK)NULL)
+        {
+            RCProcI2OMsgQ(AdapterID);
+            for (i = 0; i < 100000; i++)     /* please don't hog the bus!!! */
+                ;
+            timeout++;
+            if (timeout > 10000)
+            {
+                break;
+            }
+        }
+        if (ReturnAddr != (PU32)NULL)
+            *ReturnAddr = (U32)pPab->pCallbackFunc;
+    }
+
+    return RC_RTN_NO_ERROR ;
+}
+/*
+** =========================================================================
+** RCResetIOP()
+**
+** Send StatusGet Msg, wait for results return directly to buffer.
+**
+** =========================================================================
+*/
+RC_RETURN 
+RCResetIOP(U16 AdapterID)
+{
+    U32 msgOffset, timeout;
+    PU32 pMsg;
+    PPAB pPab;
+    volatile PU32 p32;
+    
+    pPab = PCIAdapterBlock[AdapterID];
+    msgOffset = pPab->p_atu->InQueue;
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+
+    /* calc virtual address of msg - virtual already mapped to physical */    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+    pMsg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_EXEC_IOP_RESET << 24 | I2O_HOST_TID << 12 | I2O_IOP_TID;
+    pMsg[2] = 0; /* universal context */
+    pMsg[3] = 0; /* universal context */
+    pMsg[4] = 0; /* universal context */
+    pMsg[5] = 0; /* universal context */
+    /* phys address to return status - area right after PAB */
+    pMsg[6] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+    pMsg[7] = 0;
+    pMsg[8] = 1;  /*  return 1 byte */
+
+    /* virual pointer to return buffer - clear first two dwords */
+    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32[0] = 0;
+    p32[1] = 0;
+
+    /* post to Inbound Post Q */   
+
+    pPab->p_atu->InQueue = msgOffset;
+
+    /* wait for response */
+    timeout = 1000000;
+    while(1)
+    {
+        int i;
+        
+        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
+            ;
+            
+        if (p32[0] || p32[1])
+            break;
+            
+        if (!timeout--)
+        {
+            printk("RCResetIOP timeout\n");
+            return RC_RTN_MSG_REPLY_TIMEOUT;
+        }
+    }
+    return RC_RTN_NO_ERROR;
+}
+
+/*
+** =========================================================================
+** RCShutdownLANCard()
+**
+** ResourceFlags indicates whether to return buffer resource explicitly
+** to host or keep and reuse.
+** CallbackFunction (if not NULL) is the function to be called when 
+** shutdown is complete.
+** If CallbackFunction is NULL, ReturnAddr will have a 1 placed in it when
+** shutdown is done (if not NULL).
+**
+** =========================================================================
+*/
+RC_RETURN 
+RCShutdownLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction)
+{
+    volatile PU32 pMsg;
+    U32 off;
+    PPAB pPab;
+    int i;
+    long timeout = 0;
+
+    pPab = PCIAdapterBlock[AdapterID];
+
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+
+    off = pPab->p_atu->InQueue; /* get addresss of message */
+    
+    if (0xFFFFFFFF == off)
+        return RC_RTN_FREE_Q_EMPTY;
+
+    pPab->pCallbackFunc = CallbackFunction;
+    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+    /* setup message */
+    pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_LAN_SHUTDOWN << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+    pMsg[3] = ResourceFlags << 16;   /* resource flags */
+
+    pPab->p_atu->InQueue = off;   /* send it to the I2O device */
+
+    if (CallbackFunction == (PFNCALLBACK)NULL)
+    {
+        /* call RCProcI2OMsgQ() until something in pPab->pCallbackFunc
+           or until timer goes off */
+        while (pPab->pCallbackFunc == (PFNCALLBACK)NULL)
+        {
+            RCProcI2OMsgQ(AdapterID);
+            for (i = 0; i < 100000; i++)     /* please don't hog the bus!!! */
+                ;
+            timeout++;
+            if (timeout > 10000)
+            {
+				printk("RCShutdownLANCard(): timeout\n");
+                break;
+            }
+        }
+        if (ReturnAddr != (PU32)NULL)
+            *ReturnAddr = (U32)pPab->pCallbackFunc;
+    }
+    return RC_RTN_NO_ERROR ;
+}
+
+
+/*
+** =========================================================================
+** RCSetRavlinIPandMask()
+**
+** Set the Ravlin 45/PCI cards IP address and network mask.
+**
+** IP address and mask must be in network byte order.
+** For example, IP address 1.2.3.4 and mask 255.255.255.0 would be
+** 0x04030201 and 0x00FFFFFF on a little endian machine.
+**
+** =========================================================================
+*/
+RC_RETURN
+RCSetRavlinIPandMask(U16 AdapterID, U32 ipAddr, U32 netMask)
+{
+    volatile PU32 pMsg;
+    U32 off;
+    PPAB pPab;
+
+    pPab = PCIAdapterBlock[AdapterID];
+
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+
+    off = pPab->p_atu->InQueue; /* get addresss of message */
+    
+    if (0xFFFFFFFF == off)
+        return RC_RTN_FREE_Q_EMPTY;
+    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+    /* setup private message */
+    pMsg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = 0;                 /* initiator context */
+    pMsg[3] = 0x219;             /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_IP_AND_MASK;
+    pMsg[5] = ipAddr; 
+    pMsg[6] = netMask;
+
+
+    pPab->p_atu->InQueue = off;   /* send it to the I2O device */
+    return RC_RTN_NO_ERROR ;
+
+}
+
+/*
+** =========================================================================
+** RCGetRavlinIPandMask()
+**
+** get the IP address and MASK from the card
+** 
+** =========================================================================
+*/
+RC_RETURN
+RCGetRavlinIPandMask(U16 AdapterID, PU32 pIpAddr, PU32 pNetMask, 
+                     PFNWAITCALLBACK WaitCallback)
+{
+    unsigned i, timeout;
+    U32      off;
+    PU32     pMsg, p32;
+    PPAB     pPab;
+    PATU     p_atu;
+    
+#ifdef DEBUG
+    kprintf("RCGetRavlinIPandMask: pIpAddr is 0x%08.8ulx, *IpAddr is 0x%08.8ulx\n", pIpAddr, *pIpAddr);
+#endif /* DEBUG */
+
+    pPab = PCIAdapterBlock[AdapterID];
+    
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    p_atu = pPab->p_atu;
+    off = p_atu->InQueue;   /* get addresss of message */
+ 
+    if (0xFFFFFFFF == off)
+        return RC_RTN_FREE_Q_EMPTY;
+
+    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    *p32 = 0xFFFFFFFF;
+
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+#ifdef DEBUG
+    kprintf("RCGetRavlinIPandMask: p_atu 0x%08.8ulx, off 0x%08.8ulx, p32 0x%08.8ulx\n", p_atu, off, p32);
+#endif /* DEBUG */
+    /* setup private message */
+    pMsg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
+    pMsg[2] = 0;               /* initiator context */
+    pMsg[3] = 0x218;           /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_IP_AND_MASK;
+    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+
+    p_atu->InQueue = off;   /* send it to the I2O device */
+#ifdef DEBUG
+    kprintf("RCGetRavlinIPandMask: p_atu 0x%08.8ulx, off 0x%08.8ulx, p32 0x%08.8ulx\n", p_atu, off, p32);
+#endif /* DEBUG */
+
+    /* wait for the rcpci45 board to update the info */
+    timeout = 100000;
+    while (0xffffffff == *p32)
+    {
+        if (WaitCallback)
+            (*WaitCallback)();
+    
+        for (i = 0; i < 1000; i++)
+            ;
+
+        if (!timeout--)
+        {
+#ifdef DEBUG
+            kprintf("RCGetRavlinIPandMask: Timeout\n");
+#endif /* DEBUG */
+            return RC_RTN_MSG_REPLY_TIMEOUT;
+        }
+    }
+
+#ifdef DEBUG
+    kprintf("RCGetRavlinIPandMask: after time out\n", \
+            "p32[0] (IpAddr) 0x%08.8ulx, p32[1] (IPmask) 0x%08.8ulx\n", p32[0], p32[1]);
+#endif /* DEBUG */
+    
+    /* send IP and mask to user's space  */
+    *pIpAddr  = p32[0];
+    *pNetMask = p32[1];
+
+
+#ifdef DEBUG
+    kprintf("RCGetRavlinIPandMask: pIpAddr is 0x%08.8ulx, *IpAddr is 0x%08.8ulx\n", pIpAddr, *pIpAddr);
+#endif /* DEBUG */
+
+    return RC_RTN_NO_ERROR;
+}
+
+/* 
+** /////////////////////////////////////////////////////////////////////////
+** /////////////////////////////////////////////////////////////////////////
+**
+**                        local functions
+**
+** /////////////////////////////////////////////////////////////////////////
+** /////////////////////////////////////////////////////////////////////////
+*/
+
+/*
+** =========================================================================
+** SendI2OOutboundQInitMsg()
+**
+** =========================================================================
+*/
+static int 
+SendI2OOutboundQInitMsg(PPAB pPab)
+{
+    U32 msgOffset, timeout, phyOutQFrames, i;
+    volatile PU32 pMsg;
+    volatile PU32 p32;
+    
+    
+    
+    msgOffset = pPab->p_atu->InQueue;
+
+    
+    if (msgOffset == 0xFFFFFFFF)
+    {
+#ifdef DEBUG
+        kprintf("SendI2OOutboundQInitMsg(): Inbound Free Q empty!\n");
+#endif /* DEBUG */
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+    
+    
+    /* calc virual address of msg - virual already mapped to physical */    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+#ifdef DEBUG
+    kprintf("SendI2OOutboundQInitMsg - pMsg = 0x%08.8ulx, InQ msgOffset = 0x%08.8ulx\n", pMsg, msgOffset);
+#endif /* DEBUG */
+
+    pMsg[0] = EIGHT_WORD_MSG_SIZE | TRL_OFFSET_6;
+    pMsg[1] = I2O_EXEC_OUTBOUND_INIT << 24 | I2O_HOST_TID << 12 | I2O_IOP_TID;
+    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+    pMsg[3] = 0x106; /* transaction context */
+    pMsg[4] = 4096; /* Host page frame size */
+    pMsg[5] = MSG_FRAME_SIZE  << 16 | 0x80; /* outbound msg frame size and Initcode */
+    pMsg[6] = 0xD0000004;       /* simple sgl element LE, EOB */
+    /* phys address to return status - area right after PAB */
+    pMsg[7] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+
+    /* virual pointer to return buffer - clear first two dwords */
+    p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32[0] = 0;
+    
+    /* post to Inbound Post Q */   
+    pPab->p_atu->InQueue = msgOffset;
+    
+    /* wait for response */
+    timeout = 100000;
+    while(1)
+    {
+        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
+            ;
+            
+        if (p32[0])
+            break;
+            
+        if (!timeout--)
+        {
+#ifdef DEBUG
+            kprintf("Timeout wait for InitOutQ InPrgress status from IOP\n");
+#endif /* DEBUG */
+            return RC_RTN_NO_I2O_STATUS;
+        }
+    }
+
+    timeout = 100000;
+    while(1)
+    {
+        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
+            ;
+            
+        if (p32[0] == I2O_EXEC_OUTBOUND_INIT_COMPLETE)
+            break;
+
+        if (!timeout--)
+        {
+#ifdef DEBUG
+            kprintf("Timeout wait for InitOutQ Complete status from IOP\n");
+#endif /* DEBUG */
+            return RC_RTN_NO_I2O_STATUS;
+        }
+    }
+
+    /* load PCI outbound free Q with MF physical addresses */
+    phyOutQFrames = pPab->outMsgBlockPhyAddr;
+
+    for (i = 0; i < NMBR_MSG_FRAMES; i++)
+    {
+        pPab->p_atu->OutQueue = phyOutQFrames;
+        phyOutQFrames += MSG_FRAME_SIZE;
+    }
+    return RC_RTN_NO_ERROR;
+}
+
+
+/*
+** =========================================================================
+** GetI2OStatus()
+**
+** Send StatusGet Msg, wait for results return directly to buffer.
+**
+** =========================================================================
+*/
+static int 
+GetI2OStatus(PPAB pPab)
+{
+    U32 msgOffset, timeout;
+    PU32 pMsg;
+    volatile PU32 p32;
+    
+    
+    msgOffset = pPab->p_atu->InQueue; 
+#ifdef DEBUG
+    printk("GetI2OStatus: msg offset = 0x%x\n", msgOffset);
+#endif /* DEBUG */
+    if (msgOffset == 0xFFFFFFFF)
+    {
+#ifdef DEBUG
+        kprintf("GetI2OStatus(): Inbound Free Q empty!\n");
+#endif /* DEBUG */
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+
+    /* calc virual address of msg - virual already mapped to physical */    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+    pMsg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_EXEC_STATUS_GET << 24 | I2O_HOST_TID << 12 | I2O_IOP_TID;
+    pMsg[2] = 0; /* universal context */
+    pMsg[3] = 0; /* universal context */
+    pMsg[4] = 0; /* universal context */
+    pMsg[5] = 0; /* universal context */
+    /* phys address to return status - area right after PAB */
+    pMsg[6] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+    pMsg[7] = 0;
+    pMsg[8] = 88;  /*  return 88 bytes */
+
+    /* virual pointer to return buffer - clear first two dwords */
+    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32[0] = 0;
+    p32[1] = 0;
+
+#ifdef DEBUG
+    kprintf("GetI2OStatus - pMsg:0x%08.8ulx, msgOffset:0x%08.8ulx, [1]:0x%08.8ulx, [6]:0x%08.8ulx\n",
+            pMsg, msgOffset, pMsg[1], pMsg[6]);
+#endif /* DEBUG */
+   
+    /* post to Inbound Post Q */   
+    pPab->p_atu->InQueue = msgOffset;
+    
+#ifdef DEBUG
+    kprintf("Return status to p32 = 0x%08.8ulx\n", p32);
+#endif /* DEBUG */
+    
+    /* wait for response */
+    timeout = 1000000;
+    while(1)
+    {
+        int i;
+        
+        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
+            ;
+            
+        if (p32[0] && p32[1])
+            break;
+            
+        if (!timeout--)
+        {
+#ifdef DEBUG
+            kprintf("Timeout waiting for status from IOP\n");
+            kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[0], p32[1], p32[2], p32[3]);
+            kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[4], p32[5], p32[6], p32[7]);
+            kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[8], p32[9], p32[10], p32[11]);
+#endif /* DEBUG */
+            return RC_RTN_NO_I2O_STATUS;
+        }
+    }
+            
+#ifdef DEBUG
+    kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[0], p32[1], p32[2], p32[3]);
+    kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[4], p32[5], p32[6], p32[7]);
+    kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[8], p32[9], p32[10], p32[11]);
+#endif /* DEBUG */
+    /* get IOP state */
+    pPab->IOPState = ((volatile PU8)p32)[10];
+    pPab->InboundMFrameSize  = ((volatile PU16)p32)[6];
+    
+#ifdef DEBUG
+    kprintf("IOP state 0x%02.2x InFrameSize = 0x%04.4x\n", 
+            pPab->IOPState, pPab->InboundMFrameSize);
+#endif /* DEBUG */
+    return RC_RTN_NO_ERROR;
+}
+
+
+/*
+** =========================================================================
+** SendEnableSysMsg()
+**
+**
+** =========================================================================
+*/
+static int 
+SendEnableSysMsg(PPAB pPab)
+{
+    U32 msgOffset; // timeout;
+    volatile PU32 pMsg;
+
+    msgOffset = pPab->p_atu->InQueue;
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+#ifdef DEBUG
+        kprintf("SendEnableSysMsg(): Inbound Free Q empty!\n");
+#endif /* DEBUG */
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+
+    /* calc virual address of msg - virual already mapped to physical */
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+#ifdef DEBUG
+    kprintf("SendEnableSysMsg - pMsg = 0x%08.8ulx, InQ msgOffset = 0x%08.8ulx\n", pMsg, msgOffset);
+#endif /* DEBUG */
+
+    pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = I2O_EXEC_SYS_ENABLE << 24 | I2O_HOST_TID << 12 | I2O_IOP_TID;
+    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+    pMsg[3] = 0x110; /* transaction context */
+    pMsg[4] = 0x50657465; /*  RedCreek Private */
+
+    /* post to Inbound Post Q */
+    pPab->p_atu->InQueue = msgOffset;
+
+    return RC_RTN_NO_ERROR;
+}
+
+
+/*
+** =========================================================================
+** FillI2OMsgFromTCB()
+**
+** inputs   pMsgU32 - virual pointer (mapped to physical) of message frame
+**          pXmitCntrlBlock - pointer to caller buffer control block.
+**
+** fills in LAN SGL after Transaction Control Word or Bucket Count.
+** =========================================================================
+*/
+static int 
+FillI2OMsgSGLFromTCB(PU32 pMsgFrame, PRCTCB pTransCtrlBlock)
+{
+    unsigned int nmbrBuffers, nmbrSeg, nmbrDwords, context, flags;
+    PU32 pTCB, pMsg;
+
+    /* SGL element flags */   
+#define EOB        0x40000000
+#define LE         0x80000000
+#define SIMPLE_SGL 0x10000000
+#define BC_PRESENT 0x01000000
+
+    pTCB = (PU32)pTransCtrlBlock;
+    pMsg = pMsgFrame;
+    nmbrDwords = 0;
+
+#ifdef DEBUG
+    kprintf("FillI2OMsgSGLFromTCBX\n");
+    kprintf("TCB  0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
+            pTCB[0], pTCB[1], pTCB[2], pTCB[3], pTCB[4]);
+    kprintf("pTCB 0x%08.8ulx, pMsg 0x%08.8ulx\n", pTCB, pMsg);
+#endif /* DEBUG */
+    
+    nmbrBuffers = *pTCB++;
+    
+    if (!nmbrBuffers)
+    {
+        return -1;
+    }
+
+    do
+    {
+        context = *pTCB++; /* buffer tag (context) */
+        nmbrSeg = *pTCB++; /* number of segments */
+
+        if (!nmbrSeg)
+        {
+            return -1;
+        }
+        
+        flags = SIMPLE_SGL | BC_PRESENT;
+
+        if (1 == nmbrSeg)
+        {
+            flags |= EOB;
+        
+            if (1 == nmbrBuffers)
+                flags |= LE;
+        }    
+
+        /* 1st SGL buffer element has context */
+        pMsg[0] = pTCB[0] | flags ; /* send over count (segment size) */
+        pMsg[1] = context;
+        pMsg[2] = pTCB[1]; /* send buffer segment physical address */
+        nmbrDwords += 3;
+        pMsg += 3;
+        pTCB += 2;
+
+        
+        if (--nmbrSeg)
+        {
+            do
+            {
+                flags = SIMPLE_SGL;
+                
+                if (1 == nmbrSeg)
+                {
+                    flags |= EOB;
+                
+                    if (1 == nmbrBuffers)
+                        flags |= LE;
+                }    
+                
+                pMsg[0] = pTCB[0] | flags;  /* send over count */
+                pMsg[1] = pTCB[1];   /* send buffer segment physical address */
+                nmbrDwords += 2;
+                pTCB += 2;
+                pMsg += 2;
+        
+            } while (--nmbrSeg);
+        }
+        
+    } while (--nmbrBuffers);
+    
+    return nmbrDwords;
+}
+
+
+/*
+** =========================================================================
+** ProcessOutboundI2OMsg()
+**
+** process I2O reply message
+** * change to msg structure *
+** =========================================================================
+*/
+static void 
+ProcessOutboundI2OMsg(PPAB pPab, U32 phyAddrMsg)
+{
+    PU8 p8Msg;
+    PU32 p32;
+    //  U16 count;
+    
+    
+    p8Msg = pPab->pLinOutMsgBlock + (phyAddrMsg - pPab->outMsgBlockPhyAddr);
+    p32 = (PU32)p8Msg;
+    
+#ifdef DEBUG
+    kprintf("VXD: ProcessOutboundI2OMsg - pPab 0x%08.8ulx, phyAdr 0x%08.8ulx, linAdr 0x%08.8ulx\n", pPab, phyAddrMsg, p8Msg);
+    kprintf("msg :0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[0], p32[1], p32[2], p32[3]);
+    kprintf("msg :0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[4], p32[5], p32[6], p32[7]);
+#endif /* DEBUG */
+
+    if (p32[4] >> 24 != I2O_REPLY_STATUS_SUCCESS)
+    {
+#ifdef DEBUG
+        kprintf("Message reply status not success\n");
+#endif /* DEBUG */
+        return;
+    }
+    
+    switch (p8Msg[7] )  /* function code byte */
+    {
+    case I2O_EXEC_SYS_TAB_SET:
+        msgFlag = 1;
+#ifdef DEBUG
+        kprintf("Received I2O_EXEC_SYS_TAB_SET reply\n");
+#endif /* DEBUG */
+        break;
+
+    case I2O_EXEC_HRT_GET:
+        msgFlag = 1;
+#ifdef DEBUG
+        kprintf("Received I2O_EXEC_HRT_GET reply\n");
+#endif /* DEBUG */
+        break;
+        
+    case I2O_EXEC_LCT_NOTIFY:
+        msgFlag = 1;
+#ifdef DEBUG
+        kprintf("Received I2O_EXEC_LCT_NOTIFY reply\n");
+#endif /* DEBUG */
+        break;
+        
+    case I2O_EXEC_SYS_ENABLE:
+        msgFlag = 1;
+#ifdef DEBUG
+        kprintf("Received I2O_EXEC_SYS_ENABLE reply\n");
+#endif /* DEBUG */
+        break;
+
+    default:    
+#ifdef DEBUG
+        kprintf("Received UNKNOWN reply\n");
+#endif /* DEBUG */
+        break;
+    }
+}
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/rclanmtl.h linux/drivers/net/rclanmtl.h
--- linux.vanilla/drivers/net/rclanmtl.h	Thu Jan  1 01:00:00 1970
+++ linux/drivers/net/rclanmtl.h	Fri Jan 29 14:03:56 1999
@@ -0,0 +1,637 @@
+/*
+** *************************************************************************
+**
+**
+**     R C L A N M T L . H             $Revision: 5 $
+**
+**
+**  RedCreek I2O LAN Message Transport Layer header file.
+**
+**  ---------------------------------------------------------------------
+**  ---     Copyright (c) 1997-1999, RedCreek Communications Inc.     ---
+**  ---                   All rights reserved.                        ---
+**  ---------------------------------------------------------------------
+**
+**  File Description:
+**
+**  Header file for host I2O (Intelligent I/O) LAN message transport layer 
+**  API and data types.
+**
+**  This program is free software; you can redistribute it and/or modify
+**  it under the terms of the GNU General Public License as published by
+**  the Free Software Foundation; either version 2 of the License, or
+**  (at your option) any later version.
+
+**  This program is distributed in the hope that it will be useful,
+**  but WITHOUT ANY WARRANTY; without even the implied warranty of
+**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+**  GNU General Public License for more details.
+
+**  You should have received a copy of the GNU General Public License
+**  along with this program; if not, write to the Free Software
+**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** *************************************************************************
+*/
+
+#ifndef RCLANMTL_H
+#define RCLANMTL_H
+
+/* Linux specific includes */
+#define kprintf printk
+#ifdef RC_LINUX_MODULE     /* linux modules need non-library version of string functions */
+#include <linux/string.h>
+#else
+#include <string.h>
+#endif
+
+/* PCI/45 Configuration space values */
+#define RC_PCI45_VENDOR_ID  0x4916
+#define RC_PCI45_DEVICE_ID  0x1960
+
+
+ /* RedCreek API function return values */
+#define RC_RTN_NO_ERROR             0
+#define RC_RTN_I2O_NOT_INIT         1
+#define RC_RTN_FREE_Q_EMPTY         2
+#define RC_RTN_TCB_ERROR            3
+#define RC_RTN_TRANSACTION_ERROR    4
+#define RC_RTN_ADAPTER_ALREADY_INIT 5
+#define RC_RTN_MALLOC_ERROR         6
+#define RC_RTN_ADPTR_NOT_REGISTERED 7
+#define RC_RTN_MSG_REPLY_TIMEOUT    8
+#define RC_RTN_NO_I2O_STATUS        9
+#define RC_RTN_NO_FIRM_VER         10
+#define RC_RTN_NO_LINK_SPEED       11
+
+/* Driver capability flags */
+#define WARM_REBOOT_CAPABLE      0x01
+
+ /* scalar data types */
+typedef unsigned char   U8;
+typedef unsigned char*  PU8;
+typedef unsigned short  U16;
+typedef unsigned short* PU16;
+typedef unsigned long   U32;
+typedef unsigned long*  PU32;
+typedef unsigned long   BF;
+typedef int             RC_RETURN;
+
+
+ /* 
+ ** type PFNWAITCALLBACK
+ **
+ ** pointer to void function - type used for WaitCallback in some functions 
+ */
+typedef void (*PFNWAITCALLBACK)(void);  /* void argument avoids compiler complaint */
+
+ /*
+ ** type PFNTXCALLBACK 
+ **
+ ** Pointer to user's transmit callback function.  This user function is
+ ** called from RCProcI2OMsgQ() when packet have been transmitted from buffers
+ ** given in the RCI2OSendPacket() function.  BufferContext is a pointer to
+ ** an array of 32 bit context values.  These are the values the user assigned
+ ** and passed in the TCB to the RCI2OSendPacket() function.  PcktCount
+ ** indicates the number of buffer context values in the BufferContext[] array.
+ ** The User's TransmitCallbackFunction should recover (put back in free queue)
+ ** the packet buffers associated with the buffer context values.
+ */
+typedef void (*PFNTXCALLBACK)(U32  Status,
+                              U16  PcktCount,
+                              PU32 BufferContext,
+                              U16  AdaterID);
+
+ /* 
+ ** type PFNRXCALLBACK 
+ **
+ ** Pointer to user's receive callback function.  This user function
+ ** is called from RCProcI2OMsgQ() when packets have been received into
+ ** previously posted packet buffers throught the RCPostRecvBuffers() function.
+ ** The received callback function should process the Packet Descriptor Block
+ ** pointed to by PacketDescBlock. See Packet Decription Block below.
+ */
+typedef void (*PFNRXCALLBACK)(U32  Status,
+                              U8   PktCount,
+                              U32  BucketsRemain,
+                              PU32 PacketDescBlock,
+                              U16  AdapterID);
+
+ /* 
+ ** type PFNCALLBACK 
+ **
+ ** Pointer to user's generic callback function.  This user function
+ ** can be passed to LANReset or LANShutdown and is called when the 
+ ** the reset or shutdown is complete.
+ ** Param1 and Param2 are invalid for LANReset and LANShutdown.
+ */
+typedef void (*PFNCALLBACK)(U32  Status,
+                              U32  Param1,
+                              U32  Param2,
+                              U16  AdapterID);
+
+/*
+** Status - Transmit and Receive callback status word 
+**
+** A 32 bit Status is returned to the TX and RX callback functions.  This value
+** contains both the reply status and the detailed status as follows:
+**
+**  32    24     16            0
+**  +------+------+------------+
+**  | Reply|      |  Detailed  |
+**  |Status|   0  |   Status   |
+**  +------+------+------------+
+**
+** Reply Status and Detailed Status of zero indicates No Errors.
+*/
+ /* reply message status defines */
+#define    I2O_REPLY_STATUS_SUCCESS                    0x00
+#define    I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER     0x02
+#define    I2O_REPLY_STATUS_TRANSACTION_ERROR          0x0A
+
+
+/* DetailedStatusCode defines */
+#define    I2O_LAN_DSC_SUCCESS                         0x0000
+#define    I2O_LAN_DSC_DEVICE_FAILURE                  0x0001
+#define    I2O_LAN_DSC_DESTINATION_NOT_FOUND           0x0002
+#define    I2O_LAN_DSC_TRANSMIT_ERROR                  0x0003
+#define    I2O_LAN_DSC_TRANSMIT_ABORTED                0x0004
+#define    I2O_LAN_DSC_RECEIVE_ERROR                   0x0005
+#define    I2O_LAN_DSC_RECEIVE_ABORTED                 0x0006
+#define    I2O_LAN_DSC_DMA_ERROR                       0x0007
+#define    I2O_LAN_DSC_BAD_PACKET_DETECTED             0x0008
+#define    I2O_LAN_DSC_OUT_OF_MEMORY                   0x0009
+#define    I2O_LAN_DSC_BUCKET_OVERRUN                  0x000A
+#define    I2O_LAN_DSC_IOP_INTERNAL_ERROR              0x000B
+#define    I2O_LAN_DSC_CANCELED                        0x000C
+#define    I2O_LAN_DSC_INVALID_TRANSACTION_CONTEXT     0x000D
+#define    I2O_LAN_DSC_DESTINATION_ADDRESS_DETECTED    0x000E
+#define    I2O_LAN_DSC_DESTINATION_ADDRESS_OMITTED     0x000F
+#define    I2O_LAN_DSC_PARTIAL_PACKET_RETURNED         0x0010
+
+
+/*
+** Packet Description Block   (Received packets)
+**
+** A pointer to this block structure is returned to the ReceiveCallback 
+** function.  It contains the list of packet buffers which have either been
+** filled with a packet or returned to host due to a LANReset function. 
+** Currently there will only be one packet per receive bucket (buffer) posted. 
+**
+**   32   24               0     
+**  +-----------------------+  -\
+**  |   Buffer 1 Context    |    \
+**  +-----------------------+     \
+**  |      0xC0000000       |     / First Bucket Descriptor
+**  +-----+-----------------+    /
+**  |  0  | packet 1 length |   / 
+**  +-----------------------+  -\
+**  |   Buffer 2 Context    |    \
+**  +-----------------------+     \
+**  |      0xC0000000       |     / Second Bucket Descriptor
+**  +-----+-----------------+    /
+**  |  0  | packet 2 length |   / 
+**  +-----+-----------------+  -
+**  |         ...           |  ----- more bucket descriptors
+**  +-----------------------+  -\
+**  |   Buffer n Context    |    \
+**  +-----------------------+     \
+**  |      0xC0000000       |     / Last Bucket Descriptor
+**  +-----+-----------------+    /
+**  |  0  | packet n length |   / 
+**  +-----+-----------------+  -
+**
+** Buffer Context values are those given to adapter in the TCB on calls to
+** RCPostRecvBuffers().
+**  
+*/
+
+
+
+/*
+** Transaction Control Block (TCB) structure
+**
+** A structure like this is filled in by the user and passed by reference to 
+** RCI2OSendPacket() and RCPostRecvBuffers() functions.  Minimum size is five
+** 32-bit words for one buffer with one segment descriptor.  
+** MAX_NMBR_POST_BUFFERS_PER_MSG defines the maximum single segment buffers
+** that can be described in a given TCB.
+**
+**   32                    0
+**  +-----------------------+
+**  |   Buffer Count        |  Number of buffers in the TCB
+**  +-----------------------+
+**  |   Buffer 1 Context    |  first buffer reference
+**  +-----------------------+
+**  |   Buffer 1 Seg Count  |  number of segments in buffer
+**  +-----------------------+
+**  |   Buffer 1 Seg Desc 1 |  first segment descriptor (size, physical address)
+**  +-----------------------+
+**  |         ...           |  more segment descriptors (size, physical address)
+**  +-----------------------+
+**  |   Buffer 1 Seg Desc n |  last segment descriptor (size, physical address)
+**  +-----------------------+
+**  |   Buffer 2 Context    |  second buffer reference
+**  +-----------------------+
+**  |   Buffer 2 Seg Count  |  number of segments in buffer
+**  +-----------------------+
+**  |   Buffer 2 Seg Desc 1 |  segment descriptor (size, physical address)
+**  +-----------------------+
+**  |         ...           |  more segment descriptors (size, physical address)
+**  +-----------------------+
+**  |   Buffer 2 Seg Desc n |
+**  +-----------------------+
+**  |         ...           |  more buffer descriptor blocks ...
+**  +-----------------------+
+**  |   Buffer n Context    |
+**  +-----------------------+
+**  |   Buffer n Seg Count  |
+**  +-----------------------+
+**  |   Buffer n Seg Desc 1 |
+**  +-----------------------+
+**  |         ...           |
+**  +-----------------------+
+**  |   Buffer n Seg Desc n |
+**  +-----------------------+
+**
+**
+** A TCB for one contigous packet buffer would look like the following:
+**
+**   32                    0
+**  +-----------------------+
+**  |         1             |  one buffer in the TCB
+**  +-----------------------+
+**  |  <user's Context>     |  user's buffer reference
+**  +-----------------------+
+**  |         1             |  one segment buffer
+**  +-----------------------+                            _
+**  |    <buffer size>      |  size                       \ 
+**  +-----------------------+                              \ segment descriptor
+**  |  <physical address>   |  physical address of buffer  /
+**  +-----------------------+                            _/
+**
+*/
+
+ /* Buffer Segment Descriptor */
+typedef struct
+{
+    U32 size;
+    U32 phyAddress;
+}
+ BSD, *PBSD;
+ 
+typedef PU32 PRCTCB;
+/*
+** -------------------------------------------------------------------------
+** Exported functions comprising the API to the LAN I2O message transport layer
+** -------------------------------------------------------------------------
+*/
+
+
+ /*
+ ** InitRCI2OMsgLayer()
+ ** 
+ ** Called once prior to using the I2O LAN message transport layer.  User 
+ ** provides both the physical and virual address of a locked page buffer 
+ ** that is used as a private buffer for the RedCreek I2O message
+ ** transport layer.  This buffer must be a contigous memory block of a 
+ ** minimum of 16K bytes and long word aligned.  The user also must provide
+ ** the base address of the RedCreek PCI adapter assigned by BIOS or operating
+ ** system.  The user provided value AdapterID is a zero based index of the
+ ** Ravlin 45/PCI adapter.  This interface number is used in all subsequent API
+ ** calls to identify which adpapter for which the function is intended.  
+ ** Up to sixteen interfaces are supported with this API.
+ **
+ ** Inputs:  AdapterID - interface number from 0 to 15
+ **          pciBaseAddr - virual base address of PCI (set by BIOS)
+ **          p_msgbuf - virual address to private message block (min. 16K)
+ **          p_phymsgbuf - physical address of private message block
+ **          TransmitCallbackFunction - address of user's TX callback function
+ **          ReceiveCallbackFunction  - address of user's RX callback function
+ **
+ */
+RC_RETURN RCInitI2OMsgLayer(U16 AdapterID, U32 pciBaseAddr, 
+                            PU8 p_msgbuf,  PU8 p_phymsgbuf,
+                            PFNTXCALLBACK TransmitCallbackFunction,
+                            PFNRXCALLBACK ReceiveCallbackFunction,
+                            PFNCALLBACK   RebootCallbackFunction);
+
+ /*
+ ** RCSetRavlinIPandMask()
+ **
+ ** Set the Ravlin 45/PCI cards IP address and network mask.
+ **
+ ** IP address and mask must be in network byte order.
+ ** For example, IP address 1.2.3.4 and mask 255.255.255.0 would be
+ ** 0x04030201 and 0x00FFFFFF on a little endian machine.
+ **
+ */
+RC_RETURN RCSetRavlinIPandMask(U16 AdapterID, U32 ipAddr, U32 netMask);
+
+
+/*
+** =========================================================================
+** RCGetRavlinIPandMask()
+**
+** get the IP address and MASK from the card
+** 
+** =========================================================================
+*/
+RC_RETURN
+RCGetRavlinIPandMask(U16 AdapterID, PU32 pIpAddr, PU32 pNetMask, 
+                        PFNWAITCALLBACK WaitCallback);
+
+ /* 
+ ** RCProcI2OMsgQ()
+ ** 
+ ** Called from user's polling loop or Interrupt Service Routine for a PCI 
+ ** interrupt from the RedCreek PCI adapter.  User responsible for determining
+ ** and hooking the PCI interrupt. This function will call the registered
+ ** callback functions, TransmitCallbackFunction or ReceiveCallbackFunction,
+ ** if a TX or RX transaction has completed.
+ */
+void RCProcI2OMsgQ(U16 AdapterID);
+
+
+ /*
+ ** Disable and Enable I2O interrupts.  I2O interrupts are enabled at Init time
+ ** but can be disabled and re-enabled through these two function calls.
+ ** Packets will still be put into any posted recieved buffers and packets will
+ ** be sent through RCI2OSendPacket() functions.  Disabling I2O interrupts
+ ** will prevent hardware interrupt to host even though the outbound I2O msg
+ ** queue is not emtpy.
+ */
+RC_RETURN RCEnableI2OInterrupts(U16 adapterID);
+RC_RETURN RCDisableI2OInterrupts(U16 AdapterID);
+
+
+ /* 
+ ** RCPostRecvBuffers()
+ ** 
+ ** Post user's page locked buffers for use by the PCI adapter to
+ ** return ethernet packets received from the LAN.  Transaction Control Block,
+ ** provided by user, contains buffer descriptor(s) which includes a buffer
+ ** context number along with buffer size and physical address.  See TCB above.
+ ** The buffer context and actual packet length are returned to the 
+ ** ReceiveCallbackFunction when packets have been received.  Buffers posted
+ ** to the RedCreek adapter are considered owned by the adapter until the
+ ** context is return to user through the ReceiveCallbackFunction.
+ */
+RC_RETURN RCPostRecvBuffers(U16 AdapterID, PRCTCB pTransactionCtrlBlock);
+#define MAX_NMBR_POST_BUFFERS_PER_MSG 32
+
+ /*
+ ** RCI2OSendPacket()
+ ** 
+ ** Send user's ethernet packet from a locked page buffer.  
+ ** Packet must have full MAC header, however without a CRC.  
+ ** Initiator context is a user provided value that is returned 
+ ** to the TransmitCallbackFunction when packet buffer is free.
+ ** Transmit buffer are considered owned by the adapter until context's
+ ** returned to user through the TransmitCallbackFunction.
+ */
+RC_RETURN RCI2OSendPacket(U16 AdapterID, 
+                          U32 context, 
+                          PRCTCB pTransactionCtrlBlock);
+
+
+ /* Ethernet Link Statistics structure */
+typedef struct tag_RC_link_stats
+{
+    U32 TX_good;      /* good transmit frames */
+    U32 TX_maxcol;    /* frames not TX due to MAX collisions */
+    U32 TX_latecol;   /* frames not TX due to late collisions */
+    U32 TX_urun;      /* frames not TX due to DMA underrun */
+    U32 TX_crs;       /* frames TX with lost carrier sense */
+    U32 TX_def;       /* frames deferred due to activity on link */
+    U32 TX_singlecol; /* frames TX with one and only on collision */
+    U32 TX_multcol;   /* frames TX with more than one collision */
+    U32 TX_totcol;    /* total collisions detected during TX */
+    U32 Rcv_good;     /* good frames received */
+    U32 Rcv_CRCerr;   /* frames RX and discarded with CRC errors */
+    U32 Rcv_alignerr; /* frames RX with alignment and CRC errors */
+    U32 Rcv_reserr;   /* good frames discarded due to no RX buffer */
+    U32 Rcv_orun;     /* RX frames lost due to FIFO overrun */
+    U32 Rcv_cdt;      /* RX frames with collision during RX */
+    U32 Rcv_runt;     /* RX frames shorter than 64 bytes */
+}
+ RCLINKSTATS, *P_RCLINKSTATS;
+
+ /*
+ ** RCGetLinkStatistics()
+ **
+ ** Returns link statistics in user's structure at address StatsReturnAddr
+ ** If given, not NULL, the function WaitCallback is called during the wait
+ ** loop while waiting for the adapter to respond.
+ */
+RC_RETURN RCGetLinkStatistics(U16 AdapterID,
+                              P_RCLINKSTATS StatsReturnAddr,
+                              PFNWAITCALLBACK WaitCallback);
+
+ /*
+ ** RCGetLinkStatus()
+ **
+ ** Return link status, up or down, to user's location addressed by ReturnAddr.
+ ** If given, not NULL, the function WaitCallback is called during the wait
+ ** loop while waiting for the adapter to respond.
+ */
+RC_RETURN RCGetLinkStatus(U16 AdapterID, 
+                          PU32 pReturnStatus,
+                          PFNWAITCALLBACK WaitCallback);
+                               
+ /* Link Status defines - value returned in pReturnStatus */
+#define RC_LAN_LINK_STATUS_DOWN     0
+#define RC_LAN_LINK_STATUS_UP       1
+
+ /*
+ ** RCGetMAC()
+ **
+ ** Get the current MAC address assigned to user.  RedCreek Ravlin 45/PCI 
+ ** has two MAC addresses.  One which is private to the PCI Card, and 
+ ** another MAC which is given to the user as its link layer MAC address. The
+ ** adapter runs in promiscous mode because of the dual address requirement.
+ ** The MAC address is returned to the unsigned char array pointer to by mac.
+ */
+RC_RETURN RCGetMAC(U16 AdapterID, PU8 mac, PFNWAITCALLBACK WaitCallback);
+
+ /*
+ ** RCSetMAC()
+ **
+ ** Set a new user port MAC address.  This address will be returned on
+ ** subsequent RCGetMAC() calls.
+ */
+RC_RETURN RCSetMAC(U16 AdapterID, PU8 mac);
+
+ /*
+ ** RCSetLinkSpeed()
+ **
+ ** set adapter's link speed based on given input code.
+ */
+RC_RETURN RCSetLinkSpeed(U16 AdapterID, U16 LinkSpeedCode);
+ /* Set link speed codes */
+#define LNK_SPD_AUTO_NEG_NWAY   0
+#define LNK_SPD_100MB_FULL      1
+#define LNK_SPD_100MB_HALF      2
+#define LNK_SPD_10MB_FULL       3
+#define LNK_SPD_10MB_HALF       4
+
+
+
+
+ /*
+ ** RCGetLinkSpeed()
+ **
+ ** Return link speed code.
+ */
+ /* Return link speed codes */
+#define LNK_SPD_UNKNOWN         0
+#define LNK_SPD_100MB_FULL      1
+#define LNK_SPD_100MB_HALF      2
+#define LNK_SPD_10MB_FULL       3
+#define LNK_SPD_10MB_HALF       4
+
+RC_RETURN
+RCGetLinkSpeed(U16 AdapterID, PU32 pLinkSpeedCode, PFNWAITCALLBACK WaitCallback);
+/*
+** =========================================================================
+** RCSetPromiscuousMode(U16 AdapterID, U16 Mode)
+**
+** Defined values for Mode:
+**  0 - turn off promiscuous mode
+**  1 - turn on  promiscuous mode
+**
+** =========================================================================
+*/
+#define PROMISCUOUS_MODE_OFF 0
+#define PROMISCUOUS_MODE_ON  1
+RC_RETURN
+RCSetPromiscuousMode(U16 AdapterID, U16 Mode);
+/*
+** =========================================================================
+** RCGetPromiscuousMode(U16 AdapterID, PU32 pMode, PFNWAITCALLBACK WaitCallback)
+**
+** get promiscuous mode setting
+**
+** Possible return values placed in pMode:
+**  0 = promisuous mode not set
+**  1 = promisuous mode is set
+**
+** =========================================================================
+*/
+RC_RETURN
+RCGetPromiscuousMode(U16 AdapterID, PU32 pMode, PFNWAITCALLBACK WaitCallback);
+
+/*
+** =========================================================================
+** RCSetBroadcastMode(U16 AdapterID, U16 Mode)
+**
+** Defined values for Mode:
+**  0 - turn off promiscuous mode
+**  1 - turn on  promiscuous mode
+**
+** =========================================================================
+*/
+#define BROADCAST_MODE_OFF 0
+#define BROADCAST_MODE_ON  1
+RC_RETURN
+RCSetBroadcastMode(U16 AdapterID, U16 Mode);
+/*
+** =========================================================================
+** RCGetBroadcastMode(U16 AdapterID, PU32 pMode, PFNWAITCALLBACK WaitCallback)
+**
+** get broadcast mode setting
+**
+** Possible return values placed in pMode:
+**  0 = broadcast mode not set
+**  1 = broadcast mode is set
+**
+** =========================================================================
+*/
+RC_RETURN
+RCGetBroadcastMode(U16 AdapterID, PU32 pMode, PFNWAITCALLBACK WaitCallback);
+/*
+** =========================================================================
+** RCReportDriverCapability(U16 AdapterID, U32 capability)
+**
+** Currently defined bits:
+** WARM_REBOOT_CAPABLE   0x01
+**
+** =========================================================================
+*/
+RC_RETURN
+RCReportDriverCapability(U16 AdapterID, U32 capability);
+
+/*
+** RCGetFirmwareVer()
+**
+** Return firmware version in the form "SoftwareVersion : Bt BootVersion"
+**
+** WARNING: user's space pointed to by pFirmString should be at least 60 bytes.
+*/
+RC_RETURN
+RCGetFirmwareVer(U16 AdapterID, PU8 pFirmString, PFNWAITCALLBACK WaitCallback);
+
+/*
+** ----------------------------------------------
+** LAN adapter Reset and Shutdown functions
+** ----------------------------------------------
+*/
+ /* resource flag bit assignments for RCResetLANCard() & RCShutdownLANCard() */
+#define RC_RESOURCE_RETURN_POSTED_RX_BUCKETS  0x0001 
+#define RC_RESOURCE_RETURN_PEND_TX_BUFFERS    0x0002
+
+ /*
+ ** RCResetLANCard()
+ **
+ ** Reset LAN card operation.  Causes a software reset of the ethernet
+ ** controller and restarts the command and receive units. Depending on 
+ ** the ResourceFlags given, the buffers are either returned to the
+ ** host with reply status of I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER and
+ ** detailed status of I2O_LAN_DSC_CANCELED (new receive buffers must be
+ ** posted after issuing this) OR the buffers are kept and reused by
+ ** the ethernet controller. If CallbackFunction is not NULL, the function
+ ** will be called when the reset is complete.  If the CallbackFunction is
+ ** NULL,a 1 will be put into the ReturnAddr after waiting for the reset 
+ ** to complete (please disable I2O interrupts during this method).
+ ** Any outstanding transmit or receive buffers that are complete will be
+ ** returned via the normal reply messages before the requested resource
+ ** buffers are returned.
+ ** A call to RCPostRecvBuffers() is needed to return the ethernet to full
+ ** operation if the receive buffers were returned during LANReset.
+ ** Note: The IOP status is not affected by a LAN reset.
+ */
+RC_RETURN RCResetLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction);
+
+
+ /*
+ ** RCShutdownLANCard()
+ **
+ ** Shutdown LAN card operation and put into an idle (suspended) state.
+ ** The LAN card is restarted with RCResetLANCard() function.
+ ** Depending on the ResourceFlags given, the buffers are either returned 
+ ** to the host with reply status of I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER 
+ ** and detailed status of I2O_LAN_DSC_CANCELED (new receive buffers must be
+ ** posted after issuing this) OR the buffers are kept and reused by
+ ** the ethernet controller. If CallbackFunction is not NULL, the function
+ ** will be called when the reset is complete.  If the CallbackFunction is
+ ** NULL,a 1 will be put into the ReturnAddr after waiting for the reset 
+ ** to complete (please disable I2O interrupts during this method).
+ ** Any outstanding transmit or receive buffers that are complete will be
+ ** returned via the normal reply messages before the requested resource
+ ** buffers are returned.
+ ** Note: The IOP status is not affected by a LAN shutdown.
+ */                                      
+RC_RETURN 
+RCShutdownLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction);
+
+ /*
+ ** RCResetIOP();
+ **     Initializes IOPState to I2O_IOP_STATE_RESET.
+ **     Stops access to outbound message Q.
+ **     Discards any outstanding transmit or posted receive buffers.
+ **     Clears outbound message Q. 
+ */
+RC_RETURN 
+RCResetIOP(U16 AdapterID);
+
+#endif /* RCLANMTL_H */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/rcmtl.c linux/drivers/net/rcmtl.c
--- linux.vanilla/drivers/net/rcmtl.c	Sun Dec  6 00:14:40 1998
+++ linux/drivers/net/rcmtl.c	Thu Jan  1 01:00:00 1970
@@ -1,2058 +0,0 @@
-/*
-** *************************************************************************
-**
-**
-**     R C M T L . C             $Revision: 1.1 $
-**
-**
-**  RedCreek Message Transport Layer program module.
-**
-**  ---------------------------------------------------------------------
-**  ---     Copyright (c) 1997-1998, RedCreek Communications Inc.     ---
-**  ---                   All rights reserved.                        ---
-**  ---------------------------------------------------------------------
-**
-** File Description:
-**
-** Host side message transport layer.
-**
-**  This program is free software; you can redistribute it and/or modify
-**  it under the terms of the GNU General Public License as published by
-**  the Free Software Foundation; either version 2 of the License, or
-**  (at your option) any later version.
-
-**  This program is distributed in the hope that it will be useful,
-**  but WITHOUT ANY WARRANTY; without even the implied warranty of
-**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-**  GNU General Public License for more details.
-
-**  You should have received a copy of the GNU General Public License
-**  along with this program; if not, write to the Free Software
-**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-**
-***************************************************************************/
-
-#undef DEBUG
-
-#define RC_LINUX_MODULE
-#include "rcmtl.h"
-
-#define dprintf kprintf
-
-extern int printk(const char * fmt, ...);
-
- /* RedCreek LAN device Target ID */
-#define LAN_TARGET_ID  0x10 
- /* RedCreek's OSM default LAN receive Initiator */
-#define DEFAULT_RECV_INIT_CONTEXT  0xA17  
-
-
-/*
-** message structures
-*/
-
-#define    TID_SZ                                  12
-#define    FUNCTION_SZ                             8
-
-/* Transaction Reply Lists (TRL) Control Word structure */
-
-#define    TRL_SINGLE_FIXED_LENGTH           0x00
-#define    TRL_SINGLE_VARIABLE_LENGTH        0x40
-#define    TRL_MULTIPLE_FIXED_LENGTH         0x80
-
-/* LAN Class specific functions */
-
-#define    LAN_PACKET_SEND                         0x3B
-#define    LAN_SDU_SEND                            0x3D
-#define    LAN_RECEIVE_POST                        0x3E
-#define    LAN_RESET                               0x35
-#define    LAN_SHUTDOWN                            0x37
-
-/* Private Class specfic function */
-#define    RC_PRIVATE                                 0xFF
-
-/*  RC Executive Function Codes.  */
-
-#define    RC_CMD_ADAPTER_ASSIGN                     0xB3
-#define    RC_CMD_ADAPTER_READ                       0xB2
-#define    RC_CMD_ADAPTER_RELEASE                    0xB5
-#define    RC_CMD_BIOS_INFO_SET                      0xA5
-#define    RC_CMD_BOOT_DEVICE_SET                    0xA7
-#define    RC_CMD_CONFIG_VALIDATE                    0xBB
-#define    RC_CMD_CONN_SETUP                         0xCA
-#define    RC_CMD_DEVICE_ASSIGN                      0xB7
-#define    RC_CMD_DEVICE_RELEASE                     0xB9
-#define    RC_CMD_HRT_GET                            0xA8
-#define    RC_CMD_ADAPTER_CLEAR                          0xBE
-#define    RC_CMD_ADAPTER_CONNECT                        0xC9
-#define    RC_CMD_ADAPTER_RESET                          0xBD
-#define    RC_CMD_LCT_NOTIFY                         0xA2
-#define    RC_CMD_OUTBOUND_INIT                      0xA1
-#define    RC_CMD_PATH_ENABLE                        0xD3
-#define    RC_CMD_PATH_QUIESCE                       0xC5
-#define    RC_CMD_PATH_RESET                         0xD7
-#define    RC_CMD_STATIC_MF_CREATE                   0xDD
-#define    RC_CMD_STATIC_MF_RELEASE                  0xDF
-#define    RC_CMD_STATUS_GET                         0xA0
-#define    RC_CMD_SW_DOWNLOAD                        0xA9
-#define    RC_CMD_SW_UPLOAD                          0xAB
-#define    RC_CMD_SW_REMOVE                          0xAD
-#define    RC_CMD_SYS_ENABLE                         0xD1
-#define    RC_CMD_SYS_MODIFY                         0xC1
-#define    RC_CMD_SYS_QUIESCE                        0xC3
-#define    RC_CMD_SYS_TAB_SET                        0xA3
-
-
- /* Init Outbound Q status */
-#define    RC_CMD_OUTBOUND_INIT_IN_PROGRESS          0x01
-#define    RC_CMD_OUTBOUND_INIT_REJECTED             0x02
-#define    RC_CMD_OUTBOUND_INIT_FAILED               0x03
-#define    RC_CMD_OUTBOUND_INIT_COMPLETE             0x04
-
-
-#define    UTIL_NOP                                0x00
-
-
-/* RC Get Status State values */
-
-#define    ADAPTER_STATE_INITIALIZING                  0x01
-#define    ADAPTER_STATE_RESET                         0x02
-#define    ADAPTER_STATE_HOLD                          0x04
-#define    ADAPTER_STATE_READY                         0x05
-#define    ADAPTER_STATE_OPERATIONAL                   0x08
-#define    ADAPTER_STATE_FAILED                        0x10
-#define    ADAPTER_STATE_FAULTED                       0x11
-
-
-/* Defines for Request Status Codes:  Table 3-1 Reply Status Codes.  */
-
-#define    RC_REPLY_STATUS_SUCCESS                    0x00
-#define    RC_REPLY_STATUS_ABORT_DIRTY                0x01
-#define    RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER     0x02
-#define    RC_REPLY_STATUS_ABORT_PARTIAL_TRANSFER     0x03
-#define    RC_REPLY_STATUS_ERROR_DIRTY                0x04
-#define    RC_REPLY_STATUS_ERROR_NO_DATA_TRANSFER     0x05
-#define    RC_REPLY_STATUS_ERROR_PARTIAL_TRANSFER     0x06
-#define    RC_REPLY_STATUS_PROCESS_ABORT_DIRTY        0x07
-#define    RC_REPLY_STATUS_PROCESS_ABORT_NO_DATA_TRANSFER   0x08
-#define    RC_REPLY_STATUS_PROCESS_ABORT_PARTIAL_TRANSFER   0x09
-#define    RC_REPLY_STATUS_TRANSACTION_ERROR          0x0A
-#define    RC_REPLY_STATUS_PROGRESS_REPORT            0x80
-
-
-/* DetailedStatusCode defines for ALL messages: Table 3-2 Detailed Status Codes.*/
-
-#define    RC_DS_SUCCESS                        0x0000
-#define    RC_DS_BAD_KEY                        0x0001
-#define    RC_DS_CHAIN_BUFFER_TOO_LARGE         0x0002
-#define    RC_DS_DEVICE_BUSY                    0x0003
-#define    RC_DS_DEVICE_LOCKED                  0x0004
-#define    RC_DS_DEVICE_NOT_AVAILABLE           0x0005
-#define    RC_DS_DEVICE_RESET                   0x0006
-#define    RC_DS_INAPPROPRIATE_FUNCTION         0x0007
-#define    RC_DS_INSUFFICIENT_RESOURCE_HARD     0x0008
-#define    RC_DS_INSUFFICIENT_RESOURCE_SOFT     0x0009
-#define    RC_DS_INVALID_INITIATOR_ADDRESS      0x000A
-#define    RC_DS_INVALID_MESSAGE_FLAGS          0x000B
-#define    RC_DS_INVALID_OFFSET                 0x000C
-#define    RC_DS_INVALID_PARAMETER              0x000D
-#define    RC_DS_INVALID_REQUEST                0x000E
-#define    RC_DS_INVALID_TARGET_ADDRESS         0x000F
-#define    RC_DS_MESSAGE_TOO_LARGE              0x0010
-#define    RC_DS_MESSAGE_TOO_SMALL              0x0011
-#define    RC_DS_MISSING_PARAMETER              0x0012
-#define    RC_DS_NO_SUCH_PAGE                   0x0013
-#define    RC_DS_REPLY_BUFFER_FULL              0x0014
-#define    RC_DS_TCL_ERROR                      0x0015
-#define    RC_DS_TIMEOUT                        0x0016
-#define    RC_DS_UNKNOWN_ERROR                  0x0017
-#define    RC_DS_UNKNOWN_FUNCTION               0x0018
-#define    RC_DS_UNSUPPORTED_FUNCTION           0x0019
-#define    RC_DS_UNSUPPORTED_VERSION            0x001A
-
- /* msg header defines for VersionOffset */
-#define RCMSGVER_1   0x0001
-#define SGL_OFFSET_0    RCMSGVER_1
-#define SGL_OFFSET_4    (0x0040 | RCMSGVER_1)
-#define TRL_OFFSET_5    (0x0050 | RCMSGVER_1)
-#define TRL_OFFSET_6    (0x0060 | RCMSGVER_1)
-
- /* msg header defines for MsgFlags */
-#define MSG_STATIC      0x0100
-#define MSG_64BIT_CNTXT 0x0200
-#define MSG_MULTI_TRANS 0x1000
-#define MSG_FAIL        0x2000
-#define MSG_LAST        0x4000
-#define MSG_REPLY       0x8000
-
-  /* normal LAN request message MsgFlags and VersionOffset (0x1041) */
-#define LAN_MSG_REQST  (MSG_MULTI_TRANS | SGL_OFFSET_4)
-
- /* minimum size msg */
-#define THREE_WORD_MSG_SIZE 0x00030000
-#define FOUR_WORD_MSG_SIZE  0x00040000
-#define FIVE_WORD_MSG_SIZE  0x00050000
-#define SIX_WORD_MSG_SIZE   0x00060000
-#define SEVEN_WORD_MSG_SIZE 0x00070000
-#define EIGHT_WORD_MSG_SIZE 0x00080000
-#define NINE_WORD_MSG_SIZE  0x00090000
-
-/* Special TID Assignments */
-
-#define ADAPTER_TID   0
-#define HOST_TID  1
-
- /* RedCreek private message codes */
-#define RC_PRIVATE_GET_MAC_ADDR     0x0001/**/ /* OBSOLETE */
-#define RC_PRIVATE_SET_MAC_ADDR     0x0002
-#define RC_PRIVATE_GET_LAN_STATS    0x0003
-#define RC_PRIVATE_GET_LINK_STATUS  0x0004
-#define RC_PRIVATE_SET_LINK_SPEED   0x0005
-#define RC_PRIVATE_SET_IP_AND_MASK  0x0006
-/* #define RC_PRIVATE_GET_IP_AND_MASK  0x0007 */ /* OBSOLETE */
-#define RC_PRIVATE_GET_LINK_SPEED   0x0008
-#define RC_PRIVATE_GET_FIRMWARE_REV 0x0009
-/* #define RC_PRIVATE_GET_MAC_ADDR     0x000A *//**/
-#define RC_PRIVATE_GET_IP_AND_MASK  0x000B /**/
-#define RC_PRIVATE_DEBUG_MSG        0x000C
-#define RC_PRIVATE_REPORT_DRIVER_CAPABILITY  0x000D
-
-#define RC_PRIVATE_REBOOT           0x00FF
-
-
-/* RC message header */
-typedef struct _RC_MSG_FRAME 
-{
-   U8                          VersionOffset;
-   U8                          MsgFlags;
-   U16                         MessageSize;
-   BF                          TargetAddress:TID_SZ;
-   BF                          InitiatorAddress:TID_SZ;
-   BF                          Function:FUNCTION_SZ;
-   U32                         InitiatorContext;
-   /* SGL[] */ 
-}
- RC_MSG_FRAME, *PRC_MSG_FRAME;
-
-
- /* assumed a 16K minus 256 byte space for outbound queue message frames */
-#define MSG_FRAME_SIZE  512
-#define NMBR_MSG_FRAMES 30
-
-/*
-**  Message Unit CSR definitions for RedCreek PCI45 board
-*/
-typedef struct tag_rcatu 
-{
-    volatile unsigned long APICRegSel;  /* APIC Register Select */
-    volatile unsigned long reserved0;
-    volatile unsigned long APICWinReg;  /* APIC Window Register */
-    volatile unsigned long reserved1;
-    volatile unsigned long InMsgReg0;   /* inbound message register 0 */
-    volatile unsigned long InMsgReg1;   /* inbound message register 1 */
-    volatile unsigned long OutMsgReg0;  /* outbound message register 0 */
-    volatile unsigned long OutMsgReg1;  /* outbound message register 1 */
-    volatile unsigned long InDoorReg;   /* inbound doorbell register */
-    volatile unsigned long InIntStat;   /* inbound interrupt status register */
-    volatile unsigned long InIntMask;   /* inbound interrupt mask register */
-    volatile unsigned long OutDoorReg;  /* outbound doorbell register */
-    volatile unsigned long OutIntStat;  /* outbound interrupt status register */
-    volatile unsigned long OutIntMask;  /* outbound interrupt mask register */
-    volatile unsigned long reserved2;
-    volatile unsigned long reserved3;
-    volatile unsigned long InQueue;     /* inbound queue port */
-    volatile unsigned long OutQueue;    /* outbound queue port */
-    volatile unsigned long reserved4;
-    volatile unsigned long reserver5;
-     /* RedCreek extension */
-    volatile unsigned long EtherMacLow;
-    volatile unsigned long EtherMacHi;
-    volatile unsigned long IPaddr;
-    volatile unsigned long IPmask;
-}
- ATU, *PATU;
-
- /* 
- ** typedef PAB
- **
- ** PCI Adapter Block - holds instance specific information and is located
- ** in a reserved space at the start of the message buffer allocated by user.
- */
-typedef struct
-{
-    PATU             p_atu;                /* ptr to  ATU register block */
-    PU8              pPci45LinBaseAddr;
-    PU8              pLinOutMsgBlock;
-    U32              outMsgBlockPhyAddr; 
-    PFNTXCALLBACK    pTransCallbackFunc;
-    PFNRXCALLBACK    pRecvCallbackFunc;
-    PFNCALLBACK      pRebootCallbackFunc;
-    PFNCALLBACK      pCallbackFunc;
-    U16              ADAPTERState;
-    U16              InboundMFrameSize;
-}
- PAB, *PPAB;
-
- /* 
- ** in reserved space right after PAB in host memory is area for returning
- ** values from card 
- */
-
- /* 
- ** Array of pointers to PCI Adapter Blocks.
- ** Indexed by a zero based (0-31) interface number.
- */ 
-#define MAX_ADAPTERS 32
-static PPAB  PCIAdapterBlock[MAX_ADAPTERS] = 
-{
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
-};
-
-
-/*
-** typedef NICSTAT
-**
-** Data structure for NIC statistics retruned from PCI card.  Data copied from
-** here to user allocated RCLINKSTATS (see rclanmtl.h) structure.
-*/
-typedef struct tag_NicStat 
-{
-        unsigned long   TX_good; 
-        unsigned long   TX_maxcol;
-        unsigned long   TX_latecol;
-        unsigned long   TX_urun;
-        unsigned long   TX_crs;         /* lost carrier sense */
-        unsigned long   TX_def;         /* transmit deferred */
-        unsigned long   TX_singlecol;   /* single collisions */
-        unsigned long   TX_multcol;
-        unsigned long   TX_totcol;
-        unsigned long   Rcv_good;
-        unsigned long   Rcv_CRCerr;
-        unsigned long   Rcv_alignerr;
-        unsigned long   Rcv_reserr;     /* rnr'd pkts */
-        unsigned long   Rcv_orun;
-        unsigned long   Rcv_cdt;
-        unsigned long   Rcv_runt;
-        unsigned long   dump_status;    /* last field directly from the chip */
-} 
- NICSTAT, *P_NICSTAT;
- 
-
-#define DUMP_DONE   0x0000A005      /* completed statistical dump */
-#define DUMP_CLEAR  0x0000A007      /* completed stat dump and clear counters */
-
-
-static volatile int msgFlag;
-
-
-/* local function prototypes */
-static void ProcessOutboundAdapterMsg(PPAB pPab, U32 phyMsgAddr);
-static int FillAdapterMsgSGLFromTCB(PU32 pMsg, PRCTCB pXmitCntrlBlock);
-static int GetAdapterStatus(PPAB pPab);
-static int SendAdapterOutboundQInitMsg(PPAB pPab);
-static int SendEnableSysMsg(PPAB pPab);
-
-
- /* 1st 100h bytes of message block is reserved for messenger instance */
-#define ADAPTER_BLOCK_RESERVED_SPACE 0x100
-
-/*
-** =========================================================================
-** InitRCApiMsgLayer()
-**
-** Initialize the RedCreek API Module and adapter.
-**
-** Inputs:  AdapterID - interface number from 0 to 15
-**          pciBaseAddr - virual base address of PCI (set by BIOS)
-**          p_msgbuf - virual address to private message block (min. 16K)
-**          p_phymsgbuf - physical address of private message block
-**          TransmitCallbackFunction - address of transmit callback function
-**          ReceiveCallbackFunction  - address of receive  callback function
-**
-** private message block is allocated by user.  It must be in locked pages.
-** p_msgbuf and p_phymsgbuf point to the same location.  Must be contigous
-** memory block of a minimum of 16K byte and long word aligned.
-** =========================================================================
-*/
-RC_RETURN
-InitRCApiMsgLayer(U16 AdapterID, U32 pciBaseAddr, 
-                  PU8 p_msgbuf,  PU8 p_phymsgbuf,
-                  PFNTXCALLBACK  TransmitCallbackFunction,
-                  PFNRXCALLBACK  ReceiveCallbackFunction,
-                  PFNCALLBACK    RebootCallbackFunction)
-{
-     int result;
-     PPAB pPab; 
-    
-#ifdef DEBUG
-     kprintf("InitAPI: Adapter:0x%04.4ux ATU:0x%08.8ulx msgbuf:0x%08.8ulx phymsgbuf:0x%08.8ulx\n"
-             "TransmitCallbackFunction:0x%08.8ulx  ReceiveCallbackFunction:0x%08.8ulx\n",
-             AdapterID, pciBaseAddr, p_msgbuf, p_phymsgbuf, TransmitCallbackFunction, ReceiveCallbackFunction);
-#endif /* DEBUG */
-
-    
-     /* Check if this interface already initialized - if so, shut it down */
-     if (PCIAdapterBlock[AdapterID] != NULL)
-     {
-          printk("PCIAdapterBlock[%d]!=NULL\n", AdapterID);
-//        RCResetLANCard(AdapterID, 0, (PU32)NULL, (PFNCALLBACK)NULL);
-          PCIAdapterBlock[AdapterID] = NULL;
-     }
-
-    /* 
-    ** store adapter instance values in adapter block.
-    ** Adapter block is at beginning of message buffer
-    */  
-    pPab = (PPAB)p_msgbuf;
-    
-    pPab->p_atu = (PATU)pciBaseAddr;
-    pPab->pPci45LinBaseAddr =  (PU8)pciBaseAddr;
-    
-     /* Set outbound message frame addr - skip over Adapter Block */
-    pPab->outMsgBlockPhyAddr = (U32)(p_phymsgbuf + ADAPTER_BLOCK_RESERVED_SPACE);
-    pPab->pLinOutMsgBlock    = (PU8)(p_msgbuf + ADAPTER_BLOCK_RESERVED_SPACE);
-
-     /* store callback function addresses */
-    pPab->pTransCallbackFunc = TransmitCallbackFunction;
-    pPab->pRecvCallbackFunc  = ReceiveCallbackFunction;
-    pPab->pRebootCallbackFunc  = RebootCallbackFunction;
-    pPab->pCallbackFunc  = (PFNCALLBACK)NULL;
-
-    /*
-    ** Initialize API 
-    */
-    result = GetAdapterStatus(pPab);
-    
-    if (result != RC_RTN_NO_ERROR)
-        return result;
-
-    if (pPab->ADAPTERState == ADAPTER_STATE_OPERATIONAL)
-    {
-         printk("pPab->ADAPTERState == op: resetting adapter\n");
-         RCResetLANCard(AdapterID, 0, (PU32)NULL, (PFNCALLBACK)NULL);
-    }
-        
-    result = SendAdapterOutboundQInitMsg(pPab);
-    
-    if (result != RC_RTN_NO_ERROR)
-         return result;
-
-    result = SendEnableSysMsg(pPab);
-   
-    if (result != RC_RTN_NO_ERROR)
-         return result;
-   
-    PCIAdapterBlock[AdapterID] = pPab;
-    return RC_RTN_NO_ERROR;
-}
-
-/*
-** =========================================================================
-** Disable and Enable Adapter interrupts.  Adapter interrupts are enabled at Init time
-** but can be disabled and re-enabled through these two function calls.
-** Packets will still be put into any posted received buffers and packets will
-** be sent through RCSendPacket() functions.  Disabling Adapter interrupts
-** will prevent hardware interrupt to host even though the outbound Adapter msg
-** queue is not emtpy.
-** =========================================================================
-*/
-#define i960_OUT_POST_Q_INT_BIT        0x0008 /* bit set masks interrupts */
-
-RC_RETURN RCDisableAdapterInterrupts(U16 AdapterID)
-{
-    PPAB pPab;
-
-
-    pPab = PCIAdapterBlock[AdapterID];
-    
-    if (pPab == NULL)
-        return RC_RTN_ADPTR_NOT_REGISTERED;
-        
-    pPab->p_atu->OutIntMask |= i960_OUT_POST_Q_INT_BIT;
-
-    return RC_RTN_NO_ERROR;
-}
-
-RC_RETURN RCEnableAdapterInterrupts(U16 AdapterID)
-{
-    PPAB pPab;
-
-    pPab = PCIAdapterBlock[AdapterID];
-    
-    if (pPab == NULL)
-         return RC_RTN_ADPTR_NOT_REGISTERED;
-    
-    pPab->p_atu->OutIntMask &= ~i960_OUT_POST_Q_INT_BIT;
-    
-    return RC_RTN_NO_ERROR;
-
-}
-
-
-/*
-** =========================================================================
-** RCSendPacket()
-** =========================================================================
-*/
-RC_RETURN
-RCSendPacket(U16 AdapterID, U32 InitiatorContext, PRCTCB pTransCtrlBlock)
-{
-    U32 msgOffset;
-    PU32 pMsg;
-    int size;
-    PPAB pPab;
-
-#ifdef DEBUG
-kprintf("RCSendPacket()...\n");
-#endif /* DEBUG */
-    
-    pPab = PCIAdapterBlock[AdapterID];
-
-    if (pPab == NULL)
-        return RC_RTN_ADPTR_NOT_REGISTERED;
-    
-     /* get Inbound free Q entry - reading from In Q gets free Q entry */
-     /* offset to Msg Frame in PCI msg block */
-
-    msgOffset = pPab->p_atu->InQueue; 
-
-    if (msgOffset == 0xFFFFFFFF)
-    {
-#ifdef DEBUG
-        kprintf("RCSendPacket(): Inbound Free Q empty!\n");
-#endif /* DEBUG */
-        return RC_RTN_FREE_Q_EMPTY;
-    }
-        
-     /* calc virual address of msg - virual already mapped to physical */    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
-
-    size = FillAdapterMsgSGLFromTCB(pMsg + 4, pTransCtrlBlock);
-
-    if (size == -1) /* error processing TCB - send NOP msg */
-    {
-#ifdef DEBUG
-            kprintf("RCSendPacket(): Error Rrocess TCB!\n");
-#endif /* DEBUG */
-        pMsg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0;
-        pMsg[1] = UTIL_NOP << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-        return RC_RTN_TCB_ERROR;
-    }
-    else /* send over msg header */
-    {    
-        pMsg[0] = (size + 4) << 16 | LAN_MSG_REQST; /* send over message size and flags */
-        pMsg[1] = LAN_PACKET_SEND << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-        pMsg[2] = InitiatorContext;
-        pMsg[3] = 0;  /* batch reply */
-         /* post to Inbound Post Q */   
-        pPab->p_atu->InQueue = msgOffset;
-        return RC_RTN_NO_ERROR;
-    }
-}
- 
-
-/*
-** =========================================================================
-** RCPostRecvBuffer()
-**
-** inputs:  pBufrCntrlBlock - pointer to buffer control block
-**
-** returns TRUE if successful in sending message, else FALSE.
-** =========================================================================
-*/
-RC_RETURN
-RCPostRecvBuffers(U16 AdapterID, PRCTCB pTransCtrlBlock)
-{
-    U32 msgOffset;
-    PU32 pMsg;
-    int size;
-    PPAB pPab;
-
-#ifdef DEBUG
-kprintf("RCPostRecvBuffers()...\n");
-#endif /* DEBUG */
-    
-     /* search for DeviceHandle */
-    pPab = PCIAdapterBlock[AdapterID];
-
-    if (pPab == NULL)
-        return RC_RTN_ADPTR_NOT_REGISTERED;
-    
-
-     /* get Inbound free Q entry - reading from In Q gets free Q entry */
-     /* offset to Msg Frame in PCI msg block */
-    msgOffset = pPab->p_atu->InQueue; 
-
-    if (msgOffset == 0xFFFFFFFF)
-    {
-#ifdef DEBUG
-            kprintf("RCPostRecvBuffers(): Inbound Free Q empty!\n");
-#endif /* DEBUG */
-        return RC_RTN_FREE_Q_EMPTY;
-   
-    }
-     /* calc virual address of msg - virual already mapped to physical */    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
-
-    size = FillAdapterMsgSGLFromTCB(pMsg + 4, pTransCtrlBlock);
-
-    if (size == -1) /* error prcessing TCB - send 3 DWORD private msg == NOP */
-    {
-#ifdef DEBUG
-        kprintf("RCPostRecvBuffers(): Error Processing TCB! size = %d\n", size);
-#endif /* DEBUG */
-        pMsg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0;
-        pMsg[1] = UTIL_NOP << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-         /* post to Post Q */   
-        pPab->p_atu->InQueue = msgOffset;
-        return RC_RTN_TCB_ERROR;
-    }
-    else /* send over size msg header */
-    {    
-        pMsg[0] = (size + 4) << 16 | LAN_MSG_REQST; /* send over message size and flags */
-        pMsg[1] = LAN_RECEIVE_POST << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-        pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
-        pMsg[3] = *(PU32)pTransCtrlBlock; /* number of packet buffers */
-         /* post to Post Q */   
-        pPab->p_atu->InQueue = msgOffset;
-        return RC_RTN_NO_ERROR;
-    }
-}
-
-
-/*
-** =========================================================================
-** RCProcMsgQ()
-**
-** Process outbound message queue until empty.
-** =========================================================================
-*/
-void 
-RCProcMsgQ(U16 AdapterID)
-{
-    U32 phyAddrMsg;
-    PU8 p8Msg;
-    PU32 p32;
-    U16 count;
-    PPAB pPab;
-    unsigned char debug_msg[20];
-    
-    pPab = PCIAdapterBlock[AdapterID];
-    
-    if (pPab == NULL)
-        return;
-    
-    phyAddrMsg = pPab->p_atu->OutQueue;
-
-    while (phyAddrMsg != 0xFFFFFFFF)
-    {
-        p8Msg = pPab->pLinOutMsgBlock + (phyAddrMsg - pPab->outMsgBlockPhyAddr);
-        p32 = (PU32)p8Msg;
-        
-        //printk(" msg: 0x%x  0x%x \n", p8Msg[7], p32[5]);
-
-     /* 
-     ** Send Packet Reply Msg
-     */
-        if (LAN_PACKET_SEND == p8Msg[7])  /* function code byte */
-        {
-            count = *(PU16)(p8Msg+2);
-            count -= p8Msg[0] >> 4;
-                                      /* status, count, context[], adapter */
-           (*pPab->pTransCallbackFunc)(p8Msg[19], count, p32+5, AdapterID);
-        }             
-     /* 
-     ** Receive Packet Reply Msg */
-        else if (LAN_RECEIVE_POST == p8Msg[7])
-        {
-#ifdef DEBUG    
-    kprintf("RECV_REPLY pPab:0x%08.8ulx p8Msg:0x%08.8ulx p32:0x%08.8ulx\n", pPab, p8Msg, p32);
-    kprintf("msg: 0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
-            p32[0], p32[1], p32[2], p32[3]);
-    kprintf("     0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
-            p32[4], p32[5], p32[6], p32[7]);
-    kprintf("     0x%08.8ulx:0X%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
-            p32[8], p32[9], p32[10], p32[11]);
-#endif
-               /*  status, count, buckets remaining, packetParmBlock, adapter */
-          (*pPab->pRecvCallbackFunc)(p8Msg[19], p8Msg[12], p32[5], p32+6, AdapterID);
-      
-      
-        }
-        else if (LAN_RESET == p8Msg[7] || LAN_SHUTDOWN == p8Msg[7])
-        {
-            if (pPab->pCallbackFunc)
-            {
-                (*pPab->pCallbackFunc)(p8Msg[19],0,0,AdapterID);
-            }
-            else
-            {
-                pPab->pCallbackFunc = (PFNCALLBACK) 1;
-            }
-            //PCIAdapterBlock[AdapterID] = 0;
-        }
-        else if (RC_PRIVATE == p8Msg[7])
-        {
-            //printk("i2o private 0x%x, 0x%x \n", p8Msg[7], p32[5]);
-          switch (p32[5])
-          {
-          case RC_PRIVATE_DEBUG_MSG:
-               msgFlag = 1;
-               /*printk("Received RC_PRIVATE msg\n");*/
-               debug_msg[15]  = (p32[6]&0xff000000) >> 24;
-               debug_msg[14]  = (p32[6]&0x00ff0000) >> 16;
-               debug_msg[13]  = (p32[6]&0x0000ff00) >> 8;
-               debug_msg[12]  = (p32[6]&0x000000ff);
-
-               debug_msg[11]  = (p32[7]&0xff000000) >> 24;
-               debug_msg[10]  = (p32[7]&0x00ff0000) >> 16;
-               debug_msg[ 9]  = (p32[7]&0x0000ff00) >> 8;
-               debug_msg[ 8]  = (p32[7]&0x000000ff);
-
-               debug_msg[ 7]  = (p32[8]&0xff000000) >> 24;
-               debug_msg[ 6]  = (p32[8]&0x00ff0000) >> 16;
-               debug_msg[ 5] = (p32[8]&0x0000ff00) >> 8;
-               debug_msg[ 4] = (p32[8]&0x000000ff);
-
-               debug_msg[ 3] = (p32[9]&0xff000000) >> 24;
-               debug_msg[ 2] = (p32[9]&0x00ff0000) >> 16;
-               debug_msg[ 1] = (p32[9]&0x0000ff00) >> 8;
-               debug_msg[ 0] = (p32[9]&0x000000ff);
-
-               debug_msg[16] = '\0';
-               printk (debug_msg);
-               break;
-          case RC_PRIVATE_REBOOT:
-               printk("Adapter reboot initiated...\n");
-               if (pPab->pRebootCallbackFunc)
-               {
-                    (*pPab->pRebootCallbackFunc)(0,0,0,AdapterID);
-               }
-               break;
-          default:
-                printk("Unknown private msg received: 0x%x\n",
-                      p32[5]);
-               break;
-          }
-        }
-
-     /* 
-     ** Process other Msg's
-     */
-        else
-        {
-            ProcessOutboundAdapterMsg(pPab, phyAddrMsg);
-        }
-        
-         /* return MFA to outbound free Q*/
-        pPab->p_atu->OutQueue = phyAddrMsg;
-    
-         /* any more msgs? */
-        phyAddrMsg = pPab->p_atu->OutQueue;
-    }
-}
-
-
-/*
-** =========================================================================
-**  Returns LAN interface statistical counters to space provided by caller at
-**  StatsReturnAddr.  Returns 0 if success, else RC_RETURN code.
-**  This function will call the WaitCallback function provided by
-**  user while waiting for card to respond.
-** =========================================================================
-*/
-RC_RETURN
-RCGetLinkStatistics(U16 AdapterID, 
-                    P_RCLINKSTATS StatsReturnAddr,
-                    PFNWAITCALLBACK WaitCallback)
-{
-    U32 msgOffset;
-    volatile U32 timeout;
-    volatile PU32 pMsg;
-    volatile PU32 p32, pReturnAddr;
-    P_NICSTAT pStats;
-    int i;
-    PPAB pPab;
-
-/*kprintf("Get82558Stats() StatsReturnAddr:0x%08.8ulx\n", StatsReturnAddr);*/
-
-    pPab = PCIAdapterBlock[AdapterID];
-
-    if (pPab == NULL)
-        return RC_RTN_ADPTR_NOT_REGISTERED;
-    
-    msgOffset = pPab->p_atu->InQueue;
-
-    if (msgOffset == 0xFFFFFFFF)
-    {
-    #ifdef DEBUG
-        kprintf("Get8255XStats(): Inbound Free Q empty!\n");
-    #endif
-        return RC_RTN_FREE_Q_EMPTY;
-    }
-
-     /* calc virual address of msg - virual already mapped to physical */
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
-
-/*dprintf("Get82558Stats - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n", pMsg, msgOffset);*/
-/*dprintf("Get82558Stats - pMsg = 0x%08X, InQ msgOffset = 0x%08X\n", pMsg, msgOffset);*/
-
-    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
-    pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
-    pMsg[3] = 0x112; /* transaction context */
-    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_LAN_STATS;
-    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
-
-    p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
-
-    pStats = (P_NICSTAT)p32;
-    pStats->dump_status = 0xFFFFFFFF;
-
-     /* post to Inbound Post Q */
-    pPab->p_atu->InQueue = msgOffset;
-
-    timeout = 100000;
-    while (1)
-    {
-        if (WaitCallback)
-                (*WaitCallback)();
-        
-        for (i = 0; i < 1000; i++)
-            ;
-
-        if (pStats->dump_status != 0xFFFFFFFF)
-            break;
-
-        if (!timeout--)
-        {
-        #ifdef DEBUG
-            kprintf("RCGet82558Stats() Timeout waiting for NIC statistics\n");
-        #endif
-            return RC_RTN_MSG_REPLY_TIMEOUT;
-        }
-    }
-    
-    pReturnAddr = (PU32)StatsReturnAddr;
-    
-     /* copy Nic stats to user's structure */
-    for (i = 0; i < (int) sizeof(RCLINKSTATS) / 4; i++)
-        pReturnAddr[i] = p32[i];
-   
-    return RC_RTN_NO_ERROR;     
-}
-
-
-/*
-** =========================================================================
-** Get82558LinkStatus()
-** =========================================================================
-*/
-RC_RETURN
-RCGetLinkStatus(U16 AdapterID, PU32 ReturnAddr, PFNWAITCALLBACK WaitCallback)
-{
-    U32 msgOffset;
-    volatile U32 timeout;
-    volatile PU32 pMsg;
-    volatile PU32 p32;
-    PPAB pPab;
-
-/*kprintf("Get82558LinkStatus() ReturnPhysAddr:0x%08.8ulx\n", ReturnAddr);*/
-
-    pPab = PCIAdapterBlock[AdapterID];
-
-    if (pPab == NULL)
-        return RC_RTN_ADPTR_NOT_REGISTERED;
-    
-    msgOffset = pPab->p_atu->InQueue;
-
-    if (msgOffset == 0xFFFFFFFF)
-    {
-    #ifdef DEBUG
-        dprintf("Get82558LinkStatus(): Inbound Free Q empty!\n");
-    #endif
-        return RC_RTN_FREE_Q_EMPTY;
-    }
-
-     /* calc virual address of msg - virual already mapped to physical */
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
-/*dprintf("Get82558LinkStatus - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n", pMsg, msgOffset);*/
-/*dprintf("Get82558LinkStatus - pMsg = 0x%08X, InQ msgOffset = 0x%08X\n", pMsg, msgOffset);*/
-
-    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
-    pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
-    pMsg[3] = 0x112; /* transaction context */
-    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_LINK_STATUS;
-    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
-
-    p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
-    *p32 = 0xFFFFFFFF;
-    
-     /* post to Inbound Post Q */
-    pPab->p_atu->InQueue = msgOffset;
-
-    timeout = 100000;
-    while (1)
-    {
-        U32 i;
-
-        if (WaitCallback)
-                (*WaitCallback)();
-        
-        for (i = 0; i < 1000; i++)
-            ;
-
-        if (*p32 != 0xFFFFFFFF)
-            break;
-
-        if (!timeout--)
-        {
-        #ifdef DEBUG
-            kprintf("Timeout waiting for link status\n");
-        #endif    
-            return RC_RTN_MSG_REPLY_TIMEOUT;
-        }
-    }
-    
-    *ReturnAddr = *p32; /* 1 = up 0 = down */
-    
-    return RC_RTN_NO_ERROR;
-    
-}
-
-/*
-** =========================================================================
-** RCGetMAC()
-**
-** get the MAC address the adapter is listening for in non-promiscous mode.
-** MAC address is in media format.
-** =========================================================================
-*/
-RC_RETURN
-RCGetMAC(U16 AdapterID, PU8 mac, PFNWAITCALLBACK WaitCallback)
-{
-     unsigned i, timeout;
-     U32      off;
-     PU32     p;
-     U32      temp[2];
-     PPAB     pPab;
-     PATU     p_atu;
-    
-     pPab = PCIAdapterBlock[AdapterID];
-    
-     if (pPab == NULL)
-          return RC_RTN_ADPTR_NOT_REGISTERED;
-    
-     p_atu = pPab->p_atu;
-
-     p_atu->EtherMacLow = 0;     /* first zero return data */
-     p_atu->EtherMacHi = 0;
-    
-     off = p_atu->InQueue;   /* get addresss of message */
- 
-     if (0xFFFFFFFF == off)
-          return RC_RTN_FREE_Q_EMPTY;
-
-     p = (PU32)(pPab->pPci45LinBaseAddr + off);
-
-#ifdef RCDEBUG
-     printk("RCGetMAC: p_atu 0x%08x, off 0x%08x, p 0x%08x\n", 
-            (uint)p_atu, (uint)off, (uint)p);
-#endif /* RCDEBUG */
-     /* setup private message */
-     p[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0;
-     p[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-     p[2] = 0;               /* initiator context */
-     p[3] = 0x218;           /* transaction context */
-     p[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_MAC_ADDR;
-
-
-     p_atu->InQueue = off;   /* send it to the device */
-#ifdef RCDEBUG
-     printk("RCGetMAC: p_atu 0x%08x, off 0x%08x, p 0x%08x\n", 
-            (uint)p_atu, (uint)off, (uint)p);
-#endif /* RCDEBUG */
-
-     /* wait for the rcpci45 board to update the info */
-     timeout = 1000000;
-     while (0 == p_atu->EtherMacLow) 
-     {
-          if (WaitCallback)
-               (*WaitCallback)();
-    
-          for (i = 0; i < 1000; i++)
-               ;
-
-          if (!timeout--)
-          {
-               printk("rc_getmac: Timeout\n");
-               return RC_RTN_MSG_REPLY_TIMEOUT;
-          }
-     }
-    
-     /* read the mac address  */
-     temp[0] = p_atu->EtherMacLow;
-     temp[1] = p_atu->EtherMacHi;
-     memcpy((char *)mac, (char *)temp, 6);
-
-
-#ifdef RCDEBUG
-//    printk("rc_getmac: 0x%X\n", ptr);
-#endif /* RCDEBUG */
-
-     return RC_RTN_NO_ERROR;
-}
-
- 
-/*
-** =========================================================================
-** RCSetMAC()
-**
-** set MAC address the adapter is listening for in non-promiscous mode.
-** MAC address is in media format.
-** =========================================================================
-*/
-RC_RETURN
-RCSetMAC(U16 AdapterID, PU8 mac)
-{
-    U32  off;
-    PU32 pMsg;
-    PPAB pPab;
-
-
-    pPab = PCIAdapterBlock[AdapterID];
-    
-    if (pPab == NULL)
-        return RC_RTN_ADPTR_NOT_REGISTERED;
-    
-    off = pPab->p_atu->InQueue; /* get addresss of message */
-    
-    if (0xFFFFFFFF == off)
-         return RC_RTN_FREE_Q_EMPTY;
-    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
-
-    /* setup private message */
-    pMsg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0;
-    pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-    pMsg[2] = 0;                 /* initiator context */
-    pMsg[3] = 0x219;             /* transaction context */
-    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_MAC_ADDR;
-    pMsg[5] = *(unsigned *)mac;  /* first four bytes */
-    pMsg[6] = *(unsigned *)(mac + 4); /* last two bytes */
-    
-    pPab->p_atu->InQueue = off;   /* send it to the device */
-
-    return RC_RTN_NO_ERROR ;
-}
-
-
-/*
-** =========================================================================
-** RCSetLinkSpeed()
-**
-** set ethernet link speed. 
-** input: speedControl - determines action to take as follows
-**          0 = reset and auto-negotiate (NWay)
-**          1 = Full Duplex 100BaseT
-**          2 = Half duplex 100BaseT
-**          3 = Full Duplex  10BaseT
-**          4 = Half duplex  10BaseT
-**          all other values are ignore (do nothing)
-** =========================================================================
-*/
-RC_RETURN
-RCSetLinkSpeed(U16 AdapterID, U16 LinkSpeedCode)
-{
-    U32  off;
-    PU32 pMsg;
-    PPAB pPab;
-
-    
-    pPab =PCIAdapterBlock[AdapterID];
-     
-    if (pPab == NULL)
-        return RC_RTN_ADPTR_NOT_REGISTERED;
-    
-    off = pPab->p_atu->InQueue; /* get addresss of message */
-    
-    if (0xFFFFFFFF == off)
-         return RC_RTN_FREE_Q_EMPTY;
-    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
-
-    /* setup private message */
-    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
-    pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-    pMsg[2] = 0;                 /* initiator context */
-    pMsg[3] = 0x219;             /* transaction context */
-    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_LINK_SPEED;
-    pMsg[5] = LinkSpeedCode;     /* link speed code */
-
-    pPab->p_atu->InQueue = off;   /* send it to the device */
-
-    return RC_RTN_NO_ERROR ;
-}
-
-/*
-** =========================================================================
-** RCGetLinkSpeed()
-**
-** get ethernet link speed. 
-**
-** 0 = Unknown
-** 1 = Full Duplex 100BaseT
-** 2 = Half duplex 100BaseT
-** 3 = Full Duplex  10BaseT
-** 4 = Half duplex  10BaseT
-**
-** =========================================================================
-*/
-RC_RETURN
-RCGetLinkSpeed(U16 AdapterID, PU32 pLinkSpeedCode, PFNWAITCALLBACK WaitCallback)
-{
-     U32 msgOffset, timeout;
-     PU32 pMsg;
-     volatile PU32 p32;
-     U8 AdapterLinkSpeed;
-     PPAB pPab;
-
-     pPab =PCIAdapterBlock[AdapterID];
-
-
-     msgOffset = pPab->p_atu->InQueue;
-
-
-     if (msgOffset == 0xFFFFFFFF)
-     {
-          kprintf("RCGetLinkSpeed(): Inbound Free Q empty!\n");
-          return RC_RTN_FREE_Q_EMPTY;
-     }
-
-     /* calc virtual address of msg - virtual already mapped to physical */    
-     pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
-
-     /* virtual pointer to return buffer - clear first two dwords */
-     p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
-     p32[0] = 0xff;
-
-     /* setup private message */
-     pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
-     pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-     pMsg[2] = 0;                 /* initiator context */
-     pMsg[3] = 0x219;             /* transaction context */
-     pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_LINK_SPEED;
-     /* phys address to return status - area right after PAB */
-     pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
-   
-     /* post to Inbound Post Q */   
-
-     pPab->p_atu->InQueue = msgOffset;
-
-     /* wait for response */
-     timeout = 1000000;
-     while(1)
-     {
-          int i;
-        
-          if (WaitCallback)
-                (*WaitCallback)();
-
-          for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
-               ;
-            
-          if (p32[0] != 0xff)
-               break;
-            
-          if (!timeout--)
-          {
-               kprintf("Timeout waiting for link speed from adapter\n");
-               kprintf("0x%08.8ulx\n", p32[0]);
-               return RC_RTN_NO_LINK_SPEED;
-          }
-     }
-
-     /* get Link speed */
-     AdapterLinkSpeed = (U8)((volatile PU8)p32)[0] & 0x0f;
-
-     *pLinkSpeedCode= AdapterLinkSpeed;
-
-     return RC_RTN_NO_ERROR;
-}
-
-/*
-** =========================================================================
-** RCReportDriverCapability(U16 AdapterID, U32 capability)
-**
-** Currently defined bits:
-** WARM_REBOOT_CAPABLE   0x01
-**
-** =========================================================================
-*/
-RC_RETURN
-RCReportDriverCapability(U16 AdapterID, U32 capability)
-{
-    U32  off;
-    PU32 pMsg;
-    PPAB pPab;
-
-    pPab =PCIAdapterBlock[AdapterID];
-     
-    if (pPab == NULL)
-        return RC_RTN_ADPTR_NOT_REGISTERED;
-    
-    off = pPab->p_atu->InQueue; /* get addresss of message */
-    
-    if (0xFFFFFFFF == off)
-         return RC_RTN_FREE_Q_EMPTY;
-    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
-
-    /* setup private message */
-    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
-    pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-    pMsg[2] = 0;                 /* initiator context */
-    pMsg[3] = 0x219;             /* transaction context */
-    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_REPORT_DRIVER_CAPABILITY;
-    pMsg[5] = capability;
-
-    pPab->p_atu->InQueue = off;   /* send it to the device */
-
-    return RC_RTN_NO_ERROR ;
-}
-
-/*
-** =========================================================================
-** RCGetFirmwareVer()
-**
-** Return firmware version in the form "SoftwareVersion : Bt BootVersion"
-**
-** =========================================================================
-*/
-RC_RETURN
-RCGetFirmwareVer(U16 AdapterID, PU8 pFirmString, PFNWAITCALLBACK WaitCallback)
-{
-     U32 msgOffset, timeout;
-     PU32 pMsg;
-     volatile PU32 p32;
-     PPAB pPab;
-
-     pPab =PCIAdapterBlock[AdapterID];
-
-     msgOffset = pPab->p_atu->InQueue;
-
-
-     if (msgOffset == 0xFFFFFFFF)
-     {
-          kprintf("RCGetFirmwareVer(): Inbound Free Q empty!\n");
-          return RC_RTN_FREE_Q_EMPTY;
-     }
-
-     /* calc virtual address of msg - virtual already mapped to physical */    
-     pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
-
-     /* virtual pointer to return buffer - clear first two dwords */
-     p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
-     p32[0] = 0xff;
-
-     /* setup private message */
-     pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
-     pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-     pMsg[2] = 0;                 /* initiator context */
-     pMsg[3] = 0x219;             /* transaction context */
-     pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_FIRMWARE_REV;
-     /* phys address to return status - area right after PAB */
-     pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
-
-
-   
-     /* post to Inbound Post Q */   
-
-     pPab->p_atu->InQueue = msgOffset;
-
-    
-     /* wait for response */
-     timeout = 1000000;
-     while(1)
-     {
-          int i;
-        
-          if (WaitCallback)
-                (*WaitCallback)();
-            
-          for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
-               ;
-
-          if (p32[0] != 0xff)
-               break;
-            
-          if (!timeout--)
-          {
-               kprintf("Timeout waiting for link speed from adapter\n");
-               return RC_RTN_NO_FIRM_VER;
-          }
-     }
-
-     strcpy(pFirmString, (PU8)p32);
-     return RC_RTN_NO_ERROR;
-}
-
-/*
-** =========================================================================
-** RCResetLANCard()
-**
-** ResourceFlags indicates whether to return buffer resource explicitly
-** to host or keep and reuse.
-** CallbackFunction (if not NULL) is the function to be called when 
-** reset is complete.
-** If CallbackFunction is NULL, ReturnAddr will have a 1 placed in it when
-** reset is done (if not NULL).
-**
-** =========================================================================
-*/
-RC_RETURN 
-RCResetLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction)
-{
-    unsigned long off;
-    unsigned long *pMsg;
-    PPAB pPab;
-    int i;
-    long timeout = 0;
-
-    
-    pPab =PCIAdapterBlock[AdapterID];
-     
-    if (pPab == NULL)
-        return RC_RTN_ADPTR_NOT_REGISTERED;
-
-    off = pPab->p_atu->InQueue; /* get addresss of message */
-    
-    if (0xFFFFFFFF == off)
-         return RC_RTN_FREE_Q_EMPTY;
-    
-    pPab->pCallbackFunc = CallbackFunction;
-
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
-
-    /* setup message */
-    pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0;
-    pMsg[1] = LAN_RESET << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
-    pMsg[3] = ResourceFlags << 16;   /* resource flags */
-
-    pPab->p_atu->InQueue = off;   /* send it to the device */
-
-    if (CallbackFunction == (PFNCALLBACK)NULL)
-    {
-        /* call RCProcMsgQ() until something in pPab->pCallbackFunc
-           or until timer goes off */
-        while (pPab->pCallbackFunc == (PFNCALLBACK)NULL)
-        {
-            RCProcMsgQ(AdapterID);
-            for (i = 0; i < 100000; i++)     /* please don't hog the bus!!! */
-               ;
-            timeout++;
-            if (timeout > 10000)
-            {
-                break;
-            }
-        }
-        if (ReturnAddr != (PU32)NULL)
-           *ReturnAddr = (U32)pPab->pCallbackFunc;
-    }
-
-    return RC_RTN_NO_ERROR ;
-}
-/*
-** =========================================================================
-** RCResetAdapter()
-**
-** Send StatusGet Msg, wait for results return directly to buffer.
-**
-** =========================================================================
-*/
-RC_RETURN 
-RCResetAdapter(U16 AdapterID)
-{
-     U32 msgOffset, timeout;
-     PU32 pMsg;
-     PPAB pPab;
-     volatile PU32 p32;
-    
-     pPab = PCIAdapterBlock[AdapterID];
-     msgOffset = pPab->p_atu->InQueue;
-
-     if (msgOffset == 0xFFFFFFFF)
-     {
-          return RC_RTN_FREE_Q_EMPTY;
-     }
-
-     /* calc virtual address of msg - virtual already mapped to physical */    
-     pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
-
-     pMsg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_0;
-     pMsg[1] = RC_CMD_ADAPTER_RESET << 24 | HOST_TID << 12 | ADAPTER_TID;
-     pMsg[2] = 0; /* universal context */
-     pMsg[3] = 0; /* universal context */
-     pMsg[4] = 0; /* universal context */
-     pMsg[5] = 0; /* universal context */
-     /* phys address to return status - area right after PAB */
-     pMsg[6] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
-     pMsg[7] = 0;
-     pMsg[8] = 1;  /*  return 1 byte */
-
-     /* virual pointer to return buffer - clear first two dwords */
-     p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
-     p32[0] = 0;
-     p32[1] = 0;
-
-     /* post to Inbound Post Q */   
-
-     pPab->p_atu->InQueue = msgOffset;
-
-     /* wait for response */
-     timeout = 1000000;
-     while(1)
-     {
-          int i;
-        
-          for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
-               ;
-            
-          if (p32[0] || p32[1])
-               break;
-            
-          if (!timeout--)
-          {
-               printk("RCResetAdapter timeout\n");
-               return RC_RTN_MSG_REPLY_TIMEOUT;
-          }
-     }
-     return RC_RTN_NO_ERROR;
-}
-
-/*
-** =========================================================================
-** RCShutdownLANCard()
-**
-** ResourceFlags indicates whether to return buffer resource explicitly
-** to host or keep and reuse.
-** CallbackFunction (if not NULL) is the function to be called when 
-** shutdown is complete.
-** If CallbackFunction is NULL, ReturnAddr will have a 1 placed in it when
-** shutdown is done (if not NULL).
-**
-** =========================================================================
-*/
-RC_RETURN 
-RCShutdownLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction)
-{
-    volatile PU32 pMsg;
-    U32 off;
-    PPAB pPab;
-    int i;
-    long timeout = 0;
-
-    pPab = PCIAdapterBlock[AdapterID];
-
-    if (pPab == NULL)
-        return RC_RTN_ADPTR_NOT_REGISTERED;
-
-    off = pPab->p_atu->InQueue; /* get addresss of message */
-    
-    if (0xFFFFFFFF == off)
-         return RC_RTN_FREE_Q_EMPTY;
-
-    pPab->pCallbackFunc = CallbackFunction;
-    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
-
-    /* setup message */
-    pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0;
-    pMsg[1] = LAN_SHUTDOWN << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
-    pMsg[3] = ResourceFlags << 16;   /* resource flags */
-
-    pPab->p_atu->InQueue = off;   /* send it to the device */
-
-    if (CallbackFunction == (PFNCALLBACK)NULL)
-    {
-        /* call RCProcMsgQ() until something in pPab->pCallbackFunc
-           or until timer goes off */
-        while (pPab->pCallbackFunc == (PFNCALLBACK)NULL)
-        {
-            RCProcMsgQ(AdapterID);
-            for (i = 0; i < 100000; i++)     /* please don't hog the bus!!! */
-               ;
-            timeout++;
-            if (timeout > 10000)
-            {
-                break;
-            }
-        }
-        if (ReturnAddr != (PU32)NULL)
-           *ReturnAddr = (U32)pPab->pCallbackFunc;
-    }
-    return RC_RTN_NO_ERROR ;
-}
-
-
-/*
-** =========================================================================
-** RCSetRavlinIPandMask()
-**
-** Set the Ravlin 45/PCI cards IP address and network mask.
-**
-** IP address and mask must be in network byte order.
-** For example, IP address 1.2.3.4 and mask 255.255.255.0 would be
-** 0x04030201 and 0x00FFFFFF on a little endian machine.
-**
-** =========================================================================
-*/
-RC_RETURN
-RCSetRavlinIPandMask(U16 AdapterID, U32 ipAddr, U32 netMask)
-{
-    volatile PU32 pMsg;
-    U32 off;
-    PPAB pPab;
-
-    pPab = PCIAdapterBlock[AdapterID];
-
-    if (pPab == NULL)
-        return RC_RTN_ADPTR_NOT_REGISTERED;
-
-    off = pPab->p_atu->InQueue; /* get addresss of message */
-    
-    if (0xFFFFFFFF == off)
-         return RC_RTN_FREE_Q_EMPTY;
-    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
-
-    /* setup private message */
-    pMsg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0;
-    pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-    pMsg[2] = 0;                 /* initiator context */
-    pMsg[3] = 0x219;             /* transaction context */
-    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_IP_AND_MASK;
-    pMsg[5] = ipAddr; 
-    pMsg[6] = netMask;
-
-
-    pPab->p_atu->InQueue = off;   /* send it to the device */
-    return RC_RTN_NO_ERROR ;
-
-}
-
-/*
-** =========================================================================
-** RCGetRavlinIPandMask()
-**
-** get the IP address and MASK from the card
-** 
-** =========================================================================
-*/
-RC_RETURN
-RCGetRavlinIPandMask(U16 AdapterID, PU32 pIpAddr, PU32 pNetMask, 
-                        PFNWAITCALLBACK WaitCallback)
-{
-    unsigned i, timeout;
-    U32      off;
-    PU32     pMsg, p32;
-    PPAB     pPab;
-    PATU     p_atu;
-    
-#ifdef DEBUG
-    kprintf("RCGetRavlinIPandMask: pIpAddr is 0x%08.8ulx, *IpAddr is 0x%08.8ulx\n", pIpAddr, *pIpAddr);
-#endif /* DEBUG */
-
-    pPab = PCIAdapterBlock[AdapterID];
-    
-    if (pPab == NULL)
-        return RC_RTN_ADPTR_NOT_REGISTERED;
-    
-    p_atu = pPab->p_atu;
-    off = p_atu->InQueue;   /* get addresss of message */
- 
-   if (0xFFFFFFFF == off)
-         return RC_RTN_FREE_Q_EMPTY;
-
-    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
-    *p32 = 0xFFFFFFFF;
-
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
-
-#ifdef DEBUG
-    kprintf("RCGetRavlinIPandMask: p_atu 0x%08.8ulx, off 0x%08.8ulx, p32 0x%08.8ulx\n", p_atu, off, p32);
-#endif /* DEBUG */
-    /* setup private message */
-    pMsg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0;
-    pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
-    pMsg[2] = 0;               /* initiator context */
-    pMsg[3] = 0x218;           /* transaction context */
-    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_IP_AND_MASK;
-    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
-
-    p_atu->InQueue = off;   /* send it to the device */
-#ifdef DEBUG
-    kprintf("RCGetRavlinIPandMask: p_atu 0x%08.8ulx, off 0x%08.8ulx, p32 0x%08.8ulx\n", p_atu, off, p32);
-#endif /* DEBUG */
-
-     /* wait for the rcpci45 board to update the info */
-    timeout = 100000;
-    while (0xffffffff == *p32)
-    {
-        if (WaitCallback)
-                (*WaitCallback)();
-    
-        for (i = 0; i < 1000; i++)
-            ;
-
-        if (!timeout--)
-        {
-        #ifdef DEBUG
-            kprintf("RCGetRavlinIPandMask: Timeout\n");
-        #endif /* DEBUG */
-             return RC_RTN_MSG_REPLY_TIMEOUT;
-        }
-    }
-
-#ifdef DEBUG
-    kprintf("RCGetRavlinIPandMask: after time out\n", \
-            "p32[0] (IpAddr) 0x%08.8ulx, p32[1] (IPmask) 0x%08.8ulx\n", p32[0], p32[1]);
-#endif /* DEBUG */
-    
-    /* send IP and mask to user's space  */
-    *pIpAddr  = p32[0];
-    *pNetMask = p32[1];
-
-
-#ifdef DEBUG
-    kprintf("RCGetRavlinIPandMask: pIpAddr is 0x%08.8ulx, *IpAddr is 0x%08.8ulx\n", pIpAddr, *pIpAddr);
-#endif /* DEBUG */
-
-    return RC_RTN_NO_ERROR;
-}
-
-/* 
-** /////////////////////////////////////////////////////////////////////////
-** /////////////////////////////////////////////////////////////////////////
-**
-**                        local functions
-**
-** /////////////////////////////////////////////////////////////////////////
-** /////////////////////////////////////////////////////////////////////////
-*/
-
-/*
-** =========================================================================
-** SendAdapterOutboundQInitMsg()
-**
-** =========================================================================
-*/
-static int 
-SendAdapterOutboundQInitMsg(PPAB pPab)
-{
-    U32 msgOffset, timeout, phyOutQFrames, i;
-    volatile PU32 pMsg;
-    volatile PU32 p32;
-    
-    
-    
-    msgOffset = pPab->p_atu->InQueue;
-
-    
-    if (msgOffset == 0xFFFFFFFF)
-    {
-#ifdef DEBUG
-        kprintf("SendAdapterOutboundQInitMsg(): Inbound Free Q empty!\n");
-#endif /* DEBUG */
-        return RC_RTN_FREE_Q_EMPTY;
-    }
-    
-    
-     /* calc virual address of msg - virual already mapped to physical */    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
-
-#ifdef DEBUG
-kprintf("SendAdapterOutboundQInitMsg - pMsg = 0x%08.8ulx, InQ msgOffset = 0x%08.8ulx\n", pMsg, msgOffset);
-#endif /* DEBUG */
-
-    pMsg[0] = EIGHT_WORD_MSG_SIZE | TRL_OFFSET_6;
-    pMsg[1] = RC_CMD_OUTBOUND_INIT << 24 | HOST_TID << 12 | ADAPTER_TID;
-    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
-    pMsg[3] = 0x106; /* transaction context */
-    pMsg[4] = 4096; /* Host page frame size */
-    pMsg[5] = MSG_FRAME_SIZE  << 16 | 0x80; /* outbound msg frame size and Initcode */
-    pMsg[6] = 0xD0000004;       /* simple sgl element LE, EOB */
-     /* phys address to return status - area right after PAB */
-    pMsg[7] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
-
-     /* virual pointer to return buffer - clear first two dwords */
-    p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
-    p32[0] = 0;
-    
-     /* post to Inbound Post Q */   
-    pPab->p_atu->InQueue = msgOffset;
-    
-  /* wait for response */
-    timeout = 100000;
-    while(1)
-    {
-        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
-            ;
-            
-        if (p32[0])
-            break;
-            
-        if (!timeout--)
-        {
-#ifdef DEBUG
-            kprintf("Timeout wait for InitOutQ InPrgress status from adapter\n");
-#endif /* DEBUG */
-            return RC_RTN_NO_STATUS;
-        }
-    }
-
-    timeout = 100000;
-    while(1)
-    {
-        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
-            ;
-            
-        if (p32[0] == RC_CMD_OUTBOUND_INIT_COMPLETE)
-            break;
-
-        if (!timeout--)
-        {
-#ifdef DEBUG
-            kprintf("Timeout wait for InitOutQ Complete status from adapter\n");
-#endif /* DEBUG */
-            return RC_RTN_NO_STATUS;
-        }
-    }
-
-     /* load PCI outbound free Q with MF physical addresses */
-    phyOutQFrames = pPab->outMsgBlockPhyAddr;
-
-    for (i = 0; i < NMBR_MSG_FRAMES; i++)
-    {
-        pPab->p_atu->OutQueue = phyOutQFrames;
-        phyOutQFrames += MSG_FRAME_SIZE;
-    }
-    return RC_RTN_NO_ERROR;
-}
-
-
-/*
-** =========================================================================
-** GetAdapterStatus()
-**
-** Send StatusGet Msg, wait for results return directly to buffer.
-**
-** =========================================================================
-*/
-static int 
-GetAdapterStatus(PPAB pPab)
-{
-    U32 msgOffset, timeout;
-    PU32 pMsg;
-    volatile PU32 p32;
-    
-    
-    msgOffset = pPab->p_atu->InQueue; 
-    printk("GetAdapterStatus: msg offset = 0x%x\n", msgOffset);
-    if (msgOffset == 0xFFFFFFFF)
-    {
-#ifdef DEBUG
-        kprintf("GetAdapterStatus(): Inbound Free Q empty!\n");
-#endif /* DEBUG */
-        return RC_RTN_FREE_Q_EMPTY;
-    }
-
-     /* calc virual address of msg - virual already mapped to physical */    
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
-
-    pMsg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_0;
-    pMsg[1] = RC_CMD_STATUS_GET << 24 | HOST_TID << 12 | ADAPTER_TID;
-    pMsg[2] = 0; /* universal context */
-    pMsg[3] = 0; /* universal context */
-    pMsg[4] = 0; /* universal context */
-    pMsg[5] = 0; /* universal context */
-     /* phys address to return status - area right after PAB */
-    pMsg[6] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
-    pMsg[7] = 0;
-    pMsg[8] = 88;  /*  return 88 bytes */
-
-     /* virual pointer to return buffer - clear first two dwords */
-    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
-    p32[0] = 0;
-    p32[1] = 0;
-
-#ifdef DEBUG
-kprintf("GetAdapterStatus - pMsg:0x%08.8ulx, msgOffset:0x%08.8ulx, [1]:0x%08.8ulx, [6]:0x%08.8ulx\n",
-    pMsg, msgOffset, pMsg[1], pMsg[6]);
-#endif /* DEBUG */
-   
-     /* post to Inbound Post Q */   
-    pPab->p_atu->InQueue = msgOffset;
-    
-#ifdef DEBUG
-kprintf("Return status to p32 = 0x%08.8ulx\n", p32);
-#endif /* DEBUG */
-    
-     /* wait for response */
-    timeout = 1000000;
-    while(1)
-    {
-        int i;
-        
-        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
-            ;
-            
-        if (p32[0] && p32[1])
-            break;
-            
-        if (!timeout--)
-        {
-#ifdef DEBUG
-            kprintf("Timeout waiting for status from adapter\n");
-kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[0], p32[1], p32[2], p32[3]);
-kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[4], p32[5], p32[6], p32[7]);
-kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[8], p32[9], p32[10], p32[11]);
-#endif /* DEBUG */
-            return RC_RTN_NO_STATUS;
-        }
-    }
-            
-#ifdef DEBUG
-kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[0], p32[1], p32[2], p32[3]);
-kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[4], p32[5], p32[6], p32[7]);
-kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[8], p32[9], p32[10], p32[11]);
-#endif /* DEBUG */
-    /* get adapter state */
-    pPab->ADAPTERState = ((volatile PU8)p32)[10];
-    pPab->InboundMFrameSize  = ((volatile PU16)p32)[6];
-    
-#ifdef DEBUG
-    kprintf("adapter state 0x%02.2x InFrameSize = 0x%04.4x\n", 
-                        pPab->ADAPTERState, pPab->InboundMFrameSize);
-#endif /* DEBUG */
-    return RC_RTN_NO_ERROR;
-}
-
-
-/*
-** =========================================================================
-** SendEnableSysMsg()
-**
-**
-** =========================================================================
-*/
-static int 
-SendEnableSysMsg(PPAB pPab)
-{
-    U32 msgOffset; // timeout;
-    volatile PU32 pMsg;
-
-    msgOffset = pPab->p_atu->InQueue;
-
-    if (msgOffset == 0xFFFFFFFF)
-    {
-#ifdef DEBUG
-        kprintf("SendEnableSysMsg(): Inbound Free Q empty!\n");
-#endif /* DEBUG */
-        return RC_RTN_FREE_Q_EMPTY;
-    }
-
-     /* calc virual address of msg - virual already mapped to physical */
-    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
-
-#ifdef DEBUG
-kprintf("SendEnableSysMsg - pMsg = 0x%08.8ulx, InQ msgOffset = 0x%08.8ulx\n", pMsg, msgOffset);
-#endif /* DEBUG */
-
-    pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0;
-    pMsg[1] = RC_CMD_SYS_ENABLE << 24 | HOST_TID << 12 | ADAPTER_TID;
-    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
-    pMsg[3] = 0x110; /* transaction context */
-    pMsg[4] = 0x50657465; /*  RedCreek Private */
-
-     /* post to Inbound Post Q */
-    pPab->p_atu->InQueue = msgOffset;
-
-    return RC_RTN_NO_ERROR;
-}
-
-
-/*
-** =========================================================================
-** FillI12OMsgFromTCB()
-**
-** inputs   pMsgU32 - virual pointer (mapped to physical) of message frame
-**          pXmitCntrlBlock - pointer to caller buffer control block.
-**
-** fills in LAN SGL after Transaction Control Word or Bucket Count.
-** =========================================================================
-*/
-static int 
-FillAdapterMsgSGLFromTCB(PU32 pMsgFrame, PRCTCB pTransCtrlBlock)
-{
-    unsigned int nmbrBuffers, nmbrSeg, nmbrDwords, context, flags;
-    PU32 pTCB, pMsg;
-
- /* SGL element flags */   
-#define EOB        0x40000000
-#define LE         0x80000000
-#define SIMPLE_SGL 0x10000000
-#define BC_PRESENT 0x01000000
-
-    pTCB = (PU32)pTransCtrlBlock;
-    pMsg = pMsgFrame;
-    nmbrDwords = 0;
-
-#ifdef DEBUG
- kprintf("FillAdapterMsgSGLFromTCBX\n");
-kprintf("TCB  0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
-               pTCB[0], pTCB[1], pTCB[2], pTCB[3], pTCB[4]);
-kprintf("pTCB 0x%08.8ulx, pMsg 0x%08.8ulx\n", pTCB, pMsg);
-#endif /* DEBUG */
-    
-    nmbrBuffers = *pTCB++;
-    
-    if (!nmbrBuffers)
-    {
-        return -1;
-    }
-
-    do
-    {
-        context = *pTCB++; /* buffer tag (context) */
-        nmbrSeg = *pTCB++; /* number of segments */
-
-        if (!nmbrSeg)
-        {
-            return -1;
-        }
-        
-        flags = SIMPLE_SGL | BC_PRESENT;
-
-        if (1 == nmbrSeg)
-        {
-            flags |= EOB;
-        
-            if (1 == nmbrBuffers)
-                flags |= LE;
-        }    
-
-         /* 1st SGL buffer element has context */
-        pMsg[0] = pTCB[0] | flags ; /* send over count (segment size) */
-        pMsg[1] = context;
-        pMsg[2] = pTCB[1]; /* send buffer segment physical address */
-        nmbrDwords += 3;
-        pMsg += 3;
-        pTCB += 2;
-
-        
-        if (--nmbrSeg)
-        {
-            do
-            {
-                flags = SIMPLE_SGL;
-                
-                if (1 == nmbrSeg)
-                {
-                    flags |= EOB;
-                
-                    if (1 == nmbrBuffers)
-                        flags |= LE;
-                }    
-                
-                pMsg[0] = pTCB[0] | flags;  /* send over count */
-                pMsg[1] = pTCB[1];   /* send buffer segment physical address */
-                nmbrDwords += 2;
-                pTCB += 2;
-                pMsg += 2;
-        
-            } while (--nmbrSeg);
-        }
-        
-    } while (--nmbrBuffers);
-    
-    return nmbrDwords;
-}
-
-
-/*
-** =========================================================================
-** ProcessOutboundAdapterMsg()
-**
-** process reply message
-** * change to msg structure *
-** =========================================================================
-*/
-static void 
-ProcessOutboundAdapterMsg(PPAB pPab, U32 phyAddrMsg)
-{
-    PU8 p8Msg;
-    PU32 p32;
-  //  U16 count;
-    
-    
-    p8Msg = pPab->pLinOutMsgBlock + (phyAddrMsg - pPab->outMsgBlockPhyAddr);
-    p32 = (PU32)p8Msg;
-    
-#ifdef DEBUG
- kprintf("VXD: ProcessOutboundAdapterMsg - pPab 0x%08.8ulx, phyAdr 0x%08.8ulx, linAdr 0x%08.8ulx\n", pPab, phyAddrMsg, p8Msg);
- kprintf("msg :0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[0], p32[1], p32[2], p32[3]);
- kprintf("msg :0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[4], p32[5], p32[6], p32[7]);
-#endif /* DEBUG */
-
-    if (p32[4] >> 24 != RC_REPLY_STATUS_SUCCESS)
-    {
-#ifdef DEBUG
-        kprintf("Message reply status not success\n");
-#endif /* DEBUG */
-        return;
-    }
-    
-    switch (p8Msg[7] )  /* function code byte */
-    {
-        case RC_CMD_SYS_TAB_SET:
-            msgFlag = 1;
-#ifdef DEBUG
-        kprintf("Received RC_CMD_SYS_TAB_SET reply\n");
-#endif /* DEBUG */
-            break;
-
-        case RC_CMD_HRT_GET:
-            msgFlag = 1;
-#ifdef DEBUG
-        kprintf("Received RC_CMD_HRT_GET reply\n");
-#endif /* DEBUG */
-            break;
-        
-        case RC_CMD_LCT_NOTIFY:
-            msgFlag = 1;
-#ifdef DEBUG
-        kprintf("Received RC_CMD_LCT_NOTIFY reply\n");
-#endif /* DEBUG */
-            break;
-        
-        case RC_CMD_SYS_ENABLE:
-            msgFlag = 1;
-#ifdef DEBUG
-        kprintf("Received RC_CMD_SYS_ENABLE reply\n");
-#endif /* DEBUG */
-            break;
-
-        default:    
-#ifdef DEBUG
-        kprintf("Received UNKNOWN reply\n");
-#endif /* DEBUG */
-            break;
-    }
-}
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/rcmtl.h linux/drivers/net/rcmtl.h
--- linux.vanilla/drivers/net/rcmtl.h	Sun Dec  6 00:14:40 1998
+++ linux/drivers/net/rcmtl.h	Thu Jan  1 01:00:00 1970
@@ -1,580 +0,0 @@
-/*
-** *************************************************************************
-**
-**
-**     R C M T L . H             $Revision: 3 $
-**
-**
-**  RedCreek Message Transport Layer header file.
-**
-**  ---------------------------------------------------------------------
-**  ---     Copyright (c) 1997-1998, RedCreek Communications Inc.     ---
-**  ---                   All rights reserved.                        ---
-**  ---------------------------------------------------------------------
-**
-**  File Description:
-**
-**  Header file for host message transport layer API and data types.
-**
-**  This program is free software; you can redistribute it and/or modify
-**  it under the terms of the GNU General Public License as published by
-**  the Free Software Foundation; either version 2 of the License, or
-**  (at your option) any later version.
-
-**  This program is distributed in the hope that it will be useful,
-**  but WITHOUT ANY WARRANTY; without even the implied warranty of
-**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-**  GNU General Public License for more details.
-
-**  You should have received a copy of the GNU General Public License
-**  along with this program; if not, write to the Free Software
-**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-**
-** *************************************************************************
-*/
-
-#ifndef RCMTL_H
-#define RCMTL_H
-
-/* Linux specific includes */
-#define kprintf printk
-#ifdef RC_LINUX_MODULE     /* linux modules need non-library version of string functions */
-#include <linux/string.h>
-#else
-#include <string.h>
-#endif
-
-/* PCI/45 Configuration space values */
-#define RC_PCI45_VENDOR_ID  0x4916
-#define RC_PCI45_DEVICE_ID  0x1960
-
-
- /* RedCreek API function return values */
-#define RC_RTN_NO_ERROR             0
-#define RC_RTN_NOT_INIT             1
-#define RC_RTN_FREE_Q_EMPTY         2
-#define RC_RTN_TCB_ERROR            3
-#define RC_RTN_TRANSACTION_ERROR    4
-#define RC_RTN_ADAPTER_ALREADY_INIT 5
-#define RC_RTN_MALLOC_ERROR         6
-#define RC_RTN_ADPTR_NOT_REGISTERED 7
-#define RC_RTN_MSG_REPLY_TIMEOUT    8
-#define RC_RTN_NO_STATUS            9
-#define RC_RTN_NO_FIRM_VER         10
-#define RC_RTN_NO_LINK_SPEED       11
-
-/* Driver capability flags */
-#define WARM_REBOOT_CAPABLE      0x01
-
- /* scalar data types */
-typedef unsigned char   U8;
-typedef unsigned char*  PU8;
-typedef unsigned short  U16;
-typedef unsigned short* PU16;
-typedef unsigned long   U32;
-typedef unsigned long*  PU32;
-typedef unsigned long   BF;
-typedef int             RC_RETURN;
-
-
- /* 
- ** type PFNWAITCALLBACK
- **
- ** pointer to void function - type used for WaitCallback in some functions 
- */
-typedef void (*PFNWAITCALLBACK)(void);  /* void argument avoids compiler complaint */
-
- /*
- ** type PFNTXCALLBACK 
- **
- ** Pointer to user's transmit callback function.  This user function is
- ** called from RCProcMsgQ() when packet have been transmitted from buffers
- ** given in the RCSendPacket() function.  BufferContext is a pointer to
- ** an array of 32 bit context values.  These are the values the user assigned
- ** and passed in the TCB to the RCSendPacket() function.  PcktCount
- ** indicates the number of buffer context values in the BufferContext[] array.
- ** The User's TransmitCallbackFunction should recover (put back in free queue)
- ** the packet buffers associated with the buffer context values.
- */
-typedef void (*PFNTXCALLBACK)(U32  Status,
-                              U16  PcktCount,
-                              PU32 BufferContext,
-                              U16  AdaterID);
-
- /* 
- ** type PFNRXCALLBACK 
- **
- ** Pointer to user's receive callback function.  This user function
- ** is called from RCProcMsgQ() when packets have been received into
- ** previously posted packet buffers throught the RCPostRecvBuffers() function.
- ** The received callback function should process the Packet Descriptor Block
- ** pointed to by PacketDescBlock. See Packet Decription Block below.
- */
-typedef void (*PFNRXCALLBACK)(U32  Status,
-                              U8   PktCount,
-                              U32  BucketsRemain,
-                              PU32 PacketDescBlock,
-                              U16  AdapterID);
-
- /* 
- ** type PFNCALLBACK 
- **
- ** Pointer to user's generic callback function.  This user function
- ** can be passed to LANReset or LANShutdown and is called when the 
- ** the reset or shutdown is complete.
- ** Param1 and Param2 are invalid for LANReset and LANShutdown.
- */
-typedef void (*PFNCALLBACK)(U32  Status,
-                              U32  Param1,
-                              U32  Param2,
-                              U16  AdapterID);
-
-/*
-** Status - Transmit and Receive callback status word 
-**
-** A 32 bit Status is returned to the TX and RX callback functions.  This value
-** contains both the reply status and the detailed status as follows:
-**
-**  32    24     16            0
-**  +------+------+------------+
-**  | Reply|      |  Detailed  |
-**  |Status|   0  |   Status   |
-**  +------+------+------------+
-**
-** Reply Status and Detailed Status of zero indicates No Errors.
-*/
- /* reply message status defines */
-#define    RC_REPLY_STATUS_SUCCESS                    0x00
-#define    RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER     0x02
-#define    RC_REPLY_STATUS_TRANSACTION_ERROR          0x0A
-
-
-/* DetailedStatusCode defines */
-#define    RC_DSC_SUCCESS                         0x0000
-#define    RC_DSC_DEVICE_FAILURE                  0x0001
-#define    RC_DSC_DESTINATION_NOT_FOUND           0x0002
-#define    RC_DSC_TRANSMIT_ERROR                  0x0003
-#define    RC_DSC_TRANSMIT_ABORTED                0x0004
-#define    RC_DSC_RECEIVE_ERROR                   0x0005
-#define    RC_DSC_RECEIVE_ABORTED                 0x0006
-#define    RC_DSC_DMA_ERROR                       0x0007
-#define    RC_DSC_BAD_PACKET_DETECTED             0x0008
-#define    RC_DSC_OUT_OF_MEMORY                   0x0009
-#define    RC_DSC_BUCKET_OVERRUN                  0x000A
-#define    RC_DSC_IOP_INTERNAL_ERROR              0x000B
-#define    RC_DSC_CANCELED                        0x000C
-#define    RC_DSC_INVALID_TRANSACTION_CONTEXT     0x000D
-#define    RC_DSC_DESTINATION_ADDRESS_DETECTED    0x000E
-#define    RC_DSC_DESTINATION_ADDRESS_OMITTED     0x000F
-#define    RC_DSC_PARTIAL_PACKET_RETURNED         0x0010
-
-
-/*
-** Packet Description Block   (Received packets)
-**
-** A pointer to this block structure is returned to the ReceiveCallback 
-** function.  It contains the list of packet buffers which have either been
-** filled with a packet or returned to host due to a LANReset function. 
-** Currently there will only be one packet per receive bucket (buffer) posted. 
-**
-**   32   24               0     
-**  +-----------------------+  -\
-**  |   Buffer 1 Context    |    \
-**  +-----------------------+     \
-**  |      0xC0000000       |     / First Bucket Descriptor
-**  +-----+-----------------+    /
-**  |  0  | packet 1 length |   / 
-**  +-----------------------+  -\
-**  |   Buffer 2 Context    |    \
-**  +-----------------------+     \
-**  |      0xC0000000       |     / Second Bucket Descriptor
-**  +-----+-----------------+    /
-**  |  0  | packet 2 length |   / 
-**  +-----+-----------------+  -
-**  |         ...           |  ----- more bucket descriptors
-**  +-----------------------+  -\
-**  |   Buffer n Context    |    \
-**  +-----------------------+     \
-**  |      0xC0000000       |     / Last Bucket Descriptor
-**  +-----+-----------------+    /
-**  |  0  | packet n length |   / 
-**  +-----+-----------------+  -
-**
-** Buffer Context values are those given to adapter in the TCB on calls to
-** RCPostRecvBuffers().
-**  
-*/
-
-
-
-/*
-** Transaction Control Block (TCB) structure
-**
-** A structure like this is filled in by the user and passed by reference to 
-** RCSendPacket() and RCPostRecvBuffers() functions.  Minimum size is five
-** 32-bit words for one buffer with one segment descriptor.  
-** MAX_NMBR_POST_BUFFERS_PER_MSG defines the maximum single segment buffers
-** that can be described in a given TCB.
-**
-**   32                    0
-**  +-----------------------+
-**  |   Buffer Count        |  Number of buffers in the TCB
-**  +-----------------------+
-**  |   Buffer 1 Context    |  first buffer reference
-**  +-----------------------+
-**  |   Buffer 1 Seg Count  |  number of segments in buffer
-**  +-----------------------+
-**  |   Buffer 1 Seg Desc 1 |  first segment descriptor (size, physical address)
-**  +-----------------------+
-**  |         ...           |  more segment descriptors (size, physical address)
-**  +-----------------------+
-**  |   Buffer 1 Seg Desc n |  last segment descriptor (size, physical address)
-**  +-----------------------+
-**  |   Buffer 2 Context    |  second buffer reference
-**  +-----------------------+
-**  |   Buffer 2 Seg Count  |  number of segments in buffer
-**  +-----------------------+
-**  |   Buffer 2 Seg Desc 1 |  segment descriptor (size, physical address)
-**  +-----------------------+
-**  |         ...           |  more segment descriptors (size, physical address)
-**  +-----------------------+
-**  |   Buffer 2 Seg Desc n |
-**  +-----------------------+
-**  |         ...           |  more buffer descriptor blocks ...
-**  +-----------------------+
-**  |   Buffer n Context    |
-**  +-----------------------+
-**  |   Buffer n Seg Count  |
-**  +-----------------------+
-**  |   Buffer n Seg Desc 1 |
-**  +-----------------------+
-**  |         ...           |
-**  +-----------------------+
-**  |   Buffer n Seg Desc n |
-**  +-----------------------+
-**
-**
-** A TCB for one contigous packet buffer would look like the following:
-**
-**   32                    0
-**  +-----------------------+
-**  |         1             |  one buffer in the TCB
-**  +-----------------------+
-**  |  <user's Context>     |  user's buffer reference
-**  +-----------------------+
-**  |         1             |  one segment buffer
-**  +-----------------------+                            _
-**  |    <buffer size>      |  size                       \ 
-**  +-----------------------+                              \ segment descriptor
-**  |  <physical address>   |  physical address of buffer  /
-**  +-----------------------+                            _/
-**
-*/
-
- /* Buffer Segment Descriptor */
-typedef struct
-{
-    U32 size;
-    U32 phyAddress;
-}
- BSD, *PBSD;
- 
-typedef PU32 PRCTCB;
-/*
-** -------------------------------------------------------------------------
-** Exported functions comprising the API to the message transport layer
-** -------------------------------------------------------------------------
-*/
-
-
- /*
- ** InitRCApiMsgLayer()
- ** 
- ** Called once prior to using the API message transport layer.  User 
- ** provides both the physical and virual address of a locked page buffer 
- ** that is used as a private buffer for the RedCreek API message
- ** transport layer.  This buffer must be a contigous memory block of a 
- ** minimum of 16K bytes and long word aligned.  The user also must provide
- ** the base address of the RedCreek PCI adapter assigned by BIOS or operating
- ** system.  The user provided value AdapterID is a zero based index of the
- ** Ravlin 45/PCI adapter.  This interface number is used in all subsequent API
- ** calls to identify which adpapter for which the function is intended.  
- ** Up to sixteen interfaces are supported with this API.
- **
- ** Inputs:  AdapterID - interface number from 0 to 15
- **          pciBaseAddr - virual base address of PCI (set by BIOS)
- **          p_msgbuf - virual address to private message block (min. 16K)
- **          p_phymsgbuf - physical address of private message block
- **          TransmitCallbackFunction - address of user's TX callback function
- **          ReceiveCallbackFunction  - address of user's RX callback function
- **
- */
-RC_RETURN InitRCApiMsgLayer(U16 AdapterID, U32 pciBaseAddr, 
-                            PU8 p_msgbuf,  PU8 p_phymsgbuf,
-                            PFNTXCALLBACK TransmitCallbackFunction,
-                            PFNRXCALLBACK ReceiveCallbackFunction,
-                            PFNCALLBACK   RebootCallbackFunction);
-
- /*
- ** RCSetRavlinIPandMask()
- **
- ** Set the Ravlin 45/PCI cards IP address and network mask.
- **
- ** IP address and mask must be in network byte order.
- ** For example, IP address 1.2.3.4 and mask 255.255.255.0 would be
- ** 0x04030201 and 0x00FFFFFF on a little endian machine.
- **
- */
-RC_RETURN RCSetRavlinIPandMask(U16 AdapterID, U32 ipAddr, U32 netMask);
-
-
-/*
-** =========================================================================
-** RCGetRavlinIPandMask()
-**
-** get the IP address and MASK from the card
-** 
-** =========================================================================
-*/
-RC_RETURN
-RCGetRavlinIPandMask(U16 AdapterID, PU32 pIpAddr, PU32 pNetMask, 
-                        PFNWAITCALLBACK WaitCallback);
-
- /* 
- ** RCProcMsgQ()
- ** 
- ** Called from user's polling loop or Interrupt Service Routine for a PCI 
- ** interrupt from the RedCreek PCI adapter.  User responsible for determining
- ** and hooking the PCI interrupt. This function will call the registered
- ** callback functions, TransmitCallbackFunction or ReceiveCallbackFunction,
- ** if a TX or RX transaction has completed.
- */
-void RCProcMsgQ(U16 AdapterID);
-
-
- /*
- ** Disable and Enable Adapter interrupts.  Adapter interrupts are enabled at 
- ** Init time but can be disabled and re-enabled through these two function calls.
- ** Packets will still be put into any posted recieved buffers and packets will
- ** be sent through RCSendPacket() functions.  Disabling Adapter interrupts
- ** will prevent hardware interrupt to host even though the outbound msg
- ** queue is not emtpy.
- */
-RC_RETURN RCEnableAdapterInterrupts(U16 adapterID);
-RC_RETURN RCDisableAdapterInterrupts(U16 AdapterID);
-
-
- /* 
- ** RCPostRecvBuffers()
- ** 
- ** Post user's page locked buffers for use by the PCI adapter to
- ** return ethernet packets received from the LAN.  Transaction Control Block,
- ** provided by user, contains buffer descriptor(s) which includes a buffer
- ** context number along with buffer size and physical address.  See TCB above.
- ** The buffer context and actual packet length are returned to the 
- ** ReceiveCallbackFunction when packets have been received.  Buffers posted
- ** to the RedCreek adapter are considered owned by the adapter until the
- ** context is return to user through the ReceiveCallbackFunction.
- */
-RC_RETURN RCPostRecvBuffers(U16 AdapterID, PRCTCB pTransactionCtrlBlock);
-#define MAX_NMBR_POST_BUFFERS_PER_MSG 32
-
- /*
- ** RCSendPacket()
- ** 
- ** Send user's ethernet packet from a locked page buffer.  
- ** Packet must have full MAC header, however without a CRC.  
- ** Initiator context is a user provided value that is returned 
- ** to the TransmitCallbackFunction when packet buffer is free.
- ** Transmit buffer are considered owned by the adapter until context's
- ** returned to user through the TransmitCallbackFunction.
- */
-RC_RETURN RCSendPacket(U16 AdapterID, 
-                          U32 context, 
-                          PRCTCB pTransactionCtrlBlock);
-
-
- /* Ethernet Link Statistics structure */
-typedef struct tag_RC_link_stats
-{
-    U32 TX_good;      /* good transmit frames */
-    U32 TX_maxcol;    /* frames not TX due to MAX collisions */
-    U32 TX_latecol;   /* frames not TX due to late collisions */
-    U32 TX_urun;      /* frames not TX due to DMA underrun */
-    U32 TX_crs;       /* frames TX with lost carrier sense */
-    U32 TX_def;       /* frames deferred due to activity on link */
-    U32 TX_singlecol; /* frames TX with one and only on collision */
-    U32 TX_multcol;   /* frames TX with more than one collision */
-    U32 TX_totcol;    /* total collisions detected during TX */
-    U32 Rcv_good;     /* good frames received */
-    U32 Rcv_CRCerr;   /* frames RX and discarded with CRC errors */
-    U32 Rcv_alignerr; /* frames RX with alignment and CRC errors */
-    U32 Rcv_reserr;   /* good frames discarded due to no RX buffer */
-    U32 Rcv_orun;     /* RX frames lost due to FIFO overrun */
-    U32 Rcv_cdt;      /* RX frames with collision during RX */
-    U32 Rcv_runt;     /* RX frames shorter than 64 bytes */
-}
- RCLINKSTATS, *P_RCLINKSTATS;
-
- /*
- ** RCGetLinkStatistics()
- **
- ** Returns link statistics in user's structure at address StatsReturnAddr
- ** If given, not NULL, the function WaitCallback is called during the wait
- ** loop while waiting for the adapter to respond.
- */
-RC_RETURN RCGetLinkStatistics(U16 AdapterID,
-                              P_RCLINKSTATS StatsReturnAddr,
-                              PFNWAITCALLBACK WaitCallback);
-
- /*
- ** RCGetLinkStatus()
- **
- ** Return link status, up or down, to user's location addressed by ReturnAddr.
- ** If given, not NULL, the function WaitCallback is called during the wait
- ** loop while waiting for the adapter to respond.
- */
-RC_RETURN RCGetLinkStatus(U16 AdapterID, 
-                          PU32 pReturnStatus,
-                          PFNWAITCALLBACK WaitCallback);
-                               
- /* Link Status defines - value returned in pReturnStatus */
-#define LAN_LINK_STATUS_DOWN     0
-#define LAN_LINK_STATUS_UP       1
-
- /*
- ** RCGetMAC()
- **
- ** Get the current MAC address assigned to user.  RedCreek Ravlin 45/PCI 
- ** has two MAC addresses.  One which is private to the PCI Card, and 
- ** another MAC which is given to the user as its link layer MAC address. The
- ** adapter runs in promiscous mode because of the dual address requirement.
- ** The MAC address is returned to the unsigned char array pointer to by mac.
- */
-RC_RETURN RCGetMAC(U16 AdapterID, PU8 mac, PFNWAITCALLBACK WaitCallback);
-
- /*
- ** RCSetMAC()
- **
- ** Set a new user port MAC address.  This address will be returned on
- ** subsequent RCGetMAC() calls.
- */
-RC_RETURN RCSetMAC(U16 AdapterID, PU8 mac);
-
- /*
- ** RCSetLinkSpeed()
- **
- ** set adapter's link speed based on given input code.
- */
-RC_RETURN RCSetLinkSpeed(U16 AdapterID, U16 LinkSpeedCode);
- /* Set link speed codes */
-#define LNK_SPD_AUTO_NEG_NWAY   0
-#define LNK_SPD_100MB_FULL      1
-#define LNK_SPD_100MB_HALF      2
-#define LNK_SPD_10MB_FULL       3
-#define LNK_SPD_10MB_HALF       4
-
-
-
-
- /*
- ** RCGetLinkSpeed()
- **
- ** Return link speed code.
- */
- /* Return link speed codes */
-#define LNK_SPD_UNKNOWN         0
-#define LNK_SPD_100MB_FULL      1
-#define LNK_SPD_100MB_HALF      2
-#define LNK_SPD_10MB_FULL       3
-#define LNK_SPD_10MB_HALF       4
-
-RC_RETURN
-RCGetLinkSpeed(U16 AdapterID, PU32 pLinkSpeedCode, PFNWAITCALLBACK WaitCallback);
-
-/*
-** =========================================================================
-** RCReportDriverCapability(U16 AdapterID, U32 capability)
-**
-** Currently defined bits:
-** WARM_REBOOT_CAPABLE   0x01
-**
-** =========================================================================
-*/
-RC_RETURN
-RCReportDriverCapability(U16 AdapterID, U32 capability);
-
-/*
-** RCGetFirmwareVer()
-**
-** Return firmware version in the form "SoftwareVersion : Bt BootVersion"
-**
-** WARNING: user's space pointed to by pFirmString should be at least 60 bytes.
-*/
-RC_RETURN
-RCGetFirmwareVer(U16 AdapterID, PU8 pFirmString, PFNWAITCALLBACK WaitCallback);
-
-/*
-** ----------------------------------------------
-** LAN adapter Reset and Shutdown functions
-** ----------------------------------------------
-*/
- /* resource flag bit assignments for RCResetLANCard() & RCShutdownLANCard() */
-#define RC_RESOURCE_RETURN_POSTED_RX_BUCKETS  0x0001 
-#define RC_RESOURCE_RETURN_PEND_TX_BUFFERS    0x0002
-
- /*
- ** RCResetLANCard()
- **
- ** Reset LAN card operation.  Causes a software reset of the ethernet
- ** controller and restarts the command and receive units. Depending on 
- ** the ResourceFlags given, the buffers are either returned to the
- ** host with reply status of RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER and
- ** detailed status of RC_DSC_CANCELED (new receive buffers must be
- ** posted after issuing this) OR the buffers are kept and reused by
- ** the ethernet controller. If CallbackFunction is not NULL, the function
- ** will be called when the reset is complete.  If the CallbackFunction is
- ** NULL,a 1 will be put into the ReturnAddr after waiting for the reset 
- ** to complete (please disable adapter interrupts during this method).
- ** Any outstanding transmit or receive buffers that are complete will be
- ** returned via the normal reply messages before the requested resource
- ** buffers are returned.
- ** A call to RCPostRecvBuffers() is needed to return the ethernet to full
- ** operation if the receive buffers were returned during LANReset.
- ** Note: The IOP status is not affected by a LAN reset.
- */
-RC_RETURN RCResetLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction);
-
-
- /*
- ** RCShutdownLANCard()
- **
- ** Shutdown LAN card operation and put into an idle (suspended) state.
- ** The LAN card is restarted with RCResetLANCard() function.
- ** Depending on the ResourceFlags given, the buffers are either returned 
- ** to the host with reply status of RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER 
- ** and detailed status of RC_DSC_CANCELED (new receive buffers must be
- ** posted after issuing this) OR the buffers are kept and reused by
- ** the ethernet controller. If CallbackFunction is not NULL, the function
- ** will be called when the reset is complete.  If the CallbackFunction is
- ** NULL,a 1 will be put into the ReturnAddr after waiting for the reset 
- ** to complete (please disable adapter interrupts during this method).
- ** Any outstanding transmit or receive buffers that are complete will be
- ** returned via the normal reply messages before the requested resource
- ** buffers are returned.
- ** Note: The IOP status is not affected by a LAN shutdown.
- */                                      
-RC_RETURN 
-RCShutdownLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction);
-
- /*
- ** RCResetAdapter();
- **     Initializes ADAPTERState to ADAPTER_STATE_RESET.
- **     Stops access to outbound message Q.
- **     Discards any outstanding transmit or posted receive buffers.
- **     Clears outbound message Q. 
- */
-RC_RETURN 
-RCResetAdapter(U16 AdapterID);
-
-#endif /* RCMTL_H */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/rcpci45.c linux/drivers/net/rcpci45.c
--- linux.vanilla/drivers/net/rcpci45.c	Sun Dec  6 00:14:40 1998
+++ linux/drivers/net/rcpci45.c	Fri Jan 29 14:03:29 1999
@@ -4,7 +4,7 @@
 **
 **
 **  ---------------------------------------------------------------------
-**  ---     Copyright (c) 1998, RedCreek Communications Inc.          ---
+**  ---     Copyright (c) 1998, 1999, RedCreek Communications Inc.    ---
 **  ---                   All rights reserved.                        ---
 **  ---------------------------------------------------------------------
 **
@@ -12,14 +12,13 @@
 **
 ** Known Problems
 ** 
-** Billions and Billions...  
-**
-** ... apparently added by Brian.  Pete knows of no bugs.
+** None known at this time.
 **
 **  TODO:
 **      -Get rid of the wait loops in the API and replace them
 **       with system independent delays ...something like
-**       "delayms(2)".
+**       "delayms(2)".  However, under normal circumstances, the 
+**       delays are very short so they're not a problem.
 **
 **  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
@@ -35,20 +34,22 @@
 **  along with this program; if not, write to the Free Software
 **  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 **
+**   
+**  Pete Popov, January 11,99: Fixed a couple of 2.1.x problems 
+**  (virt_to_bus() not called), tested it under 2.2pre5, and added a 
+**  #define to enable the use of the same file for both, the 2.0.x kernels 
+**  as well as the 2.1.x.
+**
+**  Ported to 2.1.x by Alan Cox 1998/12/9. 
+**
 ***************************************************************************/
 
-#define __NO_VERSION__ /* don't define kernel_verion in module.h */
-
 static char *version =
-"RedCreek Communications PCI linux driver version 1.32 Beta\n";
+"RedCreek Communications PCI linux driver version 2.00\n";
 
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/version.h>
-
-static char kernel_version [] = UTS_RELEASE;
-
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/string.h>
@@ -65,25 +66,34 @@
 #include <asm/bitops.h>
 #include <asm/io.h>
 
+#if LINUX_VERSION_CODE >= 0x020100
+#define LINUX_2_1
+#endif
+
+#ifdef LINUX_2_1
+#include <asm/uaccess.h>
+#endif
+
 #include <linux/if_ether.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 
+
 #define RC_LINUX_MODULE
-#include "rcmtl.h"
+#include "rclanmtl.h"
 #include "rcif.h"
 
 #define RUN_AT(x) (jiffies + (x))
-#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
-
-#define FREE_IRQ(irqnum, dev) free_irq(irqnum)
-#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n)
-#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs)
 
 #define NEW_MULTICAST
 #include <linux/delay.h>
 
+#ifndef LINUX_2_1
+#define ioremap vremap
+#define iounmap vfree
+#endif
+
 /* PCI/45 Configuration space values */
 #define RC_PCI45_VENDOR_ID  0x4916
 #define RC_PCI45_DEVICE_ID  0x1960
@@ -111,25 +121,25 @@
 typedef struct
 {
 
-     /* 
-      *    pointer to the device structure which is part
-      * of the interface to the Linux kernel.
-      */
-      struct device *dev;            
+    /* 
+     *    pointer to the device structure which is part
+     * of the interface to the Linux kernel.
+     */
+    struct device *dev;            
      
-     char devname[8];                /* "ethN" string */
-     U8     id;                        /* the AdapterID */
-     U32    pci_addr;               /* the pci address of the adapter */
-     U32    bus;
-     U32    function;
-     struct timer_list timer;        /*  timer */
-     struct enet_statistics  stats; /* the statistics structure */
-     struct device *next;            /* points to the next RC adapter */
-     unsigned long numOutRcvBuffers;/* number of outstanding receive buffers*/
-     unsigned char shutdown;
-     unsigned char reboot;
-     unsigned char nexus;
-     PU8    PLanApiPA;             /* Pointer to Lan Api Private Area */
+    char devname[8];                /* "ethN" string */
+    U8     id;                        /* the AdapterID */
+    U32    pci_addr;               /* the pci address of the adapter */
+    U32    bus;
+    U32    function;
+    struct timer_list timer;        /*  timer */
+    struct enet_statistics  stats; /* the statistics structure */
+    struct device *next;            /* points to the next RC adapter */
+    unsigned long numOutRcvBuffers;/* number of outstanding receive buffers*/
+    unsigned char shutdown;
+    unsigned char reboot;
+    unsigned char nexus;
+    PU8    PLanApiPA;             /* Pointer to Lan Api Private Area */
 
 }
 DPA, *PDPA;
@@ -138,10 +148,10 @@
 
 static PDPA  PCIAdapters[MAX_ADAPTERS] = 
 {
-     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
 
@@ -173,258 +183,258 @@
 int rcpci_probe(struct netdevice *dev)
 #endif
 {
-     int cards_found;
+    int cards_found;
 
-     printk(version);
+    printk(version);
 
-     root_RCdev = NULL;
-     cards_found = RCscan();
-#ifdef MODULE     
-     return cards_found ? 0 : -ENODEV;
+    root_RCdev = NULL;
+    cards_found = RCscan();
+#ifdef MODULE
+    return cards_found ? 0 : -ENODEV;
 #else
-     return -1;
-#endif          
+    return -1;
+#endif
 }
 
-static int RCscan(void)
+static int RCscan()
 {
-     int cards_found = 0;
-     struct device *dev = 0;
+    int cards_found = 0;
+    struct device *dev = 0;
 
-     if (pcibios_present()) 
-     {
-          static int pci_index = 0;
-          unsigned char pci_bus, pci_device_fn;
-          int scan_status;
-          int board_index = 0;
-
-          for (;pci_index < 0xff; pci_index++) 
-          {
-               unsigned char pci_irq_line;
-               unsigned short pci_command, vendor, device, class;
-               unsigned int pci_ioaddr;
-
-
-               scan_status =  
-                    (pcibios_find_device (RC_PCI45_VENDOR_ID, 
-                                          RC_PCI45_DEVICE_ID, 
-                                          pci_index, 
-                                          &pci_bus, 
-                                          &pci_device_fn));
-#ifdef RCDEBUG
-               printk("rc scan_status = 0x%X\n", scan_status);
-#endif
-               if (scan_status != PCIBIOS_SUCCESSFUL)
-                    break;
-               pcibios_read_config_word(pci_bus, 
-                                        pci_device_fn, 
-                                        PCI_VENDOR_ID, &vendor);
-               pcibios_read_config_word(pci_bus, 
-                                        pci_device_fn,
-                                        PCI_DEVICE_ID, &device);
-               pcibios_read_config_byte(pci_bus, 
-                                        pci_device_fn,
-                                        PCI_INTERRUPT_LINE, &pci_irq_line);
-               pcibios_read_config_dword(pci_bus, 
-                                         pci_device_fn,
-                                         PCI_BASE_ADDRESS_0, &pci_ioaddr);
-               pcibios_read_config_word(pci_bus, 
-                                        pci_device_fn,
-                                        PCI_CLASS_DEVICE, &class);
-
-               pci_ioaddr &= ~0xf;
-
-#ifdef RCDEBUG
-               printk("rc: Found RedCreek PCI adapter\n");
-               printk("rc: pci class = 0x%x  0x%x \n", class, class>>8);
-               printk("rc: pci_bus = %d,  pci_device_fn = %d\n", pci_bus, pci_device_fn);
-               printk("rc: pci_irq_line = 0x%x \n", pci_irq_line);
-               printk("rc: pci_ioaddr = 0x%x\n", pci_ioaddr);
-#endif
-
-#if 0
-               if (check_region(pci_ioaddr, 32768))
-               {
-                    printk("rc: check_region failed\n");
-                    continue;
-               }
-               else
-               {
-                    printk("rc: check_region passed\n");
-               }
+    if (pcibios_present()) 
+    {
+        static int pci_index = 0;
+        unsigned char pci_bus, pci_device_fn;
+        int scan_status;
+        int board_index = 0;
+
+        for (;pci_index < 0xff; pci_index++) 
+        {
+            unsigned char pci_irq_line;
+            unsigned short pci_command, vendor, device, class;
+            unsigned int pci_ioaddr;
+
+
+            scan_status =  
+                (pcibios_find_device (RC_PCI45_VENDOR_ID, 
+                                      RC_PCI45_DEVICE_ID, 
+                                      pci_index, 
+                                      &pci_bus, 
+                                      &pci_device_fn));
+#ifdef RCDEBUG
+            printk("rc scan_status = 0x%X\n", scan_status);
+#endif
+            if (scan_status != PCIBIOS_SUCCESSFUL)
+                break;
+            pcibios_read_config_word(pci_bus, 
+                                     pci_device_fn, 
+                                     PCI_VENDOR_ID, &vendor);
+            pcibios_read_config_word(pci_bus, 
+                                     pci_device_fn,
+                                     PCI_DEVICE_ID, &device);
+            pcibios_read_config_byte(pci_bus, 
+                                     pci_device_fn,
+                                     PCI_INTERRUPT_LINE, &pci_irq_line);
+            pcibios_read_config_dword(pci_bus, 
+                                      pci_device_fn,
+                                      PCI_BASE_ADDRESS_0, &pci_ioaddr);
+            pcibios_read_config_word(pci_bus, 
+                                     pci_device_fn,
+                                     PCI_CLASS_DEVICE, &class);
+
+            pci_ioaddr &= ~0xf;
+
+#ifdef RCDEBUG
+            printk("rc: Found RedCreek PCI adapter\n");
+            printk("rc: pci class = 0x%x  0x%x \n", class, class>>8);
+            printk("rc: pci_bus = %d,  pci_device_fn = %d\n", pci_bus, pci_device_fn);
+            printk("rc: pci_irq_line = 0x%x \n", pci_irq_line);
+            printk("rc: pci_ioaddr = 0x%x\n", pci_ioaddr);
 #endif
-               
-                /*
-                 * Get and check the bus-master and latency values.
-                 * Some PCI BIOSes fail to set the master-enable bit.
-                 */
 
-                pcibios_read_config_word(pci_bus, 
-                                         pci_device_fn,
-                                         PCI_COMMAND, 
-                                         &pci_command);
-                if ( ! (pci_command & PCI_COMMAND_MASTER)) {
-                     printk("rc: PCI Master Bit has not been set!\n");
+#if 1
+            if (check_region(pci_ioaddr, 2*32768))
+            {
+                printk("rc: check_region failed\n");
+                continue;
+            }
+            else
+            {
+                printk("rc: check_region passed\n");
+            }
+#endif
+               
+            /*
+             * Get and check the bus-master and latency values.
+             * Some PCI BIOSes fail to set the master-enable bit.
+             */
+
+            pcibios_read_config_word(pci_bus, 
+                                     pci_device_fn,
+                                     PCI_COMMAND, 
+                                     &pci_command);
+            if ( ! (pci_command & PCI_COMMAND_MASTER)) {
+                printk("rc: PCI Master Bit has not been set!\n");
                             
-                     pci_command |= PCI_COMMAND_MASTER;
-                     pcibios_write_config_word(pci_bus, 
-                                               pci_device_fn,
-                                               PCI_COMMAND, 
-                                               pci_command);
-                }
-                if ( ! (pci_command & PCI_COMMAND_MEMORY)) {
+                pci_command |= PCI_COMMAND_MASTER;
+                pcibios_write_config_word(pci_bus, 
+                                          pci_device_fn,
+                                          PCI_COMMAND, 
+                                          pci_command);
+            }
+            if ( ! (pci_command & PCI_COMMAND_MEMORY)) {
                 /*
                  * If the BIOS did not set the memory enable bit, what else
                  * did it not initialize?  Skip this adapter.
                  */
-                     printk("rc: Adapter %d, PCI Memory Bit has not been set!\n",
-                         cards_found);
-                     printk("rc: Bios problem? \n");
-                     continue;
-                }
+                printk("rc: Adapter %d, PCI Memory Bit has not been set!\n",
+                       cards_found);
+                printk("rc: Bios problem? \n");
+                continue;
+            }
                     
-               dev = RCfound_device(dev, pci_ioaddr, pci_irq_line,
-                                    pci_bus, pci_device_fn,
-                                    board_index++, cards_found);
-
-               if (dev) {
-                    dev = 0;
-                    cards_found++;
-               }
-          }
-     }
-     printk("rc: found %d cards \n", cards_found);
-     return cards_found;
+            dev = RCfound_device(dev, pci_ioaddr, pci_irq_line,
+                                 pci_bus, pci_device_fn,
+                                 board_index++, cards_found);
+
+            if (dev) {
+                dev = 0;
+                cards_found++;
+            }
+        }
+    }
+    printk("rc: found %d cards \n", cards_found);
+    return cards_found;
 }
 
 static struct device *
 RCfound_device(struct device *dev, int memaddr, int irq, 
                int bus, int function, int product_index, int card_idx)
 {
-     int dev_size = 32768;        
-     unsigned long *vaddr=0;
-     PDPA pDpa;
-     int init_status;
-
-     /* 
-      * Allocate and fill new device structure. 
-      * We need enough for struct device plus DPA plus the LAN API private
-      * area, which requires a minimum of 16KB.  The top of the allocated
-      * area will be assigned to struct device; the next chunk will be
-      * assigned to DPA; and finally, the rest will be assigned to the
-      * the LAN API layer.
-      */
-     dev = (struct device *) kmalloc(dev_size, GFP_DMA | GFP_KERNEL |GFP_ATOMIC);
-     memset(dev, 0, dev_size);
+    int dev_size = 32768;        
+    unsigned long *vaddr=0;
+    PDPA pDpa;
+    int init_status;
+
+    /* 
+     * Allocate and fill new device structure. 
+     * We need enough for struct device plus DPA plus the LAN API private
+     * area, which requires a minimum of 16KB.  The top of the allocated
+     * area will be assigned to struct device; the next chunk will be
+     * assigned to DPA; and finally, the rest will be assigned to the
+     * the LAN API layer.
+     */
+    dev = (struct device *) kmalloc(dev_size, GFP_DMA | GFP_KERNEL |GFP_ATOMIC);
+    memset(dev, 0, dev_size);
 #ifdef RCDEBUG
-     printk("rc: dev = 0x%08X\n", (uint)dev);
+    printk("rc: dev = 0x%08X\n", (uint)dev);
 #endif 
 
-     /*
-      * dev->priv will point to the start of DPA.
-      */
-     dev->priv = (void *)(((long)dev + sizeof(struct device) + 15) & ~15);
-     pDpa = dev->priv;
-     dev->name = pDpa->devname;
-
-     pDpa->dev = dev;            /* this is just for easy reference */
-     pDpa->function = function;
-     pDpa->bus = bus;
-     pDpa->id = card_idx;        /* the device number */
-     pDpa->pci_addr = memaddr;
-     PCIAdapters[card_idx] = pDpa;
-#ifdef RCDEBUG
-     printk("rc: pDpa = 0x%x, id = %d \n", (uint)pDpa, (uint)pDpa->id);
-#endif
-
-     /*
-      * Save the starting address of the LAN API private area.  We'll
-      * pass that to InitRCApiMsgLayer().
-      */
-     pDpa->PLanApiPA = (void *)(((long)pDpa + sizeof(DPA) + 0xff) & ~0xff);
+    /*
+     * dev->priv will point to the start of DPA.
+     */
+    dev->priv = (void *)(((long)dev + sizeof(struct device) + 15) & ~15);
+    pDpa = dev->priv;
+    dev->name = pDpa->devname;
+
+    pDpa->dev = dev;            /* this is just for easy reference */
+    pDpa->function = function;
+    pDpa->bus = bus;
+    pDpa->id = card_idx;        /* the device number */
+    pDpa->pci_addr = memaddr;
+    PCIAdapters[card_idx] = pDpa;
+#ifdef RCDEBUG
+    printk("rc: pDpa = 0x%x, id = %d \n", (uint)pDpa, (uint)pDpa->id);
+#endif
+
+    /*
+     * Save the starting address of the LAN API private area.  We'll
+     * pass that to RCInitI2OMsgLayer().
+     */
+    pDpa->PLanApiPA = (void *)(((long)pDpa + sizeof(DPA) + 0xff) & ~0xff);
 #ifdef RCDEBUG
-     printk("rc: pDpa->PLanApiPA = 0x%x\n", (uint)pDpa->PLanApiPA);
+    printk("rc: pDpa->PLanApiPA = 0x%x\n", (uint)pDpa->PLanApiPA);
 #endif
     
-     /* The adapter is accessable through memory-access read/write, not
-      * I/O read/write.  Thus, we need to map it to some virtual address
-      * area in order to access the registers are normal memory.
-      */
-     vaddr = (ulong *) vremap (memaddr, 32768);
+    /* The adapter is accessable through memory-access read/write, not
+     * I/O read/write.  Thus, we need to map it to some virtual address
+     * area in order to access the registers are normal memory.
+     */
+    vaddr = (ulong *) ioremap (memaddr, 2*32768);
+#ifdef RCDEBUG
+    printk("rc: RCfound_device: 0x%x, priv = 0x%x, vaddr = 0x%x\n", 
+           (uint)dev, (uint)dev->priv, (uint)vaddr);
+#endif
+    dev->base_addr = (unsigned long)vaddr;
+    dev->irq = irq;
+    dev->interrupt = 0;
+
+    /*
+     * Request a shared interrupt line.
+     */
+    if ( request_irq(dev->irq, (void *)RCinterrupt,
+                     SA_INTERRUPT|SA_SHIRQ, "RedCreek VPN Adapter", dev) )
+    {
+        printk( "RC PCI 45: %s: unable to get IRQ %d\n", (PU8)dev->name, (uint)dev->irq );
+        iounmap(vaddr);
+        kfree(dev);
+        return 0;
+    }
+
+    init_status = RCInitI2OMsgLayer(pDpa->id, dev->base_addr, 
+                                    pDpa->PLanApiPA, (PU8)virt_to_bus((void *)pDpa->PLanApiPA),
+                                    (PFNTXCALLBACK)RCxmit_callback,
+                                    (PFNRXCALLBACK)RCrecv_callback,
+                                    (PFNCALLBACK)RCreboot_callback);
 #ifdef RCDEBUG
-     printk("rc: RCfound_device: 0x%x, priv = 0x%x, vaddr = 0x%x\n", 
-            (uint)dev, (uint)dev->priv, (uint)vaddr);
+    printk("rc: I2O msg initted: status = 0x%x\n", init_status);
 #endif
-     dev->base_addr = (unsigned long)vaddr;
-     dev->irq = irq;
-     dev->interrupt = 0;
+    if (init_status)
+    {
+        printk("rc: Unable to initialize msg layer\n");
+        free_irq(dev->irq, dev);
+        iounmap(vaddr);
+        kfree(dev);
+        return 0;
+    }
+    if (RCGetMAC(pDpa->id, dev->dev_addr, NULL))
+    {
+        printk("rc: Unable to get adapter MAC\n");
+        free_irq(dev->irq, dev);
+        iounmap(vaddr);
+        kfree(dev);
+        return 0;
+    }
 
-     /*
-      * Request a shared interrupt line.
-      */
-     if ( request_irq(dev->irq, (void *)RCinterrupt,
-                      SA_INTERRUPT|SA_SHIRQ, "RedCreek VPN Adapter", dev) )
-     {
-          printk( "RC PCI 45: %s: unable to get IRQ %d\n", (PU8)dev->name, (uint)dev->irq );
-          vfree(vaddr);
-          kfree(dev);
-           return 0;
-     }
-
-     init_status = InitRCApiMsgLayer(pDpa->id, dev->base_addr, 
-                                     pDpa->PLanApiPA, pDpa->PLanApiPA,
-                                     (PFNTXCALLBACK)RCxmit_callback,
-                                     (PFNRXCALLBACK)RCrecv_callback,
-                                     (PFNCALLBACK)RCreboot_callback);
-#ifdef RCDEBUG
-     printk("rc: msg initted: status = 0x%x\n", init_status);
-#endif
-     if (init_status)
-     {
-          printk("rc: Unable to initialize msg layer\n");
-          free_irq(dev->irq, dev);
-          vfree(vaddr);
-          kfree(dev);
-          return 0;
-     }
-     if (RCGetMAC(pDpa->id, dev->dev_addr, NULL))
-     {
-          printk("rc: Unable to get adapter MAC\n");
-          free_irq(dev->irq, dev);
-          vfree(vaddr);
-          kfree(dev);
-          return 0;
-     }
-
-     DriverControlWord |= WARM_REBOOT_CAPABLE;
-     RCReportDriverCapability(pDpa->id, DriverControlWord);
-
-     dev->init = RCprobe1;
-     ether_setup(dev);            /* linux kernel interface */
-
-     pDpa->next = root_RCdev;
-     root_RCdev = dev;
-
-     if (register_netdev(dev) != 0) /* linux kernel interface */
-     {
-          printk("rc: unable to register device \n");
-          free_irq(dev->irq, dev);
-          vfree(vaddr);
-          kfree(dev);
-          return 0;
-     }
-     return dev;
+    DriverControlWord |= WARM_REBOOT_CAPABLE;
+    RCReportDriverCapability(pDpa->id, DriverControlWord);
+
+    dev->init = RCprobe1;
+    ether_setup(dev);            /* linux kernel interface */
+
+    pDpa->next = root_RCdev;
+    root_RCdev = dev;
+
+    if (register_netdev(dev) != 0) /* linux kernel interface */
+    {
+        printk("rc: unable to register device \n");
+        free_irq(dev->irq, dev);
+        iounmap(vaddr);
+        kfree(dev);
+        return 0;
+    }
+    return dev;
 }
 
 static int RCprobe1(struct device *dev)
 {
-     dev->open = RCopen;
-     dev->hard_start_xmit = RC_xmit_packet;
-     dev->stop = RCclose;
-     dev->get_stats = RCget_stats;
-     dev->do_ioctl = RCioctl;
-     dev->set_config = RCconfig;
-     return 0;
+    dev->open = RCopen;
+    dev->hard_start_xmit = RC_xmit_packet;
+    dev->stop = RCclose;
+    dev->get_stats = RCget_stats;
+    dev->do_ioctl = RCioctl;
+    dev->set_config = RCconfig;
+    return 0;
 }
 
 static int
@@ -438,25 +448,25 @@
 #ifdef RCDEBUG
     printk("rc: RCopen\n");
 #endif
-    RCEnableAdapterInterrupts(pDpa->id);
+    RCEnableI2OInterrupts(pDpa->id);
 
     if (pDpa->nexus)
     {
-       /* This is not the first time RCopen is called.  Thus,
-        * the interface was previously opened and later closed
-        * by RCclose().  RCclose() does a Shutdown; to wake up
-        * the adapter, a reset is mandatory before we can post
-        * receive buffers.  However, if the adapter initiated 
-        * a reboot while the interface was closed -- and interrupts
-        * were turned off -- we need will need to reinitialize
-        * the adapter, rather than simply waking it up.  
-        */
+        /* This is not the first time RCopen is called.  Thus,
+         * the interface was previously opened and later closed
+         * by RCclose().  RCclose() does a Shutdown; to wake up
+         * the adapter, a reset is mandatory before we can post
+         * receive buffers.  However, if the adapter initiated 
+         * a reboot while the interface was closed -- and interrupts
+         * were turned off -- we need will need to reinitialize
+         * the adapter, rather than simply waking it up.  
+         */
         printk("rc: Waking up adapter...\n");
         RCResetLANCard(pDpa->id,0,0,0);
     }
     else
     {
-       pDpa->nexus = 1;
+        pDpa->nexus = 1;
     }
 
     while(post_buffers)
@@ -469,19 +479,19 @@
 
         if ( count < requested )
         {
-             /*
-              * Check to see if we were able to post any buffers at all.
-              */
-             if (post_buffers == MAX_NMBR_RCV_BUFFERS)
-             {
-                  printk("rc: Error RCopen: not able to allocate any buffers\r\n");
-                  return(-ENOMEM);                    
-             }
-             printk("rc: Warning RCopen: not able to allocate all requested buffers\r\n");
-             break;            /* we'll try to post more buffers later */
+            /*
+             * Check to see if we were able to post any buffers at all.
+             */
+            if (post_buffers == MAX_NMBR_RCV_BUFFERS)
+            {
+                printk("rc: Error RCopen: not able to allocate any buffers\r\n");
+                return(-ENOMEM);                    
+            }
+            printk("rc: Warning RCopen: not able to allocate all requested buffers\r\n");
+            break;            /* we'll try to post more buffers later */
         }
         else
-             post_buffers -= count;
+            post_buffers -= count;
     }
     pDpa->numOutRcvBuffers = MAX_NMBR_RCV_BUFFERS - post_buffers;
     pDpa->shutdown = 0;        /* just in case */
@@ -496,68 +506,69 @@
 RC_xmit_packet(struct sk_buff *skb, struct device *dev)
 {
 
-     PDPA pDpa = (PDPA) dev->priv;
-     singleTCB tcb;
-     psingleTCB ptcb = &tcb;
-     RC_RETURN status = 0;
+    PDPA pDpa = (PDPA) dev->priv;
+    singleTCB tcb;
+    psingleTCB ptcb = &tcb;
+    RC_RETURN status = 0;
     
-     if (dev->tbusy || pDpa->shutdown || pDpa->reboot)
-     {
+        if (dev->tbusy || pDpa->shutdown || pDpa->reboot)
+        {
 #ifdef RCDEBUG
-          printk("rc: RC_xmit_packet: tbusy!\n");
+            printk("rc: RC_xmit_packet: tbusy!\n");
 #endif
-          return 1;
-     }
+            return 1;
+        }
       
-     if ( skb->len <= 0 ) 
-     {
-          printk("RC_xmit_packet: skb->len less than 0!\n");
-          return 0;
-     }
-
-     /*
-      * The user is free to reuse the TCB after RCSendPacket() returns, since
-      * the function copies the necessary info into its own private space.  Thus,
-      * our TCB can be a local structure.  The skb, on the other hand, will be
-      * freed up in our interrupt handler.
-      */
-     ptcb->bcount = 1;
-     /* 
-      * we'll get the context when the adapter interrupts us to tell us that
-      * the transmision is done. At that time, we can free skb.
-      */
-     ptcb->b.context = (U32)skb;    
-     ptcb->b.scount = 1;
-     ptcb->b.size = skb->len;
-     ptcb->b.addr = (U32)skb->data;
-
-#ifdef RCDEBUG
-     printk("rc: RC xmit: skb = 0x%x, pDpa = 0x%x, id = %d, ptcb = 0x%x\n", 
-            (uint)skb, (uint)pDpa, (uint)pDpa->id, (uint)ptcb);
-#endif
-     if ( (status = RCSendPacket(pDpa->id, (U32)NULL, (PRCTCB)ptcb))
-          != RC_RTN_NO_ERROR)
-     {
-#ifdef RCDEBUG
-          printk("rc: RC send error 0x%x\n", (uint)status);
-#endif
-          dev->tbusy = 1;
-     }
-     else
-     {
-          dev->trans_start = jiffies;
-          //       dev->tbusy = 0;
-     }
-     /*
-      * That's it!
-      */
-     return 0;
+    if ( skb->len <= 0 ) 
+    {
+        printk("RC_xmit_packet: skb->len less than 0!\n");
+        return 0;
+    }
+
+    /*
+     * The user is free to reuse the TCB after RCI2OSendPacket() returns, since
+     * the function copies the necessary info into its own private space.  Thus,
+     * our TCB can be a local structure.  The skb, on the other hand, will be
+     * freed up in our interrupt handler.
+     */
+    ptcb->bcount = 1;
+    /* 
+     * we'll get the context when the adapter interrupts us to tell us that
+     * the transmision is done. At that time, we can free skb.
+     */
+    ptcb->b.context = (U32)skb;    
+    ptcb->b.scount = 1;
+    ptcb->b.size = skb->len;
+    ptcb->b.addr = virt_to_bus((void *)skb->data);
+
+#ifdef RCDEBUG
+    printk("rc: RC xmit: skb = 0x%x, pDpa = 0x%x, id = %d, ptcb = 0x%x\n", 
+           (uint)skb, (uint)pDpa, (uint)pDpa->id, (uint)ptcb);
+#endif
+    if ( (status = RCI2OSendPacket(pDpa->id, (U32)NULL, (PRCTCB)ptcb))
+         != RC_RTN_NO_ERROR)
+    {
+#ifdef RCDEBUG
+        printk("rc: RC send error 0x%x\n", (uint)status);
+#endif
+        dev->tbusy = 1;
+        return 1;
+    }
+    else
+    {
+        dev->trans_start = jiffies;
+        //       dev->tbusy = 0;
+    }
+    /*
+     * That's it!
+     */
+    return 0;
 }
 
 /*
  * RCxmit_callback()
  *
- * The transmit callback routine. It's called by RCProcMsgQ()
+ * The transmit callback routine. It's called by RCProcI2OMsgQ()
  * because the adapter is done with one or more transmit buffers and
  * it's returning them to us, or we asked the adapter to return the
  * outstanding transmit buffers by calling RCResetLANCard() with 
@@ -574,80 +585,84 @@
     PDPA pDpa;
     struct device *dev;
 
-    pDpa = PCIAdapters[AdapterID];
-    if (!pDpa)
-    {
-        printk("rc: Fatal error: xmit callback, !pDpa\n");
-        return;
-    }
-    dev = pDpa->dev;
+        pDpa = PCIAdapters[AdapterID];
+        if (!pDpa)
+        {
+            printk("rc: Fatal error: xmit callback, !pDpa\n");
+            return;
+        }
+        dev = pDpa->dev;
 
         // printk("xmit_callback: Status = 0x%x\n", (uint)Status);
-    if (Status != RC_REPLY_STATUS_SUCCESS)
-    {
-        printk("rc: xmit_callback: Status = 0x%x\n", (uint)Status);
-    }
+        if (Status != I2O_REPLY_STATUS_SUCCESS)
+        {
+            printk("rc: xmit_callback: Status = 0x%x\n", (uint)Status);
+        }
 #ifdef RCDEBUG
-    if (pDpa->shutdown || pDpa->reboot)
-        printk("rc: xmit callback: shutdown||reboot\n");
+        if (pDpa->shutdown || pDpa->reboot)
+            printk("rc: xmit callback: shutdown||reboot\n");
 #endif
 
 #ifdef RCDEBUG     
-    printk("rc: xmit_callback: PcktCount = %d, BC = 0x%x\n", 
-            (uint)PcktCount, (uint)BufferContext);
+        printk("rc: xmit_callback: PcktCount = %d, BC = 0x%x\n", 
+               (uint)PcktCount, (uint)BufferContext);
 #endif
-    while (PcktCount--)
-    {
-        skb = (struct sk_buff *)(BufferContext[0]);
+        while (PcktCount--)
+        {
+            skb = (struct sk_buff *)(BufferContext[0]);
 #ifdef RCDEBUG
-        printk("rc: skb = 0x%x\n", (uint)skb);
+            printk("rc: skb = 0x%x\n", (uint)skb);
 #endif
-        BufferContext++;
-        dev_kfree_skb (skb, FREE_WRITE);
-    }
-    dev->tbusy = 0;
+            BufferContext++;
+#ifdef LINUX_2_1
+            dev_kfree_skb (skb);
+#else
+            dev_kfree_skb (skb, FREE_WRITE);
+#endif
+        }
+        dev->tbusy = 0;
 
 }
 
 static void
 RCreset_callback(U32 Status, U32 p1, U32 p2, U16 AdapterID)
 {
-     PDPA pDpa;
-     struct device *dev;
+    PDPA pDpa;
+    struct device *dev;
      
-     pDpa = PCIAdapters[AdapterID];
-     dev = pDpa->dev;
+    pDpa = PCIAdapters[AdapterID];
+    dev = pDpa->dev;
 #ifdef RCDEBUG
-      printk("rc: RCreset_callback Status 0x%x\n", (uint)Status);
+    printk("rc: RCreset_callback Status 0x%x\n", (uint)Status);
 #endif
-      /*
-       * Check to see why we were called.
-       */
-      if (pDpa->shutdown)
-      {
-           printk("rc: Shutting down interface\n");
-           pDpa->shutdown = 0;
-           pDpa->reboot = 0;
-           MOD_DEC_USE_COUNT; 
-      }
-      else if (pDpa->reboot)
-      {
-           printk("rc: reboot, shutdown adapter\n");
-           /*
-            * We don't set any of the flags in RCShutdownLANCard()
-            * and we don't pass a callback routine to it.
-            * The adapter will have already initiated the reboot by
-            * the time the function returns.
-            */
-		   RCDisableAdapterInterrupts(pDpa->id);
-           RCShutdownLANCard(pDpa->id,0,0,0);
-           printk("rc: scheduling timer...\n");
-           init_timer(&pDpa->timer);
-           pDpa->timer.expires = RUN_AT((30*HZ)/10); /* 3 sec. */
-           pDpa->timer.data = (unsigned long)dev;
-           pDpa->timer.function = &rc_timer;    /* timer handler */
-           add_timer(&pDpa->timer);
-      }
+    /*
+     * Check to see why we were called.
+     */
+    if (pDpa->shutdown)
+    {
+        printk("rc: Shutting down interface\n");
+        pDpa->shutdown = 0;
+        pDpa->reboot = 0;
+        MOD_DEC_USE_COUNT; 
+    }
+    else if (pDpa->reboot)
+    {
+        printk("rc: reboot, shutdown adapter\n");
+        /*
+         * We don't set any of the flags in RCShutdownLANCard()
+         * and we don't pass a callback routine to it.
+         * The adapter will have already initiated the reboot by
+         * the time the function returns.
+         */
+        RCDisableI2OInterrupts(pDpa->id);
+        RCShutdownLANCard(pDpa->id,0,0,0);
+        printk("rc: scheduling timer...\n");
+        init_timer(&pDpa->timer);
+        pDpa->timer.expires = RUN_AT((30*HZ)/10); /* 3 sec. */
+        pDpa->timer.data = (unsigned long)dev;
+        pDpa->timer.function = &rc_timer;    /* timer handler */
+        add_timer(&pDpa->timer);
+    }
 
 
 
@@ -656,33 +671,33 @@
 static void
 RCreboot_callback(U32 Status, U32 p1, U32 p2, U16 AdapterID)
 {
-     PDPA pDpa;
+    PDPA pDpa;
     
-     pDpa = PCIAdapters[AdapterID];
+    pDpa = PCIAdapters[AdapterID];
 #ifdef RCDEBUG
-     printk("rc: RCreboot: rcv buffers outstanding = %d\n", 
-            (uint)pDpa->numOutRcvBuffers);
+    printk("rc: RCreboot: rcv buffers outstanding = %d\n", 
+           (uint)pDpa->numOutRcvBuffers);
 #endif
-     if (pDpa->shutdown)
-     {
-          printk("rc: skipping reboot sequence -- shutdown already initiated\n");
-          return;
-     }
-     pDpa->reboot = 1;
-     /*
-      * OK, we reset the adapter and ask it to return all
-      * outstanding transmit buffers as well as the posted
-      * receive buffers.  When the adapter is done returning
-      * those buffers, it will call our RCreset_callback() 
-      * routine.  In that routine, we'll call RCShutdownLANCard()
-      * to tell the adapter that it's OK to start the reboot and
-      * schedule a timer callback routine to execute 3 seconds 
-      * later; this routine will reinitialize the adapter at that time.
-      */
-     RCResetLANCard(pDpa->id, 
-                    RC_RESOURCE_RETURN_POSTED_RX_BUCKETS | 
-                    RC_RESOURCE_RETURN_PEND_TX_BUFFERS,0,
-                    (PFNCALLBACK)RCreset_callback);
+    if (pDpa->shutdown)
+    {
+        printk("rc: skipping reboot sequence -- shutdown already initiated\n");
+        return;
+    }
+    pDpa->reboot = 1;
+    /*
+     * OK, we reset the adapter and ask it to return all
+     * outstanding transmit buffers as well as the posted
+     * receive buffers.  When the adapter is done returning
+     * those buffers, it will call our RCreset_callback() 
+     * routine.  In that routine, we'll call RCShutdownLANCard()
+     * to tell the adapter that it's OK to start the reboot and
+     * schedule a timer callback routine to execute 3 seconds 
+     * later; this routine will reinitialize the adapter at that time.
+     */
+    RCResetLANCard(pDpa->id, 
+                   RC_RESOURCE_RETURN_POSTED_RX_BUCKETS | 
+                   RC_RESOURCE_RETURN_PEND_TX_BUFFERS,0,
+                   (PFNCALLBACK)RCreset_callback);
 }
 
 
@@ -699,7 +714,7 @@
  * RCrecv_callback()
  * 
  * The receive packet callback routine.  This is called by
- * RCProcMsgQ() after the adapter posts buffers which have been
+ * RCProcI2OMsgQ() after the adapter posts buffers which have been
  * filled (one ethernet packet per buffer).
  */
 static void
@@ -710,134 +725,144 @@
                 U16  AdapterID)
 {
 
-     U32 len, count;
-     PDPA pDpa;
-     struct sk_buff *skb;
-     struct device *dev;
-     singleTCB tcb;
-     psingleTCB ptcb = &tcb;
+    U32 len, count;
+    PDPA pDpa;
+    struct sk_buff *skb;
+    struct device *dev;
+    singleTCB tcb;
+    psingleTCB ptcb = &tcb;
 
 
-          pDpa = PCIAdapters[AdapterID];
-          dev = pDpa->dev;
+        pDpa = PCIAdapters[AdapterID];
+        dev = pDpa->dev;
 
-          ptcb->bcount = 1;
+        ptcb->bcount = 1;
 
 #ifdef RCDEBUG
-          printk("rc: RCrecv_callback: 0x%x, 0x%x, 0x%x\n",
-                 (uint)PktCount, (uint)BucketsRemain, (uint)PacketDescBlock);
+        printk("rc: RCrecv_callback: 0x%x, 0x%x, 0x%x\n",
+               (uint)PktCount, (uint)BucketsRemain, (uint)PacketDescBlock);
 #endif
         
 #ifdef RCDEBUG
-          if ((pDpa->shutdown || pDpa->reboot) && !Status)
-               printk("shutdown||reboot && !Status: PktCount = %d\n",PktCount);
+        if ((pDpa->shutdown || pDpa->reboot) && !Status)
+            printk("shutdown||reboot && !Status: PktCount = %d\n",PktCount);
 #endif
 
-          if ( (Status != RC_REPLY_STATUS_SUCCESS) || pDpa->shutdown)
-          {
-               /*
-                * Free whatever buffers the adapter returned, but don't
-                * pass them to the kernel.
-                */
+        if ( (Status != I2O_REPLY_STATUS_SUCCESS) || pDpa->shutdown)
+        {
+            /*
+             * Free whatever buffers the adapter returned, but don't
+             * pass them to the kernel.
+             */
         
-                    if (!pDpa->shutdown && !pDpa->reboot)
-                         printk("rc: RCrecv error: status = 0x%x\n", (uint)Status);
-                    else
-                         printk("rc: Returning %d buffers, status = 0x%x\n", 
-                                PktCount, (uint)Status);
-               /*
-                * TO DO: check the nature of the failure and put the adapter in
-                * failed mode if it's a hard failure.  Send a reset to the adapter
-                * and free all outstanding memory.
-                */
-               if (Status == RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER)
-               {
-#ifdef RCDEBUG
-                    printk("RCrecv status ABORT NO DATA TRANSFER\n");
-#endif
-               }
-               /* check for reset status: RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER */ 
-               if (PacketDescBlock)
-               {
-                    while(PktCount--)
-                    {
-                         skb = (struct sk_buff *)PacketDescBlock[0];
-                         skb->free = 1;
-                         skb->lock = 0;    
-#ifdef RCDEBUG
-                         printk("free skb 0x%p\n", skb);
-#endif
-                         dev_kfree_skb(skb, FREE_READ);
-                         pDpa->numOutRcvBuffers--;
-                         PacketDescBlock += BD_SIZE; /* point to next context field */
-                    }
-               }
-               return;
-          }
-          else
-          {
-               while(PktCount--)
-               {
+                if (!pDpa->shutdown && !pDpa->reboot)
+                    printk("rc: RCrecv error: status = 0x%x\n", (uint)Status);
+                else
+                    printk("rc: Returning %d buffers, status = 0x%x\n", 
+                           PktCount, (uint)Status);
+            /*
+             * TO DO: check the nature of the failure and put the adapter in
+             * failed mode if it's a hard failure.  Send a reset to the adapter
+             * and free all outstanding memory.
+             */
+            if (Status == I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER)
+            {
+#ifdef RCDEBUG
+                printk("RCrecv status ABORT NO DATA TRANSFER\n");
+#endif
+            }
+            /* check for reset status: I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER */ 
+            if (PacketDescBlock)
+            {
+                while(PktCount--)
+                {
                     skb = (struct sk_buff *)PacketDescBlock[0];
+#ifndef LINUX_2_1
+                    skb->free = 1;
+                    skb->lock = 0;    
+#endif
 #ifdef RCDEBUG
-                    if (pDpa->shutdown)
-                         printk("shutdown: skb=0x%x\n", (uint)skb);
-
-                    printk("skb = 0x%x: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", (uint)skb,
-                           (uint)skb->data[0], (uint)skb->data[1], (uint)skb->data[2],
-                           (uint)skb->data[3], (uint)skb->data[4], (uint)skb->data[5]);
+                    printk("free skb 0x%p\n", skb);
 #endif
-                    if ( (memcmp(dev->dev_addr, skb->data, 6)) &&
-                         (!broadcast_packet(skb->data)))
+#ifdef LINUX_2_1
+                    dev_kfree_skb (skb);
+#else
+                    dev_kfree_skb(skb, FREE_READ);
+#endif
+                    pDpa->numOutRcvBuffers--;
+                    PacketDescBlock += BD_SIZE; /* point to next context field */
+                }
+            }
+            return;
+        }
+        else
+        {
+            while(PktCount--)
+            {
+                skb = (struct sk_buff *)PacketDescBlock[0];
+#ifdef RCDEBUG
+                if (pDpa->shutdown)
+                    printk("shutdown: skb=0x%x\n", (uint)skb);
+
+                printk("skb = 0x%x: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", (uint)skb,
+                       (uint)skb->data[0], (uint)skb->data[1], (uint)skb->data[2],
+                       (uint)skb->data[3], (uint)skb->data[4], (uint)skb->data[5]);
+#endif
+                if ( (memcmp(dev->dev_addr, skb->data, 6)) &&
+                     (!broadcast_packet(skb->data)))
+                {
+                    /*
+                     * Re-post the buffer to the adapter.  Since the adapter usually
+                     * return 1 to 2 receive buffers at a time, it's not too inefficient
+                     * post one buffer at a time but ... may be that should be 
+                     * optimized at some point.
+                     */
+                    ptcb->b.context = (U32)skb;    
+                    ptcb->b.scount = 1;
+                    ptcb->b.size = MAX_ETHER_SIZE;
+                    ptcb->b.addr = virt_to_bus((void *)skb->data);
+
+                    if ( RCPostRecvBuffers(pDpa->id, (PRCTCB)ptcb ) != RC_RTN_NO_ERROR)
                     {
-                         /*
-                          * Re-post the buffer to the adapter.  Since the adapter usually
-                          * return 1 to 2 receive buffers at a time, it's not too inefficient
-                          * post one buffer at a time but ... may be that should be 
-                          * optimized at some point.
-                          */
-                         ptcb->b.context = (U32)skb;    
-                         ptcb->b.scount = 1;
-                         ptcb->b.size = MAX_ETHER_SIZE;
-                         ptcb->b.addr = (U32)skb->data;
-
-                         if ( RCPostRecvBuffers(pDpa->id, (PRCTCB)ptcb ) != RC_RTN_NO_ERROR)
-                         {
-                              printk("rc: RCrecv_callback: post buffer failed!\n");
-                              skb->free = 1;
-                              dev_kfree_skb(skb, FREE_READ);
-                         }
-                         else
-                         {
-                              pDpa->numOutRcvBuffers++;
-                         }
+                        printk("rc: RCrecv_callback: post buffer failed!\n");
+#ifdef LINUX_2_1
+                        dev_kfree_skb (skb);
+#else
+                        skb->free = 1;
+                        dev_kfree_skb(skb, FREE_READ);
+#endif
                     }
                     else
                     {
-                         len = PacketDescBlock[2];
-                         skb->dev = dev;
-                         skb_put( skb, len ); /* adjust length and tail */
-                         skb->protocol = eth_type_trans(skb, dev);
-                         netif_rx(skb);    /* send the packet to the kernel */
-                         dev->last_rx = jiffies;
+                        pDpa->numOutRcvBuffers++;
                     }
-                    pDpa->numOutRcvBuffers--;
-                    PacketDescBlock += BD_SIZE; /* point to next context field */
-               }
-          } 
+                }
+                else
+                {
+                    len = PacketDescBlock[2];
+                    skb->dev = dev;
+                    skb_put( skb, len ); /* adjust length and tail */
+                    skb->protocol = eth_type_trans(skb, dev);
+                    netif_rx(skb);    /* send the packet to the kernel */
+                    dev->last_rx = jiffies;
+                }
+                pDpa->numOutRcvBuffers--;
+                PacketDescBlock += BD_SIZE; /* point to next context field */
+            }
+        } 
     
-          /*
-           * Replenish the posted receive buffers. 
-           * DO NOT replenish buffers if the driver has already
-           * initiated a reboot or shutdown!
-           */
-
-          if (!pDpa->shutdown && !pDpa->reboot)
-          {
-               count = RC_allocate_and_post_buffers(dev, 
-                                                    MAX_NMBR_RCV_BUFFERS-pDpa->numOutRcvBuffers);
-               pDpa->numOutRcvBuffers += count;
-          }
+        /*
+         * Replenish the posted receive buffers. 
+         * DO NOT replenish buffers if the driver has already
+         * initiated a reboot or shutdown!
+         */
+
+        if (!pDpa->shutdown && !pDpa->reboot)
+        {
+            count = RC_allocate_and_post_buffers(dev, 
+                                                 MAX_NMBR_RCV_BUFFERS-pDpa->numOutRcvBuffers);
+            pDpa->numOutRcvBuffers += count;
+        }
 
 }
 
@@ -846,156 +871,156 @@
  * 
  * Interrupt handler. 
  * This routine sets up a couple of pointers and calls
- * RCProcMsgQ(), which in turn process the message and
+ * RCProcI2OMsgQ(), which in turn process the message and
  * calls one of our callback functions.
  */
 static void 
 RCinterrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 
-     PDPA pDpa;
-     struct device *dev = (struct device *)(dev_id);
+    PDPA pDpa;
+    struct device *dev = (struct device *)(dev_id);
 
-     pDpa = (PDPA) (dev->priv);
+    pDpa = (PDPA) (dev->priv);
      
-     if (pDpa->shutdown)
-          printk("rc: shutdown: service irq\n");
+    if (pDpa->shutdown)
+        printk("rc: shutdown: service irq\n");
 
 #ifdef RCDEBUG
-     printk("RC irq: pDpa = 0x%x, dev = 0x%x, id = %d\n", 
-            (uint)pDpa, (uint)dev, (uint)pDpa->id);
-     printk("dev = 0x%x\n", (uint)dev);
+    printk("RC irq: pDpa = 0x%x, dev = 0x%x, id = %d\n", 
+           (uint)pDpa, (uint)dev, (uint)pDpa->id);
+    printk("dev = 0x%x\n", (uint)dev);
 #endif
-     if (dev->interrupt)
-          printk("%s: Re-entering the interrupt handler.\n", dev->name);
-     dev->interrupt = 1;
+    if (dev->interrupt)
+        printk("%s: Re-entering the interrupt handler.\n", dev->name);
+    dev->interrupt = 1;
 
-     RCProcMsgQ(pDpa->id);
-     dev->interrupt = 0;
+    RCProcI2OMsgQ(pDpa->id);
+    dev->interrupt = 0;
 
-     return;
+    return;
 }
 
 #define REBOOT_REINIT_RETRY_LIMIT 10
 static void rc_timer(unsigned long data)
 {
-     struct device *dev = (struct device *)data;
-     PDPA pDpa = (PDPA) (dev->priv);
-     int init_status;
-     static int retry = 0;
-     int post_buffers = MAX_NMBR_RCV_BUFFERS;
-     int count = 0;
-     int requested = 0;
-
-     if (pDpa->reboot)
-     {
-
-          init_status = InitRCApiMsgLayer(pDpa->id, dev->base_addr, 
-                                          pDpa->PLanApiPA, pDpa->PLanApiPA,
-                                          (PFNTXCALLBACK)RCxmit_callback,
-                                          (PFNRXCALLBACK)RCrecv_callback,
-                                          (PFNCALLBACK)RCreboot_callback);
-
-          switch(init_status)
-          {
-          case RC_RTN_NO_ERROR:
+    struct device *dev = (struct device *)data;
+    PDPA pDpa = (PDPA) (dev->priv);
+    int init_status;
+    static int retry = 0;
+    int post_buffers = MAX_NMBR_RCV_BUFFERS;
+    int count = 0;
+    int requested = 0;
+
+    if (pDpa->reboot)
+    {
+
+        init_status = RCInitI2OMsgLayer(pDpa->id, dev->base_addr, 
+                                        pDpa->PLanApiPA, pDpa->PLanApiPA,
+                                        (PFNTXCALLBACK)RCxmit_callback,
+                                        (PFNRXCALLBACK)RCrecv_callback,
+                                        (PFNCALLBACK)RCreboot_callback);
+
+        switch(init_status)
+        {
+        case RC_RTN_NO_ERROR:
  
-               pDpa->reboot = 0;
-               pDpa->shutdown = 0;        /* just in case */
-               RCReportDriverCapability(pDpa->id, DriverControlWord);
-               RCEnableAdapterInterrupts(pDpa->id);
-
-               if (dev->flags & IFF_UP)
-               {
-                    while(post_buffers)
-                    {
-                         if (post_buffers > MAX_NMBR_POST_BUFFERS_PER_MSG)
-                              requested = MAX_NMBR_POST_BUFFERS_PER_MSG;
-                         else
-                              requested = post_buffers;
-                         count = RC_allocate_and_post_buffers(dev, requested);
-                         post_buffers -= count;
-                         if ( count < requested )
-                              break;
-                    }
-                    pDpa->numOutRcvBuffers = 
-                         MAX_NMBR_RCV_BUFFERS - post_buffers;
-                    printk("rc: posted %d buffers \r\n", 
-                           (uint)pDpa->numOutRcvBuffers);
-               }
-               printk("rc: Initialization done.\n");
-               return;
-          case RC_RTN_FREE_Q_EMPTY:
-               retry++;
-               printk("rc: inbound free q emtpy\n");
-               break;
-          default:
-               retry++;
-               printk("rc: unexpected bad status after reboot\n");    
-               break;
-          }
-
-          if (retry > REBOOT_REINIT_RETRY_LIMIT)
-          {
-               printk("rc: unable to reinitialize adapter after reboot\n");
-               printk("rc: decrementing driver and closing interface\n");
-               RCDisableAdapterInterrupts(pDpa->id);
-               dev->flags &= ~IFF_UP;
-               MOD_DEC_USE_COUNT; 
-          }
-          else
-          {
-               printk("rc: rescheduling timer...\n");
-               init_timer(&pDpa->timer);
-               pDpa->timer.expires = RUN_AT((30*HZ)/10); /* 3 sec. */
-               pDpa->timer.data = (unsigned long)dev;
-               pDpa->timer.function = &rc_timer;    /* timer handler */
-               add_timer(&pDpa->timer);
-          }
-     }
-     else
-     {
-          printk("rc: timer??\n");
-     }
+            pDpa->reboot = 0;
+            pDpa->shutdown = 0;        /* just in case */
+            RCReportDriverCapability(pDpa->id, DriverControlWord);
+            RCEnableI2OInterrupts(pDpa->id);
+
+            if (dev->flags & IFF_UP)
+            {
+                while(post_buffers)
+                {
+                    if (post_buffers > MAX_NMBR_POST_BUFFERS_PER_MSG)
+                        requested = MAX_NMBR_POST_BUFFERS_PER_MSG;
+                    else
+                        requested = post_buffers;
+                    count = RC_allocate_and_post_buffers(dev, requested);
+                    post_buffers -= count;
+                    if ( count < requested )
+                        break;
+                }
+                pDpa->numOutRcvBuffers = 
+                    MAX_NMBR_RCV_BUFFERS - post_buffers;
+                printk("rc: posted %d buffers \r\n", 
+                       (uint)pDpa->numOutRcvBuffers);
+            }
+            printk("rc: Initialization done.\n");
+            return;
+        case RC_RTN_FREE_Q_EMPTY:
+            retry++;
+            printk("rc: inbound free q emtpy\n");
+            break;
+        default:
+            retry++;
+            printk("rc: unexpected bad status after reboot\n");    
+            break;
+        }
+
+        if (retry > REBOOT_REINIT_RETRY_LIMIT)
+        {
+            printk("rc: unable to reinitialize adapter after reboot\n");
+            printk("rc: decrementing driver and closing interface\n");
+            RCDisableI2OInterrupts(pDpa->id);
+            dev->flags &= ~IFF_UP;
+            MOD_DEC_USE_COUNT; 
+        }
+        else
+        {
+            printk("rc: rescheduling timer...\n");
+            init_timer(&pDpa->timer);
+            pDpa->timer.expires = RUN_AT((30*HZ)/10); /* 3 sec. */
+            pDpa->timer.data = (unsigned long)dev;
+            pDpa->timer.function = &rc_timer;    /* timer handler */
+            add_timer(&pDpa->timer);
+        }
+    }
+    else
+    {
+        printk("rc: timer??\n");
+    }
 }
 
 static int
 RCclose(struct device *dev)
 {
 
-     PDPA pDpa = (PDPA) dev->priv;
+    PDPA pDpa = (PDPA) dev->priv;
 
 #ifdef RCDEBUG
-     printk("rc: RCclose\r\n");
+    printk("rc: RCclose\r\n");
 #endif
-     if (pDpa->reboot)
-     {
-          printk("rc: skipping reset -- adapter already in reboot mode\n");
-          dev->flags &= ~IFF_UP;
-          pDpa->shutdown = 1;
-          return 0;
-     }
+    if (pDpa->reboot)
+    {
+        printk("rc: skipping reset -- adapter already in reboot mode\n");
+        dev->flags &= ~IFF_UP;
+        pDpa->shutdown = 1;
+        return 0;
+    }
 #ifdef RCDEBUG
-     printk("rc: receive buffers outstanding: %d\n", 
-            (uint)pDpa->numOutRcvBuffers);
+    printk("rc: receive buffers outstanding: %d\n", 
+           (uint)pDpa->numOutRcvBuffers);
 #endif
 
-     pDpa->shutdown = 1;
+    pDpa->shutdown = 1;
 
-     /*
-      * We can't allow the driver to be unloaded until the adapter returns
-      * all posted receive buffers.  It doesn't hurt to tell the adapter
-      * to return all posted receive buffers and outstanding xmit buffers,
-      * even if there are none.
-      */
+    /*
+     * We can't allow the driver to be unloaded until the adapter returns
+     * all posted receive buffers.  It doesn't hurt to tell the adapter
+     * to return all posted receive buffers and outstanding xmit buffers,
+     * even if there are none.
+     */
 
-     RCShutdownLANCard(pDpa->id, 
-                       RC_RESOURCE_RETURN_POSTED_RX_BUCKETS | 
-                       RC_RESOURCE_RETURN_PEND_TX_BUFFERS,0,
-                       (PFNCALLBACK)RCreset_callback);
+    RCShutdownLANCard(pDpa->id, 
+                      RC_RESOURCE_RETURN_POSTED_RX_BUCKETS | 
+                      RC_RESOURCE_RETURN_PEND_TX_BUFFERS,0,
+                      (PFNCALLBACK)RCreset_callback);
 
-     dev->flags &= ~IFF_UP;
-     return 0;
+        dev->flags &= ~IFF_UP;
+    return 0;
 }
 
 static struct enet_statistics *
@@ -1097,236 +1122,298 @@
  
     switch (cmd)  {
  
-        case RCU_PROTOCOL_REV:
-             /*
-              * Assign user protocol revision, to tell user-level
-              * controller program whether or not it's in sync.
-              */
-             rq->ifr_ifru.ifru_data = (caddr_t) USER_PROTOCOL_REV;
-             break;
+    case RCU_PROTOCOL_REV:
+        /*
+         * Assign user protocol revision, to tell user-level
+         * controller program whether or not it's in sync.
+         */
+        rq->ifr_ifru.ifru_data = (caddr_t) USER_PROTOCOL_REV;
+        break;
   
 
-        case RCU_COMMAND:
-        {
-            int error;
-
-            error=verify_area(VERIFY_WRITE, rq->ifr_data, sizeof(RCuser));
-            if (error)  {
-                return error;
-            }
-            memcpy_fromfs(&RCuser, rq->ifr_data, sizeof(RCuser));
+    case RCU_COMMAND:
+    {
+#ifdef LINUX_2_1
+        if(copy_from_user(&RCuser, rq->ifr_data, sizeof(RCuser)))
+             return -EFAULT;
+#else
+        int error;
+        error=verify_area(VERIFY_WRITE, rq->ifr_data, sizeof(RCuser));
+        if (error)  {
+            return error;
+        }
+        memcpy_fromfs(&RCuser, rq->ifr_data, sizeof(RCuser));
+#endif
         
 #ifdef RCDEBUG
-            printk("RCioctl: RCuser_cmd = 0x%x\n", RCuser.cmd);
+        printk("RCioctl: RCuser_cmd = 0x%x\n", RCuser.cmd);
 #endif
   
-            switch(RCuser.cmd)
+        switch(RCuser.cmd)
+        {
+        case RCUC_GETFWVER:
+            printk("RC GETFWVER\n");
+            RCUD_GETFWVER = &RCuser.RCUS_GETFWVER;
+            RCGetFirmwareVer(pDpa->id, (PU8) &RCUD_GETFWVER->FirmString, NULL);
+            break;
+        case RCUC_GETINFO:
+            printk("RC GETINFO\n");
+            RCUD_GETINFO = &RCuser.RCUS_GETINFO;
+            RCUD_GETINFO -> mem_start = dev->base_addr;
+            RCUD_GETINFO -> mem_end = dev->base_addr + 2*32768;
+            RCUD_GETINFO -> base_addr = pDpa->pci_addr;
+            RCUD_GETINFO -> irq = dev->irq;
+            break;
+        case RCUC_GETIPANDMASK:
+            printk("RC GETIPANDMASK\n");
+            RCUD_GETIPANDMASK = &RCuser.RCUS_GETIPANDMASK;
+            RCGetRavlinIPandMask(pDpa->id, (PU32) &RCUD_GETIPANDMASK->IpAddr,
+                                 (PU32) &RCUD_GETIPANDMASK->NetMask, NULL);
+            break;
+        case RCUC_GETLINKSTATISTICS:
+            printk("RC GETLINKSTATISTICS\n");
+            RCUD_GETLINKSTATISTICS = &RCuser.RCUS_GETLINKSTATISTICS;
+            RCGetLinkStatistics(pDpa->id, (P_RCLINKSTATS) &RCUD_GETLINKSTATISTICS->StatsReturn, NULL);
+            break;
+        case RCUC_GETLINKSTATUS:
+            printk("RC GETLINKSTATUS\n");
+            RCUD_GETLINKSTATUS = &RCuser.RCUS_GETLINKSTATUS;
+            RCGetLinkStatus(pDpa->id, (PU32) &RCUD_GETLINKSTATUS->ReturnStatus, NULL);
+            break;
+        case RCUC_GETMAC:
+            printk("RC GETMAC\n");
+            RCUD_GETMAC = &RCuser.RCUS_GETMAC;
+            RCGetMAC(pDpa->id, (PU8) &RCUD_GETMAC->mac, NULL);
+            break;
+        case RCUC_GETPROM:
+            printk("RC GETPROM\n");
+            RCUD_GETPROM = &RCuser.RCUS_GETPROM;
+            RCGetPromiscuousMode(pDpa->id, (PU32) &RCUD_GETPROM->PromMode, NULL);
+            break;
+        case RCUC_GETBROADCAST:
+            printk("RC GETBROADCAST\n");
+            RCUD_GETBROADCAST = &RCuser.RCUS_GETBROADCAST;
+            RCGetBroadcastMode(pDpa->id, (PU32) &RCUD_GETBROADCAST->BroadcastMode, NULL);
+            break;
+        case RCUC_GETSPEED:
+            printk("RC GETSPEED\n");
+            if (!(dev->flags & IFF_UP))    
             {
-                case RCUC_GETFWVER:
-                    printk("RC GETFWVER\n");
-                    RCUD_GETFWVER = &RCuser.RCUS_GETFWVER;
-                    RCGetFirmwareVer(pDpa->id, (PU8) &RCUD_GETFWVER->FirmString, NULL);
-                    break;
-                case RCUC_GETINFO:
-                    printk("RC GETINFO\n");
-                    RCUD_GETINFO = &RCuser.RCUS_GETINFO;
-                    RCUD_GETINFO -> mem_start = dev->base_addr;
-                    RCUD_GETINFO -> mem_end = dev->base_addr + 32768;
-                    RCUD_GETINFO -> base_addr = pDpa->pci_addr;
-                    RCUD_GETINFO -> irq = dev->irq;
-                    break;
-                case RCUC_GETIPANDMASK:
-                    printk("RC GETIPANDMASK\n");
-                    RCUD_GETIPANDMASK = &RCuser.RCUS_GETIPANDMASK;
-                    RCGetRavlinIPandMask(pDpa->id, (PU32) &RCUD_GETIPANDMASK->IpAddr,
-                        (PU32) &RCUD_GETIPANDMASK->NetMask, NULL);
-                    break;
-                case RCUC_GETLINKSTATISTICS:
-                    printk("RC GETLINKSTATISTICS\n");
-                    RCUD_GETLINKSTATISTICS = &RCuser.RCUS_GETLINKSTATISTICS;
-                    RCGetLinkStatistics(pDpa->id, (P_RCLINKSTATS) &RCUD_GETLINKSTATISTICS->StatsReturn, NULL);
-                    break;
-                case RCUC_GETLINKSTATUS:
-                    printk("RC GETLINKSTATUS\n");
-                    RCUD_GETLINKSTATUS = &RCuser.RCUS_GETLINKSTATUS;
-                    RCGetLinkStatus(pDpa->id, (PU32) &RCUD_GETLINKSTATUS->ReturnStatus, NULL);
-                    break;
-                case RCUC_GETMAC:
-                    printk("RC GETMAC\n");
-                    RCUD_GETMAC = &RCuser.RCUS_GETMAC;
-                    RCGetMAC(pDpa->id, (PU8) &RCUD_GETMAC->mac, NULL);
-                    break;
-                case RCUC_GETSPEED:
-                    printk("RC GETSPEED\n");
-                    if (!(dev->flags & IFF_UP))    
-                    {
-                        printk("RCioctl, GETSPEED error: interface down\n");
-                        return -ENODATA;
-                    }
-                    RCUD_GETSPEED = &RCuser.RCUS_GETSPEED;
-                    RCGetLinkSpeed(pDpa->id, (PU32) &RCUD_GETSPEED->LinkSpeedCode, NULL);
-                    printk("RC speed = 0x%ld\n", RCUD_GETSPEED->LinkSpeedCode);
-                    break;
-                default:
-                    printk("RC command default\n");
-                    RCUD_DEFAULT = &RCuser.RCUS_DEFAULT;
-                    RCUD_DEFAULT -> rc = 0x11223344;
-                    break;
+                printk("RCioctl, GETSPEED error: interface down\n");
+                return -ENODATA;
             }
-            memcpy_tofs(rq->ifr_data, &RCuser, sizeof(RCuser));
+            RCUD_GETSPEED = &RCuser.RCUS_GETSPEED;
+            RCGetLinkSpeed(pDpa->id, (PU32) &RCUD_GETSPEED->LinkSpeedCode, NULL);
+            printk("RC speed = 0x%ld\n", RCUD_GETSPEED->LinkSpeedCode);
+            break;
+        case RCUC_SETIPANDMASK:
+            printk("RC SETIPANDMASK\n");
+            RCUD_SETIPANDMASK = &RCuser.RCUS_SETIPANDMASK;
+            printk ("RC New IP Addr = %d.%d.%d.%d, ", (U8) ((RCUD_SETIPANDMASK->IpAddr) & 0xff),
+                    (U8) ((RCUD_SETIPANDMASK->IpAddr >>  8) & 0xff),
+                    (U8) ((RCUD_SETIPANDMASK->IpAddr >> 16) & 0xff),
+                    (U8) ((RCUD_SETIPANDMASK->IpAddr >> 24) & 0xff));
+            printk ("RC New Mask = %d.%d.%d.%d\n", (U8) ((RCUD_SETIPANDMASK->NetMask) & 0xff),
+                    (U8) ((RCUD_SETIPANDMASK->NetMask >>  8) & 0xff),
+                    (U8) ((RCUD_SETIPANDMASK->NetMask >> 16) & 0xff),
+                    (U8) ((RCUD_SETIPANDMASK->NetMask >> 24) & 0xff));
+            RCSetRavlinIPandMask(pDpa->id, (U32) RCUD_SETIPANDMASK->IpAddr,
+                                 (U32) RCUD_SETIPANDMASK->NetMask);
+            break;
+        case RCUC_SETMAC:
+            printk("RC SETMAC\n");
+            RCUD_SETMAC = &RCuser.RCUS_SETMAC;
+            printk ("RC New MAC addr = %02X:%02X:%02X:%02X:%02X:%02X\n",
+                    (U8) (RCUD_SETMAC->mac[0]), (U8) (RCUD_SETMAC->mac[1]), (U8) (RCUD_SETMAC->mac[2]),
+                    (U8) (RCUD_SETMAC->mac[3]), (U8) (RCUD_SETMAC->mac[4]), (U8) (RCUD_SETMAC->mac[5]));
+            RCSetMAC(pDpa->id, (PU8) &RCUD_SETMAC->mac);
+            break;
+        case RCUC_SETSPEED:
+            printk("RC SETSPEED\n");
+            RCUD_SETSPEED = &RCuser.RCUS_SETSPEED;
+            RCSetLinkSpeed(pDpa->id, (U16) RCUD_SETSPEED->LinkSpeedCode);
+            printk("RC New speed = 0x%d\n", RCUD_SETSPEED->LinkSpeedCode);
+            break;
+        case RCUC_SETPROM:
+            printk("RC SETPROM\n");
+            RCUD_SETPROM = &RCuser.RCUS_SETPROM;
+            RCSetPromiscuousMode(pDpa->id,(U16)RCUD_SETPROM->PromMode);
+            printk("RC New prom mode = 0x%d\n", RCUD_SETPROM->PromMode);
+            break;
+        case RCUC_SETBROADCAST:
+            printk("RC SETBROADCAST\n");
+            RCUD_SETBROADCAST = &RCuser.RCUS_SETBROADCAST;
+            RCSetBroadcastMode(pDpa->id,(U16)RCUD_SETBROADCAST->BroadcastMode);
+            printk("RC New broadcast mode = 0x%d\n", RCUD_SETBROADCAST->BroadcastMode);
             break;
-        }   /* RCU_COMMAND */ 
-
         default:
-            printk("RC default\n");
-            rq->ifr_ifru.ifru_data = (caddr_t) 0x12345678;
+            printk("RC command default\n");
+            RCUD_DEFAULT = &RCuser.RCUS_DEFAULT;
+            RCUD_DEFAULT -> rc = 0x11223344;
             break;
+        }
+#ifdef LINUX_2_1
+        copy_to_user(rq->ifr_data, &RCuser, sizeof(RCuser));
+#else
+        memcpy_tofs(rq->ifr_data, &RCuser, sizeof(RCuser));
+#endif
+        break;
+    }   /* RCU_COMMAND */ 
+
+    default:
+        printk("RC default\n");
+        rq->ifr_ifru.ifru_data = (caddr_t) 0x12345678;
+        break;
     }
     return 0;
 }
 
 static int RCconfig(struct device *dev, struct ifmap *map)
 {
-     /*
-      * To be completed ...
+    /*
+     * To be completed ...
       */
-      printk("rc: RCconfig\n");
-      return 0;
-      if (dev->flags & IFF_UP)    /* can't act on a running interface */
-           return -EBUSY;
+    printk("rc: RCconfig\n");
+    return 0;
+    if (dev->flags & IFF_UP)    /* can't act on a running interface */
+        return -EBUSY;
 
      /* Don't allow changing the I/O address */
-     if (map->base_addr != dev->base_addr) {
-          printk(KERN_WARNING "RC pci45: Change I/O address not implemented\n");
-          return -EOPNOTSUPP;
-     }
-     return 0;
+    if (map->base_addr != dev->base_addr) {
+        printk(KERN_WARNING "RC pci45: Change I/O address not implemented\n");
+        return -EOPNOTSUPP;
+    }
+    return 0;
 }
 
-#ifdef MODULE
-void cleanup_module(void)
+void
+cleanup_module(void)
 {
-     PDPA pDpa;
-     struct device *next;
+    PDPA pDpa;
+    struct device *next;
 
 
 #ifdef RCDEBUG
-     printk("rc: RC cleanup_module\n");
-     printk("rc: root_RCdev = 0x%x\n", (uint)root_RCdev);
+    printk("rc: RC cleanup_module\n");
+    printk("rc: root_RCdev = 0x%x\n", (uint)root_RCdev);
 #endif
 
 
-     while (root_RCdev)
-     {
-          pDpa = (PDPA) root_RCdev->priv;
+    while (root_RCdev)
+    {
+        pDpa = (PDPA) root_RCdev->priv;
 #ifdef RCDEBUG
-          printk("rc: cleanup 0x%08X\n", (uint)root_RCdev);
+        printk("rc: cleanup 0x%08X\n", (uint)root_RCdev);
 #endif
-          printk("Adapter reset: 0x%x\n", RCResetAdapter(pDpa->id));
-          unregister_netdev(root_RCdev);
-          next = pDpa->next;
-
-          vfree((unsigned long *)root_RCdev->base_addr); 
-          free_irq( root_RCdev->irq, root_RCdev );
-          kfree(root_RCdev);
-          root_RCdev = next;
-     }
+        printk("IOP reset: 0x%x\n", RCResetIOP(pDpa->id));
+        unregister_netdev(root_RCdev);
+        next = pDpa->next;
+
+        iounmap((unsigned long *)root_RCdev->base_addr); 
+        free_irq( root_RCdev->irq, root_RCdev );
+        kfree(root_RCdev);
+        root_RCdev = next;
+    }
 }
-#endif
-
 
 static int
 RC_allocate_and_post_buffers(struct device *dev, int numBuffers)
 {
 
-     int i;
-     PDPA pDpa = (PDPA)dev->priv;
-     PU32 p;
-     psingleB pB;
-     struct sk_buff *skb;
-     RC_RETURN status;
-
-     if (!numBuffers)
-          return 0;
-     else if (numBuffers > MAX_NMBR_POST_BUFFERS_PER_MSG)
-     {
+    int i;
+    PDPA pDpa = (PDPA)dev->priv;
+    PU32 p;
+    psingleB pB;
+    struct sk_buff *skb;
+    RC_RETURN status;
+
+    if (!numBuffers)
+        return 0;
+    else if (numBuffers > MAX_NMBR_POST_BUFFERS_PER_MSG)
+    {
 #ifdef RCDEBUG
-          printk("rc: Too many buffers requested!\n");
-          printk("rc: attempting to allocate only 32 buffers\n");
+        printk("rc: Too many buffers requested!\n");
+        printk("rc: attempting to allocate only 32 buffers\n");
 #endif
-          numBuffers = 32;
-     }
+        numBuffers = 32;
+    }
     
-     p = (PU32) kmalloc(sizeof(U32) + numBuffers*sizeof(singleB), GFP_ATOMIC);
+    p = (PU32) kmalloc(sizeof(U32) + numBuffers*sizeof(singleB), GFP_ATOMIC);
+
+#ifdef RCDEBUG
+    printk("rc: TCB = 0x%x\n", (uint)p);
+#endif
+
+    if (!p)
+    {
+        printk("rc: RCopen: unable to allocate TCB\n");
+        return 0;
+    }
+
+    p[0] = 0;                              /* Buffer Count */
+    pB = (psingleB)((U32)p + sizeof(U32)); /* point to the first buffer */
 
 #ifdef RCDEBUG
-     printk("rc: TCB = 0x%x\n", (uint)p);
+    printk("rc: p[0] = 0x%x, p = 0x%x, pB = 0x%x\n", (uint)p[0], (uint)p, (uint)pB);
+    printk("rc: pB = 0x%x\n", (uint)pB);
 #endif
 
-     if (!p)
-     {
-          printk("rc: RCopen: unable to allocate TCB\n");
-          return 0;
-     }
-
-     p[0] = 0;                              /* Buffer Count */
-     pB = (psingleB)((U32)p + sizeof(U32)); /* point to the first buffer */
-
-#ifdef RCDEBUG
-     printk("rc: p[0] = 0x%x, p = 0x%x, pB = 0x%x\n", (uint)p[0], (uint)p, (uint)pB);
-     printk("rc: pB = 0x%x\n", (uint)pB);
-#endif
-
-     for (i=0; i<numBuffers; i++)
-     {
-          skb = dev_alloc_skb(MAX_ETHER_SIZE+2);
-          if (!skb)
-          {
-               printk("rc: Doh! RCopen: unable to allocate enough skbs!\n");
-               if (*p != 0)        /* did we allocate any buffers at all? */
-               {
-#ifdef RCDEBUG
-                    printk("rc: will post only %d buffers \n", (uint)(*p));
-#endif
-                    break;
-               }
-               else 
-               {
-                    kfree(p);    /* Free the TCB */
-                    return 0;
-               }
-          }
-#ifdef RCDEBUG
-          printk("post 0x%x\n", (uint)skb);
-#endif
-          skb_reserve(skb, 2);    /* Align IP on 16 byte boundaries */
-          pB->context = (U32)skb;
-          pB->scount = 1;        /* segment count */
-          pB->size = MAX_ETHER_SIZE;
-          pB->addr = (U32)skb->data;
-          p[0]++;
-          pB++;
-     }
-
-     if ( (status = RCPostRecvBuffers(pDpa->id, (PRCTCB)p )) != RC_RTN_NO_ERROR)
-     {
-          printk("rc: Post buffer failed with error code 0x%x!\n", status);
-          pB = (psingleB)((U32)p + sizeof(U32)); /* point to the first buffer */
-          while(p[0])
-          {
-               skb = (struct sk_buff *)pB->context;
-               skb->free = 1;    
-#ifdef RCDEBUG
-               printk("rc: freeing 0x%x\n", (uint)skb);
-#endif
-               dev_kfree_skb(skb, FREE_READ);
-               p[0]--;
-               pB++;
-          }
-#ifdef RCDEBUG
-           printk("rc: freed all buffers, p[0] = %ld\n", p[0]);
-#endif
-     }
-     kfree(p);
-     return(p[0]);                /* return the number of posted buffers */
+    for (i=0; i<numBuffers; i++)
+    {
+        skb = dev_alloc_skb(MAX_ETHER_SIZE+2);
+        if (!skb)
+        {
+            printk("rc: Doh! RCopen: unable to allocate enough skbs!\n");
+            if (*p != 0)        /* did we allocate any buffers at all? */
+            {
+#ifdef RCDEBUG
+                printk("rc: will post only %d buffers \n", (uint)(*p));
+#endif
+                break;
+            }
+            else 
+            {
+                kfree(p);    /* Free the TCB */
+                return 0;
+            }
+        }
+#ifdef RCDEBUG
+        printk("post 0x%x\n", (uint)skb);
+#endif
+        skb_reserve(skb, 2);    /* Align IP on 16 byte boundaries */
+        pB->context = (U32)skb;
+        pB->scount = 1;        /* segment count */
+        pB->size = MAX_ETHER_SIZE;
+        pB->addr = virt_to_bus((void *)skb->data);
+        p[0]++;
+        pB++;
+    }
+
+    if ( (status = RCPostRecvBuffers(pDpa->id, (PRCTCB)p )) != RC_RTN_NO_ERROR)
+    {
+        printk("rc: Post buffer failed with error code 0x%x!\n", status);
+        pB = (psingleB)((U32)p + sizeof(U32)); /* point to the first buffer */
+        while(p[0])
+        {
+            skb = (struct sk_buff *)pB->context;
+#ifndef LINUX_2_1
+            skb->free = 1;    
+#endif
+#ifdef RCDEBUG
+            printk("rc: freeing 0x%x\n", (uint)skb);
+#endif
+#ifdef LINUX_2_1
+            dev_kfree_skb (skb);
+#else
+            dev_kfree_skb(skb, FREE_READ);
+#endif
+            p[0]--;
+            pB++;
+        }
+#ifdef RCDEBUG
+        printk("rc: freed all buffers, p[0] = %ld\n", p[0]);
+#endif
+    }
+    kfree(p);
+    return(p[0]);                /* return the number of posted buffers */
 }
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/rtl8139.c linux/drivers/net/rtl8139.c
--- linux.vanilla/drivers/net/rtl8139.c	Sun Dec  6 00:14:40 1998
+++ linux/drivers/net/rtl8139.c	Sun Dec  6 03:40:04 1998
@@ -20,11 +20,22 @@
 */
 
 static const char *version =
-"rtl8139.c:v0.99B 4/7/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/rtl8139.html\n";
+"rtl8139.c:v1.04 9/22/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/rtl8139.html\n";
 
 /* A few user-configurable values. */
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 10;
+static int max_interrupt_work = 20;
+#define rtl8129_debug debug
+static int rtl8129_debug = 1;
+
+/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
+   The RTL chips use a 64 element hash table based on the Ethernet CRC.  */
+static int multicast_filter_limit = 32;
+
+/* Used to pass the full-duplex flag, etc. */
+#define MAX_UNITS 8		/* More are supported, limit only on options */
+static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
 
 /* Size of the in-memory receive ring. */
 #define RX_BUF_LEN_IDX	3			/* 0==8K, 1==16K, 2==32K, 3==64K */
@@ -39,12 +50,13 @@
 /* The following settings are log_2(bytes)-4:  0 == 16 bytes .. 6==1024. */
 #define RX_FIFO_THRESH	4		/* Rx buffer level before first PCI xfer.  */
 #define RX_DMA_BURST	4		/* Maximum PCI burst, '4' is 256 bytes */
-#define TX_DMA_BURST	4
+#define TX_DMA_BURST	4		/* Calculate as 16<<val. */
 
 /* Operational parameters that usually are not changed. */
 /* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT  ((4000*HZ)/1000)
+#define TX_TIMEOUT  (4*HZ)
 
+#include <linux/config.h>
 #ifdef MODULE
 #ifdef MODVERSIONS
 #include <linux/modversions.h>
@@ -60,40 +72,49 @@
 #include <linux/sched.h>
 #include <linux/string.h>
 #include <linux/timer.h>
-#include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
 #include <linux/malloc.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
-#include <linux/bios32.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
 #include <asm/processor.h>		/* Processor type for cache alignment. */
 #include <asm/bitops.h>
 #include <asm/io.h>
-#include <asm/dma.h>
 
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
+/* Kernel compatibility defines, some common to David Hind's PCMCIA package.
+   This is only in the support-all-kernels source code. */
 
 #define RUN_AT(x) (jiffies + (x))
 
 #include <linux/delay.h>
 
-#if (LINUX_VERSION_CODE < 0x20123)
+#if LINUX_VERSION_CODE < 0x20123
 #define test_and_set_bit(val, addr) set_bit(val, addr)
 #endif
+#if LINUX_VERSION_CODE <= 0x20139
+#define	net_device_stats enet_statistics
+#else
+#define NETSTATS_VER2
+#endif
+#if LINUX_VERSION_CODE < 0x20155  ||  defined(CARDBUS)
+/* Grrrr, the PCI code changed, but did not consider CardBus... */
+#include <linux/bios32.h>
+#define PCI_SUPPORT_VER1
+#else
+#define PCI_SUPPORT_VER2
+#endif
+#if LINUX_VERSION_CODE < 0x20159
+#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE);
+#else
+#define dev_free_skb(skb) dev_kfree_skb(skb);
+#endif
 
 /* The I/O extent. */
 #define RTL8129_TOTAL_SIZE 0x80
 
-#ifdef HAVE_DEVLIST
-struct netdev_entry rtl8139_drv =
-{"RTL8139", rtl8139_probe, RTL8129_TOTAL_SIZE, NULL};
-#endif
-
-static int rtl8129_debug = 1;
-
 /*
 				Theory of Operation
 
@@ -139,16 +160,45 @@
 IVc. Errata
 
 */
+
+
+/* This table drives the PCI probe routines.  It's mostly boilerplate in all
+   of the drivers, and will likely be provided by some future kernel.
+   Note the matching code -- the first table entry matchs all 56** cards but
+   second only the 1234 card.
+*/
+enum pci_flags_bit {
+	PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
+	PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3,
+};
+struct pci_id_info {
+	const char *name;
+	u16	vendor_id, device_id, device_id_mask, flags;
+	int io_size;
+	struct device *(*probe1)(int pci_bus, int pci_devfn, struct device *dev,
+							 long ioaddr, int irq, int chip_idx, int fnd_cnt);
+};
+
+static struct device * rtl8129_probe1(int pci_bus, int pci_devfn,
+									  struct device *dev, long ioaddr,
+									  int irq, int chp_idx, int fnd_cnt);
+
+static struct pci_id_info pci_tbl[] =
+{{ "RealTek RTL8129 Fast Ethernet",
+   0x10ec, 0x8129, 0xffff, PCI_USES_IO|PCI_USES_MASTER, 0x80, rtl8129_probe1},
+ { "RealTek RTL8139 Fast Ethernet",
+   0x10ec, 0x8139, 0xffff, PCI_USES_IO|PCI_USES_MASTER, 0x80, rtl8129_probe1},
+ { "RealTek RTL8139 Fast Ethernet (mislabeled)",
+   0x1113, 0x1211, 0xffff, PCI_USES_IO|PCI_USES_MASTER, 0x80, rtl8129_probe1},
+ {0,},						/* 0 terminated list. */
+};
+
+/* The capability table matches the chip table above. */
+enum {HAS_MII_XCVR=0x01, HAS_CHIP_XCVR=0x02, HAS_LNK_CHNG=0x04};
+static int rtl_cap_tbl[] = {
+	HAS_MII_XCVR, HAS_CHIP_XCVR|HAS_LNK_CHNG, HAS_CHIP_XCVR|HAS_LNK_CHNG,
+};
 
-#ifndef PCI_VENDOR_ID_REALTEK
-#define PCI_VENDOR_ID_REALTEK		0x10ec
-#endif
-#ifndef PCI_DEVICE_ID_REALTEK_8129
-#define PCI_DEVICE_ID_REALTEK_8129	0x8129
-#endif
-#ifndef PCI_DEVICE_ID_REALTEK_8139
-#define PCI_DEVICE_ID_REALTEK_8139	0x8139
-#endif
 
 /* The rest of these values should never change. */
 #define NUM_TX_DESC	4			/* Number of Tx descriptor registers. */
@@ -157,7 +207,7 @@
 enum RTL8129_registers {
 	MAC0=0,						/* Ethernet hardware address. */
 	MAR0=8,						/* Multicast filter. */
-	TxStat0=0x10,				/* Transmit status (Four 32bit registers). */
+	TxStatus0=0x10,				/* Transmit status (Four 32bit registers). */
 	TxAddr0=0x20,				/* Tx descriptors (also four 32bit). */
 	RxBuf=0x30, RxEarlyCnt=0x34, RxEarlyStatus=0x36,
 	ChipCmd=0x37, RxBufPtr=0x38, RxBufAddr=0x3A,
@@ -168,7 +218,8 @@
 	Cfg9346=0x50, Config0=0x51, Config1=0x52,
 	FlashReg=0x54, GPPinData=0x58, GPPinDir=0x59, MII_SMI=0x5A, HltClk=0x5B,
 	MultiIntr=0x5C, TxSummary=0x60,
-	BMCR=0x62, BMSR=0x64, NWayAdvert=0x66, NWayLPAR=0x68, NWayExpansion=0x6A,
+	MII_BMCR=0x62, MII_BMSR=0x64, NWayAdvert=0x66, NWayLPAR=0x68,
+	NWayExpansion=0x6A,
 	/* Undocumented registers, but required for proper operation. */
 	FIFOTMS=0x70,	/* FIFO Test Mode Select */
 	CSCR=0x74,	/* Chip Status and Configuration Register. */
@@ -214,24 +265,24 @@
 	struct device *next_module;
 	int chip_id;
 	int chip_revision;
+	unsigned char pci_bus, pci_devfn;
 #if LINUX_VERSION_CODE > 0x20139
 	struct net_device_stats stats;
 #else
 	struct enet_statistics stats;
 #endif
 	struct timer_list timer;	/* Media selection timer. */
-	unsigned int cur_rx, cur_tx;		/* The next free and used entries */
-	unsigned int dirty_rx, dirty_tx;
+	unsigned int cur_rx;		/* Index into the Rx buffer of next Rx pkt. */
+	unsigned int cur_tx, dirty_tx, tx_flag;
 	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
 	struct sk_buff* tx_skbuff[NUM_TX_DESC];
 	unsigned char *tx_buf[NUM_TX_DESC];	/* Tx bounce buffers */
 	unsigned char *rx_ring;
 	unsigned char *tx_bufs;				/* Tx bounce buffer region. */
-	unsigned char mc_filter[8];			/* Current multicast filter. */
 	char phys[4];						/* MII device addresses. */
-	int in_interrupt;					/* Alpha needs word-wide lock. */
 	unsigned int tx_full:1;				/* The Tx queue is full. */
 	unsigned int full_duplex:1;			/* Full-duplex operation requested. */
+	unsigned int duplex_lock:1;			/* Full-duplex operation requested. */
 	unsigned int default_port:4;		/* Last dev->if_port value. */
 	unsigned int media2:4;				/* Secondary monitored media port. */
 	unsigned int medialock:1;			/* Don't sense media type. */
@@ -239,24 +290,21 @@
 };
 
 #ifdef MODULE
-/* Used to pass the full-duplex flag, etc. */
-static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-static int full_duplex[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-#if LINUX_VERSION_CODE > 0x20118
+#if LINUX_VERSION_CODE > 0x20115
 MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
 MODULE_DESCRIPTION("RealTek RTL8129/8139 Fast Ethernet driver");
-MODULE_PARM(debug, "i");
-MODULE_PARM(options, "1-" __MODULE_STRING(8) "i");
-MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(multicast_filter_limit, "i");
 MODULE_PARM(max_interrupt_work, "i");
+MODULE_PARM(debug, "i");
 #endif
 #endif
 
-static struct device *rtl8129_probe1(struct device *dev, int ioaddr, int irq,
-									 int chip_id, int options, int card_idx);
 static int rtl8129_open(struct device *dev);
-static int read_eeprom(int ioaddr, int location);
-static int mdio_read(int ioaddr, int phy_id, int location);
+static int read_eeprom(long ioaddr, int location);
+static int mdio_read(struct device *dev, int phy_id, int location);
+static void mdio_write(struct device *dev, int phy_id, int location, int val);
 static void rtl8129_timer(unsigned long data);
 static void rtl8129_tx_timeout(struct device *dev);
 static void rtl8129_init_ring(struct device *dev);
@@ -264,145 +312,138 @@
 static int rtl8129_rx(struct device *dev);
 static void rtl8129_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
 static int rtl8129_close(struct device *dev);
+static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd);
 static struct enet_statistics *rtl8129_get_stats(struct device *dev);
+static inline u32 ether_crc(int length, unsigned char *data);
 static void set_rx_mode(struct device *dev);
 
 
-#ifdef MODULE
 /* A list of all installed RTL8129 devices, for removing the driver module. */
 static struct device *root_rtl8129_dev = NULL;
-#endif
+
+/* Ideally we would detect all network cards in slot order.  That would
+   be best done a central PCI probe dispatch, which wouldn't work
+   well when dynamically adding drivers.  So instead we detect just the
+   Rtl81*9 cards in slot order. */
 
 int rtl8139_probe(struct device *dev)
 {
 	int cards_found = 0;
-	static int pci_index = 0;	/* Static, for multiple probe calls. */
+	int pci_index = 0;
+	unsigned char pci_bus, pci_device_fn;
 
-	/* Ideally we would detect all network cards in slot order.  That would
-	   be best done a central PCI probe dispatch, which wouldn't work
-	   well with the current structure.  So instead we detect just the
-	   Rtl81*9 cards in slot order. */
-
-	if (pcibios_present()) {
-		unsigned char pci_bus, pci_device_fn;
-
-		for (;pci_index < 0xff; pci_index++) {
-			u8 pci_irq_line, pci_latency;
-			u16 pci_command, new_command, vendor, device;
-			u32 pci_ioaddr;
+	if ( ! pcibios_present())
+		return -ENODEV;
 
-			if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8,
-#ifdef REVERSE_PROBE_ORDER
-									0xff - pci_index,
-#else
-									pci_index,
-#endif
-									&pci_bus, &pci_device_fn)
-				!= PCIBIOS_SUCCESSFUL)
+	for (;pci_index < 0xff; pci_index++) {
+		u16 vendor, device, pci_command, new_command;
+		int chip_idx, irq;
+		long ioaddr;
+
+		if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, pci_index,
+								&pci_bus, &pci_device_fn)
+			!= PCIBIOS_SUCCESSFUL)
+			break;
+		pcibios_read_config_word(pci_bus, pci_device_fn,
+								 PCI_VENDOR_ID, &vendor);
+		pcibios_read_config_word(pci_bus, pci_device_fn,
+								 PCI_DEVICE_ID, &device);
+
+		for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++)
+			if (vendor == pci_tbl[chip_idx].vendor_id
+				&& (device & pci_tbl[chip_idx].device_id_mask) ==
+				pci_tbl[chip_idx].device_id)
 				break;
-			pcibios_read_config_word(pci_bus, pci_device_fn,
-									 PCI_VENDOR_ID, &vendor);
-			if (vendor != PCI_VENDOR_ID_REALTEK)
-				continue;
+		if (pci_tbl[chip_idx].vendor_id == 0) 		/* Compiled out! */
+			continue;
 
-			pcibios_read_config_word(pci_bus, pci_device_fn,
-									 PCI_DEVICE_ID, &device);
+		{
+#if defined(PCI_SUPPORT_VER2)
+			struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn);
+			ioaddr = pdev->base_address[0] & ~3;
+			irq = pdev->irq;
+#else
+			u32 pci_ioaddr;
+			u8 pci_irq_line;
 			pcibios_read_config_byte(pci_bus, pci_device_fn,
 									 PCI_INTERRUPT_LINE, &pci_irq_line);
 			pcibios_read_config_dword(pci_bus, pci_device_fn,
 									  PCI_BASE_ADDRESS_0, &pci_ioaddr);
-			/* Remove I/O space marker in bit 0. */
-			pci_ioaddr &= ~3;
-
-			if (device != PCI_DEVICE_ID_REALTEK_8129
-				&&  device != PCI_DEVICE_ID_REALTEK_8139) {
-				printk(KERN_NOTICE "Unknown RealTek PCI ethernet chip type "
-					   "%4.4x detected: not configured.\n", device);
-				continue;
-			}
-			if (check_region(pci_ioaddr, RTL8129_TOTAL_SIZE))
-				continue;
+			ioaddr = pci_ioaddr & ~3;
+			irq = pci_irq_line;
+#endif
+		}
 
-			/* Activate the card: fix for brain-damaged Win98 BIOSes. */
-			pcibios_read_config_word(pci_bus, pci_device_fn,
-									 PCI_COMMAND, &pci_command);
-			new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO;
-			if (pci_command != new_command) {
-				printk(KERN_INFO "  The PCI BIOS has not enabled this"
-					   " device!  Updating PCI config %4.4x->%4.4x.\n",
-					   pci_command, new_command);
-				pcibios_write_config_word(pci_bus, pci_device_fn,
-										  PCI_COMMAND, new_command);
-			}
+		if ((pci_tbl[chip_idx].flags & PCI_USES_IO) &&
+			check_region(ioaddr, pci_tbl[chip_idx].io_size))
+			continue;
+
+		/* Activate the card: fix for brain-damaged Win98 BIOSes. */
+		pcibios_read_config_word(pci_bus, pci_device_fn,
+								 PCI_COMMAND, &pci_command);
+		new_command = pci_command | (pci_tbl[chip_idx].flags & 7);
+		if (pci_command != new_command) {
+			printk(KERN_INFO "  The PCI BIOS has not enabled the"
+				   " device at %d/%d!  Updating PCI command %4.4x->%4.4x.\n",
+				   pci_bus, pci_device_fn, pci_command, new_command);
+			pcibios_write_config_word(pci_bus, pci_device_fn,
+									  PCI_COMMAND, new_command);
+		}
 
-#ifdef MODULE
-			dev = rtl8129_probe1(dev, pci_ioaddr, pci_irq_line, device,
-								 options[cards_found], cards_found);
-#else
-			dev = rtl8129_probe1(dev, pci_ioaddr, pci_irq_line, device,
-								 dev ? dev->mem_start : 0, -1);
-#endif
+		dev = pci_tbl[chip_idx].probe1(pci_bus, pci_device_fn, dev, ioaddr,
+									   irq, chip_idx, cards_found);
 
-			if (dev) {
-				pcibios_read_config_byte(pci_bus, pci_device_fn,
-										 PCI_LATENCY_TIMER, &pci_latency);
-				if (pci_latency < 32) {
-					printk(KERN_NOTICE"  PCI latency timer (CFLT) is "
-						   "unreasonably low at %d.  Setting to 64 clocks.\n",
-						   pci_latency);
-					pcibios_write_config_byte(pci_bus, pci_device_fn,
-											  PCI_LATENCY_TIMER, 64);
-				} else if (rtl8129_debug > 1)
-					printk(KERN_INFO"  PCI latency timer (CFLT) is %#x.\n",
-						   pci_latency);
-				dev = 0;
-				cards_found++;
+		if (dev  && (pci_tbl[chip_idx].flags & PCI_COMMAND_MASTER)) {
+			u8 pci_latency;
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+									 PCI_LATENCY_TIMER, &pci_latency);
+			if (pci_latency < 32) {
+				printk(KERN_NOTICE "  PCI latency timer (CFLT) is "
+					   "unreasonably low at %d.  Setting to 64 clocks.\n",
+					   pci_latency);
+				pcibios_write_config_byte(pci_bus, pci_device_fn,
+										  PCI_LATENCY_TIMER, 64);
 			}
 		}
+		dev = 0;
+		cards_found++;
 	}
 
-#if defined (MODULE)
-	return cards_found;
-#else
 	return cards_found ? 0 : -ENODEV;
-#endif
 }
 
-static struct device *rtl8129_probe1(struct device *dev, int ioaddr, int irq,
-								   int chip_id, int options, int card_idx)
+static struct device * rtl8129_probe1(int pci_bus, int pci_devfn,
+									  struct device *dev, long ioaddr,
+									  int irq, int chip_idx, int found_cnt)
 {
 	static int did_version = 0;			/* Already printed version info. */
 	struct rtl8129_private *tp;
-	int i;
+	int i, option = found_cnt < MAX_UNITS ? options[found_cnt] : 0;
 
 	if (rtl8129_debug > 0  &&  did_version++ == 0)
 		printk(KERN_INFO "%s", version);
 
 	dev = init_etherdev(dev, 0);
 
-	printk(KERN_INFO "%s: RealTek RTL%x at %#3x, IRQ %d, ",
-		   dev->name, chip_id, ioaddr, irq);
+	printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ",
+		   dev->name, pci_tbl[chip_idx].name, ioaddr, irq);
 
 	/* Bring the chip out of low-power mode. */
 	outb(0x00, ioaddr + Config1);
 
-	/* Perhaps this should be read from the EEPROM? */
-	for (i = 0; i < 6; i++)
-		dev->dev_addr[i] = inb(ioaddr + MAC0 + i);
+	if (read_eeprom(ioaddr, 0) != 0xffff)
+		for (i = 0; i < 3; i++)
+			((u16 *)(dev->dev_addr))[i] = read_eeprom(ioaddr, i + 7);
+	else
+		for (i = 0; i < 6; i++)
+			dev->dev_addr[i] = inb(ioaddr + MAC0 + i);
 
 	for (i = 0; i < 5; i++)
 		printk("%2.2x:", dev->dev_addr[i]);
 	printk("%2.2x.\n", dev->dev_addr[i]);
 
-	if (rtl8129_debug > 1) {
-		printk(KERN_INFO "%s: EEPROM contents\n", dev->name);
-		for (i = 0; i < 64; i++)
-			printk(" %4.4x%s", read_eeprom(ioaddr, i),
-				   i%16 == 15 ? "\n"KERN_INFO : "");
-	}
-
 	/* We do a request_region() to register /proc/ioports info. */
-	request_region(ioaddr, RTL8129_TOTAL_SIZE, "RealTek RTL8129/39 Fast Ethernet");
+	request_region(ioaddr, pci_tbl[chip_idx].io_size, dev->name);
 
 	dev->base_addr = ioaddr;
 	dev->irq = irq;
@@ -412,22 +453,21 @@
 	memset(tp, 0, sizeof(*tp));
 	dev->priv = tp;
 
-#ifdef MODULE
 	tp->next_module = root_rtl8129_dev;
 	root_rtl8129_dev = dev;
-#endif
 
-	tp->chip_id = chip_id;
+	tp->chip_id = chip_idx;
+	tp->pci_bus = pci_bus;
+	tp->pci_devfn = pci_devfn;
 
 	/* Find the connected MII xcvrs.
 	   Doing this in open() would allow detecting external xcvrs later, but
 	   takes too much time. */
-	if (chip_id == 0x8129) {
+	if (rtl_cap_tbl[chip_idx] & HAS_MII_XCVR) {
 		int phy, phy_idx;
 		for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys);
 			 phy++) {
-			int mii_status = mdio_read(ioaddr, phy, 1);
-
+			int mii_status = mdio_read(dev, phy, 1);
 			if (mii_status != 0xffff  && mii_status != 0x0000) {
 				tp->phys[phy_idx++] = phy;
 				printk(KERN_INFO "%s: MII transceiver found at address %d.\n",
@@ -441,7 +481,7 @@
 			tp->phys[0] = -1;
 		}
 	} else {
-			tp->phys[0] = -1;
+			tp->phys[0] = 32;
 	}
 
 	/* Put the chip into low-power mode. */
@@ -450,18 +490,21 @@
 	outb('H', ioaddr + HltClk);		/* 'R' would leave the clock running. */
 
 	/* The lower four bits are the media type. */
-	if (options > 0) {
-		tp->full_duplex = (options & 16) ? 1 : 0;
-		tp->default_port = options & 15;
+	if (option > 0) {
+		tp->full_duplex = (option & 0x200) ? 1 : 0;
+		tp->default_port = option & 15;
 		if (tp->default_port)
 			tp->medialock = 1;
 	}
-#ifdef MODULE
-	if (card_idx >= 0) {
-		if (full_duplex[card_idx] >= 0)
-			tp->full_duplex = full_duplex[card_idx];
+
+	if (found_cnt < MAX_UNITS  &&  full_duplex[found_cnt] > 0)
+		tp->full_duplex = full_duplex[found_cnt];
+
+	if (tp->full_duplex) {
+		printk(KERN_INFO "%s: Media type forced to Full Duplex.\n", dev->name);
+		mdio_write(dev, tp->phys[0], 4, 0x141);
+		tp->duplex_lock = 1;
 	}
-#endif
 
 	/* The Rtl8129-specific entries in the device structure. */
 	dev->open = &rtl8129_open;
@@ -469,6 +512,7 @@
 	dev->stop = &rtl8129_close;
 	dev->get_stats = &rtl8129_get_stats;
 	dev->set_multicast_list = &set_rx_mode;
+	dev->do_ioctl = &mii_ioctl;
 
 	return dev;
 }
@@ -485,25 +529,21 @@
 #define EE_ENB			(0x80 | EE_CS)
 
 /* Delay between EEPROM clock transitions.
-   No extra delay is needed with 33Mhz PCI, but 66Mhz is untested.
+   No extra delay is needed with 33Mhz PCI, but 66Mhz may change this.
  */
 
-#ifdef _LINUX_DELAY_H
-#define eeprom_delay(nanosec)	udelay(1)
-#else
-#define eeprom_delay(nanosec)	do { ; } while (0)
-#endif
+#define eeprom_delay()	inl(ee_addr)
 
 /* The EEPROM commands include the alway-set leading bit. */
 #define EE_WRITE_CMD	(5 << 6)
 #define EE_READ_CMD		(6 << 6)
 #define EE_ERASE_CMD	(7 << 6)
 
-static int read_eeprom(int ioaddr, int location)
+static int read_eeprom(long ioaddr, int location)
 {
 	int i;
 	unsigned retval = 0;
-	int ee_addr = ioaddr + Cfg9346;
+	long ee_addr = ioaddr + Cfg9346;
 	int read_cmd = location | EE_READ_CMD;
 
 	outb(EE_ENB & ~EE_CS, ee_addr);
@@ -513,20 +553,19 @@
 	for (i = 10; i >= 0; i--) {
 		int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
 		outb(EE_ENB | dataval, ee_addr);
-		eeprom_delay(100);
+		eeprom_delay();
 		outb(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
-		eeprom_delay(150);
-		outb(EE_ENB | dataval, ee_addr);	/* Finish EEPROM a clock tick. */
-		eeprom_delay(250);
+		eeprom_delay();
 	}
 	outb(EE_ENB, ee_addr);
+	eeprom_delay();
 
 	for (i = 16; i > 0; i--) {
 		outb(EE_ENB | EE_SHIFT_CLK, ee_addr);
-		eeprom_delay(100);
+		eeprom_delay();
 		retval = (retval << 1) | ((inb(ee_addr) & EE_DATA_READ) ? 1 : 0);
 		outb(EE_ENB, ee_addr);
-		eeprom_delay(100);
+		eeprom_delay();
 	}
 
 	/* Terminate the EEPROM access. */
@@ -544,38 +583,42 @@
 #define MDIO_DATA_OUT	0x04
 #define MDIO_DATA_IN	0x02
 #define MDIO_CLK		0x01
-#ifdef _LINUX_DELAY_H
-#define mdio_delay()	udelay(1) /* Really 400ns. */
-#else
-#define mdio_delay()	do { ; } while (0)
-#endif
+#define MDIO_WRITE0 (MDIO_DIR)
+#define MDIO_WRITE1 (MDIO_DIR | MDIO_DATA_OUT)
+
+#define mdio_delay()	inb(mdio_addr)
+
+static char mii_2_8139_map[8] = {MII_BMCR, MII_BMSR, 0, 0, NWayAdvert,
+								 NWayLPAR, NWayExpansion, 0 };
 
 /* Syncronize the MII management interface by shifting 32 one bits out. */
-static void mdio_sync(int ioaddr)
+static void mdio_sync(long mdio_addr)
 {
 	int i;
-	int mdio_addr = ioaddr + MII_SMI;
 
 	for (i = 32; i >= 0; i--) {
-		outb(MDIO_DIR | MDIO_DATA_OUT, mdio_addr);
+		outb(MDIO_WRITE1, mdio_addr);
 		mdio_delay();
-		outb(MDIO_DIR | MDIO_DATA_OUT | MDIO_CLK, mdio_addr);
+		outb(MDIO_WRITE1 | MDIO_CLK, mdio_addr);
 		mdio_delay();
 	}
 	return;
 }
-static int mdio_read(int ioaddr, int phy_id, int location)
+static int mdio_read(struct device *dev, int phy_id, int location)
 {
-	int i;
-	int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+	long mdio_addr = dev->base_addr + MII_SMI;
+	int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
 	int retval = 0;
-	int mdio_addr = ioaddr + MII_SMI;
+	int i;
 
-	mdio_sync(ioaddr);
+	if ((phy_id & 0x1f) == 0) {	/* Really a 8139.  Use internal registers. */
+		return location < 8 && mii_2_8139_map[location] ?
+			inw(dev->base_addr + mii_2_8139_map[location]) : 0;
+	}
+	mdio_sync(mdio_addr);
 	/* Shift the read command bits out. */
 	for (i = 15; i >= 0; i--) {
-		int dataval =
-		  (read_cmd & (1 << i)) ? MDIO_DATA_OUT : 0;
+		int dataval = (mii_cmd & (1 << i)) ? MDIO_DATA_OUT : 0;
 
 		outb(MDIO_DIR | dataval, mdio_addr);
 		mdio_delay();
@@ -593,14 +636,45 @@
 	}
 	return (retval>>1) & 0xffff;
 }
+
+static void mdio_write(struct device *dev, int phy_id, int location, int value)
+{
+	long mdio_addr = dev->base_addr + MII_SMI;
+	int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
+	int i;
+
+	if (phy_id == 32) {			/* Really a 8139.  Use internal registers. */
+		if (location < 8  &&  mii_2_8139_map[location])
+			outw(value, dev->base_addr + mii_2_8139_map[location]);
+		return;
+	}
+	mdio_sync(mdio_addr);
+
+	/* Shift the command bits out. */
+	for (i = 31; i >= 0; i--) {
+		int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
+		outb(dataval, mdio_addr);
+		mdio_delay();
+		outb(dataval | MDIO_CLK, mdio_addr);
+		mdio_delay();
+	}
+	/* Clear out extra bits. */
+	for (i = 2; i > 0; i--) {
+		outb(0, mdio_addr);
+		mdio_delay();
+		outb(MDIO_CLK, mdio_addr);
+		mdio_delay();
+	}
+	return;
+}
+
 
 static int
 rtl8129_open(struct device *dev)
 {
 	struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	int i;
-	int full_duplex = 0;
 
 	/* Soft reset the chip. */
 	outb(CmdReset, ioaddr + ChipCmd);
@@ -636,29 +710,26 @@
 	outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) | (RX_DMA_BURST<<8),
 		 ioaddr + RxConfig);
 	outl((TX_DMA_BURST<<8)|0x03000000, ioaddr + TxConfig);
+	tp->tx_flag = (TX_FIFO_THRESH<<11) & 0x003f0000;
 
-	full_duplex = tp->full_duplex;
-	if (tp->phys[0] >= 0  ||  tp->chip_id == 0x8139) {
-		u16 mii_reg5;
-		if (tp->chip_id == 0x8139)
-			mii_reg5 = inw(ioaddr + NWayLPAR);
-		else
-			mii_reg5 = mdio_read(ioaddr, tp->phys[0], 5);
+	tp->full_duplex = tp->duplex_lock;
+	if (tp->phys[0] >= 0  ||  (rtl_cap_tbl[tp->chip_id] & HAS_MII_XCVR)) {
+		u16 mii_reg5 = mdio_read(dev, tp->phys[0], 5);
 		if (mii_reg5 == 0xffff)
 			;					/* Not there */
 		else if ((mii_reg5 & 0x0100) == 0x0100
 				 || (mii_reg5 & 0x00C0) == 0x0040)
-			full_duplex = 1;
+			tp->full_duplex = 1;
 		if (rtl8129_debug > 1)
 			printk(KERN_INFO"%s: Setting %s%s-duplex based on"
 				   " auto-negotiated partner ability %4.4x.\n", dev->name,
 				   mii_reg5 == 0 ? "" :
 				   (mii_reg5 & 0x0180) ? "100mbps " : "10mbps ",
-				   full_duplex ? "full" : "half", mii_reg5);
+				   tp->full_duplex ? "full" : "half", mii_reg5);
 	}
 
 	outb(0xC0, ioaddr + Cfg9346);
-	outb(full_duplex ? 0x60 : 0x20, ioaddr + Config1);
+	outb(tp->full_duplex ? 0x60 : 0x20, ioaddr + Config1);
 	outb(0x00, ioaddr + Cfg9346);
 
 	outl(virt_to_bus(tp->rx_ring), ioaddr + RxBuf);
@@ -678,10 +749,10 @@
 		| TxErr | TxOK | RxErr | RxOK, ioaddr + IntrMask);
 
 	if (rtl8129_debug > 1)
-		printk(KERN_DEBUG"%s: rtl8129_open() ioaddr %4.4x IRQ %d"
+		printk(KERN_DEBUG"%s: rtl8129_open() ioaddr %#lx IRQ %d"
 			   " GP Pins %2.2x %s-duplex.\n",
 			   dev->name, ioaddr, dev->irq, inb(ioaddr + GPPinData),
-			   full_duplex ? "full" : "half");
+			   tp->full_duplex ? "full" : "half");
 
 	/* Set the timer to switch to check for link beat and perhaps switch
 	   to an alternate media type. */
@@ -698,26 +769,26 @@
 {
 	struct device *dev = (struct device *)data;
 	struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	int next_tick = 0;
+	int mii_reg5 = mdio_read(dev, tp->phys[0], 5);
 
-	if (tp->chip_id == 0x8139) {
-		u16 mii_reg5 = inw(ioaddr + NWayLPAR);
-		if ((mii_reg5 & 0x0100) == 0x0100
-			|| (mii_reg5 & 0x00C0) == 0x0040)
-			if ( ! tp->full_duplex) {
-				tp->full_duplex = 1;
-				if (rtl8129_debug > 0)
-					printk(KERN_INFO "%s: Switching to full-duplex based on "
-						   "link partner ability of %4.4x.\n",
-						   dev->name, mii_reg5);
-				outb(0xC0, ioaddr + Cfg9346);
-				outb(tp->full_duplex ? 0x60 : 0x20, ioaddr + Config1);
-				outb(0x00, ioaddr + Cfg9346);
-			}
+	if (! tp->duplex_lock  &&  mii_reg5 != 0xffff) {
+		int duplex = (mii_reg5&0x0100) || (mii_reg5 & 0x01C0) == 0x0040;
+		if (tp->full_duplex != duplex) {
+			tp->full_duplex = duplex;
+			printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d link"
+				   " partner ability of %4.4x.\n", dev->name,
+				   tp->full_duplex ? "full" : "half", tp->phys[0], mii_reg5);
+			outb(0xC0, ioaddr + Cfg9346);
+			outb(tp->full_duplex ? 0x60 : 0x20, ioaddr + Config1);
+			outb(0x00, ioaddr + Cfg9346);
+		}
+		next_tick = 60*HZ;
 	}
+
 	if (rtl8129_debug > 2) {
-		if (tp->chip_id == 0x8129)
+		if (rtl_cap_tbl[tp->chip_id] & HAS_MII_XCVR)
 			printk(KERN_DEBUG"%s: Media selection tick, GP pins %2.2x.\n",
 				   dev->name, inb(ioaddr + GPPinData));
 		else
@@ -740,41 +811,51 @@
 static void rtl8129_tx_timeout(struct device *dev)
 {
 	struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
-	int ioaddr = dev->base_addr;
-	int i;
+	long ioaddr = dev->base_addr;
+	int mii_reg, i;
+
+	if (rtl8129_debug > 0)
+		printk(KERN_WARNING "%s: Transmit timeout, status %2.2x %4.4x "
+			   "media %2.2x.\n",
+			   dev->name, inb(ioaddr + ChipCmd), inw(ioaddr + IntrStatus),
+			   inb(ioaddr + GPPinData));
 
 	/* Disable interrupts by clearing the interrupt mask. */
 	outw(0x0000, ioaddr + IntrMask);
-
-	if (rtl8129_debug > 0)
-		printk(KERN_WARNING "%s: Transmit timeout, status %2.2x %4.4x.\n",
-			   dev->name, inb(ioaddr + ChipCmd), inw(ioaddr + IntrStatus));
 	/* Emit info to figure out what went wrong. */
+	printk("%s: Tx queue start entry %d  dirty entry %d.\n",
+		   dev->name, tp->cur_tx, tp->dirty_tx);
 	for (i = 0; i < NUM_TX_DESC; i++)
 		printk(KERN_DEBUG"%s:  Tx descriptor %d is %8.8x.%s\n",
-			   dev->name, i, inl(ioaddr + TxStat0 + i*4),
+			   dev->name, i, inl(ioaddr + TxStatus0 + i*4),
 			   i == tp->dirty_tx % NUM_TX_DESC ? " (queue head)" : "");
-	if (tp->chip_id == 0x8129) {
-		int mii_reg;
-		printk(KERN_DEBUG"%s: MII #%d registers are:", dev->name, tp->phys[0]);
-		for (mii_reg = 0; mii_reg < 8; mii_reg++)
-			printk(" %4.4x", mdio_read(ioaddr, tp->phys[0], mii_reg));
-		printk(".\n");
-	} else {
-		printk(KERN_DEBUG"%s: MII status register is %4.4x.\n",
-			   dev->name, inw(ioaddr + BMSR));
-	}
+	printk(KERN_DEBUG"%s: MII #%d registers are:", dev->name, tp->phys[0]);
+	for (mii_reg = 0; mii_reg < 8; mii_reg++)
+		printk(" %4.4x", mdio_read(dev, tp->phys[0], mii_reg));
+	printk(".\n");
 
 	/* Soft reset the chip. */
 	outb(CmdReset, ioaddr + ChipCmd);
+	/* Check that the chip has finished the reset. */
+	for (i = 1000; i > 0; i--)
+		if ((inb(ioaddr + ChipCmd) & CmdReset) == 0)
+			break;
 	for (i = 0; i < 6; i++)
 		outb(dev->dev_addr[i], ioaddr + MAC0 + i);
 
+	outb(0x00, ioaddr + Cfg9346);
+	tp->cur_rx = 0;
+	/* Must enable Tx/Rx before setting transfer thresholds! */
+	outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd);
+	outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) | (RX_DMA_BURST<<8),
+		 ioaddr + RxConfig);
+	outl((TX_DMA_BURST<<8), ioaddr + TxConfig);
+	set_rx_mode(dev);
 	{							/* Save the unsent Tx packets. */
 		struct sk_buff *saved_skb[NUM_TX_DESC], *skb;
-		int j = 0;
-		for (; tp->cur_tx - tp->dirty_tx > 0 ; tp->dirty_tx++)
-			saved_skb[j++] = tp->tx_skbuff[tp->dirty_tx % NUM_TX_DESC];
+		int j;
+		for (j = 0; tp->cur_tx - tp->dirty_tx > 0 ; j++, tp->dirty_tx++)
+			saved_skb[j] = tp->tx_skbuff[tp->dirty_tx % NUM_TX_DESC];
 		tp->dirty_tx = tp->cur_tx = 0;
 
 		for (i = 0; i < j; i++) {
@@ -785,27 +866,20 @@
 			} else
 				outl(virt_to_bus(skb->data), ioaddr + TxAddr0 + i*4);
 			/* Note: the chip doesn't have auto-pad! */
-			outl(((TX_FIFO_THRESH<<11) & 0x003f0000) |
-				 (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN),
-				 ioaddr + TxStat0 + i*4);
+			outl(tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN),
+				 ioaddr + TxStatus0 + i*4);
 		}
 		tp->cur_tx = i;
 		while (i < NUM_TX_DESC)
 			tp->tx_skbuff[i] = 0;
 		if (tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */
 			dev->tbusy = 0;
+			tp->tx_full = 0;
 		} else {
 			tp->tx_full = 1;
 		}
 	}
 
-	/* Must enable Tx/Rx before setting transfer thresholds! */
-	set_rx_mode(dev);
-	outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd);
-	outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) | (RX_DMA_BURST<<8),
-		 ioaddr + RxConfig);
-	outl((TX_DMA_BURST<<8), ioaddr + TxConfig);
-
 	dev->trans_start = jiffies;
 	tp->stats.tx_errors++;
 	/* Enable all known interrupts by setting the interrupt mask. */
@@ -823,8 +897,8 @@
 	int i;
 
 	tp->tx_full = 0;
-	tp->cur_rx = tp->cur_tx = 0;
-	tp->dirty_rx = tp->dirty_tx = 0;
+	tp->cur_rx = 0;
+	tp->dirty_tx = tp->cur_tx = 0;
 
 	for (i = 0; i < NUM_TX_DESC; i++) {
 		tp->tx_skbuff[i] = 0;
@@ -836,7 +910,7 @@
 rtl8129_start_xmit(struct sk_buff *skb, struct device *dev)
 {
 	struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	int entry;
 
 	/* Block a timer-based transmit from overlapping.  This could better be
@@ -858,20 +932,19 @@
 	} else
 		outl(virt_to_bus(skb->data), ioaddr + TxAddr0 + entry*4);
 	/* Note: the chip doesn't have auto-pad! */
-	outl(((TX_FIFO_THRESH<<11) & 0x003f0000) |
-		 (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN),
-		 ioaddr + TxStat0 + entry*4);
+	outl(tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN),
+		 ioaddr + TxStatus0 + entry*4);
 
 	if (++tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */
-		dev->tbusy = 0;
+		clear_bit(0, (void*)&dev->tbusy);
 	} else {
 		tp->tx_full = 1;
 	}
 
 	dev->trans_start = jiffies;
 	if (rtl8129_debug > 4)
-		printk(KERN_DEBUG"%s: Queued Tx packet at %p size %ld to slot %d.\n",
-			   dev->name, skb->data, skb->len, entry);
+		printk(KERN_DEBUG"%s: Queued Tx packet at %p size %d to slot %d.\n",
+			   dev->name, skb->data, (int)skb->len, entry);
 
 	return 0;
 }
@@ -881,23 +954,26 @@
 static void rtl8129_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
 {
 	struct device *dev = (struct device *)dev_instance;
-	struct rtl8129_private *tp;
-	int ioaddr, boguscnt = max_interrupt_work;
+	struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
+	int boguscnt = max_interrupt_work;
 	int status;
+	long ioaddr = dev->base_addr;
 
-	if (dev == NULL) {
-		printk (KERN_ERR"rtl8139_interrupt(): IRQ %d for unknown device.\n",
-				irq);
+#if defined(__i386__)
+	/* A lock to prevent simultaneous entry bug on Intel SMP machines. */
+	if (test_and_set_bit(0, (void*)&dev->interrupt)) {
+		printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n",
+			   dev->name);
+		dev->interrupt = 0;	/* Avoid halting machine. */
 		return;
 	}
-
-	ioaddr = dev->base_addr;
-	tp = (struct rtl8129_private *)dev->priv;
-	if (test_and_set_bit(0, (void*)&tp->in_interrupt)) {
+#else
+	if (dev->interrupt) {
 		printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
 		return;
 	}
 	dev->interrupt = 1;
+#endif
 
 	do {
 		status = inw(ioaddr + IntrStatus);
@@ -920,9 +996,9 @@
 
 			for (dirty_tx = tp->dirty_tx; dirty_tx < tp->cur_tx; dirty_tx++) {
 				int entry = dirty_tx % NUM_TX_DESC;
-				int txstatus = inl(ioaddr + TxStat0 + entry*4);
+				int txstatus = inl(ioaddr + TxStatus0 + entry*4);
 
-				if ( ! (txstatus & TxHostOwns))
+				if ( ! (txstatus & (TxStatOK | TxUnderrun | TxAborted)))
 					break;			/* It still hasn't been Txed */
 
 				/* Note: TxCarrierLost is always asserted at 100mbps. */
@@ -949,7 +1025,9 @@
 					/* No count for tp->stats.tx_deferred */
 #endif
 					if (txstatus & TxUnderrun) {
-						/* Todo: increase the Tx FIFO threshold. */
+						/* Add 64 to the Tx FIFO threshold. */
+						if (tp->tx_flag <  0x00300000)
+							tp->tx_flag += 0x00020000;
 						tp->stats.tx_fifo_errors++;
 					}
 					tp->stats.collisions += (txstatus >> 24) & 15;
@@ -960,7 +1038,7 @@
 				}
 
 				/* Free the original skb. */
-				dev_kfree_skb(tp->tx_skbuff[entry], FREE_WRITE);
+				dev_free_skb(tp->tx_skbuff[entry]);
 				tp->tx_skbuff[entry] = 0;
 			}
 
@@ -972,8 +1050,7 @@
 			}
 #endif
 
-			if (tp->tx_full && dev->tbusy
-				&& dirty_tx > tp->cur_tx - NUM_TX_DESC) {
+			if (tp->tx_full  &&  dirty_tx > tp->cur_tx - NUM_TX_DESC) {
 				/* The ring is no longer full, clear tbusy. */
 				tp->tx_full = 0;
 				dev->tbusy = 0;
@@ -986,10 +1063,29 @@
 		/* Check uncommon events with one test. */
 		if (status & (PCIErr|PCSTimeout |RxUnderrun|RxOverflow|RxFIFOOver
 					  |TxErr|RxErr)) {
+			if (rtl8129_debug > 2)
+				printk(KERN_NOTICE"%s: Abnormal interrupt, status %8.8x.\n",
+					   dev->name, status);
+
+			if (status == 0xffffffff)
+				break;
 			/* Update the error count. */
 			tp->stats.rx_missed_errors += inl(ioaddr + RxMissed);
 			outl(0, ioaddr + RxMissed);
 
+			if ((status & RxUnderrun)  &&
+				(rtl_cap_tbl[tp->chip_id] & HAS_LNK_CHNG)) {
+				/* Really link-change on new chips. */
+				int lpar = inw(ioaddr + NWayLPAR);
+				int duplex = (lpar&0x0100)||(lpar & 0x01C0) == 0x0040; 
+				if (tp->full_duplex != duplex) {
+					tp->full_duplex = duplex;
+					outb(0xC0, ioaddr + Cfg9346);
+					outb(tp->full_duplex ? 0x60 : 0x20, ioaddr + Config1);
+					outb(0x00, ioaddr + Cfg9346);
+				}
+				status &= ~RxUnderrun;
+			}
 			if (status & (RxUnderrun | RxOverflow | RxErr | RxFIFOOver))
 				tp->stats.rx_errors++;
 
@@ -1000,7 +1096,14 @@
 				tp->cur_rx = inw(ioaddr + RxBufAddr) % RX_BUF_LEN;
 				outw(tp->cur_rx - 16, ioaddr + RxBufPtr);
 			}
-			/* Error sources cleared above. */
+			if (status & PCIErr) {
+				u32 pci_cmd_status;
+				pcibios_read_config_dword(tp->pci_bus, tp->pci_devfn,
+										  PCI_COMMAND, &pci_cmd_status);
+
+				printk(KERN_ERR "%s: PCI Bus error %4.4x.\n",
+					   dev->name, pci_cmd_status);
+			}
 		}
 		if (--boguscnt < 0) {
 			printk(KERN_WARNING"%s: Too much work at interrupt, "
@@ -1016,18 +1119,20 @@
 		printk(KERN_DEBUG"%s: exiting interrupt, intr_status=%#4.4x.\n",
 			   dev->name, inl(ioaddr + IntrStatus));
 
+#if defined(__i386__)
+	clear_bit(0, (void*)&dev->interrupt);
+#else
 	dev->interrupt = 0;
-	clear_bit(0, (void*)&tp->in_interrupt);
+#endif
 	return;
 }
 
 /* The data sheet doesn't describe the Rx ring at all, so I'm guessing at the
    field alignments and semantics. */
-static int
-rtl8129_rx(struct device *dev)
+static int rtl8129_rx(struct device *dev)
 {
 	struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	unsigned char *rx_ring = tp->rx_ring;
 	u16 cur_rx = tp->cur_rx;
 
@@ -1103,9 +1208,16 @@
 					printk(".\n");
 					memset(rx_ring, 0xcc, 16);
 				}
-			} else
+			} else {
+#if 1  /* USE_IP_COPYSUM */
+				eth_copy_and_sum(skb, &rx_ring[ring_offset + 4],
+								 rx_size, 0);
+				skb_put(skb, rx_size);
+#else
 				memcpy(skb_put(skb, rx_size), &rx_ring[ring_offset + 4],
 					   rx_size);
+#endif
+			}
 			skb->protocol = eth_type_trans(skb, dev);
 			netif_rx(skb);
 #if LINUX_VERSION_CODE > 0x20119
@@ -1114,8 +1226,7 @@
 			tp->stats.rx_packets++;
 		}
 
-		cur_rx += rx_size + 4;
-		cur_rx = (cur_rx + 3) & ~3;
+		cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
 		outw(cur_rx - 16, ioaddr + RxBufPtr);
 	}
 	if (rtl8129_debug > 4)
@@ -1130,7 +1241,7 @@
 static int
 rtl8129_close(struct device *dev)
 {
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
 	int i;
 
@@ -1157,7 +1268,7 @@
 
 	for (i = 0; i < NUM_TX_DESC; i++) {
 		if (tp->tx_skbuff[i])
-			dev_kfree_skb(tp->tx_skbuff[i], FREE_WRITE);
+			dev_free_skb(tp->tx_skbuff[i]);
 		tp->tx_skbuff[i] = 0;
 	}
 	kfree(tp->rx_ring);
@@ -1173,11 +1284,33 @@
 	return 0;
 }
 
+static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+	struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
+	u16 *data = (u16 *)&rq->ifr_data;
+
+	switch(cmd) {
+	case SIOCDEVPRIVATE:		/* Get the address of the PHY in use. */
+		data[0] = tp->phys[0] & 0x3f;
+		/* Fall Through */
+	case SIOCDEVPRIVATE+1:		/* Read the specified MII register. */
+		data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
+		return 0;
+	case SIOCDEVPRIVATE+2:		/* Write the specified MII register */
+		if (!suser())
+			return -EPERM;
+		mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+		return 0;
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
 static struct enet_statistics *
 rtl8129_get_stats(struct device *dev)
 {
 	struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 
 	if (dev->start) {
 		tp->stats.rx_missed_errors += inl(ioaddr + RxMissed);
@@ -1188,88 +1321,69 @@
 }
 
 /* Set or clear the multicast filter for this adaptor.
-   Note that we only use exclusion around actually queueing the
-   new frame, not around filling tp->setup_frame.  This is non-deterministic
-   when re-entered but still correct. */
-
-/* The little-endian AUTODIN II ethernet CRC calculation.
-   N.B. Do not use for bulk data, use a table-based routine instead.
-   This is common code and should be moved to net/core/crc.c */
-static unsigned const ethernet_polynomial_le = 0xedb88320U;
-static inline unsigned ether_crc_le(int length, unsigned char *data)
+   This routine is not state sensitive and need not be SMP locked. */
+
+static unsigned const ethernet_polynomial = 0x04c11db7U;
+static inline u32 ether_crc(int length, unsigned char *data)
 {
-	unsigned int crc = 0xffffffff;	/* Initial value. */
-	while(--length >= 0) {
+    int crc = -1;
+
+    while(--length >= 0) {
 		unsigned char current_octet = *data++;
 		int bit;
-		for (bit = 8; --bit >= 0; current_octet >>= 1) {
-			if ((crc ^ current_octet) & 1) {
-				crc >>= 1;
-				crc ^= ethernet_polynomial_le;
-			} else
-				crc >>= 1;
-		}
-	}
-	return crc;
+		for (bit = 0; bit < 8; bit++, current_octet >>= 1)
+			crc = (crc << 1) ^
+				((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0);
+    }
+    return crc;
 }
 
+/* Bits in RxConfig. */
+enum rx_mode_bits {
+	AcceptErr=0x20, AcceptRunt=0x10, AcceptBroadcast=0x08,
+	AcceptMulticast=0x04, AcceptMyPhys=0x02, AcceptAllPhys=0x01,
+};
+
 static void set_rx_mode(struct device *dev)
 {
-	int ioaddr = dev->base_addr;
-	struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
-	unsigned char mc_filter[8];		 /* Multicast hash filter */
-	int i;
+	long ioaddr = dev->base_addr;
+	u32 mc_filter[2];		 /* Multicast hash filter */
+	int i, rx_mode;
 
-	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
+	if (rtl8129_debug > 3)
+		printk(KERN_DEBUG"%s:   set_rx_mode(%4.4x) done -- Rx config %8.8x.\n",
+			   dev->name, dev->flags, inl(ioaddr + RxConfig));
+
+	/* Note: do not reorder, GCC is clever about common statements. */
+	if (dev->flags & IFF_PROMISC) {
 		/* Unconditionally log net taps. */
 		printk(KERN_NOTICE"%s: Promiscuous mode enabled.\n", dev->name);
-		memset(mc_filter, 0xff, sizeof(mc_filter));
-		outb(0x0F, ioaddr + RxConfig);
-	} else if ((dev->mc_count > 1000)  ||  (dev->flags & IFF_ALLMULTI)) {
+		rx_mode = AcceptBroadcast|AcceptMulticast|AcceptMyPhys|AcceptAllPhys;
+		mc_filter[1] = mc_filter[0] = 0xffffffff;
+	} else if ((dev->mc_count > multicast_filter_limit)
+			   ||  (dev->flags & IFF_ALLMULTI)) {
 		/* Too many to filter perfectly -- accept all multicasts. */
-		memset(mc_filter, 0xff, sizeof(mc_filter));
-		outb(0x0E, ioaddr + RxConfig);
-	} else if (dev->mc_count == 0) {
-		outb(0x0A, ioaddr + RxConfig);
-		return;
+		rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+		mc_filter[1] = mc_filter[0] = 0xffffffff;
 	} else {
 		struct dev_mc_list *mclist;
-
-		memset(mc_filter, 0, sizeof(mc_filter));
+		rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+		mc_filter[1] = mc_filter[0] = 0;
 		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
 			 i++, mclist = mclist->next)
-			set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f,
-					mc_filter);
+			set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26, mc_filter);
 	}
-	/* ToDo: perhaps we need to stop the Tx and Rx process here? */
-	if (memcmp(mc_filter, tp->mc_filter, sizeof(mc_filter))) {
-		for (i = 0; i < 2; i++)
-			outl(((u32 *)mc_filter)[i], ioaddr + MAR0 + i*4);
-		memcpy(tp->mc_filter, mc_filter, sizeof(mc_filter));
-	}
-	if (rtl8129_debug > 3)
-		printk(KERN_DEBUG"%s:   set_rx_mode(%4.4x) done -- Rx config %8.8x.\n",
-			   dev->name, dev->flags, inl(ioaddr + RxConfig));
+	/* We can safely update without stopping the chip. */
+	outb(rx_mode, ioaddr + RxConfig);
+	outl(mc_filter[0], ioaddr + MAR0 + 0);
+	outl(mc_filter[1], ioaddr + MAR0 + 4);
 	return;
 }
 
 #ifdef MODULE
-
-/* An additional parameter that may be passed in... */
-static int debug = -1;
-
-int
-init_module(void)
+int init_module(void)
 {
-	int cards_found;
-
-	if (debug >= 0)
-		rtl8129_debug = debug;
-
-	root_rtl8129_dev = NULL;
-	cards_found = rtl8139_probe(0);
-
-	return cards_found ? 0 : -ENODEV;
+	return rtl8139_probe(0);
 }
 
 void
@@ -1279,9 +1393,13 @@
 
 	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
 	while (root_rtl8129_dev) {
-		next_dev = ((struct rtl8129_private *)root_rtl8129_dev->priv)->next_module;
+		struct rtl8129_private *tp =
+			(struct rtl8129_private *)root_rtl8129_dev->priv;
+		next_dev = tp->next_module;
 		unregister_netdev(root_rtl8129_dev);
-		release_region(root_rtl8129_dev->base_addr, RTL8129_TOTAL_SIZE);
+		release_region(root_rtl8129_dev->base_addr,
+					   pci_tbl[tp->chip_id].io_size);
+		kfree(tp);
 		kfree(root_rtl8129_dev);
 		root_rtl8129_dev = next_dev;
 	}
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/sdla.c linux/drivers/net/sdla.c
--- linux.vanilla/drivers/net/sdla.c	Sun Jun 21 18:41:06 1998
+++ linux/drivers/net/sdla.c	Mon Dec 14 18:34:34 1998
@@ -452,7 +452,7 @@
          save_flags(pflags);
          cli();
          SDLA_WINDOW(dev, window);
-         waiting = ((volatile)(cmd_buf->opp_flag));
+         waiting = ((volatile int)(cmd_buf->opp_flag));
          restore_flags(pflags);
       }
    }
@@ -566,11 +566,12 @@
    flp->dlci[i] = -*(short *)(master->dev_addr);
    master->mtu = slave->mtu;
 
-   if (slave->start)
+   if (slave->start) {
       if (flp->config.station == FRAD_STATION_CPE)
          sdla_reconfig(slave);
       else
          sdla_cmd(slave, SDLA_ADD_DLCI, 0, 0, master->dev_addr, sizeof(short), NULL, NULL);
+   }
 
    return(0);
 }
@@ -594,11 +595,12 @@
 
    MOD_DEC_USE_COUNT;
 
-   if (slave->start)
+   if (slave->start) {
       if (flp->config.station == FRAD_STATION_CPE)
          sdla_reconfig(slave);
       else
          sdla_cmd(slave, SDLA_DELETE_DLCI, 0, 0, master->dev_addr, sizeof(short), NULL, NULL);
+   }
 
    return(0);
 }
@@ -623,13 +625,14 @@
 
    ret = SDLA_RET_OK;
    len = sizeof(struct dlci_conf);
-   if (slave->start)
+   if (slave->start) {
       if (get)
          ret = sdla_cmd(slave, SDLA_READ_DLCI_CONFIGURATION, abs(flp->dlci[i]), 0,  
                      NULL, 0, &dlp->config, &len);
       else
          ret = sdla_cmd(slave, SDLA_SET_DLCI_CONFIGURATION, abs(flp->dlci[i]), 0,  
                      &dlp->config, sizeof(struct dlci_conf) - 4 * sizeof(short), NULL, NULL);
+   }
 
    return(ret == SDLA_RET_OK ? 0 : -EIO);
 }
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/skeleton.c linux/drivers/net/skeleton.c
--- linux.vanilla/drivers/net/skeleton.c	Sun Jun 21 18:41:04 1998
+++ linux/drivers/net/skeleton.c	Wed Dec  9 20:02:31 1998
@@ -362,15 +362,6 @@
 		dev->trans_start = jiffies;
 	}
 	/*
-	 * If some higher layer thinks we've missed an tx-done interrupt
-	 * we are passed NULL. Caution: dev_tint() handles the cli()/sti()
-	 * itself.
-	 */
-	if (skb == NULL) {
-		dev_tint(dev);
-		return 0;
-	}
-	/*
 	 * Block a timer-based transmit from overlapping. This could better be
 	 * done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
 	 */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/tulip.c linux/drivers/net/tulip.c
--- linux.vanilla/drivers/net/tulip.c	Sun Dec  6 00:14:40 1998
+++ linux/drivers/net/tulip.c	Fri Dec 11 20:18:43 1998
@@ -5,9 +5,9 @@
 	This software may be used and distributed according to the terms
 	of the GNU Public License, incorporated herein by reference.
 
-	This driver is for the Digital "Tulip" ethernet adapter interface.
+	This driver is for the Digital "Tulip" Ethernet adapter interface.
 	It should work with most DEC 21*4*-based chips/ethercards, as well as
-	PNIC and MXIC chips.
+	with work-alike chips from Lite-On (PNIC) and Macronix (MXIC) and ASIX.
 
 	The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
 	Center of Excellence in Space Data and Information Sciences
@@ -18,7 +18,7 @@
 */
 
 #define SMP_CHECK
-static const char version[] = "tulip.c:v0.89H 5/23/98 becker@cesdis.gsfc.nasa.gov\n";
+static const char version[] = "tulip.c:v0.90 10/20/98 becker@cesdis.gsfc.nasa.gov\n";
 
 /* A few user-configurable values. */
 
@@ -63,14 +63,20 @@
 /* Operational parameters that usually are not changed. */
 /* Time in jiffies before concluding the transmitter is hung. */
 #define TX_TIMEOUT  (4*HZ)
+#define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
+/* This is a mysterious value that can be written to CSR11 in the 21040 (only)
+   to support a pre-NWay full-duplex signaling mechanism using short frames.
+   No one knows what it should be, but if left at its default value some
+   10base2(!) packets trigger a full-duplex-request interrupt. */
+#define FULL_DUPLEX_MAGIC	0x6969
 
 #include <linux/config.h>
+#include <linux/version.h>
 #ifdef MODULE
 #ifdef MODVERSIONS
 #include <linux/modversions.h>
 #endif
 #include <linux/module.h>
-#include <linux/version.h>
 #else
 #define MOD_INC_USE_COUNT
 #define MOD_DEC_USE_COUNT
@@ -80,87 +86,65 @@
 #include <linux/sched.h>
 #include <linux/string.h>
 #include <linux/timer.h>
-#include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
 #include <linux/malloc.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
-#include <linux/bios32.h>
-#include <asm/processor.h>		/* Processor type for cache alignment. */
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
+#include <asm/processor.h>		/* Processor type for cache alignment. */
+#include <asm/bitops.h>
+#include <asm/io.h>
 
-/* Kernel compatibility defines, common to David Hind's PCMCIA package.
+/* Kernel compatibility defines, some common to David Hind's PCMCIA package.
    This is only in the support-all-kernels source code. */
-#include <linux/version.h>		/* Evil, but neccessary */
-
-#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10300
-#define RUN_AT(x) (x)			/* What to put in timer->expires.  */
-#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC)
-#define virt_to_bus(addr)  ((unsigned long)addr)
-#define bus_to_virt(addr) ((void*)addr)
 
-#else  /* 1.3.0 and later */
-#define RUN_AT(x) (jiffies + (x))
-#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
+#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115
+MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
+MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver");
+MODULE_PARM(debug, "i");
+MODULE_PARM(max_interrupt_work, "i");
+MODULE_PARM(reverse_probe, "i");
+MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
 #endif
 
-#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10338
-#ifdef MODULE
-#if !defined(CONFIG_MODVERSIONS) && !defined(__NO_VERSION__)
-char kernel_version[] = UTS_RELEASE;
-#endif
-#else
-#undef MOD_INC_USE_COUNT
-#define MOD_INC_USE_COUNT
-#undef MOD_DEC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#endif
-#endif /* 1.3.38 */
+#define RUN_AT(x) (jiffies + (x))
 
-#if (LINUX_VERSION_CODE >= 0x10344)
-#define NEW_MULTICAST
-#include <linux/delay.h>
-#endif
 #if (LINUX_VERSION_CODE >= 0x20100)
 char kernel_version[] = UTS_RELEASE;
 #endif
-#ifdef SA_SHIRQ
-#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs)
-#else
-#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs)
-#endif
 
-#if (LINUX_VERSION_CODE < 0x20123)
+#if LINUX_VERSION_CODE < 0x20123
 #define hard_smp_processor_id() smp_processor_id()
 #define test_and_set_bit(val, addr) set_bit(val, addr)
 #endif
-
-/* This my implementation of shared IRQs, now only used for 1.2.13. */
-#ifdef HAVE_SHARED_IRQ
-#define USE_SHARED_IRQ
-#include <linux/shared_irq.h>
+#if LINUX_VERSION_CODE <= 0x20139
+#define	net_device_stats enet_statistics
+#else
+#define NETSTATS_VER2
 #endif
-
-/* The total size is unusually large: The 21040 aligns each of its 16
-   longword-wide registers on a quadword boundary. */
-#define TULIP_TOTAL_SIZE 0x80
-
-#ifdef HAVE_DEVLIST
-struct netdev_entry tulip_drv =
-{"Tulip", tulip_pci_probe, TULIP_TOTAL_SIZE, NULL};
+#if LINUX_VERSION_CODE < 0x20155  ||  defined(CARDBUS)
+/* Grrrr, the PCI code changed, but did not consider CardBus... */
+#include <linux/bios32.h>
+#define PCI_SUPPORT_VER1
+#else
+#define PCI_SUPPORT_VER2
+#endif
+#if LINUX_VERSION_CODE < 0x20159
+#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE);
+#else
+#define dev_free_skb(skb) dev_kfree_skb(skb);
 #endif
 
+#define tulip_debug debug
 #ifdef TULIP_DEBUG
-int tulip_debug = TULIP_DEBUG;
+static int tulip_debug = TULIP_DEBUG;
 #else
-int tulip_debug = 1;
+static int tulip_debug = 1;
 #endif
 
 /*
@@ -170,17 +154,27 @@
 
 This device driver is designed for the DECchip "Tulip", Digital's
 single-chip ethernet controllers for PCI.  Supported members of the family
-are the 21040, 21041, 21140, 21140A, 21142, and 21143.  These chips are used on
-many PCI boards including the SMC EtherPower series.
-
+are the 21040, 21041, 21140, 21140A, 21142, and 21143.  Similar work-alike
+chips from Lite-On, Macronics, ASIX, Compex and other listed below are also
+supported. 
+
+These chips are used on at least 140 unique PCI board designs.  The great
+number of chips and board designs supported is the reason for the
+driver size and complexity.  Almost of the increasing complexity is in the
+board configuration and media selection code.  There is very little
+increasing in the operational critical path length.
 
 II. Board-specific settings
 
 PCI bus devices are configured by the system at boot time, so no jumpers
 need to be set on the board.  The system BIOS preferably should assign the
 PCI INTA signal to an otherwise unused system IRQ line.
-Note: Kernel versions earlier than 1.3.73 do not support shared PCI
-interrupt lines.
+
+Some boards have EEPROMs tables with default media entry.  The factory default
+is usually "autoselect".  This should only be overridden when using
+transceiver connections without link beat e.g. 10base2 or AUI, or (rarely!)
+for forcing full-duplex when used with old link partners that do not do
+autonegotiation. 
 
 III. Driver operation
 
@@ -228,7 +222,9 @@
 
 IV. Notes
 
-Thanks to Duke Kamstra of SMC for providing an EtherPower board.
+Thanks to Duke Kamstra of SMC for long ago providing an EtherPower board.
+Greg LaPolla at Linksys provided PNIC and other Linksys boards.
+Znyx provided a four-port card for testing.
 
 IVb. References
 
@@ -238,88 +234,105 @@
 
 IVc. Errata
 
-The DEC databook doesn't document which Rx filter settings accept broadcast
-packets.  Nor does it document how to configure the part to configure the
-serial subsystem for normal (vs. loopback) operation or how to have it
-autoswitch between internal 10baseT, SIA and AUI transceivers.
-
+The old DEC databooks were light on details.
 The 21040 databook claims that CSR13, CSR14, and CSR15 should each be the last
-register of the set CSR12-15 written.  Hmmm, now how is that possible?  */
+register of the set CSR12-15 written.  Hmmm, now how is that possible?
 
+The DEC SROM format is very badly designed not precisely defined, leading to
+part of the media selection junkheap below.  Some boards do not have EEPROM
+media tables and need to be patched up.  Worse, other boards use the DEC
+design kit media table when it isn't correct for their board.
 
-/* A few values that may be tweaked. */
-#define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
+We cannot use MII interrupts because there is no defined GPIO pin to attach
+them.
 
-/* This is a mysterious value that can be written to CSR11 in the 21040 (only)
-   to support a pre-NWay full-duplex signaling mechanism using short frames.
-   No one knows what it should be, but if left at its default value some
-   10base2(!) packets trigger a full-duplex-request interrupt. */
-#define FULL_DUPLEX_MAGIC	0x6969
+*/
 
 #ifndef PCI_VENDOR_ID_DEC		/* Now defined in linux/pci.h */
 #define PCI_VENDOR_ID_DEC			0x1011
-#define PCI_DEVICE_ID_TULIP			0x0002		/* 21040. */
-#define PCI_DEVICE_ID_TULIP_FAST	0x0009		/* 21140. */
-#endif
-
-#ifndef PCI_DEVICE_ID_DEC_TULIP_PLUS
-#define PCI_DEVICE_ID_DEC_TULIP_PLUS	0x0014		/* 21041. */
-#endif
-#ifndef PCI_DEVICE_ID_DEC_TULIP_21142
-#define PCI_DEVICE_ID_DEC_TULIP_21142	0x0019
 #endif
 
-#ifndef PCI_VENDOR_ID_LITEON
-#define PCI_VENDOR_ID_LITEON	0x11AD
-#endif
+static struct device *
+tulip_probe1(int pci_bus, int pci_devfn, struct device *dev, long ioaddr,
+			 int irq, int chip_idx, int board_idx);
+
+/* This table drives the PCI probe routines.  It's mostly boilerplate in all
+   of the drivers, and will likely be provided by some future kernel.
+   Note the matching code -- the first table entry matchs all 56** cards but
+   second only the 1234 card.
+*/
+enum pci_flags_bit {
+	PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
+	PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3,
+};
+#define PCI_ADDR0_IO (PCI_USES_IO|PCI_ADDR0)
 
-#ifndef PCI_VENDOR_ID_MXIC
-#define	PCI_VENDOR_ID_MXIC		0x10d9
-#define PCI_DEVICE_ID_MX98713	0x0512
-#define PCI_DEVICE_ID_MX98715	0x0531
-#define PCI_DEVICE_ID_MX98725	0x0531
-#endif
+struct pci_id_info {
+	const char *name;
+	u16	vendor_id, device_id, device_id_mask, flags;
+	int io_size, min_latency;
+	struct device *(*probe1)(int pci_bus, int pci_devfn, struct device *dev,
+							 long ioaddr, int irq, int chip_idx, int fnd_cnt);
+};
+#ifndef CARDBUS
+static struct pci_id_info pci_tbl[] = {
+  { "Digital DC21040 Tulip",
+	0x1011, 0x0002, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 },
+  { "Digital DC21041 Tulip",
+	0x1011, 0x0014, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 },
+  { "Digital DS21140 Tulip",
+	0x1011, 0x0009, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 },
+  { "Digital DS21143 Tulip",
+	0x1011, 0x0019, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 },
+  { "Lite-On 82c168 PNIC",
+	0x11AD, 0x0002, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 },
+  { "Macronix 98713 PMAC",
+	0x10d9, 0x0512, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 },
+  { "Macronix 98715 PMAC",
+	0x10d9, 0x0531, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 },
+  { "Macronix 98725 PMAC",
+	0x10d9, 0x0531, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 },
+  { "ASIX AX88140",
+	0x125B, 0x1400, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 },
+  {0},
+};
+#endif CARD_BUS
 
-/* The rest of these values should never change. */
+/* This table use during operation for capabilities and media timer. */
 
 static void tulip_timer(unsigned long data);
 static void t21142_timer(unsigned long data);
 static void mxic_timer(unsigned long data);
 static void pnic_timer(unsigned long data);
 
-/* A table describing the chip types. */
-enum tbl_flag { HAS_MII=1, HAS_MEDIA_TABLE = 2, CSR12_IN_SROM = 4,};
+enum tbl_flag {
+	HAS_MII=1, HAS_MEDIA_TABLE=2, CSR12_IN_SROM=4, ALWAYS_CHECK_MII=8,
+	HAS_ACPI=0x10,
+};
 static struct tulip_chip_table {
-	int vendor_id, device_id;
 	char *chip_name;
 	int io_size;
 	int valid_intrs;			/* CSR7 interrupt enable settings */
 	int flags;
 	void (*media_timer)(unsigned long data);
 } tulip_tbl[] = {
-  { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP,
-	"Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer },
-  { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_PLUS,
-	"Digital DC21041 Tulip", 128, 0x0001ebef, HAS_MEDIA_TABLE, tulip_timer },
-  { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_FAST,
-	"Digital DS21140 Tulip", 128, 0x0001ebef,
-	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM,
-	tulip_timer },
-  { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_21142,
-	"Digital DS21142/3 Tulip", 256, 0x0801fbff,
-	HAS_MII | HAS_MEDIA_TABLE, t21142_timer },
-  { PCI_VENDOR_ID_LITEON, 0x0002,
-	"Lite-On 82c168 PNIC", 256, 0x0001ebef, HAS_MII, pnic_timer },
-  { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98713,
-	"Macronix 98713 PMAC", 128, 0x0001ebef,
-	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer /* Tulip-like! */ },
-  { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98715,
-	"Macronix 98715 PMAC", 256, 0x0001ebef, HAS_MEDIA_TABLE, mxic_timer },
-  { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98725,
-	"Macronix 98725 PMAC", 256, 0x0001ebef, HAS_MEDIA_TABLE, mxic_timer },
-  { 0x125B, 0x1400, "ASIX AX88140", 128, 0x0001fbff,
+  { "Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer },
+  { "Digital DC21041 Tulip", 128, 0x0001ebef, HAS_MEDIA_TABLE, tulip_timer },
+  { "Digital DS21140 Tulip", 128, 0x0001ebef,
 	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer },
-  {0, 0, 0, 0},
+  { "Digital DS21143 Tulip", 128, 0x0801fbff,
+	HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_ACPI, t21142_timer },
+  { "Lite-On 82c168 PNIC", 256, 0x0001ebef,
+	HAS_MII, pnic_timer },
+  { "Macronix 98713 PMAC", 128, 0x0001ebef,
+	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer },
+  { "Macronix 98715 PMAC", 256, 0x0001ebef,
+	HAS_MEDIA_TABLE, mxic_timer },
+  { "Macronix 98725 PMAC", 256, 0x0001ebef,
+	HAS_MEDIA_TABLE, mxic_timer },
+  { "ASIX AX88140", 128, 0x0001fbff,
+	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer },
+  {0},
 };
 /* This matches the table above. */
 enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3,
@@ -367,6 +380,13 @@
 	u32 buffer1, buffer2;				/* We use only buffer 1.  */
 };
 
+/* Ring-wrap flag in length field, use for last ring entry.
+	0x01000000 means chain on buffer2 address,
+	0x02000000 means use the ring start address in CSR2/3.
+   Note: Some work-alike chips do not function correctly in chained mode.
+*/
+#define DESC_RING_WRAP 0x02000000
+
 struct medialeaf {
 	u8 type;
 	u8 media;
@@ -401,11 +421,7 @@
 	u32 setup_frame[48];		/* Pseudo-Tx frame to init address table. */
 	int chip_id;
 	int revision;
-#if LINUX_VERSION_CODE > 0x20139
 	struct net_device_stats stats;
-#else
-	struct enet_statistics stats;
-#endif
 	struct timer_list timer;	/* Media selection timer. */
 	int interrupt;				/* In-interrupt flag. */
 #ifdef SMP_CHECK
@@ -428,13 +444,11 @@
 	signed char phys[4], mii_cnt;		/* MII device addresses. */
 	struct mediatable *mtable;
 	int cur_index;						/* Current media index. */
-	unsigned char pci_bus, pci_dev_fn;
+	int saved_if_port;
+	unsigned char pci_bus, pci_devfn;
 	int pad0, pad1;						/* Used for 8-byte alignment */
 };
 
-static struct device *tulip_probe1(int pci_bus, int pci_devfn,
-								   struct device *dev,
-								   int chip_id, int options);
 static void parse_eeprom(struct device *dev);
 static int read_eeprom(long ioaddr, int location);
 static int mdio_read(struct device *dev, int phy_id, int location);
@@ -446,51 +460,34 @@
 static void tulip_init_ring(struct device *dev);
 static int tulip_start_xmit(struct sk_buff *skb, struct device *dev);
 static int tulip_rx(struct device *dev);
-static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs);
+static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
 static int tulip_close(struct device *dev);
-static struct enet_statistics *tulip_get_stats(struct device *dev);
+static struct net_device_stats *tulip_get_stats(struct device *dev);
 #ifdef HAVE_PRIVATE_IOCTL
 static int private_ioctl(struct device *dev, struct ifreq *rq, int cmd);
 #endif
-#ifdef NEW_MULTICAST
 static void set_rx_mode(struct device *dev);
-#else
-static void set_rx_mode(struct device *dev, int num_addrs, void *addrs);
-#endif
 
 
 
-/* A list of all installed Tulip devices, for removing the driver module. */
+/* A list of all installed Tulip devices. */
 static struct device *root_tulip_dev = NULL;
 
-/* This 21040 probe no longer uses a large fixed contiguous Rx buffer region,
-   but now receives directly into full-sized skbuffs that are allocated
-   at open() time.
-   This allows the probe routine to use the old driver initialization
-   interface. */
-
+#ifndef CARDBUS
 int tulip_probe(struct device *dev)
 {
 	int cards_found = 0;
-	static int pci_index = 0;	/* Static, for multiple probe calls. */
+	int pci_index = 0;
 	unsigned char pci_bus, pci_device_fn;
 
-	/* Ideally we would detect all network cards in slot order.  That would
-	   be best done a central PCI probe dispatch, which wouldn't work
-	   well with the current structure.  So instead we detect just the
-	   Tulip cards in slot order. */
-
-#if LINUX_VERSION_CODE >= 0x20155
-	if (! pci_present())
-		return -ENODEV;
-#else
-	if (! pcibios_present())
+	if ( ! pcibios_present())
 		return -ENODEV;
-#endif
+
 	for (;pci_index < 0xff; pci_index++) {
 		u16 vendor, device, pci_command, new_command;
-		u32 pci_ioaddr;
-		int chip_idx = 0;
+		int chip_idx;
+		int irq;
+		long ioaddr;
 
 		if (pcibios_find_class
 			(PCI_CLASS_NETWORK_ETHERNET << 8,
@@ -505,50 +502,55 @@
 		pcibios_read_config_word(pci_bus, pci_device_fn,
 								 PCI_DEVICE_ID, &device);
 
-		for (chip_idx = 0; tulip_tbl[chip_idx].chip_name; chip_idx++)
-			if (vendor == tulip_tbl[chip_idx].vendor_id  &&
-				device == tulip_tbl[chip_idx].device_id)
+		for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++)
+			if (vendor == pci_tbl[chip_idx].vendor_id
+				&& (device & pci_tbl[chip_idx].device_id_mask) ==
+				pci_tbl[chip_idx].device_id)
 				break;
-		if (tulip_tbl[chip_idx].chip_name == 0) {
-			if (vendor == PCI_VENDOR_ID_DEC  ||
-				vendor == PCI_VENDOR_ID_LITEON)
-				printk(KERN_INFO "Unknown Tulip-style PCI ethernet chip type"
-					   " %4.4x %4.4x"" detected: not configured.\n",
-					   vendor, device);
+		if (pci_tbl[chip_idx].vendor_id == 0)
 			continue;
-		}
-#if LINUX_VERSION_CODE >= 0x20155
-		pci_ioaddr = pci_find_slot(pci_bus, pci_device_fn)->base_address[0];
-#else
-		pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0,
-								  &pci_ioaddr);
+
+		{
+#if defined(PCI_SUPPORT_VER2)
+			struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn);
+			ioaddr = pdev->base_address[0] & ~3;
+			irq = pdev->irq;
+#else
+			u32 pci_ioaddr;
+			u8 pci_irq_line;
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+									  PCI_BASE_ADDRESS_0, &pci_ioaddr);
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+									 PCI_INTERRUPT_LINE, &pci_irq_line);
+			ioaddr = pci_ioaddr & ~3;
+			irq = pci_irq_line;
 #endif
-		/* Remove I/O space marker in bit 0. */
-		pci_ioaddr &= ~3;
+		}
 
-		if (tulip_debug > 2)
-			printk(KERN_DEBUG "Found %s at I/O %#x.\n",
-				   tulip_tbl[chip_idx].chip_name, pci_ioaddr);
+		if (debug > 2)
+			printk(KERN_INFO "Found %s at PCI I/O address %#lx.\n",
+				   pci_tbl[chip_idx].name, ioaddr);
 
-		if (check_region(pci_ioaddr, tulip_tbl[chip_idx].io_size))
+		if (check_region(ioaddr, pci_tbl[chip_idx].io_size))
 			continue;
 
 		pcibios_read_config_word(pci_bus, pci_device_fn,
 								 PCI_COMMAND, &pci_command);
 		new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO;
 		if (pci_command != new_command) {
-			printk(KERN_INFO "  The PCI BIOS has not enabled this"
-				   " device!  Updating PCI command %4.4x->%4.4x.\n",
-				   pci_command, new_command);
+			printk(KERN_INFO "  The PCI BIOS has not enabled the"
+				   " device at %d/%d!  Updating PCI command %4.4x->%4.4x.\n",
+				   pci_bus, pci_device_fn, pci_command, new_command);
 			pcibios_write_config_word(pci_bus, pci_device_fn,
 									  PCI_COMMAND, new_command);
 		}
 
-		dev = tulip_probe1(pci_bus, pci_device_fn, dev, chip_idx, cards_found);
+		dev = pci_tbl[chip_idx].probe1(pci_bus, pci_device_fn, dev, ioaddr,
+									   irq, chip_idx, cards_found);
 
 		/* Get and check the bus-master and latency values. */
 		if (dev) {
-			unsigned char pci_latency;
+			u8 pci_latency;
 			pcibios_read_config_byte(pci_bus, pci_device_fn,
 									 PCI_LATENCY_TIMER, &pci_latency);
 			if (pci_latency < 10) {
@@ -557,30 +559,22 @@
 					   pci_latency);
 				pcibios_write_config_byte(pci_bus, pci_device_fn,
 										  PCI_LATENCY_TIMER, 64);
-			} else if (tulip_debug > 1)
-				printk(KERN_INFO "  PCI latency timer (CFLT) is %#x, "
-					   " PCI command is %4.4x.\n",
-					   pci_latency, new_command);
-			/* Bring the 21143 out power-down mode. */
-			if (device == PCI_DEVICE_ID_DEC_TULIP_21142)
-				pcibios_write_config_dword(pci_bus, pci_device_fn,
-										   0x40, 0x40000000);
-			dev = 0;
-			cards_found++;
+			}
 		}
+		dev = 0;
+		cards_found++;
 	}
 
 	return cards_found ? 0 : -ENODEV;
 }
+#endif  /* not CARDBUS */
 
-static struct device *tulip_probe1(int pci_bus, int pci_device_fn,
-								   struct device *dev,
-								   int chip_id, int board_idx)
+static struct device *tulip_probe1(int pci_bus, int pci_devfn,
+								   struct device *dev, long ioaddr, int irq,
+								   int chip_idx, int board_idx)
 {
 	static int did_version = 0;			/* Already printed version info. */
 	struct tulip_private *tp;
-	long ioaddr;
-	int irq;
 	/* See note below on the multiport cards. */
 	static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'};
 	static int last_irq = 0;
@@ -593,36 +587,23 @@
 
 	dev = init_etherdev(dev, 0);
 
-#if LINUX_VERSION_CODE >= 0x20155
-	irq = pci_find_slot(pci_bus, pci_device_fn)->irq;
-	ioaddr = pci_find_slot(pci_bus, pci_device_fn)->base_address[0];
-#else
-	{
-		u8 pci_irq_line;
-		u32 pci_ioaddr;
-		pcibios_read_config_byte(pci_bus, pci_device_fn,
-								 PCI_INTERRUPT_LINE, &pci_irq_line);
-		pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0,
-								  &pci_ioaddr);
-		irq = pci_irq_line;
-		ioaddr = pci_ioaddr;
-	}
-#endif
-	/* Remove I/O space marker in bit 0. */
-	ioaddr &= ~3;
+	/* Bring the 21143 out of sleep mode.
+	   Caution: Snooze mode does not work with some boards! */
+	if (tulip_tbl[chip_idx].flags & HAS_ACPI)
+		pcibios_write_config_dword(pci_bus, pci_devfn, 0x40, 0x00000000);
 
 	printk(KERN_INFO "%s: %s at %#3lx,",
-		   dev->name, tulip_tbl[chip_id].chip_name, ioaddr);
+		   dev->name, tulip_tbl[chip_idx].chip_name, ioaddr);
 
 	/* Stop the chip's Tx and Rx processes. */
 	outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
 	/* Clear the missed-packet counter. */
 	(volatile int)inl(ioaddr + CSR8);
 
-	if (chip_id == DC21041) {
+	if (chip_idx == DC21041) {
 		if (inl(ioaddr + CSR9) & 0x8000) {
 			printk(" 21040 compatible mode,");
-			chip_id = DC21040;
+			chip_idx = DC21040;
 		} else {
 			printk(" 21041 mode,");
 		}
@@ -633,7 +614,7 @@
 	   EEPROM.
 	   */
 	sum = 0;
-	if (chip_id == DC21040) {
+	if (chip_idx == DC21040) {
 		outl(0, ioaddr + CSR9);		/* Reset the pointer with a dummy write. */
 		for (i = 0; i < 6; i++) {
 			int value, boguscnt = 100000;
@@ -643,7 +624,7 @@
 			dev->dev_addr[i] = value;
 			sum += value & 0xff;
 		}
-	} else if (chip_id == LC82C168) {
+	} else if (chip_idx == LC82C168) {
 		for (i = 0; i < 3; i++) {
 			int value, boguscnt = 100000;
 			outl(0x600 | i, ioaddr + 0x98);
@@ -705,8 +686,8 @@
 	last_irq = irq;
 
 	/* We do a request_region() only to register /proc/ioports info. */
-	/* Note that proper size is tulip_tbl[chip_id].chip_name, but... */
-	request_region(ioaddr, TULIP_TOTAL_SIZE, dev->name);
+	/* Note that proper size is tulip_tbl[chip_idx].chip_name, but... */
+	request_region(ioaddr, tulip_tbl[chip_idx].io_size, dev->name);
 
 	dev->base_addr = ioaddr;
 	dev->irq = irq;
@@ -720,8 +701,8 @@
 	root_tulip_dev = dev;
 
 	tp->pci_bus = pci_bus;
-	tp->pci_dev_fn = pci_device_fn;
-	tp->chip_id = chip_id;
+	tp->pci_devfn = pci_devfn;
+	tp->chip_id = chip_idx;
 
 #ifdef TULIP_FULL_DUPLEX
 	tp->full_duplex = 1;
@@ -753,7 +734,7 @@
 		tp->full_duplex_lock = 1;
 
 	/* This is logically part of probe1(), but too complex to write inline. */
-	if (tulip_tbl[chip_id].flags & HAS_MEDIA_TABLE)
+	if (tulip_tbl[chip_idx].flags & HAS_MEDIA_TABLE)
 		parse_eeprom(dev);
 
 	if (media_cap[tp->default_port] & MediaIsMII) {
@@ -762,9 +743,13 @@
 	} else
 		tp->to_advertise = 0x03e1;
 
-	if ((tp->mtable  &&  tp->mtable->has_mii) ||
-		( ! tp->mtable  &&  (tulip_tbl[tp->chip_id].flags & HAS_MII))) {
+	if ((tulip_tbl[chip_idx].flags & ALWAYS_CHECK_MII) ||
+		(tp->mtable  &&  tp->mtable->has_mii) ||
+		( ! tp->mtable  &&  (tulip_tbl[chip_idx].flags & HAS_MII))) {
 		int phy, phy_idx;
+		/* Clear undefined bits that confuse the MII interface. */
+		if (tp->chip_id == DC21142)
+			outl(inl(ioaddr+CSR15) & ~(0x800037c0), ioaddr+CSR15);
 		/* Find the connected MII xcvrs.
 		   Doing this in open() would allow detecting external xcvrs later,
 		   but takes much time. */
@@ -773,16 +758,18 @@
 			int mii_status = mdio_read(dev, phy, 1);
 			if (mii_status != 0xffff  &&  mii_status != 0x0000) {
 				int mii_reg0 = mdio_read(dev, phy, 0);
+				int mii_advert = mdio_read(dev, phy, 4);
 				int reg4 = ((mii_status>>6) & tp->to_advertise) | 1;
 				tp->phys[phy_idx] = phy;
 				tp->advertising[phy_idx++] = reg4;
-				printk(KERN_INFO "%s:  MII transceiver found at MDIO address "
-					   "%d, config %4.4x status %4.4x.\n",
-					   dev->name, phy, mii_reg0, mii_status);
-				if (1 || (media_cap[tp->default_port] & MediaIsMII)) {
+				printk(KERN_INFO "%s:  MII transceiver #%d "
+					   "config %4.4x status %4.4x advertising %4.4x.\n",
+					   dev->name, phy, mii_reg0, mii_status, mii_advert);
+				/* Fixup for DLink with miswired PHY. */
+				if (mii_advert != reg4) {
 					printk(KERN_DEBUG "%s:  Advertising %4.4x on PHY %d,"
 						   " previously advertising %4.4x.\n",
-						   dev->name, reg4, phy, mdio_read(dev, phy, 4));
+						   dev->name, reg4, phy, mii_advert);
 					mdio_write(dev, phy, 4, reg4);
 				}
 				/* Enable autonegotiation: some boards default to off. */
@@ -812,7 +799,7 @@
 #endif
 
 	/* Reset the xcvr interface and turn on heartbeat. */
-	switch (chip_id) {
+	switch (chip_idx) {
 	case DC21041:
 		outl(0x00000000, ioaddr + CSR13);
 		outl(0xFFFFFFFF, ioaddr + CSR14);
@@ -829,12 +816,19 @@
 			outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12);
 		break;
 	case DC21142:
-		outl(0x82420200, ioaddr + CSR6);
-		outl(0x0001, ioaddr + CSR13);
-		outl(0x0003FFFF, ioaddr + CSR14);
-		outl(0x0008, ioaddr + CSR15);
-		outl(0x0001, ioaddr + CSR13);
-		outl(0x1301, ioaddr + CSR12); /* Start NWay. */
+		if (tp->mii_cnt) {
+			outl(0x82020000, ioaddr + CSR6);
+			outl(0x0000, ioaddr + CSR13);
+			outl(0x0000, ioaddr + CSR14);
+			outl(0x820E0000, ioaddr + CSR6);
+		} else {
+			outl(0x82420200, ioaddr + CSR6);
+			outl(0x0001, ioaddr + CSR13);
+			outl(0x0003FFFF, ioaddr + CSR14);
+			outl(0x0008, ioaddr + CSR15);
+			outl(0x0001, ioaddr + CSR13);
+			outl(0x1301, ioaddr + CSR12); /* Start NWay. */
+		}
 		break;
 	case LC82C168:
 		if ( ! tp->mii_cnt) {
@@ -882,7 +876,7 @@
 							   0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ }},
   {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x031F,
 							0x1B01, 0x0000, /* 10base2,   CSR12 0x1B */
-							0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */ 
+							0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */
 							0x0B00, 0x009E, /* 10baseT,   CSR12 0x0B */
    }},
   {0, 0, 0, 0, {}}};
@@ -961,7 +955,7 @@
 	  }
 	  printk("\n");
 	}
-	
+
 	controller_index = 0;
 	if (ee_data[19] > 1) {		/* Multiport board. */
 		last_ee_data = ee_data;
@@ -1024,7 +1018,7 @@
 			   media & 0x0800 ? "Autosense" : medianame[media & 15]);
 		for (i = 0; i < count; i++) {
 			struct medialeaf *leaf = &mtable->mleaf[i];
-			
+
 			if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */
 				leaf->type = 0;
 				leaf->media = p[0] & 0x3f;
@@ -1070,13 +1064,9 @@
 #define EE_ENB			(0x4800 | EE_CS)
 
 /* Delay between EEPROM clock transitions.
-   The 1.2 code is a "nasty" timing loop, but PC compatible machines are
-   *supposed* to delay an ISA-compatible period for the SLOW_DOWN_IO macro.  */
-#ifdef _LINUX_DELAY_H
-#define eeprom_delay(nanosec)	udelay((nanosec + 999)/1000)
-#else
-#define eeprom_delay(nanosec)	do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
-#endif
+   Even at 33Mhz current PCI implementations don't overrun the EEPROM clock.
+   We add a bus turn-around to insure that this remains true. */
+#define eeprom_delay()	inl(ee_addr)
 
 /* The EEPROM commands include the alway-set leading bit. */
 #define EE_WRITE_CMD	(5 << 6)
@@ -1089,28 +1079,26 @@
 	unsigned short retval = 0;
 	long ee_addr = ioaddr + CSR9;
 	int read_cmd = location | EE_READ_CMD;
-	
+
 	outl(EE_ENB & ~EE_CS, ee_addr);
 	outl(EE_ENB, ee_addr);
-	
+
 	/* Shift the read command bits out. */
 	for (i = 10; i >= 0; i--) {
 		short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
 		outl(EE_ENB | dataval, ee_addr);
-		eeprom_delay(100);
+		eeprom_delay();
 		outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
-		eeprom_delay(150);
-		outl(EE_ENB | dataval, ee_addr);	/* Finish EEPROM a clock tick. */
-		eeprom_delay(250);
+		eeprom_delay();
 	}
 	outl(EE_ENB, ee_addr);
-	
+
 	for (i = 16; i > 0; i--) {
 		outl(EE_ENB | EE_SHIFT_CLK, ee_addr);
-		eeprom_delay(100);
+		eeprom_delay();
 		retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);
 		outl(EE_ENB, ee_addr);
-		eeprom_delay(100);
+		eeprom_delay();
 	}
 
 	/* Terminate the EEPROM access. */
@@ -1150,13 +1138,15 @@
 		long ioaddr = dev->base_addr;
 		int i = 1000;
 		outl(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0);
+		inl(ioaddr + 0xA0);
+		inl(ioaddr + 0xA0);
 		while (--i > 0)
 			if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000))
 				return retval & 0xffff;
 		return 0xffff;
 	}
 
-	/* Establish sync by sending at least 32 logic ones. */ 
+	/* Establish sync by sending at least 32 logic ones. */
 	for (i = 32; i >= 0; i--) {
 		outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
 		mdio_delay();
@@ -1201,7 +1191,7 @@
 		return;
 	}
 
-	/* Establish sync by sending 32 logic ones. */ 
+	/* Establish sync by sending 32 logic ones. */
 	for (i = 32; i >= 0; i--) {
 		outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
 		mdio_delay();
@@ -1232,7 +1222,7 @@
 {
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	long ioaddr = dev->base_addr;
-	int i = 0;
+	int i;
 
 	/* On some chip revs we must set the MII/SYM port before the reset!? */
 	if (tp->mii_cnt  ||  (tp->mtable  &&  tp->mtable->has_mii))
@@ -1240,11 +1230,11 @@
 
 	/* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
 	outl(0x00000001, ioaddr + CSR0);
-#ifdef _LINUX_DELAY_H
-	udelay(2);
-#else
-	SLOW_DOWN_IO;
-#endif
+
+	if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev))
+		return -EAGAIN;
+	MOD_INC_USE_COUNT;
+
 	/* Deassert reset.
 	   486: Set 8 longword cache alignment, 8 longword burst.
 	   586: Set 16 longword cache alignment, no burst limit.
@@ -1270,34 +1260,18 @@
 #endif
 	outl(0x01A00000 | (x86 <= 4 ? 0x4800 : 0x8000), ioaddr + CSR0);
 	if (x86 <= 4)
-	  printk(KERN_INFO "%s: This is a 386/486 PCI system, setting cache "
-			 "alignment to %x.\n", dev->name,
-			 0x01A00000 | (x86 <= 4 ? 0x4800 : 0x8000));
+		printk(KERN_INFO "%s: This is a 386/486 PCI system, setting cache "
+			   "alignment to %x.\n", dev->name,
+			   0x01A00000 | (x86 <= 4 ? 0x4800 : 0x8000));
 #endif
 #else
 	outl(0x01A00000 | 0x4800, ioaddr + CSR0);
 #warning Processor architecture undefined!
 #endif
 
-#ifdef SA_SHIRQ
-	if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev)) {
-		return -EAGAIN;
-	}
-#else
-	if (irq2dev_map[dev->irq] != NULL
-		|| (irq2dev_map[dev->irq] = dev) == NULL
-		|| dev->irq == 0
-		|| request_irq(dev->irq, &tulip_interrupt, 0,
-					   tulip_tbl[tp->chip_id].chip_name)) {
-		return -EAGAIN;
-	}
-#endif
-
 	if (tulip_debug > 1)
 		printk(KERN_DEBUG "%s: tulip_open() irq %d.\n", dev->name, dev->irq);
 
-	MOD_INC_USE_COUNT;
-
 	tulip_init_ring(dev);
 
 	/* This is set_rx_mode(), but without starting the transmitter. */
@@ -1327,6 +1301,7 @@
 	outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3);
 	outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4);
 
+	tp->saved_if_port = dev->if_port;
 	if (dev->if_port == 0)
 		dev->if_port = tp->default_port;
 	if (tp->chip_id == DC21041  &&  dev->if_port > 4)
@@ -1334,6 +1309,7 @@
 		dev->if_port = 4;
 
 	/* Allow selecting a default media. */
+	i = 0;
 	if (tp->mtable == NULL)
 		goto media_picked;
 	if (dev->if_port) {
@@ -1362,15 +1338,36 @@
 	tp->csr6 = 0;
 	tp->cur_index = i;
 	if (dev->if_port == 0  &&  tp->chip_id == DC21142) {
-		tp->csr6 = 0x82420200;
-		outl(0x0003FFFF, ioaddr + CSR14);
-		outl(0x0008, ioaddr + CSR15);
-		outl(0x0001, ioaddr + CSR13);
-		outl(0x1301, ioaddr + CSR12);
+		if (tp->mii_cnt) {
+			if (tulip_debug > 1)
+				printk(KERN_INFO "%s: Using MII transceiver %d, status "
+					   "%4.4x.\n",
+					   dev->name, tp->phys[0], mdio_read(dev, tp->phys[0], 1));
+			select_media(dev, 1);
+			outl(0x82020000, ioaddr + CSR6);
+			tp->csr6 = 0x820E0000;
+			dev->if_port = 11;
+			outl(0x0000, ioaddr + CSR13);
+			outl(0x0000, ioaddr + CSR14);
+			outl(0x0008, ioaddr + CSR15);
+		} else {
+			if (tulip_debug > 1)
+				printk(KERN_INFO "%s: Using default 21143 media sense.\n",
+					   dev->name);
+			tp->csr6 = 0x82420200;
+			outl(0x0003FFFF, ioaddr + CSR14);
+			outl(0x0008, ioaddr + CSR15);
+			outl(0x0001, ioaddr + CSR13);
+			outl(0x1301, ioaddr + CSR12);
+		}
 	} else if (tp->chip_id == LC82C168  &&  tp->mii_cnt && ! tp->medialock) {
 		dev->if_port = 11;
 		tp->csr6 = 0x816C0000 | (tp->full_duplex ? 0x0200 : 0);
 		outl(0x0001, ioaddr + CSR15);
+	} else if (tp->chip_id == MX98713 && ! tp->medialock) {
+		dev->if_port = 0;
+		tp->csr6 = 0x01a80000 | (tp->full_duplex ? 0x0200 : 0);
+		outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80);
 	} else
 		select_media(dev, 1);
 
@@ -1435,7 +1432,7 @@
 
 			dev->if_port = p[0] & 15;
 			if (tulip_debug > 1)
-				printk(KERN_DEBUG "%s: 21142 non-MII %s transceiver control %4.4x/%4.4x.\n",
+				printk(KERN_DEBUG "%s: 21143 non-MII %s transceiver control %4.4x/%4.4x.\n",
 					   dev->name, medianame[dev->if_port], setup[0], setup[1]);
 			if (p[0] & 0x40) {	/* SIA (CSR13-15) setup values are provided. */
 				outl(0, ioaddr + CSR13);
@@ -1793,9 +1790,47 @@
 	int new_csr6 = 0;
 
 	if (tulip_debug > 1)
-		printk(KERN_INFO"%s: 21142 negotiation status %8.8x, %s.\n",
+		printk(KERN_INFO"%s: 21143 negotiation status %8.8x, %s.\n",
 			   dev->name, csr12, medianame[dev->if_port]);
-	if (dev->if_port == 3) {
+	if (media_cap[dev->if_port] & MediaIsMII) {
+		int mii_reg1 = mdio_read(dev, tp->phys[0], 1);
+		int mii_reg5 = mdio_read(dev, tp->phys[0], 5);
+		if (tulip_debug > 1)
+			printk(KERN_INFO "%s: MII status %4.4x, Link partner report "
+				   "%4.4x, CSR6 %x.\n",
+				   dev->name, mii_reg1, mii_reg5, inl(ioaddr + CSR6));
+		if (mii_reg1 != 0xffff  &&  (mii_reg1 & 0x0004) == 0) {
+			int new_reg1 = mdio_read(dev, tp->phys[0], 1);
+			if ((new_reg1 & 0x0004) == 0) {
+				printk(KERN_INFO "%s: No link beat on the MII interface,"
+					   " status then %4.4x now %4.4x.\n",
+					   dev->name, mii_reg1, new_reg1);
+			}
+			if (tp->full_duplex_lock)
+				;
+			else {
+				int negotiated = mii_reg5 & tp->advertising[0];
+				int duplex = ((negotiated & 0x0100) != 0
+							  || (negotiated & 0x00C0) == 0x0040);
+				/* 100baseTx-FD  or  10T-FD, but not 100-HD */
+				if (tp->full_duplex != duplex) {
+					tp->full_duplex = duplex;
+					if (tp->full_duplex)
+						tp->csr6 |= 0x0200;
+					else
+						tp->csr6 &= ~0x0200;
+					outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+					outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+					if (tulip_debug > 0)
+						printk(KERN_INFO "%s: Setting %s-duplex based on MII"
+							   " Xcvr #%d parter capability of %4.4x.\n",
+							   dev->name, tp->full_duplex ? "full" : "half",
+							   tp->phys[0], mii_reg5);
+				}
+			}
+			next_tick = 60*HZ;
+		}
+	} else if (dev->if_port == 3) {
 		if (csr12 & 2) {		/* No 100mbps link beat, revert to 10mbps. */
 			new_csr6 = 0x82420200;
 			outl(new_csr6, ioaddr + CSR6);
@@ -1808,7 +1843,7 @@
 	} else if ((csr12 & 0x7000) != 0x5000) {
 		/* Negotiation failed.  Search media types. */
 		if (tulip_debug > 1)
-			printk(KERN_INFO"%s: 21142 negotiation failed, status %8.8x.\n",
+			printk(KERN_INFO"%s: 21143 negotiation failed, status %8.8x.\n",
 				   dev->name, csr12);
 		if (!(csr12 & 4)) {		/* 10mbps link beat good. */
 			new_csr6 = 0x82420000;
@@ -1834,7 +1869,7 @@
 			outl(1, ioaddr + CSR13);
 		}
 		if (tulip_debug > 1)
-			printk(KERN_INFO"%s: Testing new 21142 media %s.\n",
+			printk(KERN_INFO"%s: Testing new 21143 media %s.\n",
 				   dev->name, medianame[dev->if_port]);
 		if (new_csr6 != (tp->csr6 & ~0x00D5)) {
 			tp->csr6 &= 0x00D5;
@@ -1855,7 +1890,7 @@
 	int csr12 = inl(ioaddr + CSR12);
 
 	if (tulip_debug > 1)
-		printk(KERN_INFO"%s: 21142 link status interrupt %8.8x, CSR5 %x.\n",
+		printk(KERN_INFO"%s: 21143 link status interrupt %8.8x, CSR5 %x.\n",
 			   dev->name, csr12, inl(ioaddr + CSR5));
 
 	if ((csr12 & 0x7000) == 0x5000) {
@@ -1873,20 +1908,20 @@
 		} /* Else 10baseT-FD is handled automatically. */
 	} else if (dev->if_port == 3) {
 		if (!(csr12 & 2))
-			printk(KERN_INFO"%s: 21142 100baseTx link beat good.\n",
+			printk(KERN_INFO"%s: 21143 100baseTx link beat good.\n",
 				   dev->name);
 		else
 			dev->if_port = 0;
 	} else if (dev->if_port == 0) {
 		if (!(csr12 & 4))
-			printk(KERN_INFO"%s: 21142 10baseT link beat good.\n",
+			printk(KERN_INFO"%s: 21143 10baseT link beat good.\n",
 				   dev->name);
 	} else if (!(csr12 & 4)) {		/* 10mbps link beat good. */
-			printk(KERN_INFO"%s: 21142 10mpbs sensed media.\n",
+			printk(KERN_INFO"%s: 21143 10mpbs sensed media.\n",
 				   dev->name);
 			dev->if_port = 0;
 	} else  {		/* 100mbps link beat good. */
-		printk(KERN_INFO"%s: 21142 100baseTx sensed media.\n",
+		printk(KERN_INFO"%s: 21143 100baseTx sensed media.\n",
 			   dev->name);
 		dev->if_port = 3;
 		tp->csr6 = 0x83860000;
@@ -1896,7 +1931,6 @@
 		outl(tp->csr6 | 0x2002, ioaddr + CSR6);
 	}
 }
-	
 
 static void mxic_timer(unsigned long data)
 {
@@ -2002,96 +2036,88 @@
 
 static void tulip_tx_timeout(struct device *dev)
 {
-  struct tulip_private *tp = (struct tulip_private *)dev->priv;
-  long ioaddr = dev->base_addr;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
 
-  if (media_cap[dev->if_port] & MediaIsMII) {
-	  /* Do nothing -- the media monitor should handle this. */
-	  if (tulip_debug > 1)
-		  printk(KERN_WARNING "%s: Transmit timeout using MII device.\n",
-				 dev->name);
-	  dev->trans_start = jiffies;
-	  return;
-  } else if (tp->chip_id == DC21040) {
-	  if (inl(ioaddr + CSR12) & 0x0002) {
-		  printk(KERN_INFO "%s: transmit timed out, switching to %s media.\n",
-				 dev->name, dev->if_port ? "10baseT" : "AUI");
-		  dev->if_port ^= 1;
-		  outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13);
-	  }
-	  dev->trans_start = jiffies;
-	  return;
-  } else if (tp->chip_id == DC21041) {
-	u32 csr12 = inl(ioaddr + CSR12);
+	if (media_cap[dev->if_port] & MediaIsMII) {
+		/* Do nothing -- the media monitor should handle this. */
+		if (tulip_debug > 1)
+			printk(KERN_WARNING "%s: Transmit timeout using MII device.\n",
+				   dev->name);
+	} else if (tp->chip_id == DC21040) {
+		if ( !tp->medialock  &&  inl(ioaddr + CSR12) & 0x0002) {
+			dev->if_port ^= 1;
+			printk(KERN_INFO "%s: transmit timed out, switching to "
+				   "%s10baseT media.\n",
+				   dev->name, dev->if_port ? "non" : "");
+			outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13);
+		}
+		dev->trans_start = jiffies;
+		return;
+	} else if (tp->chip_id == DC21041) {
+		int csr12 = inl(ioaddr + CSR12);
+
+		printk(KERN_WARNING "%s: 21041 transmit timed out, status %8.8x, "
+			   "CSR12 %8.8x, CSR13 %8.8x, CSR14 %8.8x, resetting...\n",
+			   dev->name, inl(ioaddr + CSR5), csr12,
+			   inl(ioaddr + CSR13), inl(ioaddr + CSR14));
+		tp->mediasense = 1;
+		if ( ! tp->medialock) {
+			if (dev->if_port == 1 || dev->if_port == 2)
+				if (csr12 & 0x0004) {
+					dev->if_port = 2 - dev->if_port;
+				} else
+					dev->if_port = 0;
+			else
+				dev->if_port = 1;
+			select_media(dev, 0);
+		}
+	} else if (tp->chip_id == DC21140 || tp->chip_id == DC21142
+			   || tp->chip_id == MX98713) {
+		printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, "
+			   "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
+			   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12),
+			   inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15));
+		if ( ! tp->medialock  &&  tp->mtable) {
+			if (--tp->cur_index < 0) {
+				/* We start again, but should instead look for default. */
+				tp->cur_index = tp->mtable->leafcount - 1;
+			}
+			select_media(dev, 0);
+			printk(KERN_WARNING "%s: transmit timed out, switching to %s "
+				   "media.\n", dev->name, medianame[dev->if_port]);
+		}
+	} else {
+		printk(KERN_WARNING "%s: Transmit timed out, status %8.8x, CSR12 "
+			   "%8.8x, resetting...\n",
+			   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12));
+		dev->if_port = 0;
+	}
+
+#ifdef way_too_many_messages
+	printk("  Rx ring %8.8x: ", (int)tp->rx_ring);
+	for (i = 0; i < RX_RING_SIZE; i++)
+		printk(" %8.8x", (unsigned int)tp->rx_ring[i].status);
+	printk("\n  Tx ring %8.8x: ", (int)tp->tx_ring);
+	for (i = 0; i < TX_RING_SIZE; i++)
+		printk(" %8.8x", (unsigned int)tp->tx_ring[i].status);
+	printk("\n");
+#endif
+
+	/* Stop and restart the chip's Tx processes . */
+	outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+	outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+	/* Trigger an immediate transmit demand. */
+	outl(0, ioaddr + CSR1);
 
-	printk(KERN_WARNING "%s: 21041 transmit timed out, status %8.8x, CSR12 %8.8x,"
-		   " CSR13 %8.8x, CSR14 %8.8x, resetting...\n",
-		   dev->name, inl(ioaddr + CSR5), csr12,
-		   inl(ioaddr + CSR13), inl(ioaddr + CSR14));
-	tp->mediasense = 1;
-	if (dev->if_port == 1 || dev->if_port == 2)
-		if (csr12 & 0x0004) {
-			dev->if_port = 2 - dev->if_port;
-		} else
-			dev->if_port = 0;
-	else
-		dev->if_port = 1;
-	select_media(dev, 0);
-	tp->stats.tx_errors++;
 	dev->trans_start = jiffies;
+	tp->stats.tx_errors++;
 	return;
-  } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142
-			 || tp->chip_id == MX98713) {
-	  /* Stop the transmit process. */
-	  outl(tp->csr6 | 0x0002, ioaddr + CSR6);
-	  printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, "
-			 "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
-			 dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12),
-			 inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15));
-	  if (tp->mtable) {
-		  if (--tp->cur_index < 0) {
-			  /* We start again, but should instead look for default. */
-			  tp->cur_index = tp->mtable->leafcount - 1;
-		  }
-		  select_media(dev, 0);
-		  printk(KERN_WARNING "%s: transmit timed out, switching to %s media.\n",
-				 dev->name, dev->if_port ? "100baseTx" : "10baseT");
-	  }
-	  outl(tp->csr6 | 0x2002, ioaddr + CSR6);
-	  tp->stats.tx_errors++;
-	  dev->trans_start = jiffies;
-	  return;
-  } else
-	printk(KERN_WARNING "%s: transmit timed out, status %8.8x, CSR12 %8.8x,"
-		   " resetting...\n",
-		   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12));
-#ifdef way_too_many_messages
-  printk("  Rx ring %8.8x: ", (int)tp->rx_ring);
-  for (i = 0; i < RX_RING_SIZE; i++)
-	printk(" %8.8x", (unsigned int)tp->rx_ring[i].status);
-  printk("\n  Tx ring %8.8x: ", (int)tp->tx_ring);
-  for (i = 0; i < TX_RING_SIZE; i++)
-	printk(" %8.8x", (unsigned int)tp->tx_ring[i].status);
-  printk("\n");
-#endif
-
-  /* Perhaps we should reinitialize the hardware here. */
-  dev->if_port = 0;
-  /* Stop and restart the chip's Tx processes . */
-  outl(tp->csr6 | 0x0002, ioaddr + CSR6);
-  outl(tp->csr6 | 0x2002, ioaddr + CSR6);
-  /* Trigger an immediate transmit demand. */
-  outl(0, ioaddr + CSR1);
-
-  dev->trans_start = jiffies;
-  tp->stats.tx_errors++;
-  return;
 }
 
 
 /* Initialize the Rx and Tx rings, along with various 'dev' bits. */
-static void
-tulip_init_ring(struct device *dev)
+static void tulip_init_ring(struct device *dev)
 {
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	int i;
@@ -2108,7 +2134,7 @@
 			   dev_alloc_skb() provides 16 byte alignment.  But do *not*
 			   use skb_reserve() to align the IP header! */
 			struct sk_buff *skb;
-			skb = DEV_ALLOC_SKB(PKT_BUF_SZ);
+			skb = dev_alloc_skb(PKT_BUF_SZ);
 			tp->rx_skbuff[i] = skb;
 			if (skb == NULL)
 				break;			/* Bad news!  */
@@ -2122,7 +2148,7 @@
 		tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]);
 	}
 	/* Mark the last entry as wrapping the ring. */
-	tp->rx_ring[i-1].length = PKT_BUF_SZ | 0x02000000;
+	tp->rx_ring[i-1].length = PKT_BUF_SZ | DESC_RING_WRAP;
 	tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]);
 
 	/* The Tx buffer descriptor is filled in as needed, but we
@@ -2175,7 +2201,7 @@
 	  tp->tx_full = 1;
 	}
 	if (entry == TX_RING_SIZE-1)
-		flag |= 0xe2000000;
+		flag |= 0xe0000000 | DESC_RING_WRAP;
 
 	tp->tx_ring[entry].length = skb->len | flag;
 	tp->tx_ring[entry].status = 0x80000000;	/* Pass ownership to the chip. */
@@ -2190,39 +2216,28 @@
 
 /* The interrupt handler does all of the Rx thread work and cleans up
    after the Tx thread. */
-static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs)
+static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
 {
-#ifdef SA_SHIRQ		/* Use the now-standard shared IRQ implementation. */
 	struct device *dev = (struct device *)dev_instance;
-#else
-	struct device *dev = (struct device *)(irq2dev_map[irq]);
-#endif
-
-	struct tulip_private *tp;
-	long ioaddr;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	long ioaddr = dev->base_addr;
 	int csr5, work_budget = max_interrupt_work;
 
-	if (dev == NULL) {
-		printk (KERN_ERR" tulip_interrupt(): irq %d for unknown device.\n",
-				irq);
-		return;
-	}
-
-	ioaddr = dev->base_addr;
-	tp = (struct tulip_private *)dev->priv;
-	if (test_and_set_bit(0, (void*)&tp->interrupt)) {
-#ifdef SMP_CHECK
+#if defined(__i386__) && defined(SMP_CHECK)   /* && defined(__SMP__) */
+	if (test_and_set_bit(0, (void*)&dev->interrupt)) {
 		printk(KERN_ERR "%s: Re-entering the interrupt handler with proc %d,"
 			   " proc %d already handling.\n", dev->name,
 			   tp->smp_proc_id, hard_smp_processor_id());
+		dev->interrupt = 0;
+		return;
+	} else
+		tp->smp_proc_id = hard_smp_processor_id();
 #else
+	if (dev->interrupt) {
 		printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
-#endif
 		return;
 	}
 	dev->interrupt = 1;
-#ifdef SMP_CHECK
-	tp->smp_proc_id = hard_smp_processor_id();
 #endif
 
 	do {
@@ -2283,11 +2298,7 @@
 				}
 
 				/* Free the original skb. */
-#if (LINUX_VERSION_CODE > 0x20155)
-				dev_kfree_skb(tp->tx_skbuff[entry]);
-#else
-				dev_kfree_skb(tp->tx_skbuff[entry], FREE_WRITE);
-#endif
+				dev_free_skb(tp->tx_skbuff[entry]);
 				tp->tx_skbuff[entry] = 0;
 			}
 
@@ -2309,7 +2320,7 @@
 
 			tp->dirty_tx = dirty_tx;
 			if (csr5 & TxDied) {
-				if (tulip_debug > 1)
+				if (tulip_debug)
 					printk(KERN_WARNING "%s: The transmitter stopped!"
 						   "  CSR5 is %x, CSR6 %x.\n",
 						   dev->name, csr5, inl(ioaddr + CSR6));
@@ -2320,6 +2331,8 @@
 
 		/* Log errors. */
 		if (csr5 & AbnormalIntr) {	/* Abnormal error summary bit. */
+			if (csr5 == 0xffffffff)
+				break;
 			if (csr5 & TxJabber) tp->stats.tx_errors++;
 			if (csr5 & TxFIFOUnderflow) {
 				if ((tp->csr6 & 0xC000) != 0xC000)
@@ -2342,7 +2355,7 @@
 			if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)
 				&& tp->chip_id == DC21142) {
 				if (tulip_debug > 1)
-					printk(KERN_INFO"%s: 21142 link change, CSR5 = %8.8x.\n",
+					printk(KERN_INFO"%s: 21143 link change, CSR5 = %8.8x.\n",
 						   dev->name, csr5);
 				t21142_lnk_change(dev);
 			}
@@ -2351,7 +2364,7 @@
 		}
 		if (--work_budget < 0) {
 			if (tulip_debug > 1)
-				printk(KERN_WARNING "%s: Too much work at interrupt, "
+				printk(KERN_WARNING "%s: Too much work during an interrupt, "
 					   "csr5=0x%8.8x.\n", dev->name, csr5);
 			/* Acknowledge all interrupt sources. */
 			outl(0x8001ffff, ioaddr + CSR5);
@@ -2367,8 +2380,11 @@
 		printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n",
 			   dev->name, inl(ioaddr + CSR5));
 
+#if defined(__i386__)
+	clear_bit(0, (void*)&dev->interrupt);
+#else
 	dev->interrupt = 0;
-	clear_bit(0, (void*)&tp->interrupt);
+#endif
 	return;
 }
 
@@ -2389,69 +2405,67 @@
 
 		if (--rx_work_limit < 0)
 			break;
-		if ((status & 0x0300) != 0x0300) {
-			if ((status & 0xffff) != 0x7fff) { /* Ingore earlier buffers. */
-				if (tulip_debug > 1)
-					printk(KERN_WARNING "%s: Oversized Ethernet frame spanned "
-						   "multiple buffers, status %8.8x!\n",
+		if ((status & 0x38008300) != 0x0300) {
+			if ((status & 0x38000300) != 0x0300) {
+				/* Ingore earlier buffers. */
+				if ((status & 0xffff) != 0x7fff) {
+					if (tulip_debug > 1)
+						printk(KERN_WARNING "%s: Oversized Ethernet frame "
+							   "spanned multiple buffers, status %8.8x!\n",
+							   dev->name, status);
+					tp->stats.rx_length_errors++;
+				}
+			} else if (status & 0x8000) {
+				/* There was a fatal error. */
+				if (tulip_debug > 2)
+					printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n",
 						   dev->name, status);
-			  tp->stats.rx_length_errors++;
+				tp->stats.rx_errors++; /* end of a packet.*/
+				if (status & 0x0890) tp->stats.rx_length_errors++;
+				if (status & 0x0004) tp->stats.rx_frame_errors++;
+				if (status & 0x0002) tp->stats.rx_crc_errors++;
+				if (status & 0x0001) tp->stats.rx_fifo_errors++;
 			}
-		} else if (status & 0x8000) {
-			/* There was a fatal error. */
-			if (tulip_debug > 2)
-				printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n",
-					   dev->name, status);
-			tp->stats.rx_errors++; /* end of a packet.*/
-			if (status & 0x0890) tp->stats.rx_length_errors++;
-			if (status & 0x0004) tp->stats.rx_frame_errors++;
-			if (status & 0x0002) tp->stats.rx_crc_errors++;
-			if (status & 0x0001) tp->stats.rx_fifo_errors++;
 		} else {
 			/* Omit the four octet CRC from the length. */
-			short pkt_len = ((status >> 16) & 0x7FF) - 4;
+			short pkt_len = ((status >> 16) & 0x7ff) - 4;
 			struct sk_buff *skb;
 
-			/* Check if the packet is long enough to just accept without
-			   copying to a properly sized skbuff. */
+#ifndef final_version
+			if (pkt_len > 1518) {
+				printk("%s: Bogus packet size of %d (%#x).\n",
+					   dev->name, pkt_len, pkt_len);
+				pkt_len = 1518;
+				tp->stats.rx_length_errors++;
+			}
+#endif
+			/* Check if the packet is long enough to accept without copying
+			   to a minimally-sized skbuff. */
 			if (pkt_len < rx_copybreak
-				&& (skb = DEV_ALLOC_SKB(pkt_len+2)) != NULL) {
+				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
 				skb->dev = dev;
 				skb_reserve(skb, 2);	/* 16 byte align the IP header */
-#if LINUX_VERSION_CODE < 0x10300
-				memcpy(skb->data, tp->rx_ring[entry].buffer1, pkt_len);
-#elif LINUX_VERSION_CODE < 0x20200  || defined(__alpha__)
-				memcpy(skb_put(skb, pkt_len),
-					   bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len);
-#else
-#warning Code untested
+#if ! defined(__alpha__)
 				eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1),
 								 pkt_len, 0);
 				skb_put(skb, pkt_len);
+#else
+				memcpy(skb_put(skb, pkt_len),
+					   bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len);
 #endif
 				work_done++;
 			} else { 	/* Pass up the skb already on the Rx ring. */
-				skb = tp->rx_skbuff[entry];
+				char *temp = skb_put(skb = tp->rx_skbuff[entry], pkt_len);
 				tp->rx_skbuff[entry] = NULL;
 #ifndef final_version
-				{
-					void *temp = skb_put(skb, pkt_len);
-					if (bus_to_virt(tp->rx_ring[entry].buffer1) != temp)
-						printk(KERN_ERR "%s: Internal consistency error! The "
-						   "skbuff addresses do not match in tulip_rx:"
-							   " %p vs. %p / %p.\n", dev->name,
-							   bus_to_virt(tp->rx_ring[entry].buffer1),
-							   skb->head, temp);
-				}
-#else
-				skb_put(skb, pkt_len);
+				if (bus_to_virt(tp->rx_ring[entry].buffer1) != temp)
+					printk(KERN_ERR "%s: Internal fault: The skbuff addresses "
+						   "do not match in tulip_rx: %p vs. %p / %p.\n",
+						   dev->name, bus_to_virt(tp->rx_ring[entry].buffer1),
+						   skb->head, temp);
 #endif
 			}
-#if LINUX_VERSION_CODE > 0x10300
 			skb->protocol = eth_type_trans(skb, dev);
-#else
-			skb->len = pkt_len;
-#endif
 			netif_rx(skb);
 			dev->last_rx = jiffies;
 			tp->stats.rx_packets++;
@@ -2467,15 +2481,11 @@
 		entry = tp->dirty_rx % RX_RING_SIZE;
 		if (tp->rx_skbuff[entry] == NULL) {
 			struct sk_buff *skb;
-			skb = tp->rx_skbuff[entry] = DEV_ALLOC_SKB(PKT_BUF_SZ);
+			skb = tp->rx_skbuff[entry] = dev_alloc_skb(PKT_BUF_SZ);
 			if (skb == NULL)
 				break;
 			skb->dev = dev;			/* Mark as being used by this device. */
-#if LINUX_VERSION_CODE > 0x10300
 			tp->rx_ring[entry].buffer1 = virt_to_bus(skb->tail);
-#else
-			tp->rx_ring[entry].buffer1 = virt_to_bus(skb->data);
-#endif
 			work_done++;
 		}
 		tp->rx_ring[entry].status = 0x80000000;
@@ -2506,16 +2516,14 @@
 	if (tp->chip_id == DC21040)
 		outl(0x00000004, ioaddr + CSR13);
 
-	tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+	if (inl(ioaddr + CSR6) != 0xffffffff)
+		tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
 
 	del_timer(&tp->timer);
 
-#ifdef SA_SHIRQ
 	free_irq(dev->irq, dev);
-#else
-	free_irq(dev->irq);
-	irq2dev_map[dev->irq] = 0;
-#endif
+
+	dev->if_port = tp->saved_if_port;
 
 	/* Free all the skbuffs in the Rx queue. */
 	for (i = 0; i < RX_RING_SIZE; i++) {
@@ -2528,20 +2536,12 @@
 #if LINUX_VERSION_CODE < 0x20100
 			skb->free = 1;
 #endif
-#if (LINUX_VERSION_CODE > 0x20155)
-			dev_kfree_skb(skb);
-#else
-			dev_kfree_skb(skb, FREE_WRITE);
-#endif
+			dev_free_skb(skb);
 		}
 	}
 	for (i = 0; i < TX_RING_SIZE; i++) {
 		if (tp->tx_skbuff[i])
-#if (LINUX_VERSION_CODE > 0x20155)
-			dev_kfree_skb(tp->tx_skbuff[i]);
-#else
-			dev_kfree_skb(tp->tx_skbuff[i], FREE_WRITE);
-#endif
+			dev_free_skb(tp->tx_skbuff[i]);
 		tp->tx_skbuff[i] = 0;
 	}
 
@@ -2551,8 +2551,7 @@
 	return 0;
 }
 
-static struct enet_statistics *
-tulip_get_stats(struct device *dev)
+static struct net_device_stats *tulip_get_stats(struct device *dev)
 {
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	long ioaddr = dev->base_addr;
@@ -2575,7 +2574,7 @@
 
 	switch(cmd) {
 	case SIOCDEVPRIVATE:		/* Get the address of the PHY in use. */
-		if (tp->mtable  &&  tp->mtable->has_mii)
+		if (tp->mii_cnt)
 			data[0] = phy;
 		else if (tp->chip_id == DC21142)
 			data[0] = 32;
@@ -2595,11 +2594,11 @@
 					+ (csr12&0x06 ? 0x04 : 0);
 				break;
 			case 4: {
-				int csr14 = inl(ioaddr + CSR14);
-				data[3] = ((csr14>>9)&0x0380) + ((csr14>>1)&0x20) + 1;
+				data[3] = ((csr14>>9)&0x0380) +
+					((inl(ioaddr + CSR6)>>3)&0x0040) +((csr14>>1)&0x20) + 1;
 				break;
 			}
-			case 5: data[3] = inl(ioaddr + CSR12) >> 16; break;
+			case 5: data[3] = csr12 >> 16; break;
 			default: data[3] = 0; break;
 			}
 		} else {
@@ -2613,6 +2612,8 @@
 		if (!suser())
 			return -EPERM;
 		if (data[0] == 32) {  /* 21142 pseudo-MII */
+			if (data[1] == 5)
+				tp->advertising[tp->mii_cnt] = data[2];
 		} else {
 			save_flags(flags);
 			cli();
@@ -2654,11 +2655,7 @@
 	return crc;
 }
 
-#ifdef NEW_MULTICAST
 static void set_rx_mode(struct device *dev)
-#else
-static void set_rx_mode(struct device *dev, int num_addrs, void *addrs)
-#endif
 {
 	long ioaddr = dev->base_addr;
 	int csr6 = inl(ioaddr + CSR6) & ~0x00D5;
@@ -2682,38 +2679,38 @@
 		int i;
 
 		if (dev->mc_count > 14) { /* Must use a multicast hash table. */
-		  u16 hash_table[32];
-		  memset(hash_table, 0, sizeof(hash_table));
-		  /* This should work on big-endian machines as well. */
-		  for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
-			   i++, mclist = mclist->next)
-			  set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff,
-					  hash_table);
-		  /* Copy the hash table to the setup frame.
-			 NOTE that only the LOW SHORTWORD of setup_frame[] is valid! */
-		  for (i = 0; i < 32; i++)
-			*setup_frm++ = hash_table[i];
-		  setup_frm += 7;
-		  tx_flags = 0x08400000 | 192;
-		  /* Too clever: i > 15 for fall-though. */
+			u16 hash_table[32];
+			memset(hash_table, 0, sizeof(hash_table));
+			/* This should work on big-endian machines as well. */
+			for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+				 i++, mclist = mclist->next)
+				set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff,
+						hash_table);
+			/* Copy the hash table to the setup frame.
+			   NOTE that only the LOW SHORTWORD of setup_frame[] is valid! */
+			for (i = 0; i < 32; i++)
+				*setup_frm++ = hash_table[i];
+			setup_frm += 7;
+			tx_flags = 0x08400000 | 192;
+			/* Too clever: i > 15 for fall-though. */
 		} else {
-		  /* We have <= 15 addresses so we can use the wonderful
-			 16 address perfect filtering of the Tulip. */
-		  for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
-			   i++, mclist = mclist->next) {
-			/* Note that only the low shortword of setup_frame[] is valid!
-			   This code may require tweaking for non-x86 architectures! */
-			eaddrs = (u16 *)mclist->dmi_addr;
-			*setup_frm++ = *eaddrs++;
-			*setup_frm++ = *eaddrs++;
-			*setup_frm++ = *eaddrs++;
-		  }
-		  /* Fill the rest of the table with our physical address.
-			 Once again, only the low shortword or setup_frame[] is valid! */
-		  *setup_frm++ = 0xffff;
-		  *setup_frm++ = 0xffff;
-		  *setup_frm++ = 0xffff;
-		  tx_flags = 0x08000000 | 192;
+			/* We have <= 15 addresses so we can use the wonderful
+			   16 address perfect filtering of the Tulip. */
+			for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
+				 i++, mclist = mclist->next) {
+				/* Note that only the low shortword of setup_frame[] is valid!
+				   This code may require tweaking for non-x86 architectures! */
+				eaddrs = (u16 *)mclist->dmi_addr;
+				*setup_frm++ = *eaddrs++;
+				*setup_frm++ = *eaddrs++;
+				*setup_frm++ = *eaddrs++;
+			}
+			/* Fill the rest of the table with our physical address.
+			   Once again, only the low shortword or setup_frame[] is valid! */
+			*setup_frm++ = 0xffff;
+			*setup_frm++ = 0xffff;
+			*setup_frm++ = 0xffff;
+			tx_flags = 0x08000000 | 192;
 		}
 		eaddrs = (u16 *)dev->dev_addr;
 		do {
@@ -2727,7 +2724,7 @@
 		} else {
 			unsigned long flags;
 			unsigned int entry;
-			
+
 			save_flags(flags); cli();
 			entry = tp->cur_tx++ % TX_RING_SIZE;
 
@@ -2735,7 +2732,7 @@
 				/* Avoid a chip errata by prefixing a dummy entry. */
 				tp->tx_skbuff[entry] = 0;
 				tp->tx_ring[entry].length =
-					(entry == TX_RING_SIZE-1) ? 0x02000000 : 0;
+					(entry == TX_RING_SIZE-1) ? DESC_RING_WRAP : 0;
 				tp->tx_ring[entry].buffer1 = 0;
 				tp->tx_ring[entry].status = 0x80000000;
 				entry = tp->cur_tx++ % TX_RING_SIZE;
@@ -2744,7 +2741,7 @@
 			tp->tx_skbuff[entry] = 0;
 			/* Put the setup frame on the Tx list. */
 			if (entry == TX_RING_SIZE-1)
-				tx_flags |= 0x02000000;		/* Wrap ring. */
+				tx_flags |= DESC_RING_WRAP;		/* Wrap ring. */
 			tp->tx_ring[entry].length = tx_flags;
 			tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame);
 			tp->tx_ring[entry].status = 0x80000000;
@@ -2766,18 +2763,18 @@
 
 static dev_node_t *tulip_attach(dev_locator_t *loc)
 {
+	struct device *dev;
 	u16 dev_id;
 	u32 io;
-	u8 bus, devfn;
-	struct device *dev;
+	u8 bus, devfn, irq;
 
 	if (loc->bus != LOC_PCI) return NULL;
 	bus = loc->b.pci.bus; devfn = loc->b.pci.devfn;
 	printk(KERN_INFO "tulip_attach(bus %d, function %d)\n", bus, devfn);
 	pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &io);
 	pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &dev_id);
-	io &= ~3;
-	dev = tulip_probe1(bus, devfn, NULL, DC21142, -1);
+	pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq);
+	dev = tulip_probe1(bus, devfn, NULL, io & ~3, irq, DC21142, -1);
 	if (dev) {
 		dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL);
 		strcpy(node->dev_name, dev->name);
@@ -2789,6 +2786,37 @@
 	return NULL;
 }
 
+static void tulip_suspend(dev_node_t *node)
+{
+	struct device **devp, **next;
+	printk(KERN_INFO "tulip_suspend(%s)\n", node->dev_name);
+	for (devp = &root_tulip_dev; *devp; devp = next) {
+		next = &((struct tulip_private *)(*devp)->priv)->next_module;
+		if (strcmp((*devp)->name, node->dev_name) == 0) break;
+	}
+	if (*devp) {
+		struct tulip_private *tp = (struct tulip_private *)(*devp)->priv;
+		tulip_close(*devp);
+		/* Put the chip into sleep mode. */
+		pcibios_write_config_dword(tp->pci_bus,tp->pci_devfn, 0x40,0x80000000);
+	}
+}
+
+static void tulip_resume(dev_node_t *node)
+{
+	struct device **devp, **next;
+	printk(KERN_INFO "tulip_resume(%s)\n", node->dev_name);
+	for (devp = &root_tulip_dev; *devp; devp = next) {
+		next = &((struct tulip_private *)(*devp)->priv)->next_module;
+		if (strcmp((*devp)->name, node->dev_name) == 0) break;
+	}
+	if (*devp) {
+		struct tulip_private *tp = (struct tulip_private *)(*devp)->priv;
+		pcibios_write_config_dword(tp->pci_bus, tp->pci_devfn, 0x40, 0x0000);
+		tulip_open(*devp);
+	}
+}
+
 static void tulip_detach(dev_node_t *node)
 {
 	struct device **devp, **next;
@@ -2807,34 +2835,17 @@
 }
 
 struct driver_operations tulip_ops = {
-	"tulip_cb", tulip_attach, NULL, NULL, tulip_detach
+	"tulip_cb", tulip_attach, tulip_suspend, tulip_resume, tulip_detach
 };
 
 #endif  /* Cardbus support */
 
 
 #ifdef MODULE
-#if LINUX_VERSION_CODE > 0x20118
-MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
-MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver");
-MODULE_PARM(debug, "i");
-MODULE_PARM(max_interrupt_work, "i");
-MODULE_PARM(reverse_probe, "i");
-MODULE_PARM(rx_copybreak, "i");
-MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
-MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
-#endif
-
-/* An additional parameter that may be passed in... */
-static int debug = -1;
-
-int
-init_module(void)
+int init_module(void)
 {
-	if (debug >= 0)
-		tulip_debug = debug;
-
 #ifdef CARDBUS
+	reverse_probe = 0;			/* Not used. */
 	register_driver(&tulip_ops);
 	return 0;
 #else
@@ -2842,8 +2853,7 @@
 #endif
 }
 
-void
-cleanup_module(void)
+void cleanup_module(void)
 {
 	struct device *next_dev;
 
@@ -2853,9 +2863,11 @@
 
 	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
 	while (root_tulip_dev) {
-		next_dev = ((struct tulip_private *)root_tulip_dev->priv)->next_module;
+		struct tulip_private *tp = (struct tulip_private *)root_tulip_dev->priv;
+		next_dev = tp->next_module;
 		unregister_netdev(root_tulip_dev);
-		release_region(root_tulip_dev->base_addr, TULIP_TOTAL_SIZE);
+		release_region(root_tulip_dev->base_addr,
+					   tulip_tbl[tp->chip_id].io_size);
 		kfree(root_tulip_dev);
 		root_tulip_dev = next_dev;
 	}
@@ -2867,6 +2879,7 @@
  * Local variables:
  *  SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
  *  compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
+ *  cardbus-compile-command: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c -o tulip_cb.o -I/usr/src/pcmcia-cs-3.0.5/include/"
  *  c-indent-level: 4
  *  c-basic-offset: 4
  *  tab-width: 4
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/wavelan.c linux/drivers/net/wavelan.c
--- linux.vanilla/drivers/net/wavelan.c	Sun Dec  6 00:14:40 1998
+++ linux/drivers/net/wavelan.c	Tue Mar  2 00:52:05 1999
@@ -290,24 +290,26 @@
   wv_16_on(ioaddr, hacr);
 } /* psa_write */
 
-#ifdef PSA_CRC
+#ifdef SET_PSA_CRC
 /*------------------------------------------------------------------*/
 /*
- * Calculate the PSA CRC (not tested yet)
- * As the WaveLAN drivers don't use the CRC, I won't use it either.
+ * Calculate the PSA CRC
  * 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)
+ *
+ * The Windows drivers don't use the CRC, but the AP and the PtP tool
+ * depend on it.
  */
-static u_short
-psa_crc(u_short *	psa,	/* The PSA */
+static inline u_short
+psa_crc(u_char *	psa,	/* The PSA */
 	int		size)	/* Number of short for CRC */
 {
   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 */
 
-  for(byte_cnt = 0; byte_cnt <= size; byte_cnt++ )
+  for(byte_cnt = 0; byte_cnt < size; byte_cnt++ )
     {
       crc_bytes ^= psa[byte_cnt];	/* Its an xor */
 
@@ -322,7 +324,47 @@
 
   return crc_bytes;
 } /* psa_crc */
-#endif	/* PSA_CRC */
+
+/*------------------------------------------------------------------*/
+/*
+ * update the checksum field in the Wavelan's PSA
+ */
+static void
+update_psa_checksum(device *	dev,
+		    u_long	ioaddr,
+		    u_short	hacr)
+{
+  psa_t		psa;
+  u_short	crc;
+
+  /* read the parameter storage area */
+  psa_read(ioaddr, hacr, 0, (unsigned char *) &psa, sizeof(psa));
+
+  /* update the checksum */
+  crc = psa_crc((unsigned char *) &psa,
+		sizeof(psa) - sizeof(psa.psa_crc[0]) - sizeof(psa.psa_crc[1])
+		- sizeof(psa.psa_crc_status));
+
+  psa.psa_crc[0] = crc & 0xFF;
+  psa.psa_crc[1] = (crc & 0xFF00) >> 8;
+
+  /* Write it ! */
+  psa_write(ioaddr, hacr, (char *)&psa.psa_crc - (char *)&psa,
+	    (unsigned char *)&psa.psa_crc, 2);
+
+#ifdef DEBUG_IOCTL_INFO
+  printk (KERN_DEBUG "%s: update_psa_checksum(): crc = 0x%02x%02x\n",
+          dev->name, psa.psa_crc[0], psa.psa_crc[1]);
+
+  /* Check again (luxury !) */
+  crc = psa_crc ((unsigned char *) &psa,
+		 sizeof(psa) - sizeof(psa.psa_crc_status));
+
+  if(crc != 0)
+    printk(KERN_WARNING "%s: update_psa_checksum(): CRC does not agree with PSA data (even after recalculating)\n", dev->name);
+#endif /* DEBUG_IOCTL_INFO */
+} /* update_psa_checksum */
+#endif	/* SET_PSA_CRC */
 
 /*------------------------------------------------------------------*/
 /*
@@ -706,21 +748,21 @@
       unsigned short	ias_addr;
 
       /* Check mc_config command */
-      if(status & AC_SFLD_OK != 0)
+      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)
+      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)
+      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 */
@@ -1898,6 +1940,10 @@
 	  /* Disable nwid in the mmc (no filtering) */
 	  mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), MMW_LOOPT_SEL_DIS_NWID);
 	}
+#ifdef SET_PSA_CRC
+      /* update the Wavelan checksum */
+      update_psa_checksum(dev, ioaddr, lp->hacr);
+#endif
       break;
 
     case SIOCGIWNWID:
@@ -1955,6 +2001,10 @@
       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);
+#ifdef SET_PSA_CRC
+      /* update the Wavelan checksum */
+      update_psa_checksum(dev, ioaddr, lp->hacr);
+#endif
       mmc_out(ioaddr, mmwoff(0, mmw_thr_pre_set), psa.psa_thr_pre_set);
       break;
 
@@ -2002,6 +2052,10 @@
 
 	   mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), 0);
 	 }
+#ifdef SET_PSA_CRC
+       /* update the Wavelan checksum */
+       update_psa_checksum(dev, ioaddr, lp->hacr);
+#endif
        break;
 
      case SIOCGIWENCODE:
@@ -2215,6 +2269,10 @@
       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);
+#ifdef SET_PSA_CRC
+      /* update the Wavelan checksum */
+      update_psa_checksum(dev, ioaddr, lp->hacr);
+#endif
       mmc_out(ioaddr, mmwoff(0, mmw_quality_thr), psa.psa_quality_thr);
       break;
 
@@ -2325,7 +2383,7 @@
   mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0);
 
   /* Copy data to wireless stuff */
-  wstats->status = m.mmr_dce_status;
+  wstats->status = m.mmr_dce_status & 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;
@@ -2909,6 +2967,10 @@
 		(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);
+#ifdef SET_PSA_CRC
+      /* update the Wavelan checksum */
+      update_psa_checksum(dev, ioaddr, lp->hacr);
+#endif
 #endif
     }
 
@@ -2935,21 +2997,18 @@
   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_freeze = 0;
   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;
 
@@ -3414,39 +3473,29 @@
   /* Create a configure action */
   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 without 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.)
+   *
+   * Updated from Wavelan Manual WCIN085B
    */
   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) |
+  cfg.cfg_fifolim = AC_CFG_FIFOLIM(4);
+  cfg.cfg_byte8 = AC_CFG_SAV_BF(1) |
 		  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) |
+  cfg.cfg_byte10 = AC_CFG_BOFMET(1) |
+		   AC_CFG_ACR(6) |
 		   AC_CFG_LINPRIO(0);
-  cfg.cfg_ifs = 32;
-  cfg.cfg_slotl = 0;
+  cfg.cfg_ifs = 0x20;
+  cfg.cfg_slotl = 0x0C;
   cfg.cfg_byte13 = AC_CFG_RETRYNUM(15) |
-		   AC_CFG_SLTTMHI(2);
+		   AC_CFG_SLTTMHI(0);
   cfg.cfg_byte14 = AC_CFG_FLGPAD(0) |
 		   AC_CFG_BTSTF(0) |
 		   AC_CFG_CRC16(0) |
@@ -4045,6 +4094,10 @@
 #endif
 	  psa_write(ioaddr, HACR_DEFAULT,
 		    psaoff(0, psa_int_req_no), &irq_mask, 1);
+#ifdef SET_PSA_CRC
+	  /* update the Wavelan checksum */
+	  update_psa_checksum(dev, ioaddr, HACR_DEFAULT);
+#endif
 	  wv_hacr_reset(ioaddr);
 	}
     }
@@ -4227,7 +4280,7 @@
 init_module(void)
 {
   mac_addr	mac;		/* MAC address (check WaveLAN existence) */
-  int		ret = 0;
+  int		ret = -EIO;	/* Return error if no cards found */
   int		i;
 
 #ifdef DEBUG_MODULE_TRACE
@@ -4272,7 +4325,11 @@
 	      /* DeAllocate everything */
 	      /* Note : if dev->priv is mallocated, there is no way to fail */
 	      kfree_s(dev, sizeof(struct device));
-	      ret = -EIO;
+	    }
+	  else
+	    {
+              /* If at least one device OK, we do not fail */
+              ret = 0;
 	    }
 	}	/* if there is something at the address */
     }		/* Loop on all addresses. */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/wavelan.h linux/drivers/net/wavelan.h
--- linux.vanilla/drivers/net/wavelan.h	Sun Jun 21 18:41:05 1998
+++ linux/drivers/net/wavelan.h	Tue Mar  2 00:52:05 1999
@@ -28,6 +28,8 @@
 {
   { 0x08, 0x00, 0x0E },		/* AT&T Wavelan (standard) & DEC RoamAbout */
   { 0x08, 0x00, 0x6A },		/* AT&T Wavelan (alternate) */
+  { 0x00, 0x00, 0xE1 },		/* Hitachi Wavelan */
+  { 0x00, 0x60, 0x1D }		/* Lucent Wavelan (another one) */
   /* Add your card here and send me the patch ! */
 };
 
@@ -293,6 +295,7 @@
 #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 */
+#define MMR_DCE_STATUS			0x0F	/* mask to get the bits */
   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) */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/wavelan.p.h linux/drivers/net/wavelan.p.h
--- linux.vanilla/drivers/net/wavelan.p.h	Sun Jun 21 18:41:08 1998
+++ linux/drivers/net/wavelan.p.h	Fri Mar 26 17:19:10 1999
@@ -16,15 +16,9 @@
 /************************** DOCUMENTATION **************************/
 /*
  * This driver provide a Linux interface to the Wavelan ISA hardware
- * The Wavelan is a product of Lucent ("http://wavelan.netland.nl/").
+ * The Wavelan is a product of Lucent ("http://www.wavelan.com/").
  * 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...).
+ * Wavelan are also distributed by DEC (RoamAbout DS) and Digital Ocean.
  *
  * To know how to use this driver, read the NET3 HOWTO.
  * If you want to exploit the many other fonctionalities, look comments
@@ -35,12 +29,22 @@
 
 /* ------------------------ SPECIFIC NOTES ------------------------ */
 /*
+ * Web page
+ * --------
+ *	I try to maintain a web page with the Wireless LAN Howto at :
+ *		http://www-uk.hpl.hp.com/people/jt/Linux/Wavelan.html
+ *
  * wavelan.o is darn too big
  * -------------------------
  *	That's true ! There is a very simple way to reduce the driver
  *	object by 33% (yes !). Comment out the following line :
  *		#include <linux/wireless.h>
  *
+ * Debugging and options
+ * ---------------------
+ *	You will find below a set of '#define" allowing a very fine control
+ *	on the driver behaviour and the debug messages printed.
+ *
  * MAC address and hardware detection :
  * ----------------------------------
  *	The detection code of the wavelan chech that the first 3
@@ -60,23 +64,6 @@
  *	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 --------------------- */
@@ -271,6 +258,22 @@
  *	- Encryption setting from Brent Elphick (thanks a lot !)
  *	- 'ioaddr' to 'u_long' for the Alpha (thanks to Stanislav Sinyagin)
  *
+ * Other changes (not by me) :
+ * -------------------------
+ *	- Spelling and gramar "rectification".
+ *
+ * Changes made for release in 2.0.37 & 2.2.2 :
+ * ------------------------------------------
+ *	- Correct status in /proc/net/wireless
+ *	- Set PSA CRC to make PtP diagnostic tool happy (Bob Gray)
+ *	- Module init code don't fail if we found at least one card in
+ *	  the address list (Karlis Peisenieks)
+ *	- Missing parenthesis (Christopher Peterson)
+ *	- Correct i82586 configuration parameters
+ *	- Encryption initialisation bug (Robert McCormack)
+ *	- New mac addresses detected in the probe
+ *	- Increase watchdog for busy envirnoments
+ *
  * Wishes & dreams :
  * ---------------
  *	- Roaming
@@ -338,9 +341,9 @@
 
 /* Options : */
 #define USE_PSA_CONFIG		/* Use info from the PSA */
+#define SET_PSA_CRC		/* Calculate and set the CRC on 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 */
@@ -355,11 +358,11 @@
 /************************ CONSTANTS & MACROS ************************/
 
 #ifdef DEBUG_VERSION_SHOW
-static const char	*version	= "wavelan.c : v16 (wireless extensions) 17/4/97\n";
+static const char	*version	= "wavelan.c : v18 (wireless extensions) 18/2/99\n";
 #endif
 
 /* Watchdog temporisation */
-#define	WATCHDOG_JIFFIES	32	/* TODO: express in HZ. */
+#define	WATCHDOG_JIFFIES	256	/* TODO: express in HZ. */
 
 /* Macro to get the number of elements in an array */
 #define	NELS(a)				(sizeof(a) / sizeof(a[0]))
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/pci/pci.c linux/drivers/pci/pci.c
--- linux.vanilla/drivers/pci/pci.c	Sun Dec  6 00:14:41 1998
+++ linux/drivers/pci/pci.c	Fri Mar 26 17:07:42 1999
@@ -110,6 +110,8 @@
 	DEVICE( DEC,		DEC_21052,	"DC21052"),
 	DEVICE( DEC,		DEC_21150,	"DC21150"),
 	DEVICE( DEC,		DEC_21152,	"DC21152"),
+	DEVICE( DEC,		DEC_21154,	"DC21154"),
+	DEVICE( DEC,		DEC_21285,	"DC21285 Footbridge"),
 	DEVICE( CIRRUS,		CIRRUS_7548,	"GD 7548"),
 	DEVICE( CIRRUS,		CIRRUS_5430,	"GD 5430"),
 	DEVICE( CIRRUS,		CIRRUS_5434_4,	"GD 5434"),
@@ -146,7 +148,11 @@
 	DEVICE( MATROX,		MATROX_MYS,	"Mystique"),
 	DEVICE( MATROX,		MATROX_MIL_2,	"Millennium II"),
 	DEVICE( MATROX,		MATROX_MIL_2_AGP,"Millennium II AGP"),
+	DEVICE( MATROX,         MATROX_G200_PCI,"Matrox G200 PCI"),
+	DEVICE( MATROX,         MATROX_G200_AGP,"Matrox G200 AGP"),
 	DEVICE( MATROX,		MATROX_MGA_IMP,	"MGA Impression"),
+	DEVICE( MATROX,         MATROX_G100_MM, "Matrox G100 multi monitor"),
+	DEVICE( MATROX,         MATROX_G100_AGP,"Matrox G100 AGP"),
 	DEVICE( CT,		CT_65545,	"65545"),
 	DEVICE( CT,		CT_65548,	"65548"),
 	DEVICE(	CT,		CT_65550,	"65550"),
@@ -219,6 +225,9 @@
 	DEVICE( X,		X_AGX016,	"ITT AGX016"),
 	DEVICE( PICOP,		PICOP_PT86C52X,	"PT86C52x Vesuvius"),
 	DEVICE( PICOP,		PICOP_PT80C524,	"PT80C524 Nile"),
+	DEVICE( MYLEX,		MYLEX_DAC960P_V2,"DAC960P V2"),
+	DEVICE( MYLEX,		MYLEX_DAC960P_V3,"DAC960P V3"),
+	DEVICE( MYLEX,		MYLEX_DAC960P_V4,"DAC960P V4"),
 	DEVICE( APPLE,		APPLE_BANDIT,	"Bandit"),
 	DEVICE( APPLE,		APPLE_GC,	"Grand Central"),
 	DEVICE( APPLE,		APPLE_HYDRA,	"Hydra"),
@@ -321,6 +330,7 @@
 	DEVICE( TRUEVISION,	TRUEVISION_T1000,"TARGA 1000"),
 	DEVICE( INIT,		INIT_320P,	"320 P"),
 	DEVICE( INIT,		INIT_360P,	"360 P"),
+	DEVICE(	TTI,		TTI_HPT343,	"HPT343"),
 	DEVICE( VIA,		VIA_82C505,	"VT 82C505"),
 	DEVICE( VIA,		VIA_82C561,	"VT 82C561"),
 	DEVICE( VIA,		VIA_82C586_1,	"VT 82C586 Apollo IDE"),
@@ -329,6 +339,7 @@
 	DEVICE( VIA,		VIA_82C586_0,	"VT 82C586 Apollo ISA"),
 	DEVICE( VIA,		VIA_82C595,	"VT 82C595 Apollo VP2"),
 	DEVICE( VIA,		VIA_82C597_0,	"VT 82C597 Apollo VP3"),
+	DEVICE( VIA,		VIA_82C598_0,	"VT 82C598 Apollo MVP3"),
 	DEVICE( VIA,		VIA_82C926,	"VT 82C926 Amazon"),
 	DEVICE( VIA,		VIA_82C416,	"VT 82C416MV"),
 	DEVICE( VIA,		VIA_82C595_97,	"VT 82C595 Apollo VP2/97"),
@@ -336,6 +347,8 @@
 	DEVICE( VIA,		VIA_82C586_3,	"VT 82C586B Apollo ACPI"),
 	DEVICE( VIA,		VIA_86C100A,	"VT 86C100A"),
 	DEVICE( VIA,		VIA_82C597_1,	"VT 82C597 Apollo VP3 AGP"),
+	DEVICE( VIA,		VIA_82C598_1,	"VT 82C598 Apollo MVP3 AGP"),
+	DEVICE( SMC2,           SMC2_1211TX,    "1211 TX"),
 	DEVICE( VORTEX,		VORTEX_GDT60x0,	"GDT 60x0"),
 	DEVICE( VORTEX,		VORTEX_GDT6000B,"GDT 6000b"),
 	DEVICE( VORTEX,		VORTEX_GDT6x10,	"GDT 6110/6510"),
@@ -472,11 +485,13 @@
 	DEVICE( S3,		S3_ViRGE_MXP,	"ViRGE/MX+"),
 	DEVICE( S3,		S3_ViRGE_MXPMV,	"ViRGE/MX+MV"),
 	DEVICE( S3,		S3_SONICVIBES,	"SonicVibes"),
+	DEVICE( DCI,    DCI_PCCOM4,     "PC COM PCI Bus 4 port serial Adapter"),
 	DEVICE( INTEL,		INTEL_82375,	"82375EB"),
 	BRIDGE( INTEL,		INTEL_82424,	"82424ZX Saturn",	0x00),
 	DEVICE( INTEL,		INTEL_82378,	"82378IB"),
 	DEVICE( INTEL,		INTEL_82430,	"82430ZX Aries"),
 	BRIDGE( INTEL,		INTEL_82434,	"82434LX Mercury/Neptune", 0x00),
+	BRIDGE( INTEL,		INTEL_I960,	"i960", 0x00),
 	DEVICE( INTEL,		INTEL_82092AA_0,"82092AA PCMCIA bridge"),
 	DEVICE( INTEL,		INTEL_82092AA_1,"82092AA EIDE"),
 	DEVICE( INTEL,		INTEL_7116,	"SAA7116"),
@@ -512,9 +527,12 @@
  	DEVICE( INTEL,		INTEL_82450GX,	"82450GX Orion P6"),
 	DEVICE(	KTI,		KTI_ET32P2,	"ET32P2"),
 	DEVICE( ADAPTEC,	ADAPTEC_7810,	"AIC-7810 RAID"),
+	DEVICE( ADAPTEC,	ADAPTEC_7821,	"AIC-7860"),
 	DEVICE( ADAPTEC,	ADAPTEC_7850,	"AIC-7850"),
 	DEVICE( ADAPTEC,	ADAPTEC_7855,	"AIC-7855"),
+	DEVICE( ADAPTEC,	ADAPTEC_3860,	"AIC-7860"),
 	DEVICE( ADAPTEC,	ADAPTEC_5800,	"AIC-5800"),
+	DEVICE( ADAPTEC,	ADAPTEC_1480A,	"AIC-1480A"),
 	DEVICE( ADAPTEC,	ADAPTEC_7860,	"AIC-7860"),
 	DEVICE( ADAPTEC,	ADAPTEC_7861,	"AIC-7861"),
 	DEVICE( ADAPTEC,	ADAPTEC_7870,	"AIC-7870"),
@@ -528,11 +546,26 @@
 	DEVICE( ADAPTEC,	ADAPTEC_7882,	"AIC-7882U"),
 	DEVICE( ADAPTEC,	ADAPTEC_7883,	"AIC-7883U"),
 	DEVICE( ADAPTEC,	ADAPTEC_7884,	"AIC-7884U"),
+	DEVICE( ADAPTEC,	ADAPTEC_7885,	"AIC-7885U"),
+	DEVICE( ADAPTEC,	ADAPTEC_7886,	"AIC-7886U"),
+	DEVICE( ADAPTEC,	ADAPTEC_7887,	"AIC-7887U"),
+	DEVICE( ADAPTEC,	ADAPTEC_7888,	"AIC-7888U"),
 	DEVICE( ADAPTEC,	ADAPTEC_1030,	"ABA-1030 DVB receiver"),
-  DEVICE( ADAPTEC2, ADAPTEC2_2940U2, "AHA-2940U2"),
-  DEVICE( ADAPTEC2, ADAPTEC2_7890, "AIC-7890/1"),
-  DEVICE( ADAPTEC2, ADAPTEC2_3940U2, "AHA-3940U2"),
-  DEVICE( ADAPTEC2, ADAPTEC2_7896, "AIC-7896/7"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_2940U2,"AHA-2940U2"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_2930U2,"AHA-2930U2"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7890B, "AIC-7890/1"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7890,	"AIC-7890/1"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_3940U2,"AHA-3940U2"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_3950U2D, "AHA-3950U2D"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7896, "AIC-7896/7"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7892A, "AIC-7892"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7892B, "AIC-7892"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7892D, "AIC-7892"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7892P, "AIC-7892"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7899A, "AIC-7899"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7899B, "AIC-7899"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7899D, "AIC-7899"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7899P, "AIC-7899"),
   	DEVICE( ATRONICS,	ATRONICS_2015,	"IDE-2015PL"),
 	DEVICE( TIGERJET,	TIGERJET_300,	"Tiger300 ISDN"),
 	DEVICE( ARK,		ARK_STING,	"Stingray"),
@@ -662,8 +695,8 @@
 	      case PCI_CLASS_STORAGE_SCSI:		return "SCSI storage controller";
 	      case PCI_CLASS_STORAGE_IDE:		return "IDE interface";
 	      case PCI_CLASS_STORAGE_FLOPPY:		return "Floppy disk controller";
-	      case PCI_CLASS_STORAGE_IPI:		return "IPI bus controller";
-	      case PCI_CLASS_STORAGE_RAID:		return "RAID bus controller";
+	      case PCI_CLASS_STORAGE_IPI:		return "IPI storage controller";
+	      case PCI_CLASS_STORAGE_RAID:		return "RAID storage controller";
 	      case PCI_CLASS_STORAGE_OTHER:		return "Unknown mass storage controller";
 
 	      case PCI_CLASS_NETWORK_ETHERNET:		return "Ethernet controller";
@@ -769,6 +802,7 @@
 	      case PCI_VENDOR_ID_N9:		return "Number Nine";
 	      case PCI_VENDOR_ID_UMC:		return "UMC";
 	      case PCI_VENDOR_ID_X:		return "X TECHNOLOGY";
+	      case PCI_VENDOR_ID_MYLEX:		return "Mylex";
 	      case PCI_VENDOR_ID_NEXGEN:	return "Nexgen";
 	      case PCI_VENDOR_ID_QLOGIC:	return "Q Logic";
 	      case PCI_VENDOR_ID_LEADTEK:	return "Leadtek Research";
@@ -797,7 +831,9 @@
 	      case PCI_VENDOR_ID_REALTEK:	return "Realtek";
 	      case PCI_VENDOR_ID_TRUEVISION:	return "Truevision";
 	      case PCI_VENDOR_ID_INIT:		return "Initio Corp";
+	      case PCI_VENDOR_ID_TTI:		return "Triones Technologies, Inc.";
 	      case PCI_VENDOR_ID_VIA:		return "VIA Technologies";
+	      case PCI_VENDOR_ID_SMC2:          return "SMC";
 	      case PCI_VENDOR_ID_VORTEX:	return "VORTEX";
 	      case PCI_VENDOR_ID_EF:		return "Efficient Networks";
 	      case PCI_VENDOR_ID_FORE:		return "Fore Systems";
@@ -811,6 +847,7 @@
 	      case PCI_VENDOR_ID_RENDITION:	return "Rendition";
 	      case PCI_VENDOR_ID_TOSHIBA:	return "Toshiba";
 	      case PCI_VENDOR_ID_RICOH:		return "Ricoh";
+	      case PCI_VENDOR_ID_ARTOP:	return "Artop Electronics";
 	      case PCI_VENDOR_ID_ZEITNET:	return "ZeitNet";
 	      case PCI_VENDOR_ID_OMEGA:		return "Omega Micro";
 	      case PCI_VENDOR_ID_NP:		return "Network Peripherals";
@@ -831,6 +868,7 @@
 	      case PCI_VENDOR_ID_AVANCE:	return "Avance";
 	      case PCI_VENDOR_ID_NETVIN:	return "NetVin";
 	      case PCI_VENDOR_ID_S3:		return "S3 Inc.";
+  	      case PCI_VENDOR_ID_DCI:       return "Decision Computer Int.";
 	      case PCI_VENDOR_ID_INTEL:		return "Intel";
 	      case PCI_VENDOR_ID_KTI:		return "KTI";
 	      case PCI_VENDOR_ID_ADAPTEC:	return "Adaptec";
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/ChangeLog.ncr53c8xx linux/drivers/scsi/ChangeLog.ncr53c8xx
--- linux.vanilla/drivers/scsi/ChangeLog.ncr53c8xx	Fri Jul 24 17:29:01 1998
+++ linux/drivers/scsi/ChangeLog.ncr53c8xx	Mon Dec 14 18:11:08 1998
@@ -1,5 +1,265 @@
-Fri Jan 2 18:00 1998 Gerard Roudier (groudier@club-internet.fr)
-	* Revision 2.5f
+Sun Dec 13  18:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 3.1e
+	- Same work-around as for the 53c876 rev <= 0x15 for 53c896 rev 1:
+	  Disable overlapped arbitration. This will not make difference 
+	  since the chip has on-chip RAM.
+
+Thu Nov 26  22:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 3.1d
+	- The SISL RAID change requires now remap_pci_mem() stuff to be 
+	  compiled for __i386__ when normal IOs are used.
+	- Minor spelling fixes in doc files.
+
+Sat Nov 21  18:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 3.1c
+	- Ignore chips that are driven by SISL RAID (DAC 960).
+	  Change sent by Leonard Zubkoff and slightly reworked.
+	- Still a buglet in the tags initial settings that needed to be fixed.
+	  It was not possible to disable TGQ at system startup for devices 
+	  that claim TGQ support. The driver used at least 2 for the queue 
+	  depth but did'nt keep track of user settings for tags depth lower 
+	  than 2.
+
+Wed Nov 11  10:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 3.1b
+	- The driver was unhappy when configured with default_tags > MAX_TAGS
+	  Hopefully doubly-fixed.
+	- Update the Configure.help driver section that speaks of TAGS.
+
+Wed Oct 21 21:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 3.1a
+	- Changes from Eddie Dost for Sparc and Alpha:
+	  ioremap/iounmap support for Sparc.
+	  pcivtophys changed to bus_dvma_to_phys.
+	- Add the 53c876 description to the chip table. This is only usefull 
+	  for printing the right name of the controller.
+	- DEL-441 Item 2 work-around for the 53c876 rev <= 5 (0x15).
+	- Add additionnal checking of INQUIRY data:
+	  Check INQUIRY data received length is at least 7. Byte 7 of 
+	  inquiry data contains device features bits and the driver might 
+	  be confused by garbage. Also check peripheral qualifier.
+	- Cleanup of the SCSI tasks management:
+	  Remove the special case for 32 tags. Now the driver only uses the 
+	  scheme that allows up to 64 tags per LUN.
+	  Merge some code from the 896 driver.
+	  Use a 1,3,5,...MAXTAGS*2+1 tag numbering. Previous driver could  
+	  use any tag number from 1 to 253 and some non conformant devices  
+	  might have problems with large tag numbers.
+	- 'no_sync' changed to 'no_disc' in the README file. This is an old 
+	  and trivial mistake that seems to demonstrate the README file is 
+	  not often read. :)
+
+Sun Oct  4 14:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 3.0i
+	- Cosmetic changes for sparc (but not for the driver) that needs  
+	  __irq_itoa() to be used for printed IRQ value to be understandable.
+	- Some problems with the driver that didn't occur using driver 2.5f 
+	  were due to a SCSI selection problem triggered by a clearly 
+	  documented feature that in fact seems not to work: (53C8XX chips  
+	  are claimed by the manuals to be able to execute SCSI scripts just 
+	  after abitration while the SCSI core is performing SCSI selection).
+	  This optimization is broken and has been removed.
+	- Some broken scsi devices are confused when a negotiation is started 
+	  on a LUN that does not correspond to a real device. According to 
+	  SCSI specs, this is a device firmware bug. This has been worked 
+	  around by only starting negotiation if the LUN has previously be 
+	  used for at least 1 successful SCSI command.
+	- The 'last message sent' printed out on M_REJECT message reception 
+	  was read from the SFBR i/o register after the previous message had 
+	  been sent. 
+	  This was not correct and affects all previous driver versions and 
+	  the original FreeBSD one as well. The SCSI scripts has been fixed 
+	  so that it now provides the right information to the C code.
+
+Sat Jul 18 13:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 3.0g
+	- Preliminary fixes for Big Endian (sent by Eddie C. Dost).
+	  Big Endian architectures should work again with the driver.
+	  Eddie's patch has been partially applied since current 2.1.109 
+	  does not have all the Sparc changes of the vger tree.
+	- Use of BITS_PER_LONG instead of (~0UL == 0xffffffffUL) has fixed
+	  the problem observed when the driver was compiled using EGCS or 
+	  PGCC.
+
+Mon Jul 13 20:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 3.0f
+	- Some spelling fixes.
+	- linux/config.h misplaced in ncr53c8xx.h
+	- MODULE_PARM stuff added for linux 2.1.
+	- check INQUIRY response data format is exactly 2. 
+	- use BITS_PER_LONG if defined. 
+
+Sun Jun 28 12:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 3.0e
+	- Some cleanup, spelling fixes, version checks, documentations 
+	  changes, etc ...
+
+Sat Jun 20 20:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 3.0c
+	- Add a boot setup option that allows to set up device queue depths 
+	  at boot-up. This option is very usefull since Linux does not 
+	  allow to change scsi device queue depth once the system has been 
+	  booted up.
+
+Sun Jun 15 23:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 3.0a
+	- Support for up to 64 TAGS per LUN.
+	- Rewrite the TARGET vs LUN capabilities management.
+	  CmdQueue is now handled as a LUN capability as it shall be.
+	  This also fixes a bug triggered when disabling tagged command 
+	  queuing for a device that had this feature enabled.
+	- Remove the ncr_opennings() stuff that was useless under Linux 
+	  and hard to understand to me.
+	- Add "setverbose" procfs driver command. It allows to tune 
+	  verbose level after boot-up. Setting this level to zero, for 
+	  example avoid flooding the syslog file.
+	- Add KERN_XXX to some printk's.
+
+Tue Jun 10 23:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 3.0
+	- Linux config changes for 2.0.34:
+	  Remove NVRAM detection config option. This option is now enabled 
+	  by default but can be disabled by editing the driver header file.
+	  Add a PROFILE config option.
+	- Update Configure.help
+	- Add calls to new function mdelay() for milli-seconds delay if 
+	  kernel version >= 2.1.105.
+	- Replace all printf(s) by printk(s). After all, the ncr53c8xx is 
+	  a driver for Linux.
+	- Perform auto-sense on COMMAND TERMINATED. Not sure it is usefull.
+	- Some other minor changes.
+
+Tue Jun 4 23:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 2.6n
+	- Code cleanup and simplification:
+	  Remove kernel 1.2.X and 1.3.X support.
+	  Remove the _old_ target capabilities table.
+	  Remove the error recovery code that have'nt been really usefull.
+	  Use a single alignment boundary (CACHE_LINE_SIZE) for data 
+	  structures.
+	- Several aggressive SCRIPTS optimizations and changes:
+	  Reselect SCRIPTS code rewritten.
+	  Support for selection/reselection without ATN.
+	  And some others.
+	- Miscallaneous changes in the C code:
+	  Count actual number of CCB queued to the controller (future use).
+	  Lots of other minor changes.
+
+Wed May 13 20:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 2.6m
+	- Problem of missed SCSI bus reset with the 53C895 fixed by 
+	  Richard Waltham. The 53C895 needs about 650 us for the bus 
+	  mode to settle. Delays used while resetting the controller 
+	  and the bus have been adjusted. Thanks Richard!
+	- Some simplification for 64 bit arch done ccb address testing.
+	- Add a check of the MSG_OUT phase after Selection with ATN.
+	- The new tagged queue stuff seems ok, so some informationnal 
+	  message have been conditionned by verbose >= 3.
+	- Donnot reset if a SBMC interrupt reports the same bus mode.
+	- Print out the whole driver set-up. Some options were missing and 
+	  the print statement was misplaced for modules.
+	- Ignore a SCSI parity interrupt if the chip is not connected to 
+	  the SCSI bus.
+
+Sat May 1 16:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 2.6l
+	- Add CCB done queue support for Alpha and perhaps some other 
+	  architectures.
+	- Add some barriers to enforce memory ordering for x86 and 
+	  Alpha architectures.
+	- Fix something that looks like an old bug in the nego SIR 
+	  interrupt code in case of negotiation failure.
+
+Sat Apr 25 21:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 2.6k
+	- Remove all accesses to the on-chip RAM from the C code:
+	  Use SCRIPTS to load the on-chip RAM.
+	  Use SCRIPTS to repair the start queue on selection timeout.
+	  Use the copy of script in main memory to calculate the chip 
+	  context on phase mismatch.
+	- The above allows now to use the on-chip RAM without requiring 
+	  to get access to the on-chip RAM from the C code. This makes 
+	  on-chip RAM useable for linux-1.2.13 and for Linux-Alpha for 
+	  instance.
+	- Some simplifications and cleanups in the SCRIPTS and C code.
+	- Buglet fixed in parity error recovery SCRIPTS (never tested).
+	- Minor updates in README.ncr53c8xx.
+
+Wed Apr 15 21:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 2.6j
+	- Incorporate changes from linux-2.1.95 ncr53c8xx driver version.
+	- Add SMP support for linux-2.1.95 and above.
+	- Fix a bug when QUEUE FULL is returned and no commands are 
+	  disconnected. This happens with Atlas I / L912 and may happen 
+	  with Atlas II / LXY4.
+	- Nail another one on CHECK condition when requeuing the command 
+	  for auto-sense.
+	- Call scsi_done() for all completed commands after interrupt 
+	  handling.
+	- Increase the done queue to 24 entries.
+
+Sat Apr 4 20:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 2.6i
+	- CTEST0 is used by the 53C885 for Power Management and 
+	  priority setting between the 2 functions.
+	  Use SDID instead as actual target number. Just have had to 
+	  overwrite it with SSID on reselection.
+	- Split DATA_IN and DATA_OUT scripts into 2 sub-scripts.
+	  64 segments are moved from on-chip RAM scripts.
+	  If more segments, a script in main memory is used for the 
+	  additionnal segments.
+	- Since the SCRIPTS processor continues SCRIPTS execution after 
+	  having won arbitration, do some stuff prior to testing any SCSI 
+	  phase on reselection. This should have the vertue to process 
+	  scripts in parallel with the SCSI core performing selection.
+	- Increase the done queue to 12 entries.
+
+Sun Mar 29 12:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 2.6h
+	- Some fixes.
+
+Tue Mar 26 23:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 2.6g
+	- New done queue. 8 entries by default (6 always useable).
+	  Can be increased if needed.
+	- Resources management using doubly linked queues.
+	- New auto-sense and QUEUE FULL handling that does not need to 
+	  stall the NCR queue any more.
+	- New CCB starvation avoiding algorithm.
+	- Prepare CCBs for SCSI commands that cannot be queued, instead of 
+	  inserting these commands into the waiting list. The waiting list 
+	  is now only used while resetting and when memory for CCBs is not 
+	  yet available?
+
+Sun Feb 8 22:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 2.6f
+	- Some fixes in order to really support the 53C895, at least with 
+	  FAST-20 devices.
+	- Heavy changes in the target/lun resources management to allow 
+	  the scripts to jump directly to the CCB on reselection instead 
+	  of walking on the lun CCBs list. Up to 32 tags per lun are now 
+	  supported without script processor and PCI traffic overhead.
+
+Sun Jan 11 22:00 1998 Gerard Roudier (groudier@club-internet.fr)
+	* revision 2.6d
+	- new (different ?) implementation of the start queue:
+          Use a simple CALL to a launch script in the CCB.
+	- implement a minimal done queue (1 entry :-) ).
+          this avoid scanning all CCBs on INT FLY (Only scan all CCBs, on 
+          overflow). Hit ratio is better than 99.9 % on my system, so no 
+          need to have a larger done queue.
+	- generalization of the restart of CCB on special condition as 
+          Abort, QUEUE FULL, CHECK CONDITION.
+          This has been called 'silly scheduler'.
+	- make all the profiling code conditionned by a config option.
+	  This spare some PCI traffic and C code when this feature is not 
+          needed.
+	- handle more cleanly the situation where direction is unknown.
+	  The pointers patching is now performed by the SCRIPTS processor.
+	- remove some useless scripts instructions.
+
+	Ported from driver 2.5 series:
+        ------------------------------
 	- Use FAST-5 instead of SLOW for slow scsi devices according to 
 	  new SPI-2 draft.
 	- Make some changes in order to accomodate with 875 rev <= 3 
@@ -10,1142 +270,13 @@
 	  . Memory Read Line is not enabled for 875 and 875-like chips.
 	  . Programmed burst length set to 64 DWORDS (instead of 128).
 	    (Note: SYMBIOS uses 32 DWORDS for the SDMS BIOS)
-
-Sun Oct 26 12:00 1997 Gerard Roudier (groudier@club-internet.fr)
-	* revision 2.5e
 	- Add 'buschk' boot option.
 	  This option enables checking of SCSI BUS data lines after SCSI 
 	  RESET (set by default). (Submitted by Richard Waltham).
 	- Update the README file.
-
-Sat Oct 4 18:00 1997 Gerard Roudier (groudier@club-internet.fr)
-	* revision 2.5d
 	- Dispatch CONDITION MET and RESERVATION CONFLICT scsi status 
 	  as OK driver status.
 	- Update the README file and the Symbios NVRAM format definition 
 	  with removable media flags values (available with SDMS 4.09).
-
-Sat Sep 20 21:00 1997 Gerard Roudier (groudier@club-internet.fr)
-	* revision 2.5c
 	- Several PCI configuration registers fix-ups for powerpc.
 	  (Patch sent by Cort).
-
-Thu Aug 28 10:00 1997 Gerard Roudier (groudier@club-internet.fr)
-	* revision 2.5b
-	- Add 'ncr53c8xx' char pointer variable. This variable allows to 
-	  pass a boot command to the driver when it is loaded as a module.
-	  Option separator is ' ' instead of ','. Example:
-	    insmod <mod_path>/ncr53c8xx.o ncr53c8xx='verb:2 sync:0 specf:n'
-	- Always use 'driver_setup.settle_delay' for internal resets.
-	  2 seconds hardcoded is sometimes too short. Suggested by Richard W. 
-	  This delay may be shortenned in order to avoid spurious timeouts.
-	- Fix release module stuff that failed for more than 1 controller.
-	- For linux versions > 1.3.70, trust the 'dev_id' parameter passed 
-	  to the interrupt handler (dev_id = struct ncb *).
-	- Fix up in 'ncr_log_hard_error()' when the DSP points outside scripts.
-	  Suggested by Stefan Esser.
-
-Tue Aug 23 23:43 1997 Gerard Roudier (groudier@club-internet.fr)
-	* revision 2.5a
-	- Update Configure.help for inclusion in linux-2.1.51/2/3
-	- Use BASE_2 address from PCI config space instead of some 
-	  IO register for getting the on-board SRAM bus address.
-	- Remove error testing of pcibios_read/write functions.
-	  These functions are intended to be used for successfully 
-	  detected PCI devices. Expecting error condition from them 
-	  is nothing but paranoia.
-
-Thu Aug 21 23:00 1997 Gerard Roudier (groudier@club-internet.fr)
-	* revision 2.5
-	- 53C860 chip support fix.
-	- Move the 'host_status' to the last DWORD of the CCB header.
-	  This header is copied back by the script processor. This 
-	  guarantees that the header is entirely copied back over 
-	  the PCI when the CPU completes a CCB.
-	- (re)read ISTAT prior to scanning CCBs for completion. This
-	  ensure that any posted buffer are flushed prior CCBs scan.
-	- Support for BIG ENDIAN cpu. Added by Cort <cort@cs.nmt.edu>.
-	  Initial patch did'nt support disconnections and tagged commands.
-	  I've completed the patch and it seems that all is ok now.
-	  Only some powerpc under 2.1.X is supported for the moment.
-	- Misc. trivial fixes and cleanups.
-
-Sat July 26 18:00 1997 Gerard Roudier (groudier@club-internet.fr)
-	* revision 2.4
-	Several clean-ups:
-	  - Asynchronous pre-scaler calculation.
-	    Synchronous divisor calculation.
-	  - Use FE_ as feature identifier prefix instead of _F_.
-	  - Change 'ns_sync' identifier to "minsync".
-	  - Some others.
-	Apply some SPI2-R12 recommendations.
-	  - Use Slow, Fast-10, Fast-20, Fast-40 SCSI instead of SCSI-2, 
-	    FAST SCSI-2, ULTRA, ULTRA-2. 
-	  - Reset the SCSI on bus mode change.
-
-Wed July 02 22:58 1997 Gerard Roudier (groudier@club-internet.fr)
-	* revision 2.3c
-	- Add define SCSI_NCR_PCI_FIX_UP_SUPPORT for conditionnal compilation 
-	  of the corresponding pci fix-up code when a small driver is needed.
-	- Use "ncr53c8xx" as driver name for both request_irq() and 
-          request_region(). Using different names confused 'lsdev'.
-          (Suggestion sent by Henrik Storner).
-
-Wed June 24 22:08 1997 Gerard Roudier (groudier@club-internet.fr)
-	* revision 2.3b
-	- Print an error message on unexpected boot command line option.
-	- Switch to asynchronous data transfer mode after SCSI wide 
-	  negotiation.
-
-Wed June 24 22:08 1997 Gerard Roudier (groudier@club-internet.fr)
-	* revision 2.3b
-	- Print an error message on unexpected boot command line option.
-	- Switch to asynchronous data transfer mode after SCSI wide 
-	  negotiation.
-
-Wed June 14 22:00 1997 Gerard Roudier (groudier@club-internet.fr)
-	* revision 2.3a
-	- Add PCI LATENCY TIMER fixup code.
-	  Increase it if necessary according to burst size.
-	  Boot option bit : 'pcifix:4'
-	- On phase mismatch, calculate residual data size for all OUTPUT 
-	  phases. That's only required for interrupted DATA OUT phase, but 
-	  this information is usefull for problem solving.
-	- Add KERN_INFO to some messages printed to the log.
-	  (Patch sent by Wolfram Kleff).
-
-Tue June 02 22:30 1997 Gerard Roudier (groudier@club-internet.fr)
-	* revision 2.3
-	- NvRAM support code slightly improved (I think):
-	  Use IO or MMIO according to driver setup for reading the NvRAM.
-	  Use structures for NvRAM data instead of raw data.
-	- Prevent from queuing more than 1 command to the scsi SCRIPT with 
-	  negotiation attached when tagged command queueing is enabled.
-	- Fix-up for old 53C8XX chips that support PCI READ LINE but not 
-	  CACHE LINE SIZE. If the cache line size is unknown, set burst 
-	  to 8 dwords and disable READ LINE, otherwise set burst max to 
-	  the cache line size value.
-
-Sat May 24 12:30 1997 Gerard Roudier (groudier@club-internet.fr)
-	* revision 2.2c (for linux-2.1.40)
-	- Remove reference to 'x86' symbol when MODULE is defined, since this 
-	  symbol is not exported for module loading.
-	  The value of 'x86' is used for fixing up the PCI CACHE LINE SIZE 
-	  configuration register.
-	- Bytes/words read one bit at a time from the serial NVRAM were'nt 
-          initialized with zero.
-	- Some comments added. Minor cosmetic changes.
-
-Mon May 19 20:30 1997 Gerard Roudier (groudier@club-internet.fr)
-	* revision 2.2b
-	- Patch for NVRAM support by Richard Waltham applied.
-	  The code detects Symbios NVRAM format and Tekram NVRAM format. 
-	  This enhancement allows to get hosts and devices user set up 
-	  from the NVRAM.
-	- Use the NVRAM contents when present to initialize user definable 
-	  target parameters.
-	- Update the README file. 
-
-Sun May 11 22:30 1997 Gerard Roudier (groudier@club-internet.fr)
-	* revision 2.1b
-	- Cosmetic changes.
-	- Some heavy testings under pre-linux-2.1.37-6
-
-Sun May 4 22:30 1997 Gerard Roudier (groudier@club-internet.fr)
-	* revision 2.1a
-	- PFEN wrongly used for PREFETCH feature bit testing.
-	  Changed to _F_PFEN.
-	- 2 SCR_COPY that need NO FLUSH bit to be removed had been missed 
-	  in tp->getscr[] script (loads SXFER and SCNTL3 on reselection).
-
-Sat May 3 22:30 1997 Gerard Roudier (groudier@club-internet.fr)
-	* revision 2.1
-	- Use the NO FLUSH option for MOVE MEMORY (COPY) each time it is 
-	  possible. More than 100 COPY with NO FLUSH and 6 with FLUSH for  
-	  my configuration (max queued command / device = 8).
-	  This option bit is removed from the script instance for chips 
-	  that donnot support prefetching.
-	- Rewrite the ncr_exception() routine more simple (I think) and 
-	  remove useless code.
-	- Change the data_in and data_out script management.
-	  Use the bottom part of these scripts instead of the beginning.
-	  That avoids to zero the scatter/gather array when a command is 
-	  queued (1k) and to deal with some weird IID on MOVE 0 bytes when 
-	  a target wants to transfer more bytes than expected.
-	- Misc. improvements in the init code.
-	- Remove IOMAPPED/MMIO automatic switching option.
-	  Was useless and reported not reliable.
-	- Fix a double read of DSTAT and remove DFE testing in the 
-	  Phase mismatch service routine.
-	- Etc...
-
-Fri Apr 26 20:00 1997 Gerard Roudier (groudier@club-internet.fr)
-	* revision 2.0a
-	- Add support if the Diamond FirePort 40 (SYM53C875J chip)
-
-Mon Apr 22 22:00 1997 Gerard Roudier (groudier@club-internet.fr)
-	* revision 2.0
-	- incorporate __initdata and __initfunc directives in order to 
-	  allow 'init' to free unused memory after driver initialisations.
-	  Patch sent by Roberto Fichera.
-	- rewrite the init code of the driver. Now a feature descriptor 
-	  is used for each real chip types. The code is a lot more clean,
-	  since the driver uses device and revision ids only in the 
-	  detection procedure.
-	- add 'pcifix' boot command line. This command allows to fix up PCI 
-	  config space for new chips which support features based on the 
-	  cache line size and 'write and invalidate'.
-	- incorporate in the driver, the code used for error recovery  
-	  testing. This code is normally not compiled; have to define 
-	  SCSI_NCR_DEBUG_ERROR_RECOVERY in order to compile it.
-	- take into account actual SCSI bus mode for 53C895 LVD/SE controller.
-	  In single ended mode only fast20 is supported.
-          (Just to not be late since such controllers are not yet available)
-          
-
-Sat Apr 20 21:00 1997 Gerard Roudier (groudier@club-internet.fr)
-	* revision 1.18f
-	- fix an old bug included in the initial port (version 0.0).
-	  The driver allocated 10 bytes of static data and uses 12 bytes.
-	  No danger, since data are generally aligned on 4 bytes boundary 
-	  and so byte 10 and 11 are free (I hope ...)
-
-Wed Apr 16 12:00 1997 Gerard Roudier (groudier@club-internet.fr)
-	* revision 1.18e
-	- reset all when an unexpected data cycle is detected while 
-	  disconnecting.
-	- make changes to abort() ans reset() functions according to 
-	  Leonard's documentation.
-	- small fix in some message for hard errors.
-
-Sat Apr 5  13:00 1997 Gerard Roudier (groudier@club-internet.fr)
-	* revision 1.18d
-	- Probe NCR pci device ids in reverse order if asked by user from 
-	  the boot command line. Suggested by Richard Waltham.
-	- Make a separate function that prints out verbose information on 
-	  severe error (assumed from hardware).
-	- Add the transfer period factor and the max commands per lun value 
-	  to the proc info data. If debug flags are set or verbosity is 
-	  greater than 1, debug flags and verbosity are returned in proc  
-	  info data.
-	- Update the documentation.
-
-Thu Mar 20 23:00 1997 Gerard Roudier (groudier@club-internet.fr)
-	* revision 1.18c
-	- Add special features support for NCR53C885 and NCR53C896 chip.
-	  Quite obvious, but untested, and based on the fact that:
-	  The 885 supports same features as the 875.
-	  The 896 is a 64 bits PCI version of the 895.
-	- Improve recovery from SCSI GROSS ERRORS.
-	  I can get such errors by making the driver negotiate offset 8 with 
-	  a disk and setting the ncr chip to a lower offset value.
-	  I got bunches of errors that have been gracefully recovered by 
-	  the driver.
-	  The driver now uses its timer handler in order to wait 2 sec. for 
-	  devices to settle after SCSI reset and so does not uselessly freeze 
-	  the system with interrupt masked for seconds.
-	- Enable 'burst op code fetch' and 'read line' for 815 chips.
-	- Use a 2 commands queue depth instead of 1 for devices that does 
-          not support tagged command queuing.
-	- The ULTRA timing flag setting was based on the output resulting 
-	  period factor of the ncr and not on the negotiated one.
-	  This flag setting was wrong only for 24 ns negotiated period factor.
-	- Some other minor changes and cleanups.
-
-Thu Feb 27 23:00 1997 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c ncr53c8xx.h revision 1.18b
-	- 'On paper' support of the NCR53C895 Ultra-2 chip.
-	  (Clock quadrupler + 7 clock divisors)
-	- Load the main part of the script into the on-board RAM.
-	- 810A rev. 0x11 PCI problem fixed.
-	  This chip is now supported with all PCI features enabled and 
-	  16 dwords burst transfers. 
-	- Align on 32 boundary some internal structures.
-	  That fixes the 810A problem and allows cache line bursting when 
-	  moving the global header (64 bytes) from/to CCBs to/from NCB.
-	- Synchronous parameters calculation rewritten. The driver 
-	  now uses all available clock divisors and will be able to support 
-          clock frequencies that are not multiple of 40 Mhz if necessary.
-
-Sat Feb 8 22:00 1997 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c - revision 1.17a
-	- IRQ mode set up from boot setup command.
-	  irqm:0    open drain (default)
-	  irqm:1    preserve initial setting (assumed from BIOS)
-	  irqm:2    totem pole
-	- DIFF mode set up from boot setup command.
-	  Suggested by Richard Waltham.
-	  diff:0    never set up diff mode (default)
-	  diff:1    set up diff mode according to initial setting (BIOS?)
-	  diff:2    always set up diff mode
-	  diff:3    set up diff mode if GPIO3 is zero (SYMBIOS boards)
-	- Change CONFIG option for LED support.
-          CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT allows LED support and 
-	  DIFF support for SYMBIOS boards and compatibles (clones?).
-	- Set 16 DWORD bursts for 810A rev. >= 0x12 since my SC200 with 
-	  such a chip have no problem with it (MB with Triton 2 HX).
-	  810A rev. 0x11 are set to 8 DWORD bursts since they may give
-	  problems with PCI read multiple and Triton 2 HX.
-	  Thanks to Stefan for this information.
-
-Sat Jan 25 22:00 1997 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c - revision 1.17
-	- Controller LED support.
-	  Only works with LED pin wired to GPIO_FETCHN, so probably with 
-          all boards using SMDS BIOS.
-	  This option can be enabled only if CONFIG_EXPERIMENTAL is set.
-	- Assume clock doubler for 875 chip when clock frequency measurement 
-	  result is 40 MHz. May help when some old stuff as SDMS BIOS 3.0 
-	  or some old driver has broken the normal BIOS settings.
-	- Add wide negotiation control from boot setup command.
-	  May be usefull with systems using a 875 based board connected to 
-	  a wide device through a 50 pins to 68 pins converter.
-	- Add a "boot fail safe option" to the boot setup command line.
-	- Rewrite the "reset_command" routine.
-	  Low-level driver are responsible to keep the involved command 
-	  alive. The new code seems to behave correctly.
-	- Change some variables used by the script from u_long to u_int32.
-	- Remove some useless code.
-
-Sun Jan 12 12:00 1997 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c - revision 1.16e
-	- Add support of PCI burst length control from boot setup command.
-	  burst:0     disable burst
-	  burst:255   get burst from initial settings (BIOS settings?)
-	  burst:#x    set burst transfers to 1<<#x
-	- Only check xfer direction for common op-codes.
-	  For all device specific / vendor specific opcodes the driver 
-	  now uses the xfer direction decided by the target.
-
-Sun Jan 05 12:00 1997 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c - revision 1.16d
-	- The driver is now able to process scsi commands without 
-	  knowledge of xfer data direction.
-	  Stefan agreed with this change for Linux. This change is 
-	  not needed under FreeBSD since low-level drivers receive 
-	  the expected data direction for each scsi request.
-	- Save ctest5 features bits at start-up and restore them at 
-	  module release step.
-	  Avoid side effects when a ncr driver which trusts bios 
-	  settings is reloaded (could be the ncr53c8xx itself).
-
-
-Wed Jan 01 23:30 1997 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c - revision 1.16c
-	- Bad decision about 20MHz for 13 ns period factor.
-	  Was wrong, so I restore the previous algorithm.
-	- Burst length 128 not correctly set in dmode.
-
-Thu Dec 26 22:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c ncr53c8xx.h README.ncr53c8xx - revision 1.16b
-	- Remove useless code.
-	- Try to improve error recovery in case of abort and reset.
-	- Remove DEBUG_NEGO by default.
-	- Add boot setup command support.
-	  Now, all experimental config options can be removed.
-	- Update README file.
-
-
-Mon Dec 23 23:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c ncr53c8xx.h - revision 1.16a
-	New display for speed ##.# MB/s (From Stefan)
-	- I add "WIDE" qualifier after ULTRA and FAST
-	- I get "FAST WIDE SCSI-2 20 MB/s" with my Atlas. That's nice.
-
-	Richard Waltham reports SYMBIOS set the 875 to 20 MB/s for 13 ns 
-	period factor. I decide to trust SYMBIOS. 20 MB/s output speed 
-	instead of 19.2 MB/s should not cause problem. The ncr is only able 
-	to use 16.67 MB/s when 20 MB/s is not possible.
-
-	Fix from Markus Kossman: "Ultra SCSI enabled" wrongly printed 
-	when not enabled.
-
-	Set DEBUG_NEGO by default in order to get reports about sync nego. 
-	Will remove it in the next patch.
-
-Thu Dec 19 21:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c ncr53c8xx.h README.ncr53c8xx - revision 1.16
-	Incorporate new definitions in ncr53c8xx.h (From Stefan).
-	Check changes against Stefan's current version of the driver.
-	All seems ok.
-
-Sat Nov 30 21:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c ncr53c8xx.h
-	Make changes in order to support: 
-	- Clock doubler and so 80 Mhz scsi clock for 875 chips.
-	- Sync transfers below 7.5 MB/sec.
-          Use Clock/2 between 5 and 10 Mega-transfers/s and Clock/4 below 5.
-	- Ultra SCSI data transfers.
-	- Offset 16.
-
-	Works with my configuration. However I cannot test Ultra transfers,
-	since my disks are only fast scsi-2.
-
-Tue Nov 28 21:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c
-	I received yesterday my Promise SCSI Ultra board.
-	NCR53C875 rev. 3 with clock doubler.
-	Add the code to support some bus features, the large 536 dma fifo and 
-	burst 128. Works.
-
-Mon Nov 4 21:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c ncr53c8xx.h - revision 1.14c
-	Severall control command improvements:
-
-	- Allow to specify "all" to commands that apply to #target.
-	  For example: "setsync all 255" sets asynchronous data 
-	               transfers for all targets on a bus.
-
-	- Allow to control disconnection privilege per device, as follow:
-	  "setflag #target no_sync" disables disconnection for #target.
-	  "setflag #target" with no flag specified reenables it.
-
-	  Obviously #target may be specified as "all" in order to control 
-	  disconnection for all targets with a single control command.
-
-	- README file updated and some hints about SCSI problems solving added.
-
-Sun Oct 27 22:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c ncr53c8xx.h - revision 1.14b
-	Add the following config parameters:
-
-	- CONFIG_SCSI_NCR53C8XX_MAX_TAGS
-	  Max number of queued tagged commands.
-	  Allow from 2 to 12, default 4.
-
-	- CONFIG_SCSI_NCR53C8XX_SYNC
-	  Synchronous transfers frequency in MHz.
-	  Allow from 5 to 10, default 5, 0 means asynchronous.
-	  (And so remove CONFIG_SCSI_NCR53C8XX_FORCE_ASYNCHRONOUS)
-
-Sun Oct 20 16:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c
-	ncr_scatter() rewritten.
-	remove "ncr dead" detection.
- 
-Sun Oct 13 19:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c ncr53c8xx.h - revision 1.14a
-	Enabling some special features makes problems with some hardware.
-	So, disable them by default.
-	Add SCSI_NCR_SPECIAL_FEATURES define to play with.
- 
-Sun Oct 13 14:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c ncr53c8xx.h
-	Incorporate Stefan's patch for clock frequency detection.
-	(Committed in FreeBSD/ncr.c rev. 1.81).
-	The driver then does about the following:
-	    Assume 40 MHz clock for all ncr chips except:
-	    - NCR53C860 chips:
-	        Assume 80 Mhz clock.
-	    - NCR53C875 chips:
-	        If clock doubler enabled, disable it and assume 40 Mhz clock.
-	        Else if (scntl3&7)=0 measure scsi clock frequency.
-	        Else trust bios setting of scntl3&7 (3=40 Mhz, 5=80Mhz).
-
-Wed Oct 9 22:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c - release 1.14
-	For now, just change the clock detection as follow:
-	- If clock doubler selected by BIOS, assume 40 MHz clock since 
-	  clock doubler will be disabled by chip reset.
-	- Else if NCR53C860 assume 80 MHz clock.
-	- Else trust BIOS setting if (scntl3&7 >= 3)
-	- Else assume 40 MHz clock.
-
-Sat Oct 05  17:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c
-	Stefan sent me a patch that improves the clock frequency detection 
-	of the driver. Stefan uses the general timer register stime1 in 
-	order to measure as accurately as possible the scsi clock.
-	Works ok with my 825, but needs still testing. So will be 
-	released later.
-
-Sun Sep 29  17:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c
-	Preserve dcntl/dmode/ctest3/ctest4 features bits at start-up.
-	Add the define option SCSI_NCR_TRUST_BIOS_SETTING.
-	- If this option is defined, the driver will preserve the 
-	  corresponding bits of io registers.
-	- Else, the driver will set features bits according to chip 
-	  and revision ids.
-
-Sun Sep 22  17:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c
-	Remove useless fields and code and so spare cpu:
-	- profile data are accumulated in jiffies ticks and converted 
-	  to milli-seconds when read through proc fs.
-	- when IOMAPPED is not defined, try only MMIO.
-	  (avoid testing a value in order to choose between IO and MMIO)
-
-Sun Sep 01  20:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.h, ncr53c8xx.c - Version 1.13
-	Adaptation of the tagged command queuing depth control of the 
-	FreeBSD driver to Linux. Now, tagged command queueing can be 
-	disabled at run time by a "settags N 0" control command.
-	Add the following heuristic in order to manage intelligently (perhaps) 
-	QUEUE_FULL status:
-	- Each time a QUEUE FULL status is returned by a device, disable tagged 
-	  command queuing for that device.
-	- Every 100 successfully complete commands, increment the maximum 
-	  queuable commands (up to the allowed limit).
-
-Fri Aug 30  10:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c - Version 1.12c
-	Incorporate the changes of FreeBSD/ncr.c revision 1.76.
-	The changes add support for the 53c860 and 53c875, 
-	but without taking advantage of the new features.
-	Those chips are used exactly as the old 53c810.
-
-Sun Jul 21  00:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c, README.ncr53c8xx
-	Add the ncr53c8xx_select_queue_depths() function.
-	Set queue_depth to SCSI_NCR_MAX_TAGS (4 by default) for devices that 
-	support tagged command queueing.
-	For other devices, set queue_depth to 1. No need to queue a command 
-	to the driver if this command cannot be sent to the device.
-	Each time the driver hide io requests from the kernel and/or from the 
-	driver, it may break a little (or a lot) optimization algorithms that 
-	try to increase throughput by reordering io requests.
-	It is better to enable the disk write caching to reduce latencies for 
-	write operations, and to trust asynchronous read ahead from the device 
-	and from the kernel that can reduce latencies for read operations, 
-	even when tagged command queuing is not supported or enabled.
-
-Sat Jul 20  20:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c
-	Minor changes:
-	- Problem of "CCB address mismatch" that happens with the 3 versions 
-	  of the driver. The CCB is correct and Stefan Esser suggests a little 
-	  patch that seems to be a bypass.
-	  Stefan says he will change that in a future version of the BSD driver.
-	- Set burst transfers to 8 for 815 chips.
-
-Sun Jul 14  15:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c, Configure.help
-	Memory mapped io donnot work under linux/Alpha for the driver.
-	For the moment it is better to not support this feature for this 
-	architecture.
-
-Tue Jul 09  20:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c
-	Garbage printed out with the following command (fixed):
-	- cat <some proc scsi device> /proc/scsi/ncr53c8xx/0
-
-Sun Jul 07  20:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c
-	Tagged command queueing cannot be disabled at run time.
-	I probably never try that because I felt the risk.
-	Shortest patch sent to Linus. I have to plan something better.
-
-Wed Jul 03  23:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	Release 1.12a
-	Tested linux releases: 1.2.13, 2.0.0, 2.0.1
-
-Mon Jul 01  21:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	ncr53c8xx.h, ncr53c8xx.c
-	Add "clearprof" user command that clear the profile counters.
-	Automatically clear profile counters when num_kbytes=1000000000 
-	in order to avoid ugly overflow.
-	Donnot compile user command code and profile data with 1.2.13.
-
-Wed Jun 29  20:38 1996 Gerard Roudier (groudier@club-internet.fr)
-	Matthew Geier reported to me a weird problem of unexpected 
-	disconnection while asynchronous negotiation.
-	The message sent by the driver is 1-3-1-ff-00. I sent a patch to 
-	Matthew that change the message to 1-3-1-00-00.
-	The sync msgout was correct however some devices might be to happy 
-	with ff.
-
-Wed Jun 26  22:57 1996 Gerard Roudier (groudier@club-internet.fr)
-	Patch no 4 sent to Harald.
-	The drived used "wtime" for timeouts adn time measurements.
-	I change for jiffies.
-	Work with my P133.
-
-Mon Jun 24  23:05 1996 Gerard Roudier (groudier@club-internet.fr)
-	Patch no 3 sent to Harald.
-
-Sun Jun 23  22:29 1996 Gerard Roudier (groudier@club-internet.fr)
-	Patch no 2 sent to Harald.
-	I think that the driver have some chance to work.
-	
-Sun Jun 23  15:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	Harald Koenig is interested in the adaptation of the driver to 
-	Linux/Alpha.
-	I have prepared a patch and sent it to Harald.
-
-Sun Jun 16  19:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	Release 1.11
-	Tested linux releases: 1.2.13, 2.0.0
-
-Sat Jun 15 23:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.h, Configure.help, scsi/Config.in
-	Add CONFIG_SCSI_NCR53C8XX_IOMAPPED config option.
-	Prepare the 2.0.0 with the new version of the driver.
-
-Wed Jun 12 23:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	Rewrite the README file.
-	Add some documentations of the proc file system support.
-
-Sun Jun 9 18:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c
-	Add proc filesystem support of the driver.
-	Read operations returns profile information.
-	Write operations send control commands to the host adapter driver.
-
-Wed Jun 5 22:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	Change xfer direction for SCAN command to write.
-	Was bogus.
-
-Tue May 30 18:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c
-	Set the DMA FIFO to 88 for 825A and 875 boards.
-	The previous value of 536 is bogus since the script only read 7 
-	bits for the fifo size (thanks to Stefan).
-
-Mon May 27 18:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c
-	Correct the xfer direction guessing for scanner SCAN command (write).
-
-Mon May 27 18:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c
-	Add the following config options:
-	SCSI_NCR_DISABLE_MPARITY_CHECK : disable master parity checking.
-	SCSI_NCR_DISABLE_PARITY_CHECK  : disable scsi parity checking.
-	SCSI_NCR_FORCE_SYNC_NEGO       : force sync nego for all scsi 2 devices.
-
-Sat May 25 22:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	Release 1.10
-	Tested linux releases: 1.2.13, 1.3.45, 1.3.71, 1.3.90, 1.3.100
-	                       1.99.6, 1.99.7
-	Switch between Drew's driver and Bsd driver tested for 1.99.7.
-	Both driver was made as modules.
-
-Sat May 25 16:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c
-	Some weird problems happen with multi-lun configurations and HDs.
-	SDTR seems to be sent with TEST UNIT READY of lun 1.
-	Tagged Queue cannot be enabled. It seems that inqdata are 
-	filled with garbage probably due to some INQUIRY command to
-	lun 1.
-	I have fixed the problem as follow:
-	- negotiation are initiated only with a command to lun 0.
-	- inquiry data are store only for lun 0.
-
-Wed May 22 22:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c, ncr53c8xx.h
-	Have prepared the patch that allow to install the driver in the 
-	kernel tree, without moving Drew's one.
-	Seems to work. However, I have to check that nothing has been 
-	broken for 1.2.13 and 1.3.45 to 1.3.100.
-
-Sun May 4 22:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.h
-	Adapt the source to some modifications of the linux tree of 1.3.98.
-	(include/linux/scsicam.h moved to include/scsi/scsicam.h)
-
-Thu Apr 25 21:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.h, ncr53c8xx.c
-	Release 1.9
-	Prepare conditionnal compilations for the future Linux version(s).
-	Assume that these versions will be nicely compatible with current 
-	one. 
-	1.3.255, 1.4.0 or 2.0.0 ?
-	I suggest 3.0.0 for some obvious reason.
-
-Wed Apr 24 23:15 1996 Gerard Roudier (groudier@club-internet.fr)
-	* Install.ncr53c8xx
-	Add Patch-Current.ncr53c8xx to the distribution.
-	This patch is applied to the scsi Makefile at installation time for
-	Linux release V.P.S (V*1000000000+P*100000000+S > 1300000094).
-	Each time it'll be necessary I will send the patch corresponding to 
-	the current Linux release to the linux-scsi@vger.rutgers.edu.
-
-Sun Apr 21 19:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* README.ncr53c8xx
-	Update Bonnie results of linux-1.3.92 + prepatch read-ahead 1.3.93.
-	Results are so good that I must remove FreeBSD-2.0.5 results from the
-	README file, otherwise I should cause trouble for myself.
-
-Sun Apr 07 21:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.h
-	Define SCSI_NCR_MAX_LUN (8) inconditionaly.
-	Previous releases did not work for multi-lun devices.
-	This definition was wrongly conditionned:
-	(SCSI_CONFIG_MULTI_LUN instead of CONFIG_SCSI_MULTI_LUN).
-	No luck, since I donnot have multi-lun devices and could'nt
-	test it.
-	Some tests under linux-1.3.84 with an experimental patch that
-	try to do asynchronous read-ahead.
-
-Wed Apr 03 23:15 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.h, ncr53c8xx.c
-	Change some wrong "assert (target == cmd->target & 7)" to
-                          "assert (target == (cmd->target & 0xf)".
-	Remove   NCR_TIMEOUT_ALERT from ncr53c8xx.c
-	Add SCSI_NCR_TIMEOUT_ALERT to   ncr53c8xx.h
-
-Sun Mar 24 21:15 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.h
-	During "make dep" of linux-1.2.13, LINUX_VERSION_CODE is undefined.
-	Have to assume 1.2.13 in such situation.
-	Release 1.8
-
-Sun Mar 24 21:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* README.ncr53c8xx
-	Make changes according to the new uninstallation procedure.
-
-Sun Mar 24 20:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* Uninstall.ncr53c8xx
-	Add an uninstallation script to the distribution.
-	This shell script restore the standard driver.
-	Very usefull for people who prefers to use a driver that
-	does not support:
-	- Master parity check
-	- Tagged command queuing
-	- Fast Wide Scsi-2 features (up to 20 MB/sec)
-	and that would be capable to reject a Wide Negotiation that it had
-	previously initiated.
-
-Sat Mar 23 22:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* README.ncr53c8xx
-	Make changes according to the new installation procedure.
-
-Fri Mar 22 23:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* Install.ncr53c8xx
-	Add an installation script to the distribution.
-	3 differents patches are necessary:
-	- linux-1.2.13
-	- linux-1.3.45 to linux-1.3.49
-        - linux-1.3.50 to linux-1.3.77
-
-Wed Mar 13 22:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c, ncr53c8xx.h
-	Add share irq support.
-	This facility appears with linux-1.3.70. It seems that the
-	corresponding code of the kernel was questionnable before 1.3.72.
-	I decide to support this options from linux-1.3.72 and above.
-	(Add define option SCSI_NCR_SHARE_IRQ)
-	Release 1.7
-
-Tue Mar 12 22:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c
-	Download BSD ncr.c 1.67 and apply the correction of negotiations order.
-	Now the Wide and Sync negotiation are done in the proper order.
-	(Problem reported by Johannes Plass).
-	Round up correctly the announced speed in MB/sec.
-
-Tue Mar 05 22:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* README.ncr53c8xx
-	Have to upload release 1.6 for users of linux-1.3.70-71
-
-Mon Mar 04 16:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* README.ncr53c8xx
-	Add some Bonnie results to the README file.
-
-Sun Mar 03 20:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c
-	Make changes for linux-1.3.70 according to the new specification of
-	irq services interface (request_irq(), free_irq()).
-	With 26 letters, "_", and 10 digits we can build about:
-		53x62**9 C names of 10 characters.
-	Why to use SAME function names with DIFFERENT parameters ?
-
-Sat Mar 02 22:30 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c
-	Using SIMPLE QUEUE TAG for all operations is good for performances,
-	but may be bad for assumed timeout values.
-	Under heavy disk load (Bonnie), the drive may start IO process of a 
-	command, then disconnect, then execute lots of other commands 
-	before completing the interrupted command.
-	The standard value of SD_TIMEOUT (6 sec or 7 sec) seems (is) too short.
-	I fix the problem by forcing an ORDERED QUEUE TAG for the next  
-	command when I found some "old" pending command.
-	"Old" means that they might be timeouted in a few seconds.
-	Add NCR_TIMEOUT_ALERT and set it to 3 seconds.
-
-Fri Mar 01 22:30 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.h, ncr53c8xx.c
-	Add define SCSI_NCR_SEGMENT_SIZE and set it by default to 512.
-	If undefined, the driver use the scatter list given by the upper 
-	scsi driver, else it tries to split segments into shorter one 
-	according to the value of SCSI_NCR_SEGMENT_SIZE.
-
-Tue Feb 27 21:30 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.h
-	Set sg_tablesize to 127 = SCSI_NCR_MAX_SCATTER-1.
-	(was 64 = SCSI_NCR_MAX_SCATTER/2).
-	May increase the speed (about 20%) for linear read/write operations.
-	Bonnie results may be better, but I prefered the previous
-	value.
-
-Tue Feb 27 21:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr54c8xx.c, ncr53c8xx.h
-	Tagged command queueing seems to affect linux-1.3.XY kernels.
-	I decide to disable tagged queue by default and to provide a command
-	tool to enable it per device after boot-up.
-	Add scsitag.c to the distribution.
-	Usage: scsitag device
-	Examples: scsitag /dev/sda
-	          scsitag /dev/sdb
-
-Sun Feb 25 14:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c, ncr53c8xx.h
-	Add INQ7_Default definition and set the target capabilities to this
-	value by default.
-	Add some code to reject a synchronous negotiation request from a target
-	that is defined as not capable of Sync in the table of capabilities.
-	
-Sat Feb 24 23:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c
-	Add some code to fill some write-only host instance fields:
-	- base
-	- io_port
-	- n_io_port
-	- dma_channel
-	This fields may be used with some "standard" drivers that do not 
-	process correctly the release function.
-
-Fri Feb 23 23:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	I receive a mail from Jason Duerstock.
-	A new bug in ncrBsd2Linux which is probably outside the driver code
-	and inside Linux kernel code.
-	The kernel memory is quite corrupted and we cannot get any information
-	by looking into the messages about the crash. However Linus see a bug in
-	ncrBsd2Linux because it is a "non standard" driver.
-	I think too that ncrBsd2Linux is not a standard driver of Linux kernel,
-	because it has no bugs enough to become standard for Linux.
-	Drew's driver is quite standard.
-
-Wed Feb 21 23:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c, ncr53c8xx.h
-	I incorporate the differences between FreeBSD ncr.c revision 1.62 and
-	revision 1.64.
-	- Some cosmetic changes.
-	- Use M_SIMPLE_TAG by default (even for write operations).
-	  I seems to me that SIMPLE TAG is safe only if the flag "queue
-	  algorithm modifier" is set to zero.
-	  I will ask some questions to Stefan Esser about this.
-	Add option SCSI_NCR_ALWAYS_SIMPLE_TAG in ncr53c8xx.h.
-
-Fri Feb 16 23:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	I have found the bug. It is a recursion in __get_free_pages().
-	I will send a mail to Linus about this.
-
-Sat Feb 10 20:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	I am sure that the kernel stack overflow is due to a severe bug in
-	the Linux kernel.
-	I decide to try to find the bug by myself.
-
-Fri Feb 09 20:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c, ncr53c8xx.h
-	Limit commands per lun to 2 for linux-1.3.XY.
-	The patch-1.3.60 does not correct the kernel stack overflow problem.
-	I decide to make some tests with Drew's driver and Bsd2Linux with same 
-	working conditions (3 commands per lun, FAST SCSI, no command queueing).
-	I get the stack overflow problem with the 2 drivers at the same 
-	frequency.
-	With only 2 commands per LUN, I don't have the problem with any driver.
-	It seems that the madness of recursion and the recent introduction of
-	the silly generic read function have broken performance and reliability 
-	of scsi drivers.
-
-Thu Feb 08 23:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c, ncr_attach()
-	Release memory mapped region and io port if initialisation
-	does not succeed.
-
-Thu Feb 08 22:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c, ncr53c8xx.c
-	Try to spare some CPU time in queue_command() function and interrupt
-	handler:
-	Add SCSI_NCR_PARANOIA define option in ncr53c8xx.h.
-	Add SCSI_NCR_PROFILE  define option in ncr53c8xx.h.
-	Avoid useless code and function calls.
-
-Tue Feb 06 21:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c, ncr53c8xx.h, ncr_timeout()
-	Add SCSI_NCR_BROKEN_INTR define option in ncr53c8xx.h.
-	If this option is set, the timeout handler polls the interrupt status
-	register every tick (10 ms). So, boards with broken interrupt can work. 
-
-Mon Feb 05 21:30 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c
-	Print the correct speed, for devices with successfull wide negotiation.
-	For same period, such devices are two times faster then narrow ones.
-
-Mon Feb 05 21:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.h, ncr53c8xx.c, ncr_attach()
-	Add define SCSI_NCR_SETTLE_TIME in header file and set it by default
-	to 2 seconds.
-
-Sat Jan 27 14:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	Upload release 1.3
-
-Wed Jan 24 24:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c
-	Update from ncr Bsd 1.60 (Stefan Esser):
-	The handshake timeout is disabled, since
-	a few devices will delay ACK for more than a 
-	second: Scanner, CDROM writer and a few old hard 
-	disk drives.
-
-Wed Jan 24 22:30 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c
-	Set the selection timeout to 0.4 sec, since 0.25 sec
-	is recommended for scsi-1 devices.
-	Note that the Bsd ncr driver sets this timeout to 0.1 sec
-	and the linux standard ncr driver sets it to 0.8 sec.
-
-Wed Jan 24 22:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c
-	Add a 5 seconds delay after chip initialization,
-	waiting for scsi devices to settle their stomach,
-	as FreeBSD generic scsi driver does.
-
-Tue Jan 23 23:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c
-	Set burst length value according to chip type.
-	The original Bsd ncr driver sets burst length to 16 for
-	all chip types.
-
-Tue Jan 16 23:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c, ncr53c8xx.h
-	Add comments to linux specific glue code.
-
-Mon Jan 15 22:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	io mapped versus memory mapped.
-	* ncr53c8xx.c
-	Add some code to dynamicaly switch to io mapped if memory mapped
-	does not work.
-	No more need to reconfigure, compile and link the kernel if
-	memory mapped is not possible.
-
-Sun Jan 14 18:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	Patch sent to ncr mailing list by Jason Duerstock
-	<jduersto@kendall.mdcc.edu>
-	I have omitted to provide the proc_dir_entry to the middle
-	scsi driver.
-	* ncr53c8xxx.c
-	Add the declaration of the ncr53c8xx proc_dir_entry and return
-	the pointer to middle scsi driver.
-
-Sat Jan 13 01:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	ncrBsd2Linux 1.1 is ready.
-	Upload to sunsite immediatly.
-
-Fri Jan 12 23:45 1996 Gerard Roudier (groudier@club-internet.fr)
-	It seems that scsi-2 devices too may cause problems because they
-	have flawes in the firmware.
-	* ncr53c8xx.h
-	I add a table of capabilities per target.
-	This table contains one byte per target. The value of this byte
-	is anded with byte 7 of INQUIRY data.
-	Edit ncr53c8xx.h and read the corresponding comments for more
-	details.
-
-Wed Jan 10 22:35 1996 Gerard Roudier (groudier@club-internet.fr)
-	I have some time to read the scsi-1 specifications.
-	Some very old scsi devices may cause problems with the ncr Bsd driver
-	for the following raisons:
-	    The Synchronous Negotiation protocol was optional.
-	    The standardized INQUIRY data does not include the byte 7 of
-	    the scsi-2 specifications which contains the capabilities of
-	    the device.
-	I think that negotiation with such devices are very questionnable.
-	* ncr53c8xx.c
-	ncrBsd2Linux 1.1 does not negotiate with scsi-1 devices.
-
-Sat Jan 06 21:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c function ncr_attach()
-	Disable chip interrupt before soft reset in attach procedure.
-	When loadlin is used to boot the system, the state of the NCR chip
-	is unpredicable. This modification avoid unexpected interrupts.
-	
-Thu Jan 04 23:45 1996 Gerard Roudier (groudier@club-internet.fr)
-	ncrBsd2Linux 1.0 is ready.
-	Upload to sunsite immediatly.
-
-Tue Jan 02 23:00 1996 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c
-	Add a waiting list for Scsi Commands than can be inserted
-	into the start queue immediatly.
-	When a command complete, the waiting commands are requeued by calling
-	the queuecommand() function.
-
-Sun Dec 31 23:59 1995 Gerard Roudier (groudier@club-internet.fr)
-	* ncr53c8xx.c
-	Use kmalloc()/kfree() for internal data structures allocation to
-	avoid scsi memory pool shortage.
-
-Sat Dec 30 23:00 1995 Gerard Roudier (groudier@club-internet.fr)
-	ncrBsd2Linux can now use memory mapped IO.
-	Works fine.
-	* ncr53c8xx.c
-	    Call vremap() from ncr_attach() to map the physical page which
-	    contains the memory IO window.
-	    Call vfree() from ncr_detach() (release module).
-
-Fri Dec 29 23:45 1995 Gerard Roudier (groudier@club-internet.fr)
-	ncrBsd2Linux can now be configured as a module.
-	Works fine.
-	* ncr53c8xx.c: add new functions ncr53c8xx_release() and ncr_detach()
-	Add the code to (per host):
-	    Stop the timer.
-	    Stop the chip.
-	    Free allocated memory.
-
-Fri Dec 29 23:00 1995 Gerard Roudier (groudier@club-internet.fr)
-	Problem: detection routine returns 0 and can detect only one host.
-	* ncr53c8xx.c function ncr_attach()
-	ncr_attach() now returns 0 on success and -1 on error.
-	ncr53c8xx_detect() returns the number of detected hosts.
-
-Thu Dec 28 22:00 1995 Gerard Roudier (groudier@club-internet.fr)
-	I must upload the new version which corrects the severe problem with
-	WRITE_10 command.
-	Release 0.5
-	Known or probable problems with this ncr driver release:
-	--------------------------------------------------------
-	    Same as the previous release.
-
-Wed Dec 27 23:00 1995 Gerard Roudier (groudier@club-internet.fr)
-	Problem:
-	    System CRASH or scsi ERROR "extra data disgarded" on WRITE(10)
-	    command. 
-	* ncr53c8xx.c function guess_xfer_direction()
-	I add the WRITE(10) (0x2A) into the list of Data Out scsi commands.
-	It was a big mistake.
-	This BUG was introduced in the release 0.3 and is obvious present in
-	the release 0.4.
-
-Wed Dec 27 22:00 1995 Gerard Roudier (groudier@club-internet.fr)
-	Problem:
-	    When I was testing tagged command queueing and disconnections
-	    with one hard disk at a time (IBM S12), the script process hung 
-	    every 5 minutes with a non empty stall queue.
-	* ncr53c8xx.c function ncr_exception()
-	I replace "OUTB (nc_istat, INTF)" by
-	          "OUTB (nc_istat, (istat & SIGP) | INTF)".
-	This statement cleared the INTF condition, but cleared the SIGP flag too.
-	(This bug is in the original FreeBSD ncr driver).
-
-Mon Dec 25 22:00 1995 Gerard Roudier (groudier@club-internet.fr)
-	Release 0.4
-	Known or probable problems with this ncr driver release:
-	--------------------------------------------------------
-	  Hardware (or software) conflicts with some ethernet cards.
-	      See release 0.2 above.
-	  Crash with Intel saturn chipset with write-back cache enabled.
-	      The SCSI SCRIPT access the internal registers of
-	      the NCR chip by memory addressing.
-              Ensure that the memory area of the NCR chip is not cacheable.
-	      Use scanpci to get the base memory address of the ncr chip.
-	      The 128 bytes following this address must not be cached.
-
-Sat Dec 23 22:00 1995 Gerard Roudier (groudier@club-internet.fr)
-	Problem:
-		FreeBSD driver important comments
-		---------------------------------
-		We try to reduce the number of interrupts caused
-		by unexpected phase changes due to disconnects.
-		A typical harddisk may disconnect before ANY block.
-		If we wanted to avoid unexpected phase changes at all
-		we had to use a break point every 512 bytes.
-		Of course the number of scatter/gather blocks is
-		limited.
-	* ncr53c8xx.c function ncr_scatter()
-	This function has been rewritten according to the above comments.
-	The Linux scatter list is scanned, and blocks are broken as possible
-	into 512 bytes chunks.
-
-Wed Dec 22 22:00 1995 Gerard Roudier (groudier@club-internet.fr)
-	Problem: ensure that there are enough allocated Command Control Blocks
-	         for each unit to enqueue commands according to cmd_per_lun.
-	* ncr53c8xx.c function ncr_allocate_ccb()
-	Allocate all required ccb(s) instead of one at a time.
-
-Mon Dec 18 23:00 1995 Gerard Roudier (groudier@club-internet.fr)
-	Problem: A copy of the system time is used to compute timeouts.
-	         When the system time is changed , we can get spurious timeouts
-	         if scsi commands are pending.
-	* ncr53c8xx.c function ncr_timeout()
-	In FreeBSD the kernel time (volatile struct timeval time) is not
-	affected by settimeofday() or other change time functions.
-	For Linux, I replace "time" by "xtime". But "xtime" is the system time,
-	and is affected by change time functions.
-	If we detect a change <=-1s or >1s we assume system time has been changed.
-	For all active ccb(s), we recompute tlimit.
-	We set heartbeat to thistime to prevent spurious chip reset.
-
-Sun Dec 17 23:00 1995 Gerard Roudier (groudier@club-internet.fr)
-	Release 0.3.
-
-Sun Dec 17 11:00 1995 Gerard Roudier (groudier@club-internet.fr)
-
-	Problem: Linux middle-level scsi driver does not provide the
-	direction of transfert in scsi command parameters.
-	FreeBSD ncr driver need this information to patch the SCSI script
-	for SAVE DATA POINTER and to check actual data transfer direction.
-	* ncr53c8xx.c
-	I add the function guess_xfer_direction(int opcode) which try to
-	guess the transfer direction.
- 	Unfortunately my documentation about SCSI-II standard is very old.
-	It does not contain PHOTO-CD command specifications.
-	I assume input transfer direction, for unknown command. (not perfect)
-
-Wed Dec 15 23:00 1995 Gerard Roudier (groudier@club-internet.fr)
-
-	It's time to schedule the release 0.2
-	Known or probable problems with this ncr driver release:
-	--------------------------------------------------------
-	  Scsi tapes do not work.
-	  scsi-config-1.5 does not work too.
-	  Hardware (or software) conflicts with some ethernet cards.
-		The linux native ncr53c810 scsi driver does'not use memory
-		mapped IO at all.
-		The BSD driver can use memory mapped IO.
-		Under Linux, i seems to be difficult (or impossible) to map
-		a PCI memory area. So I decide to use normal IO in the code of
-		the ported driver.
-		However, the SCSI SCRIPT access the internal registers of
-		the NCR chip by memory addressing.
-		EThernet cards use a memory area to communicate with the system.
-		I think that this memory area conflicts with the memory area
-		used by the NCR chip.
-		The configuration diskette of your ethernet card can help you
-		to fix the problem (try sofware configurations).
-
-Wed Dec 15 22:00 1995 Gerard Roudier (groudier@club-internet.fr)
-
-	Problem: detection of CD change did not work.
-	* ncr53c8xx.c
-	Signal a CHECK_CONDITION (S_CHECK_COND) to the middle-level
-	scsi drivers when the scsi completion status = S_SENSE|S_GOOD.
-
-	Problem: System hang with status <4/82> after mounting the root
-	         partition. System disk is a QUANTUM EMPIRE 1080 S.
-	Submitted by rwilhelm@Physik.TU-Muenchen.DE (Robert Wilhelm)
-	Thu, 14 Dec 1995 10:18:43 +0100 (MET)
-	* ncr53c8xx.c
-	Signal a CHECK_CONDITION (S_CHECK_COND) to the middle-level
-	scsi drivers when the scsi completion status = S_SENSE|S_CHECK_COND.
-	(HS_COMPLETE=4, S_SENSE|S_CHECK_COND=x82).
-	I can't test this fix. I think that it should work.
-
-Thu Dec 14 22:00 1995 Gerard Roudier (groudier@club-internet.fr)
-
-	Problem	submitted by Bill Dyess <Bill@Dyess.com> Tue 12 Dec 1995
-	Only one lun detected on a Pioneer DRM-602X 6-disk CD-ROM changer.
-	* ncr53c8xx.h, ncr53c8xx.c
-	Define NCR_SCSI_MAX_LUN to 8 if the Linux configuration flag
-	SCSI_CONFIG_MULTI_LUN is set.
-	My CD device has only one lun. I can't test multi-lun device,
-	but I think that it should work.
-
-	* ncr53c8xx.c
-	Write the ncr_abort_command() and ncr_reset_command() functions.
-	I intend to test these functions next week.
-
-Sun Dec 10 22:00 1995 Gerard Roudier (groudier@club-internet.fr)
-	* Release 0.1
-	Works fine with hard disks under Linux 1.2.13 and Linux 1.3.45.
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- linux.vanilla/drivers/scsi/Config.in	Sun Dec  6 00:14:41 1998
+++ linux/drivers/scsi/Config.in	Fri Mar 26 17:02:11 1999
@@ -15,15 +15,14 @@
 comment 'SCSI low-level drivers'
 
 dep_tristate '7000FASST SCSI support' CONFIG_SCSI_7000FASST $CONFIG_SCSI
+dep_tristate 'ACARD SCSI support' CONFIG_SCSI_ACARD $CONFIG_SCSI
 dep_tristate 'Adaptec AHA152X/2825 support' CONFIG_SCSI_AHA152X $CONFIG_SCSI
 dep_tristate 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 $CONFIG_SCSI
 dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI
 dep_tristate 'Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
 if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then
-    bool '   Override driver defaults for commands per LUN' CONFIG_OVERRIDE_CMDS N
-    if [ "$CONFIG_OVERRIDE_CMDS" != "n" ]; then
-      int  '   Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 24
-    fi
+    bool '   Enable Tagged Command Queueing (TCQ) by default' CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT
+    int  '   Maximum number of TCQ commands per device' CONFIG_AIC7XXX_CMDS_PER_DEVICE 8
     bool '   Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS N
     int  '   Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 5
 fi
@@ -52,7 +51,10 @@
 		"Port CONFIG_SCSI_G_NCR5380_PORT \
 		Memory CONFIG_SCSI_G_NCR5380_MEM" Port
 fi
+dep_tristate 'Initio 91XX support' CONFIG_SCSI_INITIO $CONFIG_SCSI
+dep_tristate 'Initio INI-A100U2W support' CONFIG_SCSI_INIA100 $CONFIG_SCSI
 dep_tristate 'NCR53c406a SCSI support' CONFIG_SCSI_NCR53C406A $CONFIG_SCSI
+dep_tristate 'symbios 53c416 SCSI support' CONFIG_SCSI_SYM53C416 $CONFIG_SCSI
 if [ "$CONFIG_PCI" = "y" ]; then
   dep_tristate 'NCR53c7,8xx SCSI support'  CONFIG_SCSI_NCR53C7xx $CONFIG_SCSI
   if [ "$CONFIG_SCSI_NCR53C7xx" != "n" ]; then
@@ -64,12 +66,12 @@
 if [ "$CONFIG_PCI" = "y" -a "$CONFIG_SCSI_NCR53C7xx" != "y" ]; then
   dep_tristate 'NCR53C8XX SCSI support' CONFIG_SCSI_NCR53C8XX $CONFIG_SCSI
   if [ "$CONFIG_SCSI_NCR53C8XX" != "n" ]; then
-    bool '  detect and read serial NVRAMs' CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT
-    bool '  enable tagged command queueing' CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE
+    int  '  default tagged command queue depth' CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS 8
+    int  '  maximum number of queued commands' CONFIG_SCSI_NCR53C8XX_MAX_TAGS 32
+    int  '  synchronous transfers frequency in MHz' CONFIG_SCSI_NCR53C8XX_SYNC 20
+    bool '  enable profiling' CONFIG_SCSI_NCR53C8XX_PROFILE
     bool '  use normal IO' CONFIG_SCSI_NCR53C8XX_IOMAPPED
-    int  '  maximum number of queued commands' CONFIG_SCSI_NCR53C8XX_MAX_TAGS 4
-    int  '  synchronous transfers frequency in MHz' CONFIG_SCSI_NCR53C8XX_SYNC 5
-    if [ "$CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE" != "y" ]; then
+    if [ "$CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS" = "0" ]; then
       bool '  not allow targets to disconnect' CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT
     fi
     if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
@@ -82,15 +84,22 @@
     bool '  Buggy EPP chipset support' CONFIG_SCSI_PPA_HAVE_PEDANTIC
 fi
 dep_tristate 'PAS16 SCSI support' CONFIG_SCSI_PAS16 $CONFIG_SCSI
+dep_tristate 'PCI-2000 support' CONFIG_SCSI_PCI2000 $CONFIG_SCSI
+dep_tristate 'PCI2220I support' CONFIG_SCSI_PCI2220I $CONFIG_SCSI
+dep_tristate 'PSI240I support' CONFIG_SCSI_PSI240I $CONFIG_SCSI
 dep_tristate 'Qlogic FAS SCSI support' CONFIG_SCSI_QLOGIC_FAS $CONFIG_SCSI
 if [ "$CONFIG_PCI" = "y" ]; then
   dep_tristate 'Qlogic ISP SCSI support' CONFIG_SCSI_QLOGIC_ISP $CONFIG_SCSI
 fi
 dep_tristate 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE $CONFIG_SCSI
-if [ "$CONFIG_PCI" = "y" -a "$CONFIG_SCSI_AM53C974" != "y" ]; then
-  dep_tristate 'Tekram DC-390(T) SCSI support' CONFIG_SCSI_DC390T $CONFIG_SCSI
+if [ "$CONFIG_PCI" = "y" ]; then
+  dep_tristate 'Tekram DC390(T) and Am53/79C974 SCSI support' CONFIG_SCSI_DC390T $CONFIG_SCSI
+  if [ "$CONFIG_SCSI_DC390T" != "n" ]; then
+    bool '  _omit_ support for non-DC390 adapters' CONFIG_SCSI_DC390T_NOGENSUPP
+  fi
 fi
 dep_tristate 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128 $CONFIG_SCSI
+dep_tristate 'Tripace TC-2550x SCSI support' CONFIG_SCSI_TC2550 $CONFIG_SCSI
 dep_tristate 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F $CONFIG_SCSI
   if [ "$CONFIG_SCSI_U14_34F" != "n" ]; then
     bool '  enable elevator sorting' CONFIG_SCSI_U14_34F_LINKED_COMMANDS
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- linux.vanilla/drivers/scsi/Makefile	Sun Dec  6 00:14:42 1998
+++ linux/drivers/scsi/Makefile	Mon Mar 22 00:23:34 1999
@@ -145,6 +145,23 @@
   endif
 endif
 
+ifeq ($(CONFIG_SCSI_ACARD),y)
+L_OBJS += atp870u.o 
+else
+  ifeq ($(CONFIG_SCSI_ACARD),m)
+  M_OBJS += atp870u.o 
+  endif
+endif
+
+
+ifeq ($(CONFIG_SCSI_INITIO),y)
+L_OBJS += initio.o
+else
+  ifeq ($(CONFIG_SCSI_INITIO),m)
+  M_OBJS += initio.o
+  endif
+endif
+
 
 ifeq ($(CONFIG_SCSI_AHA152X),y)
 L_OBJS += aha152x.o
@@ -363,6 +380,22 @@
   endif
 endif
 
+ifeq ($(CONFIG_SCSI_SYM53C416),y) 
+L_OBJS += sym53c416.o 
+else 
+  ifeq ($(CONFIG_SCSI_SYM53C416),m) 
+  M_OBJS += sym53c416.o 
+  endif 
+endif 
+
+ifeq ($(CONFIG_SCSI_TC2550),y) 
+L_OBJS += tripace.o 
+else 
+  ifeq ($(CONFIG_SCSI_TC2550),m) 
+  M_OBJS += tripace.o 
+  endif 
+endif 
+
 ifeq ($(CONFIG_SCSI_MEGARAID),y)
 L_OBJS += megaraid.o
 else
@@ -371,6 +404,38 @@
   endif
 endif
 
+ifeq ($(CONFIG_SCSI_INIA100),y)
+L_OBJS += a100u2w.o
+else
+  ifeq ($(CONFIG_SCSI_INIA100),m)
+  M_OBJS += a100u2w.o
+  endif
+endif
+
+ifeq ($(CONFIG_SCSI_PCI2000),y)
+L_OBJS += pci2000.o
+else
+    ifeq ($(CONFIG_SCSI_PCI2000),m)
+    M_OBJS += pci2000.o
+    endif
+endif
+
+ifeq ($(CONFIG_SCSI_PCI2220I),y)
+L_OBJS += pci2220i.o
+else
+    ifeq ($(CONFIG_SCSI_PCI2220I),m)
+    M_OBJS += pci2220i.o
+    endif
+endif
+
+ifeq ($(CONFIG_SCSI_PSI240I),y)
+L_OBJS += psi240i.o
+else
+    ifeq ($(CONFIG_SCSI_PSI240I),m)
+    M_OBJS += psi240i.o
+    endif
+endif
+
 ifeq ($(CONFIG_BLK_DEV_IDESCSI),y)
 L_OBJS += ide-scsi.o
 endif
@@ -402,8 +467,14 @@
 g_NCR5380.o: g_NCR5380.c
 	$(CC) $(CFLAGS) -DGENERIC_NCR5380_OVERRIDE="{{(NCR5380_map_type)0x350,5,0, BOARD_NCR53C400}};" -c g_NCR5380.c
 
+initio.o: ini9100u.o i91uscsi.o
+	$(LD) -r -o initio.o ini9100u.o i91uscsi.o
+
 megaraid.o: megaraid.c
 	$(CC) $(CFLAGS) -c megaraid.c
+
+a100u2w.o: inia100.o i60uscsi.o
+	$(LD) -r -o a100u2w.o inia100.o i60uscsi.o
 
 scsi_mod.o: $(MX_OBJS) hosts.o scsi.o scsi_ioctl.o constants.o \
 		scsicam.o scsi_proc.o
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/README.aic7xxx linux/drivers/scsi/README.aic7xxx
--- linux.vanilla/drivers/scsi/README.aic7xxx	Sun Dec  6 00:14:42 1998
+++ linux/drivers/scsi/README.aic7xxx	Fri Mar 26 17:02:19 1999
@@ -17,6 +17,10 @@
     AHA-274xT               
     AHA-2842
     AHA-2910B               
+    AHA-2920C
+    AHA-2930
+    AHA-2930U
+    AHA-2930U2
     AHA-2940               
     AHA-2940W              
     AHA-2940U              
@@ -37,8 +41,10 @@
     AHA-3940U
     AHA-3940W
     AHA-3940UW
+    AHA-3940AUW
     AHA-3940U2W
     AHA-3950U2B
+    AHA-3950U2D
     AHA-3985
     AHA-3985U
     AHA-3985W
@@ -52,6 +58,7 @@
     AIC-787x
     AIC-788x
     AIC-789x
+    AIC-3860
 
     Bus Types
     ----------------------------
@@ -69,8 +76,30 @@
     AHA-398x - PCI RAID controllers with three separate SCSI controllers
                on-board.
 
-    NOTE: The AHA-2920 is NOT an AIC-7xxx based controller, and is not
-          handled by this driver.
+  Not Supported Devices
+  ------------------------------
+    Adaptec Cards
+    ----------------------------
+    AHA-2920 (Only the cards that use the Future Domain chipset are not
+              supported, any 2920 cards based on Adaptec AIC chipsets,
+	      such as the 2920C, are supported)
+    AAA-13x Raid Adapters
+    AAA-113x Raid Port Card
+
+    Motherboard Chipsets
+    ----------------------------
+    AIC-7810
+
+    Bus Types
+    ----------------------------
+    R - Raid Port busses are not supported.
+
+    The hardware RAID devices sold by Adaptec are *NOT* supported by this
+    driver (and will people please stop emailing me about them, they are
+    a totally separate beast from the bare SCSI controllers and this driver
+    can not be retrofitted in any sane manner to support the hardware RAID
+    features on those cards - Doug Ledford).
+    
 
   People
   ------------------------------
@@ -83,7 +112,7 @@
     Jess Johnson    jester@frenzy.com
       (AIC7xxx FAQ author)
     Doug Ledford    dledford@redhat.com
-      (Current Linux aic7xxx-5.x.x Driver/Patch/FTP/FAQ maintainer)
+      (Current Linux aic7xxx-5.x.x Driver/Patch/FTP maintainer)
     
     Special thanks go to John Aycock (aycock@cpsc.ucalgary.ca), the original
     author of the driver. John has since retired from the project. Thanks
@@ -299,13 +328,13 @@
 	binary->hex conversion then send an email to the aic7xxx mailing
 	list and someone can help you out.
 
-    "aic7xxx=tag_info:{{8,8..},{8,8..},..}" - This option is used to enable
-        tagged queueing on specific devices.  As of driver version 5.0.6, we
-        now globally enable tagged queueing by default, but we also disable
-        tagged queueing on all individual devices by default.  In order to
-        enable tagged queueing for certian devices at boot time, a user may
-        use this boot param.  The driver will then parse this message out
-        and enable the specific device entries that are present based upon
+    "aic7xxx=tag_info:{{8,8..},{8,8..},..}" - This option is used to disable
+        or enable Tagged Command Queueing (TCQ) on specific devices.  As of
+	driver version 5.1.11, TCQ is now either on or off by default
+	according to the setting you choose during the make config process.
+	In order to en/disable TCQ for certian devices at boot time, a user
+	may use this boot param.  The driver will then parse this message out
+        and en/disable the specific device entries that are present based upon
         the value given.  The param line is parsed in the following manner:
 
           { - first instance indicates the start of this parameter values
@@ -337,18 +366,13 @@
           commas with no value specified will simply increment to the next id
           without changing anything for the missing values.
 
-        tag_info:{{8,8},,{8,8}}
-          First adapter, scsi id 0 to 8, id 1 to 8, remainder stay at their
-          default.  Second adapter stays entirely at default.  Third
-          adapter, id 0 to 8, id 1 to 8, remainder at default (identical to
-          first adapter).
-
-        tag_info:{,,,{,,,64}}
+        tag_info:{,,,{,,,255}}
           First, second, and third adapters at default values.  Fourth
-          adapter, id 3 to 64.  Notice that leading commas simply increment
-          what the first number effects, and there are no need for trailing
-          commas.  When you close out an adapter, or the entire entry,
-          anything not explicitly set stays at the default value.
+          adapter, id 3 is disabled.  Notice that leading commas simply
+	  increment what the first number effects, and there are no need
+	  for trailing commas.  When you close out an adapter, or the
+	  entire entry, anything not explicitly set stays at the default
+	  value.
 
         A final note on this option.  The scanner I used for this isn't
         perfect or highly robust.  If you mess the line up, the worst that
@@ -400,10 +424,10 @@
     see this documentation, you need to use one of the advanced configuration
     programs (menuconfig and xconfig).  If you are using the "make menuconfig"
     method of configuring your kernel, then you would simply highlight the
-    option in question and hit the F1 key.  If you are using the "make xconfig"
-    method of configuring your kernel, then simply click on the help button next
-    to the option you have questions about.  The help information from the
-    Configure.help file will then get automatically displayed.
+    option in question and hit the ? key.  If you are using the "make xconfig"
+    method of configuring your kernel, then simply click on the help button
+    next to the option you have questions about.  The help information from
+    the Configure.help file will then get automatically displayed.
 
   /proc support
   ------------------------------
@@ -449,10 +473,16 @@
     ftp://ekf2.vsb.cz/pub/linux/kernel/aic7xxx/ftp.teleport.com/
       - European Linux mirror of Teleport site
 
+  Web sites
+  ------------------------------
+    http://developer.redhat.com/aic7xxx/
+      - Primary web site maintained by Doug Ledford.  I haven't actually
+        put anything up yet....but I'm planning on it.  This information
+	is put here as an add for the vapor page :)
 
 Dean W. Gehnert
 deang@teleport.com
 
 $Revision: 3.0 $
 
-Modified by Doug Ledford 1998
+Modified by Doug Ledford 1998-9
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/README.ncr53c8xx linux/drivers/scsi/README.ncr53c8xx
--- linux.vanilla/drivers/scsi/README.ncr53c8xx	Sun Dec  6 00:14:42 1998
+++ linux/drivers/scsi/README.ncr53c8xx	Mon Dec 14 18:11:09 1998
@@ -4,7 +4,7 @@
 21 Rue Carnot
 95170 DEUIL LA BARRE - FRANCE
 
-2 January 1998
+13 December 1998
 ===============================================================================
 
 1.  Introduction
@@ -21,8 +21,8 @@
       8.4  Set order type for tagged command
       8.5  Set debug mode
       8.6  Clear profile counters
-      8.7  Set flag (no_sync)
-      8.8  Debug error recovery
+      8.7  Set flag (no_disc)
+      8.8  Set verbose level
 9.  Configuration parameters
 10. Boot setup commands
       10.1 Syntax
@@ -33,9 +33,7 @@
       10.6 SCSI BUS checking boot option
 11. Some constants and flags of the ncr53c8xx.h header file
 12. Installation
-      12.1 Provided files
-      12.2 Installation procedure
-13. Control commands under linux-1.2.13
+13. Architecture dependant features
 14. Known problems
       14.1 Tagged commands with Iomega Jaz device
       14.2 Device names change when another controller is added
@@ -93,7 +91,7 @@
 
 Latest driver version and patches are available at:
 
-          ftp://linux.wauug.org/pub/roudier
+          ftp://ftp.tux.org/pub/people/gerard-roudier
 
 I am not a native speaker of English and there are probably lots of
 mistakes in this README file. Any help will be welcome.
@@ -112,19 +110,32 @@
 "Wide negotiation" is supported for chips that allow it.  The
 following table shows some characteristics of NCR 8xx family chips:
 
-       On board                        Supported by    Tested with
-Chip   SDMS BIOS   Wide   Ultra SCSI   the driver      the driver
-----   ---------   ----   ----------   ------------    -----------
-810        N         N        N            Y             Y
-810A       N         N        N            Y             Y
-815        Y         N        N            Y             Y
-825        Y         Y        N            Y             Y
-825A       Y         Y        N            Y             Y
-860        N         N        Y            Y             Y
-875        Y         Y        Y            Y             Y
-895        Y         Y        Y(1)         Y             not yet 
+       On board                                  Supported by 
+Chip   SDMS BIOS   Wide   SCSI std.   Max. sync   the driver
+----   ---------   ----   ---------   ----------  ------------
+810        N         N      FAST10    10 MB/s        Y
+810A       N         N      FAST10    10 MB/s        Y(2)
+815        Y         N      FAST10    10 MB/s        Y
+825        Y         Y      FAST10    20 MB/s        Y
+825A       Y         Y      FAST10    20 MB/s        Y(2)
+860        N         N      FAST20    20 MB/s        Y(2)
+875        Y         Y      FAST20    40 MB/s        Y(2)
+876        Y         Y      FAST20    40 MB/s        Y(2)
+895        Y         Y      FAST40    80 MB/s        Y(2)
+896        Y         Y      FAST40    80 MB/s        Y(1)
+
+(1) The ncr53c8xx driver does not support the following features of 
+    the 896:
+    - Hardware phase mismatch.
+    - 8k on-chip RAM.
+    If you have a 896 chip, you may want to use the enhanced sym53c8xx 
+    driver that supports all the features of the 896.
+    The sym53c8xx driver is available at the following URL:
+
+             ftp://ftp.tux.org/pub/roudier/896/
+
+(2) Chip also supported by the sym53c8xx enhanced driver.
 
-(1) The 895 chip is supported 'on paper'.
 
 3. Summary of other supported features.
 
@@ -152,49 +163,88 @@
 
 5. Tagged command queueing
 
-Some SCSI devices do not properly support tagged command queuing.  A
-safe configuration is to not enable tagged command queuing support at
-boot-up, and to enable support of it with the control command
-"settags" described further in this text.
-
-Once you are sure that all your devices properly support tagged
-command queuing, you can enable it by default with the
-CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE configuration option.
+Queuing more than 1 command at a time to a device allows it to perform 
+optimizations based on actual head positions and its mechanical 
+characteristics. This feature may also reduce average command latency.
+In order to really gain advantage of this feature, devices must have 
+a reasonnable cache size (No miracle is to be expected for a low-end 
+hard disk with 128 KB or less).
+Some kown SCSI devices do not properly support tagged command queuing.
+Generally, firmware revisions that fix this kind of problems are available 
+at respective vendor web/ftp sites.
+All I can say is that the hard disks I use on my machines behave well with 
+this driver with tagged command queuing enabled:
+
+- IBM S12 0662
+- Conner 1080S
+- Quantum Atlas I
+- Quantum Atlas II
+
+If your controller has NVRAM, you can configure this feature per target 
+from the user setup tool. The Tekram Setup program allows to tune the 
+maximum number of queued commands up to 32. The Symbios Setup only allows 
+to enable or disable this feature.
 
 The maximum number of simultaneous tagged commands queued to a device
-is currently set to 4 by default. It is defined in the file
-ncr53c8xx.h by SCSI_NCR_MAX_TAGS. This value is suitable for most SCSI
-disks.  With large SCSI disks (> 2GB, cache > 512KB average seek time
-< 10 ms), 8 tagged commands may give better performance.
+is currently set to 8 by default.  This value is suitable for most SCSI
+disks.  With large SCSI disks (>= 2GB, cache >= 512KB, average seek time
+<= 10 ms), using a larger value may give better performances.
+The driver supports up to 64 commands per device, but using more than 
+32 is generally not worth it, unless you are using a very large disk 
+or disk array.
+
+If your controller does not have NVRAM or if it is managed by the SDMS 
+BIOS/SETUP, you can configure tagged queueing feature and device queue 
+depths from the boot command-line. For example:
+
+  ncr53c8xx=tags:4/t2t3q15-t4q7/t1u0q32
+
+will set tagged commands queue depths as follow:
+
+- target 2  all luns  on controller 0 --> 15
+- target 3  all luns  on controller 0 --> 15
+- target 4  all luns  on controller 0 -->  7
+- target 1  lun 0     on controller 1 --> 32
+- all other target/lun                -->  4
 
 In some special conditions, some SCSI disk firmwares may return a
 QUEUE FULL status for a SCSI command. This behaviour is managed by the
-driver by the following heuristic:
+driver using the following heuristic:
 
-- Each time a QUEUE FULL status is returned, tagged command queueing is 
-  temporarily disabled. 
+- Each time a QUEUE FULL status is returned, tagged queue depth is reduced 
+  to the actual number of disconnected commands. 
 
-- Every 100 successfully completed SCSI commands, if allowed by the
-  current limit, the maximum number of queueable commands is
-  incremented and tagged command queueing is reenabled.
+- Every 1000 successfully completed SCSI commands, if allowed by the
+  current limit, the maximum number of queueable commands is incremented.
 
+Since QUEUE FULL status reception and handling is resource wasting, the 
+driver notifies by default this problem to user by indicating the actual 
+number of commands used and their status, as well as its decision on the 
+device queue depth change.
+The heuristic used by the driver in handling QUEUE FULL ensures that the 
+impact on performances is not too bad. You can get rid of the messages by 
+setting verbose level to zero, as follow:
+
+1st method: boot your system using 'ncr53c8xx=verb:0' option.
+2nd method: apply "setverbose 0" control command to the proc fs entry 
+            corresponding to your controller after boot-up.
 
 6. Parity checking
 
 The driver supports SCSI parity checking and PCI bus master parity
 checking.  These features must be enabled in order to ensure safe data
 transfers.  However, some flawed devices or mother boards will have
-problems with parity. You can disable parity by choosing first
-"CONFIG_EXPERIMENTAL".  Then, "make config" will allow to set the
-following configuration options:
-
-   CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK  (disable SCSI parity checking)
-   CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK (disable master parity checking)
-
+problems with parity. You can disable either PCI parity or SCSI parity 
+checking by entering appropriate options from the boot command line.
+(See 10: Boot setup commands).
 
 7. Profiling information
 
 Profiling information is available through the proc SCSI file system.
+Since gathering profiling information may impact performances, this 
+feature is disabled by default and requires a compilation configuration 
+option to be set to Y.
+
 The device associated with a host has the following pathname:
 
           /proc/scsi/ncr53c8xx/N     (N=0,1,2 ....)
@@ -346,7 +396,7 @@
 
     target:    target number
     tags:      number of concurrent tagged commands
-               must not be greater than SCSI_NCR_MAX_TAGS (default: 4)
+               must not be greater than SCSI_NCR_MAX_TAGS (default: 8)
 
 8.4 Set order type for tagged command
 
@@ -387,7 +437,7 @@
     The "clearprof" command allows you to clear these counters at any time.
 
 
-8.7 Set flag (no_sync)
+8.7 Set flag (no_disc)
  
     setflag <target> <flag>
 
@@ -395,35 +445,21 @@
 
     For the moment, only one flag is available:
 
-        no_sync:   not allow target to disconnect.
+        no_disc:   not allow target to disconnect.
 
     Do not specify any flag in order to reset the flag. For example:
     - setflag 4
-      will reset no_sync flag for target 4, so will allow it disconnections.
+      will reset no_disc flag for target 4, so will allow it disconnections.
     - setflag all
       will allow disconnection for all devices on the SCSI bus.
 
 
-8.8 Debug error recovery
+8.8 Set verbose level
 
-    debug_error_recovery <error to trigger>
+    setverbose #level
 
-    Available error type to trigger:
-        sge:     SCSI gross error
-        abort:   abort command from the middle-level driver
-        reset:   reset command from the middle-level driver
-        parity:  scsi parity detected in DATA IN phase
-        none:    restore driver normal behaviour
-
-    The code corresponding to this feature is normally not compiled.
-    Its purpose is driver testing only. In order to compile the code 
-    that allows to trigger error recovery you must define at compile time 
-    SCSI_NCR_DEBUG_ERROR_RECOVERY.
-    If you have compiled the driver with this option, nothing will happen 
-    as long as you donnot use the control command 'debug_error_recovery' 
-    with sge, abort, reset or parity as argument.
-    If you select an error type, it will be triggered by the driver every 
-    30 seconds.
+    The driver default verbose level is 1. This command allows to change 
+    th driver verbose level after boot-up.
 
 9. Configuration parameters
 
@@ -433,6 +469,11 @@
 support by the driver of this feature at linux start-up and enable
 this feature after boot-up only for devices that support it safely.
 
+CONFIG_SCSI_NCR53C8XX_PROFILE_SUPPORT  (default answer: n)
+    This option must be set for profiling information to be gathered 
+    and printed out through the proc file system. This features may 
+    impact performances.
+
 CONFIG_SCSI_NCR53C8XX_IOMAPPED       (default answer: n)
     Answer "y" if you suspect your mother board to not allow memory mapped I/O.
     May slow down performance a little.  This option is required by
@@ -440,13 +481,12 @@
     suffers no performance loss with this option since all IO is memory
     mapped anyway.
 
-CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE    (default answer: n)
-    Answer "y" if you are sure that all your SCSI devices that are able to 
-    accept tagged commands will proceed safely.
+CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS    (default answer: 8)
+    Default tagged command queue depth.
 
-CONFIG_SCSI_NCR53C8XX_MAX_TAGS         (default answer: 4)
+CONFIG_SCSI_NCR53C8XX_MAX_TAGS         (default answer: 8)
     This option allows you to specify the maximum number of tagged commands 
-    that can be queued to a device.
+    that can be queued to a device. The maximum supported value is 64.
 
 CONFIG_SCSI_NCR53C8XX_SYNC            (default answer: 5)
     This option allows you to specify the frequency in MHz the driver 
@@ -535,8 +575,8 @@
       disc:n     disabled
  
 Special features
-   Only apply to 810A, 825A, 860 and 875 controllers.
-   Have no effect with normal 810 and 825.
+   Only apply to 810A, 825A, 860, 875 and 895 controllers.
+   Have no effect with other ones.
       specf:y    (or 1) enabled
       specf:n    (or 0) disabled
       specf:3           enabled except Memory Write And Invalidate
@@ -545,17 +585,25 @@
    Invalidate.
 
 Ultra SCSI support
-   Only apply to 860 and 875 controllers.
+   Only apply to 860, 875 and 895 controllers.
    Have no effect with other ones.
-      ultra:y    enabled
+      ultra:2    Ultra2 enabled
+      ultra:1    Ultra enabled
       ultra:n    disabled
 
-Number of tagged commands
+Default number of tagged commands
       tags:0     (or tags:1 ) tagged command queuing disabled
       tags:#tags (#tags  > 1) tagged command queuing enabled
   #tags will be truncated to the max queued commands configuration parameter.
-  If the driver is configured with a maximum of 4 queued commands, tags:4 is 
-  the right argument to specify.
+  This option also allows to specify a command queue depth for each device 
+  that support tagged command queueing.
+  Example:
+      ncr53c8xx=tags:10/t2t3q16-t5q24/t1u2q32
+               will set devices queue depth as follow:
+      - controller #0 target #2 and target #3                  -> 16 commands,
+      - controller #0 target #5                                -> 24 commands,
+      - controller #1 target #1 logical unit #2                -> 32 commands,
+      - all other logical units (all targets, all controllers) -> 10 commands.
 
 Default synchronous period factor
       sync:255     disabled (asynchronous transfer mode)
@@ -692,7 +740,7 @@
 If the driver has been configured with default options, the equivalent 
 boot setup is:
 
-   ncr53c8xx=mpar:y,spar:y,disc:y,specf:3,fsn:n,ultra:y,fsn:n,revprob:n,verb:1\
+   ncr53c8xx=mpar:y,spar:y,disc:y,specf:3,fsn:n,ultra:2,fsn:n,revprob:n,verb:1\
              tags:0,sync:50,debug:0,burst:7,led:0,wide:1,settle:2,diff:0,irqm:0
 
 For an installation diskette or a safe but not fast system,
@@ -705,8 +753,8 @@
 
 My personnal system works flawlessly with the following equivalent setup:
 
-   ncr53c8xx=mpar:y,spar:y,disc:y,specf:1,fsn:n,ultra:y,fsn:n,revprob:n,verb:1\
-             tags:8,sync:12,debug:0,burst:7,led:1,wide:1,settle:2,diff:0,irqm:0
+   ncr53c8xx=mpar:y,spar:y,disc:y,specf:1,fsn:n,ultra:2,fsn:n,revprob:n,verb:1\
+             tags:32,sync:12,debug:0,burst:7,led:1,wide:1,settle:2,diff:0,irqm:0
 
 The driver prints its actual setup when verbosity level is 2. You can try 
 "ncr53c8xx=verb:2" to get the "static" setup of the driver, or add "verb:2" 
@@ -723,8 +771,8 @@
 
 Use 'pcifix:3' in order to allow the driver to fix both PCI features.
 
-These options only apply to new SYMBIOS chips 810A, 825A, 860 and 875 
-and are only supported for Pentium and 486 class processors.
+These options only apply to new SYMBIOS chips 810A, 825A, 860, 875 
+and 895 and are only supported for Pentium and 486 class processors.
 Recent SYMBIOS 53C8XX scsi processors are able to use PCI read multiple 
 and PCI write and invalidate commands. These features require the 
 cache line size register to be properly set in the PCI configuration 
@@ -737,7 +785,7 @@
 Optimized PCI accesses may be broken for some PCI/memory controllers or 
 make problems with some PCI boards.
 
-This fix-up works flawlessly on my system.
+This fix-up worked flawlessly on my previous system.
 (MB Triton HX / 53C875 / 53C810A)
 I use these options at my own risks as you will do if you decide to 
 use them too.
@@ -812,17 +860,12 @@
 change other "defines", you must edit the header file.  Do that only
 if you know what you are doing.
 
-SCSI_NCR_SETUP_ULTRA_SUPPORT	(default: defined)
-	Ultra SCSI support.
-	Can be changed by the following boot setup command:
-		ncr53c8xx=ultra:n
-
 SCSI_NCR_SETUP_SPECIAL_FEATURES	(default: defined)
 	If defined, the driver will enable some special features according  
 	to chip and revision id.
-        For 810A, 860, 825A and 875 scsi chips, this option enables support 
-        of features that reduce load of PCI bus and memory accesses during  
-        scsi transfer processing: burst op-code fetch, read multiple, 
+        For 810A, 860, 825A, 875 and 895 scsi chips, this option enables 
+	support of features that reduce load of PCI bus and memory accesses 
+	during  scsi transfer processing: burst op-code fetch, read multiple, 
         read line, prefetch, cache line, write and invalidate, 
         burst 128 (875 only), large dma fifo (875 only), offset 16 (875 only).
 	Can be changed by the following boot setup command:
@@ -834,7 +877,7 @@
 SCSI_NCR_SHARE_IRQ		(default: defined)
 	If defined, request shared IRQ.
 	
-SCSI_NCR_MAX_TAGS		(default: 4)
+SCSI_NCR_MAX_TAGS		(default: 8)
 	Maximum number of simultaneous tagged commands to a device.
 	Can be changed by "settags <target> <maxtags>"
 
@@ -843,7 +886,7 @@
 	negotiation. 0 means asynchronous.
 	Can be changed by "setsync <target> <period factor>"
 
-SCSI_NCR_SETUP_DEFAULT_TAGS     (default: 4)
+SCSI_NCR_SETUP_DEFAULT_TAGS     (default: 8)
 	Default number of simultaneous tagged commands to a device.
 	< 1 means tagged command queuing disabled at start-up.
 
@@ -864,7 +907,7 @@
 SCSI_NCR_SETUP_MASTER_PARITY	(default: defined)
 	If defined, SCSI parity checking is enabled.
 
-SCSI_NCR_PROFILE		(default: defined)
+SCSI_NCR_PROFILE_SUPPORT	(default: not defined)
 	If defined, profiling information is gathered.
 
 SCSI_NCR_MAX_SCATTER		(default: 128)
@@ -899,67 +942,28 @@
 
 12. Installation
 
-12.1 Provided files
+This driver is part of the linux kernel distribution.
+Driver files are located in the sub-directory "drivers/scsi" of the 
+kernel source tree.
 
-Driver and common files:
+Driver files:
 
 	README.ncr53c8xx	: this file
 	ChangeLog.ncr53c8xx	: change log
-	ConfigHelp.ncr53c8xx	: Part of Configure.help about the driver
 	ncr53c8xx.h		: definitions
 	ncr53c8xx.c		: the driver code
-	scsitag.c		: command tool to enable tagged queue
-	conf.modules		: sample of /etc/conf.modules
-
-	Install.ncr53c8xx	: installation script
-
-	Patch-1.2.13.ncr53c8xx  : patch for linux-1.2.13
-	Patch-2.0.29.ncr53c8xx  : patch for linux-2.0.29
-
-You must untar the distribution with the following command:
-
-	tar zxvf ncrBsd2Linux-2.2b-src.tar.gz
-
-The sub-directory ncr53c8xx-2.2b will be created. Change to this directory.
-
-
-12.2 Installation procedure
-
-This install script has been tested with linux-1.2.13 and 2.0.29.
-
-This procedure copies the new driver files to the kernel tree and
-applies a patch to some files of the kernel tree.
-
-	If your linux directory is at the standard location
-	"/usr/src/linux", just enter:
-		Install.ncr53c8xx
-
-	Else enter:
-		Install.ncr53c8xx <your_linux_directory>
-
-	Make the kernel:
-		Change to linux source directory
-		Configure with NCR53C7,8XX support = N
-		Configure with NCR53C8XX   support = Y (or m)
-		Make dependancies
-		Make the kernel (use make zdisk first)
-		Make and install modules if you have configured with 'm'
-
-
-13. Control commands under linux-1.2.13
 
-Profiling data and control commands using the proc SCSI file system
-are not available for linux-1.2.13.  The only control command
-available is "scsitag" which allows you to enable tagged command
-queuing support after linux boot-up.
+New driver versions are made available separately in order to allow testing 
+changes and new features prior to including them into the linux kernel 
+distribution. The following URL provides informations on latest avalaible 
+patches: 
 
-Tagged command queueing is disabled by default at system startup.
+      ftp://ftp.tux.org/pub/people/gerard-roudier/README
 
-You can enable tagged queue per device with the following command:
 
-	scsitag device_name (ex: scsitag /dev/sda)
+13. Architecture dependant features.
 
-Use "cc -o scsitag scsitag.c" to create the "scsitag" executable.
+<Not yet written>
 
 
 14. Known problems
@@ -1076,10 +1080,10 @@
   Will enable fast synchronous data transfer negotiation for all targets.
 
 - echo "setflag 3" >/proc/scsi/ncr53c8xx/0
-  Will reset flags (no_sync) for target 3, and so will allow it to disconnect 
+  Will reset flags (no_disc) for target 3, and so will allow it to disconnect 
   the SCSI Bus.
 
-- echo "settags 3 4" >/proc/scsi/ncr53c8xx/0
+- echo "settags 3 8" >/proc/scsi/ncr53c8xx/0
   Will enable tagged command queuing for target 3 if that device supports it.
 
 Once you have found the device and the feature that cause problems, just 
@@ -1100,12 +1104,14 @@
 1 Mega-transfers/second means 1 MB/s with 8 bits SCSI and 2 MB/s with 
 Wide16 SCSI.
 
-16.1 Synchronous timings for 53C875 and 53C860 Ultra-SCSI controllers
+16.1 Synchronous timings for 53C895, 53C875 and 53C860 SCSI controllers
 
  ----------------------------------------------
  Negotiated                     NCR settings
  Factor   Period   Speed        Period   Speed
  ------   ------   ------       ------   ------
+ 10       25       40.000       25       40.000   (53C895 only)
+ 11       30.2     33.112       31.25    32.000   (53C895 only)
  12       50       20.000       50       20.000
  13       52       19.230       62       16.000
  14       56       17.857       62       16.000
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/README.tmscsim linux/drivers/scsi/README.tmscsim
--- linux.vanilla/drivers/scsi/README.tmscsim	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/README.tmscsim	Sun Dec 27 20:31:39 1998
@@ -0,0 +1,418 @@
+The tmscsim driver
+==================
+
+1. Purpose and history
+2. Installation
+3. Features
+4. Configuration via /proc/scsi/tmscsim/?
+5. Configuration via boot/module params
+6. Potential improvements
+7. Bug reports, debugging and updates
+8. Acknowledgements
+
+
+1. Purpose and history
+----------------------
+The tmscsim driver supports PCI SCSI Host Adapters based on the AM53C974
+chip. AM53C974 based SCSI adapters include: 
+ Tekram DC390, DC390T
+ Dawicontrol 2974
+ QLogic Fast! PCI Basic
+ some on-board adapters
+(This is most probably not a complete list)
+
+It has originally written by C.L. Huang from the Tekram corp. to support the
+Tekram DC390(T) adapter. This is where the name comes from: tm = Tekram
+scsi = SCSI driver, m = AMD (?) as opposed to w for the DC390W/U/F
+(NCR53c8X5, X=2/7) driver. Yes, there was also a driver for the latter,
+tmscsimw, which supported DC390W/U/F adapters. It's not maintained any more,
+as the ncr53c8xx is perfectly supporting these adpaters since some time.
+
+The driver first appeared in April 1996, exclusively supported the DC390 
+and has been enhanced since then in various steps. In May 1998 support for 
+general AM53C974 based adapters and some possibilities to configure it were
+added. The non-DC390 support works by assuming some values for the data
+normally taken from the DC390 EEPROM. See below (chapter 5) for details.
+
+When using the DC390, the configuration is still be done using the DC390
+BIOS setup. The DC390 EEPROM is read and used by the driver, any boot or
+module parameters (chapter 5) are ignored! However, you can change settings
+dynamically, as described in chapter 4. 
+
+For a more detailed description of the driver's history, see the first lines
+of tmscsim.c.
+The numbering scheme isn't consistent. The first versions went from 1.00 to
+1.12, then 1.20a to 1.20t. Finally I decided to use the ncr53c8xx scheme. So
+the next revisions will be 2.0a to 2.0X (stable), 2.1a to 2.1X (experimental),
+2.2a to 2.2X (stable, again) etc. (X = anything between a and z.) If I send
+fixes to people for testing, I create intermediate versions with a digit 
+appended, e.g. 2.0c3.
+
+
+2. Installation
+---------------
+If you got any recent kernel with this driver and document included in
+linux/drivers/scsi, you basically have to do nothing special to use this
+driver. Of course you have to choose to compile SCSI support and DC390(T)
+support into your kernel or as module when configuring your kernel for
+compiling.
+
+ If you got an old kernel (pre 2.1.127, pre 2.0.37p1) with an old version of
+ this driver: Get dc390-21125-20b.diff.gz or dc390-2036p21-20b1.diff.gz from
+ my website and apply the patch. 
+
+ If you want to do it manually, you should copy the files (dc390.h,
+ tmscsim.h, tmscsim.c, scsiiom.c and README.tmscsim) from this directory to
+ linux/drivers/scsi. You have to recompile your kernel/module of course.
+
+ You should apply the three patches included in dc390-120-kernel.diff
+ (Applying them: cd /usr/src; patch -p0 <~/dc390-120-kernel.diff)
+ The patches are against 2.1.125, so you might have to manually resolve
+ rejections when applying to another kernel version.
+
+ The patches will update the kernel startup code to allow boot parameters to
+ be passed to the driver, update the Documentation and finally offer you the
+ possibility to omit the non-DC390 parts of the driver.
+ (By selecting "Omit support for non DC390" you basically disable the
+ emulation of a DC390 EEPROM for non DC390 adapters. This saves a few bytes
+ of memory.)
+
+If you got a very old kernel without the tmscsim driver (pre 2.0.31)
+I recommend upgrading your kernel. However, if you don't want to, please
+contact me to get the appropriate patches.
+
+
+Upgrading a SCSI driver is always a delicate thing to do. The 2.0 driver has
+proven stable on many systems, but it's still a good idea to take some
+precautions. In an ideal world you would have a full backup of your disks.
+The world isn't ideal and most people don't have full backups (me neither).
+So take at least the following measures:
+* make your kernel remount the FS read-only on detecting an error:
+  tune2fs -e remount-ro /dev/sd??
+* have copies of your SCSI disk's partition tables on some safe location:
+  dd if=/dev/sda of=/mnt/floppy/sda bs=512 count=1
+* make sure you are able to boot Linux (e.g. from floppy disk using InitRD)
+  if your SCSI disk gets corrupted. You can use 
+  ftp://student.physik.uni-dortmund.de/pub/linux/kernel/bootdisk.gz
+
+One more warning: I used to overclock my PCI bus to 41.67 MHz. My Tekram
+DC390F (Sym53c875) accepted this as well as my Millenium. But the Am53C974
+produced errors and started to corrupt my disks. So don't do that! A 37.50
+MHz PCI bus works for me, though, but I don't recommend using higher clocks
+than the 33.33 MHz being in the PCI spec.
+
+If you want to share the IRQ with another device and the driver refuses to
+do, you might succeed with changing the DC390_IRQ type in tmscsim.c to 
+SA_SHIRQ | SA_INTERRUPT.
+
+
+3.Features
+----------
+- SCSI
+ * Tagged command queueing
+ * Sync speed up to 10 MHz
+ * Disconnection
+ * Multiple LUNs
+
+- General / Linux interface
+ * Support for up to 4 AM53C974 adapters.
+ * DC390 EEPROM usage or boot/module params
+ * Information via cat /proc/scsi/tmscsim/?
+ * Dynamically configurable by writing to /proc/scsi/tmscsim/?
+ * Dynamic allocation of resources
+ * SMP support: Locking on io_request lock (Linux 2.1/2.2) or adapter 
+    specific locks (Linux 2.3)
+ * Uniform source code for Linux-2.x.y
+ * Support for dyn. addition/removal of devices via add/remove-single-device
+   (Try: echo "scsi add-single-device H C I L" >/proc/scsi/scsi 
+    H = Host, C = Channel, I = SCSI ID, L = SCSI LUN.) Use with care!
+ * Try to use the partition table for the determination of the mapping
+
+
+4. Configuration via /proc/scsi/tmscsim/?
+-----------------------------------------
+First of all look at the output of /proc/scsi/tmscsim/? by typing
+ cat /proc/scsi/tmscsim/?
+The "?" should be replaced by the SCSI host number. (The shell might do this
+for you.)
+You will see some info regarding the adapter and, at the end, a listing of
+the attached devices and their settings.
+
+Here's an example:
+garloff@kg1:/home/garloff > cat /proc/scsi/tmscsim/0
+Tekram DC390/AM53C974 PCI SCSI Host Adapter, Driver Version 1.20s, 1998/08/20
+SCSI Host Nr 0, AM53C974 Adapter Nr 0
+IOPortBase 0x6200, IRQLevel 0x09
+MaxID 7, MaxLUN 8, AdapterID 7, SelTimeout 250 ms
+TagMaxNum 16, Status 0, ACBFlag 0, GlitchEater 24 ns
+Statistics: Nr of Cmnds 39563, Cmnds not sent directly 0, Out of SRB conds 0
+            Nr of lost arbitrations 17
+Nr of attached devices: 4, Nr of DCBs: 4
+Idx ID LUN Prty Sync DsCn SndS TagQ STOP NegoPeriod SyncSpeed SyncOffs
+00  00  00  Yes  Yes  Yes  Yes  Yes  No    100 ns    10.0 M      15
+01  01  00  Yes  Yes  Yes  Yes  Yes  No    100 ns    10.0 M      15
+02  03  00  Yes  Yes  Yes  Yes  No   No    100 ns    10.0 M      15
+03  05  00  Yes  No   Yes  Yes  No   No   (200 ns)
+
+Note that the settings MaxID and MaxLUN are not zero- but one-based, which
+means that a setting MaxLUN=4, will result in the support of LUNs 0..3. This
+is somehow inconvenient, but the way the mid-level SCSI code expects it to be.
+
+ACB and DCB are acronyms for Adapter Control Block and Device Control Block.
+These are data structures of the driver containing information about the
+adapter and the connected SCSI devices respectively.
+
+Idx is the device index (just a consecutive number for the driver), ID and
+LUN are the SCSI ID and LUN, Prty means Parity checking, Sync synchronous
+negotiation, DsCn Disconnection, SndS Send Start command on startup (not
+used by the driver) and TagQ Tagged Command Queueing. NegoPeriod and
+SyncSpeed are somehow redundant, because they are reciprocal values 
+(1 / 112 ns = 8.9 MHz). At least in theory. The driver is able to adjust the
+NegoPeriod more accurate (4ns) than the SyncSpeed (1 / 25ns). I don't know
+if certain devices will have problems with this discrepancy. Max. speed is
+10 MHz corresp. to a min. NegoPeriod of 100 ns. 
+(The driver allows slightly higher speeds if the devices (Ultra SCSI) accept
+it, but that's out of adapter spec, on your own risk and unlikely to improve
+performance. You're likely to crash your disks.) 
+SyncOffs is the offset used for synchronous negotiations; max. is 15. 
+The last values are only shown, if Sync is enabled. (NegoPeriod is still
+displayed in brackets to show the values which will be used after enabling
+Sync.)
+The STOP parameter is for testing/debugging purposes only and should bet set
+to No. Please don't fiddle with it, unless you want to get rid of the
+contents of your disk.
+
+If you want to change a setting, you can do that by writing to
+/proc/scsi/tmscsim/?. Basically you have to imitate the output of driver.
+(Don't use the brackets for NegoPeriod on Sync disabled devices.)
+You don't have to care about capitalisation. The driver will accept space,
+tab, comma, = and : as separators.
+
+There are three kinds of changes: 
+
+(1) Change driver settings: 
+    You type the names of the parameters and the params following it.
+    Example:
+     echo "MaxLUN=8 seltimeout 200" >/proc/scsi/tmscsim/0
+
+    Note that you can only change MaxID, MaxLUN, AdapterID, SelTimeOut,
+    TagMaxNum, ACBFlag and GlitchEater. Don't change ACBFlag unless you
+    want to see what happens, if the driver hangs.
+
+(2) Change device settings: You write a config line to the driver. The Nr
+    must match the ID and LUN given. If you give "-" as parameter, it is
+    ignored and the corresponding setting won't be changed. 
+    You can use "y" or "n" instead of "Yes" and "No" if you want to.
+    You don't need to specify a full line. The driver automatically performs
+    an INQUIRY on the device if necessary to check if it is capable to operate
+    with the given settings (Sync, TagQ).
+    Examples:
+     echo "0 0 0 y y y - y - 10" >/proc/scsi/tmscsim/0
+     echo "3 5 0 y n y" >/proc/scsi/tmscsim/0
+
+    To give a short explanation of the first example: 
+    The first three numbers, "0 0 0" (Device index 0, SCSI ID 0, SCSI LUN 0),
+    select the device to which the following parameters apply. Note that it
+    would be sufficient to use the index or both SCSI ID and LUN, but I chose
+    to require all three to have a syntax similar to the output.
+    The following "y y y - y" enables Parity checking, enables Synchronous
+    transfers, Disconnection, leaves Send Start (not used) untouched and
+    enables Tagged Command Queueing for the selected device. The "-" skips
+    the Negotiation Period setting but the "10" sets the max sync. speed to
+    10 MHz. It's useless to specify both NegoPeriod and SyncSpeed as
+    discussed above. The values used in this example will result in maximum
+    performance.
+
+(3) Special commands: You can force a SCSI bus reset, an INQUIRY command and
+    the removal of a device's DCB.
+    This is only used for debugging when you meet problems. The parameter of
+    the INQUIRY and remove command is the device index as shown by the
+    output of /proc/scsi/tmscsim/? in the device listing in the first column
+    (Idx).
+    Examples:
+     echo "reset" >/proc/scsi/tmscsim/0
+     echo "inquiry 1" >/proc/scsi/tmscsim/0
+     echo "remove 2" >/proc/scsi/tmscsim/1
+
+    Note that you will meet problems when you remove a device's DCB with the
+    remove command if it contains partitions which are mounted. Only use it
+    after unmounting its partitions, telling the SCSI mid-level code to
+    remove it (scsi remove-single-device) and you really need a few bytes of
+    memory.
+
+
+I'd suggest reviewing the output of /proc/scsi/tmscsim/? after changing
+settings to see if everything changed as requested.
+
+
+5. Configuration via boot/module parameters
+-------------------------------------------
+With the DC390, the driver reads its EEPROM settings and IGNORES boot / 
+module parameters. If you want to override the EEPROM settings of a DC390, 
+you have to use the /proc/scsi/tmscsim/? interface described in the above
+chapter.
+
+However, if you do have another AM53C974 based adapter you might want to
+adjust some settings before you are able to write to the /proc/scsi/tmscsim/?
+pseudo-file, e.g. if you want to use another adapter ID than 7. (Note that
+the log message "DC390: No EEPROM found!" is normal without a DC390.)
+For this purpose, you can pass options to the driver before it is initialised
+by using kernel or module parameters. See lilo(8) or modprobe(1) manual
+pages on how to pass params to the kernel or a module.
+
+The syntax of the params is much shorter than the syntax of the /proc/...
+interface. This makes it a little bit more difficult to use. However, long
+parameter lines have the risk to be misinterpreted and the length of kernel
+parameters is limited.
+
+As the support for non-DC390 adapters works by simulating the values of the
+DC390 EEPROM, the settings are given in a DC390 BIOS' way.
+
+Here's the syntax:
+tmscsim=AdaptID,SpdIdx,DevMode,AdaptMode,TaggedCmnds
+
+Each of the parameters is a number, containing the described information:
+
+* AdaptID: The SCSI ID of the host adapter. Must be in the range 0..7
+  Default is 7.
+
+* SpdIdx: The index of the maximum speed as in the DC390 BIOS. The values
+  0..7 mean 10, 8.0, 6.7, 5.7, 5.0, 4.0, 3.1 and 2 MHz resp. Default is
+  1 (8.0 MHz).
+
+* DevMode is a bit mapped value describing the per-device features. It
+  applies to all devices. (Sync, Disc and TagQ will only apply, if the
+  device supports it.) The meaning of the bits (* = default):
+
+   Bit Val(hex) Val(dec)  Meaning
+   *0	 0x01	    1	  Parity check
+   *1	 0x02	    2	  Synchronous Negotiation
+   *2	 0x04	    4	  Disconnection
+   *3	 0x08	    8	  Send Start command on startup. (Not used)
+   *4	 0x10	   16	  Tagged Queueing
+
+  As usual, the desired value is obtained by adding the wanted values. If
+  you want to enable all values, e.g., you would use 31(0x1f). Default is 31.
+
+* AdaptMode is a bit mapped value describing the enabled adapter features.
+
+   Bit Val(hex) Val(dec)  Meaning
+   *0	 0x01	    1	  Support more than two drives. (Not used)
+   *1	 0x02	    2	  Use DOS compatible mapping for HDs greater than 1GB.
+   *2	 0x04	    4	  Reset SCSI Bus on startup.
+   *3	 0x08	    8	  Active Negation: Improves SCSI Bus noise immunity.
+    4	 0x10	   16	  Immediate return on BIOS seek command. (Not used)
+ (*)5	 0x20	   32	  Check for LUNs >= 1.
+  
+  The default for LUN Check depends on CONFIG_SCSI_MULTI_LUN.
+
+* TaggedCmnds is a number indicating the maximum number of Tagged Commands.
+  It is the binary logarithm - 1 of the actual number. Max is 4 (32).
+   Value  Number of Tagged Commands
+     0		 2
+     1		 4
+     2		 8
+    *3		16
+     4		32
+
+Example:
+ modprobe tmscsim tmscsim=6,2,31
+would set the adapter ID to 6, max. speed to 6.7 MHz, enable all device
+features and leave the adapter features and the number of Tagged Commands 
+to the defaults.
+
+As you can see, you don't need to specify all of the five params.
+
+The defaults (7,1,31,15,3) are aggressive to allow good performance. You can
+use tmscsim=7,0,31,63,4 for maximum performance, if your SCSI chain is
+perfect. If you meet problems, you can use tmscsim=-1 which is a shortcut
+for tmscsim=7,4,9,15,2. 
+
+
+6. Potential improvements
+-------------------------
+Most of the intended work on the driver has been done. Here are a few ideas
+to further improve its usability:
+
+* More intelligent abort() routine
+* Implement new_eh code (Linux-2.1+)
+* Have the mid-level code (and not the driver) handle more of the various
+  conditions.
+* Rework command queueing in the driver
+* More user friendly boot/module param syntax
+
+Further investigation on these problems:
+
+* Driver hangs with sync readcdda (xcdroast) (most probably VIA PCI error)
+
+Known problems:
+
+* There was a report that with a certain Scanner, the last SCSI command
+  won't be finished correctly. This might be a command queueing bug or a bug
+  in SCSI implementation of the scanner. Issueing another command to the
+  scanner seems to help. (Try echo "INQUIRY x" >/proc/scsi/tmscsim/?, where
+  x is the index (not the SCSI ID!) of the scanner. See 4.(3).)
+* If there is a valid partition table, the driver will use it for determing
+  the mapping. Other operating systems may not like this mapping, though
+  it's consistent with the BIOS' behaviour. Old DC390 drivers ignored the
+  partition table and used a H/S = 64/32 or 255/63 translation. So if you
+  want to be compatible to those, use this old mapping when creating
+  partition tables.
+* In some situations, the driver will get stuck in an abort loop. Please
+  disable DsCn, if you meet this problem. Please contact me for further
+  debugging.
+* 2.1.115+: Linux misses locks in sr_ioctl.c and scsi_ioctl.c
+  There used to be a patch included here, which partially solved the
+  problem. I suggest you contact Chiaki Ishikawa <ishikawa@yk.rim.or.jp>, 
+  Richard Waltham <dormouse@farsrobt.demon.co.uk> or Doug Ledford
+  <dledford@dialnet.net>, if you want to help further debugging it.
+* 2.0.35: CD changers (e.g. NAKAMICHI MBR-7.{0,2}) have problems because
+  the mid-level code doesn't handle BLIST_SINGLELUN correctly. There used
+  to be a patch included here to fix this, but I was told that it is fixed
+  in 2.0.36.
+
+
+7. Bug reports, debugging and updates
+-------------------------------------
+Whenever you have problems with the driver, you are invited to ask the
+author for help. However, I'd suggest reading the docs and trying to solve
+the problem yourself, first. 
+If you find something, which you believe to be a bug, please report it to me. 
+Please append the output of /proc/scsi/scsi, /proc/scsi/tmscsim/? and
+maybe the DC390 log messages to the report. 
+
+Bug reports should be send to me (Kurt Garloff <dc390@garloff.de>) as well
+as to the linux-scsi list (<linux-scsi@vger.rutgers.edu>), as sometimes bugs
+are caused by the SCSI mid-level code.
+
+I will ask you for some more details and probably I will also ask you to
+enable some of the DEBUG options in the driver (tmscsim.c:DC390_DEBUGXXX
+defines). The driver will produce some data for the syslog facility then.
+Beware: If your syslog gets written to a SCSI disk connected to your
+AM53C974, the logging might produce log output again, and you might end
+having your box spending most of its time doing the logging.
+
+The latest version of the driver can be found at:
+ http://www.garloff.de/kurt/linux/dc390/
+and
+ ftp://student.physik.uni-dortmund.de/pub/linux/kernel/dc390/
+(The latter might shut down some day.)
+
+
+8. Acknowledgements
+-------------------
+Thanks to Linus Torvalds, Alan Cox, David Miller, Rik v. Riel, the FSF
+people, the XFree86 team and all the others for the wonderful OS and
+software. 
+Thanks to C.L. Huang and Philip Giang (Tekram) for the initial driver
+release and support.
+Thanks to Doug Ledford, Gerard Roudier for support with SCSI coding.
+Thanks to a lot of people (espec. Chiaki Ishikawa, Andreas Haumer, Hubert 
+Tonneau) for intensively testing the driver (and even risking data loss
+doing this during early revisions). 
+
+
+-------------------------------------------------------------------------
+Written by Kurt Garloff <kurt@garloff.de> 1998/06/11
+Last updated 1998/12/25, driver revision 2.0d
+$Id: README.tmscsim,v 2.9 1998/12/25 18:04:20 garloff Exp $
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/RELEASE_NOTES.DAC960 linux/drivers/scsi/RELEASE_NOTES.DAC960
--- linux.vanilla/drivers/scsi/RELEASE_NOTES.DAC960	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/RELEASE_NOTES.DAC960	Mon Nov 30 07:59:59 1998
@@ -0,0 +1,74 @@
+	       Mylex DAC960 PCI RAID Controller Driver for Linux
+
+			Version 2.0.0 for Linux 2.0.36
+			Version 2.1.0 for Linux 2.1.130
+
+			      BETA TEST RELEASE 3
+
+			       23 November 1998
+
+			       Leonard N. Zubkoff
+			       Dandelion Digital
+			       lnz@dandelion.com
+
+	   Copyright 1998 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+
+				 RELEASE NOTES
+
+o The error reporting functionality has been greatly improved.  The driver now
+  queries the controller every 7 seconds to collect status information, and
+  reports on progress rebuilding logical drives at most every 60 seconds.
+  The status of all physical disk drives is also queried as well as updates
+  to the controller's internal error log.  This functionality allows for
+  more accurate reporting of the cause of drive failures when they occur.
+
+o The production model AcceleRAID controllers are now supported.  Mylex had
+  unfortunately provided me with pre-production AcceleRAID units which differed
+  in critical ways from the production models, and so the Beta2 driver release
+  did not function properly.
+
+o New releases of the Linux Symbios driver now contain code to notice when
+  motherboard Symbios chips are controlled by an AcceleRAID and to not
+  interfere with operation of the AcceleRAID board.  A patch is also available
+  for the Symbios driver version 3.0i.
+
+o A bug was corrected where functionality necessary for swapping to files was
+  not implemented.
+
+
+
+
+	       Mylex DAC960 PCI RAID Controller Driver for Linux
+
+			Version 2.0.0 for Linux 2.0.36
+			Version 2.1.0 for Linux 2.1.126
+
+			      BETA TEST RELEASE 2
+
+				19 October 1998
+
+			       Leonard N. Zubkoff
+			       Dandelion Digital
+			       lnz@dandelion.com
+
+	   Copyright 1998 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+
+				 RELEASE NOTES
+
+o The Mylex AcceleRAID 250 (DAC960PTL-1) and AcceleRAID 200 (DAC960PTL-0)
+  models are now supported.  Please note that if you use an AcceleRAID 200,
+  or an AcceleRAID 250 configured to use the Symbios chips on the motherboard,
+  then the Symbios driver must not be included in the Linux kernel.  This
+  restriction will be removed in a future release of these drivers.
+
+o This version of the DAC960 driver uses a more efficient interface for
+  communicating with the AcceleRAID 250, AcceleRAID 200, DAC960PJ, and
+  DAC960PG models.
+
+o A bug was corrected where the cache was not flushed at shutdown on systems
+  with a DAC960 Logical Drive mounted as the root file system.  This only
+  affected systems where the DAC960 Logical Drive was configured for write back
+  operation without a battery backup unit installed and the system was powered
+  off within a couple of seconds of system shutdown.
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/TRIPACE.REL linux/drivers/scsi/TRIPACE.REL
--- linux.vanilla/drivers/scsi/TRIPACE.REL	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/TRIPACE.REL	Fri Feb 19 15:30:36 1999
@@ -0,0 +1,21 @@
+Dated December 21 Chennai,India:-
+----- -------- -----------------
+
+
+Release Notes of 1.00.000 (904) version:-
+
+1.0 All machine dependent timing loops have been removed and
+    replaced with the udelay function.
+
+2.0 Search function has been made static
+
+3.0 Search function ,in case of failure was not reenabling interrupts
+    This has been corrected.
+
+4.0 The save_flags,cli & restore_flags in ISR has been removed.
+
+5.0 Wait_risc_halt has a sanity check and times out incase hw failure.
+
+6.0 Source code indentation with "indent -kr -i8 -bl -bli0 tripace.c"
+
+
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/aha1542.c linux/drivers/scsi/aha1542.c
--- linux.vanilla/drivers/scsi/aha1542.c	Sun Dec  6 00:14:42 1998
+++ linux/drivers/scsi/aha1542.c	Mon Dec 14 18:35:04 1998
@@ -120,8 +120,8 @@
 #define aha1542_intr_reset(base)  outb(IRST, CONTROL(base))
 
 #define WAIT(port, mask, allof, noneof)					\
- { register WAITbits;							\
-   register WAITtimeout = WAITnexttimeout;				\
+ { register int WAITbits;							\
+   register int WAITtimeout = WAITnexttimeout;				\
    while (1) {								\
      WAITbits = inb(port) & (mask);					\
      if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \
@@ -133,8 +133,8 @@
 /* Similar to WAIT, except we use the udelay call to regulate the
    amount of time we wait.  */
 #define WAITd(port, mask, allof, noneof, timeout)			\
- { register WAITbits;							\
-   register WAITtimeout = timeout;					\
+ { register int WAITbits;							\
+   register int WAITtimeout = timeout;					\
    while (1) {								\
      WAITbits = inb(port) & (mask);					\
      if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
--- linux.vanilla/drivers/scsi/aic7xxx.c	Sun Dec  6 00:14:43 1998
+++ linux/drivers/scsi/aic7xxx.c	Fri Mar 26 17:02:53 1999
@@ -100,7 +100,7 @@
  *
  * Further driver modifications made by Doug Ledford <dledford@redhat.com>
  *
- * Copyright (c) 1997-1998 Doug Ledford
+ * Copyright (c) 1997-1999 Doug Ledford
  *
  * These changes are released under the same licensing terms as the FreeBSD
  * driver written by Justin Gibbs.  Please see his Copyright notice above
@@ -336,6 +336,7 @@
 #include "aic7xxx/sequencer.h"
 #include "aic7xxx/scsi_message.h"
 #include "aic7xxx_reg.h"
+#include <scsi/scsicam.h>
 
 #include <linux/stat.h>
 #include <linux/malloc.h>        /* for kmalloc() */
@@ -354,7 +355,7 @@
     0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-#define AIC7XXX_C_VERSION  "5.1.4"
+#define AIC7XXX_C_VERSION  "5.1.13"
 
 #define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
 #define MIN(a,b)        (((a) < (b)) ? (a) : (b))
@@ -447,8 +448,10 @@
  * You can try raising me if tagged queueing is enabled, or lowering
  * me if you only have 4 SCBs.
  */
-#ifdef CONFIG_AIC7XXX_CMDS_PER_LUN
-#define AIC7XXX_CMDS_PER_LUN CONFIG_AIC7XXX_CMDS_PER_LUN
+#ifdef CONFIG_AIC7XXX_CMDS_PER_DEVICE
+#define AIC7XXX_CMDS_PER_DEVICE CONFIG_AIC7XXX_CMDS_PER_DEVICE
+#else
+#define AIC7XXX_CMDS_PER_DEVICE 8
 #endif
 
 /* Set this to the delay in seconds after SCSI bus reset. */
@@ -493,7 +496,7 @@
  *
  * *** Determining commands per LUN ***
  * 
- * When AIC7XXX_CMDS_PER_LUN is not defined, the driver will use its
+ * When AIC7XXX_CMDS_PER_DEVICE is not defined, the driver will use its
  * own algorithm to determine the commands/LUN.  If SCB paging is
  * enabled, which is always now, the default is 8 commands per lun
  * that indicates it supports tagged queueing.  All non-tagged devices
@@ -511,8 +514,13 @@
  * Make a define that will tell the driver not to use tagged queueing
  * by default.
  */
+#ifdef CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT
+#define DEFAULT_TAG_COMMANDS {0, 0, 0, 0, 0, 0, 0, 0,\
+                              0, 0, 0, 0, 0, 0, 0, 0}
+#else
 #define DEFAULT_TAG_COMMANDS {255, 255, 255, 255, 255, 255, 255, 255,\
                               255, 255, 255, 255, 255, 255, 255, 255}
+#endif
 
 /*
  * Modify this as you see fit for your system.  By setting tag_commands
@@ -551,6 +559,27 @@
 };
 */
 
+static adapter_tag_info_t aic7xxx_tag_info[] =
+{
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS}
+};
+
+
 /*
  * Define an array of board names that can be indexed by aha_type.
  * Don't forget to change this when changing the types!
@@ -577,9 +606,14 @@
   "Adaptec AHA-2944 Ultra SCSI host adapter",           /* AIC_7884 */
   "Adaptec AIC-7895 Ultra SCSI host adapter",           /* AIC_7895 */
   "Adaptec AIC-7890/1 Ultra2 SCSI host adapter",        /* AIC_7890 */
+  "Adaptec AHA-293X Ultra2 SCSI host adapter",          /* AIC_7890 */
   "Adaptec AHA-294X Ultra2 SCSI host adapter",          /* AIC_7890 */
   "Adaptec AIC-7896/7 Ultra2 SCSI host adapter",        /* AIC_7896 */
-  "Adaptec AHA-394X Ultra2 SCSI host adapter"           /* AIC_7897 */
+  "Adaptec AHA-394X Ultra2 SCSI host adapter",          /* AIC_7897 */
+  "Adaptec AHA-395X Ultra2 SCSI host adapter",          /* AIC_7897 */
+  "Adaptec PCMCIA SCSI controller",                     /* card bus stuff */
+  "Adaptec AIC-7892 Ultra 160/m SCSI host adapter",     /* AIC_7892 */
+  "Adaptec AIC-7899 Ultra 160/m SCSI host adapter",     /* AIC_7899 */
 };
 
 /*
@@ -882,6 +916,7 @@
   *  and what flags weren't.  This way, I could clean up the flag usage on
   *  a use by use basis.  Doug Ledford
   */
+        AHC_RESET_DELAY           = 0x00080000,
         AHC_A_SCANNED             = 0x00100000,
         AHC_B_SCANNED             = 0x00200000,
         AHC_MULTI_CHANNEL         = 0x00400000,
@@ -908,6 +943,8 @@
   AHC_AIC7890          = 0x0006,
   AHC_AIC7895          = 0x0007,
   AHC_AIC7896          = 0x0008,
+  AHC_AIC7892          = 0x0009,
+  AHC_AIC7899          = 0x000a,
   AHC_VL               = 0x0100,
   AHC_EISA             = 0x0200,
   AHC_PCI              = 0x0400,
@@ -924,6 +961,7 @@
   AHC_QUEUE_REGS       = 0x0040,
   AHC_SG_PRELOAD       = 0x0080,
   AHC_SPIOCAP          = 0x0100,
+  AHC_ULTRA160         = 0x0200,
   AHC_AIC7770_FE       = AHC_FENONE,
   AHC_AIC7850_FE       = AHC_SPIOCAP,
   AHC_AIC7860_FE       = AHC_ULTRA|AHC_SPIOCAP,
@@ -933,6 +971,8 @@
                          AHC_QUEUE_REGS|AHC_SG_PRELOAD,
   AHC_AIC7895_FE       = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA,
   AHC_AIC7896_FE       = AHC_AIC7890_FE,
+  AHC_AIC7892_FE       = AHC_AIC7890_FE|AHC_ULTRA160,
+  AHC_AIC7899_FE       = AHC_AIC7890_FE|AHC_ULTRA160,
 } ahc_feature;
 
 struct aic7xxx_scb {
@@ -941,7 +981,6 @@
         struct aic7xxx_scb    *q_next;        /* next scb in queue */
         volatile scb_flag_type flags;         /* current state of scb */
         struct hw_scatterlist *sg_list;       /* SG list in adapter format */
-        void                  *kmalloc_ptr;
         unsigned char          tag_action;
         unsigned char          sg_count;
         unsigned char          sense_cmd[6];  /*
@@ -952,6 +991,7 @@
                                            * don't have to calculate anything
                                            * during underflow/overflow/stat code
                                            */
+        void                  *kmalloc_ptr;
 };
 
 /*
@@ -1029,88 +1069,73 @@
    *  This is the first 64 bytes in the host struct
    */
 
-  struct Scsi_Host        *host;             /* pointer to scsi host */
-  struct aic7xxx_host     *next;             /* allow for multiple IRQs */
-  int                      host_no;          /* SCSI host number */
-  unsigned long            base;             /* card base address */
-  volatile unsigned char  *maddr;            /* memory mapped address */
-  unsigned long            mbase;            /* I/O memory address */
+  /*
+   * We are grouping things here....first, items that get either read or
+   * written with nearly every interrupt
+   */
   volatile ahc_flag_type   flags;
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)
-  spinlock_t               spin_lock;
-#endif
-  volatile unsigned char   cpu_lock_count[NR_CPUS];
-  ahc_chip                 chip;             /* chip type */
   ahc_feature              features;         /* chip features */
-  unsigned long            last_reset;
+  unsigned long            base;             /* card base address */
+  volatile unsigned char  *maddr;            /* memory mapped address */
   unsigned long            isr_count;        /* Interrupt count */
   unsigned long            spurious_int;
-  struct target_cmd       *targetcmds;
-  unsigned int             num_targetcmds;
+  scb_data_type           *scb_data;
+  volatile unsigned short  needsdtr;
+  volatile unsigned short  sdtr_pending;
+  volatile unsigned short  needwdtr;
+  volatile unsigned short  wdtr_pending;
+  struct aic7xxx_cmd_queue {
+    Scsi_Cmnd *head;
+    Scsi_Cmnd *tail;
+  } completeq;
+
+  /*
+   * Things read/written on nearly every entry into aic7xxx_queue()
+   */
+  volatile scb_queue_type  waiting_scbs;
   unsigned short           discenable;       /* Targets allowed to disconnect */
   unsigned short           tagenable;        /* Targets using tagged I/O */
   unsigned short           orderedtag;       /* Ordered Q tags allowed */
-  volatile unsigned char   activescbs;       /* active scbs */
-  volatile unsigned char   max_activescbs;
   unsigned char            unpause;          /* unpause value for HCNTRL */
   unsigned char            pause;            /* pause value for HCNTRL */
   volatile unsigned char   qoutfifonext;
+  volatile unsigned char   activescbs;       /* active scbs */
+  volatile unsigned char   max_activescbs;
   volatile unsigned char   qinfifonext;
 
-  /*
-   * MAX_TARGETS is currently == 16, so that makes these entries the next
-   * 64 bytes
-   */
-
 #define  DEVICE_PRESENT                 0x01
 #define  BUS_DEVICE_RESET_PENDING       0x02
-#define  DEVICE_TIMEOUT                 0x04
+#define  DEVICE_RESET_DELAY             0x04
 #define  DEVICE_PRINT_SDTR              0x08
 #define  DEVICE_PRINT_WDTR              0x10
-#define  DEVICE_SUCCESS                 0x20
-#define  DEVICE_TAGGED_SUCCESS          0x40
+#define  DEVICE_WAS_BUSY                0x20
 #define  DEVICE_SCANNED                 0x80
   volatile unsigned char   dev_flags[MAX_TARGETS];
   volatile unsigned char   dev_active_cmds[MAX_TARGETS];
   volatile unsigned char   dev_temp_queue_depth[MAX_TARGETS];
   unsigned char            dev_commands_sent[MAX_TARGETS];
 
-  /*
-   * The next 128 (or 256 on 64 bit machines)....
-   */
-  Scsi_Cmnd               *dev_wdtr_cmnd[MAX_TARGETS];
-  Scsi_Cmnd               *dev_sdtr_cmnd[MAX_TARGETS];
+  unsigned int             dev_timer_active; /* Which devs have a timer set */
+  struct timer_list        dev_timer;
+  unsigned long            dev_expires[MAX_TARGETS];
 
-  /*
-   * The next 64.... 
-   */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)
+  spinlock_t               spin_lock;
+  volatile unsigned char   cpu_lock_count[NR_CPUS];
+#endif
 
-  long                     dev_last_reset[MAX_TARGETS];
 
-  /*
-   * The next 64....
-   */
+#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
+  Scsi_Cmnd               *dev_wdtr_cmnd[MAX_TARGETS];
+  Scsi_Cmnd               *dev_sdtr_cmnd[MAX_TARGETS];
+#endif
 
-  unsigned char            dev_mid_level_queue_depth[MAX_TARGETS];
   unsigned char            dev_last_queue_full[MAX_TARGETS];
   unsigned char            dev_last_queue_full_count[MAX_TARGETS];
   unsigned char            dev_max_queue_depth[MAX_TARGETS];
 
-  /*
-   * The next 128....
-   */
-
   volatile scb_queue_type  delayed_scbs[MAX_TARGETS];
 
-  /*
-   *
-   */
-
-  struct timer_list        dev_timer[MAX_TARGETS];
-
-  /*
-   * The next 64....
-   */
 
   unsigned char            msg_buf[9];       /* The message for the target */
   unsigned char            msg_type;
@@ -1120,16 +1145,6 @@
   unsigned char            msg_len;          /* Length of message */
   unsigned char            msg_index;        /* Index into msg_buf array */
   transinfo_type           transinfo[MAX_TARGETS];
-  volatile scb_queue_type  waiting_scbs;     /*
-                                              * SCBs waiting for space in
-                                              * the QINFIFO.
-                                              */
-  scb_data_type           *scb_data;
-
-  struct aic7xxx_cmd_queue {
-    Scsi_Cmnd *head;
-    Scsi_Cmnd *tail;
-  } completeq;
 
 
   /*
@@ -1147,16 +1162,11 @@
   volatile unsigned char   qoutfifo[256];
   volatile unsigned char   qinfifo[256];
   unsigned int             irq;              /* IRQ for this adapter */
-  volatile unsigned short  needsdtr;
-  volatile unsigned short  sdtr_pending;
-  volatile unsigned short  needwdtr;
-  volatile unsigned short  wdtr_pending;
   int                      instance;         /* aic7xxx instance number */
   int                      scsi_id;          /* host adapter SCSI ID */
   int                      scsi_id_b;        /* channel B for twin adapters */
   unsigned int             bios_address;
   int                      board_name_index;
-  unsigned long            reset_start;
   unsigned short           needsdtr_copy;    /* default config */
   unsigned short           needwdtr_copy;    /* default config */
   unsigned short           ultraenb;         /* Ultra mode target list */
@@ -1170,6 +1180,11 @@
   struct seeprom_config    sc;
   unsigned short           sc_type;
   unsigned short           sc_size;
+  struct aic7xxx_host     *next;             /* allow for multiple IRQs */
+  struct Scsi_Host        *host;             /* pointer to scsi host */
+  int                      host_no;          /* SCSI host number */
+  unsigned long            mbase;            /* I/O memory address */
+  ahc_chip                 chip;             /* chip type */
 
   /*
    * Statistics Kept:
@@ -1184,21 +1199,27 @@
    *
    * NOTE: Enabling this feature is likely to cause a noticeable performance
    * decrease as the accesses into the stats structures blows apart multiple
-   * cache lines and is CPU time consuming.  We keep the xfer count always
-   * for use by the aic7xxx_proc.c code, but only do the bins if the
-   * proc stats code is enabled.
+   * cache lines and is CPU time consuming.
+   *
+   * NOTE: Since it doesn't really buy us much, but consumes *tons* of RAM
+   * and blows apart all sorts of cache lines, I modified this so that we
+   * no longer look at the LUN.  All LUNs now go into the same bin on each
+   * device for stats purposes.
    */
   struct aic7xxx_xferstats {
-    long xfers;                              /* total xfer count */
-    long w_total;                            /* total writes */
-    long w_total512;                         /* 512 byte blocks written */
-    long r_total;                            /* total reads */
-    long r_total512;                         /* 512 byte blocks read */
+    long w_total;                          /* total writes */
+    long r_total;                          /* total reads */
 #ifdef AIC7XXX_PROC_STATS
-    long w_bins[10];                         /* binned write */
-    long r_bins[10];                         /* binned reads */
+    long w_bins[8];                       /* binned write */
+    long r_bins[8];                       /* binned reads */
 #endif /* AIC7XXX_PROC_STATS */
-  } stats[MAX_TARGETS][MAX_LUNS];            /* [(channel << 3)|target][lun] */
+  } stats[MAX_TARGETS];                    /* [(channel << 3)|target] */
+
+#if 0
+  struct target_cmd       *targetcmds;
+  unsigned int             num_targetcmds;
+#endif
+
 };
 
 /*
@@ -1282,11 +1303,9 @@
  */
 static int aic7xxx_reverse_scan = 0;
 /*
- * This setting enables a hack to fix the IRQ settings on buggy 7895
- * MB controller setups:
- *    -1 == Disable this hack
- *     0 == Use the Channel A IRQ for both channels
- *     1 == Use the Channel B IRQ for both channels
+ * Should we force EXTENDED translation on a controller.
+ *     0 == Use whatever is in the SEEPROM or default to off
+ *     1 == Use whatever is in the SEEPROM or default to on
  */
 static unsigned int aic7xxx_extended = 0;
 /*
@@ -1426,35 +1445,6 @@
 
 #endif
 
-/*
- * See the comments earlier in the file for what this item is all about
- * If you have more than 4 controllers, you will need to increase the
- * the number of items in the array below.  Additionally, if you don't
- * want to have lilo pass a humongous config line to the aic7xxx driver,
- * then you can get in and manually adjust these instead of leaving them
- * at the default.  Pay attention to the comments earlier in this file
- * concerning this array if you are going to hand modify these values.
- */
-static adapter_tag_info_t aic7xxx_tag_info[] =
-{
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS}
-};
-
 #define VERBOSE_NORMAL         0x0000
 #define VERBOSE_NEGOTIATION    0x0001
 #define VERBOSE_SEQINT         0x0002
@@ -1511,6 +1501,21 @@
   for(i=0; i<milliseconds; i++)
     udelay(1000);
 }
+
+static inline int
+time_after_eq(unsigned long a, unsigned long b)
+{
+  return((long)((a) - (b)) >= 0L);
+}
+
+static inline int
+timer_pending(struct timer_list *timer)
+{
+  return( timer->prev != NULL );
+}
+
+#define PCI_DEVICE_ID_ADAPTEC_1480A 0x6075
+
 #endif
 
 static inline unsigned char
@@ -1950,7 +1955,10 @@
   {
     printk(KERN_INFO "(scsi%d) Downloading sequencer code...", p->host_no);
   }
+#if 0
   download_consts[TMODE_NUMCMDS] = p->num_targetcmds;
+#endif
+  download_consts[TMODE_NUMCMDS] = 0;
   cur_patch = &sequencer_patches[0];
   downloaded = 0;
   skip_addr = 0;
@@ -2794,6 +2802,7 @@
   Scsi_Cmnd *cmd;
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
   unsigned int cpu_flags = 0;
+#endif
   
   DRIVER_LOCK
   while (p->completeq.head != NULL)
@@ -2801,20 +2810,9 @@
     cmd = p->completeq.head;
     p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble;
     cmd->host_scribble = NULL;
-    sti();
     cmd->scsi_done(cmd);
-    cli();
   }
   DRIVER_UNLOCK
-#else
-  while (p->completeq.head != NULL)
-  {
-    cmd = p->completeq.head;
-    p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble;
-    cmd->host_scribble = NULL;
-    cmd->scsi_done(cmd);
-  }
-#endif
 }
 
 /*+F*************************************************************************
@@ -2873,6 +2871,7 @@
     {
       char *buffer;
       
+      p->dev_flags[tindex] |= DEVICE_PRESENT;
       if(cmd->use_sg)
       {
         struct scatterlist *sg;
@@ -2914,23 +2913,34 @@
         p->needsdtr_copy |= (1<<tindex);
 
         if (p->flags & AHC_SEEPROM_FOUND)
+        {
           p->transinfo[tindex].goal_period = p->transinfo[tindex].user_period;
-        else if (p->features & AHC_ULTRA2)
-          p->transinfo[tindex].goal_period =
-            aic7xxx_syncrates[AHC_SYNCRATE_ULTRA2].period;
-        else if (p->features & AHC_ULTRA)
-          p->transinfo[tindex].goal_period =
-            aic7xxx_syncrates[AHC_SYNCRATE_ULTRA].period;
-        else
-          p->transinfo[tindex].goal_period =
-            aic7xxx_syncrates[AHC_SYNCRATE_FAST].period;
-
-        if (p->features & AHC_ULTRA2)
-          p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
-        else if (p->transinfo[tindex].goal_width == MSG_EXT_WDTR_BUS_16_BIT)
-          p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
+          p->transinfo[tindex].goal_offset = p->transinfo[tindex].user_offset;
+        }
         else
-          p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
+        {
+          if (p->features & AHC_ULTRA2)
+          {
+            p->transinfo[tindex].goal_period =
+              aic7xxx_syncrates[AHC_SYNCRATE_ULTRA2].period;
+          }
+          else if (p->features & AHC_ULTRA)
+          {
+            p->transinfo[tindex].goal_period =
+              aic7xxx_syncrates[AHC_SYNCRATE_ULTRA].period;
+          }
+          else
+          {
+            p->transinfo[tindex].goal_period =
+              aic7xxx_syncrates[AHC_SYNCRATE_FAST].period;
+          }
+          if (p->features & AHC_ULTRA2)
+            p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
+          else if (p->transinfo[tindex].goal_width == MSG_EXT_WDTR_BUS_16_BIT)
+            p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
+          else
+            p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
+        }
       }
       else
       {
@@ -3076,12 +3086,7 @@
       int x;
 #endif /* AIC7XXX_PROC_STATS */
 
-      sp = &p->stats[TARGET_INDEX(cmd)][cmd->lun & 0x7];
-      sp->xfers++;
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-      if ( (sp->xfers > 16) && (aic7xxx_verbose > 0xffff) )
-        aic7xxx_verbose &= 0xffff;
-#endif
+      sp = &p->stats[TARGET_INDEX(cmd)];
 
       /*
        * For block devices, cmd->request.cmd is always == either READ or
@@ -3093,7 +3098,10 @@
            (cmd->data_cmnd[0] == WRITE_FILEMARKS) )
       {
         sp->w_total++;
-        sp->w_total512 += (actual >> 9);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+        if ( (sp->w_total > 16) && (aic7xxx_verbose > 0xffff) )
+          aic7xxx_verbose &= 0xffff;
+#endif
 #ifdef AIC7XXX_PROC_STATS
         ptr = sp->w_bins;
 #endif /* AIC7XXX_PROC_STATS */
@@ -3101,23 +3109,32 @@
       else
       {
         sp->r_total++;
-        sp->r_total512 += (actual >> 9);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+        if ( (sp->r_total > 16) && (aic7xxx_verbose > 0xffff) )
+          aic7xxx_verbose &= 0xffff;
+#endif
 #ifdef AIC7XXX_PROC_STATS
         ptr = sp->r_bins;
 #endif /* AIC7XXX_PROC_STATS */
       }
 #ifdef AIC7XXX_PROC_STATS
-      for (x = 9; x <= 17; x++)
+      x = -10;
+      while(actual)
       {
-        if (actual < (1 << x))
-        {
-          ptr[x - 9]++;
-          break;
-        }
+        actual >>= 1;
+        x++;
       }
-      if (x > 17)
+      if (x < 0)
       {
-        ptr[x - 9]++;
+        ptr[0]++;
+      }
+      else if (x > 7)
+      {
+        ptr[7]++;
+      }
+      else
+      {
+        ptr[x]++;
       }
 #endif /* AIC7XXX_PROC_STATS */
     }
@@ -3401,42 +3418,24 @@
 
     for (i = min_target; i <= max_target; i++)
     {
+      if ( i == p->scsi_id )
+      {
+        continue;
+      }
       if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
         printk(INFO_LEAD "Cleaning up status information "
           "and delayed_scbs.\n", p->host_no, channel, i, lun);
-      if ( !(p->dev_flags[i] & DEVICE_TAGGED_SUCCESS) &&
-            (p->dev_active_cmds[i]) &&
-            (p->tagenable & (0x01 << i)) )
-      {
-        printk(INFO_LEAD "Device appears to be choking on tagged commands.\n",
-          p->host_no, channel, i, lun);
-        printk(INFO_LEAD "Will use untagged I/O instead.\n", p->host_no,
-          channel, i, lun);
-        p->dev_max_queue_depth[i] = 1;
-        p->dev_temp_queue_depth[i] = 1;
-        p->tagenable &= ~(0x01 << i);
-        p->orderedtag &= ~(0x01 << i);
-      }
       p->dev_flags[i] &= ~BUS_DEVICE_RESET_PENDING;
       if ( tag == SCB_LIST_NULL )
       {
-        p->dev_flags[i] |= DEVICE_PRINT_WDTR | DEVICE_PRINT_SDTR;
-        p->dev_last_reset[i] = jiffies;
+        p->dev_flags[i] |= DEVICE_PRINT_WDTR | DEVICE_PRINT_SDTR |
+                           DEVICE_RESET_DELAY;
+        p->dev_expires[i] = jiffies + (4 * HZ);
+        p->dev_timer_active |= (0x01 << i);
         p->dev_last_queue_full_count[i] = 0;
         p->dev_last_queue_full[i] = 0;
         p->dev_temp_queue_depth[i] =
           p->dev_max_queue_depth[i];
-  /*
-   * In case this isn't a full bus reset, we want to add a 4 second timer in
-   * here so that we can delay all re-sent commands for this device for the
-   * 4 seconds and then have our timer routine pick them back up.
-   */
-        if(p->dev_timer[i].expires)
-        {
-          del_timer(&p->dev_timer[i]);
-        }
-        p->dev_timer[i].expires = jiffies + (4 * HZ);
-        add_timer(&p->dev_timer[i]);
       }
       for(j=0; j<MAX_LUNS; j++)
       {
@@ -3483,11 +3482,13 @@
             "delayed_scbs queue!\n", p->host_no, channel, i, lun);
         scbq_init(&p->delayed_scbs[i]);
       }
-      if ( (p->delayed_scbs[i].head == NULL) &&
-           (p->dev_timer[i].expires) )
+      if ( !(p->dev_timer_active & (0x01 << MAX_TARGETS)) ||
+            time_after_eq(p->dev_timer.expires, p->dev_expires[i]) )
       {
-        del_timer(&p->dev_timer[i]);
-        p->dev_timer[i].expires = 0;
+        del_timer(&p->dev_timer);
+        p->dev_timer.expires = p->dev_expires[i];
+        add_timer(&p->dev_timer);
+        p->dev_timer_active |= (0x01 << MAX_TARGETS);
       }
     }
   }
@@ -3620,7 +3621,7 @@
   if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
     printk(INFO_LEAD "Cleaning disconnected scbs "
       "list.\n", p->host_no, channel, target, lun);
-  if (p->features & AHC_PAGESCBS)
+  if (p->flags & AHC_PAGESCBS)
   {
     unsigned char next, prev, scb_index;
 
@@ -3672,7 +3673,7 @@
    * Walk the free list making sure no entries on the free list have
    * a valid SCB_TAG value or SCB_CONTROL byte.
    */
-  if (p->features & AHC_PAGESCBS)
+  if (p->flags & AHC_PAGESCBS)
   {
     unsigned char next;
 
@@ -3953,12 +3954,6 @@
    */
   aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);
 
-  /*
-   * Convince Mid Level SCSI code to leave us be for a little bit...
-   */
-  p->last_reset = jiffies;
-  p->host->last_reset = (jiffies + (HZ * AIC7XXX_RESET_DELAY));
-
   if ( !(p->features & AHC_TWIN) )
   {
     restart_sequencer(p);
@@ -4000,38 +3995,18 @@
     tindex = TARGET_INDEX(scb->cmd);
     if ( !scb->tag_action && (p->tagenable & (1<<tindex)) )
     {
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-      if (aic7xxx_verbose > 0xffff)
-        printk(INFO_LEAD "Reducing Queue depth for untagged command.\n",
-               p->host_no, CTL_OF_SCB(scb));
-#endif
       p->dev_temp_queue_depth[tindex] = 1;
     }
     if ( (p->dev_active_cmds[tindex] >=
           p->dev_temp_queue_depth[tindex]) ||
-         (p->dev_last_reset[tindex] >= (jiffies - (4 * HZ))) )
+         (p->dev_flags[tindex] & (DEVICE_RESET_DELAY|DEVICE_WAS_BUSY)) ||
+         (p->flags & AHC_RESET_DELAY) )
     {
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-      if (aic7xxx_verbose > 0xffff)
-        printk(INFO_LEAD "Moving SCB to Delayed Queue.\n",
-               p->host_no, CTL_OF_SCB(scb));
-#endif
       scbq_insert_tail(&p->delayed_scbs[tindex], scb);
-      if ( !(p->dev_timer[tindex].expires) &&
-           !(p->dev_active_cmds[tindex]) )
-      {
-        p->dev_timer[tindex].expires = p->dev_last_reset[tindex] + (4 * HZ);
-        add_timer(&p->dev_timer[tindex]);
-      }
     }
     else
     {
         scb->flags &= ~SCB_WAITINGQ;
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-        if (aic7xxx_verbose > 0xffff)
-          printk(INFO_LEAD "Sending command %d/0x%x to QINFIFO\n", p->host_no,
-                 CTL_OF_SCB(scb), scb->hscb->tag, scb->flags);
-#endif
         p->dev_active_cmds[tindex]++;
         p->activescbs++;
         if ( !(scb->tag_action) )
@@ -4044,16 +4019,6 @@
   }
   if (sent)
   {
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-    if (aic7xxx_verbose > 0xffff)
-    {
-      printk(INFO_LEAD "Sending commands to QINFIFO\n", p->host_no,
-             -1, -1, -1);
-      if ( (p->isr_count < 16) && (aic7xxx_panic_on_abort) &&
-           (p->flags & AHC_PAGESCBS) )
-        aic7xxx_check_scbs(p, "While sending commands to QINFIFO");
-    }
-#endif
     if (p->features & AHC_QUEUE_REGS)
       aic_outb(p, p->qinfifonext, HNSCB_QOFF);
     else
@@ -4154,17 +4119,21 @@
 #else
   spin_lock_irqsave(&io_request_lock, cpu_flags);
 #endif
+  p->dev_timer_active &= ~(0x01 << MAX_TARGETS);
+  if ( (p->dev_timer_active & (0x01 << p->scsi_id)) &&
+       time_after_eq(jiffies, p->dev_expires[p->scsi_id]) )
+  {
+    p->flags &= ~AHC_RESET_DELAY;
+    p->dev_timer_active &= ~(0x01 << p->scsi_id);
+  }
   for(i=0; i<MAX_TARGETS; i++)
   {
-    if ( (p->dev_timer[i].expires) && 
-          (p->dev_timer[i].expires <= jiffies) )
+    if ( (i != p->scsi_id) &&
+         (p->dev_timer_active & (0x01 << i)) &&
+         time_after_eq(jiffies, p->dev_expires[i]) )
     {
-      p->dev_timer[i].expires = 0;
-      if ( (p->dev_timer[i].prev != NULL) ||
-           (p->dev_timer[i].next != NULL) )
-      {
-        del_timer(&p->dev_timer[i]);
-      }
+      p->dev_timer_active &= ~(0x01 << i);
+      p->dev_flags[i] &= ~(DEVICE_RESET_DELAY|DEVICE_WAS_BUSY);
       p->dev_temp_queue_depth[i] =  p->dev_max_queue_depth[i];
       j = 0;
       while ( ((scb = scbq_remove_head(&p->delayed_scbs[i])) != NULL) &&
@@ -4184,7 +4153,27 @@
          */
       }
     }
+    else if ( p->dev_timer_active & (0x01 << i) )
+    {
+      if ( p->dev_timer_active & (0x01 << MAX_TARGETS) )
+      {
+        if ( time_after_eq(p->dev_timer.expires, p->dev_expires[i]) )
+        {
+          p->dev_timer.expires = p->dev_expires[i];
+        }
+      }
+      else
+      {
+        p->dev_timer.expires = p->dev_expires[i];
+        p->dev_timer_active |= (0x01 << MAX_TARGETS);
+      }
+    }
+  }
+  if ( p->dev_timer_active & (0x01 << MAX_TARGETS) )
+  {
+    add_timer(&p->dev_timer);
   }
+
   aic7xxx_run_waiting_queues(p);
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)   
   DRIVER_UNLOCK
@@ -4459,8 +4448,7 @@
              (scb->tag_action) &&
             !(scb->flags & SCB_MSGOUT_BITS) )
         {
-          if ((scb->tag_action == MSG_ORDERED_Q_TAG) &&
-              (p->dev_flags[tindex] & DEVICE_TAGGED_SUCCESS))
+          if (scb->tag_action == MSG_ORDERED_Q_TAG)
           {
             /*
              * OK...the device seems able to accept tagged commands, but
@@ -4481,8 +4469,7 @@
             aic_outb(p, MSG_IDENTIFYFLAG, MSG_OUT);
             aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
           }
-          else if ( (scb->tag_action == MSG_SIMPLE_Q_TAG) &&
-                   !(p->dev_flags[tindex] & DEVICE_TAGGED_SUCCESS) )
+          else if (scb->tag_action == MSG_SIMPLE_Q_TAG)
           {
             unsigned char i, reset = 0;
             struct aic7xxx_scb *scbp;
@@ -4897,17 +4884,29 @@
               p->activescbs--;
               scb->flags |= SCB_WAITINGQ | SCB_WAS_BUSY;
                   
-              if (p->dev_timer[tindex].expires == 0) 
+              if ( !(p->dev_timer_active & (0x01 << tindex)) ) 
               {
+                p->dev_timer_active |= (0x01 << tindex);
                 if ( p->dev_active_cmds[tindex] )
                 {
-                  p->dev_timer[tindex].expires = jiffies + (HZ * 2);
-                  add_timer(&p->dev_timer[tindex]);
+                  p->dev_expires[tindex] = jiffies + HZ;
                 }
                 else
                 {
-                  p->dev_timer[tindex].expires = jiffies + (HZ / 2);
-                  add_timer(&p->dev_timer[tindex]);
+                  p->dev_expires[tindex] = jiffies + (HZ / 10);
+                }
+                if ( !(p->dev_timer_active & (0x01 << MAX_TARGETS)) )
+                {
+                  p->dev_timer.expires = p->dev_expires[tindex];
+                  p->dev_timer_active |= (0x01 << MAX_TARGETS);
+                  add_timer(&p->dev_timer);
+                }
+                else if ( time_after_eq(p->dev_timer.expires,
+                                        p->dev_expires[tindex]) )
+                {
+                  del_timer(&p->dev_timer);
+                  p->dev_timer.expires = p->dev_expires[tindex];
+                  add_timer(&p->dev_timer);
                 }
               }
 #ifdef AIC7XXX_VERBOSE_DEBUGGING
@@ -4949,6 +4948,10 @@
                   p->dev_last_queue_full[tindex] = 0;
                   p->dev_last_queue_full_count[tindex] = 0;
                 }
+                else
+                {
+                  p->dev_flags[tindex] |= DEVICE_WAS_BUSY;
+                }
               }
               break;
             }
@@ -5267,50 +5270,61 @@
         {
           maxsync = AHC_SYNCRATE_FAST;
         }
-  
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-        if (aic7xxx_verbose > 0xffff)
-        {
-          printk(INFO_LEAD "Finished receipt of SDTR, parsing %d/%d\n",
-                 p->host_no, CTL_OF_SCB(scb), period, offset);
-          syncrate = aic7xxx_find_syncrate(p, &period, maxsync);
-          printk(INFO_LEAD "After find_syncrate() %d/%d\n",
-                 p->host_no, CTL_OF_SCB(scb), period, offset);
-          aic7xxx_validate_offset(p, syncrate, &offset,
-                                  target_scsirate & WIDEXFER);
-          printk(INFO_LEAD "After validate_offset() %d/%d\n",
-                 p->host_no, CTL_OF_SCB(scb), period, offset);
-          aic7xxx_set_syncrate(p, syncrate, target, channel, period,
-                               offset, AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
-          printk(INFO_LEAD "Final values of Period/Offset as set: %d/%d\n",
-                 p->host_no, CTL_OF_SCB(scb), period, offset);
-        }
-        else
+        /*
+         * We might have a device that is starting negotiation with us
+         * before we can start up negotiation with it....be prepared to
+         * have a device ask for a higher speed then we want to give it
+         * in that case
+         */
+        if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) !=
+             (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR) )
         {
-          syncrate = aic7xxx_find_syncrate(p, &period, maxsync);
-          aic7xxx_validate_offset(p, syncrate, &offset,
-                                  target_scsirate & WIDEXFER);
-          aic7xxx_set_syncrate(p, syncrate, target, channel, period,
-                               offset, AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
+          if (!(p->dev_flags[tindex] & DEVICE_SCANNED))
+          {
+            /*
+             * Not only is the device starting this up, but it also hasn't
+             * been scanned yet, so this would likely be our TUR or our
+             * INQUIRY command at scan time, so we need to use the
+             * settings from the SEEPROM if they existed.  Of course, even
+             * if we didn't find a SEEPROM, we stuffed default values into
+             * the user settings anyway, so use those in all cases.
+             */
+            p->transinfo[tindex].goal_period =
+              p->transinfo[tindex].user_period;
+            p->transinfo[tindex].goal_offset =
+              p->transinfo[tindex].user_offset;
+            p->needsdtr_copy |= target_mask;
+          }
+          if ( !p->transinfo[tindex].goal_offset )
+            period = 255;
+          if ( p->transinfo[tindex].goal_period > period )
+            period = p->transinfo[tindex].goal_period;
         }
-#else
+  
         syncrate = aic7xxx_find_syncrate(p, &period, maxsync);
         aic7xxx_validate_offset(p, syncrate, &offset,
                                 target_scsirate & WIDEXFER);
         aic7xxx_set_syncrate(p, syncrate, target, channel, period,
                              offset, AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
-#endif
 
-        if (offset == 0)
+        /*
+         * Did we drop to async?  If so, are we sending a reply?  If we are,
+         * then we have to make sure that the reply value reflects the proper
+         * settings so we need to set the goal values according to what
+         * we need to send.
+         */
+        if ( (offset == 0) || (offset != saved_offset) ||
+             ((scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) !=
+              (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR) ) )
         {
-          /*
-           * Uhh ohh, things fell through to async....update the goal
-           * items and the needsdtr_copy to reflect this...
-           */
           aic7xxx_set_syncrate(p, syncrate, target, channel, period,
-                 offset, AHC_TRANS_GOAL|AHC_TRANS_QUITE);
-          p->needsdtr_copy &= ~target_mask;
+                               offset, AHC_TRANS_GOAL|AHC_TRANS_QUITE);
+          if ( offset == 0 )
+          {
+            p->needsdtr_copy &= ~target_mask;
+          }
         }
+        
         /*
          * Did we start this, if not, or if we went to low and had to
          * go async, then send an SDTR back to the target
@@ -5325,32 +5339,6 @@
         }
         else
         {
-          /*
-           * Send a reply SDTR back.  Even if we sent the first one, it
-           * is valid to send another one out immediately to re-negotiate
-           * things, and a few devices don't like getting rejects after
-           * we already sent them one SDTR.  Just send an SDTR for async
-           * this time if need be (or for the correct params if we didn't
-           * start all of this).  If this is a Reject Reply type message,
-           * then we've put the async settings into the goal area for
-           * future reference (when we get the AWAITING_MSG interrupt).
-           * If this is a case where we are responding to the target's
-           * initiated SDTR, then leave our own goal and user values in
-           * place (unless the device hasn't been scanned yet, in which
-           * case, put the user values into the goal values so we don't
-           * send out an Async message).
-           */
-          if ( !(p->dev_flags[tindex] & DEVICE_SCANNED) )
-          {
-            p->transinfo[tindex].goal_width =
-              p->transinfo[tindex].user_width;
-            p->transinfo[tindex].goal_period =
-              p->transinfo[tindex].user_period;
-            p->transinfo[tindex].goal_offset =
-              p->transinfo[tindex].user_offset;
-            p->needwdtr_copy |= target_mask;
-            p->needsdtr_copy |= target_mask;
-          }
           scb->flags &= ~SCB_MSGOUT_BITS;
           scb->flags |= SCB_MSGOUT_SDTR;
           aic_outb(p, HOST_MSG, MSG_OUT);
@@ -5419,12 +5407,12 @@
              * it contacted us first, mark it as such and copy the user stuff
              * over to the goal stuff.
              */
-            p->transinfo[tindex].goal_width =
-              p->transinfo[tindex].user_width;
             p->transinfo[tindex].goal_period =
               p->transinfo[tindex].user_period;
             p->transinfo[tindex].goal_offset =
               p->transinfo[tindex].user_offset;
+            p->transinfo[tindex].goal_width =
+              p->transinfo[tindex].user_width;
             p->needwdtr_copy |= target_mask;
             p->needsdtr_copy |= target_mask;
           }
@@ -5444,6 +5432,8 @@
             {
               p->needwdtr_copy &= ~target_mask;
               bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+              aic7xxx_set_width(p, target, channel, lun, bus_width,
+                                AHC_TRANS_GOAL|AHC_TRANS_QUITE);
               break;
             }
           }
@@ -5820,23 +5810,19 @@
        * Put this SCB back on the free list.
        */
       aic7xxx_add_curscb_to_free_list(p);
-      /*
-       * XXX - If we queued an abort tag, go clean up the disconnected list.
-       * We know that this particular SCB had to be the queued abort since
-       * the disconnected SCB would have gotten a reconnect instead.
-       * However, if this is an abort command, then DID_TIMEOUT isn't
-       * appropriate, neither is returning the command for that matter.
-       * What we need to do then is to let the command timeout again so
-       * we get a reset since this abort just failed.
-       */
 #ifdef AIC7XXX_VERBOSE_DEBUGGING
       if (aic7xxx_verbose > 0xffff)
         printk(INFO_LEAD "Selection Timeout.\n", p->host_no, CTL_OF_SCB(scb));
 #endif
-      if (p->flags & SCB_QUEUED_ABORT)
+      if (scb->flags & SCB_QUEUED_ABORT)
       {
+        /*
+         * We know that this particular SCB had to be the queued abort since
+         * the disconnected SCB would have gotten a reconnect instead.
+         * What we need to do then is to let the command timeout again so
+         * we get a reset since this abort just failed.
+         */
         cmd->result = 0;
-        scb->flags &= ~SCB_QUEUED_ABORT;
         scb = NULL;
       }
     }
@@ -6253,12 +6239,6 @@
             aic7xxx_calculate_residual(p, scb);
           }
           cmd->result |= (aic7xxx_error(cmd) << 16);
-          if (scb->tag_action)
-            p->dev_flags[TARGET_INDEX(cmd)] |= 
-               DEVICE_TAGGED_SUCCESS | DEVICE_SUCCESS | DEVICE_PRESENT;
-          else
-            p->dev_flags[TARGET_INDEX(cmd)] |= 
-               DEVICE_SUCCESS | DEVICE_PRESENT;
           aic7xxx_done(p, scb);
           break;
       }      
@@ -6384,7 +6364,7 @@
  *   Determines the queue depth for a given device.  There are two ways
  *   a queue depth can be obtained for a tagged queueing device.  One
  *   way is the default queue depth which is determined by whether
- *   AIC7XXX_CMDS_PER_LUN is defined.  If it is defined, then it is used
+ *   AIC7XXX_CMDS_PER_DEVICE is defined.  If it is defined, then it is used
  *   as the default queue depth.  Otherwise, we use either 4 or 8 as the
  *   default queue depth (dependent on the number of hardware SCBs).
  *   The other way we determine queue depth is through the use of the
@@ -6404,7 +6384,6 @@
   target_mask = (1 << tindex);
 
   device->queue_depth = default_depth;
-  p->dev_mid_level_queue_depth[tindex] = 3;
   p->dev_temp_queue_depth[tindex] = 1;
   p->dev_max_queue_depth[tindex] = 1;
   p->tagenable &= ~target_mask;
@@ -6413,11 +6392,7 @@
   {
     int tag_enabled = TRUE;
 
-#ifdef AIC7XXX_CMDS_PER_LUN
-    default_depth = AIC7XXX_CMDS_PER_LUN;
-#else
-    default_depth = 8;  /* Not many SCBs to work with. */
-#endif
+    default_depth = AIC7XXX_CMDS_PER_DEVICE;
  
     if (!(p->discenable & target_mask))
     {
@@ -6469,7 +6444,6 @@
         }
         p->dev_max_queue_depth[tindex] = device->queue_depth;
         p->dev_temp_queue_depth[tindex] = device->queue_depth;
-        p->dev_mid_level_queue_depth[tindex] = device->queue_depth;
         p->tagenable |= target_mask;
         p->orderedtag |= target_mask;
         device->tagged_queue = 1;
@@ -6980,7 +6954,7 @@
   }
   printk("\n");
 #endif
-  if (checksum != scarray[len - 1])
+  if ( (checksum != scarray[len - 1]) || (checksum == 0) )
   {
     return (0);
   }
@@ -7464,7 +7438,6 @@
   }
 
   p->host = host;
-  p->last_reset = 0;
   p->host_no = host->host_no;
   host->unique_id = p->instance;
   p->isr_count = 0;
@@ -7473,6 +7446,10 @@
   p->completeq.tail = NULL;
   scbq_init(&p->scb_data->free_scbs);
   scbq_init(&p->waiting_scbs);
+  init_timer(&p->dev_timer);
+  p->dev_timer.data = (unsigned long)p;
+  p->dev_timer.function = (void *)aic7xxx_timer;
+  p->dev_timer_active = 0;
 
   for (i = 0; i < NUMBER(p->untagged_scbs); i++)
   {
@@ -7491,17 +7468,12 @@
     p->dev_commands_sent[i] = 0;
     p->dev_flags[i] = 0;
     p->dev_active_cmds[i] = 0;
-    p->dev_last_reset[i] = 0;
     p->dev_last_queue_full[i] = 0;
     p->dev_last_queue_full_count[i] = 0;
     p->dev_max_queue_depth[i] = 1;
     p->dev_temp_queue_depth[i] = 1;
-    p->dev_mid_level_queue_depth[i] = 3;
+    p->dev_expires[i] = 0;
     scbq_init(&p->delayed_scbs[i]);
-    init_timer(&p->dev_timer[i]);
-    p->dev_timer[i].expires = 0;
-    p->dev_timer[i].data = (unsigned long)p;
-    p->dev_timer[i].function = (void *)aic7xxx_timer;
   }
 
   printk(KERN_INFO "(scsi%d) <%s> found at ", p->host_no,
@@ -7551,10 +7523,9 @@
   }
   aic_outb(p, 0, SEQ_FLAGS);
 
-  /*
-   * Detect SCB parameters and initialize the SCB array.
-   */
   detect_maxscb(p);
+
+
   printk("%d/%d SCBs\n", p->scb_data->maxhscbs, p->scb_data->maxscbs);
   if (aic7xxx_verbose & VERBOSE_PROBE2)
   {
@@ -7673,7 +7644,7 @@
     aic_outb(p, p->scsi_id_b, SCSIID);
     scsi_conf = aic_inb(p, SCSICONF + 1);
     aic_outb(p, DFON | SPIOEN, SXFRCTL0);
-    aic_outb(p, (scsi_conf & ENSPCHK) | term | 
+    aic_outb(p, (scsi_conf & ENSPCHK) | STIMESEL | term | 
          ENSTIMER | ACTNEGEN, SXFRCTL1);
     aic_outb(p, 0, SIMODE0);
     aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1);
@@ -7690,7 +7661,7 @@
     aic_outb(p, p->scsi_id, SCSIID);
   scsi_conf = aic_inb(p, SCSICONF);
   aic_outb(p, DFON | SPIOEN, SXFRCTL0);
-  aic_outb(p, (scsi_conf & ENSPCHK) | term | 
+  aic_outb(p, (scsi_conf & ENSPCHK) | STIMESEL | term | 
        ENSTIMER | ACTNEGEN, SXFRCTL1);
   aic_outb(p, 0, SIMODE0);
   aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1);
@@ -7865,6 +7836,11 @@
    */
   aic7xxx_loadseq(p);
 
+  /*
+   * Make sure the AUTOFLUSHDIS bit is *not* set in the SBLKCTL register
+   */
+  aic_outb(p, aic_inb(p, SBLKCTL) & ~AUTOFLUSHDIS, SBLKCTL);
+
   if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
   {
     aic_outb(p, ENABLE, BCTL);  /* Enable the boards BUS drivers. */
@@ -8151,6 +8127,8 @@
         p->flags |= AHC_TERM_ENB_A;
       if ( (p->features & AHC_TWIN) && (aic_inb(p, SCSICONF + 1) & TERM_ENB) )
         p->flags |= AHC_TERM_ENB_B;
+      aic_outb(p, 0, DISC_DSB);
+      aic_outb(p, 0, DISC_DSB + 1);
       break;
 
     case (AHC_AIC7770|AHC_VL):
@@ -8169,6 +8147,21 @@
           have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
                                       scarray, p->sc_size, C46);
       }
+      if (!have_seeprom)
+      {
+        p->sc_size = 128;
+        have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
+                                    scarray, p->sc_size, p->sc_type);
+        if (!have_seeprom)
+        {
+          if(p->sc_type == C46)
+            have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
+                                        scarray, p->sc_size, C56_66);
+          else
+            have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
+                                        scarray, p->sc_size, C46);
+        }
+      }
       break;
   }
 
@@ -8194,13 +8187,16 @@
     {
       printk("aic7xxx: Using leftover BIOS values.\n");
     }
-    if ( *sxfrctl1 & STPWEN )
+    if ( ((p->chip & ~AHC_CHIPID_MASK) == AHC_PCI) && (*sxfrctl1 & STPWEN) )
     {
       p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
       sc->adapter_control &= ~CFAUTOTERM;
       sc->adapter_control |= CFSTERM | CFWSTERM | CFLVDSTERM;
     }
-    p->flags |= AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B;
+    if (aic7xxx_extended)
+      p->flags |= (AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B);
+    else
+      p->flags &= ~(AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B);
   }
   else
   {
@@ -8288,7 +8284,7 @@
     mask = (0x01 << i);
     if (!have_seeprom)
     {
-      if(aic_inb(p, SCSISEQ) != 0)
+      if (aic_inb(p, SCSISEQ) != 0)
       {
         /*
          * OK...the BIOS set things up and left behind the settings we need.
@@ -8348,19 +8344,30 @@
     }
     if (p->flags & AHC_NEWEEPROM_FMT)
     {
-      if (sc->device_flags[i] & CFSYNCHISULTRA)
-      {
-        p->ultraenb |= mask;
-      }
-      else if (sc->device_flags[i] & CFNEWULTRAFORMAT)
+      if ( (sc->device_flags[i] & CFNEWULTRAFORMAT) &&
+          !(p->features & AHC_ULTRA2) )
       {
-        if ( (sc->device_flags[i] & (CFSYNCHISULTRA | CFXFER)) == 0x03 )
+        /*
+         * I know of two different Ultra BIOSes that do this differently.
+         * One on the Gigabyte 6BXU mb that wants flags[i] & CFXFER to
+         * be == to 0x03 and SYNCISULTRA to be true to mean 40MByte/s
+         * while on the IBM Netfinity 5000 they want the same thing
+         * to be something else, while flags[i] & CFXFER == 0x03 and
+         * SYNCISULTRA false should be 40MByte/s.  So, we set both to
+         * 40MByte/s and the lower speeds be damned.  People will have
+         * to select around the conversely mapped lower speeds in order
+         * to select lower speeds on these boards.
+         */
+        if ((sc->device_flags[i] & (CFXFER)) == 0x03)
         {
           sc->device_flags[i] &= ~CFXFER;
           sc->device_flags[i] |= CFSYNCHISULTRA;
-          p->ultraenb |= mask;
         }
       }
+      if (sc->device_flags[i] & CFSYNCHISULTRA)
+      {
+        p->ultraenb |= mask;
+      }
     }
     else if (sc->adapter_control & CFULTRAEN)
     {
@@ -8635,12 +8642,6 @@
         current_p = current_p->next;
       current_p->next = temp_p;
     }
-    if (aic7xxx_extended)
-    {
-      temp_p->flags |= AHC_EXTEND_TRANS_A;
-      if (temp_p->flags & AHC_MULTI_CHANNEL)
-        temp_p->flags |= AHC_EXTEND_TRANS_B;
-    }
 
     switch (type)
     {
@@ -8797,6 +8798,14 @@
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AHC_AIC7850,
        AHC_PAGESCBS, AHC_AIC7850_FE,                         6,
        32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7821, AHC_AIC7860,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7860_FE,                                       7,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_3860, AHC_AIC7860,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7860_FE,                                       7,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AHC_AIC7860,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
        AHC_AIC7860_FE,                                       7,
@@ -8839,6 +8848,18 @@
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AHC_AIC7880,
        AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
        32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7885, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7886, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7887, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7888, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7895, AHC_AIC7895,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
        AHC_AIC7895_FE,                                      19,
@@ -8847,25 +8868,70 @@
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
        AHC_AIC7890_FE,                                      20,
        32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2940U2, AHC_AIC7890,
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7890B, AHC_AIC7890,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7890_FE,                                      20,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2930U2, AHC_AIC7890,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
        AHC_AIC7890_FE,                                      21,
        32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2940U2, AHC_AIC7890,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7890_FE,                                      22,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7896, AHC_AIC7896,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
-       AHC_AIC7896_FE,                                      22,
+       AHC_AIC7896_FE,                                      23,
        32, C56_66 },
       {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3940U2, AHC_AIC7896,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
-       AHC_AIC7896_FE,                                      23,
+       AHC_AIC7896_FE,                                      24,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3950U2D, AHC_AIC7896,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7896_FE,                                      25,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_1480A, AHC_AIC7860,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7860_FE,                                      26,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892A, AHC_AIC7892,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7892_FE,                                      27,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892B, AHC_AIC7892,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7892_FE,                                      27,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892D, AHC_AIC7892,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7892_FE,                                      27,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892P, AHC_AIC7892,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7892_FE,                                      27,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899A, AHC_AIC7899,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7899_FE,                                      28,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899B, AHC_AIC7899,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7899_FE,                                      28,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899D, AHC_AIC7899,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7899_FE,                                      28,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899P, AHC_AIC7899,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7899_FE,                                      28,
        32, C56_66 },
     };
 
     unsigned short command;
     unsigned int  devconfig, i, oldverbose;
-#ifdef MMAPIO
-    unsigned long page_offset, base;
-#endif
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
     struct pci_dev *pdev = NULL;
 #else
@@ -9016,21 +9082,68 @@
           temp_p->mbase &= PCI_BASE_ADDRESS_MEM_MASK;
           temp_p->unpause = INTEN;
           temp_p->pause = temp_p->unpause | PAUSE;
+          if ( ((temp_p->base == 0) &&
+                (temp_p->mbase == 0)) ||
+               (temp_p->irq == 0) )
+          {
+            printk("aic7xxx: <%s> at PCI %d/%d\n", 
+              board_names[aic_pdevs[i].board_name_index],
+              PCI_SLOT(temp_p->pci_device_fn),
+              PCI_FUNC(temp_p->pci_device_fn));
+            printk("aic7xxx: Controller disabled by BIOS, ignoring.\n");
+            kfree(temp_p);
+            temp_p = NULL;
+            continue;
+          }
 
 #ifdef MMAPIO
-          base = temp_p->mbase & PAGE_MASK;
-          page_offset = temp_p->mbase - base;
+          {
+            unsigned long page_offset, base;
+
+            base = temp_p->mbase & PAGE_MASK;
+            page_offset = temp_p->mbase - base;
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
-          temp_p->maddr = ioremap_nocache(base, page_offset + 256);
+            temp_p->maddr = ioremap_nocache(base, page_offset + 256);
 #else
-          temp_p->maddr = vremap(base, page_offset + 256);
+            temp_p->maddr = vremap(base, page_offset + 256);
 #endif
-          if(temp_p->maddr)
-          {
-            temp_p->maddr += page_offset;
+            if(temp_p->maddr)
+            {
+              temp_p->maddr += page_offset;
+              /*
+               * We need to check the I/O with the MMAPed address.  Some machines
+               * simply fail to work with MMAPed I/O and certain controllers.
+               */
+              if(aic_inb(temp_p, HCNTRL) == 0xff)
+              {
+                /*
+                 * OK.....we failed our test....go back to programmed I/O
+                 */
+                printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d\n", 
+                  board_names[aic_pdevs[i].board_name_index],
+                  PCI_SLOT(temp_p->pci_device_fn),
+                  PCI_FUNC(temp_p->pci_device_fn));
+                printk(KERN_INFO "aic7xxx: MMAPed I/O failed, reverting to "
+                                 "Programmed I/O.\n");
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)
+                iounmap((void *) (((unsigned long) temp_p->maddr) & PAGE_MASK));
+#else
+                vfree((void *) (((unsigned long) temp_p->maddr) & PAGE_MASK));
+#endif
+                temp_p->maddr = 0;
+              }
+            }
           }
 #endif
 
+          /*
+           * We HAVE to make sure the first pause_sequencer() and all other
+           * subsequent I/O that isn't PCI config space I/O takes place
+           * after the MMAPed I/O region is configured and tested.  The
+           * problem is the PowerPC architecture that doesn't support
+           * programmed I/O at all, so we have to have the MMAP I/O set up
+           * for this pause to even work on those machines.
+           */
           pause_sequencer(temp_p);
 
           /*
@@ -9065,44 +9178,36 @@
           }
 
           /*
-           * Doing a switch based upon i is really gross, but since Justin
-           * changed around the chip ID stuff, we can't use that any more.
-           * Since we don't scan the devices the same way as FreeBSD, we end
-           * up doing this gross hack in order to avoid totally splitting
-           * away from Justin's init code in ahc_pci.c
+           * We need to set the CHNL? assignments before loading the SEEPROM
+           * The 3940 and 3985 cards (original stuff, not any of the later
+           * stuff) are 7870 and 7880 class chips.  The Ultra2 stuff falls
+           * under 7896 and 7897.  The 7895 is in a class by itself :)
            */
-          switch (i)
+          switch (temp_p->chip & AHC_CHIPID_MASK)
           {
-            case 7:   /* 3940 */
-            case 12:  /* 3940-Ultra */
-              switch(PCI_SLOT(temp_p->pci_device_fn))
+            case AHC_AIC7870: /* 3840 / 3985 */
+            case AHC_AIC7880: /* 3840 UW / 3985 UW */
+              if(temp_p->flags & AHC_MULTI_CHANNEL)
               {
-                case 5:
-                  temp_p->flags |= AHC_CHNLB;
-                  break;
-                default:
-                  break;
-              }
-              break;
-
-            case 8:   /* 3985 */
-            case 13:  /* 3985-Ultra */
-              switch(PCI_SLOT(temp_p->pci_device_fn))
-              {
-                case 8:
-                  temp_p->flags |= AHC_CHNLB;
-                  break;
-                case 12:
-                  temp_p->flags |= AHC_CHNLC;
-                  break;
-                default:
-                  break;
+                switch(PCI_SLOT(temp_p->pci_device_fn))
+                {
+                  case 5:
+                    temp_p->flags |= AHC_CHNLB;
+                    break;
+                  case 8:
+                    temp_p->flags |= AHC_CHNLB;
+                    break;
+                  case 12:
+                    temp_p->flags |= AHC_CHNLC;
+                    break;
+                  default:
+                    break;
+                }
               }
               break;
 
-            case 15:
-            case 18:
-            case 19:
+            case AHC_AIC7895: /* 7895 */
+            case AHC_AIC7896: /* 7896/7 */
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
               if (PCI_FUNC(temp_p->pdev->devfn) != 0)
               {
@@ -9178,16 +9283,6 @@
               aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
                                 CACHETHEN | MPARCKEN | USCBSIZE32 |
                                 CIOPARCKEN) & ~DPARCKEN, DSCOMMAND0);
-              /* FALLTHROUGH */
-            default:
-              /*
-               * We attempt to read a SEEPROM on *everything*.  If we fail,
-               * then we fail, but this covers things like 2910c cards that
-               * now have SEEPROMs with their 7856 chipset that we would
-               * otherwise ignore.  They still don't have a BIOS, but they
-               * have a SEEPROM that the SCSISelect utility on the Adaptec
-               * diskettes can configure.
-               */
               aic7xxx_load_seeprom(temp_p, &sxfrctl1);
               break;
             case AHC_AIC7850:
@@ -9199,14 +9294,13 @@
               aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
                                 CACHETHEN | MPARCKEN) & ~DPARCKEN,
                        DSCOMMAND0);
+              /* FALLTHROUGH */
+            default:
               aic7xxx_load_seeprom(temp_p, &sxfrctl1);
               break;
             case AHC_AIC7880:
               /*
-               * Only set the DSCOMMAND0 register if this is a Rev B.
-               * chipset.  For those, we also enable Ultra mode by
-               * force due to brain-damage on the part of some BIOSes
-               * We overload the devconfig variable here since we can.
+               * Check the rev of the chipset before we change DSCOMMAND0
                */
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
               pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
@@ -9270,13 +9364,10 @@
           }
 
           /*
-           * We do another switch based on i so that we can exclude all
-           * 3895 devices from the next option since the 3895 cards use
-           * shared external SCB RAM while all other cards have dedicated
-           * external SCB RAM per channel.  Also exclude the 7850 and
-           * 7860 based stuff since they can have garbage in the bit
-           * that indicates external RAM and get some of this stuff
-           * wrong as a result.
+           * We only support external SCB RAM on the 7895/6/7 chipsets.
+           * We could support it on the 7890/1 easy enough, but I don't
+           * know of any 7890/1 based cards that have it.  I do know
+           * of 7895/6/7 cards that have it and they work properly.
            */
           switch(temp_p->chip & AHC_CHIPID_MASK)
           {
@@ -9340,9 +9431,6 @@
             aic_outb(temp_p, DFTHRSH_100, DSPCISTATUS);
           }
 
-          if (aic7xxx_extended)
-            temp_p->flags |= AHC_EXTEND_TRANS_A;
-
           if ( list_p == NULL )
           {
             list_p = current_p = temp_p;
@@ -10109,8 +10197,6 @@
                 "message buffer\n", p->host_no, CTL_OF_SCB(scb));
         scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
         aic7xxx_error(scb->cmd) = DID_RESET;
-        p->dev_flags[TARGET_INDEX(scb->cmd)] &= 
-                ~DEVICE_SUCCESS;
         p->dev_flags[TARGET_INDEX(scb->cmd)] |= 
                 BUS_DEVICE_RESET_PENDING;
         /* Send the abort message to the active SCB. */
@@ -10131,8 +10217,6 @@
                 "in use\n", p->host_no, CTL_OF_SCB(scb));
         scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
         aic7xxx_error(scb->cmd) = DID_RESET;
-        p->dev_flags[TARGET_INDEX(scb->cmd)] &= 
-                ~DEVICE_SUCCESS;
         p->dev_flags[TARGET_INDEX(scb->cmd)] |= 
                 BUS_DEVICE_RESET_PENDING;
         return(SCSI_RESET_ERROR);
@@ -10160,7 +10244,6 @@
          */
     scb->hscb->control |= MK_MESSAGE;
     scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
-    p->dev_flags[TARGET_INDEX(scb->cmd)] &= ~DEVICE_SUCCESS;
     p->dev_flags[TARGET_INDEX(scb->cmd)] |= 
         BUS_DEVICE_RESET_PENDING;
     if (hscb_index != SCB_LIST_NULL)
@@ -10240,7 +10323,7 @@
     {
       mask = (0x01 << i);
       printk(INFO_LEAD "dev_flags=0x%x, WDTR:%c/%c/%c, SDTR:%c/%c/%c,"
-             " q_depth=%d:%d:%d\n",
+             " q_depth=%d:%d\n",
         p->host_no, 0, i, 0, p->dev_flags[i],
         (p->wdtr_pending & mask) ? 'Y' : 'N',
         (p->needwdtr & mask) ? 'Y' : 'N',
@@ -10249,7 +10332,7 @@
         (p->needsdtr & mask) ? 'Y' : 'N',
         (p->needsdtr_copy & mask) ? 'Y' : 'N',
         p->dev_active_cmds[i],
-        p->dev_max_queue_depth[i], p->dev_mid_level_queue_depth[i]);
+        p->dev_max_queue_depth[i] );
       printk(INFO_LEAD "targ_scsirate=0x%x", p->host_no, 0, i, 0,
              aic_inb(p, TARG_SCSIRATE + i));
       if (p->features & AHC_ULTRA2)
@@ -10890,7 +10973,7 @@
   {
     action = HOST_RESET;
   }
-  if ( ((jiffies - p->dev_last_reset[tindex]) < (HZ * 3)) &&
+  if ( (p->dev_flags[tindex] & DEVICE_RESET_DELAY) &&
        !(action & (HOST_RESET | BUS_RESET)))
   {
     if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
@@ -10902,23 +10985,14 @@
     }
     action = BUS_RESET;
   }
-  if ( ((jiffies - p->last_reset) < (HZ * 3)) &&
-        (action & (HOST_RESET | BUS_RESET)) )
+  if ( (p->flags & AHC_RESET_DELAY) &&
+       (action & (HOST_RESET | BUS_RESET)) )
   {
     if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
       printk(INFO_LEAD "Reset called too soon after "
         "last bus reset, delaying.\n", p->host_no, CTL_OF_CMD(cmd));
     action = RESET_DELAY;
   }
-  if ( (action & (BUS_RESET | HOST_RESET)) && (p->flags & AHC_IN_RESET)
-        && ((jiffies - p->reset_start) > (2 * HZ * 3)) )
-  {
-    printk(KERN_ERR "(scsi%d:%d:%d:%d) Yikes!!  Card must have left to go "
-        "back to Adaptec!!\n", p->host_no, CTL_OF_CMD(cmd));
-    unpause_sequencer(p, FALSE);
-    DRIVER_UNLOCK
-    return(SCSI_RESET_SNOOZE);
-  }
 /*
  *  By this point, we want to already know what we are going to do and
  *  only have the following code implement our course of action.
@@ -10950,15 +11024,23 @@
     case BUS_RESET:
     case HOST_RESET:
     default:
-      p->reset_start = jiffies;
-      p->flags |= AHC_IN_RESET;
+      p->flags |= AHC_IN_RESET | AHC_RESET_DELAY;
+      p->dev_expires[p->scsi_id] = jiffies + (3 * HZ);
+      p->dev_timer_active |= (0x01 << p->scsi_id);
+      if ( !(p->dev_timer_active & (0x01 << MAX_TARGETS)) ||
+            time_after_eq(p->dev_timer.expires, p->dev_expires[p->scsi_id]) )
+      {
+        del_timer(&p->dev_timer);
+        p->dev_timer.expires = p->dev_expires[p->scsi_id];
+        add_timer(&p->dev_timer);
+        p->dev_timer_active |= (0x01 << MAX_TARGETS);
+      }
       aic7xxx_reset_channel(p, cmd->channel, TRUE);
       if ( (p->features & AHC_TWIN) && (action & HOST_RESET) )
       {
         aic7xxx_reset_channel(p, cmd->channel ^ 0x01, TRUE);
         restart_sequencer(p);
       }
-      p->last_reset = jiffies;
       if (action != HOST_RESET)
         result = SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
       else
@@ -10973,9 +11055,27 @@
         p->msg_len = 0;
       }
       aic7xxx_run_done_queue(p, TRUE);
+      /*
+       * If this a SCSI_RESET_SYNCHRONOUS then the command we were given is
+       * in need of being re-started, so send it on through to aic7xxx_queue
+       * and let it set until the delay is over.  This keeps it from dying
+       * entirely and avoids getting a bogus dead command back through the
+       * mid-level code due to too many retries.
+       */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,132)
+      if ( flags & SCSI_RESET_SYNCHRONOUS )
+      {
+        cmd->result = DID_BUS_BUSY << 16;
+        cmd->done(cmd);
+      }
+#endif
       p->flags &= ~AHC_IN_RESET;
-      /*  We can't rely on run_waiting_queues to unpause the sequencer for
-       *  PCI based controllers since we use AAP */
+      /*
+       * We can't rely on run_waiting_queues to unpause the sequencer for
+       * PCI based controllers since we use AAP.  NOTE: this also sets
+       * the timer for the one command we might have queued in the case
+       * of a synch reset.
+       */
       aic7xxx_run_waiting_queues(p);
       unpause_sequencer(p, FALSE);
       DRIVER_UNLOCK
@@ -10994,16 +11094,21 @@
 int
 aic7xxx_biosparam(Disk *disk, kdev_t dev, int geom[])
 {
-  int heads, sectors, cylinders;
+  int heads, sectors, cylinders, ret;
   struct aic7xxx_host *p;
+  struct buffer_head *bh;
 
   p = (struct aic7xxx_host *) disk->device->host->hostdata;
+  bh = bread(MKDEV(MAJOR(dev), MINOR(dev)&~0xf), 0, 1024);
 
-  /*
-   * XXX - if I could portably find the card's configuration
-   *       information, then this could be autodetected instead
-   *       of left to a boot-time switch.
-   */
+  if ( bh )
+  {
+    ret = scsi_partsize(bh, disk->capacity, &geom[2], &geom[0], &geom[1]);
+    brelse(bh);
+    if ( ret != -1 )
+      return(ret);
+  }
+  
   heads = 64;
   sectors = 32;
   cylinders = disk->capacity / (heads * sectors);
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/aic7xxx_proc.c linux/drivers/scsi/aic7xxx_proc.c
--- linux.vanilla/drivers/scsi/aic7xxx_proc.c	Sun Dec  6 00:14:43 1998
+++ linux/drivers/scsi/aic7xxx_proc.c	Fri Mar 26 17:03:11 1999
@@ -31,7 +31,7 @@
 
 #define	BLS	(&aic7xxx_buffer[size])
 #define HDRB \
-"        < 512 512-1K   1-2K   2-4K   4-8K  8-16K 16-32K 32-64K 64-128K >128K"
+"             < 2K      2K+     4K+     8K+    16K+    32K+    64K+   128K+"
 
 #ifdef PROC_DEBUG
 extern int vsprintf(char *, const char *, va_list);
@@ -86,7 +86,7 @@
   int    size = 0;
   unsigned char i;
   struct aic7xxx_xferstats *sp;
-  unsigned char target, lun;
+  unsigned char target;
 
   HBAptr = NULL;
 
@@ -130,15 +130,12 @@
   size = 4096;
   for (target = 0; target < MAX_TARGETS; target++)
   {
-    for (lun = 0; lun < MAX_LUNS; lun++)
-    {
-      if (p->stats[target][lun].xfers != 0)
+    if (p->dev_flags[target] & DEVICE_PRESENT)
 #ifdef AIC7XXX_PROC_STATS
-        size += 512;
+      size += 512;
 #else
-        size += 256;
+      size += 256;
 #endif
-    }
   }
   if (aic7xxx_buffer_size != size)
   {
@@ -163,21 +160,17 @@
   size += sprintf(BLS, "%s", AIC7XXX_H_VERSION);
   size += sprintf(BLS, "\n");
   size += sprintf(BLS, "Compile Options:\n");
-#ifdef AIC7XXX_RESET_DELAY
-  size += sprintf(BLS, "  AIC7XXX_RESET_DELAY    : %d\n", AIC7XXX_RESET_DELAY);
+#ifdef CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT
+  size += sprintf(BLS, "  TCQ Enabled By Default : Enabled\n");
+#else
+  size += sprintf(BLS, "  TCQ Enabled By Default : Disabled\n");
 #endif
-  size += sprintf(BLS, "  AIC7XXX_TAGGED_QUEUEING: Adapter Support Enabled\n");
-  size += sprintf(BLS, "                             Check below to see "
-                       "which\n"
-                       "                             devices use tagged "
-                       "queueing\n");
-  size += sprintf(BLS, "  AIC7XXX_PAGE_ENABLE    : Enabled (This is no longer "
-                       "an option)\n");
 #ifdef AIC7XXX_PROC_STATS
   size += sprintf(BLS, "  AIC7XXX_PROC_STATS     : Enabled\n");
 #else
   size += sprintf(BLS, "  AIC7XXX_PROC_STATS     : Disabled\n");
 #endif
+  size += sprintf(BLS, "  AIC7XXX_RESET_DELAY    : %d\n", AIC7XXX_RESET_DELAY);
   size += sprintf(BLS, "\n");
   size += sprintf(BLS, "Adapter Configuration:\n");
   size += sprintf(BLS, "           SCSI Adapter: %s\n",
@@ -253,11 +246,7 @@
   }
   size += sprintf(BLS, " Tag Queue Enable Flags: 0x%04x\n", p->tagenable);
   size += sprintf(BLS, "Ordered Queue Tag Flags: 0x%04x\n", p->orderedtag);
-#ifdef AIC7XXX_CMDS_PER_LUN
-  size += sprintf(BLS, "Default Tag Queue Depth: %d\n", AIC7XXX_CMDS_PER_LUN);
-#else
-  size += sprintf(BLS, "Default Tag Queue Depth: %d\n", 8);
-#endif
+  size += sprintf(BLS, "Default Tag Queue Depth: %d\n", AIC7XXX_CMDS_PER_DEVICE);
   size += sprintf(BLS, "    Tagged Queue By Device array for aic7xxx host "
                        "instance %d:\n", p->instance);
   size += sprintf(BLS, "      {");
@@ -272,85 +261,83 @@
   size += sprintf(BLS, "%d}\n", p->dev_max_queue_depth[i]);
 
   size += sprintf(BLS, "\n");
-  size += sprintf(BLS, "Statistics:\n");
+  size += sprintf(BLS, "Statistics:\n\n");
   for (target = 0; target < MAX_TARGETS; target++)
   {
-    for (lun = 0; lun < MAX_LUNS; lun++)
+    sp = &p->stats[target];
+    if ((p->dev_flags[target] & DEVICE_PRESENT) == 0)
     {
-      sp = &p->stats[target][lun];
-      if (sp->xfers == 0)
-      {
-        continue;
-      }
-      if (p->features & AHC_TWIN)
+      continue;
+    }
+    if (p->features & AHC_TWIN)
+    {
+      size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
+          p->host_no, (target >> 3), (target & 0x7), 0);
+    }
+    else
+    {
+      size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
+          p->host_no, 0, target, 0);
+    }
+    size += sprintf(BLS, "  Device using %s/%s",
+          (p->transinfo[target].cur_width == MSG_EXT_WDTR_BUS_16_BIT) ?
+          "Wide" : "Narrow",
+          (p->transinfo[target].cur_offset != 0) ?
+          "Sync transfers at " : "Async transfers.\n" );
+    if (p->transinfo[target].cur_offset != 0)
+    {
+      struct aic7xxx_syncrate *sync_rate;
+      int period = p->transinfo[target].cur_period;
+      int rate = (p->transinfo[target].cur_width ==
+                  MSG_EXT_WDTR_BUS_16_BIT) ? 1 : 0;
+
+      sync_rate = aic7xxx_find_syncrate(p, &period, AHC_SYNCRATE_ULTRA2);
+      if (sync_rate != NULL)
       {
-        size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
-            p->host_no, (target >> 3), (target & 0x7), lun);
+        size += sprintf(BLS, "%s MByte/sec, offset %d\n",
+                        sync_rate->rate[rate],
+                        p->transinfo[target].cur_offset );
       }
       else
       {
-        size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
-            p->host_no, 0, target, lun);
-      }
-      size += sprintf(BLS, "  Device using %s/%s\n",
-            (p->transinfo[target].cur_width == MSG_EXT_WDTR_BUS_16_BIT) ?
-            "Wide" : "Narrow",
-            (p->transinfo[target].cur_offset != 0) ?
-            "Sync transfers at" : "Async transfers." );
-      if (p->transinfo[target].cur_offset != 0)
-      {
-        struct aic7xxx_syncrate *sync_rate;
-        int period = p->transinfo[target].cur_period;
-        int rate = (p->transinfo[target].cur_width ==
-                    MSG_EXT_WDTR_BUS_16_BIT) ? 1 : 0;
-
-        sync_rate = aic7xxx_find_syncrate(p, &period, AHC_SYNCRATE_ULTRA2);
-        if (sync_rate != NULL)
-        {
-          size += sprintf(BLS, "  %s MByte/sec, offset %d\n",
-                          sync_rate->rate[rate],
-                          p->transinfo[target].cur_offset );
-        }
-        else
-        {
-          size += sprintf(BLS, "  3.3 MByte/sec, offset %d\n",
-                          p->transinfo[target].cur_offset );
-        }
+        size += sprintf(BLS, "3.3 MByte/sec, offset %d\n",
+                        p->transinfo[target].cur_offset );
       }
-      size += sprintf(BLS, "    Device Negotiation Settings\n");
-      size += sprintf(BLS, "        Period Offset Bus Width\n");
-      size += sprintf(BLS, "User       %03d    %03d        %d\n",
-                      p->transinfo[target].user_period,
-                      p->transinfo[target].user_offset,
-                      p->transinfo[target].user_width);
-      size += sprintf(BLS, "Goal       %03d    %03d        %d\n",
-                      p->transinfo[target].goal_period,
-                      p->transinfo[target].goal_offset,
-                      p->transinfo[target].goal_width);
-      size += sprintf(BLS, "Current    %03d    %03d        %d\n",
-                      p->transinfo[target].cur_period,
-                      p->transinfo[target].cur_offset,
-                      p->transinfo[target].cur_width);
-      size += sprintf(BLS, "    Total transfers %ld (%ld read;%ld written)\n",
-          sp->xfers, sp->r_total, sp->w_total);
-      size += sprintf(BLS, "      blks(512) rd=%ld; blks(512) wr=%ld\n",
-          sp->r_total512, sp->w_total512);
+    }
+    size += sprintf(BLS, "  Transinfo settings: ");
+    size += sprintf(BLS, "current(%d/%d/%d), ",
+                    p->transinfo[target].cur_period,
+                    p->transinfo[target].cur_offset,
+                    p->transinfo[target].cur_width);
+    size += sprintf(BLS, "goal(%d/%d/%d), ",
+                    p->transinfo[target].goal_period,
+                    p->transinfo[target].goal_offset,
+                    p->transinfo[target].goal_width);
+    size += sprintf(BLS, "user(%d/%d/%d)\n",
+                    p->transinfo[target].user_period,
+                    p->transinfo[target].user_offset,
+                    p->transinfo[target].user_width);
 #ifdef AIC7XXX_PROC_STATS
-      size += sprintf(BLS, "%s\n", HDRB);
-      size += sprintf(BLS, " Reads:");
-      for (i = 0; i < NUMBER(sp->r_bins); i++)
-      {
-        size += sprintf(BLS, "%6ld ", sp->r_bins[i]);
-      }
-      size += sprintf(BLS, "\n");
-      size += sprintf(BLS, "Writes:");
-      for (i = 0; i < NUMBER(sp->w_bins); i++)
-      {
-        size += sprintf(BLS, "%6ld ", sp->w_bins[i]);
-      }
-#endif /* AIC7XXX_PROC_STATS */
-      size += sprintf(BLS, "\n\n");
+    size += sprintf(BLS, "  Total transfers %ld (%ld reads and %ld writes)\n",
+        sp->r_total + sp->w_total, sp->r_total, sp->w_total);
+    size += sprintf(BLS, "%s\n", HDRB);
+    size += sprintf(BLS, "   Reads:");
+    for (i = 0; i < NUMBER(sp->r_bins); i++)
+    {
+      size += sprintf(BLS, " %7ld", sp->r_bins[i]);
+    }
+    size += sprintf(BLS, "\n");
+    size += sprintf(BLS, "  Writes:");
+    for (i = 0; i < NUMBER(sp->w_bins); i++)
+    {
+      size += sprintf(BLS, " %7ld", sp->w_bins[i]);
     }
+    size += sprintf(BLS, "\n");
+#else
+    size += sprintf(BLS, "  Total transfers %ld (%ld reads and %ld writes)\n",
+        sp->r_total + sp->w_total, sp->r_total, sp->w_total);
+#endif /* AIC7XXX_PROC_STATS */
+    size += sprintf(BLS, "\n\n");
   }
 
   if (size >= aic7xxx_buffer_size)
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/atp870u.c linux/drivers/scsi/atp870u.c
--- linux.vanilla/drivers/scsi/atp870u.c	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/atp870u.c	Fri Dec 11 17:06:49 1998
@@ -0,0 +1,2029 @@
+/* $Id: atp870u.c,v 1.0 1997/05/07 15:22:00 root Exp root $
+ *  linux/kernel/atp870u.c
+ *
+ *  Copyright (C) 1997	Wu Ching Chen
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+
+#include "atp870u.h"
+#include <linux/config.h>	/* for CONFIG_PCI */
+
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_atp870u = {
+    PROC_SCSI_ATP870U, 7, "atp870u",
+    S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+void mydlyu(unsigned int);
+/*
+static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/atp870u.c,v 1.0 1997/05/07 15:22:00 root Exp root $";
+*/
+
+static unsigned char admaxu=1,host_idu[2],chip_veru[2],scam_on[2],global_map[2];
+static unsigned short int active_idu[2],wide_idu[2],sync_idu,ultra_map[2];
+static int  workingu[2]={0,0};
+static Scsi_Cmnd *querequ[2][qcnt],*curr_req[2][16];
+static unsigned char devspu[2][16] = {{0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
+				0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20},
+			       {0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
+				0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20}};
+static unsigned char dirctu[2][16],last_cmd[2],in_snd[2],in_int[2];
+static unsigned char ata_cdbu[2][16];
+static unsigned int ioportu[2]={0,0};
+static unsigned int irqnumu[2]={0,0};
+static unsigned short int pciportu[2];
+static unsigned long prdaddru[2][16],tran_lenu[2][16],last_lenu[2][16];
+static unsigned char prd_tableu[2][16][1024];
+static unsigned char *prd_posu[2][16];
+static unsigned char quhdu[2],quendu[2];
+static unsigned char devtypeu[2][16] = {{ 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 struct Scsi_Host * atp_host[2]={NULL,NULL};
+
+static void atp870u_intr_handle(int irq, void *dev_id, struct pt_regs *regs)
+{
+    unsigned short int	tmpcip,id;
+    unsigned char	i,j,h,tarid,lun;
+    unsigned char  *prd;
+    Scsi_Cmnd *workrequ;
+    unsigned int workportu,tmport;
+    unsigned long adrcntu,k;
+    int      errstus;
+
+    for ( h=0; h < 2; h++ )
+    {
+	if ( ( irq & 0x0f ) == irqnumu[h] )
+	{
+	   goto irq_numok;
+	}
+    }
+    return;
+irq_numok:
+    in_int[h]=1;
+    workportu=ioportu[h];
+    tmport=workportu;
+
+    if ( workingu[h] != 0 )
+    {
+       tmport += 0x1f;
+       j=inb(tmport);
+       tmpcip=pciportu[h];
+       if ((inb(tmpcip) & 0x08) != 0)
+       {
+	  tmpcip += 0x2;
+	  while((inb(tmpcip) & 0x08) != 0);
+       }
+       tmpcip=pciportu[h];
+       outb(0x00,tmpcip);
+       tmport -=0x08;
+       i=inb(tmport);
+       if ((j & 0x40) == 0)
+       {
+	  if ((last_cmd[h] & 0x40) == 0)
+	  {
+	     last_cmd[h]=0xff;
+	  }
+       }
+       else
+       {
+	  last_cmd[h] |= 0x40;
+       }
+       tmport -= 0x02;
+       tarid=inb(tmport);
+       tmport += 0x02;
+       if ((tarid & 0x40) != 0)
+       {
+	  tarid=(tarid & 0x07) | 0x08;
+       }
+       else
+       {
+	  tarid &= 0x07;
+       }
+       if ( i == 0x85 )
+       {
+	  if (wide_idu[h] != 0)
+	  {
+	     tmport=workportu+0x1b;
+	     j=inb(tmport) & 0x0e;
+	     j |= 0x01;
+	     outb(j,tmport);
+	  }
+	  if (((quhdu[h] != quendu[h]) || (last_cmd[h] != 0xff)) &&
+	      (in_snd[h] == 0))
+	  {
+	     send_s870(h);
+	  }
+	  in_int[h]=0;
+	  return;
+       }
+       if ( i == 0x21 )
+       {
+	  tmport -= 0x05;
+	  adrcntu=0;
+	  ((unsigned char *)&adrcntu)[2]=inb(tmport++);
+	  ((unsigned char *)&adrcntu)[1]=inb(tmport++);
+	  ((unsigned char *)&adrcntu)[0]=inb(tmport);
+	  k=last_lenu[h][tarid];
+	  k -= adrcntu;
+	  tran_lenu[h][tarid]= k;
+	  last_lenu[h][tarid]=adrcntu;
+	  tmport -= 0x04;
+	  outb(0x41,tmport);
+	  tmport += 0x08;
+	  outb(0x08,tmport);
+	  in_int[h]=0;
+	  return ;
+       }
+
+       if ((i == 0x80) || (i == 0x8f))
+       {
+	  lun=0;
+	  tmport -= 0x07;
+	  j=inb(tmport);
+	  if ( j == 0x44 )
+	  {
+	     tmport += 0x0d;
+	     lun=inb(tmport) & 0x07;
+	  }
+	  else
+	  {
+	     if ( j == 0x41 )
+	     {
+		tmport += 0x02;
+		adrcntu=0;
+		((unsigned char *)&adrcntu)[2]=inb(tmport++);
+		((unsigned char *)&adrcntu)[1]=inb(tmport++);
+		((unsigned char *)&adrcntu)[0]=inb(tmport);
+		k=last_lenu[h][tarid];
+		k -= adrcntu;
+		tran_lenu[h][tarid]= k;
+		last_lenu[h][tarid]=adrcntu;
+		tmport += 0x04;
+		outb(0x08,tmport);
+		in_int[h]=0;
+		return ;
+	     }
+	     else
+	     {
+		outb(0x46,tmport);
+		dirctu[h][tarid]=0x00;
+		tmport += 0x02;
+		outb(0x00,tmport++);
+		outb(0x00,tmport++);
+		outb(0x00,tmport++);
+		tmport+=0x03;
+		outb(0x08,tmport);
+		in_int[h]=0;
+		return;
+	     }
+	  }
+	  tmport=workportu + 0x10;
+	  outb(0x45,tmport);
+	  tmport += 0x06;
+	  tarid=inb(tmport);
+	  if ((tarid & 0x10) != 0)
+	  {
+	     tarid=(tarid & 0x07) | 0x08;
+	  }
+	  else
+	  {
+	     tarid &= 0x07;
+	  }
+	  workrequ=curr_req[h][tarid];
+	  tmport=workportu + 0x0f;
+	  outb(lun,tmport);
+	  tmport += 0x02;
+	  outb(devspu[h][tarid],tmport++);
+	  adrcntu=tran_lenu[h][tarid];
+	  k=last_lenu[h][tarid];
+	  outb(((unsigned char *)&k)[2],tmport++);
+	  outb(((unsigned char *)&k)[1],tmport++);
+	  outb(((unsigned char *)&k)[0],tmport++);
+	  j=tarid;
+	  if ( tarid > 7 )
+	  {
+	     j = (j & 0x07) | 0x40;
+	  }
+	  j |= dirctu[h][tarid];
+	  outb(j,tmport++);
+	  outb(0x80,tmport);
+	  tmport=workportu + 0x1b;
+	  j=inb(tmport) & 0x0e;
+	  id=1;
+	  id=id << tarid;
+	  if ((id & wide_idu[h]) != 0)
+	  {
+	     j |= 0x01;
+	  }
+	  outb(j,tmport);
+	  if ( last_lenu[h][tarid] == 0 )
+	  {
+	     tmport=workportu + 0x18;
+	     outb(0x08,tmport);
+	     in_int[h]=0;
+	     return ;
+	  }
+	  prd=prd_posu[h][tarid];
+	  while ( adrcntu != 0 )
+	  {
+	       id=((unsigned short int *)(prd))[2];
+	       if ( id == 0 )
+	       {
+		  k=0x10000;
+	       }
+	       else
+	       {
+		  k=id;
+	       }
+	       if ( k > adrcntu )
+	       {
+		  ((unsigned short int *)(prd))[2] =(unsigned short int)
+						     (k - adrcntu);
+		  ((unsigned long *)(prd))[0] += adrcntu;
+		  adrcntu=0;
+		  prd_posu[h][tarid]=prd;
+	       }
+	       else
+	       {
+		  adrcntu -= k;
+		  prdaddru[h][tarid] += 0x08;
+		  prd += 0x08;
+		  if ( adrcntu == 0 )
+		  {
+		     prd_posu[h][tarid]=prd;
+		  }
+	       }
+	  }
+	  tmpcip=pciportu[h] + 0x04;
+	  outl(prdaddru[h][tarid],tmpcip);
+	  tmpcip -= 0x02;
+	  outb(0x06,tmpcip);
+	  outb(0x00,tmpcip);
+	  tmpcip -= 0x02;
+	  tmport=workportu + 0x18;
+	  if ( dirctu[h][tarid] != 0 )
+	  {
+	     outb(0x08,tmport);
+	     outb(0x01,tmpcip);
+	     in_int[h]=0;
+	     return;
+	  }
+	  outb(0x08,tmport);
+	  outb(0x09,tmpcip);
+	  in_int[h]=0;
+	  return;
+       }
+
+       workrequ=curr_req[h][tarid];
+       if ( i == 0x42 )
+       {
+	  errstus=0x02;
+	  workrequ->result=errstus;
+	  goto go_42;
+       }
+       if ( i == 0x16 )
+       {
+	  errstus=0;
+	  tmport -= 0x08;
+	  errstus=inb(tmport);
+	  workrequ->result=errstus;
+/*	  if ( errstus == 0x02 )
+	  {
+	     tmport +=0x10;
+	     if ((inb(tmport) & 0x80) != 0)
+	     {
+		printk(" autosense ");
+	     }
+	     tmport -=0x09;
+	     outb(0,tmport);
+	     tmport=workportu+0x3a;
+	     outb((unsigned char)(inb(tmport) | 0x10),tmport);
+	     tmport -= 0x39;
+
+	     outb(0x08,tmport++);
+	     outb(0x7f,tmport++);
+	     outb(0x03,tmport++);
+	     outb(0x00,tmport++);
+	     outb(0x00,tmport++);
+	     outb(0x00,tmport++);
+	     outb(0x0e,tmport++);
+	     outb(0x00,tmport);
+	     tmport+=0x07;
+	     outb(0x00,tmport++);
+	     tmport++;
+	     outb(devspu[h][workrequ->target],tmport++);
+	     outb(0x00,tmport++);
+	     outb(0x00,tmport++);
+	     outb(0x0e,tmport++);
+	     tmport+=0x03;
+	     outb(0x09,tmport);
+	     tmport+=0x07;
+	     i=0;
+	     adrcntu=(unsigned long)(&workrequ->sense_buffer[0]);
+get_sens:
+	     j=inb(tmport);
+	     if ((j & 0x01) != 0)
+	     {
+		tmport-=0x06;
+		(unsigned char)(((caddr_t) adrcntu)[i++])=inb(tmport);
+		tmport+=0x06;
+		goto get_sens;
+	     }
+	     if ((j & 0x80) == 0)
+	     {
+		goto get_sens;
+	     }
+	     if ((j & 0x40) == 0)
+	     {
+		tmport-=0x08;
+		i=inb(tmport);
+	     }
+	     tmport=workportu+0x3a;
+	     outb((unsigned char)(inb(tmport) & 0xef),tmport);
+	     tmport=workportu+0x01;
+	     outb(0x2c,tmport);
+	     tmport += 0x15;
+	     outb(0x80,tmport);
+	  }   */
+go_42:
+	  (*workrequ->scsi_done)(workrequ);
+	  curr_req[h][tarid]=0;
+	  workingu[h]--;
+	  if (wide_idu[h] != 0)
+	  {
+	     tmport=workportu+0x1b;
+	     j=inb(tmport) & 0x0e;
+	     j |= 0x01;
+	     outb(j,tmport);
+	  }
+	  if (((last_cmd[h] != 0xff) || (quhdu[h] != quendu[h])) &&
+	      (in_snd[h] == 0))
+	  {
+	     send_s870(h);
+	  }
+	  in_int[h]=0;
+	  return;
+       }
+   if ( i == 0x4f )
+   {
+      i=0x89;
+   }
+   i &= 0x0f;
+   if ( i == 0x09 )
+   {
+      tmpcip=tmpcip+4;
+      outl(prdaddru[h][tarid],tmpcip);
+      tmpcip=tmpcip-2;
+      outb(0x06,tmpcip);
+      outb(0x00,tmpcip);
+      tmpcip=tmpcip-2;
+      tmport=workportu+0x10;
+      outb(0x41,tmport);
+      dirctu[h][tarid]=0x00;
+      tmport += 0x08;
+      outb(0x08,tmport);
+      outb(0x09,tmpcip);
+      in_int[h]=0;
+      return;
+   }
+   if ( i == 0x08 )
+   {
+      tmpcip=tmpcip+4;
+      outl(prdaddru[h][tarid],tmpcip);
+      tmpcip=tmpcip-2;
+      outb(0x06,tmpcip);
+      outb(0x00,tmpcip);
+      tmpcip=tmpcip-2;
+      tmport=workportu+0x10;
+      outb(0x41,tmport);
+      tmport += 0x05;
+      outb((unsigned char)(inb(tmport) | 0x20),tmport);
+      dirctu[h][tarid]=0x20;
+      tmport += 0x03;
+      outb(0x08,tmport);
+      outb(0x01,tmpcip);
+      in_int[h]=0;
+      return;
+   }
+   tmport -= 0x07;
+   if ( i == 0x0a )
+   {
+      outb(0x30,tmport);
+   }
+   else
+   {
+      outb(0x46,tmport);
+   }
+   dirctu[h][tarid]=0x00;
+   tmport += 0x02;
+   outb(0x00,tmport++);
+   outb(0x00,tmport++);
+   outb(0x00,tmport++);
+   tmport+=0x03;
+   outb(0x08,tmport);
+   in_int[h]=0;
+   return;
+  }
+  else
+  {
+     tmport=workportu+0x17;
+     inb(tmport);
+     workingu[h]=0;
+     in_int[h]=0;
+     return;
+  }
+}
+
+int atp870u_queuecommand(Scsi_Cmnd * req_p, void (*done)(Scsi_Cmnd *))
+{
+    unsigned char i,h;
+    unsigned long flags;
+    unsigned short int m;
+    unsigned int tmport;
+
+    for( h=0; h <= admaxu; h++ )
+    {
+       if ( req_p->host == atp_host[h] )
+       {
+	  goto host_ok;
+       }
+    }
+    return 0;
+host_ok:
+   if ( req_p->channel != 0 )
+   {
+      req_p->result = 0x00040000;
+      done(req_p);
+      return 0;
+   }
+   m=1;
+   m=  m << req_p->target;
+   if ( ( m & active_idu[h] ) == 0 )
+   {
+      req_p->result = 0x00040000;
+      done(req_p);
+      return 0;
+   }
+   if (done)
+   {
+      req_p->scsi_done = done;
+   }
+   else
+   {
+      printk("atp870u_queuecommand: done can't be NULL\n");
+      req_p->result = 0;
+      done(req_p);
+      return 0;
+   }
+   quendu[h]++;
+   if ( quendu[h] >= qcnt )
+   {
+      quendu[h]=0;
+   }
+   wait_que_empty:
+     if ( quhdu[h] == quendu[h] )
+     {
+	goto wait_que_empty;
+     }
+     save_flags(flags);
+     cli();
+     querequ[h][quendu[h]]=req_p;
+     if ( quendu[h] == 0 )
+     {
+	i=qcnt-1;
+     }
+     else
+     {
+	i=quendu[h]-1;
+     }
+     tmport = ioportu[h]+0x1c;
+     restore_flags(flags);
+     if ((inb(tmport) == 0) && (in_int[h] == 0) && (in_snd[h] == 0))
+     {
+	 send_s870(h);
+     }
+     return 0;
+}
+
+void mydlyu(unsigned int dlycnt )
+{
+    unsigned int i ;
+    for ( i = 0 ; i < dlycnt ; i++ )
+    {
+       inb(0x80);
+    }
+}
+
+void send_s870(unsigned char h)
+{
+     unsigned int  tmport;
+     Scsi_Cmnd *workrequ;
+     unsigned long flags;
+     unsigned int   i;
+     unsigned char  j,tarid;
+     unsigned char  *prd;
+     unsigned short int   tmpcip,w;
+     unsigned long  l,bttl;
+     unsigned int workportu;
+     struct scatterlist * sgpnt;
+
+	save_flags(flags);
+	cli();
+	if ( in_snd[h] != 0 )
+	{
+	   restore_flags(flags);
+	   return;
+	}
+	in_snd[h]=1;
+	if ((last_cmd[h] != 0xff) && ((last_cmd[h] & 0x40) != 0))
+	{
+	   last_cmd[h] &= 0x0f;
+	   workrequ=curr_req[h][last_cmd[h]];
+	   goto cmd_subp;
+	}
+	workingu[h]++;
+	j=quhdu[h];
+	quhdu[h]++;
+	if ( quhdu[h] >= qcnt )
+	{
+	   quhdu[h]=0;
+	}
+	workrequ=querequ[h][quhdu[h]];
+	if ( curr_req[h][workrequ->target] == 0 )
+	{
+	   curr_req[h][workrequ->target]=workrequ;
+	   last_cmd[h]=workrequ->target;
+	   goto cmd_subp;
+	}
+	quhdu[h]=j;
+	workingu[h]--;
+	in_snd[h]=0;
+	restore_flags(flags);
+	return ;
+cmd_subp:
+   workportu=ioportu[h];
+   tmport=workportu+0x1f;
+   if ((inb(tmport) & 0xb0) != 0)
+   {
+      goto abortsnd;
+   }
+   tmport=workportu+0x1c;
+   if ( inb(tmport) == 0 )
+   {
+      goto oktosend;
+   }
+abortsnd:
+   last_cmd[h] |= 0x40;
+   in_snd[h]=0;
+   restore_flags(flags);
+   return;
+oktosend:
+   memcpy(&ata_cdbu[h][0], &workrequ->cmnd[0], workrequ->cmd_len);
+   if ( ata_cdbu[h][0] == 0x25 )
+   {
+      if ( workrequ->request_bufflen > 8 )
+      {
+	 workrequ->request_bufflen=0x08;
+      }
+   }
+   if ( ata_cdbu[h][0] == 0x12 )
+   {
+      if ( workrequ->request_bufflen > 0x24 )
+      {
+	 workrequ->request_bufflen = 0x24;
+	 ata_cdbu[h][4]=0x24;
+      }
+   }
+
+   tmport=workportu+0x1b;
+   j=inb(tmport) & 0x0e;
+   tarid=workrequ->target;
+   w=1;
+   w = w << tarid;
+   if ((w & wide_idu[h]) != 0)
+   {
+      j |= 0x01;
+   }
+   outb(j,tmport);
+   tmport=workportu;
+   outb(workrequ->cmd_len,tmport++);
+   outb(0x2c,tmport++);
+   outb(0xcf,tmport++);
+   for ( i=0 ; i < workrequ->cmd_len ; i++ )
+   {
+       outb(ata_cdbu[h][i],tmport++);
+   }
+   tmport=workportu+0x0f;
+   outb(0x00,tmport);
+   tmport+=0x02;
+   outb(devspu[h][tarid],tmport++);
+   if (workrequ->use_sg)
+   {
+
+     l=0;
+     sgpnt = (struct scatterlist *) workrequ->request_buffer;
+     for(i=0; i<workrequ->use_sg; i++)
+     {
+       if(sgpnt[i].length == 0 || workrequ->use_sg > ATP870U_SCATTER)
+       {
+	 panic("Foooooooood fight!");
+       }
+       l += sgpnt[i].length;
+     }
+   }
+   else
+   {
+     l=workrequ->request_bufflen;
+   }
+   outb((unsigned char)(((unsigned char *)(&l))[2]),tmport++);
+   outb((unsigned char)(((unsigned char *)(&l))[1]),tmport++);
+   outb((unsigned char)(((unsigned char *)(&l))[0]),tmport++);
+   j=tarid;
+   last_lenu[h][j]=l;
+   tran_lenu[h][j]=0;
+   if ((j & 0x08) != 0)
+   {
+      j=(j & 0x07) | 0x40;
+   }
+   if ((ata_cdbu[h][0] == 0x0a) || (ata_cdbu[h][0] == 0x2a) ||
+       (ata_cdbu[h][0] == 0xaa) || (ata_cdbu[h][0] == 0x15))
+   {
+      outb((unsigned char)(j | 0x20),tmport++);
+   }
+   else
+   {
+      outb(j,tmport++);
+   }
+   outb(0x80,tmport);
+   tmport=workportu + 0x1c;
+   dirctu[h][tarid]=0;
+   if ( l == 0 )
+   {
+      if ( inb(tmport) == 0 )
+      {
+	 tmport=workportu+0x18;
+	 outb(0x08,tmport);
+      }
+      else
+      {
+	last_cmd[h] |= 0x40;
+      }
+      in_snd[h]=0;
+      restore_flags(flags);
+      return;
+   }
+   tmpcip=pciportu[h];
+   prd=&prd_tableu[h][tarid][0];
+   prd_posu[h][tarid]=prd;
+   if (workrequ->use_sg)
+   {
+     sgpnt = (struct scatterlist *) workrequ->request_buffer;
+     i=0;
+     for(j=0; j<workrequ->use_sg; j++)
+     {
+	(unsigned long)(((unsigned long *)(prd))[i >> 1])=(unsigned long)sgpnt[j].address;
+	(unsigned short int)(((unsigned short int *)(prd))[i+2])=sgpnt[j].length;
+	(unsigned short int)(((unsigned short int *)(prd))[i+3])=0;
+	i +=0x04;
+     }
+     (unsigned short int)(((unsigned short int *)(prd))[i-1])=0x8000;
+   }
+   else
+   {
+     bttl=(unsigned long)workrequ->request_buffer;
+     l=workrequ->request_bufflen;
+     i=0;
+     while ( l > 0x10000 )
+     {
+	(unsigned short int)(((unsigned short int *)(prd))[i+3])=0x0000;
+	(unsigned short int)(((unsigned short int *)(prd))[i+2])=0x0000;
+	(unsigned long)(((unsigned long *)(prd))[i >> 1])=bttl;
+	l -= 0x10000;
+	bttl += 0x10000;
+	i += 0x04;
+     }
+     (unsigned short int)(((unsigned short int *)(prd))[i+3])=0x8000;
+     (unsigned short int)(((unsigned short int *)(prd))[i+2])=l;
+     (unsigned long)(((unsigned long *)(prd))[i >> 1])=bttl;
+   }
+   tmpcip=tmpcip+4;
+   prdaddru[h][tarid]=(unsigned long)&prd_tableu[h][tarid][0];
+   outl(prdaddru[h][tarid],tmpcip);
+   tmpcip=tmpcip-2;
+   outb(0x06,tmpcip);
+   outb(0x00,tmpcip);
+   tmpcip=tmpcip-2;
+   if ((ata_cdbu[h][0] == 0x0a) || (ata_cdbu[h][0] == 0x2a) ||
+       (ata_cdbu[h][0] == 0xaa) || (ata_cdbu[h][0] == 0x15))
+   {
+      dirctu[h][tarid]=0x20;
+      if ( inb(tmport) == 0 )
+      {
+	 tmport=workportu+0x18;
+	 outb(0x08,tmport);
+	 outb(0x01,tmpcip);
+      }
+      else
+      {
+	 last_cmd[h] |= 0x40;
+      }
+      in_snd[h]=0;
+      restore_flags(flags);
+      return;
+   }
+   if ( inb(tmport) == 0 )
+   {
+      tmport=workportu+0x18;
+      outb(0x08,tmport);
+      outb(0x09,tmpcip);
+   }
+   else
+   {
+      last_cmd[h] |= 0x40;
+   }
+   in_snd[h]=0;
+   restore_flags(flags);
+   return;
+
+}
+
+static void internal_done(Scsi_Cmnd * SCpnt)
+{
+	SCpnt->SCp.Status++;
+}
+
+int atp870u_command(Scsi_Cmnd * SCpnt)
+{
+
+    atp870u_queuecommand(SCpnt, internal_done);
+
+    SCpnt->SCp.Status = 0;
+    while (!SCpnt->SCp.Status)
+	barrier();
+    return SCpnt->result;
+}
+
+unsigned char fun_scam ( unsigned char host,unsigned short int * val )
+{
+    unsigned int  tmport ;
+    unsigned short int	 i,k;
+    unsigned char     j;
+
+    tmport = ioportu[host]+0x1c;
+    outw(*val,tmport);
+FUN_D7:
+    for ( i=0; i < 10; i++ )	     /* stable >= bus settle delay(400 ns)  */
+    {
+	k=inw(tmport);
+	j= (unsigned char)(k >> 8);
+	if ((k & 0x8000) != 0)	     /* DB7 all release?    */
+	{
+	   goto  FUN_D7;
+	}
+    }
+    *val |= 0x4000;		    /* assert DB6	    */
+    outw(*val,tmport);
+    *val &= 0xdfff;		    /* assert DB5	    */
+    outw(*val,tmport);
+FUN_D5:
+    for ( i=0; i < 10; i++ )	    /* stable >= bus settle delay(400 ns) */
+    {
+       if ((inw(tmport) & 0x2000) != 0)   /* DB5 all release?	*/
+       {
+	  goto	FUN_D5;
+       }
+    }
+    *val |= 0x8000;		     /* no DB4-0, assert DB7	*/
+    *val &= 0xe0ff;
+    outw(*val,tmport);
+    *val &= 0xbfff;		     /* release DB6		*/
+    outw(*val,tmport);
+FUN_D6:
+    for ( i=0; i < 10; i++ )	     /* stable >= bus settle delay(400 ns)  */
+    {
+       if ((inw(tmport) & 0x4000) != 0)   /* DB6 all release?  */
+       {
+	  goto	FUN_D6;
+       }
+    }
+
+    return j;
+}
+
+void tscam( unsigned char host )
+{
+
+    unsigned int  tmport ;
+    unsigned char  i,j,k;
+    unsigned long  n;
+    unsigned short int	m,assignid_map,val;
+    unsigned char  mbuf[33],quintet[2];
+    static unsigned char g2q_tab[8]={ 0x38,0x31,0x32,0x2b,0x34,0x2d,0x2e,0x27 };
+
+
+    for ( i=0; i < 0x10; i++ )
+    {
+	mydlyu(0xffff);
+    }
+
+    tmport = ioportu[host]+1;
+    outb(0x08,tmport++);
+    outb(0x7f,tmport);
+    tmport = ioportu[host]+0x11;
+    outb(0x20,tmport);
+
+    if ((scam_on[host] & 0x40) == 0)
+    {
+       return;
+    }
+
+    m=1;
+    m <<= host_idu[host];
+    j=16;
+    if ( chip_veru[host] < 4 )
+    {
+       m |= 0xff00;
+       j=8;
+    }
+    assignid_map=m;
+    tmport = ioportu[host]+0x02;
+    outb(0x02,tmport++);	/* 2*2=4ms,3EH 2/32*3E=3.9ms */
+    outb(0,tmport++);
+    outb(0,tmport++);
+    outb(0,tmport++);
+    outb(0,tmport++);
+    outb(0,tmport++);
+    outb(0,tmport++);
+
+    for ( i = 0 ; i < j ; i ++ )
+    {
+	m=1;
+	m=m<<i;
+	if ( ( m & assignid_map ) != 0 )
+	{
+	   continue;
+	}
+    tmport = ioportu[host]+0x0f;
+    outb(0,tmport++);
+    tmport += 0x02;
+    outb(0,tmport++);
+    outb(0,tmport++);
+    outb(0,tmport++);
+    if ( i > 7 )
+    {
+       k=(i & 0x07) | 0x40;
+    }
+    else
+    {
+       k=i;
+    }
+    outb(k,tmport++);
+    tmport = ioportu[host]+0x1b;
+    if ( chip_veru[host] == 4 )
+    {
+       outb((unsigned char)((inb(tmport) & 0x0e) | 0x01),tmport);
+    }
+    else
+    {
+       outb((unsigned char)(inb(tmport) & 0x0e),tmport);
+    }
+wait_rdyok:
+    tmport = ioportu[host]+0x18;
+    outb(0x09,tmport);
+    tmport += 0x07;
+
+    while ((inb(tmport) & 0x80) == 0x00);
+    tmport -= 0x08;
+    k=inb(tmport);
+    if ( k != 0x16 )
+    {
+       if ((k == 0x85) || (k == 0x42))
+       {
+	  continue;
+       }
+       tmport = ioportu[host]+0x10;
+       outb(0x41,tmport);
+       goto wait_rdyok;
+    }
+    assignid_map |= m;
+
+    }
+    tmport = ioportu[host]+0x02;
+    outb(0x7f,tmport);
+    tmport = ioportu[host]+0x1b;
+    outb(0x02,tmport);
+
+    outb(0,0x80);
+
+    val=0x0080;      /* bsy  */
+    tmport = ioportu[host]+0x1c;
+    outw(val,tmport);
+    val |=0x0040;    /* sel  */
+    outw(val,tmport);
+    val |=0x0004;    /* msg  */
+    outw(val,tmport);
+    inb(0x80);		      /* 2 deskew delay(45ns*2=90ns) */
+    val &=0x007f;    /* no bsy	*/
+    outw(val,tmport);
+    mydlyu(0xffff);  /* recommanded SCAM selection response time */
+    mydlyu(0xffff);
+    val &=0x00fb;    /* after 1ms no msg */
+    outw(val,tmport);
+wait_nomsg:
+    if ((inb(tmport) & 0x04) != 0)
+    {
+       goto wait_nomsg;
+    }
+    outb(1,0x80);
+    mydlyu(100);
+    for ( n=0; n < 0x30000; n++ )
+    {
+	if ((inb(tmport) & 0x80) != 0)	   /* bsy ? */
+	{
+	   goto wait_io;
+	}
+    }
+    goto  TCM_SYNC;
+wait_io:
+    for ( n=0; n < 0x30000; n++ )
+    {
+	if ((inb(tmport) & 0x81) == 0x0081)
+	{
+	   goto wait_io1;
+	}
+    }
+    goto  TCM_SYNC;
+wait_io1:
+    inb(0x80);
+    val |=0x8003;    /* io,cd,db7  */
+    outw(val,tmport);
+    inb(0x80);
+    val &=0x00bf;    /* no sel	   */
+    outw(val,tmport);
+    outb(2,0x80);
+TCM_SYNC:
+    mydlyu(0x800);
+    if ((inb(tmport) & 0x80) == 0x00)	 /* bsy ? */
+    {
+       outw(0,tmport--);
+       outb(0,tmport);
+       tmport=ioportu[host] + 0x15;
+       outb(0,tmport);
+       tmport += 0x03;
+       outb(0x09,tmport);
+       tmport += 0x07;
+       while ((inb(tmport) & 0x80) == 0);
+       tmport -= 0x08;
+       inb(tmport);
+       return;
+    }
+
+    val &= 0x00ff;		 /* synchronization  */
+    val |= 0x3f00;
+    fun_scam(host,&val);
+    outb(3,0x80);
+    val &= 0x00ff;		 /* isolation	     */
+    val |= 0x2000;
+    fun_scam(host,&val);
+    outb(4,0x80);
+    i=8;
+    j=0;
+TCM_ID:
+    if ((inw(tmport) & 0x2000) == 0)
+    {
+       goto TCM_ID;
+    }
+    outb(5,0x80);
+    val &= 0x00ff;		 /* get ID_STRING */
+    val |= 0x2000;
+    k=fun_scam(host,&val);
+    if ((k & 0x03) == 0)
+    {
+       goto TCM_5;
+    }
+    mbuf[j] <<= 0x01;
+    mbuf[j] &= 0xfe;
+    if ((k & 0x02) != 0)
+    {
+       mbuf[j] |= 0x01;
+    }
+    i--;
+    if ( i > 0 )
+    {
+       goto TCM_ID;
+    }
+    j++;
+    i=8;
+    goto TCM_ID;
+
+TCM_5:			     /* isolation complete..  */
+/*    mbuf[32]=0;
+    printk(" \n%x %x %x %s\n ",assignid_map,mbuf[0],mbuf[1],&mbuf[2]); */
+    i=15;
+    j=mbuf[0];
+    if ((j & 0x20) != 0)     /* bit5=1:ID upto 7      */
+    {
+       i=7;
+    }
+    if ((j & 0x06) == 0)     /* IDvalid?	      */
+    {
+       goto  G2Q5;
+    }
+    k=mbuf[1];
+small_id:
+    m=1;
+    m <<= k;
+    if ((m & assignid_map) == 0)
+    {
+       goto G2Q_QUIN;
+    }
+    if ( k > 0 )
+    {
+       k--;
+       goto small_id;
+    }
+G2Q5:			      /* srch from max acceptable ID#  */
+    k=i;		      /* max acceptable ID#	       */
+G2Q_LP:
+    m=1;
+    m <<= k;
+    if ((m & assignid_map) == 0)
+    {
+       goto G2Q_QUIN;
+    }
+    if ( k > 0 )
+    {
+       k--;
+       goto G2Q_LP;
+    }
+G2Q_QUIN:		      /* k=binID#,	 */
+    assignid_map |= m;
+    if ( k < 8 )
+    {
+       quintet[0]=0x38;       /* 1st dft ID<8	 */
+    }
+    else
+    {
+       quintet[0]=0x31;       /* 1st  ID>=8	 */
+    }
+    k &= 0x07;
+    quintet[1]=g2q_tab[k];
+
+    val &= 0x00ff;	       /* AssignID 1stQuintet,AH=001xxxxx  */
+    m=quintet[0] << 8;
+    val |= m;
+    fun_scam(host,&val);
+    val &= 0x00ff;	       /* AssignID 2ndQuintet,AH=001xxxxx */
+    m=quintet[1] << 8;
+    val |= m;
+    fun_scam(host,&val);
+
+    goto TCM_SYNC;
+
+}
+
+void is870(unsigned long host,unsigned int wkport )
+{
+    unsigned int  tmport ;
+    unsigned char i,j,k,rmb;
+    unsigned short int m;
+    static unsigned char mbuf[512];
+    static unsigned char satn[9] = { 0,0,0,0,0,0,0,6,6 };
+    static unsigned char inqd[9] = { 0x12,0,0,0,0x24,0,0,0x24,6 };
+    static unsigned char synn[6] = { 0x80,1,3,1,0x19,0x0e };
+    static unsigned char synu[6] = { 0x80,1,3,1,0x0c,0x0e };
+    static unsigned char synw[6] = { 0x80,1,3,1,0x0c,0x07 };
+    static unsigned char wide[6] = { 0x80,1,2,3,1,0 };
+
+    sync_idu=0;
+    tmport=wkport+0x3a;
+    outb((unsigned char)(inb(tmport) | 0x10),tmport);
+
+    for ( i = 0 ; i < 16 ; i ++ )
+    {
+	if ((chip_veru[host] != 4) && (i > 7))
+	{
+	   break;
+	}
+	m=1;
+	m=m<<i;
+	if ( ( m & active_idu[host] ) != 0 )
+	{
+	   continue;
+	}
+	if ( i == host_idu[host] )
+	{
+	   printk("         ID: %2d  Host Adapter\n",host_idu[host]);
+	   continue;
+	}
+	if ( chip_veru[host] == 4 )
+	{
+	   tmport=wkport+0x1b;
+	   j=(inb(tmport) & 0x0e) | 0x01;
+	   outb(j,tmport);
+	}
+    tmport=wkport+1;
+    outb(0x08,tmport++);
+    outb(0x7f,tmport++);
+    outb(satn[0],tmport++);
+    outb(satn[1],tmport++);
+    outb(satn[2],tmport++);
+    outb(satn[3],tmport++);
+    outb(satn[4],tmport++);
+    outb(satn[5],tmport++);
+    tmport+=0x06;
+    outb(0,tmport);
+    tmport+=0x02;
+    outb(devspu[host][i],tmport++);
+    outb(0,tmport++);
+    outb(satn[6],tmport++);
+    outb(satn[7],tmport++);
+    j=i;
+    if ((j & 0x08) != 0)
+    {
+       j=(j & 0x07) | 0x40;
+    }
+    outb(j,tmport);
+    tmport+=0x03;
+    outb(satn[8],tmport);
+    tmport+=0x07;
+
+    while ((inb(tmport) & 0x80) == 0x00);
+    tmport-=0x08;
+    if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e))
+    {
+       continue;
+    }
+    while ( inb(tmport) != 0x8e );
+    active_idu[host] |= m;
+
+    tmport=wkport+0x10;
+    outb(0x30,tmport);
+    tmport=wkport+0x04;
+    outb(0x00,tmport);
+
+phase_cmd:
+    tmport=wkport+0x18;
+    outb(0x08,tmport);
+    tmport+=0x07;
+    while ((inb(tmport) & 0x80) == 0x00);
+    tmport-=0x08;
+    j=inb(tmport);
+    if ( j != 0x16 )
+    {
+       tmport=wkport+0x10;
+       outb(0x41,tmport);
+       goto phase_cmd;
+    }
+sel_ok:
+       tmport=wkport+3;
+       outb(inqd[0],tmport++);
+       outb(inqd[1],tmport++);
+       outb(inqd[2],tmport++);
+       outb(inqd[3],tmport++);
+       outb(inqd[4],tmport++);
+       outb(inqd[5],tmport);
+       tmport+=0x07;
+       outb(0,tmport);
+       tmport+=0x02;
+       outb(devspu[host][i],tmport++);
+       outb(0,tmport++);
+       outb(inqd[6],tmport++);
+       outb(inqd[7],tmport++);
+       tmport+=0x03;
+       outb(inqd[8],tmport);
+       tmport+=0x07;
+       while ((inb(tmport) & 0x80) == 0x00);
+       tmport-=0x08;
+       if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e))
+       {
+	  continue;
+       }
+       while ( inb(tmport) != 0x8e );
+       if ( chip_veru[host] == 4 )
+       {
+	  tmport=wkport+0x1b;
+	  j=inb(tmport) & 0x0e;
+	  outb(j,tmport);
+       }
+       tmport=wkport+0x18;
+       outb(0x08,tmport);
+       tmport += 0x07;
+       j=0;
+rd_inq_data:
+       k=inb(tmport);
+       if ((k & 0x01) != 0 )
+       {
+	  tmport-=0x06;
+	  mbuf[j++]=inb(tmport);
+	  tmport+=0x06;
+	  goto rd_inq_data;
+       }
+       if ((k & 0x80) == 0 )
+       {
+	  goto rd_inq_data;
+       }
+       tmport-=0x08;
+       j=inb(tmport);
+       if ( j == 0x16 )
+       {
+	  goto inq_ok;
+       }
+    tmport=wkport+0x10;
+    outb(0x46,tmport);
+    tmport+=0x02;
+    outb(0,tmport++);
+    outb(0,tmport++);
+    outb(0,tmport++);
+    tmport+=0x03;
+    outb(0x08,tmport);
+    tmport+=0x07;
+    while ((inb(tmport) & 0x80) == 0x00);
+    tmport-=0x08;
+    if (inb(tmport) != 0x16)
+    {
+       goto sel_ok;
+    }
+inq_ok:
+     mbuf[36]=0;
+     printk("         ID: %2d  %s\n",i,&mbuf[8]);
+     devtypeu[host][i]=mbuf[0];
+     rmb=mbuf[1];
+     if ( chip_veru[host] != 4 )
+     {
+	goto not_wide;
+     }
+     if ((mbuf[7] & 0x60) == 0)
+     {
+	goto not_wide;
+     }
+     if ((global_map[host] & 0x20) == 0)
+     {
+	goto not_wide;
+     }
+     tmport=wkport+0x1b;
+     j=(inb(tmport) & 0x0e) | 0x01;
+     outb(j,tmport);
+    tmport=wkport+3;
+    outb(satn[0],tmport++);
+    outb(satn[1],tmport++);
+    outb(satn[2],tmport++);
+    outb(satn[3],tmport++);
+    outb(satn[4],tmport++);
+    outb(satn[5],tmport++);
+    tmport+=0x06;
+    outb(0,tmport);
+    tmport+=0x02;
+    outb(devspu[host][i],tmport++);
+    outb(0,tmport++);
+    outb(satn[6],tmport++);
+    outb(satn[7],tmport++);
+    tmport+=0x03;
+    outb(satn[8],tmport);
+    tmport+=0x07;
+
+    while ((inb(tmport) & 0x80) == 0x00);
+    tmport-=0x08;
+    if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e))
+    {
+       continue;
+    }
+    while ( inb(tmport) != 0x8e );
+try_wide:
+    j=0;
+    tmport=wkport+0x14;
+    outb(0x05,tmport);
+    tmport += 0x04;
+    outb(0x20,tmport);
+    tmport+=0x07;
+
+    while ((inb(tmport) & 0x80) == 0 )
+    {
+       if ((inb(tmport) & 0x01) != 0 )
+       {
+	  tmport-=0x06;
+	  outb(wide[j++],tmport);
+	  tmport+=0x06;
+       }
+    }
+    tmport-=0x08;
+    while ((inb(tmport) & 0x80) == 0x00);
+    j=inb(tmport) & 0x0f;
+    if ( j == 0x0f )
+    {
+       goto widep_in;
+    }
+    if ( j == 0x0a )
+    {
+       goto widep_cmd;
+    }
+    if ( j == 0x0e )
+    {
+       goto try_wide;
+    }
+    continue;
+widep_out:
+    tmport=wkport+0x18;
+    outb(0x20,tmport);
+    tmport+=0x07;
+    while ((inb(tmport) & 0x80) == 0 )
+    {
+	if ((inb(tmport) & 0x01) != 0 )
+	{
+	   tmport-=0x06;
+	   outb(0,tmport);
+	   tmport+=0x06;
+	}
+    }
+    tmport-=0x08;
+    j=inb(tmport) & 0x0f;
+    if ( j == 0x0f )
+    {
+       goto widep_in;
+    }
+    if ( j == 0x0a )
+    {
+       goto widep_cmd;
+    }
+    if ( j == 0x0e )
+    {
+       goto widep_out;
+    }
+    continue;
+widep_in:
+    tmport=wkport+0x14;
+    outb(0xff,tmport);
+    tmport += 0x04;
+    outb(0x20,tmport);
+    tmport+=0x07;
+    k=0;
+widep_in1:
+    j=inb(tmport);
+    if ((j & 0x01) != 0)
+    {
+       tmport-=0x06;
+       mbuf[k++]=inb(tmport);
+       tmport+=0x06;
+       goto widep_in1;
+    }
+    if ((j & 0x80) == 0x00)
+    {
+       goto widep_in1;
+    }
+    tmport-=0x08;
+    j=inb(tmport) & 0x0f;
+    if ( j == 0x0f )
+    {
+       goto widep_in;
+    }
+    if ( j == 0x0a )
+    {
+       goto widep_cmd;
+    }
+    if ( j == 0x0e )
+    {
+       goto widep_out;
+    }
+    continue;
+widep_cmd:
+    tmport=wkport+0x10;
+    outb(0x30,tmport);
+    tmport=wkport+0x14;
+    outb(0x00,tmport);
+    tmport+=0x04;
+    outb(0x08,tmport);
+    tmport+=0x07;
+    while ((inb(tmport) & 0x80) == 0x00);
+    tmport-=0x08;
+    j=inb(tmport);
+    if ( j != 0x16 )
+    {
+       if ( j == 0x4e )
+       {
+	  goto widep_out;
+       }
+       continue;
+    }
+    if ( mbuf[0] != 0x01 )
+    {
+       goto not_wide;
+    }
+    if ( mbuf[1] != 0x02 )
+    {
+       goto not_wide;
+    }
+    if ( mbuf[2] != 0x03 )
+    {
+       goto not_wide;
+    }
+    if ( mbuf[3] != 0x01 )
+    {
+       goto not_wide;
+    }
+    m=1;
+    m = m << i;
+    wide_idu[host] |= m;
+not_wide:
+    if ((devtypeu[host][i] == 0x00) || (devtypeu[host][i] == 0x07))
+    {
+       goto set_sync;
+    }
+    continue;
+set_sync:
+    tmport=wkport+0x1b;
+    j=inb(tmport) & 0x0e;
+    if ((m & wide_idu[host]) != 0 )
+    {
+       j |= 0x01;
+    }
+    outb(j,tmport);
+    tmport=wkport+3;
+    outb(satn[0],tmport++);
+    outb(satn[1],tmport++);
+    outb(satn[2],tmport++);
+    outb(satn[3],tmport++);
+    outb(satn[4],tmport++);
+    outb(satn[5],tmport++);
+    tmport+=0x06;
+    outb(0,tmport);
+    tmport+=0x02;
+    outb(devspu[host][i],tmport++);
+    outb(0,tmport++);
+    outb(satn[6],tmport++);
+    outb(satn[7],tmport++);
+    tmport+=0x03;
+    outb(satn[8],tmport);
+    tmport+=0x07;
+
+    while ((inb(tmport) & 0x80) == 0x00);
+    tmport-=0x08;
+    if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e))
+    {
+       continue;
+    }
+    while ( inb(tmport) != 0x8e);
+try_sync:
+    j=0;
+    tmport=wkport+0x14;
+    outb(0x06,tmport);
+    tmport += 0x04;
+    outb(0x20,tmport);
+    tmport+=0x07;
+
+    while ((inb(tmport) & 0x80) == 0 )
+    {
+       if ((inb(tmport) & 0x01) != 0 )
+       {
+	  tmport-=0x06;
+	  if ( rmb != 0 )
+	  {
+	     outb(synn[j++],tmport);
+	  }
+	  else
+	  {
+	     if ((m & wide_idu[host]) != 0)
+	     {
+		outb(synw[j++],tmport);
+	     }
+	     else
+	     {
+		if ((m & ultra_map[host]) != 0)
+		{
+		   outb(synu[j++],tmport);
+		}
+		else
+		{
+		   outb(synn[j++],tmport);
+		}
+	     }
+	  }
+	  tmport+=0x06;
+       }
+    }
+    tmport-=0x08;
+    while ((inb(tmport) & 0x80) == 0x00);
+    j=inb(tmport) & 0x0f;
+    if ( j == 0x0f )
+    {
+       goto phase_ins;
+    }
+    if ( j == 0x0a )
+    {
+       goto phase_cmds;
+    }
+    if ( j == 0x0e )
+    {
+       goto try_sync;
+    }
+    continue;
+phase_outs:
+    tmport=wkport+0x18;
+    outb(0x20,tmport);
+    tmport+=0x07;
+    while ((inb(tmport) & 0x80) == 0x00)
+    {
+      if ((inb(tmport) & 0x01) != 0x00)
+      {
+	 tmport-=0x06;
+	 outb(0x00,tmport);
+	 tmport+=0x06;
+      }
+    }
+    tmport-=0x08;
+    j=inb(tmport);
+    if ( j == 0x85 )
+    {
+       goto tar_dcons;
+    }
+    j &= 0x0f;
+    if ( j == 0x0f )
+    {
+       goto phase_ins;
+    }
+    if ( j == 0x0a )
+    {
+       goto phase_cmds;
+    }
+    if ( j == 0x0e )
+    {
+       goto phase_outs;
+    }
+    continue;
+phase_ins:
+    tmport=wkport+0x14;
+    outb(0xff,tmport);
+    tmport += 0x04;
+    outb(0x20,tmport);
+    tmport+=0x07;
+    k=0;
+phase_ins1:
+    j=inb(tmport);
+    if ((j & 0x01) != 0x00)
+    {
+       tmport-=0x06;
+       mbuf[k++]=inb(tmport);
+       tmport+=0x06;
+       goto phase_ins1;
+    }
+    if ((j & 0x80) == 0x00)
+    {
+       goto phase_ins1;
+    }
+    tmport-=0x08;
+    while ((inb(tmport) & 0x80) == 0x00);
+    j=inb(tmport);
+    if ( j == 0x85 )
+    {
+       goto tar_dcons;
+    }
+    j &= 0x0f;
+    if ( j == 0x0f )
+    {
+       goto phase_ins;
+    }
+    if ( j == 0x0a )
+    {
+       goto phase_cmds;
+    }
+    if ( j == 0x0e )
+    {
+       goto phase_outs;
+    }
+    continue;
+phase_cmds:
+    tmport=wkport+0x10;
+    outb(0x30,tmport);
+tar_dcons:
+    tmport=wkport+0x14;
+    outb(0x00,tmport);
+    tmport+=0x04;
+    outb(0x08,tmport);
+    tmport+=0x07;
+    while ((inb(tmport) & 0x80) == 0x00);
+    tmport-=0x08;
+    j=inb(tmport);
+    if ( j != 0x16 )
+    {
+       continue;
+    }
+    if ( mbuf[0] != 0x01 )
+    {
+       continue;
+    }
+    if ( mbuf[1] != 0x03 )
+    {
+       continue;
+    }
+    if ( mbuf[4] == 0x00 )
+    {
+       continue;
+    }
+    if ( mbuf[3] > 0x64 )
+    {
+       continue;
+    }
+    if ( mbuf[4] > 0x0c )
+    {
+       mbuf[4]=0x0c;
+    }
+    devspu[host][i] = mbuf[4];
+    if ((mbuf[3] < 0x0d) && (rmb == 0))
+    {
+       j=0xa0;
+       goto set_syn_ok;
+    }
+    if ( mbuf[3] < 0x1a )
+    {
+       j=0x20;
+       goto set_syn_ok;
+    }
+    if ( mbuf[3] < 0x33 )
+    {
+       j=0x40;
+       goto set_syn_ok;
+    }
+    if ( mbuf[3] < 0x4c )
+    {
+       j=0x50;
+       goto set_syn_ok;
+    }
+    j=0x60;
+set_syn_ok:
+    devspu[host][i] = (devspu[host][i] & 0x0f) | j;
+   }
+   tmport=wkport+0x3a;
+   outb((unsigned char)(inb(tmport) & 0xef),tmport);
+}
+
+/* return non-zero on detection */
+int atp870u_detect(Scsi_Host_Template * tpnt)
+{
+    unsigned char irq,h,k;
+    unsigned long flags;
+    unsigned int base_io,error,tmport;
+    unsigned short index = 0;
+    unsigned char pci_bus[3], pci_device_fn[3], chip_ver[3],host_id;
+    struct Scsi_Host * shpnt = NULL;
+    int count = 0;
+    static unsigned short devid[7]={0x8002,0x8010,0x8020,0x8030,0x8040,0x8050,0};
+
+    printk("aec671x_detect: \n");
+    if (!pcibios_present())
+    {
+       printk("   NO BIOS32 SUPPORT.\n");
+       return count;
+    }
+
+    tpnt->proc_dir = &proc_scsi_atp870u;
+
+    for ( h = 0 ; h < 2 ; h++ )
+    {
+      active_idu[h]=0;
+      wide_idu[h]=0;
+      host_idu[h]=0x07;
+      quhdu[h]=0;
+      quendu[h]=0;
+      pci_bus[h]=0;
+      pci_device_fn[h]=0xff;
+      chip_ver[h]=0;
+      last_cmd[h]=0xff;
+      in_snd[h]=0;
+      in_int[h]=0;
+      for ( k = 0 ; k < qcnt ; k++ )
+      {
+	  querequ[h][k]=0;
+      }
+      for ( k = 0 ; k < 16 ; k++ )
+      {
+	  curr_req[h][k]=0;
+      }
+    }
+    h=0;
+    while ( devid[h] != 0 )
+    {
+       if (pcibios_find_device(0x1191,devid[h],index,&pci_bus[2],&pci_device_fn[2]))
+       {
+	  h++;
+	  index=0;
+	  continue;
+       }
+       chip_ver[2]=0;
+       if ( devid[h] == 0x8002 )
+       {
+	  error = pcibios_read_config_byte(pci_bus[2],pci_device_fn[2],0x08,&chip_ver[2]);
+	  if ( chip_ver[2] < 2 )
+	  {
+	     goto nxt_devfn;
+	  }
+       }
+       if ( devid[h] == 0x8010 )
+       {
+	  chip_ver[2]=0x04;
+       }
+       if ( pci_device_fn[2] < pci_device_fn[0] )
+       {
+	  pci_bus[1]=pci_bus[0];
+	  pci_device_fn[1]=pci_device_fn[0];
+	  chip_ver[1]=chip_ver[0];
+	  pci_bus[0]=pci_bus[2];
+	  pci_device_fn[0]=pci_device_fn[2];
+	  chip_ver[0]=chip_ver[2];
+       }
+       else if ( pci_device_fn[2] < pci_device_fn[1] )
+       {
+	  pci_bus[1]=pci_bus[2];
+	  pci_device_fn[1]=pci_device_fn[2];
+	  chip_ver[1]=chip_ver[2];
+       }
+nxt_devfn:
+       index++;
+       if ( index > 3 )
+       {
+	  index=0;
+	  h++;
+       }
+    }
+    for ( h=0; h < 2; h++ )
+    {
+    if ( pci_device_fn[h] == 0xff )
+    {
+       return count;
+    }
+    /* Found an atp870u/w. */
+    error = pcibios_read_config_dword(pci_bus[h],pci_device_fn[h],0x10,&base_io);
+    error += pcibios_read_config_byte(pci_bus[h],pci_device_fn[h],0x3c,&irq);
+    error += pcibios_read_config_byte(pci_bus[h],pci_device_fn[h],0x49,&host_id);
+
+    base_io &= 0xfffffff8;
+    printk("   ACARD AEC-671X PCI Ultra/W SCSI-3 Host Adapter: %d    IO:%x, IRQ:%d.\n"
+			     ,h,base_io,irq);
+    ioportu[h]=base_io;
+    pciportu[h]=base_io + 0x20;
+    irqnumu[h]=irq;
+    host_id &= 0x07;
+    host_idu[h]=host_id;
+    chip_veru[h]=chip_ver[h];
+
+    tmport=base_io+0x22;
+    scam_on[h]=inb(tmport);
+    tmport += 0x0b;
+    global_map[h]=inb(tmport++);
+    ultra_map[h]=inw(tmport);
+    if ( ultra_map[h] == 0 )
+    {
+       scam_on[h]=0x00;
+       global_map[h]=0x20;
+       ultra_map[h]=0xffff;
+    }
+
+    shpnt = scsi_register(tpnt,4);
+
+    save_flags(flags);
+    cli();
+    if (request_irq(irq,atp870u_intr_handle, 0, "atp870u", NULL))
+    {
+       printk("Unable to allocate IRQ for Acard controller.\n");
+       goto unregister;
+    }
+
+    tmport=base_io+0x3a;
+    k=(inb(tmport) & 0xf3) | 0x10;
+    outb(k,tmport);
+    outb((k & 0xdf),tmport);
+    mydlyu(0x8000);
+    outb(k,tmport);
+    mydlyu(0x8000);
+    tmport=base_io;
+    outb((host_id | 0x08),tmport);
+    tmport += 0x18;
+    outb(0,tmport);
+    tmport += 0x07;
+    while ((inb(tmport) & 0x80) == 0);
+    tmport -= 0x08;
+    inb(tmport);
+    tmport = base_io +1;
+    outb(8,tmport++);
+    outb(0x7f,tmport);
+    tmport = base_io + 0x11;
+    outb(0x20,tmport);
+
+    tscam(h);
+    is870(h,base_io);
+    tmport=base_io+0x3a;
+    outb((inb(tmport) & 0xef),tmport);
+
+    atp_host[h] = shpnt;
+    if ( chip_ver[h] == 4 )
+    {
+       shpnt->max_id = 16;
+    }
+    shpnt->this_id = host_id;
+    shpnt->unique_id = base_io;
+    shpnt->io_port = base_io;
+    shpnt->n_io_port = 0x40;  /* Number of bytes of I/O space used */
+    shpnt->irq = irq;
+    restore_flags(flags);
+    request_region(base_io, 0x40,"atp870u");  /* Register the IO ports that we use */
+    count++;
+    index++;
+    continue;
+unregister:
+    scsi_unregister(shpnt);
+    restore_flags(flags);
+    index++;
+    continue;
+    }
+
+    return count;
+}
+
+/* The abort command does not leave the device in a clean state where
+   it is available to be used again.  Until this gets worked out, we will
+   leave it commented out.  */
+
+int atp870u_abort(Scsi_Cmnd * SCpnt)
+{
+    unsigned char h,j;
+    unsigned int  tmport;
+/*    printk(" atp870u_abort: \n");   */
+    for ( h=0; h <= admaxu; h++ )
+    {
+	if ( SCpnt->host == atp_host[h] )
+	{
+	   goto find_adp;
+	}
+    }
+    panic("Abort host not found !");
+find_adp:
+    printk(" workingu=%x last_cmd=%x ",workingu[h],last_cmd[h]);
+    printk(" quhdu=%x quendu=%x ",quhdu[h],quendu[h]);
+    tmport=ioportu[h];
+    for ( j=0; j < 0x17; j++)
+    {
+	printk(" r%2x=%2x",j,inb(tmport++));
+    }
+    tmport += 0x05;
+    printk(" r1c=%2x",inb(tmport));
+    tmport += 0x03;
+    printk(" r1f=%2x in_snd=%2x ",inb(tmport),in_snd[h]);
+    tmport++;
+    printk(" r20=%2x",inb(tmport));
+    tmport += 0x02;
+    printk(" r22=%2x \n",inb(tmport));
+    return (SCSI_ABORT_SNOOZE);
+}
+
+int atp870u_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags)
+{
+    unsigned char h;
+    /*
+     * See if a bus reset was suggested.
+     */
+/*    printk("atp870u_reset: \n");    */
+    for( h=0; h <= admaxu; h++ )
+    {
+       if ( SCpnt->host == atp_host[h] )
+       {
+	  goto find_host;
+       }
+    }
+    panic("Reset bus host not found !");
+find_host:
+/*    SCpnt->result = 0x00080000;
+    SCpnt->scsi_done(SCpnt);
+    workingu[h]=0;
+    quhdu[h]=0;
+    quendu[h]=0;
+    return (SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET);  */
+    return (SCSI_RESET_SNOOZE);
+}
+
+const char *
+atp870u_info(struct Scsi_Host *notused)
+{
+  static char buffer[128];
+
+  strcpy(buffer, "ACARD AEC-6710/6712 PCI Ultra/W SCSI-3 Adapter Driver V1.0 ");
+
+  return buffer;
+}
+
+int
+atp870u_set_info(char *buffer, int length, struct Scsi_Host *HBAptr)
+{
+  return (-ENOSYS);  /* Currently this is a no-op */
+}
+
+#define BLS buffer + len + size
+int
+atp870u_proc_info(char *buffer, char **start, off_t offset, int length,
+    int hostno, int inout)
+{
+  struct Scsi_Host *HBAptr;
+  static u8 buff[512];
+  int	i;
+  int	size = 0;
+  int	len = 0;
+  off_t begin = 0;
+  off_t pos = 0;
+
+  HBAptr = NULL;
+  for (i = 0; i < 2; i++)
+  {
+    if ((HBAptr = atp_host[i]) != NULL)
+    {
+      if (HBAptr->host_no == hostno)
+      {
+	break;
+      }
+      HBAptr = NULL;
+    }
+  }
+
+  if (HBAptr == NULL)
+  {
+    size += sprintf(BLS, "Can't find adapter for host number %d\n", hostno);
+    len += size; pos = begin + len; size = 0;
+    goto stop_output;
+  }
+
+  if (inout == TRUE) /* Has data been written to the file? */
+  {
+    return (atp870u_set_info(buffer, length, HBAptr));
+  }
+
+  if (offset == 0)
+  {
+    memset(buff, 0, sizeof(buff));
+  }
+
+  size += sprintf(BLS, "ACARD AEC-671X Driver Version: 1.0\n");
+  len += size; pos = begin + len; size = 0;
+
+  size += sprintf(BLS, "\n");
+  size += sprintf(BLS, "Adapter Configuration:\n");
+  size += sprintf(BLS, "               Base IO: %#.4x\n", HBAptr->io_port);
+  size += sprintf(BLS, "                   IRQ: %d\n", HBAptr->irq);
+  len += size; pos = begin + len; size = 0;
+
+stop_output:
+  *start = buffer + (offset - begin);	/* Start of wanted data */
+  len -= (offset - begin);	/* Start slop */
+  if (len > length)
+  {
+    len = length;		/* Ending slop */
+  }
+
+  return (len);
+}
+
+#include "sd.h"
+
+int atp870u_biosparam(Scsi_Disk * disk, kdev_t dev, int * ip)
+{
+  int heads, sectors, cylinders;
+
+  heads = 64;
+  sectors = 32;
+  cylinders = disk->capacity / (heads * sectors);
+
+  if ( cylinders > 1024 )
+  {
+    heads = 255;
+    sectors = 63;
+    cylinders = disk->capacity / (heads * sectors);
+  }
+
+  ip[0] = heads;
+  ip[1] = sectors;
+  ip[2] = cylinders;
+
+  return 0;
+}
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = ATP870U;
+
+#include "scsi_module.c"
+#endif
+
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/atp870u.h linux/drivers/scsi/atp870u.h
--- linux.vanilla/drivers/scsi/atp870u.h	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/atp870u.h	Fri Dec 11 19:15:54 1998
@@ -0,0 +1,64 @@
+#ifndef _ATP870U_H
+
+/* $Id: atp870u.h,v 1.0 1997/05/07 15:09:00 root Exp root $
+ *
+ * Header file for the ACARD 870U/W driver for Linux
+ *
+ * $Log: atp870u.h,v $
+ * Revision 1.0  1997/05/07  15:09:00  root
+ * Initial revision
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+/* I/O Port */
+
+#define MAX_CDB 12
+#define MAX_SENSE 14
+
+int atp870u_detect(Scsi_Host_Template *);
+int atp870u_command(Scsi_Cmnd *);
+int atp870u_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int atp870u_abort(Scsi_Cmnd *);
+int atp870u_reset(Scsi_Cmnd *, unsigned int);
+int atp870u_biosparam(Disk *, kdev_t, int*);
+void send_s870(unsigned char);
+
+#define qcnt            32
+#define ATP870U_SCATTER 127
+#define ATP870U_CMDLUN 1
+
+#ifndef NULL
+        #define NULL 0
+#endif
+
+extern struct proc_dir_entry proc_scsi_atp870u;
+
+extern const char *atp870u_info(struct Scsi_Host *);
+
+extern int atp870u_proc_info(char *, char **, off_t, int, int, int);
+
+#define ATP870U {  NULL, NULL,                          \
+                     &proc_scsi_atp870u,/* proc_dir_entry */ \
+                     atp870u_proc_info,                 \
+                     NULL,                              \
+                     atp870u_detect,                    \
+                     NULL,                              \
+                     atp870u_info,                      \
+                     atp870u_command,                   \
+                     atp870u_queuecommand,              \
+                     atp870u_abort,                     \
+                     atp870u_reset,                     \
+                     NULL,                              \
+                     atp870u_biosparam,                 \
+                     qcnt,                              \
+                     7,                                 \
+                     ATP870U_SCATTER,                   \
+                     ATP870U_CMDLUN,                    \
+                     0,                                 \
+                     0,                                 \
+                     ENABLE_CLUSTERING}
+
+#endif
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/dc390.h linux/drivers/scsi/dc390.h
--- linux.vanilla/drivers/scsi/dc390.h	Sun Jun 21 18:41:18 1998
+++ linux/drivers/scsi/dc390.h	Fri Mar 26 17:14:47 1999
@@ -4,143 +4,55 @@
  *	Description: Device Driver for Tekram DC-390(T) PCI SCSI       *
  *		     Bus Master Host Adapter			       *
  ***********************************************************************/
-
-/* Kernel version autodetection */
+/* $Id: dc390.h,v 2.12 1998/12/25 17:33:27 garloff Exp $ */
 
 #include <linux/version.h>
-/* Convert Linux Version, Patch-level, Sub-level to LINUX_VERSION_CODE. */
-#define ASC_LINUX_VERSION(V, P, S)	(((V) * 65536) + ((P) * 256) + (S))
-
-#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,50)
-#define VERSION_ELF_1_2_13
-#elseif LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,95)
-#define VERSION_1_3_85
-#else
-#define VERSION_2_0_0
-#endif
 
 /*
- * AMD 53C974 driver, header file
+ * DC390/AMD 53C974 driver, header file
  */
 
 #ifndef DC390_H
 #define DC390_H
 
+#define DC390_BANNER "Tekram DC390/AM53C974"
+#define DC390_VERSION "2.0d 1998/12/25"
+
 #if defined(HOSTS_C) || defined(MODULE)
 
-#ifdef	VERSION_2_0_0
 #include <scsi/scsicam.h>
-#else
-#include <linux/scsicam.h>
-#endif
 
 extern int DC390_detect(Scsi_Host_Template *psht);
 extern int DC390_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *));
 extern int DC390_abort(Scsi_Cmnd *cmd);
-
-#ifdef	VERSION_2_0_0
 extern int DC390_reset(Scsi_Cmnd *cmd, unsigned int resetFlags);
-#else
-extern int DC390_reset(Scsi_Cmnd *cmd);
-#endif
-
-#ifdef	VERSION_ELF_1_2_13
-extern int DC390_bios_param(Disk *disk, int devno, int geom[]);
-#else
 extern int DC390_bios_param(Disk *disk, kdev_t devno, int geom[]);
-#endif
 
 #ifdef MODULE
 static int DC390_release(struct Scsi_Host *);
 #else
-#define DC390_release NULL
-#endif
-
-#ifndef VERSION_ELF_1_2_13
-extern struct proc_dir_entry proc_scsi_tmscsim;
-extern int tmscsim_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int inout);
-#endif
-
-#ifdef	VERSION_2_0_0
-
-#define DC390_T    {			\
-	NULL,	/* *next */		\
-	NULL,	/* *usage_count */	\
-	&proc_scsi_tmscsim,	/* *proc_dir */ 	\
-	tmscsim_proc_info,	/* (*proc_info)() */	\
-	"Tekram DC390(T) V1.11 Feb-05-1997",  /* *name */ \
-	DC390_detect,			\
-	DC390_release,	/* (*release)() */	\
-	NULL,	/* *(*info)() */	\
-	NULL,	/* (*command)() */	\
-	DC390_queue_command,	\
-	DC390_abort,		\
-	DC390_reset,		\
-	NULL, /* slave attach */\
-	DC390_bios_param,	\
-	10,/* can queue(-1) */	\
-	7, /* id(-1) */ 	\
-	SG_ALL, 		\
-	2, /* cmd per lun(2) */ \
-	0, /* present */	\
-	0, /* unchecked isa dma */ \
-	DISABLE_CLUSTERING	\
-	}
+# define DC390_release NULL
 #endif
 
+extern struct proc_dir_entry DC390_proc_scsi_tmscsim;
+extern int DC390_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int inout);
 
-#ifdef	VERSION_1_3_85
-
-#define DC390_T    {			\
-	NULL,	/* *next */		\
-	NULL,	/* *usage_count */	\
-	&proc_scsi_tmscsim,	/* *proc_dir */ 	\
-	tmscsim_proc_info,	/* (*proc_info)() */	\
-	"Tekram DC390(T) V1.11 Feb-05-1997",  /* *name */ \
-	DC390_detect,			\
-	DC390_release,	/* (*release)() */	\
-	NULL,	/* *(*info)() */	\
-	NULL,	/* (*command)() */	\
-	DC390_queue_command,	\
-	DC390_abort,		\
-	DC390_reset,		\
-	NULL, /* slave attach */\
-	DC390_bios_param,	\
-	10,/* can queue(-1) */	\
-	7, /* id(-1) */ 	\
-	SG_ALL, 		\
-	2, /* cmd per lun(2) */ \
-	0, /* present */	\
-	0, /* unchecked isa dma */ \
-	DISABLE_CLUSTERING	\
-	}
-#endif
-
-
-#ifdef	VERSION_ELF_1_2_13
-
-#define DC390_T     {		\
-	NULL,			\
-	NULL,			\
-	"Tekram DC390(T) V1.11 Feb-05-1997",\
-	DC390_detect,		\
-	DC390_release,			\
-	NULL, /* info */	\
-	NULL, /* command, deprecated */ \
-	DC390_queue_command,	\
-	DC390_abort,		\
-	DC390_reset,		\
-	NULL, /* slave attach */\
-	DC390_bios_param,	\
-	10,/* can queue(-1) */	\
-	7, /* id(-1) */ 	\
-	16,/* old (SG_ALL) */	\
-	2, /* cmd per lun(2) */ \
-	0, /* present */	\
-	0, /* unchecked isa dma */ \
-	DISABLE_CLUSTERING	\
-	}
-#endif
+#define DC390_T    {					\
+   proc_dir:       &DC390_proc_scsi_tmscsim,		\
+   proc_info:      DC390_proc_info,			\
+   name:           DC390_BANNER " V" DC390_VERSION,	\
+   detect:         DC390_detect,			\
+   release:        DC390_release,			\
+   queuecommand:   DC390_queue_command,			\
+   abort:          DC390_abort,				\
+   reset:          DC390_reset,				\
+   bios_param:     DC390_bios_param,			\
+   can_queue:      17,					\
+   this_id:        7,					\
+   sg_tablesize:   SG_ALL,				\
+   cmd_per_lun:    8,					\
+   use_clustering: DISABLE_CLUSTERING			\
+   }
 
 #endif /* defined(HOSTS_C) || defined(MODULE) */
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- linux.vanilla/drivers/scsi/hosts.c	Sun Dec  6 00:14:43 1998
+++ linux/drivers/scsi/hosts.c	Mon Mar 22 00:23:12 1999
@@ -153,6 +153,10 @@
 #include "NCR53c406a.h"
 #endif
 
+#ifdef CONFIG_SCSI_SYM53C416 
+#include "sym53c416.h" 
+#endif
+
 #ifdef CONFIG_SCSI_DC390T
 #include "dc390.h"
 #endif
@@ -169,6 +173,10 @@
 #include "ppa.h"
 #endif
 
+#ifdef CONFIG_SCSI_ACARD
+#include "atp870u.h"
+#endif
+
 #ifdef CONFIG_SCSI_SUNESP
 #include "esp.h"
 #endif
@@ -181,6 +189,30 @@
 #include "gdth.h"
 #endif
 
+#ifdef CONFIG_SCSI_INITIO
+#include "ini9100u.h"
+#endif
+
+#ifdef CONFIG_SCSI_TC2550
+#include "tripace.h"
+#endif
+
+#ifdef CONFIG_SCSI_INIA100
+#include "inia100.h"
+#endif
+
+#ifdef CONFIG_SCSI_PCI2000
+#include "pci2000.h"
+#endif
+
+#ifdef CONFIG_SCSI_PCI2220I
+#include "pci2220i.h"
+#endif
+
+#ifdef CONFIG_SCSI_PSI240I
+#include "psi240i.h"
+#endif
+
 #ifdef CONFIG_SCSI_DEBUG
 #include "scsi_debug.h"
 #endif
@@ -274,6 +306,9 @@
 #ifdef CONFIG_SCSI_NCR53C406A	/* 53C406A should come before QLOGIC */
     NCR53c406a,
 #endif
+#ifdef CONFIG_SCSI_SYM53C416 
+    SYM53C416, 
+#endif 
 #ifdef CONFIG_SCSI_QLOGIC_FAS
     QLOGICFAS,
 #endif
@@ -319,14 +354,32 @@
 #ifdef CONFIG_SCSI_MEGARAID
     MEGARAID,
 #endif
+#ifdef CONFIG_SCSI_INIA100
+    INIA100,
+#endif
 #ifdef CONFIG_SCSI_PPA
     PPA,
 #endif
+#ifdef CONFIG_SCSI_ACARD
+    ATP870U,
+#endif
 #ifdef CONFIG_SCSI_SUNESP
     SCSI_SPARC_ESP,
 #endif
 #ifdef CONFIG_SCSI_GDTH
     GDTH,
+#endif
+#ifdef CONFIG_SCSI_INITIO
+    INI9100U,
+#endif
+#ifdef CONFIG_SCSI_PCI2000
+    PCI2000,
+#endif
+#ifdef CONFIG_SCSI_PCI2220I
+    PCI2220I,
+#endif
+#ifdef CONFIG_SCSI_PSI240I
+    PSI240I,
 #endif
 #ifdef CONFIG_BLK_DEV_IDESCSI
     IDESCSI,
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/i60uscsi.c linux/drivers/scsi/i60uscsi.c
--- linux.vanilla/drivers/scsi/i60uscsi.c	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/i60uscsi.c	Mon Mar  1 21:53:35 1999
@@ -0,0 +1,956 @@
+/**************************************************************************
+ * Initio A100 device driver for Linux.
+ *
+ * Copyright (c) 1994-1998 Initio Corporation
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *************************************************************************
+ *
+ * module: i60uscsi.c 
+ * DESCRIPTION:
+ * 	This is the Linux low-level SCSI driver for Initio INIA100 SCSI host
+ * adapters
+ *
+ * 07/02/98 hl	- v.91n Initial drivers.
+ * 09/14/98 hl - v1.01 Support new Kernel.
+ * 09/22/98 hl - v1.01a Support reset.
+ * 09/24/98 hl - v1.01b Fixed reset.
+ * 10/05/98 hl - v1.02 split the source code and release.
+ * 12/19/98 bv - v1.02a Use spinlocks for 2.1.95 and up
+ **************************************************************************/
+
+#ifndef CVT_LINUX_VERSION
+#define CVT_LINUX_VERSION(V,P,S)        (V * 65536 + P * 256 + S)
+#endif
+
+#include <linux/sched.h>
+#include <asm/io.h>
+#include "i60uscsi.h"
+
+
+/* ---- INTERNAL FUNCTIONS ---- */
+static UCHAR waitChipReady(ORC_HCS * hcsp);
+static UCHAR waitFWReady(ORC_HCS * hcsp);
+static UCHAR waitFWReady(ORC_HCS * hcsp);
+static UCHAR waitSCSIRSTdone(ORC_HCS * hcsp);
+static UCHAR waitHDOoff(ORC_HCS * hcsp);
+static UCHAR waitHDIset(ORC_HCS * hcsp, UCHAR * pData);
+static unsigned short get_FW_version(ORC_HCS * hcsp);
+static UCHAR set_NVRAM(ORC_HCS * hcsp, unsigned char address, unsigned char value);
+static UCHAR get_NVRAM(ORC_HCS * hcsp, unsigned char address, unsigned char *pDataIn);
+static int se2_rd_all(ORC_HCS * hcsp);
+static void se2_update_all(ORC_HCS * hcsp);	/* setup default pattern        */
+static void read_eeprom(ORC_HCS * hcsp);
+static UCHAR load_FW(ORC_HCS * hcsp);
+static void setup_SCBs(ORC_HCS * hcsp);
+static void initAFlag(ORC_HCS * hcsp);
+ORC_SCB *orc_alloc_scb(ORC_HCS * hcsp);
+
+/* ---- EXTERNAL FUNCTIONS ---- */
+extern void inia100SCBPost(BYTE * pHcb, BYTE * pScb);
+
+/* ---- INTERNAL VARIABLES ---- */
+ORC_HCS orc_hcs[MAX_SUPPORTED_ADAPTERS];
+static INIA100_ADPT_STRUCT inia100_adpt[MAX_SUPPORTED_ADAPTERS];
+/* set by inia100_setup according to the command line */
+int orc_num_scb;
+
+NVRAM nvram, *nvramp = &nvram;
+static UCHAR dftNvRam[64] =
+{
+/*----------header -------------*/
+	0x01,			/* 0x00: Sub System Vendor ID 0 */
+	0x11,			/* 0x01: Sub System Vendor ID 1 */
+	0x60,			/* 0x02: Sub System ID 0        */
+	0x10,			/* 0x03: Sub System ID 1        */
+	0x00,			/* 0x04: SubClass               */
+	0x01,			/* 0x05: Vendor ID 0            */
+	0x11,			/* 0x06: Vendor ID 1            */
+	0x60,			/* 0x07: Device ID 0            */
+	0x10,			/* 0x08: Device ID 1            */
+	0x00,			/* 0x09: Reserved               */
+	0x00,			/* 0x0A: Reserved               */
+	0x01,			/* 0x0B: Revision of Data Structure     */
+				/* -- Host Adapter Structure --- */
+	0x01,			/* 0x0C: Number Of SCSI Channel */
+	0x01,			/* 0x0D: BIOS Configuration 1   */
+	0x00,			/* 0x0E: BIOS Configuration 2   */
+	0x00,			/* 0x0F: BIOS Configuration 3   */
+				/* --- SCSI Channel 0 Configuration --- */
+	0x07,			/* 0x10: H/A ID                 */
+	0x83,			/* 0x11: Channel Configuration  */
+	0x20,			/* 0x12: MAX TAG per target     */
+	0x0A,			/* 0x13: SCSI Reset Recovering time     */
+	0x00,			/* 0x14: Channel Configuration4 */
+	0x00,			/* 0x15: Channel Configuration5 */
+				/* SCSI Channel 0 Target Configuration  */
+				/* 0x16-0x25                    */
+	0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
+	0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
+				/* --- SCSI Channel 1 Configuration --- */
+	0x07,			/* 0x26: H/A ID                 */
+	0x83,			/* 0x27: Channel Configuration  */
+	0x20,			/* 0x28: MAX TAG per target     */
+	0x0A,			/* 0x29: SCSI Reset Recovering time     */
+	0x00,			/* 0x2A: Channel Configuration4 */
+	0x00,			/* 0x2B: Channel Configuration5 */
+				/* SCSI Channel 1 Target Configuration  */
+				/* 0x2C-0x3B                    */
+	0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
+	0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
+	0x00,			/* 0x3C: Reserved               */
+	0x00,			/* 0x3D: Reserved               */
+	0x00,			/* 0x3E: Reserved               */
+	0x00			/* 0x3F: Checksum               */
+};
+
+
+/***************************************************************************/
+static void waitForPause(unsigned amount)
+{
+	ULONG the_time = jiffies + amount;	/* 0.01 seconds per jiffy */
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	while (time_before_eq(jiffies, the_time));
+#else
+	while (jiffies < the_time);
+#endif
+}
+
+/***************************************************************************/
+UCHAR waitChipReady(ORC_HCS * hcsp)
+{
+	int i;
+
+	for (i = 0; i < 2000; i++) {	/* Wait 1 second for report timeout     */
+		if (ORC_RD(hcsp->HCS_Base, ORC_HCTRL) & HOSTSTOP)	/* Wait HOSTSTOP set */
+			return (TRUE);
+		waitForPause(5);	/* wait 500ms before try again  */
+	}
+	return (FALSE);
+}
+
+/***************************************************************************/
+UCHAR waitFWReady(ORC_HCS * hcsp)
+{
+	int i;
+
+	for (i = 0; i < 2000; i++) {	/* Wait 1 second for report timeout     */
+		if (ORC_RD(hcsp->HCS_Base, ORC_HSTUS) & RREADY)		/* Wait READY set */
+			return (TRUE);
+		waitForPause(5);	/* wait 500ms before try again  */
+	}
+	return (FALSE);
+}
+
+/***************************************************************************/
+UCHAR waitSCSIRSTdone(ORC_HCS * hcsp)
+{
+	int i;
+
+	for (i = 0; i < 2000; i++) {	/* Wait 1 second for report timeout     */
+		if (!(ORC_RD(hcsp->HCS_Base, ORC_HCTRL) & SCSIRST))	/* Wait SCSIRST done */
+			return (TRUE);
+		waitForPause(5);	/* wait 500ms before try again  */
+	}
+	return (FALSE);
+}
+
+/***************************************************************************/
+UCHAR waitHDOoff(ORC_HCS * hcsp)
+{
+	int i;
+
+	for (i = 0; i < 2000; i++) {	/* Wait 1 second for report timeout     */
+		if (!(ORC_RD(hcsp->HCS_Base, ORC_HCTRL) & HDO))		/* Wait HDO off */
+			return (TRUE);
+		waitForPause(5);	/* wait 500ms before try again  */
+	}
+	return (FALSE);
+}
+
+/***************************************************************************/
+UCHAR waitHDIset(ORC_HCS * hcsp, UCHAR * pData)
+{
+	int i;
+
+	for (i = 0; i < 2000; i++) {	/* Wait 1 second for report timeout     */
+		if ((*pData = ORC_RD(hcsp->HCS_Base, ORC_HSTUS)) & HDI)
+			return (TRUE);	/* Wait HDI set */
+		waitForPause(5);	/* wait 500ms before try again  */
+	}
+	return (FALSE);
+}
+
+/***************************************************************************/
+unsigned short get_FW_version(ORC_HCS * hcsp)
+{
+	UCHAR bData;
+	union {
+		unsigned short sVersion;
+		unsigned char cVersion[2];
+	} Version;
+
+	ORC_WR(hcsp->HCS_Base + ORC_HDATA, ORC_CMD_VERSION);
+	ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);
+	if (waitHDOoff(hcsp) == FALSE)	/* Wait HDO off   */
+		return (FALSE);
+
+	if (waitHDIset(hcsp, &bData) == FALSE)	/* Wait HDI set   */
+		return (FALSE);
+	Version.cVersion[0] = ORC_RD(hcsp->HCS_Base, ORC_HDATA);
+	ORC_WR(hcsp->HCS_Base + ORC_HSTUS, bData);	/* Clear HDI            */
+
+	if (waitHDIset(hcsp, &bData) == FALSE)	/* Wait HDI set   */
+		return (FALSE);
+	Version.cVersion[1] = ORC_RD(hcsp->HCS_Base, ORC_HDATA);
+	ORC_WR(hcsp->HCS_Base + ORC_HSTUS, bData);	/* Clear HDI            */
+
+	return (Version.sVersion);
+}
+
+/***************************************************************************/
+UCHAR set_NVRAM(ORC_HCS * hcsp, unsigned char address, unsigned char value)
+{
+	ORC_WR(hcsp->HCS_Base + ORC_HDATA, ORC_CMD_SET_NVM);	/* Write command */
+	ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);
+	if (waitHDOoff(hcsp) == FALSE)	/* Wait HDO off   */
+		return (FALSE);
+
+	ORC_WR(hcsp->HCS_Base + ORC_HDATA, address);	/* Write address */
+	ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);
+	if (waitHDOoff(hcsp) == FALSE)	/* Wait HDO off   */
+		return (FALSE);
+
+	ORC_WR(hcsp->HCS_Base + ORC_HDATA, value);	/* Write value  */
+	ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);
+	if (waitHDOoff(hcsp) == FALSE)	/* Wait HDO off   */
+		return (FALSE);
+
+	return (TRUE);
+}
+
+/***************************************************************************/
+UCHAR get_NVRAM(ORC_HCS * hcsp, unsigned char address, unsigned char *pDataIn)
+{
+	unsigned char bData;
+
+	ORC_WR(hcsp->HCS_Base + ORC_HDATA, ORC_CMD_GET_NVM);	/* Write command */
+	ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);
+	if (waitHDOoff(hcsp) == FALSE)	/* Wait HDO off   */
+		return (FALSE);
+
+	ORC_WR(hcsp->HCS_Base + ORC_HDATA, address);	/* Write address */
+	ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);
+	if (waitHDOoff(hcsp) == FALSE)	/* Wait HDO off   */
+		return (FALSE);
+
+	if (waitHDIset(hcsp, &bData) == FALSE)	/* Wait HDI set   */
+		return (FALSE);
+	*pDataIn = ORC_RD(hcsp->HCS_Base, ORC_HDATA);
+	ORC_WR(hcsp->HCS_Base + ORC_HSTUS, bData);	/* Clear HDI    */
+
+	return (TRUE);
+}
+
+/***************************************************************************/
+void orc_exec_scb(ORC_HCS * hcsp, ORC_SCB * scbp)
+{
+	scbp->SCB_Status = SCB_POST;
+	ORC_WR(hcsp->HCS_Base + ORC_PQUEUE, scbp->SCB_ScbIdx);
+	return;
+}
+
+
+/***********************************************************************
+ Read SCSI H/A configuration parameters from serial EEPROM
+************************************************************************/
+int se2_rd_all(ORC_HCS * hcsp)
+{
+	int i;
+	UCHAR *np, chksum = 0;
+
+	np = (UCHAR *) nvramp;
+	for (i = 0; i < 64; i++, np++) {	/* <01> */
+		if (get_NVRAM(hcsp, (unsigned char) i, np) == FALSE)
+			return -1;
+//      *np++ = get_NVRAM(hcsp, (unsigned char ) i);
+	}
+
+/*------ Is ckecksum ok ? ------*/
+	np = (UCHAR *) nvramp;
+	for (i = 0; i < 63; i++)
+		chksum += *np++;
+
+	if (nvramp->CheckSum != (UCHAR) chksum)
+		return -1;
+	return 1;
+}
+
+/************************************************************************
+ Update SCSI H/A configuration parameters from serial EEPROM
+*************************************************************************/
+void se2_update_all(ORC_HCS * hcsp)
+{				/* setup default pattern  */
+	int i;
+	UCHAR *np, *np1, chksum = 0;
+
+	/* Calculate checksum first   */
+	np = (UCHAR *) dftNvRam;
+	for (i = 0; i < 63; i++)
+		chksum += *np++;
+	*np = chksum;
+
+	np = (UCHAR *) dftNvRam;
+	np1 = (UCHAR *) nvramp;
+	for (i = 0; i < 64; i++, np++, np1++) {
+		if (*np != *np1) {
+			set_NVRAM(hcsp, (unsigned char) i, *np);
+		}
+	}
+	return;
+}
+
+/*************************************************************************
+ Function name  : read_eeprom
+**************************************************************************/
+void read_eeprom(ORC_HCS * hcsp)
+{
+	if (se2_rd_all(hcsp) != 1) {
+		se2_update_all(hcsp);	/* setup default pattern        */
+		se2_rd_all(hcsp);	/* load again                   */
+	}
+}
+
+
+/***************************************************************************/
+UCHAR load_FW(ORC_HCS * hcsp)
+{
+	U32 dData;
+	USHORT wBIOSAddress;
+	USHORT i;
+	UCHAR *pData, bData;
+
+
+	bData = ORC_RD(hcsp->HCS_Base, ORC_GCFG);
+	ORC_WR(hcsp->HCS_Base + ORC_GCFG, bData | EEPRG);	/* Enable EEPROM programming */
+	ORC_WR(hcsp->HCS_Base + ORC_EBIOSADR2, 0x00);
+	ORC_WRSHORT(hcsp->HCS_Base + ORC_EBIOSADR0, 0x00);
+	if (ORC_RD(hcsp->HCS_Base, ORC_EBIOSDATA) != 0x55) {
+		ORC_WR(hcsp->HCS_Base + ORC_GCFG, bData);	/* Disable EEPROM programming */
+		return (FALSE);
+	}
+	ORC_WRSHORT(hcsp->HCS_Base + ORC_EBIOSADR0, 0x01);
+	if (ORC_RD(hcsp->HCS_Base, ORC_EBIOSDATA) != 0xAA) {
+		ORC_WR(hcsp->HCS_Base + ORC_GCFG, bData);	/* Disable EEPROM programming */
+		return (FALSE);
+	}
+	ORC_WR(hcsp->HCS_Base + ORC_RISCCTL, PRGMRST | DOWNLOAD);	/* Enable SRAM programming */
+	pData = (UCHAR *) & dData;
+	dData = 0;		/* Initial FW address to 0 */
+	ORC_WRSHORT(hcsp->HCS_Base + ORC_EBIOSADR0, 0x10);
+	*pData = ORC_RD(hcsp->HCS_Base, ORC_EBIOSDATA);		/* Read from BIOS */
+	ORC_WRSHORT(hcsp->HCS_Base + ORC_EBIOSADR0, 0x11);
+	*(pData + 1) = ORC_RD(hcsp->HCS_Base, ORC_EBIOSDATA);	/* Read from BIOS */
+	ORC_WRSHORT(hcsp->HCS_Base + ORC_EBIOSADR0, 0x12);
+	*(pData + 2) = ORC_RD(hcsp->HCS_Base, ORC_EBIOSDATA);	/* Read from BIOS */
+	ORC_WR(hcsp->HCS_Base + ORC_EBIOSADR2, *(pData + 2));
+	ORC_WRLONG(hcsp->HCS_Base + ORC_FWBASEADR, dData);	/* Write FW address */
+
+	wBIOSAddress = (USHORT) dData;	/* FW code locate at BIOS address + ? */
+	for (i = 0, pData = (UCHAR *) & dData;	/* Download the code    */
+	     i < 0x1000;	/* Firmware code size = 4K      */
+	     i++, wBIOSAddress++) {
+		ORC_WRSHORT(hcsp->HCS_Base + ORC_EBIOSADR0, wBIOSAddress);
+		*pData++ = ORC_RD(hcsp->HCS_Base, ORC_EBIOSDATA);	/* Read from BIOS */
+		if ((i % 4) == 3) {
+			ORC_WRLONG(hcsp->HCS_Base + ORC_RISCRAM, dData);	/* Write every 4 bytes */
+			pData = (UCHAR *) & dData;
+		}
+	}
+
+	ORC_WR(hcsp->HCS_Base + ORC_RISCCTL, PRGMRST | DOWNLOAD);	/* Reset program count 0 */
+	wBIOSAddress -= 0x1000;	/* Reset the BIOS adddress      */
+	for (i = 0, pData = (UCHAR *) & dData;	/* Check the code       */
+	     i < 0x1000;	/* Firmware code size = 4K      */
+	     i++, wBIOSAddress++) {
+		ORC_WRSHORT(hcsp->HCS_Base + ORC_EBIOSADR0, wBIOSAddress);
+		*pData++ = ORC_RD(hcsp->HCS_Base, ORC_EBIOSDATA);	/* Read from BIOS */
+		if ((i % 4) == 3) {
+			if (ORC_RDLONG(hcsp->HCS_Base, ORC_RISCRAM) != dData) {
+				ORC_WR(hcsp->HCS_Base + ORC_RISCCTL, PRGMRST);	/* Reset program to 0 */
+				ORC_WR(hcsp->HCS_Base + ORC_GCFG, bData);	/*Disable EEPROM programming */
+				return (FALSE);
+			}
+			pData = (UCHAR *) & dData;
+		}
+	}
+	ORC_WR(hcsp->HCS_Base + ORC_RISCCTL, PRGMRST);	/* Reset program to 0   */
+	ORC_WR(hcsp->HCS_Base + ORC_GCFG, bData);	/* Disable EEPROM programming */
+	return (TRUE);
+}
+
+/***************************************************************************/
+void setup_SCBs(ORC_HCS * hcsp)
+{
+	ORC_SCB *pVirScb;
+	int i;
+	UCHAR j;
+	ESCB *pVirEscb;
+	PVOID pPhysEscb;
+	PVOID tPhysEscb;
+
+	j = 0;
+	pVirScb = NULL;
+	tPhysEscb = (PVOID) NULL;
+	pPhysEscb = (PVOID) NULL;
+	/* Setup SCB HCS_Base and SCB Size registers */
+	ORC_WR(hcsp->HCS_Base + ORC_SCBSIZE, orc_num_scb);	/* Total number of SCBs */
+	/* SCB HCS_Base address 0      */
+	ORC_WRLONG(hcsp->HCS_Base + ORC_SCBBASE0, hcsp->HCS_physScbArray);
+	/* SCB HCS_Base address 1      */
+	ORC_WRLONG(hcsp->HCS_Base + ORC_SCBBASE1, hcsp->HCS_physScbArray);
+
+	/* setup scatter list address with one buffer */
+	pVirScb = (ORC_SCB *) hcsp->HCS_virScbArray;
+	pVirEscb = (ESCB *) hcsp->HCS_virEscbArray;
+
+	for (i = 0; i < orc_num_scb; i++) {
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+		pPhysEscb = (PVOID) ((ULONG) hcsp->HCS_virEscbArray + (sizeof(ESCB) * i));
+		pVirScb->SCB_SGPAddr = (U32) VIRT_TO_BUS(pPhysEscb);
+		pVirScb->SCB_SensePAddr = (U32) VIRT_TO_BUS(pPhysEscb);
+#else
+		pPhysEscb = (PVOID) (hcsp->HCS_physEscbArray + (sizeof(ESCB) * i));
+		pVirScb->SCB_SGPAddr = (U32) pPhysEscb;
+		pVirScb->SCB_SensePAddr = (U32) pPhysEscb;
+#endif
+		pVirScb->SCB_EScb = pVirEscb;
+		pVirScb->SCB_ScbIdx = i;
+		pVirScb++;
+		pVirEscb++;
+	}
+
+	return;
+}
+
+/***************************************************************************/
+static void initAFlag(ORC_HCS * hcsp)
+{
+	UCHAR i, j;
+
+	for (i = 0; i < MAX_CHANNELS; i++) {
+		for (j = 0; j < 8; j++) {
+			hcsp->BitAllocFlag[i][j] = 0xffffffff;
+		}
+	}
+}
+
+/***************************************************************************/
+int init_orchid(ORC_HCS * hcsp)
+{
+	UBYTE *readBytep;
+	USHORT revision;
+	UCHAR i;
+
+	initAFlag(hcsp);
+	ORC_WR(hcsp->HCS_Base + ORC_GIMSK, 0xFF);	/* Disable all interrupt        */
+	if (ORC_RD(hcsp->HCS_Base, ORC_HSTUS) & RREADY) {	/* Orchid is ready              */
+		revision = get_FW_version(hcsp);
+		if (revision == 0xFFFF) {
+			ORC_WR(hcsp->HCS_Base + ORC_HCTRL, DEVRST);	/* Reset Host Adapter   */
+			if (waitChipReady(hcsp) == FALSE)
+				return (-1);
+			load_FW(hcsp);	/* Download FW                  */
+			setup_SCBs(hcsp);	/* Setup SCB HCS_Base and SCB Size registers */
+			ORC_WR(hcsp->HCS_Base + ORC_HCTRL, 0);	/* clear HOSTSTOP       */
+			if (waitFWReady(hcsp) == FALSE)
+				return (-1);
+			/* Wait for firmware ready     */
+		} else {
+			setup_SCBs(hcsp);	/* Setup SCB HCS_Base and SCB Size registers */
+		}
+	} else {		/* Orchid is not Ready          */
+		ORC_WR(hcsp->HCS_Base + ORC_HCTRL, DEVRST);	/* Reset Host Adapter   */
+		if (waitChipReady(hcsp) == FALSE)
+			return (-1);
+		load_FW(hcsp);	/* Download FW                  */
+		setup_SCBs(hcsp);	/* Setup SCB HCS_Base and SCB Size registers */
+		ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);	/* Do Hardware Reset &  */
+
+		/*     clear HOSTSTOP  */
+		if (waitFWReady(hcsp) == FALSE)		/* Wait for firmware ready      */
+			return (-1);
+	}
+
+/*------------- get serial EEProm settting -------*/
+
+	read_eeprom(hcsp);
+
+	if (nvramp->Revision != 1)
+		return (-1);
+
+	hcsp->HCS_SCSI_ID = nvramp->SCSI0Id;
+	hcsp->HCS_BIOS = nvramp->BIOSConfig1;
+	hcsp->HCS_MaxTar = MAX_TARGETS;
+	readBytep = (UCHAR *) & (nvramp->Target00Config);
+	for (i = 0; i < 16; readBytep++, i++) {
+		hcsp->TargetFlag[i] = *readBytep;
+		hcsp->MaximumTags[i] = orc_num_scb;
+	}			/* for                          */
+
+	if (nvramp->SCSI0Config & NCC_BUSRESET) {	/* Reset SCSI bus               */
+		hcsp->HCS_Flags |= HCF_SCSI_RESET;
+	}
+	ORC_WR(hcsp->HCS_Base + ORC_GIMSK, 0xFB);	/* enable RP FIFO interrupt     */
+	return (0);
+}
+
+/*****************************************************************************
+ Function name  : orc_reset_scsi_bus
+ Description    : Reset registers, reset a hanging bus and
+                  kill active and disconnected commands for target w/o soft reset
+ Input          : pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : pSRB  -       Pointer to SCSI request block.
+*****************************************************************************/
+int orc_reset_scsi_bus(ORC_HCS * pHCB)
+{				/* I need Host Control Block Information */
+	ULONG flags;
+
+#if 0
+	printk("inia100: enter inia100_reset\n");
+#endif
+
+#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(2,1,95)
+	save_flags(flags);
+	cli();
+#else
+	spin_lock_irqsave(&(pHCB->BitAllocFlagLock), flags);
+#endif
+
+	initAFlag(pHCB);
+	/* reset scsi bus */
+	ORC_WR(pHCB->HCS_Base + ORC_HCTRL, SCSIRST);
+	if (waitSCSIRSTdone(pHCB) == FALSE) {
+#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(2,1,95)
+		restore_flags(flags);
+#else
+		spin_unlock_irqrestore(&(pHCB->BitAllocFlagLock), flags);
+#endif
+		return (SCSI_RESET_ERROR);
+	} else {
+#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(2,1,95)
+		restore_flags(flags);
+#else
+		spin_unlock_irqrestore(&(pHCB->BitAllocFlagLock), flags);
+#endif
+		return (SCSI_RESET_SUCCESS);
+	}
+}
+
+/*****************************************************************************
+ Function name  : orc_device_reset
+ Description    : Reset registers, reset a hanging bus and
+                  kill active and disconnected commands for target w/o soft reset
+ Input          : pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : pSRB  -       Pointer to SCSI request block.
+*****************************************************************************/
+int orc_device_reset(ORC_HCS * pHCB, ULONG SCpnt, unsigned int target, unsigned int ResetFlags)
+{				/* I need Host Control Block Information */
+	ORC_SCB *pScb;
+	ESCB *pVirEscb;
+	ORC_SCB *pVirScb;
+	UCHAR i;
+	ULONG flags;
+
+#if 0
+	printk("inia100: enter inia100_reset\n");
+#endif
+
+#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(2,1,95)
+	save_flags(flags);
+	cli();
+#else
+	spin_lock_irqsave(&(pHCB->BitAllocFlagLock), flags);
+#endif
+	pScb = (ORC_SCB *) NULL;
+	pVirEscb = (ESCB *) NULL;
+
+	/* setup scatter list address with one buffer */
+	pVirScb = (ORC_SCB *) pHCB->HCS_virScbArray;
+
+	initAFlag(pHCB);
+	/* device reset */
+	for (i = 0; i < orc_num_scb; i++) {
+		pVirEscb = pVirScb->SCB_EScb;
+		if ((pVirScb->SCB_Status) && (pVirEscb->SCB_Srb == (unsigned char *) SCpnt))
+			break;
+		pVirScb++;
+	}
+
+	if (i == orc_num_scb) {
+		printk("Unable to Reset - No SCB Found\n");
+#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(2,1,95)
+		restore_flags(flags);
+#else
+		spin_unlock_irqrestore(&(pHCB->BitAllocFlagLock), flags);
+#endif
+		return (SCSI_RESET_NOT_RUNNING);
+	}
+	if ((pScb = orc_alloc_scb(pHCB)) == NULL) {
+#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(2,1,95)
+		restore_flags(flags);
+#else
+		spin_unlock_irqrestore(&(pHCB->BitAllocFlagLock), flags);
+#endif
+		return (SCSI_RESET_NOT_RUNNING);
+	}
+	pScb->SCB_Opcode = ORC_BUSDEVRST;
+	pScb->SCB_Target = target;
+	pScb->SCB_HaStat = 0;
+	pScb->SCB_TaStat = 0;
+	pScb->SCB_Status = 0x0;
+	pScb->SCB_Link = 0xFF;
+	pScb->SCB_Reserved0 = 0;
+	pScb->SCB_Reserved1 = 0;
+	pScb->SCB_XferLen = 0;
+	pScb->SCB_SGLen = 0;
+
+	pVirEscb->SCB_Srb = 0;
+	if (ResetFlags & SCSI_RESET_SYNCHRONOUS) {
+		pVirEscb->SCB_Srb = (unsigned char *) SCpnt;
+	}
+	orc_exec_scb(pHCB, pScb);	/* Start execute SCB            */
+#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(2,1,95)
+	restore_flags(flags);
+#else
+	spin_unlock_irqrestore(&(pHCB->BitAllocFlagLock), flags);
+#endif
+	return SCSI_RESET_PENDING;
+}
+
+
+/***************************************************************************/
+ORC_SCB *orc_alloc_scb(ORC_HCS * hcsp)
+{
+	ORC_SCB *pTmpScb;
+	UCHAR Ch;
+	ULONG idx;
+	UCHAR index;
+	UCHAR i;
+	ULONG flags;
+
+#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(2,1,95)
+	save_flags(flags);
+	cli();
+#else
+	spin_lock_irqsave(&(hcsp->BitAllocFlagLock), flags);
+#endif
+	Ch = hcsp->HCS_Index;
+	for (i = 0; i < 8; i++) {
+		for (index = 0; index < 32; index++) {
+			if ((hcsp->BitAllocFlag[Ch][i] >> index) & 0x01) {
+				hcsp->BitAllocFlag[Ch][i] &= ~(1 << index);
+				break;
+			}
+		}
+		idx = index + 32 * i;
+		pTmpScb = (PVOID) ((ULONG) hcsp->HCS_virScbArray + (idx * sizeof(ORC_SCB)));
+#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(2,1,95)
+		restore_flags(flags);
+#else
+		spin_unlock_irqrestore(&(hcsp->BitAllocFlagLock), flags);
+#endif
+		return (pTmpScb);
+	}
+#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(2,1,95)
+	restore_flags(flags);
+#else
+	spin_unlock_irqrestore(&(hcsp->BitAllocFlagLock), flags);
+#endif
+	return (NULL);
+}
+
+
+/***************************************************************************/
+void orc_release_scb(ORC_HCS * hcsp, ORC_SCB * scbp)
+{
+	ULONG flags;
+	UCHAR Index;
+	UCHAR i;
+	UCHAR Ch;
+
+#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(2,1,95)
+	save_flags(flags);
+	cli();
+#else
+	spin_lock_irqsave(&(hcsp->BitAllocFlagLock), flags);
+#endif
+	Ch = hcsp->HCS_Index;
+	Index = scbp->SCB_ScbIdx;
+	i = Index / 32;
+	Index %= 32;
+	hcsp->BitAllocFlag[Ch][i] |= (1 << Index);
+#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(2,1,95)
+	restore_flags(flags);
+#else
+	spin_unlock_irqrestore(&(hcsp->BitAllocFlagLock), flags);
+#endif
+}
+
+
+/*****************************************************************************
+ Function name	: Addinia100_into_Adapter_table
+ Description	: This function will scan PCI bus to get all Orchid card
+ Input		: None.
+ Output		: None.
+ Return		: SUCCESSFUL	- Successful scan
+ ohterwise	- No drives founded
+*****************************************************************************/
+int Addinia100_into_Adapter_table(WORD wBIOS, WORD wBASE, BYTE bInterrupt,
+				  BYTE bBus, BYTE bDevice)
+{
+	unsigned int i, j;
+
+	for (i = 0; i < MAX_SUPPORTED_ADAPTERS; i++) {
+		if (inia100_adpt[i].ADPT_BIOS < wBIOS)
+			continue;
+		if (inia100_adpt[i].ADPT_BIOS == wBIOS) {
+			if (inia100_adpt[i].ADPT_BASE == wBASE)
+				if (inia100_adpt[i].ADPT_Bus != 0xFF)
+					return (FAILURE);
+				else if (inia100_adpt[i].ADPT_BASE < wBASE)
+					continue;
+		}
+		for (j = MAX_SUPPORTED_ADAPTERS - 1; j > i; j--) {
+			inia100_adpt[j].ADPT_BASE = inia100_adpt[j - 1].ADPT_BASE;
+			inia100_adpt[j].ADPT_INTR = inia100_adpt[j - 1].ADPT_INTR;
+			inia100_adpt[j].ADPT_BIOS = inia100_adpt[j - 1].ADPT_BIOS;
+			inia100_adpt[j].ADPT_Bus = inia100_adpt[j - 1].ADPT_Bus;
+			inia100_adpt[j].ADPT_Device = inia100_adpt[j - 1].ADPT_Device;
+		}
+		inia100_adpt[i].ADPT_BASE = wBASE;
+		inia100_adpt[i].ADPT_INTR = bInterrupt;
+		inia100_adpt[i].ADPT_BIOS = wBIOS;
+		inia100_adpt[i].ADPT_Bus = bBus;
+		inia100_adpt[i].ADPT_Device = bDevice;
+		return (SUCCESSFUL);
+	}
+	return (FAILURE);
+}
+
+
+/*****************************************************************************
+ Function name	: init_inia100Adapter_table
+ Description	: This function will scan PCI bus to get all Orchid card
+ Input		: None.
+ Output		: None.
+ Return		: SUCCESSFUL	- Successful scan
+ ohterwise	- No drives founded
+*****************************************************************************/
+void init_inia100Adapter_table(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_SUPPORTED_ADAPTERS; i++) {	/* Initialize adapter structure */
+		inia100_adpt[i].ADPT_BIOS = 0xffff;
+		inia100_adpt[i].ADPT_BASE = 0xffff;
+		inia100_adpt[i].ADPT_INTR = 0xff;
+		inia100_adpt[i].ADPT_Bus = 0xff;
+		inia100_adpt[i].ADPT_Device = 0xff;
+	}
+}
+
+/*****************************************************************************
+ Function name  : get_orcPCIConfig
+ Description    : 
+ Input          : pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : pSRB  -       Pointer to SCSI request block.
+*****************************************************************************/
+void get_orcPCIConfig(ORC_HCS * pCurHcb, int ch_idx)
+{
+	pCurHcb->HCS_Base = inia100_adpt[ch_idx].ADPT_BASE;	/* Supply base address  */
+	pCurHcb->HCS_BIOS = inia100_adpt[ch_idx].ADPT_BIOS;	/* Supply BIOS address  */
+	pCurHcb->HCS_Intr = inia100_adpt[ch_idx].ADPT_INTR;	/* Supply interrupt line */
+	return;
+}
+
+
+/*****************************************************************************
+ Function name  : abort_SCB
+ Description    : Abort a queued command.
+	                 (commands that are on the bus can't be aborted easily)
+ Input          : pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : pSRB  -       Pointer to SCSI request block.
+*****************************************************************************/
+int abort_SCB(ORC_HCS * hcsp, ORC_SCB * pScb)
+{
+	unsigned char bData, bStatus;
+
+	ORC_WR(hcsp->HCS_Base + ORC_HDATA, ORC_CMD_ABORT_SCB);	/* Write command */
+	ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);
+	if (waitHDOoff(hcsp) == FALSE)	/* Wait HDO off   */
+		return (FALSE);
+
+	ORC_WR(hcsp->HCS_Base + ORC_HDATA, pScb->SCB_ScbIdx);	/* Write address */
+	ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);
+	if (waitHDOoff(hcsp) == FALSE)	/* Wait HDO off   */
+		return (FALSE);
+
+	if (waitHDIset(hcsp, &bData) == FALSE)	/* Wait HDI set   */
+		return (FALSE);
+	bStatus = ORC_RD(hcsp->HCS_Base, ORC_HDATA);
+	ORC_WR(hcsp->HCS_Base + ORC_HSTUS, bData);	/* Clear HDI    */
+
+	if (bStatus == 1)	/* 0 - Successfully               */
+		return (FALSE);	/* 1 - Fail                     */
+	return (TRUE);
+}
+
+/*****************************************************************************
+ Function name  : inia100_abort
+ Description    : Abort a queued command.
+	                 (commands that are on the bus can't be aborted easily)
+ Input          : pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : pSRB  -       Pointer to SCSI request block.
+*****************************************************************************/
+int orc_abort_srb(ORC_HCS * hcsp, ULONG SCpnt)
+{
+	ESCB *pVirEscb;
+	ORC_SCB *pVirScb;
+	UCHAR i;
+	ULONG flags;
+
+#if 0
+	printk("inia100: abort SRB \n");
+#endif
+#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(2,1,95)
+	save_flags(flags);
+	cli();
+#else
+	spin_lock_irqsave(&(hcsp->BitAllocFlagLock), flags);
+#endif
+
+	pVirScb = (ORC_SCB *) hcsp->HCS_virScbArray;
+
+	for (i = 0; i < orc_num_scb; i++, pVirScb++) {
+		pVirEscb = pVirScb->SCB_EScb;
+		if ((pVirScb->SCB_Status) && (pVirEscb->SCB_Srb == (unsigned char *) SCpnt)) {
+			if (pVirScb->SCB_TagMsg == 0) {
+#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(2,1,95)
+				restore_flags(flags);
+#else
+				spin_unlock_irqrestore(&(hcsp->BitAllocFlagLock), flags);
+#endif
+				return (SCSI_ABORT_BUSY);
+			} else {
+				if (abort_SCB(hcsp, pVirScb)) {
+					pVirEscb->SCB_Srb = NULL;
+#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(2,1,95)
+					restore_flags(flags);
+#else
+					spin_unlock_irqrestore(&(hcsp->BitAllocFlagLock), flags);
+#endif
+					return (SCSI_ABORT_SUCCESS);
+				} else {
+#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(2,1,95)
+					restore_flags(flags);
+#else
+					spin_unlock_irqrestore(&(hcsp->BitAllocFlagLock), flags);
+#endif
+					return (SCSI_ABORT_NOT_RUNNING);
+				}
+			}
+		}
+	}
+#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(2,1,95)
+	restore_flags(flags);
+#else
+	spin_unlock_irqrestore(&(hcsp->BitAllocFlagLock), flags);
+#endif
+	return (SCSI_ABORT_NOT_RUNNING);
+}
+
+/***********************************************************************
+ Routine Description:
+	  This is the interrupt service routine for the Orchid SCSI adapter.
+	  It reads the interrupt register to determine if the adapter is indeed
+	  the source of the interrupt and clears the interrupt at the device.
+ Arguments:
+	  HwDeviceExtension - HBA miniport driver's adapter data storage
+ Return Value:
+***********************************************************************/
+void orc_interrupt(
+			  ORC_HCS * hcsp
+)
+{
+	BYTE bScbIdx;
+	ORC_SCB *pScb;
+
+	if (ORC_RD(hcsp->HCS_Base, ORC_RQUEUECNT) == 0) {
+		return;		// (FALSE);
+
+	}
+	do {
+		bScbIdx = ORC_RD(hcsp->HCS_Base, ORC_RQUEUE);
+
+		pScb = (ORC_SCB *) ((ULONG) hcsp->HCS_virScbArray + (ULONG) (sizeof(ORC_SCB) * bScbIdx));
+		pScb->SCB_Status = 0x0;
+
+		inia100SCBPost((BYTE *) hcsp, (BYTE *) pScb);
+	} while (ORC_RD(hcsp->HCS_Base, ORC_RQUEUECNT));
+	return;			//(TRUE);
+
+}				/* End of I1060Interrupt() */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/i60uscsi.h linux/drivers/scsi/i60uscsi.h
--- linux.vanilla/drivers/scsi/i60uscsi.h	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/i60uscsi.h	Mon Mar  1 21:53:35 1999
@@ -0,0 +1,572 @@
+/**************************************************************************
+ * Initio A100 device driver for Linux.
+ *
+ * Copyright (c) 1994-1998 Initio Corporation
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ **************************************************************************
+ *
+ * Module: inia100.h
+ * Description: INI-A100U2W LINUX device driver header
+ * Revision History:
+ *	06/18/98 HL, Initial Version 1.02
+ *	12/19/98 bv, v1.02a Use spinlocks for 2.1.95 and up.
+ **************************************************************************/
+
+#define ULONG   unsigned long
+#define PVOID   void *
+#define USHORT  unsigned short
+#define UCHAR   unsigned char
+#define BYTE    unsigned char
+#define WORD    unsigned short
+#define DWORD   unsigned long
+#define UBYTE   unsigned char
+#define UWORD   unsigned short
+#define UDWORD  unsigned long
+#ifdef ALPHA
+#define U32     unsigned int
+#else
+#define U32     unsigned long
+#endif
+
+#ifndef NULL
+#define NULL     0		/* zero          */
+#endif
+#ifndef TRUE
+#define TRUE     (1)		/* boolean true  */
+#endif
+#ifndef FALSE
+#define FALSE    (0)		/* boolean false */
+#endif
+#ifndef FAILURE
+#define FAILURE  (-1)
+#endif
+#if 1
+#define ORC_MAXQUEUE		245
+#else
+#define ORC_MAXQUEUE		25
+#endif
+
+#define TOTAL_SG_ENTRY		32
+#define MAX_TARGETS		16
+#define IMAX_CDB			15
+#define SENSE_SIZE		14
+#define MAX_SUPPORTED_ADAPTERS  4
+#define SUCCESSFUL              0x00
+
+#define I920_DEVICE_ID	0x0002	/* Initio's inic-950 product ID   */
+
+/************************************************************************/
+/*              Scatter-Gather Element Structure                        */
+/************************************************************************/
+typedef struct ORC_SG_Struc {
+	U32 SG_Ptr;		/* Data Pointer */
+	U32 SG_Len;		/* Data Length */
+} ORC_SG;
+
+typedef struct inia100_Adpt_Struc {
+	UWORD ADPT_BIOS;	/* 0 */
+	UWORD ADPT_BASE;	/* 1 */
+	UBYTE ADPT_Bus;		/* 2 */
+	UBYTE ADPT_Device;	/* 3 */
+	UBYTE ADPT_INTR;	/* 4 */
+} INIA100_ADPT_STRUCT;
+
+
+/* SCSI related definition                                              */
+#define DISC_NOT_ALLOW          0x80	/* Disconnect is not allowed    */
+#define DISC_ALLOW              0xC0	/* Disconnect is allowed        */
+
+
+#define ORC_OFFSET_SCB			16
+#define ORC_MAX_SCBS		    250
+#define MAX_CHANNELS       2
+#define MAX_ESCB_ELE				64
+#define TCF_DRV_255_63     0x0400
+
+/********************************************************/
+/*      Orchid Configuration Register Set               */
+/********************************************************/
+#define ORC_PVID	0x00	/* Vendor ID                      */
+#define ORC_VENDOR_ID	0x1101	/* Orchid vendor ID               */
+#define ORC_PDID        0x02	/* Device ID                    */
+#define ORC_DEVICE_ID	0x1060	/* Orchid device ID               */
+#define ORC_COMMAND	0x04	/* Command                        */
+#define BUSMS		0x04	/* BUS MASTER Enable              */
+#define IOSPA		0x01	/* IO Space Enable                */
+#define ORC_STATUS	0x06	/* Status register                */
+#define ORC_REVISION	0x08	/* Revision number                */
+#define ORC_BASE	0x10	/* Base address                   */
+#define ORC_BIOS	0x50	/* Expansion ROM base address     */
+#define ORC_INT_NUM	0x3C	/* Interrupt line         */
+#define ORC_INT_PIN	0x3D	/* Interrupt pin          */
+
+
+/********************************************************/
+/*      Orchid Host Command Set                         */
+/********************************************************/
+#define ORC_CMD_NOP		0x00	/* Host command - NOP             */
+#define ORC_CMD_VERSION		0x01	/* Host command - Get F/W version */
+#define ORC_CMD_ECHO		0x02	/* Host command - ECHO            */
+#define ORC_CMD_SET_NVM		0x03	/* Host command - Set NVRAM       */
+#define ORC_CMD_GET_NVM		0x04	/* Host command - Get NVRAM       */
+#define ORC_CMD_GET_BUS_STATUS	0x05	/* Host command - Get SCSI bus status */
+#define ORC_CMD_ABORT_SCB	0x06	/* Host command - Abort SCB       */
+#define ORC_CMD_ISSUE_SCB	0x07	/* Host command - Issue SCB       */
+
+/********************************************************/
+/*              Orchid Register Set                     */
+/********************************************************/
+#define ORC_GINTS	0xA0	/* Global Interrupt Status        */
+#define QINT		0x04	/* Reply Queue Interrupt  */
+#define ORC_GIMSK	0xA1	/* Global Interrupt MASK  */
+#define MQINT		0x04	/* Mask Reply Queue Interrupt     */
+#define	ORC_GCFG	0xA2	/* Global Configure               */
+#define EEPRG		0x01	/* Enable EEPROM programming */
+#define	ORC_GSTAT	0xA3	/* Global status          */
+#define WIDEBUS		0x10	/* Wide SCSI Devices connected    */
+#define ORC_HDATA	0xA4	/* Host Data                      */
+#define ORC_HCTRL	0xA5	/* Host Control                   */
+#define SCSIRST		0x80	/* SCSI bus reset         */
+#define HDO			0x40	/* Host data out          */
+#define HOSTSTOP		0x02	/* Host stop RISC engine  */
+#define DEVRST		0x01	/* Device reset                   */
+#define ORC_HSTUS	0xA6	/* Host Status                    */
+#define HDI			0x02	/* Host data in                   */
+#define RREADY		0x01	/* RISC engine is ready to receive */
+#define	ORC_NVRAM	0xA7	/* Nvram port address             */
+#define SE2CS		0x008
+#define SE2CLK		0x004
+#define SE2DO		0x002
+#define SE2DI		0x001
+#define ORC_PQUEUE	0xA8	/* Posting queue FIFO             */
+#define ORC_PQCNT	0xA9	/* Posting queue FIFO Cnt */
+#define ORC_RQUEUE	0xAA	/* Reply queue FIFO               */
+#define ORC_RQUEUECNT	0xAB	/* Reply queue FIFO Cnt           */
+#define	ORC_FWBASEADR	0xAC	/* Firmware base address  */
+
+#define	ORC_EBIOSADR0 0xB0	/* External Bios address */
+#define	ORC_EBIOSADR1 0xB1	/* External Bios address */
+#define	ORC_EBIOSADR2 0xB2	/* External Bios address */
+#define	ORC_EBIOSDATA 0xB3	/* External Bios address */
+
+#define	ORC_SCBSIZE	0xB7	/* SCB size register              */
+#define	ORC_SCBBASE0	0xB8	/* SCB base address 0             */
+#define	ORC_SCBBASE1	0xBC	/* SCB base address 1             */
+
+#define	ORC_RISCCTL	0xE0	/* RISC Control                   */
+#define PRGMRST		0x002
+#define DOWNLOAD		0x001
+#define	ORC_PRGMCTR0	0xE2	/* RISC program counter           */
+#define	ORC_PRGMCTR1	0xE3	/* RISC program counter           */
+#define	ORC_RISCRAM	0xEC	/* RISC RAM data port 4 bytes     */
+
+typedef struct orc_extended_scb {	/* Extended SCB                 */
+	ORC_SG ESCB_SGList[TOTAL_SG_ENTRY];	/*0 Start of SG list              */
+	unsigned char *SCB_Srb;	/*50 SRB Pointer */
+//         Scsi_Cmnd    *SCB_Srb;       /*50 SRB Pointer */
+} ESCB;
+
+/***********************************************************************
+		SCSI Control Block
+************************************************************************/
+typedef struct orc_scb {	/* Scsi_Ctrl_Blk                */
+	UBYTE SCB_Opcode;	/*00 SCB command code&residual  */
+	UBYTE SCB_Flags;	/*01 SCB Flags                  */
+	UBYTE SCB_Target;	/*02 Target Id                  */
+	UBYTE SCB_Lun;		/*03 Lun                        */
+	U32 SCB_Reserved0;	/*04 Reserved for ORCHID must 0 */
+	U32 SCB_XferLen;	/*08 Data Transfer Length       */
+	U32 SCB_Reserved1;	/*0C Reserved for ORCHID must 0 */
+	U32 SCB_SGLen;		/*10 SG list # * 8              */
+	U32 SCB_SGPAddr;	/*14 SG List Buf physical Addr  */
+	U32 SCB_SGPAddrHigh;	/*18 SG Buffer high physical Addr */
+	UBYTE SCB_HaStat;	/*1C Host Status                */
+	UBYTE SCB_TaStat;	/*1D Target Status              */
+	UBYTE SCB_Status;	/*1E SCB status                 */
+	UBYTE SCB_Link;		/*1F Link pointer, default 0xFF */
+	UBYTE SCB_SenseLen;	/*20 Sense Allocation Length    */
+	UBYTE SCB_CDBLen;	/*21 CDB Length                 */
+	UBYTE SCB_Ident;	/*22 Identify                   */
+	UBYTE SCB_TagMsg;	/*23 Tag Message                */
+	UBYTE SCB_CDB[IMAX_CDB];	/*24 SCSI CDBs                  */
+	UBYTE SCB_ScbIdx;	/*3C Index for this ORCSCB      */
+	U32 SCB_SensePAddr;	/*34 Sense Buffer physical Addr */
+
+	ESCB *SCB_EScb;		/*38 Extended SCB Pointer       */
+#ifndef ALPHA
+	UBYTE SCB_Reserved2[4];	/*3E Reserved for Driver use    */
+#endif
+} ORC_SCB;
+
+/* Opcodes of ORCSCB_Opcode */
+#define ORC_EXECSCSI	0x00	/* SCSI initiator command with residual */
+#define ORC_BUSDEVRST	0x01	/* SCSI Bus Device Reset  */
+
+/* Status of ORCSCB_Status */
+#define SCB_COMPLETE	0x00	/* SCB request completed  */
+#define SCB_POST	0x01	/* SCB is posted by the HOST      */
+
+/* Bit Definition for ORCSCB_Flags */
+#define SCF_DISINT	0x01	/* Disable HOST interrupt */
+#define SCF_DIR		0x18	/* Direction bits         */
+#define SCF_NO_DCHK	0x00	/* Direction determined by SCSI   */
+#define SCF_DIN		0x08	/* From Target to Initiator       */
+#define SCF_DOUT	0x10	/* From Initiator to Target       */
+#define SCF_NO_XF	0x18	/* No data transfer               */
+#define SCF_POLL   0x40
+
+/* Error Codes for ORCSCB_HaStat */
+#define HOST_SEL_TOUT	0x11
+#define HOST_DO_DU	0x12
+#define HOST_BUS_FREE	0x13
+#define HOST_BAD_PHAS	0x14
+#define HOST_INV_CMD	0x16
+#define HOST_SCSI_RST	0x1B
+#define HOST_DEV_RST	0x1C
+
+
+/* Error Codes for ORCSCB_TaStat */
+#define TARGET_CHK_COND	0x02
+#define TARGET_BUSY	0x08
+#define TARGET_TAG_FULL	0x28
+
+
+/* Queue tag msg: Simple_quque_tag, Head_of_queue_tag, Ordered_queue_tag */
+#define MSG_STAG	0x20
+#define MSG_HTAG	0x21
+#define MSG_OTAG	0x22
+
+#define MSG_IGNOREWIDE	0x23
+
+#define MSG_IDENT	0x80
+#define MSG_DISC	0x40	/* Disconnect allowed             */
+
+
+/* SCSI MESSAGE */
+#define	MSG_EXTEND	0x01
+#define	MSG_SDP		0x02
+#define	MSG_ABORT	0x06
+#define	MSG_REJ		0x07
+#define	MSG_NOP		0x08
+#define	MSG_PARITY	0x09
+#define	MSG_DEVRST	0x0C
+#define	MSG_STAG	0x20
+
+/***********************************************************************
+		Target Device Control Structure
+**********************************************************************/
+
+typedef struct ORC_Tar_Ctrl_Struc {
+	UBYTE TCS_DrvDASD;	/* 6 */
+	UBYTE TCS_DrvSCSI;	/* 7 */
+	UBYTE TCS_DrvHead;	/* 8 */
+	UWORD TCS_DrvFlags;	/* 4 */
+	UBYTE TCS_DrvSector;	/* 7 */
+} ORC_TCS, *PORC_TCS;
+
+/* Bit Definition for TCF_DrvFlags */
+#define	TCS_DF_NODASD_SUPT	0x20	/* Suppress OS/2 DASD Mgr support */
+#define	TCS_DF_NOSCSI_SUPT	0x40	/* Suppress OS/2 SCSI Mgr support */
+
+
+/***********************************************************************
+              Host Adapter Control Structure
+************************************************************************/
+typedef struct ORC_Ha_Ctrl_Struc {
+	USHORT HCS_Base;	/* 00 */
+	UBYTE HCS_Index;	/* 02 */
+	UBYTE HCS_Intr;		/* 04 */
+	UBYTE HCS_SCSI_ID;	/* 06    H/A SCSI ID */
+	UBYTE HCS_BIOS;		/* 07    BIOS configuration */
+
+	UBYTE HCS_Flags;	/* 0B */
+	UBYTE HCS_HAConfig1;	/* 1B    SCSI0MAXTags */
+	UBYTE HCS_MaxTar;	/* 1B    SCSI0MAXTags */
+
+	USHORT HCS_Units;	/* Number of units this adapter  */
+	USHORT HCS_AFlags;	/* Adapter info. defined flags   */
+	ULONG HCS_Timeout;	/* Adapter timeout value   */
+	PVOID HCS_virScbArray;	/* 28 Virtual Pointer to SCB array     */
+	U32 HCS_physScbArray;	/* Scb Physical address */
+	PVOID HCS_virEscbArray;	/* Virtual pointer to ESCB Scatter list */
+	U32 HCS_physEscbArray;	/* scatter list Physical address */
+	UBYTE TargetFlag[16];	/* 30  target configuration, TCF_EN_TAG */
+	UBYTE MaximumTags[16];	/* 40  ORC_MAX_SCBS */
+	UBYTE ActiveTags[16][16];	/* 50 */
+	ORC_TCS HCS_Tcs[16];	/* 28 */
+	U32 BitAllocFlag[MAX_CHANNELS][8];	/* Max STB is 256, So 256/32 */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spinlock_t BitAllocFlagLock;
+#endif
+	ULONG pSRB_head;
+	ULONG pSRB_tail;
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spinlock_t pSRB_lock;
+#endif
+} ORC_HCS;
+
+/* Bit Definition for HCS_Flags */
+
+#define HCF_SCSI_RESET	0x01	/* SCSI BUS RESET         */
+#define HCF_PARITY    	0x02	/* parity card                    */
+#define HCF_LVDS     	0x10	/* parity card                    */
+
+/* Bit Definition for TargetFlag */
+
+#define TCF_EN_255	    0x08
+#define TCF_EN_TAG	    0x10
+#define TCF_BUSY	      0x20
+#define TCF_DISCONNECT	0x40
+#define TCF_SPIN_UP	  0x80
+
+/* Bit Definition for HCS_AFlags */
+#define	HCS_AF_IGNORE		0x01	/* Adapter ignore         */
+#define	HCS_AF_DISABLE_RESET	0x10	/* Adapter disable reset  */
+#define	HCS_AF_DISABLE_ADPT	0x80	/* Adapter disable                */
+
+
+/*---------------------------------------*/
+/* TimeOut for RESET to complete (30s)   */
+/*                                       */
+/* After a RESET the drive is checked    */
+/* every 200ms.                          */
+/*---------------------------------------*/
+#define DELAYED_RESET_MAX       (30*1000L)
+#define DELAYED_RESET_INTERVAL  200L
+
+/*----------------------------------------------*/
+/* TimeOut for IRQ from last interrupt (5s)     */
+/*----------------------------------------------*/
+#define IRQ_TIMEOUT_INTERVAL    (5*1000L)
+
+/*----------------------------------------------*/
+/* Retry Delay interval (200ms)                 */
+/*----------------------------------------------*/
+#define DELAYED_RETRY_INTERVAL  200L
+
+#define	INQUIRY_SIZE		36
+#define	CAPACITY_SIZE		8
+#define	DEFAULT_SENSE_LEN	14
+
+#define	DEVICE_NOT_FOUND	0x86
+
+/*----------------------------------------------*/
+/* Definition for PCI device                    */
+/*----------------------------------------------*/
+#define	MAX_PCI_DEVICES	21
+#define	MAX_PCI_BUSES	8
+
+typedef struct Adpt_Struc {
+	USHORT ADPT_BIOS;	/* 0 */
+	UBYTE ADPT_BASE;	/* 1 */
+	UBYTE ADPT_Bus;		/* 2 */
+	UBYTE ADPT_Device;	/* 3 */
+	UBYTE ADPT_Reserved[3];
+} JACS, *PJACS;
+
+typedef struct _NVRAM {
+/*----------header ---------------*/
+	UCHAR SubVendorID0;	/* 00 - Sub Vendor ID           */
+	UCHAR SubVendorID1;	/* 00 - Sub Vendor ID           */
+	UCHAR SubSysID0;	/* 02 - Sub System ID           */
+	UCHAR SubSysID1;	/* 02 - Sub System ID           */
+	UCHAR SubClass;		/* 04 - Sub Class               */
+	UCHAR VendorID0;	/* 05 - Vendor ID               */
+	UCHAR VendorID1;	/* 05 - Vendor ID               */
+	UCHAR DeviceID0;	/* 07 - Device ID               */
+	UCHAR DeviceID1;	/* 07 - Device ID               */
+	UCHAR Reserved0[2];	/* 09 - Reserved                */
+	UCHAR Revision;		/* 0B - Revision of data structure */
+	/* ----Host Adapter Structure ---- */
+	UCHAR NumOfCh;		/* 0C - Number of SCSI channel  */
+	UCHAR BIOSConfig1;	/* 0D - BIOS configuration 1    */
+	UCHAR BIOSConfig2;	/* 0E - BIOS boot channel&target ID */
+	UCHAR BIOSConfig3;	/* 0F - BIOS configuration 3    */
+	/* ----SCSI channel Structure ---- */
+	/* from "CTRL-I SCSI Host Adapter SetUp menu "  */
+	UCHAR SCSI0Id;		/* 10 - Channel 0 SCSI ID       */
+	UCHAR SCSI0Config;	/* 11 - Channel 0 SCSI configuration */
+	UCHAR SCSI0MaxTags;	/* 12 - Channel 0 Maximum tags  */
+	UCHAR SCSI0ResetTime;	/* 13 - Channel 0 Reset recovering time */
+	UCHAR ReservedforChannel0[2];	/* 14 - Reserved                */
+
+	/* ----SCSI target Structure ----  */
+	/* from "CTRL-I SCSI device SetUp menu "                        */
+	UCHAR Target00Config;	/* 16 - Channel 0 Target 0 config */
+	UCHAR Target01Config;	/* 17 - Channel 0 Target 1 config */
+	UCHAR Target02Config;	/* 18 - Channel 0 Target 2 config */
+	UCHAR Target03Config;	/* 19 - Channel 0 Target 3 config */
+	UCHAR Target04Config;	/* 1A - Channel 0 Target 4 config */
+	UCHAR Target05Config;	/* 1B - Channel 0 Target 5 config */
+	UCHAR Target06Config;	/* 1C - Channel 0 Target 6 config */
+	UCHAR Target07Config;	/* 1D - Channel 0 Target 7 config */
+	UCHAR Target08Config;	/* 1E - Channel 0 Target 8 config */
+	UCHAR Target09Config;	/* 1F - Channel 0 Target 9 config */
+	UCHAR Target0AConfig;	/* 20 - Channel 0 Target A config */
+	UCHAR Target0BConfig;	/* 21 - Channel 0 Target B config */
+	UCHAR Target0CConfig;	/* 22 - Channel 0 Target C config */
+	UCHAR Target0DConfig;	/* 23 - Channel 0 Target D config */
+	UCHAR Target0EConfig;	/* 24 - Channel 0 Target E config */
+	UCHAR Target0FConfig;	/* 25 - Channel 0 Target F config */
+
+	UCHAR SCSI1Id;		/* 26 - Channel 1 SCSI ID       */
+	UCHAR SCSI1Config;	/* 27 - Channel 1 SCSI configuration */
+	UCHAR SCSI1MaxTags;	/* 28 - Channel 1 Maximum tags  */
+	UCHAR SCSI1ResetTime;	/* 29 - Channel 1 Reset recovering time */
+	UCHAR ReservedforChannel1[2];	/* 2A - Reserved                */
+
+	/* ----SCSI target Structure ----  */
+	/* from "CTRL-I SCSI device SetUp menu "                                          */
+	UCHAR Target10Config;	/* 2C - Channel 1 Target 0 config */
+	UCHAR Target11Config;	/* 2D - Channel 1 Target 1 config */
+	UCHAR Target12Config;	/* 2E - Channel 1 Target 2 config */
+	UCHAR Target13Config;	/* 2F - Channel 1 Target 3 config */
+	UCHAR Target14Config;	/* 30 - Channel 1 Target 4 config */
+	UCHAR Target15Config;	/* 31 - Channel 1 Target 5 config */
+	UCHAR Target16Config;	/* 32 - Channel 1 Target 6 config */
+	UCHAR Target17Config;	/* 33 - Channel 1 Target 7 config */
+	UCHAR Target18Config;	/* 34 - Channel 1 Target 8 config */
+	UCHAR Target19Config;	/* 35 - Channel 1 Target 9 config */
+	UCHAR Target1AConfig;	/* 36 - Channel 1 Target A config */
+	UCHAR Target1BConfig;	/* 37 - Channel 1 Target B config */
+	UCHAR Target1CConfig;	/* 38 - Channel 1 Target C config */
+	UCHAR Target1DConfig;	/* 39 - Channel 1 Target D config */
+	UCHAR Target1EConfig;	/* 3A - Channel 1 Target E config */
+	UCHAR Target1FConfig;	/* 3B - Channel 1 Target F config */
+	UCHAR reserved[3];	/* 3C - Reserved                */
+	/* ---------- CheckSum ----------       */
+	UCHAR CheckSum;		/* 3F - Checksum of NVRam       */
+} NVRAM, *PNVRAM;
+
+/* Bios Configuration for nvram->BIOSConfig1                            */
+#define NBC_BIOSENABLE	0x01	/* BIOS enable                    */
+#define NBC_CDROM	0x02	/* Support bootable CDROM */
+#define NBC_REMOVABLE	0x04	/* Support removable drive        */
+
+/* Bios Configuration for nvram->BIOSConfig2                            */
+#define	NBB_TARGET_MASK	0x0F	/* Boot SCSI target ID number     */
+#define	NBB_CHANL_MASK	0xF0	/* Boot SCSI channel number       */
+
+/* Bit definition for nvram->SCSIConfig                                 */
+#define NCC_BUSRESET	0x01	/* Reset SCSI bus at power up     */
+#define NCC_PARITYCHK	0x02	/* SCSI parity enable             */
+#define NCC_LVDS	0x10	/* Enable LVDS                    */
+#define NCC_ACTTERM1	0x20	/* Enable active terminator 1     */
+#define NCC_ACTTERM2	0x40	/* Enable active terminator 2     */
+#define NCC_AUTOTERM	0x80	/* Enable auto termination        */
+
+/* Bit definition for nvram->TargetxConfig                              */
+#define	NTC_PERIOD	0x07	/* Maximum Sync. Speed            */
+#define NTC_1GIGA	0x08	/* 255 head / 63 sectors (64/32) */
+#define NTC_NO_SYNC	0x10	/* NO SYNC. NEGO          */
+#define NTC_NO_WIDESYNC	0x20	/* NO WIDE SYNC. NEGO             */
+#define	NTC_DISC_ENABLE	0x40	/* Enable SCSI disconnect */
+#define NTC_SPINUP	0x80	/* Start disk drive               */
+
+/* Default NVRam values                                                 */
+#define NBC_DEFAULT	(NBC_ENABLE)
+#define NCC_DEFAULT	(NCC_BUSRESET | NCC_AUTOTERM | NCC_PARITYCHK)
+#define NCC_MAX_TAGS	0x20	/* Maximum tags per target        */
+#define	NCC_RESET_TIME	0x0A	/* SCSI RESET recovering time     */
+#define NTC_DEFAULT	(NTC_1GIGA | NTC_NO_WIDESYNC | NTC_DISC_ENABLE)
+
+typedef union {			/* Union define for mechanism 1   */
+	struct {
+		unsigned char RegNum;
+		unsigned char FcnNum:3;
+		unsigned char DeviceNum:5;
+		unsigned char BusNum;
+		unsigned char Reserved:7;
+		unsigned char Enable:1;
+	} sConfigAdr;
+	unsigned long lConfigAdr;
+} CONFIG_ADR;
+
+typedef union {			/* Union define for mechanism 2   */
+	struct {
+		unsigned char RegNum;
+		unsigned char DeviceNum;
+		unsigned short Reserved;
+	} sHostAdr;
+	unsigned long lHostAdr;
+} HOST_ADR;
+
+#define ORC_RD(x,y)             (UCHAR)(inb(  (int)((ULONG)((ULONG)x+(UCHAR)y)) ))
+#define ORC_RDLONG(x,y)         (long)(inl((int)((ULONG)((ULONG)x+(UCHAR)y)) ))
+
+#define ORC_WR(     adr,data)   outb( (UCHAR)(data), (int)(adr))
+#define ORC_WRSHORT(adr,data)   outw( (UWORD)(data), (int)(adr))
+#define ORC_WRLONG( adr,data)   outl( (ULONG)(data), (int)(adr))
+
+
+#define SCSI_ABORT_SNOOZE 0
+#define SCSI_ABORT_SUCCESS 1
+#define SCSI_ABORT_PENDING 2
+#define SCSI_ABORT_BUSY 3
+#define SCSI_ABORT_NOT_RUNNING 4
+#define SCSI_ABORT_ERROR 5
+
+#define SCSI_RESET_SNOOZE 0
+#define SCSI_RESET_PUNT 1
+#define SCSI_RESET_SUCCESS 2
+#define SCSI_RESET_PENDING 3
+#define SCSI_RESET_WAKEUP 4
+#define SCSI_RESET_NOT_RUNNING 5
+#define SCSI_RESET_ERROR 6
+
+#define SCSI_RESET_SYNCHRONOUS		0x01
+#define SCSI_RESET_ASYNCHRONOUS		0x02
+#define SCSI_RESET_SUGGEST_BUS_RESET	0x04
+#define SCSI_RESET_SUGGEST_HOST_RESET	0x08
+
+#define SCSI_RESET_BUS_RESET 0x100
+#define SCSI_RESET_HOST_RESET 0x200
+#define SCSI_RESET_ACTION   0xff
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/i91uscsi.c linux/drivers/scsi/i91uscsi.c
--- linux.vanilla/drivers/scsi/i91uscsi.c	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/i91uscsi.c	Tue Mar  2 00:42:14 1999
@@ -0,0 +1,2808 @@
+/**************************************************************************
+ * Initio 9100 device driver for Linux.
+ *
+ * Copyright (c) 1994-1998 Initio Corporation
+ * Copyright (c) 1998 Bas Vermeulen <bvermeul@blackstar.xs4all.nl>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ ************************************************************************
+    Module: i91uscsi.c
+    Description: PCI I/F for INI-910 SCSI Bus Master Controller
+    Revision History:
+	11/09/94 Tim Chen, Initiali Version 0.90A
+	01/17/95 TC, release ver 1.01
+	02/09/95 TC  modify ReadPCIConfig, try both mechanisms;
+	02/15/95 TC  add support for INI-9100W
+	06/04/96 HC, Change to fit LINUX from jaspci.c
+	11/18/96 HC, Port for tulip
+	07/08/98 hc, Support 0002134A
+        07/23/98 wh, Change the abort_srb routine.
+	09/16/98 hl, Support ALPHA, Rewrite the returnNumberAdapters	<01>
+	12/09/98 bv, Removed unused code, changed tul_se2_wait to
+		     use udelay(30) and tul_do_pause to enable 
+		     interrupts for >= 2.1.95
+	12/13/98 bv, Use spinlocks instead of cli() for serialized
+		     access to HCS_Semaph, HCS_FirstAvail and HCS_LastAvail
+		     members of the HCS structure.
+	01/09/98 bv, Fix a deadlock on SMP system.
+**********************************************************************/
+
+#define DEBUG_INTERRUPT 0
+#define DEBUG_QUEUE     0
+#define DEBUG_STATE     0
+#define INT_DISC	0
+
+
+#ifndef CVT_LINUX_VERSION
+#define	CVT_LINUX_VERSION(V,P,S)	(V * 65536 + P * 256 + S)
+#endif
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/blk.h>
+#include <asm/io.h>
+
+#include "i91uscsi.h"
+
+/*--- external functions --*/
+static void tul_se2_wait(void);
+
+/*--- forward refrence ---*/
+static SCB *tul_find_busy_scb(HCS * pCurHcb, WORD tarlun);
+static SCB *tul_find_done_scb(HCS * pCurHcb);
+
+static int tulip_main(HCS * pCurHcb);
+
+static int tul_next_state(HCS * pCurHcb);
+static int tul_state_1(HCS * pCurHcb);
+static int tul_state_2(HCS * pCurHcb);
+static int tul_state_3(HCS * pCurHcb);
+static int tul_state_4(HCS * pCurHcb);
+static int tul_state_5(HCS * pCurHcb);
+static int tul_state_6(HCS * pCurHcb);
+static int tul_state_7(HCS * pCurHcb);
+static int tul_xfer_data_in(HCS * pCurHcb);
+static int tul_xfer_data_out(HCS * pCurHcb);
+static int tul_xpad_in(HCS * pCurHcb);
+static int tul_xpad_out(HCS * pCurHcb);
+static int tul_status_msg(HCS * pCurHcb);
+
+static int tul_msgin(HCS * pCurHcb);
+static int tul_msgin_sync(HCS * pCurHcb);
+static int tul_msgin_accept(HCS * pCurHcb);
+static int tul_msgout_reject(HCS * pCurHcb);
+static int tul_msgin_extend(HCS * pCurHcb);
+
+static int tul_msgout_ide(HCS * pCurHcb);
+static int tul_msgout_abort_targ(HCS * pCurHcb);
+static int tul_msgout_abort_tag(HCS * pCurHcb);
+
+static int tul_bus_device_reset(HCS * pCurHcb);
+static void tul_select_atn(HCS * pCurHcb, SCB * pCurScb);
+static void tul_select_atn3(HCS * pCurHcb, SCB * pCurScb);
+static void tul_select_atn_stop(HCS * pCurHcb, SCB * pCurScb);
+static int int_tul_busfree(HCS * pCurHcb);
+int int_tul_scsi_rst(HCS * pCurHcb);
+static int int_tul_bad_seq(HCS * pCurHcb);
+static int int_tul_resel(HCS * pCurHcb);
+static int tul_sync_done(HCS * pCurHcb);
+static int wdtr_done(HCS * pCurHcb);
+static int wait_tulip(HCS * pCurHcb);
+static int tul_wait_done_disc(HCS * pCurHcb);
+static int tul_wait_disc(HCS * pCurHcb);
+static void tulip_scsi(HCS * pCurHcb);
+static int tul_post_scsi_rst(HCS * pCurHcb);
+
+static void tul_se2_ew_en(WORD CurBase);
+static void tul_se2_ew_ds(WORD CurBase);
+static int tul_se2_rd_all(WORD CurBase);
+static void tul_se2_update_all(WORD CurBase);	/* setup default pattern */
+static void tul_read_eeprom(WORD CurBase);
+
+				/* ---- EXTERNAL VARIABLES ---- */
+HCS tul_hcs[MAX_SUPPORTED_ADAPTERS];
+				/* ---- INTERNAL VARIABLES ---- */
+static INI_ADPT_STRUCT i91u_adpt[MAX_SUPPORTED_ADAPTERS];
+
+/*NVRAM nvram, *nvramp = &nvram; */
+static NVRAM i91unvram;
+static NVRAM *i91unvramp;
+
+
+
+static UCHAR i91udftNvRam[64] =
+{
+/*----------- header -----------*/
+	0x25, 0xc9,		/* Signature    */
+	0x40,			/* Size         */
+	0x01,			/* Revision     */
+	/* -- Host Adapter Structure -- */
+	0x95,			/* ModelByte0   */
+	0x00,			/* ModelByte1   */
+	0x00,			/* ModelInfo    */
+	0x01,			/* NumOfCh      */
+	NBC1_DEFAULT,		/* BIOSConfig1  */
+	0,			/* BIOSConfig2  */
+	0,			/* HAConfig1    */
+	0,			/* HAConfig2    */
+	/* SCSI channel 0 and target Structure  */
+	7,			/* SCSIid       */
+	NCC1_DEFAULT,		/* SCSIconfig1  */
+	0,			/* SCSIconfig2  */
+	0x10,			/* NumSCSItarget */
+
+	NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+	NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+	NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+	NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+
+	/* SCSI channel 1 and target Structure  */
+	7,			/* SCSIid       */
+	NCC1_DEFAULT,		/* SCSIconfig1  */
+	0,			/* SCSIconfig2  */
+	0x10,			/* NumSCSItarget */
+
+	NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+	NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+	NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+	NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0};			/*      - CheckSum -            */
+
+
+static UCHAR tul_rate_tbl[8] =	/* fast 20      */
+{
+				/* nanosecond devide by 4 */
+	12,			/* 50ns,  20M   */
+	18,			/* 75ns,  13.3M */
+	25,			/* 100ns, 10M   */
+	31,			/* 125ns, 8M    */
+	37,			/* 150ns, 6.6M  */
+	43,			/* 175ns, 5.7M  */
+	50,			/* 200ns, 5M    */
+	62			/* 250ns, 4M    */
+};
+
+extern int tul_num_ch;
+
+
+static void tul_do_pause(unsigned amount)
+{				/* Pause for amount*10 milliseconds */
+	unsigned long the_time = jiffies + amount;	/* 0.01 seconds per jiffy */
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	while (time_before_eq(jiffies, the_time));
+#else
+	while (jiffies < the_time);
+#endif
+}
+
+/*-- forward reference --*/
+
+/*******************************************************************
+	Use memeory refresh time        ~ 15us * 2
+********************************************************************/
+void tul_se2_wait()
+{
+#if 1
+	udelay(30);
+#else
+	UCHAR readByte;
+
+	readByte = TUL_RD(0, 0x61);
+	if ((readByte & 0x10) == 0x10) {
+		for (;;) {
+			readByte = TUL_RD(0, 0x61);
+			if ((readByte & 0x10) == 0x10)
+				break;
+		}
+		for (;;) {
+			readByte = TUL_RD(0, 0x61);
+			if ((readByte & 0x10) != 0x10)
+				break;
+		}
+	} else {
+		for (;;) {
+			readByte = TUL_RD(0, 0x61);
+			if ((readByte & 0x10) == 0x10)
+				break;
+		}
+		for (;;) {
+			readByte = TUL_RD(0, 0x61);
+			if ((readByte & 0x10) != 0x10)
+				break;
+		}
+	}
+#endif
+}
+
+
+/******************************************************************
+ Input: instruction for  Serial E2PROM
+
+ EX: se2_rd(0 call se2_instr() to send address and read command
+
+	 StartBit  OP_Code   Address                Data
+	 --------- --------  ------------------     -------
+	 1         1 , 0     A5,A4,A3,A2,A1,A0      D15-D0
+
+		 +-----------------------------------------------------
+		 |
+ CS -----+
+			+--+  +--+  +--+  +--+  +--+
+			^  |  ^  |  ^  |  ^  |  ^  |
+			|  |  |  |  |  |  |  |  |  |
+ CLK -------+  +--+  +--+  +--+  +--+  +--
+ (leading edge trigger)
+
+		 +--1-----1--+
+		 | SB    OP  |  OP    A5    A4
+ DI  ----+           +--0------------------
+ (address and cmd sent to nvram)
+
+	 -------------------------------------------+
+												|
+ DO                                             +---
+ (data sent from nvram)
+
+
+******************************************************************/
+void tul_se2_instr(WORD CurBase, UCHAR instr)
+{
+	int i;
+	UCHAR b;
+
+	TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2DO);	/* cs+start bit */
+	tul_se2_wait();
+	TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2CLK | SE2DO);	/* +CLK */
+	tul_se2_wait();
+
+	for (i = 0; i < 8; i++) {
+		if (instr & 0x80)
+			b = SE2CS | SE2DO;	/* -CLK+dataBit */
+		else
+			b = SE2CS;	/* -CLK */
+		TUL_WR(CurBase + TUL_NVRAM, b);
+		tul_se2_wait();
+		TUL_WR(CurBase + TUL_NVRAM, b | SE2CLK);	/* +CLK */
+		tul_se2_wait();
+		instr <<= 1;
+	}
+	TUL_WR(CurBase + TUL_NVRAM, SE2CS);	/* -CLK */
+	tul_se2_wait();
+	return;
+}
+
+
+/******************************************************************
+ Function name  : tul_se2_ew_en
+ Description    : Enable erase/write state of serial EEPROM
+******************************************************************/
+void tul_se2_ew_en(WORD CurBase)
+{
+	tul_se2_instr(CurBase, 0x30);	/* EWEN */
+	TUL_WR(CurBase + TUL_NVRAM, 0);		/* -CS  */
+	tul_se2_wait();
+	return;
+}
+
+
+/************************************************************************
+ Disable erase/write state of serial EEPROM
+*************************************************************************/
+void tul_se2_ew_ds(WORD CurBase)
+{
+	tul_se2_instr(CurBase, 0);	/* EWDS */
+	TUL_WR(CurBase + TUL_NVRAM, 0);		/* -CS  */
+	tul_se2_wait();
+	return;
+}
+
+
+/******************************************************************
+	Input  :address of Serial E2PROM
+	Output :value stored in  Serial E2PROM
+*******************************************************************/
+USHORT tul_se2_rd(WORD CurBase, ULONG adr)
+{
+	UCHAR instr, readByte;
+	USHORT readWord;
+	int i;
+
+	instr = (UCHAR) (adr | 0x80);
+	tul_se2_instr(CurBase, instr);	/* READ INSTR */
+	readWord = 0;
+
+	for (i = 15; i >= 0; i--) {
+		TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2CLK);	/* +CLK */
+		tul_se2_wait();
+		TUL_WR(CurBase + TUL_NVRAM, SE2CS);	/* -CLK */
+
+		/* sample data after the following edge of clock  */
+		readByte = TUL_RD(CurBase, TUL_NVRAM);
+		readByte &= SE2DI;
+		readWord += (readByte << i);
+		tul_se2_wait();	/* 6/20/95 */
+	}
+
+	TUL_WR(CurBase + TUL_NVRAM, 0);		/* no chip select */
+	tul_se2_wait();
+	return readWord;
+}
+
+
+/******************************************************************
+ Input: new value in  Serial E2PROM, address of Serial E2PROM
+*******************************************************************/
+void tul_se2_wr(WORD CurBase, UCHAR adr, USHORT writeWord)
+{
+	UCHAR readByte;
+	UCHAR instr;
+	int i;
+
+	instr = (UCHAR) (adr | 0x40);
+	tul_se2_instr(CurBase, instr);	/* WRITE INSTR */
+	for (i = 15; i >= 0; i--) {
+		if (writeWord & 0x8000)
+			TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2DO);	/* -CLK+dataBit 1 */
+		else
+			TUL_WR(CurBase + TUL_NVRAM, SE2CS);	/* -CLK+dataBit 0 */
+		tul_se2_wait();
+		TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2CLK);	/* +CLK */
+		tul_se2_wait();
+		writeWord <<= 1;
+	}
+	TUL_WR(CurBase + TUL_NVRAM, SE2CS);	/* -CLK */
+	tul_se2_wait();
+	TUL_WR(CurBase + TUL_NVRAM, 0);		/* -CS  */
+	tul_se2_wait();
+
+	TUL_WR(CurBase + TUL_NVRAM, SE2CS);	/* +CS  */
+	tul_se2_wait();
+
+	for (;;) {
+		TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2CLK);	/* +CLK */
+		tul_se2_wait();
+		TUL_WR(CurBase + TUL_NVRAM, SE2CS);	/* -CLK */
+		tul_se2_wait();
+		if ((readByte = TUL_RD(CurBase, TUL_NVRAM)) & SE2DI)
+			break;	/* write complete */
+	}
+	TUL_WR(CurBase + TUL_NVRAM, 0);		/* -CS */
+	return;
+}
+
+
+/***********************************************************************
+ Read SCSI H/A configuration parameters from serial EEPROM
+************************************************************************/
+int tul_se2_rd_all(WORD CurBase)
+{
+	int i;
+	ULONG chksum = 0;
+	USHORT *np;
+
+	i91unvramp = &i91unvram;
+	np = (USHORT *) i91unvramp;
+	for (i = 0; i < 32; i++) {
+		*np++ = tul_se2_rd(CurBase, i);
+	}
+
+/*--------------------Is signature "ini" ok ? ----------------*/
+	if (i91unvramp->NVM_Signature != INI_SIGNATURE)
+		return -1;
+/*---------------------- Is ckecksum ok ? ----------------------*/
+	np = (USHORT *) i91unvramp;
+	for (i = 0; i < 31; i++)
+		chksum += *np++;
+	if (i91unvramp->NVM_CheckSum != (USHORT) chksum)
+		return -1;
+	return 1;
+}
+
+
+/***********************************************************************
+ Update SCSI H/A configuration parameters from serial EEPROM
+************************************************************************/
+void tul_se2_update_all(WORD CurBase)
+{				/* setup default pattern */
+	int i;
+	ULONG chksum = 0;
+	USHORT *np, *np1;
+
+	i91unvramp = &i91unvram;
+	/* Calculate checksum first */
+	np = (USHORT *) i91udftNvRam;
+	for (i = 0; i < 31; i++)
+		chksum += *np++;
+	*np = (USHORT) chksum;
+	tul_se2_ew_en(CurBase);	/* Enable write  */
+
+	np = (USHORT *) i91udftNvRam;
+	np1 = (USHORT *) i91unvramp;
+	for (i = 0; i < 32; i++, np++, np1++) {
+		if (*np != *np1) {
+			tul_se2_wr(CurBase, i, *np);
+		}
+	}
+
+	tul_se2_ew_ds(CurBase);	/* Disable write   */
+	return;
+}
+
+/*************************************************************************
+ Function name  : read_eeprom
+**************************************************************************/
+void tul_read_eeprom(WORD CurBase)
+{
+	UCHAR gctrl;
+
+	i91unvramp = &i91unvram;
+/*------Enable EEProm programming ---*/
+	gctrl = TUL_RD(CurBase, TUL_GCTRL);
+	TUL_WR(CurBase + TUL_GCTRL, gctrl | TUL_GCTRL_EEPROM_BIT);
+	if (tul_se2_rd_all(CurBase) != 1) {
+		tul_se2_update_all(CurBase);	/* setup default pattern */
+		tul_se2_rd_all(CurBase);	/* load again  */
+	}
+/*------ Disable EEProm programming ---*/
+	gctrl = TUL_RD(CurBase, TUL_GCTRL);
+	TUL_WR(CurBase + TUL_GCTRL, gctrl & ~TUL_GCTRL_EEPROM_BIT);
+}				/* read_eeprom */
+
+int Addi91u_into_Adapter_table(WORD wBIOS, WORD wBASE, BYTE bInterrupt,
+			       BYTE bBus, BYTE bDevice)
+{
+	int i, j;
+
+	for (i = 0; i < MAX_SUPPORTED_ADAPTERS; i++) {
+		if (i91u_adpt[i].ADPT_BIOS < wBIOS)
+			continue;
+		if (i91u_adpt[i].ADPT_BIOS == wBIOS) {
+			if (i91u_adpt[i].ADPT_BASE == wBASE) {
+				if (i91u_adpt[i].ADPT_Bus != 0xFF)
+					return (FAILURE);
+			} else if (i91u_adpt[i].ADPT_BASE < wBASE)
+					continue;
+		}
+		for (j = MAX_SUPPORTED_ADAPTERS - 1; j > i; j--) {
+			i91u_adpt[j].ADPT_BASE = i91u_adpt[j - 1].ADPT_BASE;
+			i91u_adpt[j].ADPT_INTR = i91u_adpt[j - 1].ADPT_INTR;
+			i91u_adpt[j].ADPT_BIOS = i91u_adpt[j - 1].ADPT_BIOS;
+			i91u_adpt[j].ADPT_Bus = i91u_adpt[j - 1].ADPT_Bus;
+			i91u_adpt[j].ADPT_Device = i91u_adpt[j - 1].ADPT_Device;
+		}
+		i91u_adpt[i].ADPT_BASE = wBASE;
+		i91u_adpt[i].ADPT_INTR = bInterrupt;
+		i91u_adpt[i].ADPT_BIOS = wBIOS;
+		i91u_adpt[i].ADPT_Bus = bBus;
+		i91u_adpt[i].ADPT_Device = bDevice;
+		return (SUCCESSFUL);
+	}
+	return (FAILURE);
+}
+
+void init_i91uAdapter_table(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_SUPPORTED_ADAPTERS; i++) {	/* Initialize adapter structure */
+		i91u_adpt[i].ADPT_BIOS = 0xffff;
+		i91u_adpt[i].ADPT_BASE = 0xffff;
+		i91u_adpt[i].ADPT_INTR = 0xff;
+		i91u_adpt[i].ADPT_Bus = 0xff;
+		i91u_adpt[i].ADPT_Device = 0xff;
+	}
+	return;
+}
+
+void tul_stop_bm(HCS * pCurHcb)
+{
+
+	if (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & XPEND) {	/* if DMA xfer is pending, abort DMA xfer */
+		TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_X_ABT | TAX_X_CLR_FIFO);
+		/* wait Abort DMA xfer done */
+		while ((TUL_RD(pCurHcb->HCS_Base, TUL_Int) & XABT) == 0);
+	}
+	TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+}
+
+/***************************************************************************/
+void get_tulipPCIConfig(HCS * pCurHcb, int ch_idx)
+{
+	pCurHcb->HCS_Base = i91u_adpt[ch_idx].ADPT_BASE;	/* Supply base address  */
+	pCurHcb->HCS_BIOS = i91u_adpt[ch_idx].ADPT_BIOS;	/* Supply BIOS address  */
+	pCurHcb->HCS_Intr = i91u_adpt[ch_idx].ADPT_INTR;	/* Supply interrupt line */
+	return;
+}
+
+/***************************************************************************/
+int tul_reset_scsi(HCS * pCurHcb, int seconds)
+{
+	TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_RST_BUS);
+
+	while (!((pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt)) & TSS_SCSIRST_INT));
+	/* reset tulip chip */
+
+	TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, 0);
+
+	/* Stall for a while, wait for target's firmware ready,make it 2 sec ! */
+	/* SONY 5200 tape drive won't work if only stall for 1 sec */
+	tul_do_pause(seconds * 100);
+
+	TUL_RD(pCurHcb->HCS_Base, TUL_SInt);
+
+	return (SCSI_RESET_SUCCESS);
+}
+
+/***************************************************************************/
+int init_tulip(HCS * pCurHcb, SCB * scbp, int tul_num_scb, BYTE * pbBiosAdr, int seconds)
+{
+	int i;
+	WORD *pwFlags;
+	BYTE *pbHeads;
+	SCB *pTmpScb, *pPrevScb = NULL;
+
+	pCurHcb->HCS_NumScbs = tul_num_scb;
+	pCurHcb->HCS_Semaph = 1;
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	pCurHcb->HCS_SemaphLock = SPIN_LOCK_UNLOCKED;
+#endif
+	pCurHcb->HCS_JSStatus0 = 0;
+	pCurHcb->HCS_Scb = scbp;
+	pCurHcb->HCS_NxtPend = scbp;
+	pCurHcb->HCS_NxtAvail = scbp;
+	for (i = 0, pTmpScb = scbp; i < tul_num_scb; i++, pTmpScb++) {
+		pTmpScb->SCB_TagId = i;
+		if (i != 0)
+			pPrevScb->SCB_NxtScb = pTmpScb;
+		pPrevScb = pTmpScb;
+	}
+	pPrevScb->SCB_NxtScb = NULL;
+	pCurHcb->HCS_ScbEnd = pTmpScb;
+	pCurHcb->HCS_FirstAvail = scbp;
+	pCurHcb->HCS_LastAvail = pPrevScb;
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	pCurHcb->HCS_AvailLock = SPIN_LOCK_UNLOCKED;
+#endif
+	pCurHcb->HCS_FirstPend = NULL;
+	pCurHcb->HCS_LastPend = NULL;
+	pCurHcb->HCS_FirstBusy = NULL;
+	pCurHcb->HCS_LastBusy = NULL;
+	pCurHcb->HCS_FirstDone = NULL;
+	pCurHcb->HCS_LastDone = NULL;
+	pCurHcb->HCS_ActScb = NULL;
+	pCurHcb->HCS_ActTcs = NULL;
+
+	tul_read_eeprom(pCurHcb->HCS_Base);
+/*---------- get H/A configuration -------------*/
+	if (i91unvramp->NVM_SCSIInfo[0].NVM_NumOfTarg == 8)
+		pCurHcb->HCS_MaxTar = 8;
+	else
+		pCurHcb->HCS_MaxTar = 16;
+
+	pCurHcb->HCS_Config = i91unvramp->NVM_SCSIInfo[0].NVM_ChConfig1;
+
+	pCurHcb->HCS_SCSI_ID = i91unvramp->NVM_SCSIInfo[0].NVM_ChSCSIID;
+	pCurHcb->HCS_IdMask = ~(1 << pCurHcb->HCS_SCSI_ID);
+
+#if CHK_PARITY
+	/* Enable parity error response */
+	TUL_WR(pCurHcb->HCS_Base + TUL_PCMD, TUL_RD(pCurHcb->HCS_Base, TUL_PCMD) | 0x40);
+#endif
+
+	/* Mask all the interrupt       */
+	TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+
+	tul_stop_bm(pCurHcb);
+	/* --- Initialize the tulip --- */
+	TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_RST_CHIP);
+
+	/* program HBA's SCSI ID        */
+	TUL_WR(pCurHcb->HCS_Base + TUL_SScsiId, pCurHcb->HCS_SCSI_ID << 4);
+
+	/* Enable Initiator Mode ,phase latch,alternate sync period mode,
+	   disable SCSI reset */
+	if (pCurHcb->HCS_Config & HCC_EN_PAR)
+		pCurHcb->HCS_SConf1 = (TSC_INITDEFAULT | TSC_EN_SCSI_PAR);
+	else
+		pCurHcb->HCS_SConf1 = (TSC_INITDEFAULT);
+	TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurHcb->HCS_SConf1);
+
+	/* Enable HW reselect           */
+	TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT);
+
+	TUL_WR(pCurHcb->HCS_Base + TUL_SPeriod, 0);
+
+	/* selection time out = 250 ms */
+	TUL_WR(pCurHcb->HCS_Base + TUL_STimeOut, 153);
+
+/*--------- Enable SCSI terminator -----*/
+	TUL_WR(pCurHcb->HCS_Base + TUL_XCtrl, (pCurHcb->HCS_Config & (HCC_ACT_TERM1 | HCC_ACT_TERM2)));
+	TUL_WR(pCurHcb->HCS_Base + TUL_GCTRL1,
+	       ((pCurHcb->HCS_Config & HCC_AUTO_TERM) >> 4) | (TUL_RD(pCurHcb->HCS_Base, TUL_GCTRL1) & 0xFE));
+
+	for (i = 0,
+	     pwFlags = (WORD *) & (i91unvramp->NVM_SCSIInfo[0].NVM_Targ0Config),
+	     pbHeads = pbBiosAdr + 0x180;
+	     i < pCurHcb->HCS_MaxTar;
+	     i++, pwFlags++) {
+		pCurHcb->HCS_Tcs[i].TCS_Flags = *pwFlags & ~(TCF_SYNC_DONE | TCF_WDTR_DONE);
+		if (pCurHcb->HCS_Tcs[i].TCS_Flags & TCF_EN_255)
+			pCurHcb->HCS_Tcs[i].TCS_DrvFlags = TCF_DRV_255_63;
+		else
+			pCurHcb->HCS_Tcs[i].TCS_DrvFlags = 0;
+		pCurHcb->HCS_Tcs[i].TCS_JS_Period = 0;
+		pCurHcb->HCS_Tcs[i].TCS_SConfig0 = pCurHcb->HCS_SConf1;
+		pCurHcb->HCS_Tcs[i].TCS_DrvHead = *pbHeads++;
+		if (pCurHcb->HCS_Tcs[i].TCS_DrvHead == 255)
+			pCurHcb->HCS_Tcs[i].TCS_DrvFlags = TCF_DRV_255_63;
+		else
+			pCurHcb->HCS_Tcs[i].TCS_DrvFlags = 0;
+		pCurHcb->HCS_Tcs[i].TCS_DrvSector = *pbHeads++;
+		pCurHcb->HCS_Tcs[i].TCS_Flags &= ~TCF_BUSY;
+		pCurHcb->HCS_ActTags[i] = 0;
+		pCurHcb->HCS_MaxTags[i] = 0xFF;
+	}			/* for                          */
+	printk("i91u: PCI Base=0x%04X, IRQ=%d, BIOS=0x%04X0, SCSI ID=%d\n",
+	       pCurHcb->HCS_Base, pCurHcb->HCS_Intr,
+	       pCurHcb->HCS_BIOS, pCurHcb->HCS_SCSI_ID);
+/*------------------- reset SCSI Bus ---------------------------*/
+	if (pCurHcb->HCS_Config & HCC_SCSI_RESET) {
+		printk("i91u: Reset SCSI Bus ... \n");
+		tul_reset_scsi(pCurHcb, seconds);
+	}
+	TUL_WR(pCurHcb->HCS_Base + TUL_SCFG1, 0x17);
+	TUL_WR(pCurHcb->HCS_Base + TUL_SIntEnable, 0xE9);
+	return (0);
+}
+
+/***************************************************************************/
+SCB *tul_alloc_scb(HCS * hcsp)
+{
+	SCB *pTmpScb;
+	ULONG flags;
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_lock_irqsave(&(hcsp->HCS_AvailLock), flags);
+#else
+	save_flags(flags);
+	cli();
+#endif
+	if ((pTmpScb = hcsp->HCS_FirstAvail) != NULL) {
+#if DEBUG_QUEUE
+		printk("find scb at %08lx\n", (ULONG) pTmpScb);
+#endif
+		if ((hcsp->HCS_FirstAvail = pTmpScb->SCB_NxtScb) == NULL)
+			hcsp->HCS_LastAvail = NULL;
+		pTmpScb->SCB_NxtScb = NULL;
+		pTmpScb->SCB_Status = SCB_RENT;
+	}
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_unlock_irqrestore(&(hcsp->HCS_AvailLock), flags);
+#else
+	restore_flags(flags);
+#endif
+	return (pTmpScb);
+}
+
+/***************************************************************************/
+void tul_release_scb(HCS * hcsp, SCB * scbp)
+{
+	ULONG flags;
+
+#if DEBUG_QUEUE
+	printk("Release SCB %lx; ", (ULONG) scbp);
+#endif
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_lock_irqsave(&(hcsp->HCS_AvailLock), flags);
+#else
+	save_flags(flags);
+	cli();
+#endif
+	scbp->SCB_Srb = 0;
+	scbp->SCB_Status = 0;
+	scbp->SCB_NxtScb = NULL;
+	if (hcsp->HCS_LastAvail != NULL) {
+		hcsp->HCS_LastAvail->SCB_NxtScb = scbp;
+		hcsp->HCS_LastAvail = scbp;
+	} else {
+		hcsp->HCS_FirstAvail = scbp;
+		hcsp->HCS_LastAvail = scbp;
+	}
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_unlock_irqrestore(&(hcsp->HCS_AvailLock), flags);
+#else
+	restore_flags(flags);
+#endif
+}
+
+/***************************************************************************/
+void tul_append_pend_scb(HCS * pCurHcb, SCB * scbp)
+{
+
+#if DEBUG_QUEUE
+	printk("Append pend SCB %lx; ", (ULONG) scbp);
+#endif
+	scbp->SCB_Status = SCB_PEND;
+	scbp->SCB_NxtScb = NULL;
+	if (pCurHcb->HCS_LastPend != NULL) {
+		pCurHcb->HCS_LastPend->SCB_NxtScb = scbp;
+		pCurHcb->HCS_LastPend = scbp;
+	} else {
+		pCurHcb->HCS_FirstPend = scbp;
+		pCurHcb->HCS_LastPend = scbp;
+	}
+}
+
+/***************************************************************************/
+void tul_push_pend_scb(HCS * pCurHcb, SCB * scbp)
+{
+
+#if DEBUG_QUEUE
+	printk("Push pend SCB %lx; ", (ULONG) scbp);
+#endif
+	scbp->SCB_Status = SCB_PEND;
+	if ((scbp->SCB_NxtScb = pCurHcb->HCS_FirstPend) != NULL) {
+		pCurHcb->HCS_FirstPend = scbp;
+	} else {
+		pCurHcb->HCS_FirstPend = scbp;
+		pCurHcb->HCS_LastPend = scbp;
+	}
+}
+
+/***************************************************************************/
+SCB *tul_find_first_pend_scb(HCS * pCurHcb)
+{
+	SCB *pFirstPend;
+
+
+	pFirstPend = pCurHcb->HCS_FirstPend;
+	while (pFirstPend != NULL) {
+		if (pFirstPend->SCB_Opcode != ExecSCSI) {
+			return (pFirstPend);
+		}
+		if (pFirstPend->SCB_TagMsg == 0) {
+			if ((pCurHcb->HCS_ActTags[pFirstPend->SCB_Target] == 0) &&
+			    !(pCurHcb->HCS_Tcs[pFirstPend->SCB_Target].TCS_Flags & TCF_BUSY)) {
+				return (pFirstPend);
+			}
+		} else {
+			if ((pCurHcb->HCS_ActTags[pFirstPend->SCB_Target] >=
+			  pCurHcb->HCS_MaxTags[pFirstPend->SCB_Target]) |
+			    (pCurHcb->HCS_Tcs[pFirstPend->SCB_Target].TCS_Flags & TCF_BUSY)) {
+				pFirstPend = pFirstPend->SCB_NxtScb;
+				continue;
+			}
+			return (pFirstPend);
+		}
+		pFirstPend = pFirstPend->SCB_NxtScb;
+	}
+
+
+	return (pFirstPend);
+}
+/***************************************************************************/
+SCB *tul_pop_pend_scb(HCS * pCurHcb)
+{
+	SCB *pTmpScb;
+
+	if ((pTmpScb = pCurHcb->HCS_FirstPend) != NULL) {
+		if ((pCurHcb->HCS_FirstPend = pTmpScb->SCB_NxtScb) == NULL)
+			pCurHcb->HCS_LastPend = NULL;
+		pTmpScb->SCB_NxtScb = NULL;
+	}
+#if DEBUG_QUEUE
+	printk("Pop pend SCB %lx; ", (ULONG) pTmpScb);
+#endif
+	return (pTmpScb);
+}
+
+
+/***************************************************************************/
+void tul_unlink_pend_scb(HCS * pCurHcb, SCB * pCurScb)
+{
+	SCB *pTmpScb, *pPrevScb;
+
+#if DEBUG_QUEUE
+	printk("unlink pend SCB %lx; ", (ULONG) pCurScb);
+#endif
+
+	pPrevScb = pTmpScb = pCurHcb->HCS_FirstPend;
+	while (pTmpScb != NULL) {
+		if (pCurScb == pTmpScb) {	/* Unlink this SCB              */
+			if (pTmpScb == pCurHcb->HCS_FirstPend) {
+				if ((pCurHcb->HCS_FirstPend = pTmpScb->SCB_NxtScb) == NULL)
+					pCurHcb->HCS_LastPend = NULL;
+			} else {
+				pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb;
+				if (pTmpScb == pCurHcb->HCS_LastPend)
+					pCurHcb->HCS_LastPend = pPrevScb;
+			}
+			pTmpScb->SCB_NxtScb = NULL;
+			break;
+		}
+		pPrevScb = pTmpScb;
+		pTmpScb = pTmpScb->SCB_NxtScb;
+	}
+	return;
+}
+/***************************************************************************/
+void tul_append_busy_scb(HCS * pCurHcb, SCB * scbp)
+{
+
+#if DEBUG_QUEUE
+	printk("append busy SCB %lx; ", (ULONG) scbp);
+#endif
+	if (scbp->SCB_TagMsg)
+		pCurHcb->HCS_ActTags[scbp->SCB_Target]++;
+	else
+		pCurHcb->HCS_Tcs[scbp->SCB_Target].TCS_Flags |= TCF_BUSY;
+	scbp->SCB_Status = SCB_BUSY;
+	scbp->SCB_NxtScb = NULL;
+	if (pCurHcb->HCS_LastBusy != NULL) {
+		pCurHcb->HCS_LastBusy->SCB_NxtScb = scbp;
+		pCurHcb->HCS_LastBusy = scbp;
+	} else {
+		pCurHcb->HCS_FirstBusy = scbp;
+		pCurHcb->HCS_LastBusy = scbp;
+	}
+}
+
+/***************************************************************************/
+SCB *tul_pop_busy_scb(HCS * pCurHcb)
+{
+	SCB *pTmpScb;
+
+
+	if ((pTmpScb = pCurHcb->HCS_FirstBusy) != NULL) {
+		if ((pCurHcb->HCS_FirstBusy = pTmpScb->SCB_NxtScb) == NULL)
+			pCurHcb->HCS_LastBusy = NULL;
+		pTmpScb->SCB_NxtScb = NULL;
+		if (pTmpScb->SCB_TagMsg)
+			pCurHcb->HCS_ActTags[pTmpScb->SCB_Target]--;
+		else
+			pCurHcb->HCS_Tcs[pTmpScb->SCB_Target].TCS_Flags &= ~TCF_BUSY;
+	}
+#if DEBUG_QUEUE
+	printk("Pop busy SCB %lx; ", (ULONG) pTmpScb);
+#endif
+	return (pTmpScb);
+}
+
+/***************************************************************************/
+void tul_unlink_busy_scb(HCS * pCurHcb, SCB * pCurScb)
+{
+	SCB *pTmpScb, *pPrevScb;
+
+#if DEBUG_QUEUE
+	printk("unlink busy SCB %lx; ", (ULONG) pCurScb);
+#endif
+
+	pPrevScb = pTmpScb = pCurHcb->HCS_FirstBusy;
+	while (pTmpScb != NULL) {
+		if (pCurScb == pTmpScb) {	/* Unlink this SCB              */
+			if (pTmpScb == pCurHcb->HCS_FirstBusy) {
+				if ((pCurHcb->HCS_FirstBusy = pTmpScb->SCB_NxtScb) == NULL)
+					pCurHcb->HCS_LastBusy = NULL;
+			} else {
+				pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb;
+				if (pTmpScb == pCurHcb->HCS_LastBusy)
+					pCurHcb->HCS_LastBusy = pPrevScb;
+			}
+			pTmpScb->SCB_NxtScb = NULL;
+			if (pTmpScb->SCB_TagMsg)
+				pCurHcb->HCS_ActTags[pTmpScb->SCB_Target]--;
+			else
+				pCurHcb->HCS_Tcs[pTmpScb->SCB_Target].TCS_Flags &= ~TCF_BUSY;
+			break;
+		}
+		pPrevScb = pTmpScb;
+		pTmpScb = pTmpScb->SCB_NxtScb;
+	}
+	return;
+}
+
+/***************************************************************************/
+SCB *tul_find_busy_scb(HCS * pCurHcb, WORD tarlun)
+{
+	SCB *pTmpScb, *pPrevScb;
+	WORD scbp_tarlun;
+
+
+	pPrevScb = pTmpScb = pCurHcb->HCS_FirstBusy;
+	while (pTmpScb != NULL) {
+		scbp_tarlun = (pTmpScb->SCB_Lun << 8) | (pTmpScb->SCB_Target);
+		if (scbp_tarlun == tarlun) {	/* Unlink this SCB              */
+			break;
+		}
+		pPrevScb = pTmpScb;
+		pTmpScb = pTmpScb->SCB_NxtScb;
+	}
+#if DEBUG_QUEUE
+	printk("find busy SCB %lx; ", (ULONG) pTmpScb);
+#endif
+	return (pTmpScb);
+}
+
+/***************************************************************************/
+void tul_append_done_scb(HCS * pCurHcb, SCB * scbp)
+{
+
+#if DEBUG_QUEUE
+	printk("append done SCB %lx; ", (ULONG) scbp);
+#endif
+
+	scbp->SCB_Status = SCB_DONE;
+	scbp->SCB_NxtScb = NULL;
+	if (pCurHcb->HCS_LastDone != NULL) {
+		pCurHcb->HCS_LastDone->SCB_NxtScb = scbp;
+		pCurHcb->HCS_LastDone = scbp;
+	} else {
+		pCurHcb->HCS_FirstDone = scbp;
+		pCurHcb->HCS_LastDone = scbp;
+	}
+}
+
+/***************************************************************************/
+SCB *tul_find_done_scb(HCS * pCurHcb)
+{
+	SCB *pTmpScb;
+
+
+	if ((pTmpScb = pCurHcb->HCS_FirstDone) != NULL) {
+		if ((pCurHcb->HCS_FirstDone = pTmpScb->SCB_NxtScb) == NULL)
+			pCurHcb->HCS_LastDone = NULL;
+		pTmpScb->SCB_NxtScb = NULL;
+	}
+#if DEBUG_QUEUE
+	printk("find done SCB %lx; ", (ULONG) pTmpScb);
+#endif
+	return (pTmpScb);
+}
+
+/***************************************************************************/
+int tul_abort_srb(HCS * pCurHcb, ULONG srbp)
+{
+	ULONG flags;
+	SCB *pTmpScb, *pPrevScb;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+	save_flags(flags);
+	cli();
+#endif
+
+	if ((pCurHcb->HCS_Semaph == 0) && (pCurHcb->HCS_ActScb == NULL)) {
+		TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+		/* disable Jasmin SCSI Int        */
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+                spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#endif
+
+		tulip_main(pCurHcb);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+        	spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+#endif
+
+		pCurHcb->HCS_Semaph = 1;
+		TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+		spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+		restore_flags(flags);
+#endif
+
+		return SCSI_ABORT_SNOOZE;
+	}
+	pPrevScb = pTmpScb = pCurHcb->HCS_FirstPend;	/* Check Pend queue */
+	while (pTmpScb != NULL) {
+		/* 07/27/98 */
+		if (pTmpScb->SCB_Srb == (unsigned char *) srbp) {
+			if (pTmpScb == pCurHcb->HCS_ActScb) {
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+				spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+				restore_flags(flags);
+#endif
+				return SCSI_ABORT_BUSY;
+			} else if (pTmpScb == pCurHcb->HCS_FirstPend) {
+				if ((pCurHcb->HCS_FirstPend = pTmpScb->SCB_NxtScb) == NULL)
+					pCurHcb->HCS_LastPend = NULL;
+			} else {
+				pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb;
+				if (pTmpScb == pCurHcb->HCS_LastPend)
+					pCurHcb->HCS_LastPend = pPrevScb;
+			}
+			pTmpScb->SCB_HaStat = HOST_ABORTED;
+			pTmpScb->SCB_Flags |= SCF_DONE;
+			if (pTmpScb->SCB_Flags & SCF_POST)
+				(*pTmpScb->SCB_Post) ((BYTE *) pCurHcb, (BYTE *) pTmpScb);
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+			spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+			restore_flags(flags);
+#endif
+			return SCSI_ABORT_SUCCESS;
+		}
+		pPrevScb = pTmpScb;
+		pTmpScb = pTmpScb->SCB_NxtScb;
+	}
+
+	pPrevScb = pTmpScb = pCurHcb->HCS_FirstBusy;	/* Check Busy queue */
+	while (pTmpScb != NULL) {
+
+		if (pTmpScb->SCB_Srb == (unsigned char *) srbp) {
+
+			if (pTmpScb == pCurHcb->HCS_ActScb) {
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+				spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+				restore_flags(flags);
+#endif
+				return SCSI_ABORT_BUSY;
+			} else if (pTmpScb->SCB_TagMsg == 0) {
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+				spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+				restore_flags(flags);
+#endif
+				return SCSI_ABORT_BUSY;
+			} else {
+				pCurHcb->HCS_ActTags[pTmpScb->SCB_Target]--;
+				if (pTmpScb == pCurHcb->HCS_FirstBusy) {
+					if ((pCurHcb->HCS_FirstBusy = pTmpScb->SCB_NxtScb) == NULL)
+						pCurHcb->HCS_LastBusy = NULL;
+				} else {
+					pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb;
+					if (pTmpScb == pCurHcb->HCS_LastBusy)
+						pCurHcb->HCS_LastBusy = pPrevScb;
+				}
+				pTmpScb->SCB_NxtScb = NULL;
+
+
+				pTmpScb->SCB_HaStat = HOST_ABORTED;
+				pTmpScb->SCB_Flags |= SCF_DONE;
+				if (pTmpScb->SCB_Flags & SCF_POST)
+					(*pTmpScb->SCB_Post) ((BYTE *) pCurHcb, (BYTE *) pTmpScb);
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+				spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+				restore_flags(flags);
+#endif
+				return SCSI_ABORT_SUCCESS;
+			}
+		}
+		pPrevScb = pTmpScb;
+		pTmpScb = pTmpScb->SCB_NxtScb;
+	}
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+	restore_flags(flags);
+#endif
+	return (SCSI_ABORT_NOT_RUNNING);
+}
+
+/***************************************************************************/
+int tul_bad_seq(HCS * pCurHcb)
+{
+	SCB *pCurScb;
+
+	printk("tul_bad_seg c=%d\n", pCurHcb->HCS_Index);
+
+	if ((pCurScb = pCurHcb->HCS_ActScb) != NULL) {
+		tul_unlink_busy_scb(pCurHcb, pCurScb);
+		pCurScb->SCB_HaStat = HOST_BAD_PHAS;
+		pCurScb->SCB_TaStat = 0;
+		tul_append_done_scb(pCurHcb, pCurScb);
+	}
+	tul_stop_bm(pCurHcb);
+
+	tul_reset_scsi(pCurHcb, 8);	/* 7/29/98 */
+
+	return (tul_post_scsi_rst(pCurHcb));
+}
+
+/************************************************************************/
+int tul_device_reset(HCS * pCurHcb, ULONG pSrb, unsigned int target, unsigned int ResetFlags)
+{
+	ULONG flags;
+	SCB *pScb;
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+	save_flags(flags);
+	cli();
+#endif
+
+	if (ResetFlags & SCSI_RESET_ASYNCHRONOUS) {
+
+		if ((pCurHcb->HCS_Semaph == 0) && (pCurHcb->HCS_ActScb == NULL)) {
+			TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+			/* disable Jasmin SCSI Int        */
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+        		spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#endif
+
+			tulip_main(pCurHcb);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+        		spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+#endif
+
+			pCurHcb->HCS_Semaph = 1;
+			TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+			spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+			restore_flags(flags);
+#endif
+
+			return SCSI_RESET_SNOOZE;
+		}
+		pScb = pCurHcb->HCS_FirstBusy;	/* Check Busy queue */
+		while (pScb != NULL) {
+			if (pScb->SCB_Srb == (unsigned char *) pSrb)
+				break;
+			pScb = pScb->SCB_NxtScb;
+		}
+		if (pScb == NULL) {
+			printk("Unable to Reset - No SCB Found\n");
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+			spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+			restore_flags(flags);
+#endif
+			return SCSI_RESET_NOT_RUNNING;
+		}
+	}
+	if ((pScb = tul_alloc_scb(pCurHcb)) == NULL) {
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+		spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+		restore_flags(flags);
+#endif
+		return SCSI_RESET_NOT_RUNNING;
+	}
+	pScb->SCB_Opcode = BusDevRst;
+	pScb->SCB_Flags = SCF_POST;
+	pScb->SCB_Target = target;
+	pScb->SCB_Mode = 0;
+
+	pScb->SCB_Srb = 0;
+	if (ResetFlags & SCSI_RESET_SYNCHRONOUS) {
+		pScb->SCB_Srb = (unsigned char *) pSrb;
+	}
+	tul_push_pend_scb(pCurHcb, pScb);	/* push this SCB to Pending queue */
+
+	if (pCurHcb->HCS_Semaph == 1) {
+		TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+		/* disable Jasmin SCSI Int        */
+		pCurHcb->HCS_Semaph = 0;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+        	spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#endif
+
+		tulip_main(pCurHcb);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+                spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+#endif
+
+		pCurHcb->HCS_Semaph = 1;
+		TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F);
+	}
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+	restore_flags(flags);
+#endif
+	return SCSI_RESET_PENDING;
+}
+
+int tul_reset_scsi_bus(HCS * pCurHcb)
+{
+	ULONG flags;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+	save_flags(flags);
+	cli();
+#endif
+	TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+	pCurHcb->HCS_Semaph = 0;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+	restore_flags(flags);
+#endif
+
+	tul_stop_bm(pCurHcb);
+
+	tul_reset_scsi(pCurHcb, 2);	/* 7/29/98 */
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+	save_flags(flags);
+	cli();
+#endif
+	tul_post_scsi_rst(pCurHcb);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+        spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#endif
+
+	tulip_main(pCurHcb);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+        spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+#endif
+
+	pCurHcb->HCS_Semaph = 1;
+	TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F);
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+	restore_flags(flags);
+#endif
+	return (SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET);
+}
+
+/************************************************************************/
+void tul_exec_scb(HCS * pCurHcb, SCB * pCurScb)
+{
+	ULONG flags;
+
+	pCurScb->SCB_Mode = 0;
+
+	pCurScb->SCB_SGIdx = 0;
+	pCurScb->SCB_SGMax = pCurScb->SCB_SGLen;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+	save_flags(flags);
+	cli();
+#endif
+
+	tul_append_pend_scb(pCurHcb, pCurScb);	/* Append this SCB to Pending queue */
+
+/* VVVVV 07/21/98 */
+	if (pCurHcb->HCS_Semaph == 1) {
+		TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+		/* disable Jasmin SCSI Int        */
+		pCurHcb->HCS_Semaph = 0;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+        	spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#endif
+
+		tulip_main(pCurHcb);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+        	spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+#endif
+
+		pCurHcb->HCS_Semaph = 1;
+		TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F);
+	}
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+#else
+	restore_flags(flags);
+#endif
+	return;
+}
+
+/***************************************************************************/
+int tul_isr(HCS * pCurHcb)
+{
+	/* Enter critical section       */
+
+	if (TUL_RD(pCurHcb->HCS_Base, TUL_Int) & TSS_INT_PENDING) {
+		if (pCurHcb->HCS_Semaph == 1) {
+			TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+			/* Disable Tulip SCSI Int */
+			pCurHcb->HCS_Semaph = 0;
+
+			tulip_main(pCurHcb);
+
+			pCurHcb->HCS_Semaph = 1;
+			TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F);
+			return (1);
+		}
+	}
+	return (0);
+}
+
+/***************************************************************************/
+int tulip_main(HCS * pCurHcb)
+{
+	SCB *pCurScb;
+
+	for (;;) {
+
+		tulip_scsi(pCurHcb);	/* Call tulip_scsi              */
+
+		while ((pCurScb = tul_find_done_scb(pCurHcb)) != NULL) {	/* find done entry */
+			if (pCurScb->SCB_TaStat == QUEUE_FULL) {
+				pCurHcb->HCS_MaxTags[pCurScb->SCB_Target] =
+				    pCurHcb->HCS_ActTags[pCurScb->SCB_Target] - 1;
+				pCurScb->SCB_TaStat = 0;
+				tul_append_pend_scb(pCurHcb, pCurScb);
+				continue;
+			}
+			if (!(pCurScb->SCB_Mode & SCM_RSENS)) {		/* not in auto req. sense mode */
+				if (pCurScb->SCB_TaStat == 2) {
+
+					/* clr sync. nego flag */
+
+					if (pCurScb->SCB_Flags & SCF_SENSE) {
+						BYTE len;
+						len = pCurScb->SCB_SenseLen;
+						if (len == 0)
+							len = 1;
+						pCurScb->SCB_BufLen = pCurScb->SCB_SenseLen;
+						pCurScb->SCB_BufPtr = pCurScb->SCB_SensePtr;
+						pCurScb->SCB_Flags &= ~(SCF_SG | SCF_DIR);	/* for xfer_data_in */
+/*                      pCurScb->SCB_Flags |= SCF_NO_DCHK;      */
+						/* so, we won't report worng direction in xfer_data_in,
+						   and won't report HOST_DO_DU in state_6 */
+						pCurScb->SCB_Mode = SCM_RSENS;
+						pCurScb->SCB_Ident &= 0xBF;	/* Disable Disconnect */
+						pCurScb->SCB_TagMsg = 0;
+						pCurScb->SCB_TaStat = 0;
+						pCurScb->SCB_CDBLen = 6;
+						pCurScb->SCB_CDB[0] = SCSICMD_RequestSense;
+						pCurScb->SCB_CDB[1] = 0;
+						pCurScb->SCB_CDB[2] = 0;
+						pCurScb->SCB_CDB[3] = 0;
+						pCurScb->SCB_CDB[4] = len;
+						pCurScb->SCB_CDB[5] = 0;
+						tul_push_pend_scb(pCurHcb, pCurScb);
+						break;
+					}
+				}
+			} else {	/* in request sense mode */
+
+				if (pCurScb->SCB_TaStat == 2) {		/* check contition status again after sending
+									   requset sense cmd 0x3 */
+					pCurScb->SCB_HaStat = HOST_BAD_PHAS;
+				}
+				pCurScb->SCB_TaStat = 2;
+			}
+			pCurScb->SCB_Flags |= SCF_DONE;
+			if (pCurScb->SCB_Flags & SCF_POST) {
+				(*pCurScb->SCB_Post) ((BYTE *) pCurHcb, (BYTE *) pCurScb);
+			}
+		}		/* while */
+
+		/* find_active: */
+		if (TUL_RD(pCurHcb->HCS_Base, TUL_SStatus0) & TSS_INT_PENDING)
+			continue;
+
+		if (pCurHcb->HCS_ActScb) {	/* return to OS and wait for xfer_done_ISR/Selected_ISR */
+			return 1;	/* return to OS, enable interrupt */
+		}
+		/* Check pending SCB            */
+		if (tul_find_first_pend_scb(pCurHcb) == NULL) {
+			return 1;	/* return to OS, enable interrupt */
+		}
+	}			/* End of for loop */
+	/* statement won't reach here */
+}
+
+
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+/***************************************************************************/
+void tulip_scsi(HCS * pCurHcb)
+{
+	SCB *pCurScb;
+	TCS *pCurTcb;
+
+	/* make sure to service interrupt asap */
+
+	if ((pCurHcb->HCS_JSStatus0 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus0)) & TSS_INT_PENDING) {
+
+		pCurHcb->HCS_Phase = pCurHcb->HCS_JSStatus0 & TSS_PH_MASK;
+		pCurHcb->HCS_JSStatus1 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus1);
+		pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt);
+		if (pCurHcb->HCS_JSInt & TSS_SCSIRST_INT) {	/* SCSI bus reset detected      */
+			int_tul_scsi_rst(pCurHcb);
+			return;
+		}
+		if (pCurHcb->HCS_JSInt & TSS_RESEL_INT) {	/* if selected/reselected interrupt */
+			if (int_tul_resel(pCurHcb) == 0)
+				tul_next_state(pCurHcb);
+			return;
+		}
+		if (pCurHcb->HCS_JSInt & TSS_SEL_TIMEOUT) {
+			int_tul_busfree(pCurHcb);
+			return;
+		}
+		if (pCurHcb->HCS_JSInt & TSS_DISC_INT) {	/* BUS disconnection            */
+			int_tul_busfree(pCurHcb);	/* unexpected bus free or sel timeout */
+			return;
+		}
+		if (pCurHcb->HCS_JSInt & (TSS_FUNC_COMP | TSS_BUS_SERV)) {	/* func complete or Bus service */
+			if ((pCurScb = pCurHcb->HCS_ActScb) != NULL)
+				tul_next_state(pCurHcb);
+			return;
+		}
+	}
+	if (pCurHcb->HCS_ActScb != NULL)
+		return;
+
+	if ((pCurScb = tul_find_first_pend_scb(pCurHcb)) == NULL)
+		return;
+
+	/* program HBA's SCSI ID & target SCSI ID */
+	TUL_WR(pCurHcb->HCS_Base + TUL_SScsiId,
+	     (pCurHcb->HCS_SCSI_ID << 4) | (pCurScb->SCB_Target & 0x0F));
+	if (pCurScb->SCB_Opcode == ExecSCSI) {
+		pCurTcb = &pCurHcb->HCS_Tcs[pCurScb->SCB_Target];
+
+		if (pCurScb->SCB_TagMsg)
+			pCurTcb->TCS_DrvFlags |= TCF_DRV_EN_TAG;
+		else
+			pCurTcb->TCS_DrvFlags &= ~TCF_DRV_EN_TAG;
+
+		TUL_WR(pCurHcb->HCS_Base + TUL_SPeriod, pCurTcb->TCS_JS_Period);
+		if ((pCurTcb->TCS_Flags & (TCF_WDTR_DONE | TCF_NO_WDTR)) == 0) {	/* do wdtr negotiation          */
+			tul_select_atn_stop(pCurHcb, pCurScb);
+		} else {
+			if ((pCurTcb->TCS_Flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) {	/* do sync negotiation          */
+				tul_select_atn_stop(pCurHcb, pCurScb);
+			} else {
+				if (pCurScb->SCB_TagMsg)
+					tul_select_atn3(pCurHcb, pCurScb);
+				else
+					tul_select_atn(pCurHcb, pCurScb);
+			}
+		}
+		if (pCurScb->SCB_Flags & SCF_POLL) {
+			while (wait_tulip(pCurHcb) != -1) {
+				if (tul_next_state(pCurHcb) == -1)
+					break;
+			}
+		}
+	} else if (pCurScb->SCB_Opcode == BusDevRst) {
+		tul_select_atn_stop(pCurHcb, pCurScb);
+		pCurScb->SCB_NxtStat = 8;
+		if (pCurScb->SCB_Flags & SCF_POLL) {
+			while (wait_tulip(pCurHcb) != -1) {
+				if (tul_next_state(pCurHcb) == -1)
+					break;
+			}
+		}
+	} else if (pCurScb->SCB_Opcode == AbortCmd) {
+		ULONG srbp;
+
+		srbp = (ULONG) pCurScb->SCB_Srb;
+/* 08/03/98 */
+		if (tul_abort_srb(pCurHcb, srbp) != 0) {
+
+
+			tul_unlink_pend_scb(pCurHcb, pCurScb);
+
+			tul_release_scb(pCurHcb, pCurScb);
+		} else {
+			pCurScb->SCB_Opcode = BusDevRst;
+			tul_select_atn_stop(pCurHcb, pCurScb);
+			pCurScb->SCB_NxtStat = 8;
+		}
+
+/* 08/03/98 */
+	} else {
+		tul_unlink_pend_scb(pCurHcb, pCurScb);
+		pCurScb->SCB_HaStat = 0x16;	/* bad command */
+		tul_append_done_scb(pCurHcb, pCurScb);
+	}
+	return;
+}
+
+
+/***************************************************************************/
+int tul_next_state(HCS * pCurHcb)
+{
+	int next;
+
+	next = pCurHcb->HCS_ActScb->SCB_NxtStat;
+	for (;;) {
+		switch (next) {
+		case 1:
+			next = tul_state_1(pCurHcb);
+			break;
+		case 2:
+			next = tul_state_2(pCurHcb);
+			break;
+		case 3:
+			next = tul_state_3(pCurHcb);
+			break;
+		case 4:
+			next = tul_state_4(pCurHcb);
+			break;
+		case 5:
+			next = tul_state_5(pCurHcb);
+			break;
+		case 6:
+			next = tul_state_6(pCurHcb);
+			break;
+		case 7:
+			next = tul_state_7(pCurHcb);
+			break;
+		case 8:
+			return (tul_bus_device_reset(pCurHcb));
+		default:
+			return (tul_bad_seq(pCurHcb));
+		}
+		if (next <= 0)
+			return next;
+	}
+}
+
+
+/***************************************************************************/
+/* sTate after selection with attention & stop */
+int tul_state_1(HCS * pCurHcb)
+{
+	SCB *pCurScb = pCurHcb->HCS_ActScb;
+	TCS *pCurTcb = pCurHcb->HCS_ActTcs;
+#if DEBUG_STATE
+	printk("-s1-");
+#endif
+
+	tul_unlink_pend_scb(pCurHcb, pCurScb);
+	tul_append_busy_scb(pCurHcb, pCurScb);
+
+	TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurTcb->TCS_SConfig0);
+	/* ATN on */
+	if (pCurHcb->HCS_Phase == MSG_OUT) {
+
+		TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, (TSC_EN_BUS_IN | TSC_HW_RESELECT));
+
+		TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_Ident);
+
+		if (pCurScb->SCB_TagMsg) {
+			TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_TagMsg);
+			TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_TagId);
+		}
+		if ((pCurTcb->TCS_Flags & (TCF_WDTR_DONE | TCF_NO_WDTR)) == 0) {
+
+			pCurTcb->TCS_Flags |= TCF_WDTR_DONE;
+
+			TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_EXTEND);
+			TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 2);	/* Extended msg length */
+			TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 3);	/* Sync request */
+			TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 1);	/* Start from 16 bits */
+		} else if ((pCurTcb->TCS_Flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) {
+
+			pCurTcb->TCS_Flags |= TCF_SYNC_DONE;
+
+			TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_EXTEND);
+			TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 3);	/* extended msg length */
+			TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 1);	/* sync request */
+			TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, tul_rate_tbl[pCurTcb->TCS_Flags & TCF_SCSI_RATE]);
+			TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MAX_OFFSET);	/* REQ/ACK offset */
+		}
+		TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+		if (wait_tulip(pCurHcb) == -1)
+			return (-1);
+	}
+	TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+	TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, (TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)));
+	return (3);
+}
+
+
+/***************************************************************************/
+/* state after selection with attention */
+/* state after selection with attention3 */
+int tul_state_2(HCS * pCurHcb)
+{
+	SCB *pCurScb = pCurHcb->HCS_ActScb;
+	TCS *pCurTcb = pCurHcb->HCS_ActTcs;
+#if DEBUG_STATE
+	printk("-s2-");
+#endif
+
+	tul_unlink_pend_scb(pCurHcb, pCurScb);
+	tul_append_busy_scb(pCurHcb, pCurScb);
+
+	TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurTcb->TCS_SConfig0);
+
+	if (pCurHcb->HCS_JSStatus1 & TSS_CMD_PH_CMP) {
+		return (4);
+	}
+	TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+	TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, (TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)));
+	return (3);
+}
+
+/***************************************************************************/
+/* state before CDB xfer is done */
+int tul_state_3(HCS * pCurHcb)
+{
+	SCB *pCurScb = pCurHcb->HCS_ActScb;
+	TCS *pCurTcb = pCurHcb->HCS_ActTcs;
+	int i;
+
+#if DEBUG_STATE
+	printk("-s3-");
+#endif
+	for (;;) {
+		switch (pCurHcb->HCS_Phase) {
+		case CMD_OUT:	/* Command out phase            */
+			for (i = 0; i < (int) pCurScb->SCB_CDBLen; i++)
+				TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_CDB[i]);
+			TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+			if (wait_tulip(pCurHcb) == -1)
+				return (-1);
+			if (pCurHcb->HCS_Phase == CMD_OUT) {
+				return (tul_bad_seq(pCurHcb));
+			}
+			return (4);
+
+		case MSG_IN:	/* Message in phase             */
+			pCurScb->SCB_NxtStat = 3;
+			if (tul_msgin(pCurHcb) == -1)
+				return (-1);
+			break;
+
+		case STATUS_IN:	/* Status phase                 */
+			if (tul_status_msg(pCurHcb) == -1)
+				return (-1);
+			break;
+
+		case MSG_OUT:	/* Message out phase            */
+			if (pCurTcb->TCS_Flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) {
+
+				TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_NOP);		/* msg nop */
+				TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+				if (wait_tulip(pCurHcb) == -1)
+					return (-1);
+
+			} else {
+				pCurTcb->TCS_Flags |= TCF_SYNC_DONE;
+
+				TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_EXTEND);
+				TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 3);	/* ext. msg len */
+				TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 1);	/* sync request */
+				TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, tul_rate_tbl[pCurTcb->TCS_Flags & TCF_SCSI_RATE]);
+				TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MAX_OFFSET);	/* REQ/ACK offset */
+				TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+				if (wait_tulip(pCurHcb) == -1)
+					return (-1);
+				TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+				TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7));
+
+			}
+			break;
+
+		default:
+			return (tul_bad_seq(pCurHcb));
+		}
+	}
+}
+
+
+/***************************************************************************/
+int tul_state_4(HCS * pCurHcb)
+{
+	SCB *pCurScb = pCurHcb->HCS_ActScb;
+
+#if DEBUG_STATE
+	printk("-s4-");
+#endif
+	if ((pCurScb->SCB_Flags & SCF_DIR) == SCF_NO_XF) {
+		return (6);	/* Go to state 6                */
+	}
+	for (;;) {
+		if (pCurScb->SCB_BufLen == 0)
+			return (6);	/* Go to state 6                */
+
+		switch (pCurHcb->HCS_Phase) {
+
+		case STATUS_IN:	/* Status phase                 */
+			if ((pCurScb->SCB_Flags & SCF_DIR) != 0) {	/* if direction bit set then report data underrun */
+				pCurScb->SCB_HaStat = HOST_DO_DU;
+			}
+			if ((tul_status_msg(pCurHcb)) == -1)
+				return (-1);
+			break;
+
+		case MSG_IN:	/* Message in phase             */
+			pCurScb->SCB_NxtStat = 0x4;
+			if (tul_msgin(pCurHcb) == -1)
+				return (-1);
+			break;
+
+		case MSG_OUT:	/* Message out phase            */
+			if (pCurHcb->HCS_JSStatus0 & TSS_PAR_ERROR) {
+				pCurScb->SCB_BufLen = 0;
+				pCurScb->SCB_HaStat = HOST_DO_DU;
+				if (tul_msgout_ide(pCurHcb) == -1)
+					return (-1);
+				return (6);	/* Go to state 6                */
+			} else {
+				TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_NOP);		/* msg nop */
+				TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+				if (wait_tulip(pCurHcb) == -1)
+					return (-1);
+			}
+			break;
+
+		case DATA_IN:	/* Data in phase                */
+			return (tul_xfer_data_in(pCurHcb));
+
+		case DATA_OUT:	/* Data out phase               */
+			return (tul_xfer_data_out(pCurHcb));
+
+		default:
+			return (tul_bad_seq(pCurHcb));
+		}
+	}
+}
+
+
+/***************************************************************************/
+/* state after dma xfer done or phase change before xfer done */
+int tul_state_5(HCS * pCurHcb)
+{
+	SCB *pCurScb = pCurHcb->HCS_ActScb;
+	long cnt, xcnt;		/* cannot use unsigned !! code: if (xcnt < 0) */
+
+#if DEBUG_STATE
+	printk("-s5-");
+#endif
+/*------ get remaining count -------*/
+
+	cnt = TUL_RDLONG(pCurHcb->HCS_Base, TUL_SCnt0) & 0x0FFFFFF;
+
+	if (TUL_RD(pCurHcb->HCS_Base, TUL_XCmd) & 0x20) {
+		/* ----------------------- DATA_IN ----------------------------- */
+		/* check scsi parity error */
+		if (pCurHcb->HCS_JSStatus0 & TSS_PAR_ERROR) {
+			pCurScb->SCB_HaStat = HOST_DO_DU;
+		}
+		if (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & XPEND) {	/* DMA xfer pending, Send STOP  */
+			/* tell Hardware  scsi xfer has been terminated */
+			TUL_WR(pCurHcb->HCS_Base + TUL_XCtrl, TUL_RD(pCurHcb->HCS_Base, TUL_XCtrl) | 0x80);
+			/* wait until DMA xfer not pending */
+			while (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & XPEND);
+		}
+	} else {
+/*-------- DATA OUT -----------*/
+		if ((TUL_RD(pCurHcb->HCS_Base, TUL_SStatus1) & TSS_XFER_CMP) == 0) {
+			if (pCurHcb->HCS_ActTcs->TCS_JS_Period & TSC_WIDE_SCSI)
+				cnt += (TUL_RD(pCurHcb->HCS_Base, TUL_SFifoCnt) & 0x1F) << 1;
+			else
+				cnt += (TUL_RD(pCurHcb->HCS_Base, TUL_SFifoCnt) & 0x1F);
+		}
+		if (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & XPEND) {	/* if DMA xfer is pending, abort DMA xfer */
+			TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_X_ABT);
+			/* wait Abort DMA xfer done */
+			while ((TUL_RD(pCurHcb->HCS_Base, TUL_Int) & XABT) == 0);
+		}
+		if ((cnt == 1) && (pCurHcb->HCS_Phase == DATA_OUT)) {
+			TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+			if (wait_tulip(pCurHcb) == -1) {
+				return (-1);
+			}
+			cnt = 0;
+		} else {
+			if ((TUL_RD(pCurHcb->HCS_Base, TUL_SStatus1) & TSS_XFER_CMP) == 0)
+				TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+		}
+	}
+
+	if (cnt == 0) {
+		pCurScb->SCB_BufLen = 0;
+		return (6);	/* Go to state 6                */
+	}
+	/* Update active data pointer */
+	xcnt = (long) pCurScb->SCB_BufLen - cnt;	/* xcnt== bytes already xferred */
+	pCurScb->SCB_BufLen = (U32) cnt;	/* cnt == bytes left to be xferred */
+	if (pCurScb->SCB_Flags & SCF_SG) {
+		register SG *sgp;
+		ULONG i;
+
+		sgp = &pCurScb->SCB_SGList[pCurScb->SCB_SGIdx];
+		for (i = pCurScb->SCB_SGIdx; i < pCurScb->SCB_SGMax; sgp++, i++) {
+			xcnt -= (long) sgp->SG_Len;
+			if (xcnt < 0) {		/* this sgp xfer half done */
+				xcnt += (long) sgp->SG_Len;	/* xcnt == bytes xferred in this sgp */
+				sgp->SG_Ptr += (U32) xcnt;	/* new ptr to be xfer */
+				sgp->SG_Len -= (U32) xcnt;	/* new len to be xfer */
+				pCurScb->SCB_BufPtr += ((U32) (i - pCurScb->SCB_SGIdx) << 3);
+				/* new SG table ptr */
+				pCurScb->SCB_SGLen = (BYTE) (pCurScb->SCB_SGMax - i);
+				/* new SG table len */
+				pCurScb->SCB_SGIdx = (WORD) i;
+				/* for next disc and come in this loop */
+				return (4);	/* Go to state 4                */
+			}
+			/* else (xcnt >= 0 , i.e. this sgp already xferred */
+		}		/* for */
+		return (6);	/* Go to state 6                */
+	} else {
+		pCurScb->SCB_BufPtr += (U32) xcnt;
+	}
+	return (4);		/* Go to state 4                */
+}
+
+/***************************************************************************/
+/* state after Data phase */
+int tul_state_6(HCS * pCurHcb)
+{
+	SCB *pCurScb = pCurHcb->HCS_ActScb;
+
+#if DEBUG_STATE
+	printk("-s6-");
+#endif
+	for (;;) {
+		switch (pCurHcb->HCS_Phase) {
+		case STATUS_IN:	/* Status phase                 */
+			if ((tul_status_msg(pCurHcb)) == -1)
+				return (-1);
+			break;
+
+		case MSG_IN:	/* Message in phase             */
+			pCurScb->SCB_NxtStat = 6;
+			if ((tul_msgin(pCurHcb)) == -1)
+				return (-1);
+			break;
+
+		case MSG_OUT:	/* Message out phase            */
+			TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_NOP);		/* msg nop */
+			TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+			if (wait_tulip(pCurHcb) == -1)
+				return (-1);
+			break;
+
+		case DATA_IN:	/* Data in phase                */
+			return (tul_xpad_in(pCurHcb));
+
+		case DATA_OUT:	/* Data out phase               */
+			return (tul_xpad_out(pCurHcb));
+
+		default:
+			return (tul_bad_seq(pCurHcb));
+		}
+	}
+}
+
+/***************************************************************************/
+int tul_state_7(HCS * pCurHcb)
+{
+	int cnt, i;
+
+#if DEBUG_STATE
+	printk("-s7-");
+#endif
+	/* flush SCSI FIFO */
+	cnt = TUL_RD(pCurHcb->HCS_Base, TUL_SFifoCnt) & 0x1F;
+	if (cnt) {
+		for (i = 0; i < cnt; i++)
+			TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);
+	}
+	switch (pCurHcb->HCS_Phase) {
+	case DATA_IN:		/* Data in phase                */
+	case DATA_OUT:		/* Data out phase               */
+		return (tul_bad_seq(pCurHcb));
+	default:
+		return (6);	/* Go to state 6                */
+	}
+}
+
+/***************************************************************************/
+int tul_xfer_data_in(HCS * pCurHcb)
+{
+	SCB *pCurScb = pCurHcb->HCS_ActScb;
+
+	if ((pCurScb->SCB_Flags & SCF_DIR) == SCF_DOUT) {
+		return (6);	/* wrong direction */
+	}
+	TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, pCurScb->SCB_BufLen);
+
+	TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_DMA_IN);	/* 7/25/95 */
+
+	if (pCurScb->SCB_Flags & SCF_SG) {	/* S/G xfer */
+		TUL_WRLONG(pCurHcb->HCS_Base + TUL_XCntH, ((ULONG) pCurScb->SCB_SGLen) << 3);
+		TUL_WRLONG(pCurHcb->HCS_Base + TUL_XAddH, pCurScb->SCB_BufPtr);
+		TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_SG_IN);
+	} else {
+		TUL_WRLONG(pCurHcb->HCS_Base + TUL_XCntH, pCurScb->SCB_BufLen);
+		TUL_WRLONG(pCurHcb->HCS_Base + TUL_XAddH, pCurScb->SCB_BufPtr);
+		TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_X_IN);
+	}
+	pCurScb->SCB_NxtStat = 0x5;
+	return (0);		/* return to OS, wait xfer done , let jas_isr come in */
+}
+
+
+/***************************************************************************/
+int tul_xfer_data_out(HCS * pCurHcb)
+{
+	SCB *pCurScb = pCurHcb->HCS_ActScb;
+
+	if ((pCurScb->SCB_Flags & SCF_DIR) == SCF_DIN) {
+		return (6);	/* wrong direction */
+	}
+	TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, pCurScb->SCB_BufLen);
+	TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_DMA_OUT);
+
+	if (pCurScb->SCB_Flags & SCF_SG) {	/* S/G xfer */
+		TUL_WRLONG(pCurHcb->HCS_Base + TUL_XCntH, ((ULONG) pCurScb->SCB_SGLen) << 3);
+		TUL_WRLONG(pCurHcb->HCS_Base + TUL_XAddH, pCurScb->SCB_BufPtr);
+		TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_SG_OUT);
+	} else {
+		TUL_WRLONG(pCurHcb->HCS_Base + TUL_XCntH, pCurScb->SCB_BufLen);
+		TUL_WRLONG(pCurHcb->HCS_Base + TUL_XAddH, pCurScb->SCB_BufPtr);
+		TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_X_OUT);
+	}
+
+	pCurScb->SCB_NxtStat = 0x5;
+	return (0);		/* return to OS, wait xfer done , let jas_isr come in */
+}
+
+
+/***************************************************************************/
+int tul_xpad_in(HCS * pCurHcb)
+{
+	SCB *pCurScb = pCurHcb->HCS_ActScb;
+	TCS *pCurTcb = pCurHcb->HCS_ActTcs;
+
+	if ((pCurScb->SCB_Flags & SCF_DIR) != SCF_NO_DCHK) {
+		pCurScb->SCB_HaStat = HOST_DO_DU;	/* over run             */
+	}
+	for (;;) {
+		if (pCurTcb->TCS_JS_Period & TSC_WIDE_SCSI)
+			TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 2);
+		else
+			TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+
+		TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+		if ((wait_tulip(pCurHcb)) == -1) {
+			return (-1);
+		}
+		if (pCurHcb->HCS_Phase != DATA_IN) {
+			TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+			return (6);
+		}
+		TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);
+	}
+}
+
+int tul_xpad_out(HCS * pCurHcb)
+{
+	SCB *pCurScb = pCurHcb->HCS_ActScb;
+	TCS *pCurTcb = pCurHcb->HCS_ActTcs;
+
+	if ((pCurScb->SCB_Flags & SCF_DIR) != SCF_NO_DCHK) {
+		pCurScb->SCB_HaStat = HOST_DO_DU;	/* over run             */
+	}
+	for (;;) {
+		if (pCurTcb->TCS_JS_Period & TSC_WIDE_SCSI)
+			TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 2);
+		else
+			TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+
+		TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 0);
+		TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+		if ((wait_tulip(pCurHcb)) == -1) {
+			return (-1);
+		}
+		if (pCurHcb->HCS_Phase != DATA_OUT) {	/* Disable wide CPU to allow read 16 bits */
+			TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT);
+			TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+			return (6);
+		}
+	}
+}
+
+
+/***************************************************************************/
+int tul_status_msg(HCS * pCurHcb)
+{				/* status & MSG_IN */
+	SCB *pCurScb = pCurHcb->HCS_ActScb;
+	BYTE msg;
+
+	TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_CMD_COMP);
+	if ((wait_tulip(pCurHcb)) == -1) {
+		return (-1);
+	}
+	/* get status */
+	pCurScb->SCB_TaStat = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);
+
+	if (pCurHcb->HCS_Phase == MSG_OUT) {
+		if (pCurHcb->HCS_JSStatus0 & TSS_PAR_ERROR) {
+			TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_PARITY);
+		} else {
+			TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_NOP);
+		}
+		TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+		return (wait_tulip(pCurHcb));
+	}
+	if (pCurHcb->HCS_Phase == MSG_IN) {
+		msg = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);
+		if (pCurHcb->HCS_JSStatus0 & TSS_PAR_ERROR) {	/* Parity error                 */
+			if ((tul_msgin_accept(pCurHcb)) == -1)
+				return (-1);
+			if (pCurHcb->HCS_Phase != MSG_OUT)
+				return (tul_bad_seq(pCurHcb));
+			TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_PARITY);
+			TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+			return (wait_tulip(pCurHcb));
+		}
+		if (msg == 0) {	/* Command complete             */
+
+			if ((pCurScb->SCB_TaStat & 0x18) == 0x10) {	/* No link support              */
+				return (tul_bad_seq(pCurHcb));
+			}
+			TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+			TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_MSG_ACCEPT);
+			return tul_wait_done_disc(pCurHcb);
+
+		}
+		if ((msg == MSG_LINK_COMP) || (msg == MSG_LINK_FLAG)) {
+			if ((pCurScb->SCB_TaStat & 0x18) == 0x10)
+				return (tul_msgin_accept(pCurHcb));
+		}
+	}
+	return (tul_bad_seq(pCurHcb));
+}
+
+
+/***************************************************************************/
+/* scsi bus free */
+int int_tul_busfree(HCS * pCurHcb)
+{
+	SCB *pCurScb = pCurHcb->HCS_ActScb;
+
+	if (pCurScb != NULL) {
+		if (pCurScb->SCB_Status & SCB_SELECT) {		/* selection timeout */
+			tul_unlink_pend_scb(pCurHcb, pCurScb);
+			pCurScb->SCB_HaStat = HOST_SEL_TOUT;
+			tul_append_done_scb(pCurHcb, pCurScb);
+		} else {	/* Unexpected bus free          */
+			tul_unlink_busy_scb(pCurHcb, pCurScb);
+			pCurScb->SCB_HaStat = HOST_BUS_FREE;
+			tul_append_done_scb(pCurHcb, pCurScb);
+		}
+		pCurHcb->HCS_ActScb = NULL;
+		pCurHcb->HCS_ActTcs = NULL;
+	}
+	TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);		/* Flush SCSI FIFO  */
+	TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT);
+	TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT);	/* Enable HW reselect       */
+	return (-1);
+}
+
+
+/***************************************************************************/
+/* scsi bus reset */
+int int_tul_scsi_rst(HCS * pCurHcb)
+{
+	SCB *pCurScb;
+	int i;
+
+	/* if DMA xfer is pending, abort DMA xfer */
+	if (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & 0x01) {
+		TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_X_ABT | TAX_X_CLR_FIFO);
+		/* wait Abort DMA xfer done */
+		while ((TUL_RD(pCurHcb->HCS_Base, TUL_Int) & 0x04) == 0);
+		TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+	}
+	/* Abort all active & disconnected scb */
+	while ((pCurScb = tul_pop_busy_scb(pCurHcb)) != NULL) {
+		pCurScb->SCB_HaStat = HOST_BAD_PHAS;
+		tul_append_done_scb(pCurHcb, pCurScb);
+	}
+	pCurHcb->HCS_ActScb = NULL;
+	pCurHcb->HCS_ActTcs = NULL;
+
+	/* clr sync nego. done flag */
+	for (i = 0; i < pCurHcb->HCS_MaxTar; i++) {
+		pCurHcb->HCS_Tcs[i].TCS_Flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE);
+	}
+	return (-1);
+}
+
+
+/***************************************************************************/
+/* scsi reselection */
+int int_tul_resel(HCS * pCurHcb)
+{
+	SCB *pCurScb;
+	TCS *pCurTcb;
+	BYTE tag, msg = 0;
+	BYTE tar, lun;
+
+	if ((pCurScb = pCurHcb->HCS_ActScb) != NULL) {
+		if (pCurScb->SCB_Status & SCB_SELECT) {		/* if waiting for selection complete */
+			pCurScb->SCB_Status &= ~SCB_SELECT;
+		}
+		pCurHcb->HCS_ActScb = NULL;
+	}
+	/* --------- get target id---------------------- */
+	tar = TUL_RD(pCurHcb->HCS_Base, TUL_SBusId);
+	/* ------ get LUN from Identify message----------- */
+	lun = TUL_RD(pCurHcb->HCS_Base, TUL_SIdent) & 0x0F;
+	/* 07/22/98 from 0x1F -> 0x0F */
+	pCurTcb = &pCurHcb->HCS_Tcs[tar];
+	pCurHcb->HCS_ActTcs = pCurTcb;
+	TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurTcb->TCS_SConfig0);
+	TUL_WR(pCurHcb->HCS_Base + TUL_SPeriod, pCurTcb->TCS_JS_Period);
+
+
+	/* ------------- tag queueing ? ------------------- */
+	if (pCurTcb->TCS_DrvFlags & TCF_DRV_EN_TAG) {
+		if ((tul_msgin_accept(pCurHcb)) == -1)
+			return (-1);
+		if (pCurHcb->HCS_Phase != MSG_IN)
+			goto no_tag;
+		TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+		TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+		if ((wait_tulip(pCurHcb)) == -1)
+			return (-1);
+		msg = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);	/* Read Tag Message    */
+
+		if ((msg < MSG_STAG) || (msg > MSG_OTAG))	/* Is simple Tag      */
+			goto no_tag;
+
+		if ((tul_msgin_accept(pCurHcb)) == -1)
+			return (-1);
+
+		if (pCurHcb->HCS_Phase != MSG_IN)
+			goto no_tag;
+
+		TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+		TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+		if ((wait_tulip(pCurHcb)) == -1)
+			return (-1);
+		tag = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);	/* Read Tag ID       */
+		pCurScb = pCurHcb->HCS_Scb + tag;
+		if ((pCurScb->SCB_Target != tar) || (pCurScb->SCB_Lun != lun)) {
+			return tul_msgout_abort_tag(pCurHcb);
+		}
+		if (pCurScb->SCB_Status != SCB_BUSY) {	/* 03/24/95             */
+			return tul_msgout_abort_tag(pCurHcb);
+		}
+		pCurHcb->HCS_ActScb = pCurScb;
+		if ((tul_msgin_accept(pCurHcb)) == -1)
+			return (-1);
+	} else {		/* No tag               */
+	      no_tag:
+		if ((pCurScb = tul_find_busy_scb(pCurHcb, tar | (lun << 8))) == NULL) {
+			return tul_msgout_abort_targ(pCurHcb);
+		}
+		pCurHcb->HCS_ActScb = pCurScb;
+		if (!(pCurTcb->TCS_DrvFlags & TCF_DRV_EN_TAG)) {
+			if ((tul_msgin_accept(pCurHcb)) == -1)
+				return (-1);
+		}
+	}
+	return 0;
+}
+
+
+/***************************************************************************/
+int int_tul_bad_seq(HCS * pCurHcb)
+{				/* target wrong phase           */
+	SCB *pCurScb;
+	int i;
+
+	tul_reset_scsi(pCurHcb, 10);
+
+	while ((pCurScb = tul_pop_busy_scb(pCurHcb)) != NULL) {
+		pCurScb->SCB_HaStat = HOST_BAD_PHAS;
+		tul_append_done_scb(pCurHcb, pCurScb);
+	}
+	for (i = 0; i < pCurHcb->HCS_MaxTar; i++) {
+		pCurHcb->HCS_Tcs[i].TCS_Flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE);;
+	}
+	return (-1);
+}
+
+
+/***************************************************************************/
+int tul_msgout_abort_targ(HCS * pCurHcb)
+{
+
+	TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+	if (tul_msgin_accept(pCurHcb) == -1)
+		return (-1);
+	if (pCurHcb->HCS_Phase != MSG_OUT)
+		return (tul_bad_seq(pCurHcb));
+
+	TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_ABORT);
+	TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+
+	return tul_wait_disc(pCurHcb);
+}
+
+/***************************************************************************/
+int tul_msgout_abort_tag(HCS * pCurHcb)
+{
+
+	TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+	if (tul_msgin_accept(pCurHcb) == -1)
+		return (-1);
+	if (pCurHcb->HCS_Phase != MSG_OUT)
+		return (tul_bad_seq(pCurHcb));
+
+	TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_ABORT_TAG);
+	TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+
+	return tul_wait_disc(pCurHcb);
+
+}
+
+/***************************************************************************/
+int tul_msgin(HCS * pCurHcb)
+{
+	TCS *pCurTcb;
+
+	for (;;) {
+
+		TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+
+		TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+		TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+		if ((wait_tulip(pCurHcb)) == -1)
+			return (-1);
+
+		switch (TUL_RD(pCurHcb->HCS_Base, TUL_SFifo)) {
+		case MSG_DISC:	/* Disconnect msg */
+			TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_MSG_ACCEPT);
+
+			return tul_wait_disc(pCurHcb);
+
+		case MSG_SDP:
+		case MSG_RESTORE:
+		case MSG_NOP:
+			tul_msgin_accept(pCurHcb);
+			break;
+
+		case MSG_REJ:	/* Clear ATN first              */
+			TUL_WR(pCurHcb->HCS_Base + TUL_SSignal,
+			       (TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)));
+			pCurTcb = pCurHcb->HCS_ActTcs;
+			if ((pCurTcb->TCS_Flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) {	/* do sync nego */
+				TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+			}
+			tul_msgin_accept(pCurHcb);
+			break;
+
+		case MSG_EXTEND:	/* extended msg */
+			tul_msgin_extend(pCurHcb);
+			break;
+
+		case MSG_IGNOREWIDE:
+			tul_msgin_accept(pCurHcb);
+			break;
+
+			/* get */
+			TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+			if (wait_tulip(pCurHcb) == -1)
+				return -1;
+
+			TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 0);	/* put pad  */
+			TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);	/* get IGNORE field */
+			TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);	/* get pad */
+
+			tul_msgin_accept(pCurHcb);
+			break;
+
+		case MSG_COMP:
+			{
+				TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+				TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_MSG_ACCEPT);
+				return tul_wait_done_disc(pCurHcb);
+			}
+		default:
+			tul_msgout_reject(pCurHcb);
+			break;
+		}
+		if (pCurHcb->HCS_Phase != MSG_IN)
+			return (pCurHcb->HCS_Phase);
+	}
+	/* statement won't reach here */
+}
+
+
+
+
+/***************************************************************************/
+int tul_msgout_reject(HCS * pCurHcb)
+{
+
+	TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+
+	if ((tul_msgin_accept(pCurHcb)) == -1)
+		return (-1);
+
+	if (pCurHcb->HCS_Phase == MSG_OUT) {
+		TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_REJ);		/* Msg reject           */
+		TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+		return (wait_tulip(pCurHcb));
+	}
+	return (pCurHcb->HCS_Phase);
+}
+
+
+
+/***************************************************************************/
+int tul_msgout_ide(HCS * pCurHcb)
+{
+	TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_IDE);		/* Initiator Detected Error */
+	TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+	return (wait_tulip(pCurHcb));
+}
+
+
+/***************************************************************************/
+int tul_msgin_extend(HCS * pCurHcb)
+{
+	BYTE len, idx;
+
+	if (tul_msgin_accept(pCurHcb) != MSG_IN)
+		return (pCurHcb->HCS_Phase);
+
+	/* Get extended msg length      */
+	TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+	TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+	if (wait_tulip(pCurHcb) == -1)
+		return (-1);
+
+	len = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);
+	pCurHcb->HCS_Msg[0] = len;
+	for (idx = 1; len != 0; len--) {
+
+		if ((tul_msgin_accept(pCurHcb)) != MSG_IN)
+			return (pCurHcb->HCS_Phase);
+		TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+		TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+		if (wait_tulip(pCurHcb) == -1)
+			return (-1);
+		pCurHcb->HCS_Msg[idx++] = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);
+	}
+	if (pCurHcb->HCS_Msg[1] == 1) {		/* if it's synchronous data transfer request */
+		if (pCurHcb->HCS_Msg[0] != 3)	/* if length is not right */
+			return (tul_msgout_reject(pCurHcb));
+		if (pCurHcb->HCS_ActTcs->TCS_Flags & TCF_NO_SYNC_NEGO) {	/* Set OFFSET=0 to do async, nego back */
+			pCurHcb->HCS_Msg[3] = 0;
+		} else {
+			if ((tul_msgin_sync(pCurHcb) == 0) &&
+			    (pCurHcb->HCS_ActTcs->TCS_Flags & TCF_SYNC_DONE)) {
+				tul_sync_done(pCurHcb);
+				return (tul_msgin_accept(pCurHcb));
+			}
+		}
+
+		TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+		if ((tul_msgin_accept(pCurHcb)) != MSG_OUT)
+			return (pCurHcb->HCS_Phase);
+		/* sync msg out */
+		TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+
+		tul_sync_done(pCurHcb);
+
+		TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_EXTEND);
+		TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 3);
+		TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 1);
+		TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurHcb->HCS_Msg[2]);
+		TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurHcb->HCS_Msg[3]);
+
+		TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+		return (wait_tulip(pCurHcb));
+	}
+	if ((pCurHcb->HCS_Msg[0] != 2) || (pCurHcb->HCS_Msg[1] != 3))
+		return (tul_msgout_reject(pCurHcb));
+	/* if it's WIDE DATA XFER REQ   */
+	if (pCurHcb->HCS_ActTcs->TCS_Flags & TCF_NO_WDTR) {
+		pCurHcb->HCS_Msg[2] = 0;
+	} else {
+		if (pCurHcb->HCS_Msg[2] > 2)	/* > 32 bits            */
+			return (tul_msgout_reject(pCurHcb));
+		if (pCurHcb->HCS_Msg[2] == 2) {		/* == 32                */
+			pCurHcb->HCS_Msg[2] = 1;
+		} else {
+			if ((pCurHcb->HCS_ActTcs->TCS_Flags & TCF_NO_WDTR) == 0) {
+				wdtr_done(pCurHcb);
+				if ((pCurHcb->HCS_ActTcs->TCS_Flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0)
+					TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+				return (tul_msgin_accept(pCurHcb));
+			}
+		}
+	}
+	TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+
+	if (tul_msgin_accept(pCurHcb) != MSG_OUT)
+		return (pCurHcb->HCS_Phase);
+	/* WDTR msg out                 */
+	TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_EXTEND);
+	TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 2);
+	TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 3);
+	TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurHcb->HCS_Msg[2]);
+	TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+	return (wait_tulip(pCurHcb));
+}
+
+/***************************************************************************/
+int tul_msgin_sync(HCS * pCurHcb)
+{
+	char default_period;
+
+	default_period = tul_rate_tbl[pCurHcb->HCS_ActTcs->TCS_Flags & TCF_SCSI_RATE];
+	if (pCurHcb->HCS_Msg[3] > MAX_OFFSET) {
+		pCurHcb->HCS_Msg[3] = MAX_OFFSET;
+		if (pCurHcb->HCS_Msg[2] < default_period) {
+			pCurHcb->HCS_Msg[2] = default_period;
+			return 1;
+		}
+		if (pCurHcb->HCS_Msg[2] >= 59) {	/* Change to async              */
+			pCurHcb->HCS_Msg[3] = 0;
+		}
+		return 1;
+	}
+	/* offset requests asynchronous transfers ? */
+	if (pCurHcb->HCS_Msg[3] == 0) {
+		return 0;
+	}
+	if (pCurHcb->HCS_Msg[2] < default_period) {
+		pCurHcb->HCS_Msg[2] = default_period;
+		return 1;
+	}
+	if (pCurHcb->HCS_Msg[2] >= 59) {
+		pCurHcb->HCS_Msg[3] = 0;
+		return 1;
+	}
+	return 0;
+}
+
+
+/***************************************************************************/
+int wdtr_done(HCS * pCurHcb)
+{
+	pCurHcb->HCS_ActTcs->TCS_Flags &= ~TCF_SYNC_DONE;
+	pCurHcb->HCS_ActTcs->TCS_Flags |= TCF_WDTR_DONE;
+
+	pCurHcb->HCS_ActTcs->TCS_JS_Period = 0;
+	if (pCurHcb->HCS_Msg[2]) {	/* if 16 bit */
+		pCurHcb->HCS_ActTcs->TCS_JS_Period |= TSC_WIDE_SCSI;
+	}
+	pCurHcb->HCS_ActTcs->TCS_SConfig0 &= ~TSC_ALT_PERIOD;
+	TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurHcb->HCS_ActTcs->TCS_SConfig0);
+	TUL_WR(pCurHcb->HCS_Base + TUL_SPeriod, pCurHcb->HCS_ActTcs->TCS_JS_Period);
+
+	return 1;
+}
+
+/***************************************************************************/
+int tul_sync_done(HCS * pCurHcb)
+{
+	int i;
+
+	pCurHcb->HCS_ActTcs->TCS_Flags |= TCF_SYNC_DONE;
+
+	if (pCurHcb->HCS_Msg[3]) {
+		pCurHcb->HCS_ActTcs->TCS_JS_Period |= pCurHcb->HCS_Msg[3];
+		for (i = 0; i < 8; i++) {
+			if (tul_rate_tbl[i] >= pCurHcb->HCS_Msg[2])	/* pick the big one */
+				break;
+		}
+		pCurHcb->HCS_ActTcs->TCS_JS_Period |= (i << 4);
+		pCurHcb->HCS_ActTcs->TCS_SConfig0 |= TSC_ALT_PERIOD;
+	}
+	TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurHcb->HCS_ActTcs->TCS_SConfig0);
+	TUL_WR(pCurHcb->HCS_Base + TUL_SPeriod, pCurHcb->HCS_ActTcs->TCS_JS_Period);
+
+	return (-1);
+}
+
+
+int tul_post_scsi_rst(HCS * pCurHcb)
+{
+	SCB *pCurScb;
+	TCS *pCurTcb;
+	int i;
+
+	pCurHcb->HCS_ActScb = 0;
+	pCurHcb->HCS_ActTcs = 0;
+	pCurHcb->HCS_Flags = 0;
+
+	while ((pCurScb = tul_pop_busy_scb(pCurHcb)) != NULL) {
+		pCurScb->SCB_HaStat = HOST_BAD_PHAS;
+		tul_append_done_scb(pCurHcb, pCurScb);
+	}
+	/* clear sync done flag         */
+	pCurTcb = &pCurHcb->HCS_Tcs[0];
+	for (i = 0; i < pCurHcb->HCS_MaxTar; pCurTcb++, i++) {
+		pCurTcb->TCS_Flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE);
+		/* Initialize the sync. xfer register values to an asyn xfer */
+		pCurTcb->TCS_JS_Period = 0;
+		pCurTcb->TCS_SConfig0 = pCurHcb->HCS_SConf1;
+		pCurHcb->HCS_ActTags[0] = 0;	/* 07/22/98 */
+		pCurHcb->HCS_Tcs[i].TCS_Flags &= ~TCF_BUSY;	/* 07/22/98 */
+	}			/* for */
+
+	return (-1);
+}
+
+/***************************************************************************/
+void tul_select_atn_stop(HCS * pCurHcb, SCB * pCurScb)
+{
+	pCurScb->SCB_Status |= SCB_SELECT;
+	pCurScb->SCB_NxtStat = 0x1;
+	pCurHcb->HCS_ActScb = pCurScb;
+	pCurHcb->HCS_ActTcs = &pCurHcb->HCS_Tcs[pCurScb->SCB_Target];
+	TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_SELATNSTOP);
+	return;
+}
+
+
+/***************************************************************************/
+void tul_select_atn(HCS * pCurHcb, SCB * pCurScb)
+{
+	int i;
+
+	pCurScb->SCB_Status |= SCB_SELECT;
+	pCurScb->SCB_NxtStat = 0x2;
+
+	TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_Ident);
+	for (i = 0; i < (int) pCurScb->SCB_CDBLen; i++)
+		TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_CDB[i]);
+	pCurHcb->HCS_ActTcs = &pCurHcb->HCS_Tcs[pCurScb->SCB_Target];
+	pCurHcb->HCS_ActScb = pCurScb;
+	TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_SEL_ATN);
+	return;
+}
+
+/***************************************************************************/
+void tul_select_atn3(HCS * pCurHcb, SCB * pCurScb)
+{
+	int i;
+
+	pCurScb->SCB_Status |= SCB_SELECT;
+	pCurScb->SCB_NxtStat = 0x2;
+
+	TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_Ident);
+	TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_TagMsg);
+	TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_TagId);
+	for (i = 0; i < (int) pCurScb->SCB_CDBLen; i++)
+		TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_CDB[i]);
+	pCurHcb->HCS_ActTcs = &pCurHcb->HCS_Tcs[pCurScb->SCB_Target];
+	pCurHcb->HCS_ActScb = pCurScb;
+	TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_SEL_ATN3);
+	return;
+}
+
+/***************************************************************************/
+/* SCSI Bus Device Reset */
+int tul_bus_device_reset(HCS * pCurHcb)
+{
+	SCB *pCurScb = pCurHcb->HCS_ActScb;
+	TCS *pCurTcb = pCurHcb->HCS_ActTcs;
+	SCB *pTmpScb, *pPrevScb;
+	BYTE tar;
+
+	if (pCurHcb->HCS_Phase != MSG_OUT) {
+		return (int_tul_bad_seq(pCurHcb));	/* Unexpected phase             */
+	}
+	tul_unlink_pend_scb(pCurHcb, pCurScb);
+	tul_release_scb(pCurHcb, pCurScb);
+
+
+	tar = pCurScb->SCB_Target;	/* target                       */
+	pCurTcb->TCS_Flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE | TCF_BUSY);
+	/* clr sync. nego & WDTR flags  07/22/98 */
+
+	/* abort all SCB with same target */
+	pPrevScb = pTmpScb = pCurHcb->HCS_FirstBusy;	/* Check Busy queue */
+	while (pTmpScb != NULL) {
+
+		if (pTmpScb->SCB_Target == tar) {
+			/* unlink it */
+			if (pTmpScb == pCurHcb->HCS_FirstBusy) {
+				if ((pCurHcb->HCS_FirstBusy = pTmpScb->SCB_NxtScb) == NULL)
+					pCurHcb->HCS_LastBusy = NULL;
+			} else {
+				pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb;
+				if (pTmpScb == pCurHcb->HCS_LastBusy)
+					pCurHcb->HCS_LastBusy = pPrevScb;
+			}
+			pTmpScb->SCB_HaStat = HOST_ABORTED;
+			tul_append_done_scb(pCurHcb, pTmpScb);
+		}
+		/* Previous haven't change      */
+		else {
+			pPrevScb = pTmpScb;
+		}
+		pTmpScb = pTmpScb->SCB_NxtScb;
+	}
+
+	TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_DEVRST);
+	TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+
+	return tul_wait_disc(pCurHcb);
+
+}
+
+/***************************************************************************/
+int tul_msgin_accept(HCS * pCurHcb)
+{
+	TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_MSG_ACCEPT);
+	return (wait_tulip(pCurHcb));
+}
+
+/***************************************************************************/
+int wait_tulip(HCS * pCurHcb)
+{
+
+	while (!((pCurHcb->HCS_JSStatus0 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus0))
+		 & TSS_INT_PENDING));
+
+	pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt);
+	pCurHcb->HCS_Phase = pCurHcb->HCS_JSStatus0 & TSS_PH_MASK;
+	pCurHcb->HCS_JSStatus1 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus1);
+
+	if (pCurHcb->HCS_JSInt & TSS_RESEL_INT) {	/* if SCSI bus reset detected   */
+		return (int_tul_resel(pCurHcb));
+	}
+	if (pCurHcb->HCS_JSInt & TSS_SEL_TIMEOUT) {	/* if selected/reselected timeout interrupt */
+		return (int_tul_busfree(pCurHcb));
+	}
+	if (pCurHcb->HCS_JSInt & TSS_SCSIRST_INT) {	/* if SCSI bus reset detected   */
+		return (int_tul_scsi_rst(pCurHcb));
+	}
+	if (pCurHcb->HCS_JSInt & TSS_DISC_INT) {	/* BUS disconnection            */
+		if (pCurHcb->HCS_Flags & HCF_EXPECT_DONE_DISC) {
+			TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);		/* Flush SCSI FIFO  */
+			tul_unlink_busy_scb(pCurHcb, pCurHcb->HCS_ActScb);
+			pCurHcb->HCS_ActScb->SCB_HaStat = 0;
+			tul_append_done_scb(pCurHcb, pCurHcb->HCS_ActScb);
+			pCurHcb->HCS_ActScb = NULL;
+			pCurHcb->HCS_ActTcs = NULL;
+			pCurHcb->HCS_Flags &= ~HCF_EXPECT_DONE_DISC;
+			TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT);
+			TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT);	/* Enable HW reselect       */
+			return (-1);
+		}
+		if (pCurHcb->HCS_Flags & HCF_EXPECT_DISC) {
+			TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);		/* Flush SCSI FIFO  */
+			pCurHcb->HCS_ActScb = NULL;
+			pCurHcb->HCS_ActTcs = NULL;
+			pCurHcb->HCS_Flags &= ~HCF_EXPECT_DISC;
+			TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT);
+			TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT);	/* Enable HW reselect       */
+			return (-1);
+		}
+		return (int_tul_busfree(pCurHcb));
+	}
+	if (pCurHcb->HCS_JSInt & (TSS_FUNC_COMP | TSS_BUS_SERV)) {
+		return (pCurHcb->HCS_Phase);
+	}
+	return (pCurHcb->HCS_Phase);
+}
+/***************************************************************************/
+int tul_wait_disc(HCS * pCurHcb)
+{
+
+	while (!((pCurHcb->HCS_JSStatus0 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus0))
+		 & TSS_INT_PENDING));
+
+
+	pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt);
+
+	if (pCurHcb->HCS_JSInt & TSS_SCSIRST_INT) {	/* if SCSI bus reset detected   */
+		return (int_tul_scsi_rst(pCurHcb));
+	}
+	if (pCurHcb->HCS_JSInt & TSS_DISC_INT) {	/* BUS disconnection            */
+		TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);		/* Flush SCSI FIFO  */
+		TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT);
+		TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT);	/* Enable HW reselect       */
+		pCurHcb->HCS_ActScb = NULL;
+		return (-1);
+	}
+	return (tul_bad_seq(pCurHcb));
+}
+
+/***************************************************************************/
+int tul_wait_done_disc(HCS * pCurHcb)
+{
+
+
+	while (!((pCurHcb->HCS_JSStatus0 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus0))
+		 & TSS_INT_PENDING));
+
+	pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt);
+
+
+	if (pCurHcb->HCS_JSInt & TSS_SCSIRST_INT) {	/* if SCSI bus reset detected   */
+		return (int_tul_scsi_rst(pCurHcb));
+	}
+	if (pCurHcb->HCS_JSInt & TSS_DISC_INT) {	/* BUS disconnection            */
+		TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);		/* Flush SCSI FIFO  */
+		TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT);
+		TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT);	/* Enable HW reselect       */
+		tul_unlink_busy_scb(pCurHcb, pCurHcb->HCS_ActScb);
+
+		tul_append_done_scb(pCurHcb, pCurHcb->HCS_ActScb);
+		pCurHcb->HCS_ActScb = NULL;
+		return (-1);
+	}
+	return (tul_bad_seq(pCurHcb));
+}
+
+/**************************** EOF *********************************/
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/i91uscsi.h linux/drivers/scsi/i91uscsi.h
--- linux.vanilla/drivers/scsi/i91uscsi.h	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/i91uscsi.h	Fri Mar 26 17:20:22 1999
@@ -0,0 +1,857 @@
+/**************************************************************************
+ * Initio 9100 device driver for Linux.
+ *
+ * Copyright (c) 1994-1998 Initio Corporation
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ **************************************************************************/
+
+#include <linux/config.h>
+
+#define ULONG   unsigned long
+#define USHORT  unsigned short
+#define UCHAR   unsigned char
+#define BYTE    unsigned char
+#define WORD    unsigned short
+#define DWORD   unsigned long
+#define UBYTE   unsigned char
+#define UWORD   unsigned short
+#define UDWORD  unsigned long
+#ifdef ALPHA
+#define U32     unsigned int
+#else
+#define U32     unsigned long
+#endif
+
+#ifndef NULL
+#define NULL     0		/* zero          */
+#endif
+#ifndef TRUE
+#define TRUE     (1)		/* boolean true  */
+#endif
+#ifndef FALSE
+#define FALSE    (0)		/* boolean false */
+#endif
+#ifndef FAILURE
+#define FAILURE  (-1)
+#endif
+
+#define TOTAL_SG_ENTRY		32
+#define MAX_SUPPORTED_ADAPTERS  8
+#define MAX_OFFSET		15
+#define MAX_TARGETS		16
+
+#define INI_VENDOR_ID   0x1101	/* Initio's PCI vendor ID       */
+#define I950_DEVICE_ID	0x9500	/* Initio's inic-950 product ID   */
+#define I940_DEVICE_ID	0x9400	/* Initio's inic-940 product ID   */
+#define I935_DEVICE_ID	0x9401	/* Initio's inic-935 product ID   */
+
+#define	_I91USCSI_H
+
+typedef struct {
+	unsigned short base;
+	unsigned short vec;
+} i91u_config;
+
+/***************************************/
+/*  Tulip Configuration Register Set */
+/***************************************/
+#define TUL_PVID        0x00	/* Vendor ID                    */
+#define TUL_PDID        0x02	/* Device ID                    */
+#define TUL_PCMD        0x04	/* Command                      */
+#define TUL_PSTUS       0x06	/* Status                       */
+#define TUL_PRID        0x08	/* Revision number              */
+#define TUL_PPI         0x09	/* Programming interface        */
+#define TUL_PSC         0x0A	/* Sub Class                    */
+#define TUL_PBC         0x0B	/* Base Class                   */
+#define TUL_PCLS        0x0C	/* Cache line size              */
+#define TUL_PLTR        0x0D	/* Latency timer                */
+#define TUL_PHDT        0x0E	/* Header type                  */
+#define TUL_PBIST       0x0F	/* BIST                         */
+#define TUL_PBAD        0x10	/* Base address                 */
+#define TUL_PBAD1       0x14	/* Base address                 */
+#define TUL_PBAD2       0x18	/* Base address                 */
+#define TUL_PBAD3       0x1C	/* Base address                 */
+#define TUL_PBAD4       0x20	/* Base address                 */
+#define TUL_PBAD5       0x24	/* Base address                 */
+#define TUL_PRSVD       0x28	/* Reserved                     */
+#define TUL_PRSVD1      0x2C	/* Reserved                     */
+#define TUL_PRAD        0x30	/* Expansion ROM base address   */
+#define TUL_PRSVD2      0x34	/* Reserved                     */
+#define TUL_PRSVD3      0x38	/* Reserved                     */
+#define TUL_PINTL       0x3C	/* Interrupt line               */
+#define TUL_PINTP       0x3D	/* Interrupt pin                */
+#define TUL_PIGNT       0x3E	/* MIN_GNT                      */
+#define TUL_PMGNT       0x3F	/* MAX_GNT                      */
+
+/************************/
+/*  Jasmin Register Set */
+/************************/
+#define TUL_HACFG0      0x40	/* H/A Configuration Register 0         */
+#define TUL_HACFG1      0x41	/* H/A Configuration Register 1         */
+#define TUL_HACFG2      0x42	/* H/A Configuration Register 2         */
+
+#define TUL_SDCFG0      0x44	/* SCSI Device Configuration 0          */
+#define TUL_SDCFG1      0x45	/* SCSI Device Configuration 1          */
+#define TUL_SDCFG2      0x46	/* SCSI Device Configuration 2          */
+#define TUL_SDCFG3      0x47	/* SCSI Device Configuration 3          */
+
+#define TUL_GINTS       0x50	/* Global Interrupt Status Register     */
+#define TUL_GIMSK       0x52	/* Global Interrupt MASK Register       */
+#define TUL_GCTRL       0x54	/* Global Control Register              */
+#define TUL_GCTRL_EEPROM_BIT    0x04
+#define TUL_GCTRL1      0x55	/* Global Control Register              */
+#define TUL_DMACFG      0x5B	/* DMA configuration                    */
+#define TUL_NVRAM       0x5D	/* Non-volatile RAM port                */
+
+#define TUL_SCnt0       0x80	/* 00 R/W Transfer Counter Low          */
+#define TUL_SCnt1       0x81	/* 01 R/W Transfer Counter Mid          */
+#define TUL_SCnt2       0x82	/* 02 R/W Transfer Count High           */
+#define TUL_SFifoCnt    0x83	/* 03 R   FIFO counter                  */
+#define TUL_SIntEnable  0x84	/* 03 W   Interrupt enble               */
+#define TUL_SInt        0x84	/* 04 R   Interrupt Register            */
+#define TUL_SCtrl0      0x85	/* 05 W   Control 0                     */
+#define TUL_SStatus0    0x85	/* 05 R   Status 0                      */
+#define TUL_SCtrl1      0x86	/* 06 W   Control 1                     */
+#define TUL_SStatus1    0x86	/* 06 R   Status 1                      */
+#define TUL_SConfig     0x87	/* 07 W   Configuration                 */
+#define TUL_SStatus2    0x87	/* 07 R   Status 2                      */
+#define TUL_SPeriod     0x88	/* 08 W   Sync. Transfer Period & Offset */
+#define TUL_SOffset     0x88	/* 08 R   Offset                        */
+#define TUL_SScsiId     0x89	/* 09 W   SCSI ID                       */
+#define TUL_SBusId      0x89	/* 09 R   SCSI BUS ID                   */
+#define TUL_STimeOut    0x8A	/* 0A W   Sel/Resel Time Out Register   */
+#define TUL_SIdent      0x8A	/* 0A R   Identify Message Register     */
+#define TUL_SAvail      0x8A	/* 0A R   Availiable Counter Register   */
+#define TUL_SData       0x8B	/* 0B R/W SCSI data in/out              */
+#define TUL_SFifo       0x8C	/* 0C R/W FIFO                          */
+#define TUL_SSignal     0x90	/* 10 R/W SCSI signal in/out            */
+#define TUL_SCmd        0x91	/* 11 R/W Command                       */
+#define TUL_STest0      0x92	/* 12 R/W Test0                         */
+#define TUL_STest1      0x93	/* 13 R/W Test1                         */
+#define TUL_SCFG1	0x94	/* 14 R/W Configuration                 */
+
+#define TUL_XAddH       0xC0	/*DMA Transfer Physical Address         */
+#define TUL_XAddW       0xC8	/*DMA Current Transfer Physical Address */
+#define TUL_XCntH       0xD0	/*DMA Transfer Counter                  */
+#define TUL_XCntW       0xD4	/*DMA Current Transfer Counter          */
+#define TUL_XCmd        0xD8	/*DMA Command Register                  */
+#define TUL_Int         0xDC	/*Interrupt Register                    */
+#define TUL_XStatus     0xDD	/*DMA status Register                   */
+#define TUL_Mask        0xE0	/*Interrupt Mask Register               */
+#define TUL_XCtrl       0xE4	/*DMA Control Register                  */
+#define TUL_XCtrl1      0xE5	/*DMA Control Register 1                */
+#define TUL_XFifo       0xE8	/*DMA FIFO                              */
+
+#define TUL_WCtrl       0xF7	/*Bus master wait state control         */
+#define TUL_DCtrl       0xFB	/*DMA delay control                     */
+
+/*----------------------------------------------------------------------*/
+/*   bit definition for Command register of Configuration Space Header  */
+/*----------------------------------------------------------------------*/
+#define BUSMS           0x04	/* BUS MASTER Enable                    */
+#define IOSPA           0x01	/* IO Space Enable                      */
+
+/*----------------------------------------------------------------------*/
+/* Command Codes of Tulip SCSI Command register                         */
+/*----------------------------------------------------------------------*/
+#define TSC_EN_RESEL    0x80	/* Enable Reselection                   */
+#define TSC_CMD_COMP    0x84	/* Command Complete Sequence            */
+#define TSC_SEL         0x01	/* Select Without ATN Sequence          */
+#define TSC_SEL_ATN     0x11	/* Select With ATN Sequence             */
+#define TSC_SEL_ATN_DMA 0x51	/* Select With ATN Sequence with DMA    */
+#define TSC_SEL_ATN3    0x31	/* Select With ATN3 Sequence            */
+#define TSC_SEL_ATNSTOP 0x12	/* Select With ATN and Stop Sequence    */
+#define TSC_SELATNSTOP  0x1E	/* Select With ATN and Stop Sequence    */
+
+#define TSC_SEL_ATN_DIRECT_IN   0x95	/* Select With ATN Sequence     */
+#define TSC_SEL_ATN_DIRECT_OUT  0x15	/* Select With ATN Sequence     */
+#define TSC_SEL_ATN3_DIRECT_IN  0xB5	/* Select With ATN3 Sequence    */
+#define TSC_SEL_ATN3_DIRECT_OUT 0x35	/* Select With ATN3 Sequence    */
+#define TSC_XF_DMA_OUT_DIRECT   0x06	/* DMA Xfer Infomation out      */
+#define TSC_XF_DMA_IN_DIRECT    0x86	/* DMA Xfer Infomation in       */
+
+#define TSC_XF_DMA_OUT  0x43	/* DMA Xfer Infomation out              */
+#define TSC_XF_DMA_IN   0xC3	/* DMA Xfer Infomation in               */
+#define TSC_XF_FIFO_OUT 0x03	/* FIFO Xfer Infomation out             */
+#define TSC_XF_FIFO_IN  0x83	/* FIFO Xfer Infomation in              */
+
+#define TSC_MSG_ACCEPT  0x0F	/* Message Accept                       */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Control 0 Register                     */
+/*----------------------------------------------------------------------*/
+#define TSC_RST_SEQ     0x20	/* Reset sequence counter               */
+#define TSC_FLUSH_FIFO  0x10	/* Flush FIFO                           */
+#define TSC_ABT_CMD     0x04	/* Abort command (sequence)             */
+#define TSC_RST_CHIP    0x02	/* Reset SCSI Chip                      */
+#define TSC_RST_BUS     0x01	/* Reset SCSI Bus                       */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Control 1 Register                     */
+/*----------------------------------------------------------------------*/
+#define TSC_EN_SCAM     0x80	/* Enable SCAM                          */
+#define TSC_TIMER       0x40	/* Select timeout unit                  */
+#define TSC_EN_SCSI2    0x20	/* SCSI-2 mode                          */
+#define TSC_PWDN        0x10	/* Power down mode                      */
+#define TSC_WIDE_CPU    0x08	/* Wide CPU                             */
+#define TSC_HW_RESELECT 0x04	/* Enable HW reselect                   */
+#define TSC_EN_BUS_OUT  0x02	/* Enable SCSI data bus out latch       */
+#define TSC_EN_BUS_IN   0x01	/* Enable SCSI data bus in latch        */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Configuration Register                 */
+/*----------------------------------------------------------------------*/
+#define TSC_EN_LATCH    0x80	/* Enable phase latch                   */
+#define TSC_INITIATOR   0x40	/* Initiator mode                       */
+#define TSC_EN_SCSI_PAR 0x20	/* Enable SCSI parity                   */
+#define TSC_DMA_8BIT    0x10	/* Alternate dma 8-bits mode            */
+#define TSC_DMA_16BIT   0x08	/* Alternate dma 16-bits mode           */
+#define TSC_EN_WDACK    0x04	/* Enable DACK while wide SCSI xfer     */
+#define TSC_ALT_PERIOD  0x02	/* Alternate sync period mode           */
+#define TSC_DIS_SCSIRST 0x01	/* Disable SCSI bus reset us            */
+
+#define TSC_INITDEFAULT (TSC_INITIATOR | TSC_EN_LATCH | TSC_ALT_PERIOD | TSC_DIS_SCSIRST)
+
+#define TSC_WIDE_SCSI   0x80	/* Enable Wide SCSI                     */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI signal Register                        */
+/*----------------------------------------------------------------------*/
+#define TSC_RST_ACK     0x00	/* Release ACK signal                   */
+#define TSC_RST_ATN     0x00	/* Release ATN signal                   */
+#define TSC_RST_BSY     0x00	/* Release BSY signal                   */
+
+#define TSC_SET_ACK     0x40	/* ACK signal                           */
+#define TSC_SET_ATN     0x08	/* ATN signal                           */
+
+#define TSC_REQI        0x80	/* REQ signal                           */
+#define TSC_ACKI        0x40	/* ACK signal                           */
+#define TSC_BSYI        0x20	/* BSY signal                           */
+#define TSC_SELI        0x10	/* SEL signal                           */
+#define TSC_ATNI        0x08	/* ATN signal                           */
+#define TSC_MSGI        0x04	/* MSG signal                           */
+#define TSC_CDI         0x02	/* C/D signal                           */
+#define TSC_IOI         0x01	/* I/O signal                           */
+
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Status 0 Register                      */
+/*----------------------------------------------------------------------*/
+#define TSS_INT_PENDING 0x80	/* Interrupt pending            */
+#define TSS_SEQ_ACTIVE  0x40	/* Sequencer active             */
+#define TSS_XFER_CNT    0x20	/* Transfer counter zero        */
+#define TSS_FIFO_EMPTY  0x10	/* FIFO empty                   */
+#define TSS_PAR_ERROR   0x08	/* SCSI parity error            */
+#define TSS_PH_MASK     0x07	/* SCSI phase mask              */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Status 1 Register                      */
+/*----------------------------------------------------------------------*/
+#define TSS_STATUS_RCV  0x08	/* Status received              */
+#define TSS_MSG_SEND    0x40	/* Message sent                 */
+#define TSS_CMD_PH_CMP  0x20	/* command phase done              */
+#define TSS_DATA_PH_CMP 0x10	/* Data phase done              */
+#define TSS_STATUS_SEND 0x08	/* Status sent                  */
+#define TSS_XFER_CMP    0x04	/* Transfer completed           */
+#define TSS_SEL_CMP     0x02	/* Selection completed          */
+#define TSS_ARB_CMP     0x01	/* Arbitration completed        */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Status 2 Register                      */
+/*----------------------------------------------------------------------*/
+#define TSS_CMD_ABTED   0x80	/* Command aborted              */
+#define TSS_OFFSET_0    0x40	/* Offset counter zero          */
+#define TSS_FIFO_FULL   0x20	/* FIFO full                    */
+#define TSS_TIMEOUT_0   0x10	/* Timeout counter zero         */
+#define TSS_BUSY_RLS    0x08	/* Busy release                 */
+#define TSS_PH_MISMATCH 0x04	/* Phase mismatch               */
+#define TSS_SCSI_BUS_EN 0x02	/* SCSI data bus enable         */
+#define TSS_SCSIRST     0x01	/* SCSI bus reset in progress   */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Interrupt Register                     */
+/*----------------------------------------------------------------------*/
+#define TSS_RESEL_INT   0x80	/* Reselected interrupt         */
+#define TSS_SEL_TIMEOUT 0x40	/* Selected/reselected timeout  */
+#define TSS_BUS_SERV    0x20
+#define TSS_SCSIRST_INT 0x10	/* SCSI bus reset detected      */
+#define TSS_DISC_INT    0x08	/* Disconnected interrupt       */
+#define TSS_SEL_INT     0x04	/* Select interrupt             */
+#define TSS_SCAM_SEL    0x02	/* SCAM selected                */
+#define TSS_FUNC_COMP   0x01
+
+/*----------------------------------------------------------------------*/
+/* SCSI Phase Codes.                                                    */
+/*----------------------------------------------------------------------*/
+#define DATA_OUT        0
+#define DATA_IN         1	/* 4                            */
+#define CMD_OUT         2
+#define STATUS_IN       3	/* 6                            */
+#define MSG_OUT         6	/* 3                            */
+#define MSG_IN          7
+
+
+
+/*----------------------------------------------------------------------*/
+/* Command Codes of Tulip xfer Command register                         */
+/*----------------------------------------------------------------------*/
+#define TAX_X_FORC      0x02
+#define TAX_X_ABT       0x04
+#define TAX_X_CLR_FIFO  0x08
+
+#define TAX_X_IN        0x21
+#define TAX_X_OUT       0x01
+#define TAX_SG_IN       0xA1
+#define TAX_SG_OUT      0x81
+
+/*----------------------------------------------------------------------*/
+/* Tulip Interrupt Register                                             */
+/*----------------------------------------------------------------------*/
+#define XCMP            0x01
+#define FCMP            0x02
+#define XABT            0x04
+#define XERR            0x08
+#define SCMP            0x10
+#define IPEND           0x80
+
+/*----------------------------------------------------------------------*/
+/* Tulip DMA Status Register                                            */
+/*----------------------------------------------------------------------*/
+#define XPEND           0x01	/* Transfer pending             */
+#define FEMPTY          0x02	/* FIFO empty                   */
+
+
+
+/*----------------------------------------------------------------------*/
+/* bit definition for TUL_GCTRL                                         */
+/*----------------------------------------------------------------------*/
+#define EXTSG           0x80
+#define EXTAD           0x60
+#define SEG4K           0x08
+#define EEPRG           0x04
+#define MRMUL           0x02
+
+/*----------------------------------------------------------------------*/
+/* bit definition for TUL_NVRAM                                         */
+/*----------------------------------------------------------------------*/
+#define SE2CS           0x08
+#define SE2CLK          0x04
+#define SE2DO           0x02
+#define SE2DI           0x01
+
+
+/************************************************************************/
+/*              Scatter-Gather Element Structure                        */
+/************************************************************************/
+typedef struct SG_Struc {
+	U32 SG_Ptr;		/* Data Pointer */
+	U32 SG_Len;		/* Data Length */
+} SG;
+
+/***********************************************************************
+		SCSI Control Block
+************************************************************************/
+typedef struct Scsi_Ctrl_Blk {
+	struct Scsi_Ctrl_Blk *SCB_NxtScb;
+	UBYTE SCB_Status;	/*4 */
+	UBYTE SCB_NxtStat;	/*5 */
+	UBYTE SCB_Mode;		/*6 */
+	UBYTE SCB_Msgin;	/*7 SCB_Res0 */
+	UWORD SCB_SGIdx;	/*8 */
+	UWORD SCB_SGMax;	/*A */
+#ifdef ALPHA
+	U32 SCB_Reserved[2];	/*C */
+#else
+	U32 SCB_Reserved[3];	/*C */
+#endif
+
+	U32 SCB_XferLen;	/*18 Current xfer len           */
+	U32 SCB_TotXLen;	/*1C Total xfer len             */
+	U32 SCB_PAddr;		/*20 SCB phy. Addr. */
+
+	UBYTE SCB_Opcode;	/*24 SCB command code */
+	UBYTE SCB_Flags;	/*25 SCB Flags */
+	UBYTE SCB_Target;	/*26 Target Id */
+	UBYTE SCB_Lun;		/*27 Lun */
+	U32 SCB_BufPtr;		/*28 Data Buffer Pointer */
+	U32 SCB_BufLen;		/*2C Data Allocation Length */
+	UBYTE SCB_SGLen;	/*30 SG list # */
+	UBYTE SCB_SenseLen;	/*31 Sense Allocation Length */
+	UBYTE SCB_HaStat;	/*32 */
+	UBYTE SCB_TaStat;	/*33 */
+	UBYTE SCB_CDBLen;	/*34 CDB Length */
+	UBYTE SCB_Ident;	/*35 Identify */
+	UBYTE SCB_TagMsg;	/*36 Tag Message */
+	UBYTE SCB_TagId;	/*37 Queue Tag */
+	UBYTE SCB_CDB[12];	/*38 */
+	U32 SCB_SGPAddr;	/*44 SG List/Sense Buf phy. Addr. */
+	U32 SCB_SensePtr;	/*48 Sense data pointer */
+	void (*SCB_Post) (BYTE *, BYTE *);	/*4C POST routine */
+	unsigned char *SCB_Srb;	/*50 SRB Pointer */
+	SG SCB_SGList[TOTAL_SG_ENTRY];	/*54 Start of SG list */
+} SCB;
+
+/* Bit Definition for SCB_Status */
+#define SCB_RENT        0x01
+#define SCB_PEND        0x02
+#define SCB_CONTIG      0x04	/* Contigent Allegiance */
+#define SCB_SELECT      0x08
+#define SCB_BUSY        0x10
+#define SCB_DONE        0x20
+
+
+/* Opcodes of SCB_Opcode */
+#define ExecSCSI        0x1
+#define BusDevRst       0x2
+#define AbortCmd        0x3
+
+
+/* Bit Definition for SCB_Mode */
+#define SCM_RSENS       0x01	/* request sense mode */
+
+
+/* Bit Definition for SCB_Flags */
+#define SCF_DONE        0x01
+#define SCF_POST        0x02
+#define SCF_SENSE       0x04
+#define SCF_DIR         0x18
+#define SCF_NO_DCHK     0x00
+#define SCF_DIN         0x08
+#define SCF_DOUT        0x10
+#define SCF_NO_XF       0x18
+#define SCF_WR_VF       0x20	/* Write verify turn on         */
+#define SCF_POLL        0x40
+#define SCF_SG          0x80
+
+/* Error Codes for SCB_HaStat */
+#define HOST_SEL_TOUT   0x11
+#define HOST_DO_DU      0x12
+#define HOST_BUS_FREE   0x13
+#define HOST_BAD_PHAS   0x14
+#define HOST_INV_CMD    0x16
+#define HOST_ABORTED    0x1A	/* 07/21/98 */
+#define HOST_SCSI_RST   0x1B
+#define HOST_DEV_RST    0x1C
+
+/* Error Codes for SCB_TaStat */
+#define TARGET_CHKCOND  0x02
+#define TARGET_BUSY     0x08
+#define QUEUE_FULL	0x28
+
+/* SCSI MESSAGE */
+#define MSG_COMP        0x00
+#define MSG_EXTEND      0x01
+#define MSG_SDP         0x02
+#define MSG_RESTORE     0x03
+#define MSG_DISC        0x04
+#define MSG_IDE         0x05
+#define MSG_ABORT       0x06
+#define MSG_REJ         0x07
+#define MSG_NOP         0x08
+#define MSG_PARITY      0x09
+#define MSG_LINK_COMP   0x0A
+#define MSG_LINK_FLAG   0x0B
+#define MSG_DEVRST      0x0C
+#define MSG_ABORT_TAG   0x0D
+
+/* Queue tag msg: Simple_quque_tag, Head_of_queue_tag, Ordered_queue_tag */
+#define MSG_STAG        0x20
+#define MSG_HTAG        0x21
+#define MSG_OTAG        0x22
+
+#define MSG_IGNOREWIDE  0x23
+
+#define MSG_IDENT   0x80
+
+/***********************************************************************
+		Target Device Control Structure
+**********************************************************************/
+
+typedef struct Tar_Ctrl_Struc {
+	UWORD TCS_Flags;	/* 0 */
+	UBYTE TCS_JS_Period;	/* 2 */
+	UBYTE TCS_SConfig0;	/* 3 */
+
+	UWORD TCS_DrvFlags;	/* 4 */
+	UBYTE TCS_DrvHead;	/* 6 */
+	UBYTE TCS_DrvSector;	/* 7 */
+} TCS;
+
+/***********************************************************************
+		Target Device Control Structure
+**********************************************************************/
+
+/* Bit Definition for TCF_Flags */
+#define TCF_SCSI_RATE           0x0007
+#define TCF_EN_DISC             0x0008
+#define TCF_NO_SYNC_NEGO        0x0010
+#define TCF_NO_WDTR             0x0020
+#define TCF_EN_255              0x0040
+#define TCF_EN_START            0x0080
+#define TCF_WDTR_DONE           0x0100
+#define TCF_SYNC_DONE           0x0200
+#define TCF_BUSY                0x0400
+
+
+/* Bit Definition for TCF_DrvFlags */
+#define TCF_DRV_BUSY            0x01	/* Indicate target busy(driver) */
+#define TCF_DRV_EN_TAG          0x0800
+#define TCF_DRV_255_63          0x0400
+
+typedef struct I91u_Adpt_Struc {
+	UWORD ADPT_BIOS;	/* 0 */
+	UWORD ADPT_BASE;	/* 1 */
+	UBYTE ADPT_Bus;		/* 2 */
+	UBYTE ADPT_Device;	/* 3 */
+	UBYTE ADPT_INTR;	/* 4 */
+} INI_ADPT_STRUCT;
+
+
+/***********************************************************************
+	      Host Adapter Control Structure
+************************************************************************/
+typedef struct Ha_Ctrl_Struc {
+	UWORD HCS_Base;		/* 00 */
+	UWORD HCS_BIOS;		/* 02 */
+	UBYTE HCS_Intr;		/* 04 */
+	UBYTE HCS_SCSI_ID;	/* 05 */
+	UBYTE HCS_MaxTar;	/* 06 */
+	UBYTE HCS_NumScbs;	/* 07 */
+
+	UBYTE HCS_Flags;	/* 08 */
+	UBYTE HCS_Index;	/* 09 */
+	UBYTE HCS_HaId;		/* 0A */
+	UBYTE HCS_Config;	/* 0B */
+	UWORD HCS_IdMask;	/* 0C */
+	UBYTE HCS_Semaph;	/* 0E */
+	UBYTE HCS_Phase;	/* 0F */
+	UBYTE HCS_JSStatus0;	/* 10 */
+	UBYTE HCS_JSInt;	/* 11 */
+	UBYTE HCS_JSStatus1;	/* 12 */
+	UBYTE HCS_SConf1;	/* 13 */
+
+	UBYTE HCS_Msg[8];	/* 14 */
+	SCB *HCS_NxtAvail;	/* 1C */
+	SCB *HCS_Scb;		/* 20 */
+	SCB *HCS_ScbEnd;	/* 24 */
+	SCB *HCS_NxtPend;	/* 28 */
+	SCB *HCS_NxtContig;	/* 2C */
+	SCB *HCS_ActScb;	/* 30 */
+	TCS *HCS_ActTcs;	/* 34 */
+
+	SCB *HCS_FirstAvail;	/* 38 */
+	SCB *HCS_LastAvail;	/* 3C */
+	SCB *HCS_FirstPend;	/* 40 */
+	SCB *HCS_LastPend;	/* 44 */
+	SCB *HCS_FirstBusy;	/* 48 */
+	SCB *HCS_LastBusy;	/* 4C */
+	SCB *HCS_FirstDone;	/* 50 */
+	SCB *HCS_LastDone;	/* 54 */
+	UBYTE HCS_MaxTags[16];	/* 58 */
+	UBYTE HCS_ActTags[16];	/* 68 */
+	TCS HCS_Tcs[MAX_TARGETS];	/* 78 */
+	ULONG pSRB_head;	/* SRB save queue header     */
+	ULONG pSRB_tail;	/* SRB save queue tail       */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spinlock_t HCS_AvailLock;
+	spinlock_t HCS_SemaphLock;
+	spinlock_t pSRB_lock;	/* SRB queue lock            */
+#endif
+} HCS;
+
+/* Bit Definition for HCB_Config */
+#define HCC_SCSI_RESET          0x01
+#define HCC_EN_PAR              0x02
+#define HCC_ACT_TERM1           0x04
+#define HCC_ACT_TERM2           0x08
+#define HCC_AUTO_TERM           0x10
+#define HCC_EN_PWR              0x80
+
+/* Bit Definition for HCB_Flags */
+#define HCF_EXPECT_DISC         0x01
+#define HCF_EXPECT_SELECT       0x02
+#define HCF_EXPECT_RESET        0x10
+#define HCF_EXPECT_DONE_DISC    0x20
+
+/******************************************************************
+	Serial EEProm
+*******************************************************************/
+
+typedef struct _NVRAM_SCSI {	/* SCSI channel configuration   */
+	UCHAR NVM_ChSCSIID;	/* 0Ch -> Channel SCSI ID       */
+	UCHAR NVM_ChConfig1;	/* 0Dh -> Channel config 1      */
+	UCHAR NVM_ChConfig2;	/* 0Eh -> Channel config 2      */
+	UCHAR NVM_NumOfTarg;	/* 0Fh -> Number of SCSI target */
+	/* SCSI target configuration    */
+	UCHAR NVM_Targ0Config;	/* 10h -> Target 0 configuration */
+	UCHAR NVM_Targ1Config;	/* 11h -> Target 1 configuration */
+	UCHAR NVM_Targ2Config;	/* 12h -> Target 2 configuration */
+	UCHAR NVM_Targ3Config;	/* 13h -> Target 3 configuration */
+	UCHAR NVM_Targ4Config;	/* 14h -> Target 4 configuration */
+	UCHAR NVM_Targ5Config;	/* 15h -> Target 5 configuration */
+	UCHAR NVM_Targ6Config;	/* 16h -> Target 6 configuration */
+	UCHAR NVM_Targ7Config;	/* 17h -> Target 7 configuration */
+	UCHAR NVM_Targ8Config;	/* 18h -> Target 8 configuration */
+	UCHAR NVM_Targ9Config;	/* 19h -> Target 9 configuration */
+	UCHAR NVM_TargAConfig;	/* 1Ah -> Target A configuration */
+	UCHAR NVM_TargBConfig;	/* 1Bh -> Target B configuration */
+	UCHAR NVM_TargCConfig;	/* 1Ch -> Target C configuration */
+	UCHAR NVM_TargDConfig;	/* 1Dh -> Target D configuration */
+	UCHAR NVM_TargEConfig;	/* 1Eh -> Target E configuration */
+	UCHAR NVM_TargFConfig;	/* 1Fh -> Target F configuration */
+} NVRAM_SCSI;
+
+typedef struct _NVRAM {
+/*----------header ---------------*/
+	USHORT NVM_Signature;	/* 0,1: Signature */
+	UCHAR NVM_Size;		/* 2:   Size of data structure */
+	UCHAR NVM_Revision;	/* 3:   Revision of data structure */
+	/* ----Host Adapter Structure ---- */
+	UCHAR NVM_ModelByte0;	/* 4:   Model number (byte 0) */
+	UCHAR NVM_ModelByte1;	/* 5:   Model number (byte 1) */
+	UCHAR NVM_ModelInfo;	/* 6:   Model information         */
+	UCHAR NVM_NumOfCh;	/* 7:   Number of SCSI channel */
+	UCHAR NVM_BIOSConfig1;	/* 8:   BIOS configuration 1  */
+	UCHAR NVM_BIOSConfig2;	/* 9:   BIOS configuration 2  */
+	UCHAR NVM_HAConfig1;	/* A:   Hoat adapter configuration 1 */
+	UCHAR NVM_HAConfig2;	/* B:   Hoat adapter configuration 2 */
+	NVRAM_SCSI NVM_SCSIInfo[2];
+	UCHAR NVM_reserved[10];
+	/* ---------- CheckSum ----------       */
+	USHORT NVM_CheckSum;	/* 0x3E, 0x3F: Checksum of NVRam        */
+} NVRAM, *PNVRAM;
+
+/* Bios Configuration for nvram->BIOSConfig1                            */
+#define NBC1_ENABLE             0x01	/* BIOS enable                  */
+#define NBC1_8DRIVE             0x02	/* Support more than 2 drives   */
+#define NBC1_REMOVABLE          0x04	/* Support removable drive      */
+#define NBC1_INT19              0x08	/* Intercept int 19h            */
+#define NBC1_BIOSSCAN           0x10	/* Dynamic BIOS scan            */
+#define NBC1_LUNSUPPORT         0x40	/* Support LUN                  */
+
+/* HA Configuration Byte 1                                              */
+#define NHC1_BOOTIDMASK 0x0F	/* Boot ID number               */
+#define NHC1_LUNMASK    0x70	/* Boot LUN number              */
+#define NHC1_CHANMASK   0x80	/* Boot Channel number          */
+
+/* Bit definition for nvram->SCSIconfig1                                */
+#define NCC1_BUSRESET           0x01	/* Reset SCSI bus at power up   */
+#define NCC1_PARITYCHK          0x02	/* SCSI parity enable           */
+#define NCC1_ACTTERM1           0x04	/* Enable active terminator 1   */
+#define NCC1_ACTTERM2           0x08	/* Enable active terminator 2   */
+#define NCC1_AUTOTERM           0x10	/* Enable auto terminator       */
+#define NCC1_PWRMGR             0x80	/* Enable power management      */
+
+/* Bit definition for SCSI Target configuration byte                    */
+#define NTC_DISCONNECT          0x08	/* Enable SCSI disconnect       */
+#define NTC_SYNC                0x10	/* SYNC_NEGO                    */
+#define NTC_NO_WDTR             0x20	/* SYNC_NEGO                    */
+#define NTC_1GIGA               0x40	/* 255 head / 63 sectors (64/32) */
+#define NTC_SPINUP              0x80	/* Start disk drive             */
+
+/*      Default NVRam values                                            */
+#define INI_SIGNATURE           0xC925
+#define NBC1_DEFAULT            (NBC1_ENABLE)
+#define NCC1_DEFAULT            (NCC1_BUSRESET | NCC1_AUTOTERM | NCC1_PARITYCHK)
+#define NTC_DEFAULT             (NTC_NO_WDTR | NTC_1GIGA | NTC_DISCONNECT)
+
+/* SCSI related definition                                              */
+#define DISC_NOT_ALLOW          0x80	/* Disconnect is not allowed    */
+#define DISC_ALLOW              0xC0	/* Disconnect is allowed        */
+#define SCSICMD_RequestSense    0x03
+
+
+/*----------------------------------------------------------------------*/
+/*                              PCI                                     */
+/*----------------------------------------------------------------------*/
+#define PCI_FUNCTION_ID         0xB1
+#define PCI_BIOS_PRESENT        0x01
+#define FIND_PCI_DEVICE         0x02
+#define FIND_PCI_CLASS_CODE     0x03
+#define GENERATE_SPECIAL_CYCLE  0x06
+#define READ_CONFIG_BYTE        0x08
+#define READ_CONFIG_WORD        0x09
+#define READ_CONFIG_DWORD       0x0A
+#define WRITE_CONFIG_BYTE       0x0B
+#define WRITE_CONFIG_WORD       0x0C
+#define WRITE_CONFIG_DWORD      0x0D
+
+#define SUCCESSFUL              0x00
+#define FUNC_NOT_SUPPORTED      0x81
+#define BAD_VENDOR_ID           0x83	/* Bad vendor ID                */
+#define DEVICE_NOT_FOUND        0x86	/* PCI device not found         */
+#define BAD_REGISTER_NUMBER     0x87
+
+#define MAX_PCI_DEVICES         21	/* Maximum devices supportted   */
+
+#define MAX_PCI_CHANL           4
+
+typedef struct _BIOS32_ENTRY_STRUCTURE {
+	DWORD Signatures;	/* Should be "_32_"             */
+	DWORD BIOS32Entry;	/* 32-bit physical address      */
+	BYTE Revision;		/* Revision level, should be 0  */
+	BYTE Length;		/* Multiply of 16, should be 1  */
+	BYTE CheckSum;		/* Checksum of whole structure  */
+	BYTE Reserved[5];	/* Reserved                     */
+} BIOS32_ENTRY_STRUCTURE, *PBIOS32_ENTRY_STRUCTURE;
+
+typedef struct {
+	union {
+		unsigned int eax;
+		struct {
+			unsigned short ax;
+		} word;
+		struct {
+			unsigned char al;
+			unsigned char ah;
+		} byte;
+	} eax;
+	union {
+		unsigned int ebx;
+		struct {
+			unsigned short bx;
+		} word;
+		struct {
+			unsigned char bl;
+			unsigned char bh;
+		} byte;
+	} ebx;
+	union {
+		unsigned int ecx;
+		struct {
+			unsigned short cx;
+		} word;
+		struct {
+			unsigned char cl;
+			unsigned char ch;
+		} byte;
+	} ecx;
+	union {
+		unsigned int edx;
+		struct {
+			unsigned short dx;
+		} word;
+		struct {
+			unsigned char dl;
+			unsigned char dh;
+		} byte;
+	} edx;
+	union {
+		unsigned int edi;
+		struct {
+			unsigned short di;
+		} word;
+	} edi;
+	union {
+		unsigned int esi;
+		struct {
+			unsigned short si;
+		} word;
+	} esi;
+} REGS;
+
+typedef union {			/* Union define for mechanism 1 */
+	struct {
+		unsigned char RegNum;
+		unsigned char FcnNum:3;
+		unsigned char DeviceNum:5;
+		unsigned char BusNum;
+		unsigned char Reserved:7;
+		unsigned char Enable:1;
+	} sConfigAdr;
+	unsigned long lConfigAdr;
+} CONFIG_ADR;
+
+typedef union {			/* Union define for mechanism 2 */
+	struct {
+		unsigned char RegNum;
+		unsigned char DeviceNum;
+		unsigned short Reserved;
+	} sHostAdr;
+	unsigned long lHostAdr;
+} HOST_ADR;
+
+typedef struct _HCSinfo {
+	ULONG base;
+	UCHAR vec;
+	UCHAR bios;		/* High byte of BIOS address */
+	USHORT BaseAndBios;	/* high byte: pHcsInfo->bios,low byte:pHcsInfo->base */
+} HCSINFO;
+
+#define TUL_RD(x,y)             (UCHAR)(inb(  (int)((ULONG)(x+y)) ))
+#define TUL_RDLONG(x,y)         (ULONG)(inl((int)((ULONG)(x+y)) ))
+#define TUL_WR(     adr,data)   outb( (UCHAR)(data), (int)(adr))
+#define TUL_WRSHORT(adr,data)   outw( (UWORD)(data), (int)(adr))
+#define TUL_WRLONG( adr,data)   outl( (ULONG)(data), (int)(adr))
+
+#define SCSI_ABORT_SNOOZE 0
+#define SCSI_ABORT_SUCCESS 1
+#define SCSI_ABORT_PENDING 2
+#define SCSI_ABORT_BUSY 3
+#define SCSI_ABORT_NOT_RUNNING 4
+#define SCSI_ABORT_ERROR 5
+
+#define SCSI_RESET_SNOOZE 0
+#define SCSI_RESET_PUNT 1
+#define SCSI_RESET_SUCCESS 2
+#define SCSI_RESET_PENDING 3
+#define SCSI_RESET_WAKEUP 4
+#define SCSI_RESET_NOT_RUNNING 5
+#define SCSI_RESET_ERROR 6
+
+#define SCSI_RESET_SYNCHRONOUS		0x01
+#define SCSI_RESET_ASYNCHRONOUS		0x02
+#define SCSI_RESET_SUGGEST_BUS_RESET	0x04
+#define SCSI_RESET_SUGGEST_HOST_RESET	0x08
+
+#define SCSI_RESET_BUS_RESET 0x100
+#define SCSI_RESET_HOST_RESET 0x200
+#define SCSI_RESET_ACTION   0xff
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/ini9100u.c linux/drivers/scsi/ini9100u.c
--- linux.vanilla/drivers/scsi/ini9100u.c	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/ini9100u.c	Tue Mar  2 00:42:14 1999
@@ -0,0 +1,1104 @@
+/**************************************************************************
+ * Initio 9100 device driver for Linux.
+ *
+ * Copyright (c) 1994-1998 Initio Corporation
+ * Copyright (c) 1998 Bas Vermeulen <bvermeul@blackstar.xs4all.nl>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *************************************************************************
+ *
+ * DESCRIPTION:
+ *
+ * This is the Linux low-level SCSI driver for Initio INI-9X00U/UW SCSI host
+ * adapters
+ *
+ * 08/06/97 hc	- v1.01h
+ *		- Support inic-940 and inic-935
+ * 09/26/97 hc	- v1.01i
+ *		- Make correction from J.W. Schultz suggestion
+ * 10/13/97 hc	- Support reset function
+ * 10/21/97 hc	- v1.01j
+ *		- Support 32 LUN (SCSI 3)
+ * 01/14/98 hc	- v1.01k
+ *		- Fix memory allocation problem
+ * 03/04/98 hc	- v1.01l
+ *		- Fix tape rewind which will hang the system problem
+ *		- Set can_queue to tul_num_scb
+ * 06/25/98 hc	- v1.01m
+ *		- Get it work for kernel version >= 2.1.75
+ *		- Dynamic assign SCSI bus reset holding time in init_tulip()
+ * 07/02/98 hc	- v1.01n
+ *		- Support 0002134A
+ * 08/07/98 hc  - v1.01o
+ *		- Change the tul_abort_srb routine to use scsi_done. <01>
+ * 09/07/98 hl  - v1.02
+ *              - Change the INI9100U define and proc_dir_entry to
+ *                reflect the newer Kernel 2.1.118, but the v1.o1o
+ *                should work with Kernel 2.1.118.
+ * 09/20/98 wh  - v1.02a
+ *              - Support Abort command.
+ *              - Handle reset routine.
+ * 09/21/98 hl  - v1.03
+ *              - remove comments.
+ * 12/09/98 bv	- v1.03a
+ *		- Removed unused code
+ * 12/13/98 bv	- v1.03b
+ *		- Remove cli() locking for kernels >= 2.1.95. This uses
+ *		  spinlocks to serialize access to the pSRB_head and
+ *		  pSRB_tail members of the HCS structure.
+ * 09/01/99 bv	- v1.03d
+ *		- Fixed a deadlock problem in SMP.
+ * 21/01/99 bv	- v1.03e
+ *		- Add support for the Domex 3192U PCI SCSI
+ *		  This is a slightly modified patch by
+ *		  Brian Macy <bmacy@sunshinecomputing.com>
+ * 22/02/99 bv	- v1.03f
+ *		- Didn't detect the INIC-950 in 2.0.x correctly.
+ *		  Now fixed.
+ **************************************************************************/
+
+#define CVT_LINUX_VERSION(V,P,S)        (V * 65536 + P * 256 + S)
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+#include <stdarg.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#if LINUX_VERSION_CODE <= CVT_LINUX_VERSION(2,1,92)
+#include <linux/bios32.h>
+#endif
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,23)
+#include <linux/init.h>
+#endif
+#include <linux/blk.h>
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+#include <asm/spinlock.h>
+#endif
+#include "sd.h"
+#include "scsi.h"
+#include "hosts.h"
+#include "ini9100u.h"
+#include <linux/stat.h>
+#include <linux/malloc.h>
+#include <linux/config.h>
+
+#else
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include "../block/blk.h"
+#include "scsi.h"
+#include "sd.h"
+#include "hosts.h"
+#include <linux/malloc.h>
+#include "ini9100u.h"
+#endif
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,93)
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#endif
+#endif
+
+#ifdef DEBUG_i91u
+unsigned int i91u_debug = DEBUG_DEFAULT;
+#endif
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = INI9100U;
+#include "scsi_module.c"
+#endif
+
+char *i91uCopyright = "Copyright (C) 1996-98";
+char *i91uInitioName = "by Initio Corporation";
+char *i91uProductName = "INI-9X00U/UW";
+char *i91uVersion = "v1.03f";
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+struct proc_dir_entry proc_scsi_ini9100u =
+{
+	PROC_SCSI_INI9100U, 7, "INI9100U",
+	S_IFDIR | S_IRUGO | S_IXUGO, 2,
+	0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+#endif
+
+#define TULSZ(sz)     (sizeof(sz) / sizeof(sz[0]))
+#define TUL_RDWORD(x,y)         (short)(inl((int)((ULONG)((ULONG)x+(UCHAR)y)) ))
+
+/* set by i91_setup according to the command line */
+static int setup_called = 0;
+
+static int tul_num_ch = 4;	/* Maximum 4 adapters           */
+static int tul_num_scb;
+static int tul_tag_enable = 1;
+static SCB *tul_scb;
+
+#ifdef DEBUG_i91u
+static int setup_debug = 0;
+#endif
+
+static char *setup_str = (char *) NULL;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void i91u_intr0(int irq, void *dev_id, struct pt_regs *);
+static void i91u_intr1(int irq, void *dev_id, struct pt_regs *);
+static void i91u_intr2(int irq, void *dev_id, struct pt_regs *);
+static void i91u_intr3(int irq, void *dev_id, struct pt_regs *);
+static void i91u_intr4(int irq, void *dev_id, struct pt_regs *);
+static void i91u_intr5(int irq, void *dev_id, struct pt_regs *);
+static void i91u_intr6(int irq, void *dev_id, struct pt_regs *);
+static void i91u_intr7(int irq, void *dev_id, struct pt_regs *);
+#else
+static void i91u_intr0(int irq, struct pt_regs *);
+static void i91u_intr1(int irq, struct pt_regs *);
+static void i91u_intr2(int irq, struct pt_regs *);
+static void i91u_intr3(int irq, struct pt_regs *);
+static void i91u_intr4(int irq, struct pt_regs *);
+static void i91u_intr5(int irq, struct pt_regs *);
+static void i91u_intr6(int irq, struct pt_regs *);
+static void i91u_intr7(int irq, struct pt_regs *);
+#endif
+
+static void i91u_panic(char *msg);
+
+static void i91uSCBPost(BYTE * pHcb, BYTE * pScb);
+
+				/* ---- EXTERNAL FUNCTIONS ---- */
+					/* Get total number of adapters */
+extern void init_i91uAdapter_table(void);
+extern int Addi91u_into_Adapter_table(WORD, WORD, BYTE, BYTE, BYTE);
+extern int tul_ReturnNumberOfAdapters(void);
+extern void get_tulipPCIConfig(HCS * pHCB, int iChannel_index);
+extern int init_tulip(HCS * pHCB, SCB * pSCB, int tul_num_scb, BYTE * pbBiosAdr, int reset_time);
+extern SCB *tul_alloc_scb(HCS * pHCB);
+extern int tul_abort_srb(HCS * pHCB, Scsi_Cmnd * pSRB);
+extern void tul_exec_scb(HCS * pHCB, SCB * pSCB);
+extern void tul_release_scb(HCS * pHCB, SCB * pSCB);
+extern void tul_stop_bm(HCS * pHCB);
+extern int tul_reset_scsi(HCS * pCurHcb, int seconds);
+extern int tul_isr(HCS * pHCB);
+extern int tul_reset(HCS * pHCB, Scsi_Cmnd * pSRB, unsigned char target);
+extern int tul_reset_scsi_bus(HCS * pCurHcb);
+extern int tul_device_reset(HCS * pCurHcb, ULONG pSrb, unsigned int target, unsigned int ResetFlags);
+				/* ---- EXTERNAL VARIABLES ---- */
+extern HCS tul_hcs[];
+
+const PCI_ID i91u_pci_devices[] = {
+	{ INI_VENDOR_ID, I950_DEVICE_ID },
+	{ INI_VENDOR_ID, I940_DEVICE_ID },
+	{ INI_VENDOR_ID, I935_DEVICE_ID },
+	{ INI_VENDOR_ID, I920_DEVICE_ID },
+	{ DMX_VENDOR_ID, I920_DEVICE_ID },
+};
+
+/*
+ *  queue services:
+ */
+/*****************************************************************************
+ Function name  : i91uAppendSRBToQueue
+ Description    : This function will push current request into save list
+ Input          : pSRB  -       Pointer to SCSI request block.
+		  pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : None.
+*****************************************************************************/
+static void i91uAppendSRBToQueue(HCS * pHCB, Scsi_Cmnd * pSRB)
+{
+	ULONG flags;
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_lock_irqsave(&(pHCB->pSRB_lock), flags);
+#else
+	save_flags(flags);
+	cli();
+#endif
+
+	pSRB->next = NULL;	/* Pointer to next              */
+
+	if (pHCB->pSRB_head == NULL)
+		pHCB->pSRB_head = pSRB;
+	else
+		pHCB->pSRB_tail->next = pSRB;	/* Pointer to next              */
+	pHCB->pSRB_tail = pSRB;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_unlock_irqrestore(&(pHCB->pSRB_lock), flags);
+#else
+	restore_flags(flags);
+#endif
+	return;
+}
+
+/*****************************************************************************
+ Function name  : i91uPopSRBFromQueue
+ Description    : This function will pop current request from save list
+ Input          : pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : pSRB  -       Pointer to SCSI request block.
+*****************************************************************************/
+static Scsi_Cmnd *i91uPopSRBFromQueue(HCS * pHCB)
+{
+	Scsi_Cmnd *pSRB;
+	ULONG flags;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_lock_irqsave(&(pHCB->pSRB_lock), flags);
+#else
+	save_flags(flags);
+	cli();
+#endif
+
+	if ((pSRB = pHCB->pSRB_head) != NULL) {
+		pHCB->pSRB_head = pHCB->pSRB_head->next;
+		pSRB->next = NULL;
+	}
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_unlock_irqrestore(&(pHCB->pSRB_lock), flags);
+#else
+	restore_flags(flags);
+#endif
+
+	return (pSRB);
+}
+
+/* called from init/main.c */
+
+void i91u_setup(char *str, int *ints)
+{
+	if (setup_called)
+		i91u_panic("i91u: i91u_setup called twice.\n");
+
+	setup_called = ints[0];
+	setup_str = str;
+
+#ifdef DEBUG_i91u
+	setup_debug = ints[0] >= 1 ? ints[1] : DEBUG_DEFAULT;
+#endif
+}
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,93)
+int tul_NewReturnNumberOfAdapters(void)
+{
+	struct pci_dev *pDev = NULL;	/* Start from none              */
+	int iAdapters = 0;
+	long dRegValue;
+	WORD wBIOS;
+	int i = 0;
+
+	init_i91uAdapter_table();
+
+	for (i = 0; i < TULSZ(i91u_pci_devices); i++)
+	{
+		while ((pDev = pci_find_device(i91u_pci_devices[i].vendor_id, i91u_pci_devices[i].device_id, pDev)) != NULL) {
+			pci_read_config_dword(pDev, 0x44, (u32 *) & dRegValue);
+			wBIOS = (UWORD) (dRegValue & 0xFF);
+			if (((dRegValue & 0xFF00) >> 8) == 0xFF)
+				dRegValue = 0;
+			wBIOS = (wBIOS << 8) + ((UWORD) ((dRegValue & 0xFF00) >> 8));
+			if (Addi91u_into_Adapter_table(wBIOS,
+							(pDev->base_address[0] & 0xFFFE),
+					       		pDev->irq,
+					       		pDev->bus->number,
+					       		(pDev->devfn >> 3)
+		    		) == 0)
+				iAdapters++;
+		}
+	}
+
+	return (iAdapters);
+}
+
+#else				/* <01> */
+
+/*****************************************************************************
+ Function name	: tul_ReturnNumberOfAdapters
+ Description	: This function will scan PCI bus to get all Orchid card
+ Input		: None.
+ Output		: None.
+ Return		: SUCCESSFUL	- Successful scan
+		  ohterwise	- No drives founded
+*****************************************************************************/
+int tul_ReturnNumberOfAdapters(void)
+{
+	unsigned int i, iAdapters;
+	unsigned int dRegValue;
+	unsigned short command;
+	WORD wBIOS, wBASE;
+	BYTE bPCIBusNum, bInterrupt, bPCIDeviceNum;
+
+	iAdapters = 0;
+	/*
+	 * PCI-bus probe.
+	 */
+	if (pcibios_present()) {
+#ifdef MMAPIO
+		unsigned long page_offset, base;
+#endif
+
+		int index;
+		unsigned char pci_bus, pci_devfn;
+
+		bPCIBusNum = 0;
+		bPCIDeviceNum = 0;
+		init_i91uAdapter_table();
+		for (i = 0; i < TULSZ(i91u_pci_devices); i++) {
+			index = 0;
+			while (!(pcibios_find_device(i91u_pci_devices[i].vendor_id,
+					   i91u_pci_devices[i].device_id,
+					 index++, &pci_bus, &pci_devfn)))
+			{
+				if (i == 2) {
+					   printk("i91u: The RAID controller is not supported by\n");
+					   printk("i91u:         this driver, we are ignoring it.\n");
+				} else {
+					/*
+					 * Read sundry information from PCI BIOS.
+					 */
+					bPCIBusNum = pci_bus;
+					bPCIDeviceNum = pci_devfn;
+					pcibios_read_config_dword(pci_bus, pci_devfn, PCI_BASE_ADDRESS_0,
+							     &dRegValue);
+					if (dRegValue == -1) {	/* Check return code            */
+						printk("\n\ri91u: tulip read configuration error.\n");
+						return (0);	/* Read configuration space error  */
+					}
+					/* <02> read from base address + 0x50 offset to get the wBIOS balue. */
+					wBASE = (WORD) dRegValue;
+
+					/* Now read the interrupt line  */
+					pcibios_read_config_dword(pci_bus, pci_devfn, PCI_INTERRUPT_LINE,
+							     &dRegValue);
+					bInterrupt = dRegValue & 0xFF;	/* Assign interrupt line      */
+					pcibios_read_config_word(pci_bus, pci_devfn, PCI_COMMAND, &command);
+					pcibios_write_config_word(pci_bus, pci_devfn, PCI_COMMAND,
+								  command | PCI_COMMAND_MASTER | PCI_COMMAND_IO);
+					wBASE &= PCI_BASE_ADDRESS_IO_MASK;
+					wBIOS = TUL_RDWORD(wBASE, 0x50);
+
+#ifdef MMAPIO
+					base = wBASE & PAGE_MASK;
+					page_offset = wBASE - base;
+
+					/*
+					 * replace the next line with this one if you are using 2.1.x:
+					 * temp_p->maddr = ioremap(base, page_offset + 256);
+					 */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,0)
+					wBASE = ioremap(base, page_offset + 256);
+#else
+					wBASE = (WORD) vremap(base, page_offset + 256);
+#endif
+					if (wBASE) {
+						wBASE += page_offset;
+					}
+#endif
+
+					if (Addi91u_into_Adapter_table(wBIOS, wBASE, bInterrupt, bPCIBusNum,
+						   bPCIDeviceNum) == 0x0)
+						iAdapters++;
+				}
+			}	/* while(pdev=....) */
+		}		/* for PCI_DEVICES */
+	}			/* PCI BIOS present */
+	return (iAdapters);
+}
+#endif
+
+int i91u_detect(Scsi_Host_Template * tpnt)
+{
+	SCB *pSCB;
+	HCS *pHCB;
+	struct Scsi_Host *hreg;
+	unsigned long i;	/* 01/14/98                     */
+	int ok = 0, iAdapters;
+	ULONG dBiosAdr;
+	BYTE *pbBiosAdr;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+	tpnt->proc_dir = &proc_scsi_ini9100u;
+#endif
+
+	if (setup_called) {	/* Setup by i91u_setup          */
+		printk("i91u: processing commandline: ");
+
+#ifdef DEBUG_i91u
+		if (setup_called > 1) {
+			printk("\ni91u: %s\n", setup_str);
+			printk("i91u: usage: i91u[=<DEBUG>]\n");
+			i91u_panic("i91u panics in line %d", __LINE__);
+		}
+		i91u_debug = setup_debug;
+#endif
+	}
+	/* Get total number of adapters in the motherboard */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,93)
+#ifdef CONFIG_PCI
+	iAdapters = tul_NewReturnNumberOfAdapters();
+#else
+	iAdapters = tul_ReturnNumberOfAdapters();
+#endif
+#else
+	iAdapters = tul_ReturnNumberOfAdapters();
+#endif
+
+	if (iAdapters == 0)	/* If no tulip founded, return */
+		return (0);
+
+	tul_num_ch = (iAdapters > tul_num_ch) ? tul_num_ch : iAdapters;
+	/* Update actually channel number */
+	if (tul_tag_enable) {	/* 1.01i                  */
+		tul_num_scb = MAX_TARGETS * i91u_MAXQUEUE;
+	} else {
+		tul_num_scb = MAX_TARGETS + 3;	/* 1-tape, 1-CD_ROM, 1- extra */
+	}			/* Update actually SCBs per adapter */
+
+	/* Get total memory needed for HCS */
+	i = tul_num_ch * sizeof(HCS);
+	memset((unsigned char *) &tul_hcs[0], 0, i);	/* Initialize tul_hcs 0 */
+	/* Get total memory needed for SCB */
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+	for (; tul_num_scb >= MAX_TARGETS + 3; tul_num_scb--) {
+		i = tul_num_ch * tul_num_scb * sizeof(SCB);
+		if ((tul_scb = (SCB *) kmalloc(i, GFP_ATOMIC | GFP_DMA)) != NULL)
+			break;
+	}
+#else
+	i = tul_num_ch * tul_num_scb * sizeof(SCB);
+	tul_scb = (SCB *) scsi_init_malloc(i, GFP_ATOMIC | GFP_DMA);
+#endif
+	if (tul_scb == NULL) {
+		printk("i91u: SCB memory allocation error\n");
+		return (0);
+	}
+	memset((unsigned char *) tul_scb, 0, i);
+
+	pSCB = tul_scb;
+	for (i = 0; i < tul_num_ch * tul_num_scb; i++, pSCB++) {
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+		pSCB->SCB_SGPAddr = (U32) VIRT_TO_BUS(&pSCB->SCB_SGList[0]);
+#else
+		pSCB->SCB_SGPAddr = (U32) (&pSCB->SCB_SGList[0]);
+#endif
+	}
+
+	for (i = 0, pHCB = &tul_hcs[0];		/* Get pointer for control block */
+	     i < tul_num_ch;
+	     i++, pHCB++) {
+		pHCB->pSRB_head = NULL;		/* Initial SRB save queue       */
+		pHCB->pSRB_tail = NULL;		/* Initial SRB save queue       */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+		pHCB->pSRB_lock = SPIN_LOCK_UNLOCKED;	/* SRB save queue lock */
+#endif
+		request_region(pHCB->HCS_Base, 0x100, "i91u");	/* Register */
+
+		get_tulipPCIConfig(pHCB, i);
+
+		dBiosAdr = pHCB->HCS_BIOS;
+		dBiosAdr = (dBiosAdr << 4);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+		pbBiosAdr = phys_to_virt(dBiosAdr);
+#endif
+
+		init_tulip(pHCB, tul_scb + (i * tul_num_scb), tul_num_scb, pbBiosAdr, 10);
+		pHCB->HCS_Index = i;	/* 7/29/98 */
+		hreg = scsi_register(tpnt, sizeof(HCS));
+		hreg->io_port = pHCB->HCS_Base;
+		hreg->n_io_port = 0xff;
+		hreg->can_queue = tul_num_scb;	/* 03/05/98                      */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+		hreg->unique_id = pHCB->HCS_Base;
+		hreg->max_id = pHCB->HCS_MaxTar;
+#endif
+		hreg->max_lun = 32;	/* 10/21/97                     */
+		hreg->irq = pHCB->HCS_Intr;
+		hreg->this_id = pHCB->HCS_SCSI_ID;	/* Assign HCS index           */
+		hreg->base = (UCHAR *) pHCB;
+		hreg->sg_tablesize = TOTAL_SG_ENTRY;	/* Maximun support is 32 */
+
+		/* Initial tulip chip           */
+		switch (i) {
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+		case 0:
+			ok = request_irq(pHCB->HCS_Intr, i91u_intr0, SA_INTERRUPT | SA_SHIRQ, "i91u", NULL);
+			break;
+		case 1:
+			ok = request_irq(pHCB->HCS_Intr, i91u_intr1, SA_INTERRUPT | SA_SHIRQ, "i91u", NULL);
+			break;
+		case 2:
+			ok = request_irq(pHCB->HCS_Intr, i91u_intr2, SA_INTERRUPT | SA_SHIRQ, "i91u", NULL);
+			break;
+		case 3:
+			ok = request_irq(pHCB->HCS_Intr, i91u_intr3, SA_INTERRUPT | SA_SHIRQ, "i91u", NULL);
+			break;
+		case 4:
+			ok = request_irq(pHCB->HCS_Intr, i91u_intr4, SA_INTERRUPT | SA_SHIRQ, "i91u", NULL);
+			break;
+		case 5:
+			ok = request_irq(pHCB->HCS_Intr, i91u_intr5, SA_INTERRUPT | SA_SHIRQ, "i91u", NULL);
+			break;
+		case 6:
+			ok = request_irq(pHCB->HCS_Intr, i91u_intr6, SA_INTERRUPT | SA_SHIRQ, "i91u", NULL);
+			break;
+		case 7:
+			ok = request_irq(pHCB->HCS_Intr, i91u_intr7, SA_INTERRUPT | SA_SHIRQ, "i91u", NULL);
+			break;
+		default:
+			i91u_panic("i91u: Too many host adapters\n");
+			break;
+		}
+		if (ok < 0) {
+			if (ok == -EINVAL) {
+				printk("i91u: bad IRQ %d.\n", pHCB->HCS_Intr);
+				printk("         Contact author.\n");
+			} else if (ok == -EBUSY)
+				printk("i91u: IRQ %d already in use. Configure another.\n",
+				       pHCB->HCS_Intr);
+			else {
+				printk("\ni91u: Unexpected error code on requesting IRQ %d.\n",
+				       pHCB->HCS_Intr);
+				printk("         Contact author.\n");
+			}
+			i91u_panic("i91u: driver needs an IRQ.\n");
+		}
+#endif
+	}
+
+	tpnt->this_id = -1;
+	tpnt->can_queue = 1;
+
+	return 1;
+}
+
+static void i91uBuildSCB(HCS * pHCB, SCB * pSCB, Scsi_Cmnd * SCpnt)
+{				/* Create corresponding SCB     */
+	struct scatterlist *pSrbSG;
+	SG *pSG;		/* Pointer to SG list           */
+	int i;
+	long TotalLen;
+
+	pSCB->SCB_Post = i91uSCBPost;	/* i91u's callback routine      */
+	pSCB->SCB_Srb = SCpnt;
+	pSCB->SCB_Opcode = ExecSCSI;
+	pSCB->SCB_Flags = SCF_POST;	/* After SCSI done, call post routine */
+	pSCB->SCB_Target = SCpnt->target;
+	pSCB->SCB_Lun = SCpnt->lun;
+	pSCB->SCB_Ident = SCpnt->lun | DISC_ALLOW;
+	pSCB->SCB_Flags |= SCF_SENSE;	/* Turn on auto request sense   */
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+	pSCB->SCB_SensePtr = (U32) VIRT_TO_BUS(SCpnt->sense_buffer);
+#else
+	pSCB->SCB_SensePtr = (U32) (SCpnt->sense_buffer);
+#endif
+
+	pSCB->SCB_SenseLen = SENSE_SIZE;
+
+	pSCB->SCB_CDBLen = SCpnt->cmd_len;
+	pSCB->SCB_HaStat = 0;
+	pSCB->SCB_TaStat = 0;
+	memcpy(&pSCB->SCB_CDB[0], &SCpnt->cmnd, SCpnt->cmd_len);
+
+	if (SCpnt->device->tagged_supported) {	/* Tag Support                  */
+		pSCB->SCB_TagMsg = SIMPLE_QUEUE_TAG;	/* Do simple tag only   */
+	} else {
+		pSCB->SCB_TagMsg = 0;	/* No tag support               */
+	}
+
+	if (SCpnt->use_sg) {
+		pSrbSG = (struct scatterlist *) SCpnt->request_buffer;
+		if (SCpnt->use_sg == 1) {	/* If only one entry in the list *//*      treat it as regular I/O */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+			pSCB->SCB_BufPtr = (U32) VIRT_TO_BUS(pSrbSG->address);
+#else
+			pSCB->SCB_BufPtr = (U32) (pSrbSG->address);
+#endif
+			TotalLen = pSrbSG->length;
+			pSCB->SCB_SGLen = 0;
+		} else {	/* Assign SG physical address   */
+			pSCB->SCB_BufPtr = pSCB->SCB_SGPAddr;
+			pSCB->SCB_Flags |= SCF_SG;	/* Turn on SG list flag       */
+			for (i = 0, TotalLen = 0, pSG = &pSCB->SCB_SGList[0];	/* 1.01g */
+			     i < SCpnt->use_sg;
+			     i++, pSG++, pSrbSG++) {
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+				pSG->SG_Ptr = (U32) VIRT_TO_BUS(pSrbSG->address);
+#else
+				pSG->SG_Ptr = (U32) (pSrbSG->address);
+#endif
+				TotalLen += pSG->SG_Len = pSrbSG->length;
+			}
+			pSCB->SCB_SGLen = i;
+		}
+		pSCB->SCB_BufLen = (SCpnt->request_bufflen > TotalLen) ?
+		    TotalLen : SCpnt->request_bufflen;
+	} else {		/* Non SG                       */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+		pSCB->SCB_BufPtr = (U32) VIRT_TO_BUS(SCpnt->request_buffer);
+#else
+		pSCB->SCB_BufPtr = (U32) (SCpnt->request_buffer);
+#endif
+		pSCB->SCB_BufLen = SCpnt->request_bufflen;
+		pSCB->SCB_SGLen = 0;
+	}
+
+	return;
+}
+
+/* 
+ *  Queue a command and setup interrupts for a free bus.
+ */
+int i91u_queue(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
+{
+	register SCB *pSCB;
+	HCS *pHCB;		/* Point to Host adapter control block */
+
+	if (SCpnt->lun > 16) {	/* 07/22/98 */
+
+		SCpnt->result = (DID_TIME_OUT << 16);
+		done(SCpnt);	/* Notify system DONE           */
+		return (0);
+	}
+	pHCB = (HCS *) SCpnt->host->base;
+
+	SCpnt->scsi_done = done;
+	/* Get free SCSI control block  */
+	if ((pSCB = tul_alloc_scb(pHCB)) == NULL) {
+		i91uAppendSRBToQueue(pHCB, SCpnt);	/* Buffer this request  */
+		return (0);
+	}
+	i91uBuildSCB(pHCB, pSCB, SCpnt);
+	tul_exec_scb(pHCB, pSCB);	/* Start execute SCB            */
+	return (0);
+}
+
+/*
+ *  We only support command in interrupt-driven fashion
+ */
+int i91u_command(Scsi_Cmnd * SCpnt)
+{
+	printk("i91u: interrupt driven driver; use i91u_queue()\n");
+	return -1;
+}
+
+/*
+ *  Abort a queued command
+ *  (commands that are on the bus can't be aborted easily)
+ */
+int i91u_abort(Scsi_Cmnd * SCpnt)
+{
+	HCS *pHCB;
+
+	pHCB = (HCS *) SCpnt->host->base;
+	return tul_abort_srb(pHCB, SCpnt);
+}
+
+/*
+ *  Reset registers, reset a hanging bus and
+ *  kill active and disconnected commands for target w/o soft reset
+ */
+int i91u_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags)
+{				/* I need Host Control Block Information */
+	HCS *pHCB;
+
+	pHCB = (HCS *) SCpnt->host->base;
+
+	if (reset_flags & (SCSI_RESET_SUGGEST_BUS_RESET | SCSI_RESET_SUGGEST_HOST_RESET))
+		return tul_reset_scsi_bus(pHCB);
+	else
+		return tul_device_reset(pHCB, (ULONG) SCpnt, SCpnt->target, reset_flags);
+}
+
+/*
+ * Return the "logical geometry"
+ */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+int i91u_biosparam(Scsi_Disk * disk, kdev_t dev, int *info_array)
+#else
+int i91u_biosparam(Scsi_Disk * disk, int dev, int *info_array)
+#endif
+{
+	HCS *pHcb;		/* Point to Host adapter control block */
+	TCS *pTcb;
+
+	pHcb = (HCS *) disk->device->host->base;
+	pTcb = &pHcb->HCS_Tcs[disk->device->id];
+
+	if (pTcb->TCS_DrvHead) {
+		info_array[0] = pTcb->TCS_DrvHead;
+		info_array[1] = pTcb->TCS_DrvSector;
+		info_array[2] = disk->capacity / pTcb->TCS_DrvHead / pTcb->TCS_DrvSector;
+	} else {
+		if (pTcb->TCS_DrvFlags & TCF_DRV_255_63) {
+			info_array[0] = 255;
+			info_array[1] = 63;
+			info_array[2] = disk->capacity / 255 / 63;
+		} else {
+			info_array[0] = 64;
+			info_array[1] = 32;
+			info_array[2] = disk->capacity >> 11;
+		}
+	}
+
+#if defined(DEBUG_BIOSPARAM)
+	if (i91u_debug & debug_biosparam) {
+		printk("bios geometry: head=%d, sec=%d, cyl=%d\n",
+		       info_array[0], info_array[1], info_array[2]);
+		printk("WARNING: check, if the bios geometry is correct.\n");
+	}
+#endif
+
+	return 0;
+}
+
+/*****************************************************************************
+ Function name  : i91uSCBPost
+ Description    : This is callback routine be called when tulip finish one
+			SCSI command.
+ Input          : pHCB  -       Pointer to host adapter control block.
+		  pSCB  -       Pointer to SCSI control block.
+ Output         : None.
+ Return         : None.
+*****************************************************************************/
+static void i91uSCBPost(BYTE * pHcb, BYTE * pScb)
+{
+	Scsi_Cmnd *pSRB;	/* Pointer to SCSI request block */
+	HCS *pHCB;
+	SCB *pSCB;
+
+	pHCB = (HCS *) pHcb;
+	pSCB = (SCB *) pScb;
+	if ((pSRB = pSCB->SCB_Srb) == 0) {
+		printk("i91uSCBPost: SRB pointer is empty\n");
+
+		tul_release_scb(pHCB, pSCB);	/* Release SCB for current channel */
+		return;
+	}
+	switch (pSCB->SCB_HaStat) {
+	case 0x0:
+	case 0xa:		/* Linked command complete without error and linked normally */
+	case 0xb:		/* Linked command complete without error interrupt generated */
+		pSCB->SCB_HaStat = 0;
+		break;
+
+	case 0x11:		/* Selection time out-The initiator selection or target
+				   reselection was not complete within the SCSI Time out period */
+		pSCB->SCB_HaStat = DID_TIME_OUT;
+		break;
+
+	case 0x14:		/* Target bus phase sequence failure-An invalid bus phase or bus
+				   phase sequence was requested by the target. The host adapter
+				   will generate a SCSI Reset Condition, notifying the host with
+				   a SCRD interrupt */
+		pSCB->SCB_HaStat = DID_RESET;
+		break;
+
+	case 0x1a:		/* SCB Aborted. 07/21/98 */
+		pSCB->SCB_HaStat = DID_ABORT;
+		break;
+
+	case 0x12:		/* Data overrun/underrun-The target attempted to transfer more data
+				   than was allocated by the Data Length field or the sum of the
+				   Scatter / Gather Data Length fields. */
+	case 0x13:		/* Unexpected bus free-The target dropped the SCSI BSY at an unexpected time. */
+	case 0x16:		/* Invalid SCB Operation Code. */
+
+	default:
+		printk("ini9100u: %x %x\n", pSCB->SCB_HaStat, pSCB->SCB_TaStat);
+		pSCB->SCB_HaStat = DID_ERROR;	/* Couldn't find any better */
+		break;
+	}
+
+	pSRB->result = pSCB->SCB_TaStat | (pSCB->SCB_HaStat << 16);
+
+	if (pSRB == NULL) {
+		printk("pSRB is NULL\n");
+	}
+	pSRB->scsi_done(pSRB);	/* Notify system DONE           */
+	if ((pSRB = i91uPopSRBFromQueue(pHCB)) != NULL)
+		/* Find the next pending SRB    */
+	{			/* Assume resend will success   */
+		/* Reuse old SCB                */
+		i91uBuildSCB(pHCB, pSCB, pSRB);		/* Create corresponding SCB     */
+
+		tul_exec_scb(pHCB, pSCB);	/* Start execute SCB            */
+	} else {		/* No Pending SRB               */
+		tul_release_scb(pHCB, pSCB);	/* Release SCB for current channel */
+	}
+	return;
+}
+
+/*
+ * Interrupts handler (main routine of the driver)
+ */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void i91u_intr0(int irqno, void *dev_id, struct pt_regs *regs)
+#else
+static void i91u_intr0(int irqno, struct pt_regs *regs)
+#endif
+{
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	unsigned long flags;
+#endif
+
+	if (tul_hcs[0].HCS_Intr != irqno)
+		return;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_lock_irqsave(&io_request_lock, flags);
+#endif
+
+	tul_isr(&tul_hcs[0]);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_unlock_irqrestore(&io_request_lock, flags);
+#endif
+}
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void i91u_intr1(int irqno, void *dev_id, struct pt_regs *regs)
+#else
+static void i91u_intr1(int irqno, struct pt_regs *regs)
+#endif
+{
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	unsigned long flags;
+#endif
+
+	if (tul_hcs[1].HCS_Intr != irqno)
+		return;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_lock_irqsave(&io_request_lock, flags);
+#endif
+
+	tul_isr(&tul_hcs[1]);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_unlock_irqrestore(&io_request_lock, flags);
+#endif
+}
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void i91u_intr2(int irqno, void *dev_id, struct pt_regs *regs)
+#else
+static void i91u_intr2(int irqno, struct pt_regs *regs)
+#endif
+{
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	unsigned long flags;
+#endif
+
+	if (tul_hcs[2].HCS_Intr != irqno)
+		return;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_lock_irqsave(&io_request_lock, flags);
+#endif
+
+	tul_isr(&tul_hcs[2]);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_unlock_irqrestore(&io_request_lock, flags);
+#endif
+}
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void i91u_intr3(int irqno, void *dev_id, struct pt_regs *regs)
+#else
+static void i91u_intr3(int irqno, struct pt_regs *regs)
+#endif
+{
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	unsigned long flags;
+#endif
+
+	if (tul_hcs[3].HCS_Intr != irqno)
+		return;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_lock_irqsave(&io_request_lock, flags);
+#endif
+
+	tul_isr(&tul_hcs[3]);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_unlock_irqrestore(&io_request_lock, flags);
+#endif
+}
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void i91u_intr4(int irqno, void *dev_id, struct pt_regs *regs)
+#else
+static void i91u_intr4(int irqno, struct pt_regs *regs)
+#endif
+{
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	unsigned long flags;
+#endif
+
+	if (tul_hcs[4].HCS_Intr != irqno)
+		return;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_lock_irqsave(&io_request_lock, flags);
+#endif
+
+	tul_isr(&tul_hcs[4]);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_unlock_irqrestore(&io_request_lock, flags);
+#endif
+}
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void i91u_intr5(int irqno, void *dev_id, struct pt_regs *regs)
+#else
+static void i91u_intr5(int irqno, struct pt_regs *regs)
+#endif
+{
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	unsigned long flags;
+#endif
+
+	if (tul_hcs[5].HCS_Intr != irqno)
+		return;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_lock_irqsave(&io_request_lock, flags);
+#endif
+
+	tul_isr(&tul_hcs[5]);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_unlock_irqrestore(&io_request_lock, flags);
+#endif
+}
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void i91u_intr6(int irqno, void *dev_id, struct pt_regs *regs)
+#else
+static void i91u_intr6(int irqno, struct pt_regs *regs)
+#endif
+{
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	unsigned long flags;
+#endif
+
+	if (tul_hcs[6].HCS_Intr != irqno)
+		return;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_lock_irqsave(&io_request_lock, flags);
+#endif
+
+	tul_isr(&tul_hcs[6]);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_unlock_irqrestore(&io_request_lock, flags);
+#endif
+}
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void i91u_intr7(int irqno, void *dev_id, struct pt_regs *regs)
+#else
+static void i91u_intr7(int irqno, struct pt_regs *regs)
+#endif
+{
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	unsigned long flags;
+#endif
+
+	if (tul_hcs[7].HCS_Intr != irqno)
+		return;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_lock_irqsave(&io_request_lock, flags);
+#endif
+
+	tul_isr(&tul_hcs[7]);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_unlock_irqrestore(&io_request_lock, flags);
+#endif
+}
+
+/* 
+ * Dump the current driver status and panic...
+ */
+static void i91u_panic(char *msg)
+{
+	printk("\ni91u_panic: %s\n", msg);
+	panic("i91u panic");
+}
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/ini9100u.h linux/drivers/scsi/ini9100u.h
--- linux.vanilla/drivers/scsi/ini9100u.h	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/ini9100u.h	Fri Mar 26 17:14:47 1999
@@ -0,0 +1,345 @@
+/**************************************************************************
+ * Initio 9100 device driver for Linux.
+ *
+ * Copyright (c) 1994-1998 Initio Corporation
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *************************************************************************
+ *
+ * Module: ini9100u.h
+ * Description: INI-9100U/UW LINUX device driver header
+ * Revision History:
+ * 06/18/96 Harry Chen, Initial Version 1.00A (Beta)
+ * 06/23/98 hc	- v1.01k
+ *		- Get it work for kernel version >= 2.1.75
+ * 12/09/98 bv	- v1.03a
+ *		- Removed unused code
+ * 12/13/98 bv	- v1.03b
+ *		- Add spinlocks to HCS structure.
+ * 21/01/99 bv	- v1.03e
+ *		- Added PCI_ID structure
+ **************************************************************************/
+
+#ifndef	CVT_LINUX_VERSION
+#define	CVT_LINUX_VERSION(V,P,S)	(((V) * 65536) + ((P) * 256) + (S))
+#endif
+
+#ifndef	LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+
+#include "sd.h"
+
+extern int i91u_detect(Scsi_Host_Template *);
+extern int i91u_command(Scsi_Cmnd *);
+extern int i91u_queue(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
+extern int i91u_abort(Scsi_Cmnd *);
+extern int i91u_reset(Scsi_Cmnd *, unsigned int);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1, 3, 0)
+extern int i91u_biosparam(Scsi_Disk *, kdev_t, int *);	/*for linux v2.0 */
+extern struct proc_dir_entry proc_scsi_ini9100u;
+#else
+extern int i91u_biosparam(Disk *, int, int *);	/*for linux v1.13 */
+#endif
+
+#define i91u_REVID "Initio INI-9X00U/UW SCSI device driver; Revision: 1.03f"
+
+#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(1, 3, 0)
+#define INI9100U	{ \
+		NULL, \
+		NULL, \
+		i91u_REVID, \
+		i91u_detect, \
+		NULL, \
+		NULL, \
+		i91u_command, \
+		i91u_queue, \
+		i91u_abort, \
+		i91u_reset, \
+		NULL, \
+		i91u_biosparam, \
+		1, \
+		7, \
+		SG_ALL, \
+		1, \
+		0, \
+		0, \
+		ENABLE_CLUSTERING \
+}
+#else
+
+#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(2, 1, 75)
+#define INI9100U	{ \
+		NULL, \
+		NULL, \
+		&proc_scsi_ini9100u, \
+		NULL, \
+		i91u_REVID, \
+		i91u_detect, \
+		NULL, \
+		NULL, \
+		i91u_command, \
+		i91u_queue, \
+		i91u_abort, \
+		i91u_reset, \
+		NULL, \
+		i91u_biosparam, \
+		1, \
+		7, \
+		SG_ALL, \
+		1, \
+		0, \
+		0, \
+		ENABLE_CLUSTERING \
+}
+#else				/* Version >= 2.1.75 */
+#define INI9100U	{ \
+	next:		NULL,						\
+	module:		NULL,						\
+	proc_dir:	&proc_scsi_ini9100u, \
+	proc_info:	NULL,				\
+	name:		i91u_REVID, \
+	detect:		i91u_detect, \
+	release:	NULL, \
+	info:		NULL,					\
+	command:	i91u_command, \
+	queuecommand:	i91u_queue, \
+ 	eh_strategy_handler: NULL, \
+ 	eh_abort_handler: NULL, \
+ 	eh_device_reset_handler: NULL, \
+ 	eh_bus_reset_handler: NULL, \
+ 	eh_host_reset_handler: NULL, \
+	abort:		i91u_abort, \
+	reset:		i91u_reset, \
+	slave_attach:	NULL, \
+	bios_param:	i91u_biosparam, \
+	can_queue:	1, \
+	this_id:	1, \
+	sg_tablesize:	SG_ALL, \
+	cmd_per_lun: 	1, \
+	present:	0, \
+	unchecked_isa_dma: 0, \
+	use_clustering:	ENABLE_CLUSTERING, \
+ use_new_eh_code: 0 \
+}
+#endif
+#endif
+
+
+#define VIRT_TO_BUS(i)  (unsigned int) virt_to_bus((void *)(i))
+#define ULONG   unsigned long
+#define USHORT  unsigned short
+#define UCHAR   unsigned char
+#define BYTE    unsigned char
+#define WORD    unsigned short
+#define DWORD   unsigned long
+#define UBYTE   unsigned char
+#define UWORD   unsigned short
+#define UDWORD  unsigned long
+#ifdef ALPHA
+#define U32   unsigned int
+#else
+#define U32   unsigned long
+#endif
+
+#ifndef NULL
+#define NULL     0		/* zero          */
+#endif
+#ifndef TRUE
+#define TRUE     (1)		/* boolean true  */
+#endif
+#ifndef FALSE
+#define FALSE    (0)		/* boolean false */
+#endif
+#ifndef FAILURE
+#define FAILURE  (-1)
+#endif
+
+#define i91u_MAXQUEUE		2
+#define TOTAL_SG_ENTRY		32
+#define MAX_TARGETS		16
+#define SENSE_SIZE		14
+
+#define INI_VENDOR_ID   0x1101	/* Initio's PCI vendor ID       */
+#define DMX_VENDOR_ID	0x134a	/* Domex's PCI vendor ID	*/
+#define I950_DEVICE_ID	0x9500	/* Initio's inic-950 product ID   */
+#define I940_DEVICE_ID	0x9400	/* Initio's inic-940 product ID   */
+#define I935_DEVICE_ID	0x9401	/* Initio's inic-935 product ID   */
+#define I920_DEVICE_ID	0x0002	/* Initio's other product ID      */
+
+/************************************************************************/
+/*              Vendor ID/Device ID Pair Structure			*/
+/************************************************************************/
+typedef struct PCI_ID_Struc {
+	unsigned short vendor_id;
+	unsigned short device_id;
+} PCI_ID;
+
+/************************************************************************/
+/*              Scatter-Gather Element Structure                        */
+/************************************************************************/
+typedef struct SG_Struc {
+	U32 SG_Ptr;		/* Data Pointer */
+	U32 SG_Len;		/* Data Length */
+} SG;
+
+/***********************************************************************
+		SCSI Control Block
+************************************************************************/
+typedef struct Scsi_Ctrl_Blk {
+	U32 SCB_InitioReserved[9];	/* 0 */
+
+	UBYTE SCB_Opcode;	/*24 SCB command code */
+	UBYTE SCB_Flags;	/*25 SCB Flags */
+	UBYTE SCB_Target;	/*26 Target Id */
+	UBYTE SCB_Lun;		/*27 Lun */
+	U32 SCB_BufPtr;		/*28 Data Buffer Pointer */
+	U32 SCB_BufLen;		/*2C Data Allocation Length */
+	UBYTE SCB_SGLen;	/*30 SG list # */
+	UBYTE SCB_SenseLen;	/*31 Sense Allocation Length */
+	UBYTE SCB_HaStat;	/*32 */
+	UBYTE SCB_TaStat;	/*33 */
+	UBYTE SCB_CDBLen;	/*34 CDB Length */
+	UBYTE SCB_Ident;	/*35 Identify */
+	UBYTE SCB_TagMsg;	/*36 Tag Message */
+	UBYTE SCB_TagId;	/*37 Queue Tag */
+	UBYTE SCB_CDB[12];	/*38 */
+	U32 SCB_SGPAddr;	/*44 SG List/Sense Buf phy. Addr. */
+	U32 SCB_SensePtr;	/*48 Sense data pointer */
+	void (*SCB_Post) (BYTE *, BYTE *);	/*4C POST routine */
+	Scsi_Cmnd *SCB_Srb;	/*50 SRB Pointer */
+	SG SCB_SGList[TOTAL_SG_ENTRY];	/*54 Start of SG list */
+} SCB;
+
+/* Opcodes of SCB_Opcode */
+#define ExecSCSI        0x1
+#define BusDevRst       0x2
+#define AbortCmd        0x3
+
+/* Bit Definition for SCB_Flags */
+#define SCF_DONE        0x01
+#define SCF_POST        0x02
+#define SCF_SENSE       0x04
+#define SCF_DIR         0x18
+#define SCF_NO_DCHK     0x00
+#define SCF_DIN         0x08
+#define SCF_DOUT        0x10
+#define SCF_NO_XF       0x18
+#define SCF_POLL        0x40
+#define SCF_SG          0x80
+
+/* Error Codes for SCB_HaStat */
+#define HOST_SEL_TOUT   0x11
+#define HOST_DO_DU      0x12
+#define HOST_BUS_FREE   0x13
+#define HOST_BAD_PHAS   0x14
+#define HOST_INV_CMD    0x16
+#define HOST_SCSI_RST   0x1B
+#define HOST_DEV_RST    0x1C
+
+/* Error Codes for SCB_TaStat */
+#define TARGET_CHKCOND  0x02
+#define TARGET_BUSY     0x08
+
+/* Queue tag msg: Simple_quque_tag, Head_of_queue_tag, Ordered_queue_tag */
+#define MSG_STAG        0x20
+#define MSG_HTAG        0x21
+#define MSG_OTAG        0x22
+
+/***********************************************************************
+		Target Device Control Structure
+**********************************************************************/
+
+typedef struct Tar_Ctrl_Struc {
+	ULONG TCS_InitioReserved;	/* 0 */
+
+	UWORD TCS_DrvFlags;	/* 4 */
+	UBYTE TCS_DrvHead;	/* 6 */
+	UBYTE TCS_DrvSector;	/* 7 */
+} TCS;
+
+/***********************************************************************
+		Target Device Control Structure
+**********************************************************************/
+/* Bit Definition for TCF_DrvFlags */
+#define TCF_DRV_255_63          0x0400
+
+/***********************************************************************
+	      Host Adapter Control Structure
+************************************************************************/
+typedef struct Ha_Ctrl_Struc {
+	UWORD HCS_Base;		/* 00 */
+	UWORD HCS_BIOS;		/* 02 */
+	UBYTE HCS_Intr;		/* 04 */
+	UBYTE HCS_SCSI_ID;	/* 05 */
+	UBYTE HCS_MaxTar;	/* 06 */
+	UBYTE HCS_NumScbs;	/* 07 */
+
+	UBYTE HCS_Flags;	/* 08 */
+	UBYTE HCS_Index;	/* 09 */
+	UBYTE HCS_Reserved[2];	/* 0a */
+	ULONG HCS_InitioReserved[27];	/* 0C */
+	TCS HCS_Tcs[16];	/* 78 -> 16 Targets */
+	Scsi_Cmnd *pSRB_head;	/* SRB save queue header     */
+	Scsi_Cmnd *pSRB_tail;	/* SRB save queue tail       */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spinlock_t HCS_AvailLock;
+	spinlock_t HCS_SemaphLock;
+	spinlock_t pSRB_lock;
+#endif
+} HCS;
+
+/* Bit Definition for HCB_Flags */
+#define HCF_EXPECT_RESET        0x10
+
+/* SCSI related definition                                              */
+#define DISC_NOT_ALLOW          0x80	/* Disconnect is not allowed    */
+#define DISC_ALLOW              0xC0	/* Disconnect is allowed        */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/inia100.c linux/drivers/scsi/inia100.c
--- linux.vanilla/drivers/scsi/inia100.c	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/inia100.c	Mon Mar  1 21:53:35 1999
@@ -0,0 +1,953 @@
+/**************************************************************************
+ * Initio A100 device driver for Linux.
+ *
+ * Copyright (c) 1994-1998 Initio Corporation
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ **************************************************************************
+ * 
+ * module: inia100.c
+ * DESCRIPTION:
+ * 	This is the Linux low-level SCSI driver for Initio INIA100 SCSI host
+ * 	adapters
+ * 09/24/98 hl - v1.02 initial production release.
+ * 12/19/98 bv - v1.02a Use spinlocks for 2.1.95 and up.
+ **************************************************************************/
+
+#define CVT_LINUX_VERSION(V,P,S)        (V * 65536 + P * 256 + S)
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+#include <stdarg.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#if LINUX_VERSION_CODE <= CVT_LINUX_VERSION(2,1,92)
+#include <linux/bios32.h>
+#endif
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,23)
+#include <linux/init.h>
+#endif
+#include <linux/blk.h>
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+#include <asm/spinlock.h>
+#endif
+#include "sd.h"
+#include "scsi.h"
+#include "hosts.h"
+#include "inia100.h"
+#include <linux/stat.h>
+#include <linux/malloc.h>
+#include <linux/config.h>
+
+
+#else
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include "../block/blk.h"
+#include "scsi.h"
+#include "sd.h"
+#include "hosts.h"
+#include <linux/malloc.h>
+#include "inia100.h"
+#endif
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = INIA100;
+#include "scsi_module.c"
+#endif
+
+#define ORC_RDWORD(x,y)         (short)(inl((int)((ULONG)((ULONG)x+(UCHAR)y)) ))
+
+char *inia100_Copyright = "Copyright (C) 1998-99";
+char *inia100_InitioName = "by Initio Corporation";
+char *inia100_ProductName = "INI-A100U2W";
+char *inia100_Version = "v1.02a";
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+struct proc_dir_entry proc_scsi_inia100 =
+{
+	PROC_SCSI_INIA100, 7, "INIA100",
+	S_IFDIR | S_IRUGO | S_IXUGO, 2,
+	0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+#endif
+
+/* set by inia100_setup according to the command line */
+static int setup_called = 0;
+static int orc_num_ch = MAX_SUPPORTED_ADAPTERS;		/* Maximum 4 adapters           */
+
+/* ---- INTERNAL VARIABLES ---- */
+#define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
+static char *setup_str = (char *) NULL;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void inia100_intr0(int irq, void *dev_id, struct pt_regs *);
+static void inia100_intr1(int irq, void *dev_id, struct pt_regs *);
+static void inia100_intr2(int irq, void *dev_id, struct pt_regs *);
+static void inia100_intr3(int irq, void *dev_id, struct pt_regs *);
+static void inia100_intr4(int irq, void *dev_id, struct pt_regs *);
+static void inia100_intr5(int irq, void *dev_id, struct pt_regs *);
+static void inia100_intr6(int irq, void *dev_id, struct pt_regs *);
+static void inia100_intr7(int irq, void *dev_id, struct pt_regs *);
+#else
+static void inia100_intr0(int irq, struct pt_regs *);
+static void inia100_intr1(int irq, struct pt_regs *);
+static void inia100_intr2(int irq, struct pt_regs *);
+static void inia100_intr3(int irq, struct pt_regs *);
+static void inia100_intr4(int irq, struct pt_regs *);
+static void inia100_intr5(int irq, struct pt_regs *);
+static void inia100_intr6(int irq, struct pt_regs *);
+static void inia100_intr7(int irq, struct pt_regs *);
+#endif
+
+static void inia100_panic(char *msg);
+void inia100SCBPost(BYTE * pHcb, BYTE * pScb);
+
+/* ---- EXTERNAL VARIABLES ---- */
+extern int Addinia100_into_Adapter_table(WORD, WORD, BYTE, BYTE, BYTE);
+extern void init_inia100Adapter_table(void);
+extern ORC_SCB *orc_alloc_scb(ORC_HCS * hcsp);
+extern void orc_exec_scb(ORC_HCS * hcsp, ORC_SCB * scbp);
+extern void orc_release_scb(ORC_HCS * hcsp, ORC_SCB * scbp);
+extern void orc_interrupt(ORC_HCS * hcsp);
+extern int orc_device_reset(ORC_HCS * pHCB, ULONG SCpnt, unsigned int target, unsigned int ResetFlags);
+extern int orc_reset_scsi_bus(ORC_HCS * pHCB);
+extern int abort_SCB(ORC_HCS * hcsp, ORC_SCB * pScb);
+extern int orc_abort_srb(ORC_HCS * hcsp, ULONG SCpnt);
+extern void get_orcPCIConfig(ORC_HCS * pCurHcb, int ch_idx);
+extern int init_orchid(ORC_HCS * hcsp);
+
+extern int orc_num_scb;
+extern ORC_HCS orc_hcs[];
+
+/*****************************************************************************
+ Function name  : inia100AppendSRBToQueue
+ Description    : This function will push current request into save list
+ Input          : pSRB  -       Pointer to SCSI request block.
+		  pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : None.
+*****************************************************************************/
+static void inia100AppendSRBToQueue(ORC_HCS * pHCB, Scsi_Cmnd * pSRB)
+{
+	ULONG flags;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_lock_irqsave(&(pHCB->pSRB_lock), flags);
+#else
+	save_flags(flags);
+	cli();
+#endif
+
+	pSRB->next = NULL;	/* Pointer to next */
+	if (pHCB->pSRB_head == NULL)
+		pHCB->pSRB_head = pSRB;
+	else
+		pHCB->pSRB_tail->next = pSRB;	/* Pointer to next */
+	pHCB->pSRB_tail = pSRB;
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_unlock_irqrestore(&(pHCB->pSRB_lock), flags);
+#else
+	restore_flags(flags);
+#endif
+	return;
+}
+
+/*****************************************************************************
+ Function name  : inia100PopSRBFromQueue
+ Description    : This function will pop current request from save list
+ Input          : pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : pSRB  -       Pointer to SCSI request block.
+*****************************************************************************/
+static Scsi_Cmnd *inia100PopSRBFromQueue(ORC_HCS * pHCB)
+{
+	Scsi_Cmnd *pSRB;
+	ULONG flags;
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_lock_irqsave(&(pHCB->pSRB_lock), flags);
+#else
+	save_flags(flags);
+	cli();
+#endif
+
+	if ((pSRB = (Scsi_Cmnd *) pHCB->pSRB_head) != NULL) {
+		pHCB->pSRB_head = pHCB->pSRB_head->next;
+		pSRB->next = NULL;
+	}
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_unlock_irqrestore(&(pHCB->pSRB_lock), flags);
+#else
+	restore_flags(flags);
+#endif
+	return (pSRB);
+}
+
+/*****************************************************************************
+ Function name  : inia100_setup
+ Description    : 
+ Input          : pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : pSRB  -       Pointer to SCSI request block.
+*****************************************************************************/
+void inia100_setup(char *str, int *ints)
+{
+	if (setup_called)
+		inia100_panic("inia100: inia100_setup called twice.\n");
+
+	setup_called = ints[0];
+	setup_str = str;
+}
+
+/*****************************************************************************
+ Function name	: orc_ReturnNumberOfAdapters
+ Description	: This function will scan PCI bus to get all Orchid card
+ Input		: None.
+ Output		: None.
+ Return		: SUCCESSFUL	- Successful scan
+		  ohterwise	- No drives founded
+*****************************************************************************/
+int orc_ReturnNumberOfAdapters(void)
+{
+	unsigned int i, iAdapters;
+
+	iAdapters = 0;
+	/*
+	 * PCI-bus probe.
+	 */
+	if (pcibios_present()) {
+		struct {
+			unsigned short vendor_id;
+			unsigned short device_id;
+		} const inia100_pci_devices[] =
+		{
+			{ORC_VENDOR_ID, I920_DEVICE_ID},
+			{ORC_VENDOR_ID, ORC_DEVICE_ID}
+		};
+
+		unsigned int dRegValue;
+		unsigned short command;
+		WORD wBIOS, wBASE;
+		BYTE bPCIBusNum, bInterrupt, bPCIDeviceNum;
+
+#ifdef MMAPIO
+		unsigned long page_offset, base;
+#endif
+
+#if LINUX_VERSION_CODE > CVT_LINUX_VERSION(2,1,92)
+		struct pci_dev *pdev = NULL;
+#else
+		int index;
+		unsigned char pci_bus, pci_devfn;
+#endif
+
+		bPCIBusNum = 0;
+		bPCIDeviceNum = 0;
+		init_inia100Adapter_table();
+		for (i = 0; i < NUMBER(inia100_pci_devices); i++) {
+#if LINUX_VERSION_CODE > CVT_LINUX_VERSION(2,1,92)
+			pdev = NULL;
+			while ((pdev = pci_find_device(inia100_pci_devices[i].vendor_id,
+					inia100_pci_devices[i].device_id,
+						       pdev)))
+#else
+			index = 0;
+			while (!(pcibios_find_device(inia100_pci_devices[i].vendor_id,
+					inia100_pci_devices[i].device_id,
+					 index++, &pci_bus, &pci_devfn)))
+#endif
+			{
+				if (iAdapters >= MAX_SUPPORTED_ADAPTERS)
+					break;	/* Never greater than maximum   */
+
+				if (i == 0) {
+					/*
+					   printk("inia100: The RAID controller is not supported by\n");
+					   printk("inia100:         this driver, we are ignoring it.\n");
+					 */
+				} else {
+					/*
+					 * Read sundry information from PCI BIOS.
+					 */
+#if LINUX_VERSION_CODE > CVT_LINUX_VERSION(2,1,92)
+					bPCIBusNum = pdev->bus->number;
+					bPCIDeviceNum = pdev->devfn;
+					dRegValue = pdev->base_address[0];
+					if (dRegValue == -1) {	/* Check return code            */
+						printk("\n\rinia100: orchid read configuration error.\n");
+						return (0);	/* Read configuration space error  */
+					}
+					/* <02> read from base address + 0x50 offset to get the wBIOS balue. */
+					wBASE = (WORD) dRegValue;
+
+					/* Now read the interrupt line  */
+					dRegValue = pdev->irq;
+					bInterrupt = dRegValue & 0xFF;	/* Assign interrupt line      */
+					pci_read_config_word(pdev, PCI_COMMAND, &command);
+					pci_write_config_word(pdev, PCI_COMMAND,
+							      command | PCI_COMMAND_MASTER | PCI_COMMAND_IO);
+
+#else
+					bPCIBusNum = pci_bus;
+					bPCIDeviceNum = pci_devfn;
+					pcibios_read_config_dword(pci_bus, pci_devfn, PCI_BASE_ADDRESS_0,
+							     &dRegValue);
+					if (dRegValue == -1) {	/* Check return code            */
+						printk("\n\rinia100: Orchid read configuration error.\n");
+						return (0);	/* Read configuration space error  */
+					}
+					/* <02> read from base address + 0x50 offset to get the wBIOS balue. */
+					wBASE = (WORD) dRegValue;
+
+					/* Now read the interrupt line  */
+					pcibios_read_config_dword(pci_bus, pci_devfn, PCI_INTERRUPT_LINE,
+							     &dRegValue);
+					bInterrupt = dRegValue & 0xFF;	/* Assign interrupt line      */
+					pcibios_read_config_word(pci_bus, pci_devfn, PCI_COMMAND, &command);
+					pcibios_write_config_word(pci_bus, pci_devfn, PCI_COMMAND,
+								  command | PCI_COMMAND_MASTER | PCI_COMMAND_IO);
+#endif
+					wBASE &= PCI_BASE_ADDRESS_IO_MASK;
+					wBIOS = ORC_RDWORD(wBASE, 0x50);
+
+#ifdef MMAPIO
+					base = wBASE & PAGE_MASK;
+					page_offset = wBASE - base;
+
+					/*
+					 * replace the next line with this one if you are using 2.1.x:
+					 * temp_p->maddr = ioremap(base, page_offset + 256);
+					 */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,0)
+					wBASE = ioremap(base, page_offset + 256);
+#else
+					wBASE = (WORD) vremap(base, page_offset + 256);
+#endif
+					if (wBASE) {
+						wBASE += page_offset;
+					}
+#endif
+
+					if (Addinia100_into_Adapter_table(wBIOS, wBASE, bInterrupt, bPCIBusNum,
+					    bPCIDeviceNum) == SUCCESSFUL)
+						iAdapters++;
+				}
+			}	/* while(pdev=....) */
+		}		/* for PCI_DEVICES */
+	}			/* PCI BIOS present */
+	return (iAdapters);
+}
+
+/*****************************************************************************
+ Function name  : inia100_detect
+ Description    : 
+ Input          : pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : pSRB  -       Pointer to SCSI request block.
+*****************************************************************************/
+int inia100_detect(Scsi_Host_Template * tpnt)
+{
+	ORC_HCS *pHCB;
+	struct Scsi_Host *hreg;
+	U32 sz;
+	U32 i;			/* 01/14/98                     */
+	int ok = 0, iAdapters;
+	ULONG dBiosAdr;
+	BYTE *pbBiosAdr;
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+	tpnt->proc_dir = &proc_scsi_inia100;
+#endif
+	if (setup_called) {
+		/* Setup by inia100_setup          */
+		printk("inia100: processing commandline: ");
+	}
+	/* Get total number of adapters in the motherboard */
+	iAdapters = orc_ReturnNumberOfAdapters();
+
+	/* printk("inia100: Total Initio Adapters = %d\n", iAdapters); */
+	if (iAdapters == 0)	/* If no orc founded, return */
+		return (0);
+
+	orc_num_ch = (iAdapters > orc_num_ch) ? orc_num_ch : iAdapters;
+	orc_num_scb = ORC_MAXQUEUE;
+
+	/* clear the memory needed for HCS */
+	i = orc_num_ch * sizeof(ORC_HCS);
+	memset((unsigned char *) &orc_hcs[0], 0, i);	/* Initialize orc_hcs 0   */
+
+#if 0
+	printk("orc_num_scb= %x orc_num_ch= %x hcsize= %x scbsize= %x escbsize= %x\n",
+	       orc_num_scb, orc_num_ch, sizeof(ORC_HCS), sizeof(ORC_SCB), sizeof(ESCB));
+#endif
+
+	for (i = 0, pHCB = &orc_hcs[0];		/* Get pointer for control block */
+	     i < orc_num_ch;
+	     i++, pHCB++) {
+
+		pHCB->pSRB_head = NULL;		/* Initial SRB save queue       */
+		pHCB->pSRB_tail = NULL;		/* Initial SRB save queue       */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+		pHCB->pSRB_lock = SPIN_LOCK_UNLOCKED; /* SRB save queue lock */
+#endif
+		/* Get total memory needed for SCB */
+		sz = orc_num_scb * sizeof(ORC_SCB);
+		if ((pHCB->HCS_virScbArray = (PVOID) kmalloc(sz, GFP_ATOMIC | GFP_DMA)) == NULL) {
+			printk("inia100: SCB memory allocation error\n");
+			return (0);
+		}
+		memset((unsigned char *) pHCB->HCS_virScbArray, 0, sz);
+		pHCB->HCS_physScbArray = (U32) VIRT_TO_BUS(pHCB->HCS_virScbArray);
+
+		/* Get total memory needed for ESCB */
+		sz = orc_num_scb * sizeof(ESCB);
+		if ((pHCB->HCS_virEscbArray = (PVOID) kmalloc(sz, GFP_ATOMIC | GFP_DMA)) == NULL) {
+			printk("inia100: ESCB memory allocation error\n");
+			return (0);
+		}
+		memset((unsigned char *) pHCB->HCS_virEscbArray, 0, sz);
+		pHCB->HCS_physEscbArray = (U32) VIRT_TO_BUS(pHCB->HCS_virEscbArray);
+
+		request_region(pHCB->HCS_Base, 0x100, "inia100");	/* Register */
+		get_orcPCIConfig(pHCB, i);
+
+		dBiosAdr = pHCB->HCS_BIOS;
+		dBiosAdr = (dBiosAdr << 4);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+		pbBiosAdr = phys_to_virt(dBiosAdr);
+#endif
+
+		if (init_orchid(pHCB)) {	/* Initial orchid chip    */
+			printk("inia100: initial orchid fail!!\n");
+			return (0);
+		}
+		hreg = scsi_register(tpnt, sizeof(ORC_HCS));
+		if (hreg == NULL) {
+			printk("Invalid scsi_register pointer.\n");
+		}
+		hreg->io_port = pHCB->HCS_Base;
+		hreg->n_io_port = 0xff;
+		hreg->can_queue = orc_num_scb;	/* 03/05/98                   */
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+		hreg->unique_id = pHCB->HCS_Base;
+		hreg->max_id = pHCB->HCS_MaxTar;
+#endif
+
+		hreg->max_lun = 32;	/* 10/21/97                     */
+/*
+   hreg->max_lun = 8;
+   hreg->max_channel = 1;
+ */
+		hreg->irq = pHCB->HCS_Intr;
+		hreg->this_id = pHCB->HCS_SCSI_ID;	/* Assign HCS index           */
+		hreg->base = (UCHAR *) pHCB;
+
+#if 1
+		hreg->sg_tablesize = TOTAL_SG_ENTRY;	/* Maximun support is 32 */
+#else
+		hreg->sg_tablesize = SG_NONE;	/* No SG                        */
+#endif
+
+		/* Initial orc chip           */
+		switch (i) {
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+		case 0:
+			ok = request_irq(pHCB->HCS_Intr, inia100_intr0, SA_INTERRUPT | SA_SHIRQ, "inia100", NULL);
+			break;
+		case 1:
+			ok = request_irq(pHCB->HCS_Intr, inia100_intr1, SA_INTERRUPT | SA_SHIRQ, "inia100", NULL);
+			break;
+		case 2:
+			ok = request_irq(pHCB->HCS_Intr, inia100_intr2, SA_INTERRUPT | SA_SHIRQ, "inia100", NULL);
+			break;
+		case 3:
+			ok = request_irq(pHCB->HCS_Intr, inia100_intr3, SA_INTERRUPT | SA_SHIRQ, "inia100", NULL);
+			break;
+		case 4:
+			ok = request_irq(pHCB->HCS_Intr, inia100_intr4, SA_INTERRUPT | SA_SHIRQ, "inia100", NULL);
+			break;
+		case 5:
+			ok = request_irq(pHCB->HCS_Intr, inia100_intr5, SA_INTERRUPT | SA_SHIRQ, "inia100", NULL);
+			break;
+		case 6:
+			ok = request_irq(pHCB->HCS_Intr, inia100_intr6, SA_INTERRUPT | SA_SHIRQ, "inia100", NULL);
+			break;
+		case 7:
+			ok = request_irq(pHCB->HCS_Intr, inia100_intr7, SA_INTERRUPT | SA_SHIRQ, "inia100", NULL);
+			break;
+		default:
+			inia100_panic("inia100: Too many host adapters\n");
+			break;
+		}
+
+		if (ok < 0) {
+			if (ok == -EINVAL) {
+				printk("inia100: bad IRQ %d.\n", pHCB->HCS_Intr);
+				printk("         Contact author.\n");
+			} else {
+				if (ok == -EBUSY)
+					printk("inia100: IRQ %d already in use. Configure another.\n", pHCB->HCS_Intr);
+				else {
+					printk("\ninia100: Unexpected error code on requesting IRQ %d.\n",
+					       pHCB->HCS_Intr);
+					printk("         Contact author.\n");
+				}
+			}
+			inia100_panic("inia100: driver needs an IRQ.\n");
+		}
+#endif
+	}
+
+	tpnt->this_id = -1;
+	tpnt->can_queue = 1;
+	return 1;
+}
+
+/*****************************************************************************
+ Function name  : inia100BuildSCB
+ Description    : 
+ Input          : pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : pSRB  -       Pointer to SCSI request block.
+*****************************************************************************/
+static void inia100BuildSCB(ORC_HCS * pHCB, ORC_SCB * pSCB, Scsi_Cmnd * SCpnt)
+{				/* Create corresponding SCB     */
+	struct scatterlist *pSrbSG;
+	ORC_SG *pSG;		/* Pointer to SG list           */
+	int i;
+	U32 TotalLen;
+	ESCB *pEScb;
+
+	pEScb = pSCB->SCB_EScb;
+	pEScb->SCB_Srb = SCpnt;
+	pSG = NULL;
+
+	pSCB->SCB_Opcode = ORC_EXECSCSI;
+	pSCB->SCB_Flags = SCF_NO_DCHK;	/* Clear done bit               */
+	pSCB->SCB_Target = SCpnt->target;
+	pSCB->SCB_Lun = SCpnt->lun;
+	pSCB->SCB_Reserved0 = 0;
+	pSCB->SCB_Reserved1 = 0;
+	pSCB->SCB_SGLen = 0;
+
+	if ((pSCB->SCB_XferLen = (U32) SCpnt->request_bufflen)) {
+		pSG = (ORC_SG *) & pEScb->ESCB_SGList[0];
+		if (SCpnt->use_sg) {
+			TotalLen = 0;
+			pSCB->SCB_SGLen = (U32) (SCpnt->use_sg * 8);
+			pSrbSG = (struct scatterlist *) SCpnt->request_buffer;
+			for (i = 0; i < SCpnt->use_sg; i++, pSG++, pSrbSG++) {
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+				pSG->SG_Ptr = (U32) (VIRT_TO_BUS(pSrbSG->address));
+#else
+				pSG->SG_Ptr = (U32) pSrbSG->address;
+#endif
+				pSG->SG_Len = (U32) pSrbSG->length;
+				TotalLen += (U32) pSrbSG->length;
+			}
+		} else {	/* Non SG                       */
+			pSCB->SCB_SGLen = 0x8;
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+			pSG->SG_Ptr = (U32) (VIRT_TO_BUS(SCpnt->request_buffer));
+#else
+			pSG->SG_PTR = (U32) SCpnt->request_buffer;
+#endif
+			pSG->SG_Len = (U32) SCpnt->request_bufflen;
+		}
+	}
+	pSCB->SCB_SGPAddr = (U32) pSCB->SCB_SensePAddr;
+	pSCB->SCB_HaStat = 0;
+	pSCB->SCB_TaStat = 0;
+	pSCB->SCB_Link = 0xFF;
+	pSCB->SCB_SenseLen = SENSE_SIZE;
+	pSCB->SCB_CDBLen = SCpnt->cmd_len;
+	if (pSCB->SCB_CDBLen >= IMAX_CDB) {
+		printk("max cdb length= %x\b", SCpnt->cmd_len);
+		pSCB->SCB_CDBLen = IMAX_CDB;
+	}
+	pSCB->SCB_Ident = SCpnt->lun | DISC_ALLOW;
+	if (SCpnt->device->tagged_supported) {	/* Tag Support                  */
+		pSCB->SCB_TagMsg = SIMPLE_QUEUE_TAG;	/* Do simple tag only   */
+	} else {
+		pSCB->SCB_TagMsg = 0;	/* No tag support               */
+	}
+	memcpy(&pSCB->SCB_CDB[0], &SCpnt->cmnd, pSCB->SCB_CDBLen);
+	return;
+}
+
+/*****************************************************************************
+ Function name  : inia100_queue
+ Description    : Queue a command and setup interrupts for a free bus.
+ Input          : pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : pSRB  -       Pointer to SCSI request block.
+*****************************************************************************/
+int inia100_queue(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
+{
+	register ORC_SCB *pSCB;
+	ORC_HCS *pHCB;		/* Point to Host adapter control block */
+
+	if (SCpnt->lun > 16) {
+		SCpnt->result = (DID_TIME_OUT << 16);
+		done(SCpnt);	/* Notify system DONE           */
+		return (0);
+	}
+	pHCB = (ORC_HCS *) SCpnt->host->base;
+	SCpnt->scsi_done = done;
+	/* Get free SCSI control block  */
+	if ((pSCB = orc_alloc_scb(pHCB)) == NULL) {
+		inia100AppendSRBToQueue(pHCB, SCpnt);	/* Buffer this request  */
+		/* printk("inia100_entry: can't allocate SCB\n"); */
+		return (0);
+	}
+	inia100BuildSCB(pHCB, pSCB, SCpnt);
+	orc_exec_scb(pHCB, pSCB);	/* Start execute SCB            */
+
+	return (0);
+}
+
+/*****************************************************************************
+ Function name  : inia100_command
+ Description    : We only support command in interrupt-driven fashion
+ Input          : pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : pSRB  -       Pointer to SCSI request block.
+*****************************************************************************/
+int inia100_command(Scsi_Cmnd * SCpnt)
+{
+	printk("inia100: interrupt driven driver; use inia100_queue()\n");
+	return -1;
+}
+
+/*****************************************************************************
+ Function name  : inia100_abort
+ Description    : Abort a queued command.
+	                 (commands that are on the bus can't be aborted easily)
+ Input          : pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : pSRB  -       Pointer to SCSI request block.
+*****************************************************************************/
+int inia100_abort(Scsi_Cmnd * SCpnt)
+{
+	ORC_HCS *hcsp;
+
+	hcsp = (ORC_HCS *) SCpnt->host->base;
+	return orc_abort_srb(hcsp, (ULONG) SCpnt);
+}
+
+/*****************************************************************************
+ Function name  : inia100_reset
+ Description    : Reset registers, reset a hanging bus and
+                  kill active and disconnected commands for target w/o soft reset
+ Input          : pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : pSRB  -       Pointer to SCSI request block.
+*****************************************************************************/
+int inia100_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags)
+{				/* I need Host Control Block Information */
+	ORC_HCS *pHCB;
+	pHCB = (ORC_HCS *) SCpnt->host->base;
+
+	if (reset_flags & (SCSI_RESET_SUGGEST_BUS_RESET | SCSI_RESET_SUGGEST_HOST_RESET))
+		return orc_reset_scsi_bus(pHCB);
+	else
+		return orc_device_reset(pHCB, (ULONG) SCpnt, SCpnt->target, reset_flags);
+
+}
+
+/*****************************************************************************
+ Function name  : inia100SCBPost
+ Description    : This is callback routine be called when orc finish one
+			SCSI command.
+ Input          : pHCB  -       Pointer to host adapter control block.
+		  pSCB  -       Pointer to SCSI control block.
+ Output         : None.
+ Return         : None.
+*****************************************************************************/
+void inia100SCBPost(BYTE * pHcb, BYTE * pScb)
+{
+	Scsi_Cmnd *pSRB;	/* Pointer to SCSI request block */
+	ORC_HCS *pHCB;
+	ORC_SCB *pSCB;
+	ESCB *pEScb;
+
+	pHCB = (ORC_HCS *) pHcb;
+	pSCB = (ORC_SCB *) pScb;
+	pEScb = pSCB->SCB_EScb;
+	if ((pSRB = (Scsi_Cmnd *) pEScb->SCB_Srb) == 0) {
+		printk("inia100SCBPost: SRB pointer is empty\n");
+		orc_release_scb(pHCB, pSCB);	/* Release SCB for current channel */
+		return;
+	}
+	pEScb->SCB_Srb = NULL;
+
+	switch (pSCB->SCB_HaStat) {
+	case 0x0:
+	case 0xa:		/* Linked command complete without error and linked normally */
+	case 0xb:		/* Linked command complete without error interrupt generated */
+		pSCB->SCB_HaStat = 0;
+		break;
+
+	case 0x11:		/* Selection time out-The initiator selection or target
+				   reselection was not complete within the SCSI Time out period */
+		pSCB->SCB_HaStat = DID_TIME_OUT;
+		break;
+
+	case 0x14:		/* Target bus phase sequence failure-An invalid bus phase or bus
+				   phase sequence was requested by the target. The host adapter
+				   will generate a SCSI Reset Condition, notifying the host with
+				   a SCRD interrupt */
+		pSCB->SCB_HaStat = DID_RESET;
+		break;
+
+	case 0x1a:		/* SCB Aborted. 07/21/98 */
+		pSCB->SCB_HaStat = DID_ABORT;
+		break;
+
+	case 0x12:		/* Data overrun/underrun-The target attempted to transfer more data
+				   than was allocated by the Data Length field or the sum of the
+				   Scatter / Gather Data Length fields. */
+	case 0x13:		/* Unexpected bus free-The target dropped the SCSI BSY at an unexpected time. */
+	case 0x16:		/* Invalid CCB Operation Code-The first byte of the CCB was invalid. */
+
+	default:
+		printk("inia100: %x %x\n", pSCB->SCB_HaStat, pSCB->SCB_TaStat);
+		pSCB->SCB_HaStat = DID_ERROR;	/* Couldn't find any better */
+		break;
+	}
+
+	if (pSCB->SCB_TaStat == 2) {	/* Check condition              */
+		memcpy((unsigned char *) &pSRB->sense_buffer[0],
+		   (unsigned char *) &pEScb->ESCB_SGList[0], SENSE_SIZE);
+	}
+	pSRB->result = pSCB->SCB_TaStat | (pSCB->SCB_HaStat << 16);
+	pSRB->scsi_done(pSRB);	/* Notify system DONE           */
+
+	/* Find the next pending SRB    */
+	if ((pSRB = inia100PopSRBFromQueue(pHCB)) != NULL) {	/* Assume resend will success   */
+		/* Reuse old SCB                */
+		inia100BuildSCB(pHCB, pSCB, pSRB);	/* Create corresponding SCB     */
+		orc_exec_scb(pHCB, pSCB);	/* Start execute SCB            */
+	} else {		/* No Pending SRB               */
+		orc_release_scb(pHCB, pSCB);	/* Release SCB for current channel */
+	}
+	return;
+}
+
+/*****************************************************************************
+ Function name  : inia100_biosparam
+ Description    : Return the "logical geometry"
+ Input          : pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : pSRB  -       Pointer to SCSI request block.
+*****************************************************************************/
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+int inia100_biosparam(Scsi_Disk * disk, kdev_t dev, int *info_array)
+#else
+int inia100_biosparam(Scsi_Disk * disk, int dev, int *info_array)
+#endif
+{
+	ORC_HCS *pHcb;		/* Point to Host adapter control block */
+	ORC_TCS *pTcb;
+
+	pHcb = (ORC_HCS *) disk->device->host->base;
+	pTcb = &pHcb->HCS_Tcs[disk->device->id];
+
+	if (pTcb->TCS_DrvHead) {
+		info_array[0] = pTcb->TCS_DrvHead;
+		info_array[1] = pTcb->TCS_DrvSector;
+		info_array[2] = disk->capacity / pTcb->TCS_DrvHead / pTcb->TCS_DrvSector;
+	} else {
+		if (pTcb->TCS_DrvFlags & TCF_DRV_255_63) {
+			info_array[0] = 255;
+			info_array[1] = 63;
+			info_array[2] = disk->capacity / 255 / 63;
+		} else {
+			info_array[0] = 64;
+			info_array[1] = 32;
+			info_array[2] = disk->capacity >> 11;
+		}
+	}
+	return 0;
+}
+
+
+static void subIntr(ORC_HCS * pHCB, int irqno)
+{
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	unsigned long flags;
+
+	spin_lock_irqsave(&io_request_lock, flags);
+#endif
+
+	if (pHCB->HCS_Intr != irqno) {
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+		spin_unlock_irqrestore(&io_request_lock, flags);
+#endif
+		return;
+	}
+	orc_interrupt(pHCB);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spin_unlock_irqrestore(&io_request_lock, flags);
+#endif
+}
+
+/*
+ * Interrupts handler (main routine of the driver)
+ */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void inia100_intr0(int irqno, void *dev_id, struct pt_regs *regs)
+#else
+static void inia100_intr0(int irqno, struct pt_regs *regs)
+#endif
+{
+	subIntr(&orc_hcs[0], irqno);
+}
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void inia100_intr1(int irqno, void *dev_id, struct pt_regs *regs)
+#else
+static void inia100_intr1(int irqno, struct pt_regs *regs)
+#endif
+{
+	subIntr(&orc_hcs[1], irqno);
+}
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void inia100_intr2(int irqno, void *dev_id, struct pt_regs *regs)
+#else
+static void inia100_intr2(int irqno, struct pt_regs *regs)
+#endif
+{
+	subIntr(&orc_hcs[2], irqno);
+}
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void inia100_intr3(int irqno, void *dev_id, struct pt_regs *regs)
+#else
+static void inia100_intr3(int irqno, struct pt_regs *regs)
+#endif
+{
+	subIntr(&orc_hcs[3], irqno);
+}
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void inia100_intr4(int irqno, void *dev_id, struct pt_regs *regs)
+#else
+static void inia100_intr4(int irqno, struct pt_regs *regs)
+#endif
+{
+	subIntr(&orc_hcs[4], irqno);
+}
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void inia100_intr5(int irqno, void *dev_id, struct pt_regs *regs)
+#else
+static void inia100_intr5(int irqno, struct pt_regs *regs)
+#endif
+{
+	subIntr(&orc_hcs[5], irqno);
+}
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void inia100_intr6(int irqno, void *dev_id, struct pt_regs *regs)
+#else
+static void inia100_intr6(int irqno, struct pt_regs *regs)
+#endif
+{
+	subIntr(&orc_hcs[6], irqno);
+}
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0)
+static void inia100_intr7(int irqno, void *dev_id, struct pt_regs *regs)
+#else
+static void inia100_intr7(int irqno, struct pt_regs *regs)
+#endif
+{
+	subIntr(&orc_hcs[7], irqno);
+}
+
+/* 
+ * Dump the current driver status and panic...
+ */
+static void inia100_panic(char *msg)
+{
+	printk("\ninia100_panic: %s\n", msg);
+	panic("inia100 panic");
+}
+
+/*#include "inia100scsi.c" */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/inia100.h linux/drivers/scsi/inia100.h
--- linux.vanilla/drivers/scsi/inia100.h	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/inia100.h	Fri Mar 26 17:14:47 1999
@@ -0,0 +1,503 @@
+/**************************************************************************
+ * Initio A100 device driver for Linux.
+ *
+ * Copyright (c) 1994-1998 Initio Corporation
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ **************************************************************************
+ *
+ * Module: inia100.h
+ * Description: INI-A100U2W LINUX device driver header
+ * Revision History:
+ *	06/18/98 HL, Initial production Version 1.02
+ *	12/19/98 bv, Use spinlocks for 2.1.95 and up
+ ****************************************************************************/
+
+#ifndef	CVT_LINUX_VERSION
+#define	CVT_LINUX_VERSION(V,P,S)	(((V) * 65536) + ((P) * 256) + (S))
+#endif
+
+#ifndef	LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+
+#include "sd.h"
+
+extern int inia100_detect(Scsi_Host_Template *);
+extern int inia100_command(Scsi_Cmnd *);
+extern int inia100_queue(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
+extern int inia100_abort(Scsi_Cmnd *);
+extern int inia100_reset(Scsi_Cmnd *, unsigned int);
+
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1, 3, 0)
+extern int inia100_biosparam(Scsi_Disk *, kdev_t, int *);	/*for linux v2.0 */
+extern struct proc_dir_entry proc_scsi_inia100;
+#else
+extern int inia100_biosparam(Disk *, int, int *);	/*for linux v1.13 */
+#endif
+
+#define inia100_REVID "Initio INI-A100U2W SCSI device driver; Revision: 1.02a"
+
+#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(1, 3, 0)
+#define INIA100	{ \
+		NULL, \
+		NULL, \
+		inia100_REVID, \
+		inia100_detect, \
+		NULL, \
+		NULL, \
+		inia100_command, \
+		inia100_queue, \
+		inia100_abort, \
+		inia100_reset, \
+		NULL, \
+		inia100_biosparam, \
+		1, \
+7, \
+SG_ALL, \
+1, \
+0, \
+0, \
+ENABLE_CLUSTERING \
+}
+
+#else
+
+#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(2, 1, 75)
+#define INIA100	{ \
+		NULL, \
+		NULL, \
+		&proc_scsi_inia100, \
+		NULL, \
+		inia100_REVID, \
+		inia100_detect, \
+		NULL, \
+		NULL, \
+		inia100_command, \
+		inia100_queue, \
+		inia100_abort, \
+		inia100_reset, \
+		NULL, \
+		inia100_biosparam, \
+		1, \
+		7, \
+		0, \
+		1, \
+		0, \
+		0, \
+		ENABLE_CLUSTERING \
+}
+#else				/* Version >= 2.1.75 */
+#define INIA100	{ \
+	next:		NULL,						\
+	module:		NULL,						\
+	proc_dir:	&proc_scsi_inia100, \
+	proc_info:	NULL,				\
+	name:		inia100_REVID, \
+	detect:		inia100_detect, \
+	release:	NULL, \
+	info:		NULL,					\
+	command:	inia100_command, \
+	queuecommand:	inia100_queue, \
+ 	eh_strategy_handler: NULL, \
+ 	eh_abort_handler: NULL, \
+ 	eh_device_reset_handler: NULL, \
+ 	eh_bus_reset_handler: NULL, \
+ 	eh_host_reset_handler: NULL, \
+	abort:		inia100_abort, \
+	reset:		inia100_reset, \
+	slave_attach:	NULL, \
+	bios_param:	inia100_biosparam, \
+	can_queue:	1, \
+	this_id:	1, \
+	sg_tablesize:	SG_ALL, \
+	cmd_per_lun: 	1, \
+	present:	0, \
+	unchecked_isa_dma: 0, \
+	use_clustering:	ENABLE_CLUSTERING, \
+ use_new_eh_code: 0 \
+}
+#endif
+#endif
+
+#define VIRT_TO_BUS(i)  (unsigned int) virt_to_bus((void *)(i))
+#define ULONG   unsigned long
+#define PVOID   void *
+#define USHORT  unsigned short
+#define UCHAR   unsigned char
+#define BYTE    unsigned char
+#define WORD    unsigned short
+#define DWORD   unsigned long
+#define UBYTE   unsigned char
+#define UWORD   unsigned short
+#define UDWORD  unsigned long
+#ifdef ALPHA
+#define U32     unsigned int
+#else
+#define U32     unsigned long
+#endif
+
+#ifndef NULL
+#define NULL     0		/* zero          */
+#endif
+#ifndef TRUE
+#define TRUE     (1)		/* boolean true  */
+#endif
+#ifndef FALSE
+#define FALSE    (0)		/* boolean false */
+#endif
+#ifndef FAILURE
+#define FAILURE  (-1)
+#endif
+#if 1
+#define ORC_MAXQUEUE		245
+#else
+#define ORC_MAXQUEUE		25
+#endif
+
+#define TOTAL_SG_ENTRY		32
+#define MAX_TARGETS		16
+#define IMAX_CDB			15
+#define SENSE_SIZE		14
+#define MAX_SUPPORTED_ADAPTERS  4
+#define SUCCESSFUL              0x00
+
+#define I920_DEVICE_ID	0x0002	/* Initio's inic-950 product ID   */
+
+/************************************************************************/
+/*              Scatter-Gather Element Structure                        */
+/************************************************************************/
+typedef struct ORC_SG_Struc {
+	U32 SG_Ptr;		/* Data Pointer */
+	U32 SG_Len;		/* Data Length */
+} ORC_SG;
+
+
+/* SCSI related definition                                              */
+#define DISC_NOT_ALLOW          0x80	/* Disconnect is not allowed    */
+#define DISC_ALLOW              0xC0	/* Disconnect is allowed        */
+
+
+#define ORC_OFFSET_SCB			16
+#define ORC_MAX_SCBS		    250
+#define MAX_CHANNELS       2
+#define MAX_ESCB_ELE				64
+#define TCF_DRV_255_63     0x0400
+
+/********************************************************/
+/*      Orchid Configuration Register Set               */
+/********************************************************/
+#define ORC_PVID	0x00	/* Vendor ID                      */
+#define ORC_VENDOR_ID	0x1101	/* Orchid vendor ID               */
+#define ORC_PDID        0x02	/* Device ID                    */
+#define ORC_DEVICE_ID	0x1060	/* Orchid device ID               */
+#define ORC_COMMAND	0x04	/* Command                        */
+#define BUSMS		0x04	/* BUS MASTER Enable              */
+#define IOSPA		0x01	/* IO Space Enable                */
+#define ORC_STATUS	0x06	/* Status register                */
+#define ORC_REVISION	0x08	/* Revision number                */
+#define ORC_BASE	0x10	/* Base address                   */
+#define ORC_BIOS	0x50	/* Expansion ROM base address     */
+#define ORC_INT_NUM	0x3C	/* Interrupt line         */
+#define ORC_INT_PIN	0x3D	/* Interrupt pin          */
+
+/********************************************************/
+/*      Orchid Host Command Set                         */
+/********************************************************/
+#define ORC_CMD_NOP		0x00	/* Host command - NOP             */
+#define ORC_CMD_VERSION		0x01	/* Host command - Get F/W version */
+#define ORC_CMD_ECHO		0x02	/* Host command - ECHO            */
+#define ORC_CMD_SET_NVM		0x03	/* Host command - Set NVRAM       */
+#define ORC_CMD_GET_NVM		0x04	/* Host command - Get NVRAM       */
+#define ORC_CMD_GET_BUS_STATUS	0x05	/* Host command - Get SCSI bus status */
+#define ORC_CMD_ABORT_SCB	0x06	/* Host command - Abort SCB       */
+#define ORC_CMD_ISSUE_SCB	0x07	/* Host command - Issue SCB       */
+
+/********************************************************/
+/*              Orchid Register Set                     */
+/********************************************************/
+#define ORC_GINTS	0xA0	/* Global Interrupt Status        */
+#define QINT		0x04	/* Reply Queue Interrupt  */
+#define ORC_GIMSK	0xA1	/* Global Interrupt MASK  */
+#define MQINT		0x04	/* Mask Reply Queue Interrupt     */
+#define	ORC_GCFG	0xA2	/* Global Configure               */
+#define EEPRG		0x01	/* Enable EEPROM programming */
+#define	ORC_GSTAT	0xA3	/* Global status          */
+#define WIDEBUS		0x10	/* Wide SCSI Devices connected    */
+#define ORC_HDATA	0xA4	/* Host Data                      */
+#define ORC_HCTRL	0xA5	/* Host Control                   */
+#define SCSIRST		0x80	/* SCSI bus reset         */
+#define HDO			0x40	/* Host data out          */
+#define HOSTSTOP		0x02	/* Host stop RISC engine  */
+#define DEVRST		0x01	/* Device reset                   */
+#define ORC_HSTUS	0xA6	/* Host Status                    */
+#define HDI			0x02	/* Host data in                   */
+#define RREADY		0x01	/* RISC engine is ready to receive */
+#define	ORC_NVRAM	0xA7	/* Nvram port address             */
+#define SE2CS		0x008
+#define SE2CLK		0x004
+#define SE2DO		0x002
+#define SE2DI		0x001
+#define ORC_PQUEUE	0xA8	/* Posting queue FIFO             */
+#define ORC_PQCNT	0xA9	/* Posting queue FIFO Cnt */
+#define ORC_RQUEUE	0xAA	/* Reply queue FIFO               */
+#define ORC_RQUEUECNT	0xAB	/* Reply queue FIFO Cnt           */
+#define	ORC_FWBASEADR	0xAC	/* Firmware base address  */
+
+#define	ORC_EBIOSADR0 0xB0	/* External Bios address */
+#define	ORC_EBIOSADR1 0xB1	/* External Bios address */
+#define	ORC_EBIOSADR2 0xB2	/* External Bios address */
+#define	ORC_EBIOSDATA 0xB3	/* External Bios address */
+
+#define	ORC_SCBSIZE	0xB7	/* SCB size register              */
+#define	ORC_SCBBASE0	0xB8	/* SCB base address 0             */
+#define	ORC_SCBBASE1	0xBC	/* SCB base address 1             */
+
+#define	ORC_RISCCTL	0xE0	/* RISC Control                   */
+#define PRGMRST		0x002
+#define DOWNLOAD		0x001
+#define	ORC_PRGMCTR0	0xE2	/* RISC program counter           */
+#define	ORC_PRGMCTR1	0xE3	/* RISC program counter           */
+#define	ORC_RISCRAM	0xEC	/* RISC RAM data port 4 bytes     */
+
+typedef struct orc_extended_scb {	/* Extended SCB                 */
+	ORC_SG ESCB_SGList[TOTAL_SG_ENTRY];	/*0 Start of SG list              */
+	Scsi_Cmnd *SCB_Srb;	/*50 SRB Pointer */
+} ESCB;
+
+/***********************************************************************
+		SCSI Control Block
+************************************************************************/
+typedef struct orc_scb {	/* Scsi_Ctrl_Blk                */
+	UBYTE SCB_Opcode;	/*00 SCB command code&residual  */
+	UBYTE SCB_Flags;	/*01 SCB Flags                  */
+	UBYTE SCB_Target;	/*02 Target Id                  */
+	UBYTE SCB_Lun;		/*03 Lun                        */
+	U32 SCB_Reserved0;	/*04 Reserved for ORCHID must 0 */
+	U32 SCB_XferLen;	/*08 Data Transfer Length       */
+	U32 SCB_Reserved1;	/*0C Reserved for ORCHID must 0 */
+	U32 SCB_SGLen;		/*10 SG list # * 8              */
+	U32 SCB_SGPAddr;	/*14 SG List Buf physical Addr  */
+	U32 SCB_SGPAddrHigh;	/*18 SG Buffer high physical Addr */
+	UBYTE SCB_HaStat;	/*1C Host Status                */
+	UBYTE SCB_TaStat;	/*1D Target Status              */
+	UBYTE SCB_Status;	/*1E SCB status                 */
+	UBYTE SCB_Link;		/*1F Link pointer, default 0xFF */
+	UBYTE SCB_SenseLen;	/*20 Sense Allocation Length    */
+	UBYTE SCB_CDBLen;	/*21 CDB Length                 */
+	UBYTE SCB_Ident;	/*22 Identify                   */
+	UBYTE SCB_TagMsg;	/*23 Tag Message                */
+	UBYTE SCB_CDB[IMAX_CDB];	/*24 SCSI CDBs                  */
+	UBYTE SCB_ScbIdx;	/*3C Index for this ORCSCB      */
+	U32 SCB_SensePAddr;	/*34 Sense Buffer physical Addr */
+
+	ESCB *SCB_EScb;		/*38 Extended SCB Pointer       */
+#ifndef ALPHA
+	UBYTE SCB_Reserved2[4];	/*3E Reserved for Driver use    */
+#endif
+} ORC_SCB;
+
+/* Opcodes of ORCSCB_Opcode */
+#define ORC_EXECSCSI	0x00	/* SCSI initiator command with residual */
+#define ORC_BUSDEVRST	0x01	/* SCSI Bus Device Reset  */
+
+/* Status of ORCSCB_Status */
+#define SCB_COMPLETE	0x00	/* SCB request completed  */
+#define SCB_POST	0x01	/* SCB is posted by the HOST      */
+
+/* Bit Definition for ORCSCB_Flags */
+#define SCF_DISINT	0x01	/* Disable HOST interrupt */
+#define SCF_DIR		0x18	/* Direction bits         */
+#define SCF_NO_DCHK	0x00	/* Direction determined by SCSI   */
+#define SCF_DIN		0x08	/* From Target to Initiator       */
+#define SCF_DOUT	0x10	/* From Initiator to Target       */
+#define SCF_NO_XF	0x18	/* No data transfer               */
+#define SCF_POLL   0x40
+
+/* Error Codes for ORCSCB_HaStat */
+#define HOST_SEL_TOUT	0x11
+#define HOST_DO_DU	0x12
+#define HOST_BUS_FREE	0x13
+#define HOST_BAD_PHAS	0x14
+#define HOST_INV_CMD	0x16
+#define HOST_SCSI_RST	0x1B
+#define HOST_DEV_RST	0x1C
+
+
+/* Error Codes for ORCSCB_TaStat */
+#define TARGET_CHK_COND	0x02
+#define TARGET_BUSY	0x08
+#define TARGET_TAG_FULL	0x28
+
+
+/* Queue tag msg: Simple_quque_tag, Head_of_queue_tag, Ordered_queue_tag */
+#define MSG_STAG	0x20
+#define MSG_HTAG	0x21
+#define MSG_OTAG	0x22
+
+#define MSG_IGNOREWIDE	0x23
+
+#define MSG_IDENT	0x80
+#define MSG_DISC	0x40	/* Disconnect allowed             */
+
+
+/* SCSI MESSAGE */
+#define	MSG_EXTEND	0x01
+#define	MSG_SDP		0x02
+#define	MSG_ABORT	0x06
+#define	MSG_REJ		0x07
+#define	MSG_NOP		0x08
+#define	MSG_PARITY	0x09
+#define	MSG_DEVRST	0x0C
+#define	MSG_STAG	0x20
+
+/***********************************************************************
+		Target Device Control Structure
+**********************************************************************/
+
+typedef struct ORC_Tar_Ctrl_Struc {
+	UBYTE TCS_DrvDASD;	/* 6 */
+	UBYTE TCS_DrvSCSI;	/* 7 */
+	UBYTE TCS_DrvHead;	/* 8 */
+	UWORD TCS_DrvFlags;	/* 4 */
+	UBYTE TCS_DrvSector;	/* 7 */
+} ORC_TCS, *PORC_TCS;
+
+/* Bit Definition for TCF_DrvFlags */
+#define	TCS_DF_NODASD_SUPT	0x20	/* Suppress OS/2 DASD Mgr support */
+#define	TCS_DF_NOSCSI_SUPT	0x40	/* Suppress OS/2 SCSI Mgr support */
+
+
+/***********************************************************************
+              Host Adapter Control Structure
+************************************************************************/
+typedef struct ORC_Ha_Ctrl_Struc {
+	USHORT HCS_Base;	/* 00 */
+	UBYTE HCS_Index;	/* 02 */
+	UBYTE HCS_Intr;		/* 04 */
+	UBYTE HCS_SCSI_ID;	/* 06    H/A SCSI ID */
+	UBYTE HCS_BIOS;		/* 07    BIOS configuration */
+
+	UBYTE HCS_Flags;	/* 0B */
+	UBYTE HCS_HAConfig1;	/* 1B    SCSI0MAXTags */
+	UBYTE HCS_MaxTar;	/* 1B    SCSI0MAXTags */
+
+	USHORT HCS_Units;	/* Number of units this adapter  */
+	USHORT HCS_AFlags;	/* Adapter info. defined flags   */
+	ULONG HCS_Timeout;	/* Adapter timeout value   */
+	PVOID HCS_virScbArray;	/* 28 Virtual Pointer to SCB array     */
+	U32 HCS_physScbArray;	/* Scb Physical address */
+	PVOID HCS_virEscbArray;	/* Virtual pointer to ESCB Scatter list */
+	U32 HCS_physEscbArray;	/* scatter list Physical address */
+	UBYTE TargetFlag[16];	/* 30  target configuration, TCF_EN_TAG */
+	UBYTE MaximumTags[16];	/* 40  ORC_MAX_SCBS */
+	UBYTE ActiveTags[16][16];	/* 50 */
+	ORC_TCS HCS_Tcs[16];	/* 28 */
+	U32 BitAllocFlag[MAX_CHANNELS][8];	/* Max STB is 256, So 256/32 */
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spinlock_t BitAllocFlagLock;
+#endif
+	Scsi_Cmnd *pSRB_head;
+	Scsi_Cmnd *pSRB_tail;
+#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95)
+	spinlock_t pSRB_lock;
+#endif
+} ORC_HCS;
+
+/* Bit Definition for HCS_Flags */
+
+#define HCF_SCSI_RESET	0x01	/* SCSI BUS RESET         */
+#define HCF_PARITY    	0x02	/* parity card                    */
+#define HCF_LVDS     	0x10	/* parity card                    */
+
+/* Bit Definition for TargetFlag */
+
+#define TCF_EN_255	    0x08
+#define TCF_EN_TAG	    0x10
+#define TCF_BUSY	      0x20
+#define TCF_DISCONNECT	0x40
+#define TCF_SPIN_UP	  0x80
+
+/* Bit Definition for HCS_AFlags */
+#define	HCS_AF_IGNORE		0x01	/* Adapter ignore         */
+#define	HCS_AF_DISABLE_RESET	0x10	/* Adapter disable reset  */
+#define	HCS_AF_DISABLE_ADPT	0x80	/* Adapter disable                */
+
+
+/*---------------------------------------*/
+/* TimeOut for RESET to complete (30s)   */
+/*                                       */
+/* After a RESET the drive is checked    */
+/* every 200ms.                          */
+/*---------------------------------------*/
+#define DELAYED_RESET_MAX       (30*1000L)
+#define DELAYED_RESET_INTERVAL  200L
+
+/*----------------------------------------------*/
+/* TimeOut for IRQ from last interrupt (5s)     */
+/*----------------------------------------------*/
+#define IRQ_TIMEOUT_INTERVAL    (5*1000L)
+
+/*----------------------------------------------*/
+/* Retry Delay interval (200ms)                 */
+/*----------------------------------------------*/
+#define DELAYED_RETRY_INTERVAL  200L
+
+#define	INQUIRY_SIZE		36
+#define	CAPACITY_SIZE		8
+#define	DEFAULT_SENSE_LEN	14
+
+#define	DEVICE_NOT_FOUND	0x86
+
+/*----------------------------------------------*/
+/* Definition for PCI device                    */
+/*----------------------------------------------*/
+#define	MAX_PCI_DEVICES	21
+#define	MAX_PCI_BUSES	8
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/megaraid.c linux/drivers/scsi/megaraid.c
--- linux.vanilla/drivers/scsi/megaraid.c	Sun Dec  6 00:14:43 1998
+++ linux/drivers/scsi/megaraid.c	Fri Jan 29 14:12:31 1999
@@ -1,24 +1,27 @@
 /*===================================================================
  *
  *                    Linux MegaRAID device driver
- * 
+ *
  * Copyright 1998 American Megatrends Inc.
  *
- *		This program is free software; you can redistribute it and/or
- *		modify it under the terms of the GNU General Public License
- *		as published by the Free Software Foundation; either version
- *		2 of the License, or (at your option) any later version.
+ *              This program is 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.
  *
- * Version : 0.92
+ * Version : 0.93
  * 
  * Description: Linux device driver for AMI MegaRAID controller
  *
+ * Supported controllers: MegaRAID 418, 428, 438, 466
+ * 
+ * Maintainer: Jeff L Jones <jeffreyj@ami.com>
+ *
  * History:
  *
  * Version 0.90:
- *     Works and has been tested with the MegaRAID 428 controller, and
- *     the MegaRAID 438 controller.  Probably works with the 466 also,
- *     but not tested.
+ *     Original source contributed by Dell; integrated it into the kernel and
+ *     cleaned up some things.  Added support for 438/466 controllers.
  *
  * Version 0.91:
  *     Aligned mailbox area on 16-byte boundry.
@@ -35,28 +38,33 @@
  *     Removed setting of SA_INTERRUPT flag when requesting Irq.
  *
  * Version 0.92ac:
- *	Small changes to the comments/formatting. Plus a couple of
- *	added notes. Returned to the authors. No actual code changes
- *	save printk levels.
- *	8 Oct 98	Alan Cox <alan.cox@linux.org>
+ *      Small changes to the comments/formatting. Plus a couple of
+ *      added notes. Returned to the authors. No actual code changes
+ *      save printk levels.
+ *      8 Oct 98        Alan Cox <alan.cox@linux.org>
+ *
+ *     Merged with 2.1.131 source tree.
+ *     12 Dec 98       K. Baranowski <kgb@knm.org.pl>                          
+ *
+ * Version 0.93:
+ *     Added support for vendor specific ioctl commands (0x80+xxh)
+ *     Changed some fields in MEGARAID struct to better values.
+ *     Added signature check for Rp controllers under 2.0 kernels
+ *     Changed busy-wait loop to be time-based
+ *     Fixed SMP race condition in isr
+ *     Added kfree (sgList) on release
+ *     Added #include linux/version.h to megaraid.h for hosts.h
+ *     Changed max_id to represent max logical drives instead of targets.
+ *
  *
  * BUGS:
- *     Tested with 2.1.90, but unfortunately there is a bug in pci.c which
- *     fails to detect our controller.  Does work with 2.1.118--don't know
- *     which kernel in between it was fixed in.
- *     With SMP enabled under 2.1.118 with more than one processor, gets an
- *     error message "scsi_end_request: buffer-list destroyed" under heavy
- *     IO, but doesn't seem to affect operation, or data integrity.  The
- *     message doesn't occur without SMP enabled, or with one proccessor with
- *     SMP enabled, or under any combination under 2.0 kernels.
+ *     Some older 2.1 kernels (eg. 2.1.90) have a bug in pci.c that
+ *     fails to detect the controller as a pci device on the system.
  *
  *===================================================================*/
-#define QISR 1
 
 #define CRLFSTR "\n"
 
-#define MULTIQ 1
-
 #include <linux/config.h>
 #include <linux/version.h>
 
@@ -66,10 +74,8 @@
 #if LINUX_VERSION_CODE >= 0x20100
 char kernel_version[] = UTS_RELEASE;
 
-/* originally ported by Dell Corporation; updated, released, and maintained by
-   American Megatrends */
-MODULE_AUTHOR("American Megatrends Inc."); 
-MODULE_DESCRIPTION("AMI MegaRAID driver");    
+MODULE_AUTHOR ("American Megatrends Inc.");
+MODULE_DESCRIPTION ("AMI MegaRAID driver");
 #endif
 #endif
 
@@ -107,18 +113,21 @@
 
 #include "megaraid.h"
 
-/*================================================================
- *
- *                          #Defines
- *
- *================================================================*/
+//================================================================
+//
+//                          #Defines
+//
+//================================================================
 
 #if LINUX_VERSION_CODE < 0x020100
 #define ioremap vremap
 #define iounmap vfree
 
 /* simulate spin locks */
-typedef struct {volatile char lock;} spinlock_t;
+typedef struct {
+  volatile char lock;
+} spinlock_t;
+
 #define spin_lock_init(x) { (x)->lock = 0;}
 #define spin_lock_irqsave(x,flags) { while ((x)->lock) barrier();\
                                         (x)->lock=1; save_flags(flags);\
@@ -153,125 +162,124 @@
   spin_unlock_irqrestore(&mega_lock,cpuflag);\
 };
 
-u_long RDINDOOR(mega_host_config *megaCfg)
+u_long RDINDOOR (mega_host_config * megaCfg)
 {
-  return readl(megaCfg->base + 0x20);
+  return readl (megaCfg->base + 0x20);
 }
 
-void WRINDOOR(mega_host_config *megaCfg, u_long value)
+void WRINDOOR (mega_host_config * megaCfg, u_long value)
 {
-  writel(value,megaCfg->base+0x20);
+  writel (value, megaCfg->base + 0x20);
 }
 
-u_long RDOUTDOOR(mega_host_config *megaCfg)
+u_long RDOUTDOOR (mega_host_config * megaCfg)
 {
-  return readl(megaCfg->base+0x2C);
+  return readl (megaCfg->base + 0x2C);
 }
 
-void WROUTDOOR(mega_host_config *megaCfg, u_long value)
+void WROUTDOOR (mega_host_config * megaCfg, u_long value)
 {
-  writel(value,megaCfg->base+0x2C);
+  writel (value, megaCfg->base + 0x2C);
 }
 
-/*================================================================
- *
- *                    Function prototypes
- *
- *================================================================*/
-static int  MegaIssueCmd(mega_host_config *megaCfg,
-			 u_char *mboxData,
-			 mega_scb *scb,
+//================================================================
+//
+//                    Function prototypes
+//
+//================================================================
+static int MegaIssueCmd (mega_host_config * megaCfg,
+			 u_char * mboxData,
+			 mega_scb * scb,
 			 int intr);
-static int  build_sglist(mega_host_config *megaCfg, mega_scb *scb, 
-			 u_long *buffer, u_long *length);
+static int build_sglist (mega_host_config * megaCfg, mega_scb * scb,
+			 u_long * buffer, u_long * length);
 
-static void mega_runque(void *);
-static void mega_rundoneq(void);
-static void mega_cmd_done(mega_host_config *,mega_scb *, int);
+static void mega_runque (void *);
+static void mega_rundoneq (void);
+static void mega_cmd_done (mega_host_config *, mega_scb *, int);
+static mega_scb *mega_ioctl (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt);
 
 /* set SERDEBUG to 1 to enable serial debugging */
 #define SERDEBUG 0
 #if SERDEBUG
-static void ser_init(void);
-static void ser_puts(char *str);
-static void ser_putc(char c);
-static int  ser_printk(const char *fmt, ...);
+static void ser_init (void);
+static void ser_puts (char *str);
+static void ser_putc (char c);
+static int ser_printk (const char *fmt,...);
 #endif
 
-/*================================================================
- *
- *                    Global variables
- *
- *================================================================*/
-static int               numCtlrs = 0;
-static mega_host_config *megaCtlrs[4] = { 0 };
+//================================================================
+//
+//                    Global variables
+//
+//================================================================
+static int numCtlrs = 0;
+static mega_host_config *megaCtlrs[12] = {0};
 
 /* Change this to 0 if you want to see the raw drives */
-static int use_raid   = 1;
+static int use_raid = 1;
 
 /* Queue of pending/completed SCBs */
-static mega_scb  *qPending   = NULL;
+static mega_scb *qPending = NULL;
 static Scsi_Cmnd *qCompleted = NULL;
 
 volatile static spinlock_t mega_lock;
-static struct tq_struct runq = {0,0,mega_runque,NULL};
+static struct tq_struct runq = {0, 0, mega_runque, NULL};
 
-struct proc_dir_entry proc_scsi_megaraid = {
+struct proc_dir_entry proc_scsi_megaraid =
+{
   PROC_SCSI_MEGARAID, 8, "megaraid",
   S_IFDIR | S_IRUGO | S_IXUGO, 2
 };
 
 #if SERDEBUG
-static char strbuf[MAX_SERBUF+1];
+static char strbuf[MAX_SERBUF + 1];
 
-static void ser_init()
+static void ser_init ()
 {
-    unsigned port=COM_BASE;
+  unsigned port = COM_BASE;
 
-    outb(0x80,port+3);
-    outb(0,port+1);
-    /* 9600 Baud, if 19200: outb(6,port) */
-    outb(12, port);
-    outb(3,port+3);
-    outb(0,port+1);
+  outb (0x80, port + 3);
+  outb (0, port + 1);
+  /* 9600 Baud, if 19200: outb(6,port) */
+  outb (12, port);
+  outb (3, port + 3);
+  outb (0, port + 1);
 }
 
-static void ser_puts(char *str)
+static void ser_puts (char *str)
 {
-    char *ptr;
+  char *ptr;
 
-    ser_init();
-    for (ptr=str;*ptr;++ptr)
-        ser_putc(*ptr);
+  ser_init ();
+  for (ptr = str; *ptr; ++ptr)
+    ser_putc (*ptr);
 }
 
-static void ser_putc(char c)
+static void ser_putc (char c)
 {
-    unsigned port=COM_BASE;
+  unsigned port = COM_BASE;
 
-    while ((inb(port+5) & 0x20)==0);
-    outb(c,port);
-    if (c==0x0a)
-    {
-        while ((inb(port+5) & 0x20)==0);
-        outb(0x0d,port);
-    }
+  while ((inb (port + 5) & 0x20) == 0);
+  outb (c, port);
+  if (c == 0x0a) {
+    while ((inb (port + 5) & 0x20) == 0);
+    outb (0x0d, port);
+  }
 }
 
-static int ser_printk(const char *fmt, ...)
+static int ser_printk (const char *fmt,...)
 {
-    va_list args;
-    int i;
-    long flags;
+  va_list args;
+  int i;
+  long flags;
 
-    spin_lock_irqsave(mega_lock,flags);
-    va_start(args,fmt);
-    i = vsprintf(strbuf,fmt,args);
-    ser_puts(strbuf);
-    va_end(args);
-    spin_unlock_irqrestore(&mega_lock,flags);
+  va_start (args, fmt);
+  i = vsprintf (strbuf, fmt, args);
+  ser_puts (strbuf);
+  va_end (args);
 
-    return i;
+  return i;
 }
 
 #define TRACE(a)    { ser_printk a;}
@@ -280,14 +288,14 @@
 #define TRACE(A)
 #endif
 
-void callDone(Scsi_Cmnd *SCpnt)
+void callDone (Scsi_Cmnd * SCpnt)
 {
   if (SCpnt->result) {
-    TRACE(("*** %.08lx %.02x <%d.%d.%d> = %x\n", SCpnt->serial_number, 
-	   SCpnt->cmnd[0], SCpnt->channel, SCpnt->target, SCpnt->lun, 
-	   SCpnt->result));
+    TRACE (("*** %.08lx %.02x <%d.%d.%d> = %x\n", SCpnt->serial_number,
+	    SCpnt->cmnd[0], SCpnt->channel, SCpnt->target, SCpnt->lun,
+	    SCpnt->result));
   }
-  SCpnt->scsi_done(SCpnt);
+  SCpnt->scsi_done (SCpnt);
 }
 
 /*-------------------------------------------------------------------------
@@ -296,82 +304,79 @@
  *
  *-------------------------------------------------------------------------*/
 
-/*================================================
- * Initialize SCB structures
- *================================================*/
-static void initSCB(mega_host_config *megaCfg)
+//================================================
+// Initialize SCB structures
+//================================================
+static void initSCB (mega_host_config * megaCfg)
 {
   int idx;
 
-  for(idx=0; idx<megaCfg->max_cmds; idx++) {
-    megaCfg->scbList[idx].idx    = -1;
-    megaCfg->scbList[idx].flag   = 0;
+  for (idx = 0; idx < megaCfg->max_cmds; idx++) {
+    megaCfg->scbList[idx].idx = -1;
+    megaCfg->scbList[idx].flag = 0;
     megaCfg->scbList[idx].sgList = NULL;
-    megaCfg->scbList[idx].SCpnt  = NULL;
+    megaCfg->scbList[idx].SCpnt = NULL;
   }
 }
 
-/*===========================
- * Allocate a SCB structure
- *===========================*/
-static mega_scb *allocateSCB(mega_host_config *megaCfg,Scsi_Cmnd *SCpnt)
+//===========================
+// Allocate a SCB structure
+//===========================
+static mega_scb * allocateSCB (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt)
 {
-  int        idx;
-  long       flags;
+  int idx;
+  long flags;
 
-  spin_lock_irqsave(&mega_lock,flags);
-  for(idx=0; idx<megaCfg->max_cmds; idx++) {
+  spin_lock_irqsave (&mega_lock, flags);
+  for (idx = 0; idx < megaCfg->max_cmds; idx++) {
     if (megaCfg->scbList[idx].idx < 0) {
 
-      /* Set Index and SCB pointer */ 
-      megaCfg->scbList[idx].flag  = 0;
-      megaCfg->scbList[idx].idx   = idx;
+      /* Set Index and SCB pointer */
+      megaCfg->scbList[idx].flag = 0;
+      megaCfg->scbList[idx].idx = idx;
       megaCfg->scbList[idx].SCpnt = SCpnt;
-      megaCfg->scbList[idx].next  = NULL;
-      spin_unlock_irqrestore(&mega_lock,flags);
+      megaCfg->scbList[idx].next = NULL;
+      spin_unlock_irqrestore (&mega_lock, flags);
 
       if (megaCfg->scbList[idx].sgList == NULL) {
 	megaCfg->scbList[idx].sgList =
-                  kmalloc(sizeof(mega_sglist)*MAX_SGLIST,GFP_ATOMIC|GFP_DMA);
+	  kmalloc (sizeof (mega_sglist) * MAX_SGLIST, GFP_ATOMIC | GFP_DMA);
       }
 
       return &megaCfg->scbList[idx];
     }
   }
-  spin_unlock_irqrestore(&mega_lock,flags);
+  spin_unlock_irqrestore (&mega_lock, flags);
+
+  printk (KERN_WARNING "Megaraid: Could not allocate free SCB!!!\n");
 
-  printk(KERN_WARNING "Megaraid: Could not allocate free SCB!!!\n");
-  
   return NULL;
 }
 
-/*=======================
- * Free a SCB structure
- *=======================*/
-static void freeSCB(mega_scb *scb)
-{
-  long flags;
-
-  spin_lock_irqsave(&mega_lock,flags);
-  scb->flag  = 0;
-  scb->idx   = -1;
-  scb->next  = NULL;
+//=======================
+// Free a SCB structure
+//=======================
+static void freeSCB (mega_scb * scb)
+{
+  scb->flag = 0;
+  scb->idx = -1;
+  scb->next = NULL;
   scb->SCpnt = NULL;
-  spin_unlock_irqrestore(&mega_lock,flags);
 }
 
 /* Run through the list of completed requests */
-static void mega_rundoneq()
+static void mega_rundoneq ()
 {
   mega_host_config *megaCfg;
-  Scsi_Cmnd        *SCpnt;
-  long              islogical;
+  Scsi_Cmnd *SCpnt;
+  long islogical;
 
-  while(1) {
-    DEQUEUE(SCpnt, Scsi_Cmnd, qCompleted, host_scribble);
-    if (SCpnt == NULL) return;
+  while (1) {
+    DEQUEUE (SCpnt, Scsi_Cmnd, qCompleted, host_scribble);
+    if (SCpnt == NULL)
+      return;
 
-    megaCfg = (mega_host_config *)SCpnt->host->hostdata;
+    megaCfg = (mega_host_config *) SCpnt->host->hostdata;
 
     /* Check if we're allowing access to RAID drives or physical
      *  if use_raid == 1 and this wasn't a disk on the max channel or
@@ -380,14 +385,15 @@
      */
     islogical = (SCpnt->channel == megaCfg->host->max_channel) ? 1 : 0;
     if (SCpnt->cmnd[0] == INQUIRY &&
-	((((u_char*)SCpnt->request_buffer)[0] & 0x1F) == TYPE_DISK) &&
+	((((u_char *) SCpnt->request_buffer)[0] & 0x1F) == TYPE_DISK) &&
 	(islogical != use_raid)) {
-       SCpnt->result = 0xF0;
+      SCpnt->result = 0xF0;
     }
 
     /* Convert result to error */
-    switch(SCpnt->result) {
-    case 0x00: case 0x02:
+    switch (SCpnt->result) {
+    case 0x00:
+    case 0x02:
       SCpnt->result |= (DID_OK << 16);
       break;
     case 0x8:
@@ -399,16 +405,20 @@
     }
 
     /* Callback */
-    callDone(SCpnt);
+    callDone (SCpnt);
   }
 }
 
 /* Add command to the list of completed requests */
-static void mega_cmd_done(mega_host_config *megaCfg,mega_scb *pScb, int status)
+static void mega_cmd_done (mega_host_config * megaCfg, mega_scb * pScb, int status)
 {
+  long flags;
+
   pScb->SCpnt->result = status;
-  ENQUEUE(pScb->SCpnt, Scsi_Cmnd, qCompleted, host_scribble);
-  freeSCB(pScb);
+  ENQUEUE (pScb->SCpnt, Scsi_Cmnd, qCompleted, host_scribble);
+  spin_lock_irqsave (&mega_lock, flags);
+  freeSCB (pScb);
+  spin_unlock_irqrestore (&mega_lock, flags);
 }
 
 /*----------------------------------------------------
@@ -416,45 +426,42 @@
  *
  * Run as a scheduled task 
  *----------------------------------------------------*/
-static void mega_runque(void *dummy)
+static void mega_runque (void *dummy)
 {
   mega_host_config *megaCfg;
-  mega_scb         *pScb;
-  long              flags;
+  mega_scb *pScb;
+  long flags;
 
   /* Take care of any completed requests */
-  mega_rundoneq();
+  mega_rundoneq ();
 
-  DEQUEUE(pScb,mega_scb,qPending,next);
+  DEQUEUE (pScb, mega_scb, qPending, next);
 
   if (pScb) {
-    megaCfg = (mega_host_config *)pScb->SCpnt->host->hostdata;
+    megaCfg = (mega_host_config *) pScb->SCpnt->host->hostdata;
 
-    if (megaCfg->mbox->busy || megaCfg->flag & (IN_ISR|PENDING)) {
-      printk(KERN_DEBUG "PENDING = %x, IN_ISR = %x, mbox.busy = %x\n",(u_int)(megaCfg->flag
-            & PENDING), (u_int)(megaCfg->flag & IN_ISR), megaCfg->mbox->busy);
-      TRACE(("%.08lx %.02x <%d.%d.%d> intr%d busy%d isr%d pending%d\n",
-	     pScb->SCpnt->serial_number,
-	     pScb->SCpnt->cmnd[0],
-	     pScb->SCpnt->channel,
-	     pScb->SCpnt->target,
-	     pScb->SCpnt->lun,
-	     intr_count,
-	     megaCfg->mbox->busy,
-	     (megaCfg->flag & IN_ISR)  ? 1 : 0,
-	     (megaCfg->flag & PENDING) ? 1 : 0));
+    if (megaCfg->mbox->busy || megaCfg->flag & (IN_ISR | PENDING)) {
+      TRACE (("%.08lx %.02x <%d.%d.%d> busy%d isr%d pending%d\n",
+	      pScb->SCpnt->serial_number,
+	      pScb->SCpnt->cmnd[0],
+	      pScb->SCpnt->channel,
+	      pScb->SCpnt->target,
+	      pScb->SCpnt->lun,
+	      megaCfg->mbox->busy,
+	      (megaCfg->flag & IN_ISR) ? 1 : 0,
+	      (megaCfg->flag & PENDING) ? 1 : 0));
     }
 
-    if (MegaIssueCmd(megaCfg, pScb->mboxData, pScb, 1)) {
-      printk(KERN_DEBUG "MegaIssueCmd returned BUSY.  Rescheduling command.\n");
+    if (MegaIssueCmd (megaCfg, pScb->mboxData, pScb, 1)) {
       /* We're BUSY... come back later */
-      spin_lock_irqsave(&mega_lock,flags);
+      spin_lock_irqsave (&mega_lock, flags);
       pScb->next = qPending;
-      qPending   = pScb;
-      spin_unlock_irqrestore(&mega_lock,flags);
+      qPending = pScb;
+      spin_unlock_irqrestore (&mega_lock, flags);
 
-      if (!(megaCfg->flag & PENDING)) { /* If PENDING, irq will schedule task */
-          queue_task(&runq, &tq_scheduler);
+      if (!(megaCfg->flag & PENDING)) {
+	/* If PENDING, irq will schedule task */
+	queue_task (&runq, &tq_scheduler);
       }
     }
   }
@@ -468,17 +475,20 @@
  * If NULL is returned, the scsi_done function MUST have been called
  *
  *-------------------------------------------------------------------*/
-static mega_scb *mega_build_cmd(mega_host_config *megaCfg, Scsi_Cmnd *SCpnt)
+static mega_scb * mega_build_cmd (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt)
 {
-  mega_scb      *pScb;
-  mega_mailbox  *mbox;
+  mega_scb *pScb;
+  mega_mailbox *mbox;
   mega_passthru *pthru;
-  long           seg;
+  long seg;
+
+  if (SCpnt->cmnd[0] & 0x80)	/* ioctl from megamgr */
+    return mega_ioctl (megaCfg, SCpnt);
 
   /* We don't support multi-luns */
   if (SCpnt->lun != 0) {
     SCpnt->result = (DID_BAD_TARGET << 16);
-    callDone(SCpnt);
+    callDone (SCpnt);
     return NULL;
   }
 
@@ -488,44 +498,44 @@
    *
    *-----------------------------------------------------*/
   if (SCpnt->channel == megaCfg->host->max_channel) {
-    switch(SCpnt->cmnd[0]) {
+    switch (SCpnt->cmnd[0]) {
     case TEST_UNIT_READY:
-      memset(SCpnt->request_buffer, 0, SCpnt->request_bufflen);
+      memset (SCpnt->request_buffer, 0, SCpnt->request_bufflen);
       SCpnt->result = (DID_OK << 16);
-      callDone(SCpnt);
+      callDone (SCpnt);
       return NULL;
 
     case MODE_SENSE:
-      memset(SCpnt->request_buffer, 0, SCpnt->cmnd[4]);
+      memset (SCpnt->request_buffer, 0, SCpnt->cmnd[4]);
       SCpnt->result = (DID_OK << 16);
-      callDone(SCpnt);
+      callDone (SCpnt);
       return NULL;
 
     case READ_CAPACITY:
     case INQUIRY:
       /* Allocate a SCB and initialize passthru */
-      if ((pScb = allocateSCB(megaCfg,SCpnt)) == NULL) {
+      if ((pScb = allocateSCB (megaCfg, SCpnt)) == NULL) {
 	SCpnt->result = (DID_ERROR << 16);
-	callDone(SCpnt);
+	callDone (SCpnt);
 	return NULL;
       }
       pthru = &pScb->pthru;
-      mbox  = (mega_mailbox *)&pScb->mboxData;
+      mbox = (mega_mailbox *) & pScb->mboxData;
 
-      memset(mbox,  0, sizeof(pScb->mboxData));
-      memset(pthru, 0, sizeof(mega_passthru));
-      pthru->timeout      = 0;
-      pthru->ars          = 0;
-      pthru->islogical    = 1;
-      pthru->logdrv       = SCpnt->target;
-      pthru->cdblen       = SCpnt->cmd_len;
-      pthru->dataxferaddr = virt_to_bus(SCpnt->request_buffer);
-      pthru->dataxferlen  = SCpnt->request_bufflen;
-      memcpy(pthru->cdb, SCpnt->cmnd, SCpnt->cmd_len);
+      memset (mbox, 0, sizeof (pScb->mboxData));
+      memset (pthru, 0, sizeof (mega_passthru));
+      pthru->timeout = 0;
+      pthru->ars = 0;
+      pthru->islogical = 1;
+      pthru->logdrv = SCpnt->target;
+      pthru->cdblen = SCpnt->cmd_len;
+      pthru->dataxferaddr = virt_to_bus (SCpnt->request_buffer);
+      pthru->dataxferlen = SCpnt->request_bufflen;
+      memcpy (pthru->cdb, SCpnt->cmnd, SCpnt->cmd_len);
 
       /* Initialize mailbox area */
-      mbox->cmd      = MEGA_MBOXCMD_PASSTHRU;
-      mbox->xferaddr = virt_to_bus(pthru);
+      mbox->cmd = MEGA_MBOXCMD_PASSTHRU;
+      mbox->xferaddr = virt_to_bus (pthru);
 
       return pScb;
 
@@ -534,51 +544,51 @@
     case READ_10:
     case WRITE_10:
       /* Allocate a SCB and initialize mailbox */
-      if ((pScb = allocateSCB(megaCfg,SCpnt)) == NULL) {
+      if ((pScb = allocateSCB (megaCfg, SCpnt)) == NULL) {
 	SCpnt->result = (DID_ERROR << 16);
-	callDone(SCpnt);
+	callDone (SCpnt);
 	return NULL;
       }
-      mbox = (mega_mailbox *)&pScb->mboxData;
+      mbox = (mega_mailbox *) & pScb->mboxData;
 
-      memset(mbox, 0, sizeof(pScb->mboxData));
+      memset (mbox, 0, sizeof (pScb->mboxData));
       mbox->logdrv = SCpnt->target;
-      mbox->cmd    = (*SCpnt->cmnd == READ_6 || *SCpnt->cmnd == READ_10) ?
+      mbox->cmd = (*SCpnt->cmnd == READ_6 || *SCpnt->cmnd == READ_10) ?
 	MEGA_MBOXCMD_LREAD : MEGA_MBOXCMD_LWRITE;
-      
+
       /* 6-byte */
       if (*SCpnt->cmnd == READ_6 || *SCpnt->cmnd == WRITE_6) {
-	mbox->numsectors = 
-	  (u_long)SCpnt->cmnd[4];
-	mbox->lba = 
-	  ((u_long)SCpnt->cmnd[1] << 16) |
-	  ((u_long)SCpnt->cmnd[2] << 8) |
-	  (u_long)SCpnt->cmnd[3];
+	mbox->numsectors =
+	  (u_long) SCpnt->cmnd[4];
+	mbox->lba =
+	  ((u_long) SCpnt->cmnd[1] << 16) |
+	  ((u_long) SCpnt->cmnd[2] << 8) |
+	  (u_long) SCpnt->cmnd[3];
 	mbox->lba &= 0x1FFFFF;
       }
-      
+
       /* 10-byte */
       if (*SCpnt->cmnd == READ_10 || *SCpnt->cmnd == WRITE_10) {
-	mbox->numsectors = 
-	  (u_long)SCpnt->cmnd[8] |
-	  ((u_long)SCpnt->cmnd[7] << 8);
+	mbox->numsectors =
+	  (u_long) SCpnt->cmnd[8] |
+	  ((u_long) SCpnt->cmnd[7] << 8);
 	mbox->lba =
-	  ((u_long)SCpnt->cmnd[2] << 24) |
-	  ((u_long)SCpnt->cmnd[3] << 16) |
-	  ((u_long)SCpnt->cmnd[4] << 8) |
-	  (u_long)SCpnt->cmnd[5];
+	  ((u_long) SCpnt->cmnd[2] << 24) |
+	  ((u_long) SCpnt->cmnd[3] << 16) |
+	  ((u_long) SCpnt->cmnd[4] << 8) |
+	  (u_long) SCpnt->cmnd[5];
       }
-      
+
       /* Calculate Scatter-Gather info */
-      mbox->numsgelements = build_sglist(megaCfg, pScb, 
-					 (u_long*)&mbox->xferaddr,
-					 (u_long*)&seg);
+      mbox->numsgelements = build_sglist (megaCfg, pScb,
+					  (u_long *) & mbox->xferaddr,
+					  (u_long *) & seg);
 
       return pScb;
-      
+
     default:
       SCpnt->result = (DID_BAD_TARGET << 16);
-      callDone(SCpnt);
+      callDone (SCpnt);
       return NULL;
     }
   }
@@ -589,31 +599,31 @@
    *-----------------------------------------------------*/
   else {
     /* Allocate a SCB and initialize passthru */
-    if ((pScb = allocateSCB(megaCfg,SCpnt)) == NULL) {
+    if ((pScb = allocateSCB (megaCfg, SCpnt)) == NULL) {
       SCpnt->result = (DID_ERROR << 16);
-      callDone(SCpnt);
+      callDone (SCpnt);
       return NULL;
     }
     pthru = &pScb->pthru;
-    mbox  = (mega_mailbox *)pScb->mboxData;
-    
-    memset(mbox,  0, sizeof(pScb->mboxData));
-    memset(pthru, 0, sizeof(mega_passthru));
-    pthru->timeout   = 0;
-    pthru->ars       = 0;
+    mbox = (mega_mailbox *) pScb->mboxData;
+
+    memset (mbox, 0, sizeof (pScb->mboxData));
+    memset (pthru, 0, sizeof (mega_passthru));
+    pthru->timeout = 0;
+    pthru->ars = 0;
     pthru->islogical = 0;
-    pthru->channel   = SCpnt->channel;
-    pthru->target    = SCpnt->target;
-    pthru->cdblen    = SCpnt->cmd_len;
-    memcpy(pthru->cdb, SCpnt->cmnd, SCpnt->cmd_len);
-   
-    pthru->numsgelements = build_sglist(megaCfg, pScb,
-					(u_long *)&pthru->dataxferaddr,
-					(u_long *)&pthru->dataxferlen);
-    
+    pthru->channel = SCpnt->channel;
+    pthru->target = SCpnt->target;
+    pthru->cdblen = SCpnt->cmd_len;
+    memcpy (pthru->cdb, SCpnt->cmnd, SCpnt->cmd_len);
+
+    pthru->numsgelements = build_sglist (megaCfg, pScb,
+					 (u_long *) & pthru->dataxferaddr,
+					 (u_long *) & pthru->dataxferlen);
+
     /* Initialize mailbox */
-    mbox->cmd      = MEGA_MBOXCMD_PASSTHRU;
-    mbox->xferaddr = virt_to_bus(pthru);
+    mbox->cmd = MEGA_MBOXCMD_PASSTHRU;
+    mbox->xferaddr = virt_to_bus (pthru);
 
     return pScb;
   }
@@ -621,81 +631,164 @@
 }
 
 /*--------------------------------------------------------------------
+ * build RAID commands for controller, passed down through ioctl()
+ *--------------------------------------------------------------------*/
+static mega_scb * mega_ioctl (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt)
+{
+  mega_scb *pScb;
+  mega_ioctl_mbox *mbox;
+  mega_mailbox *mailbox;
+  mega_passthru *pthru;
+  long seg;
+
+  if ((pScb = allocateSCB (megaCfg, SCpnt)) == NULL) {
+    SCpnt->result = (DID_ERROR << 16);
+    callDone (SCpnt);
+    return NULL;
+  }
+
+  mbox = (mega_ioctl_mbox *) & pScb->mboxData;
+  mailbox = (mega_mailbox *) & pScb->mboxData;
+  memset (mailbox, 0, sizeof (pScb->mboxData));
+
+  if (SCpnt->cmnd[0] == 0x83) {	/* passthrough command */
+    char cdblen = SCpnt->cmnd[2];
+
+    pthru = &pScb->pthru;
+    memset (pthru, 0, sizeof (mega_passthru));
+    pthru->islogical = SCpnt->cmnd[cdblen + 3] & 0x80;
+    pthru->timeout = SCpnt->cmnd[cdblen + 3] & 0x07;
+    pthru->reqsenselen = 10;	/* ? MAX_SENSE; */
+    pthru->ars = SCpnt->cmnd[cdblen + 3] & 0x08;
+    pthru->logdrv = SCpnt->cmnd[cdblen + 4];
+    pthru->channel = SCpnt->cmnd[cdblen + 5];
+    pthru->target = SCpnt->cmnd[cdblen + 6];
+    pthru->cdblen = cdblen;
+    memcpy (pthru->cdb, SCpnt->cmnd, SCpnt->cmnd[2]);
+
+    mailbox->cmd = MEGA_MBOXCMD_PASSTHRU;
+    mailbox->xferaddr = virt_to_bus (pthru);
+
+    pthru->numsgelements = build_sglist (megaCfg, pScb,
+					 (u_long *) & pthru->dataxferaddr,
+					 (u_long *) & pthru->dataxferlen);
+
+    return pScb;
+  }
+  /* else normal (nonpassthru) command */
+
+  mbox->cmd = SCpnt->cmnd[0] & 0x7F;
+  mbox->channel = SCpnt->cmnd[1];
+  mbox->param = SCpnt->cmnd[2];
+  mbox->pad[0] = SCpnt->cmnd[3];
+  mbox->logdrv = SCpnt->cmnd[4];
+
+  mbox->numsgelements = build_sglist (megaCfg, pScb,
+				      (u_long *) & mbox->xferaddr,
+				      (u_long *) & seg);
+
+  return (pScb);
+}
+
+
+/*--------------------------------------------------------------------
  * Interrupt service routine
  *--------------------------------------------------------------------*/
-static void megaraid_isr(int irq, void *devp, struct pt_regs *regs)
+static void megaraid_isr (int irq, void *devp, struct pt_regs *regs)
 {
-  mega_host_config *megaCfg;
-  u_char            byte, idx, sIdx;
-  u_long            dword;
-  mega_mailbox     *mbox;
-  mega_scb         *pScb;
-  long              flags;
-  int               qCnt, qStatus;
+  mega_host_config    *megaCfg;
+  u_char byte, idx, sIdx;
+  u_long dword;
+  mega_mailbox *mbox;
+  mega_scb *pScb;
+  long flags;
+  int qCnt, qStatus;
 
-  megaCfg = (mega_host_config *)devp;
-  mbox    = (mega_mailbox *)megaCfg->mbox;
+  megaCfg = (mega_host_config *) devp;
+  mbox = (mega_mailbox *) megaCfg->mbox;
 
   if (megaCfg->host->irq == irq) {
-    spin_lock_irqsave(&mega_lock,flags);
+
+#if LINUX_VERSION_CODE >= 0x20100
+    spin_lock_irqsave (&io_request_lock, flags);
+#endif
+
+    spin_lock_irqsave (&mega_lock, flags);
 
     if (megaCfg->flag & IN_ISR) {
-      TRACE(("ISR called reentrantly!!\n"));
+      TRACE (("ISR called reentrantly!!\n"));
     }
 
     megaCfg->flag |= IN_ISR;
 
     /* Check if a valid interrupt is pending */
     if (megaCfg->flag & BOARD_QUARTZ) {
-        dword = RDOUTDOOR(megaCfg);
-        if (dword != 0x10001234) {
-            /* Spurious interrupt */
-            megaCfg->flag &= ~IN_ISR;
-            spin_unlock_irqrestore(&mega_lock,flags);
-            return;
-        }
-        WROUTDOOR(megaCfg,dword);
-    } else {
-        byte = READ_PORT(megaCfg->host->io_port, INTR_PORT);
-        if ((byte & VALID_INTR_BYTE) == 0) {
-          /* Spurious interrupt */
-          megaCfg->flag &= ~IN_ISR;
-          spin_unlock_irqrestore(&mega_lock,flags);
-          return;
-        }
-        WRITE_PORT(megaCfg->host->io_port, INTR_PORT, byte);
+      dword = RDOUTDOOR (megaCfg);
+      if (dword != 0x10001234) {
+	/* Spurious interrupt */
+	megaCfg->flag &= ~IN_ISR;
+	spin_unlock_irqrestore (&mega_lock, flags);
+#if LINUX_VERSION_CODE >= 0x20100
+        spin_unlock_irqrestore (&io_request_lock, flags);
+#endif
+	return;
+      }
+      WROUTDOOR (megaCfg, dword);
+    }
+    else {
+      byte = READ_PORT (megaCfg->host->io_port, INTR_PORT);
+      if ((byte & VALID_INTR_BYTE) == 0) {
+	/* Spurious interrupt */
+	megaCfg->flag &= ~IN_ISR;
+	spin_unlock_irqrestore (&mega_lock, flags);
+#if LINUX_VERSION_CODE >= 0x20100
+        spin_unlock_irqrestore (&io_request_lock, flags);
+#endif
+	return;
+      }
+      WRITE_PORT (megaCfg->host->io_port, INTR_PORT, byte);
     }
-    
-    qCnt    = mbox->numstatus;
+
+    qCnt = mbox->numstatus;
     qStatus = mbox->status;
 
-    if (qCnt > 1) {TRACE(("ISR: Received %d status\n", qCnt))
-        printk(KERN_DEBUG "Got numstatus = %d\n",qCnt);
+    if (qCnt > 1) {
+      TRACE (("ISR: Received %d status\n", qCnt))
+	printk (KERN_DEBUG "Got numstatus = %d\n", qCnt);
     }
-    
-    for(idx=0; idx<qCnt; idx++) {
+
+    for (idx = 0; idx < qCnt; idx++) {
       sIdx = mbox->completed[idx];
       if (sIdx > 0) {
-	pScb = &megaCfg->scbList[sIdx-1];
-        spin_unlock_irqrestore(&mega_lock,flags); /* locks within cmd_done */
-	mega_cmd_done(megaCfg,&megaCfg->scbList[sIdx-1], qStatus);
-        spin_lock_irqsave(&mega_lock,flags);
+	pScb = &megaCfg->scbList[sIdx - 1];
+	spin_unlock_irqrestore (&mega_lock, flags); /* megalock within cmd_done */
+	mega_cmd_done (megaCfg, &megaCfg->scbList[sIdx - 1], qStatus);
+	spin_lock_irqsave (&mega_lock, flags);
       }
     }
     if (megaCfg->flag & BOARD_QUARTZ) {
-        WRINDOOR(megaCfg,virt_to_bus(megaCfg->mbox)|0x2);
-        while (RDINDOOR(megaCfg) & 0x02);
-    } else {
-        CLEAR_INTR(megaCfg->host->io_port);
+      WRINDOOR (megaCfg, virt_to_bus (megaCfg->mbox) | 0x2);
+      while (RDINDOOR (megaCfg) & 0x02);
+    }
+    else {
+      CLEAR_INTR (megaCfg->host->io_port);
     }
 
     megaCfg->flag &= ~IN_ISR;
     megaCfg->flag &= ~PENDING;
 
+    spin_unlock_irqrestore (&mega_lock, flags);
+    mega_runque (NULL);
+
+#if LINUX_VERSION_CODE >= 0x20100
+    spin_unlock_irqrestore (&io_request_lock, flags);
+#endif
+
+#if 0
     /* Queue as a delayed ISR routine */
-    queue_task_irq_off(&runq, &tq_immediate);
-    mark_bh(IMMEDIATE_BH);
-    spin_unlock_irqrestore(&mega_lock,flags);
+    queue_task_irq_off (&runq, &tq_immediate);
+    mark_bh (IMMEDIATE_BH);
+#endif
 
   }
 }
@@ -703,111 +796,128 @@
 /*==================================================*/
 /* Wait until the controller's mailbox is available */
 /*==================================================*/
-static int busyWaitMbox(mega_host_config *megaCfg)
+static int busyWaitMbox (mega_host_config * megaCfg)
 {
-  mega_mailbox *mbox = (mega_mailbox *)megaCfg->mbox;
-  long          counter;
+  mega_mailbox *mbox = (mega_mailbox *) megaCfg->mbox;
+  long counter;
 
-  for(counter=0; counter<0xFFFFFF; counter++) {
-    if (!mbox->busy) return 0;
+  for (counter = 0; counter < 30000; counter++) {
+    udelay (100);
+    if (!mbox->busy)
+      return 0;
   }
-  return -1;
-}
-
-/*=====================================================
- * Post a command to the card
- *
- * Arguments:
- *   mega_host_config *megaCfg - Controller structure
- *   u_char *mboxData - Mailbox area, 16 bytes
- *   mega_scb *pScb   - SCB posting (or NULL if N/A)
- *   int intr         - if 1, interrupt, 0 is blocking
- *=====================================================*/
-static int MegaIssueCmd(mega_host_config *megaCfg,
-			u_char *mboxData,
-			mega_scb *pScb,
-			int intr)
-{
-  mega_mailbox *mbox = (mega_mailbox *)megaCfg->mbox;
-  long          flags;
-  u_char        byte;
-  u_long        cmdDone;
+  return -1;			/* give up after 3 seconds */
+}
 
-  mboxData[0x1] = (pScb ? pScb->idx+1 : 0x00);  /* Set cmdid */
-  mboxData[0xF] = 1;                            /* Set busy */
+//=====================================================
+// Post a command to the card
+//
+// Arguments:
+//   mega_host_config *megaCfg - Controller structure
+//   u_char *mboxData - Mailbox area, 16 bytes
+//   mega_scb *pScb   - SCB posting (or NULL if N/A)
+//   int intr         - if 1, interrupt, 0 is blocking
+//=====================================================
+static int MegaIssueCmd (mega_host_config * megaCfg,
+	      u_char * mboxData,
+	      mega_scb * pScb,
+	      int intr)
+{
+  mega_mailbox *mbox = (mega_mailbox *) megaCfg->mbox;
+  long flags;
+  u_char byte;
+  u_long cmdDone;
+
+  mboxData[0x1] = (pScb ? pScb->idx + 1 : 0x00);	/* Set cmdid */
+  mboxData[0xF] = 1;		/* Set busy */
+
+  spin_lock_irqsave(&mega_lock,flags);
 
   /* one bad report of problem when issuing a command while pending.
    * Wasn't able to duplicate, but it doesn't really affect performance
    * anyway, so don't allow command while PENDING
    */
+
   if (megaCfg->flag & PENDING) {
+    spin_unlock_irqrestore(&mega_lock,flags);
     return -1;
   }
 
   /* Wait until mailbox is free */
-  if (busyWaitMbox(megaCfg)) {
+  if (busyWaitMbox (megaCfg)) {
     if (pScb) {
-      TRACE(("Mailbox busy %.08lx <%d.%d.%d>\n", pScb->SCpnt->serial_number,
-	     pScb->SCpnt->channel, pScb->SCpnt->target, pScb->SCpnt->lun));
+      TRACE (("Mailbox busy %.08lx <%d.%d.%d>\n", pScb->SCpnt->serial_number,
+	      pScb->SCpnt->channel, pScb->SCpnt->target, pScb->SCpnt->lun));
+    } else {
+	TRACE(("pScb NULL in MegaIssueCmd!\n"));
     }
+    spin_unlock_irqrestore(&mega_lock,flags);
     return -1;
   }
 
   /* Copy mailbox data into host structure */
-  spin_lock_irqsave(&mega_lock,flags);
-  memset(mbox, 0, sizeof(mega_mailbox));
-  memcpy(mbox, mboxData, 16);
-  spin_unlock_irqrestore(&mega_lock,flags);
+  memset (mbox, 0, sizeof (mega_mailbox));
+  memcpy (mbox, mboxData, 16);
 
   /* Kick IO */
   megaCfg->flag |= PENDING;
   if (intr) {
     /* Issue interrupt (non-blocking) command */
     if (megaCfg->flag & BOARD_QUARTZ) {
-        mbox->mraid_poll = 0; 
-        mbox->mraid_ack = 0; 
-        WRINDOOR(megaCfg, virt_to_bus(megaCfg->mbox) | 0x1);
-    } else {
-        ENABLE_INTR(megaCfg->host->io_port);
-        ISSUE_COMMAND(megaCfg->host->io_port);
+      mbox->mraid_poll = 0;
+      mbox->mraid_ack = 0;
+      WRINDOOR (megaCfg, virt_to_bus (megaCfg->mbox) | 0x1);
+    }
+    else {
+      ENABLE_INTR (megaCfg->host->io_port);
+      ISSUE_COMMAND (megaCfg->host->io_port);
     }
+    spin_unlock_irqrestore(&mega_lock,flags);
   }
-  else {      /* Issue non-ISR (blocking) command */
+  else {			/* Issue non-ISR (blocking) command */
 
     if (megaCfg->flag & BOARD_QUARTZ) {
 
-      mbox->mraid_poll = 0; 
-      mbox->mraid_ack = 0; 
-      WRINDOOR(megaCfg, virt_to_bus(megaCfg->mbox) | 0x1);
+      mbox->mraid_poll = 0;
+      mbox->mraid_ack = 0;
+      WRINDOOR (megaCfg, virt_to_bus (megaCfg->mbox) | 0x1);
 
-      while((cmdDone=RDOUTDOOR(megaCfg)) != 0x10001234);
-      WROUTDOOR(megaCfg, cmdDone);
+      while ((cmdDone = RDOUTDOOR (megaCfg)) != 0x10001234);
+      WROUTDOOR (megaCfg, cmdDone);
 
+      spin_unlock_irqrestore(&mega_lock,flags);
       if (pScb) {
-	mega_cmd_done(megaCfg,pScb, mbox->status);
-	mega_rundoneq();
+	mega_cmd_done (megaCfg, pScb, mbox->status);
+	mega_rundoneq ();
       }
 
-      WRINDOOR(megaCfg,virt_to_bus(megaCfg->mbox) | 0x2);
-      while(RDINDOOR(megaCfg) & 0x2);
+      WRINDOOR (megaCfg, virt_to_bus (megaCfg->mbox) | 0x2);
+      while (RDINDOOR (megaCfg) & 0x2);
 
       megaCfg->flag &= ~PENDING;
+
     }
     else {
-      DISABLE_INTR(megaCfg->host->io_port);
-      ISSUE_COMMAND(megaCfg->host->io_port);
-      
-      while(!((byte=READ_PORT(megaCfg->host->io_port,INTR_PORT))&INTR_VALID));
-      WRITE_PORT(megaCfg->host->io_port, INTR_PORT, byte);
-      
-      ENABLE_INTR(megaCfg->host->io_port);
-      CLEAR_INTR(megaCfg->host->io_port);
-      
+      DISABLE_INTR (megaCfg->host->io_port);
+      ISSUE_COMMAND (megaCfg->host->io_port);
+
+      while (!((byte = READ_PORT (megaCfg->host->io_port, INTR_PORT)) & INTR_VALID));
+      WRITE_PORT (megaCfg->host->io_port, INTR_PORT, byte);
+
+
+      ENABLE_INTR (megaCfg->host->io_port);
+      CLEAR_INTR (megaCfg->host->io_port);
+      megaCfg->flag &= ~PENDING;
+      spin_unlock_irqrestore(&mega_lock,flags);
+
       if (pScb) {
-	mega_cmd_done(megaCfg,pScb, mbox->status);
-	mega_rundoneq();
+	mega_cmd_done (megaCfg, pScb, mbox->status);
+	mega_rundoneq ();
       }
-      megaCfg->flag &= ~PENDING;
+      else {
+	TRACE (("Error: NULL pScb!\n"));
+      }
+
     }
   }
 
@@ -817,40 +927,40 @@
 /*-------------------------------------------------------------------
  * Copies data to SGLIST
  *-------------------------------------------------------------------*/
-static int build_sglist(mega_host_config *megaCfg, mega_scb *scb, 
-			u_long *buffer, u_long *length)
+static int build_sglist (mega_host_config * megaCfg, mega_scb * scb,
+	      u_long * buffer, u_long * length)
 {
   struct scatterlist *sgList;
   int idx;
 
   /* Scatter-gather not used */
   if (scb->SCpnt->use_sg == 0) {
-    *buffer = virt_to_bus(scb->SCpnt->request_buffer);
-    *length = (u_long)scb->SCpnt->request_bufflen;
+    *buffer = virt_to_bus (scb->SCpnt->request_buffer);
+    *length = (u_long) scb->SCpnt->request_bufflen;
     return 0;
   }
 
-  sgList = (struct scatterlist *)scb->SCpnt->buffer;
+  sgList = (struct scatterlist *) scb->SCpnt->buffer;
   if (scb->SCpnt->use_sg == 1) {
-    *buffer = virt_to_bus(sgList[0].address);
-    *length = (u_long)sgList[0].length;
+    *buffer = virt_to_bus (sgList[0].address);
+    *length = (u_long) sgList[0].length;
     return 0;
   }
 
   /* Copy Scatter-Gather list info into controller structure */
-  for(idx=0; idx<scb->SCpnt->use_sg; idx++) {
-    scb->sgList[idx].address = virt_to_bus(sgList[idx].address);
-    scb->sgList[idx].length  = (u_long)sgList[idx].length;
+  for (idx = 0; idx < scb->SCpnt->use_sg; idx++) {
+    scb->sgList[idx].address = virt_to_bus (sgList[idx].address);
+    scb->sgList[idx].length = (u_long) sgList[idx].length;
   }
-  
+
   /* Reset pointer and length fields */
-  *buffer = virt_to_bus(scb->sgList);
+  *buffer = virt_to_bus (scb->sgList);
   *length = 0;
 
   /* Return count of SG requests */
   return scb->SCpnt->use_sg;
 }
-    
+
 /*--------------------------------------------------------------------
  * Initializes the adress of the controller's mailbox register
  *  The mailbox register is used to issue commands to the card.
@@ -867,25 +977,25 @@
  *   10 01 numstatus byte
  *   11 01 status byte
  *--------------------------------------------------------------------*/
-static int mega_register_mailbox(mega_host_config *megaCfg, u_long paddr)
+static int mega_register_mailbox (mega_host_config * megaCfg, u_long paddr)
 {
   /* align on 16-byte boundry */
   megaCfg->mbox = &megaCfg->mailbox;
-  megaCfg->mbox = (mega_mailbox *) ((((ulong)megaCfg->mbox) + 16)&0xfffffff0);
-  paddr = (paddr+16)&0xfffffff0;
+  megaCfg->mbox = (mega_mailbox *) ((((ulong) megaCfg->mbox) + 16) & 0xfffffff0);
+  paddr = (paddr + 16) & 0xfffffff0;
 
   /* Register mailbox area with the firmware */
   if (megaCfg->flag & BOARD_QUARTZ) {
   }
   else {
-    WRITE_PORT(megaCfg->host->io_port, MBOX_PORT0, paddr         & 0xFF);
-    WRITE_PORT(megaCfg->host->io_port, MBOX_PORT1, (paddr >>  8) & 0xFF);
-    WRITE_PORT(megaCfg->host->io_port, MBOX_PORT2, (paddr >> 16) & 0xFF);
-    WRITE_PORT(megaCfg->host->io_port, MBOX_PORT3, (paddr >> 24) & 0xFF);
-    WRITE_PORT(megaCfg->host->io_port, ENABLE_MBOX_REGION, ENABLE_MBOX_BYTE);
-    
-    CLEAR_INTR(megaCfg->host->io_port);
-    ENABLE_INTR(megaCfg->host->io_port);
+    WRITE_PORT (megaCfg->host->io_port, MBOX_PORT0, paddr & 0xFF);
+    WRITE_PORT (megaCfg->host->io_port, MBOX_PORT1, (paddr >> 8) & 0xFF);
+    WRITE_PORT (megaCfg->host->io_port, MBOX_PORT2, (paddr >> 16) & 0xFF);
+    WRITE_PORT (megaCfg->host->io_port, MBOX_PORT3, (paddr >> 24) & 0xFF);
+    WRITE_PORT (megaCfg->host->io_port, ENABLE_MBOX_REGION, ENABLE_MBOX_BYTE);
+
+    CLEAR_INTR (megaCfg->host->io_port);
+    ENABLE_INTR (megaCfg->host->io_port);
   }
   return 0;
 }
@@ -893,77 +1003,79 @@
 /*-------------------------------------------------------------------
  * Issue an adapter info query to the controller
  *-------------------------------------------------------------------*/
-static int mega_i_query_adapter(mega_host_config *megaCfg)
+static int mega_i_query_adapter (mega_host_config * megaCfg)
 {
   mega_RAIDINQ *adapterInfo;
   mega_mailbox *mbox;
-  u_char        mboxData[16];
-  u_long        paddr;
+  u_char mboxData[16];
+  u_long paddr;
 
-  spin_lock_init(&mega_lock);
+  spin_lock_init (&mega_lock);
   /* Initialize adapter inquiry */
-  paddr = virt_to_bus(megaCfg->mega_buffer);
-  mbox  = (mega_mailbox *)mboxData;
+  paddr = virt_to_bus (megaCfg->mega_buffer);
+  mbox = (mega_mailbox *) mboxData;
 
-  memset((void *)megaCfg->mega_buffer, 0, sizeof(megaCfg->mega_buffer));
-  memset(mbox, 0, 16);
+  memset ((void *) megaCfg->mega_buffer, 0, sizeof (megaCfg->mega_buffer));
+  memset (mbox, 0, 16);
 
   /* Initialize mailbox registers */
-  mbox->cmd      = MEGA_MBOXCMD_ADAPTERINQ;
+  mbox->cmd = MEGA_MBOXCMD_ADAPTERINQ;
   mbox->xferaddr = paddr;
 
   /* Issue a blocking command to the card */
-  MegaIssueCmd(megaCfg, mboxData, NULL, 0);
-  
+  MegaIssueCmd (megaCfg, mboxData, NULL, 0);
+
   /* Initialize host/local structures with Adapter info */
-  adapterInfo = (mega_RAIDINQ *)megaCfg->mega_buffer;
+  adapterInfo = (mega_RAIDINQ *) megaCfg->mega_buffer;
   megaCfg->host->max_channel = adapterInfo->AdpInfo.ChanPresent;
-  megaCfg->host->max_id      = adapterInfo->AdpInfo.MaxTargPerChan;
-  megaCfg->numldrv           = adapterInfo->LogdrvInfo.NumLDrv;
+/*  megaCfg->host->max_id = adapterInfo->AdpInfo.MaxTargPerChan; */
+  megaCfg->host->max_id = 9; /* max logical drives + 1 */
+  megaCfg->numldrv = adapterInfo->LogdrvInfo.NumLDrv;
 
 #if 0
-  printk(KERN_DEBUG "---- Logical drive info ----\n");
-  for(i=0; i<megaCfg->numldrv; i++) {
-    printk(KERN_DEBUG "%d: size: %ld prop: %x state: %x\n",i,
-	   adapterInfo->LogdrvInfo.LDrvSize[i],
-	   adapterInfo->LogdrvInfo.LDrvProp[i],
-	   adapterInfo->LogdrvInfo.LDrvState[i]);
-  }
-  printk(KERN_DEBUG "---- Physical drive info ----\n");
-  for(i=0; i<MAX_PHYSICAL_DRIVES; i++) {
-    if (i && !(i % 8)) printk("\n");
-    printk("%d: %x   ", i, adapterInfo->PhysdrvInfo.PDrvState[i]);
+  printk ("KERN_DEBUG ---- Logical drive info ----\n");
+  for (i = 0; i < megaCfg->numldrv; i++) {
+    printk ("%d: size: %ld prop: %x state: %x\n", i,
+	    adapterInfo->LogdrvInfo.LDrvSize[i],
+	    adapterInfo->LogdrvInfo.LDrvProp[i],
+	    adapterInfo->LogdrvInfo.LDrvState[i]);
+  }
+  printk (KERN_DEBUG "---- Physical drive info ----\n");
+  for (i = 0; i < MAX_PHYSICAL_DRIVES; i++) {
+    if (i && !(i % 8))
+      printk ("\n");
+    printk ("%d: %x   ", i, adapterInfo->PhysdrvInfo.PDrvState[i]);
   }
-  printk("\n");
+  printk ("\n");
 #endif
 
   megaCfg->max_cmds = adapterInfo->AdpInfo.MaxConcCmds;
 
-#ifdef HP            /* use HP firmware and bios version encoding */
-      sprintf(megaCfg->fwVer,"%c%d%d.%d%d",
-          adapterInfo->AdpInfo.FwVer[2],
-          adapterInfo->AdpInfo.FwVer[1] >> 8,
-          adapterInfo->AdpInfo.FwVer[1] & 0x0f,
-          adapterInfo->AdpInfo.FwVer[2] >> 8,
-          adapterInfo->AdpInfo.FwVer[2] & 0x0f);
-      sprintf(megaCfg->biosVer,"%c%d%d.%d%d",
-          adapterInfo->AdpInfo.BiosVer[2],
-          adapterInfo->AdpInfo.BiosVer[1] >> 8,
-          adapterInfo->AdpInfo.BiosVer[1] & 0x0f,
-          adapterInfo->AdpInfo.BiosVer[2] >> 8,
-          adapterInfo->AdpInfo.BiosVer[2] & 0x0f);
+#ifdef HP			/* use HP firmware and bios version encoding */
+  sprintf (megaCfg->fwVer, "%c%d%d.%d%d",
+	   adapterInfo->AdpInfo.FwVer[2],
+	   adapterInfo->AdpInfo.FwVer[1] >> 8,
+	   adapterInfo->AdpInfo.FwVer[1] & 0x0f,
+	   adapterInfo->AdpInfo.FwVer[2] >> 8,
+	   adapterInfo->AdpInfo.FwVer[2] & 0x0f);
+  sprintf (megaCfg->biosVer, "%c%d%d.%d%d",
+	   adapterInfo->AdpInfo.BiosVer[2],
+	   adapterInfo->AdpInfo.BiosVer[1] >> 8,
+	   adapterInfo->AdpInfo.BiosVer[1] & 0x0f,
+	   adapterInfo->AdpInfo.BiosVer[2] >> 8,
+	   adapterInfo->AdpInfo.BiosVer[2] & 0x0f);
 #else
-      memcpy(megaCfg->fwVer, adapterInfo->AdpInfo.FwVer, 4);
-      megaCfg->fwVer[4] = 0;
+  memcpy (megaCfg->fwVer, adapterInfo->AdpInfo.FwVer, 4);
+  megaCfg->fwVer[4] = 0;
 
-      memcpy(megaCfg->biosVer, adapterInfo->AdpInfo.BiosVer, 4);
-      megaCfg->biosVer[4] = 0;
+  memcpy (megaCfg->biosVer, adapterInfo->AdpInfo.BiosVer, 4);
+  megaCfg->biosVer[4] = 0;
 #endif
 
-  printk(KERN_INFO "megaraid: [%s:%s] detected %d logical drives" CRLFSTR,
-	 megaCfg->fwVer,
-	 megaCfg->biosVer,
-	 megaCfg->numldrv);
+  printk (KERN_INFO "megaraid: [%s:%s] detected %d logical drives" CRLFSTR,
+	  megaCfg->fwVer,
+	  megaCfg->biosVer,
+	  megaCfg->numldrv);
   return 0;
 }
 
@@ -976,47 +1088,56 @@
 /*----------------------------------------------------------
  * Returns data to be displayed in /proc/scsi/megaraid/X
  *----------------------------------------------------------*/
-int megaraid_proc_info(char *buffer, char **start, off_t offset,
-		       int length, int inode, int inout)
+int megaraid_proc_info (char *buffer, char **start, off_t offset,
+		    int length, int inode, int inout)
 {
   *start = buffer;
   return 0;
 }
 
-int findCard(Scsi_Host_Template *pHostTmpl, 
-	     u_short pciVendor, u_short pciDev,
-	     long flag)
+int findCard (Scsi_Host_Template * pHostTmpl,
+	  u_short pciVendor, u_short pciDev,
+	  long flag)
 {
   mega_host_config *megaCfg;
   struct Scsi_Host *host;
-  u_char            pciBus, pciDevFun, megaIrq;
-  u_long            megaBase;
-  u_short           pciIdx = 0;
+  u_char pciBus, pciDevFun, megaIrq;
+  u_long megaBase;
+  u_short pciIdx = 0;
 
 #if LINUX_VERSION_CODE < 0x20100
-  while(!pcibios_find_device(pciVendor, pciDev, pciIdx,&pciBus,&pciDevFun)) {
+  while (!pcibios_find_device (pciVendor, pciDev, pciIdx, &pciBus, &pciDevFun)) {
+    if (flag & BOARD_QUARTZ) {
+      u_int magic;
+      pcibios_read_config_dword (pciBus, pciDevFun,
+				 PCI_CONF_AMISIG,
+				 &magic);
+      if (magic != AMI_SIGNATURE) {
+	continue;		/* not an AMI board */
+      }
+    }
 #else
-  struct pci_dev   *pdev=pci_devices;
+  struct pci_dev *pdev = pci_devices;
 
-  while((pdev = pci_find_device(pciVendor, pciDev, pdev))) {
+  while ((pdev = pci_find_device (pciVendor, pciDev, pdev))) {
     pciBus = pdev->bus->number;
     pciDevFun = pdev->devfn;
 #endif
-    printk(KERN_INFO "megaraid: found 0x%4.04x:0x%4.04x:idx %d:bus %d:slot %d:fun %d\n",
-	   pciVendor, 
-	   pciDev, 
-	   pciIdx, pciBus, 
-	   PCI_SLOT(pciDevFun), 
-	   PCI_FUNC(pciDevFun));
-    
+    printk (KERN_INFO "megaraid: found 0x%4.04x:0x%4.04x:idx %d:bus %d:slot %d:fun %d\n",
+	    pciVendor,
+	    pciDev,
+	    pciIdx, pciBus,
+	    PCI_SLOT (pciDevFun),
+	    PCI_FUNC (pciDevFun));
+
     /* Read the base port and IRQ from PCI */
 #if LINUX_VERSION_CODE < 0x20100
-    pcibios_read_config_dword(pciBus, pciDevFun,
-			     PCI_BASE_ADDRESS_0,
-			     (u_int *)&megaBase);
-    pcibios_read_config_byte(pciBus, pciDevFun,			       
-			     PCI_INTERRUPT_LINE,
-			     &megaIrq);
+    pcibios_read_config_dword (pciBus, pciDevFun,
+			       PCI_BASE_ADDRESS_0,
+			       (u_int *) & megaBase);
+    pcibios_read_config_byte (pciBus, pciDevFun,
+			      PCI_INTERRUPT_LINE,
+			      &megaIrq);
 #else
     megaBase = pdev->base_address[0];
     megaIrq = pdev->irq;
@@ -1025,7 +1146,7 @@
 
     if (flag & BOARD_QUARTZ) {
       megaBase &= PCI_BASE_ADDRESS_MEM_MASK;
-      megaBase = (long) ioremap(megaBase,128);
+      megaBase = (long) ioremap (megaBase, 128);
     }
     else {
       megaBase &= PCI_BASE_ADDRESS_IO_MASK;
@@ -1033,47 +1154,47 @@
     }
 
     /* Initialize SCSI Host structure */
-    host    = scsi_register(pHostTmpl, sizeof(mega_host_config));
-    megaCfg = (mega_host_config *)host->hostdata;
-    memset(megaCfg, 0, sizeof(mega_host_config));
-
-    printk(KERN_INFO " scsi%d: Found a MegaRAID controller at 0x%x, IRQ: %d" CRLFSTR, 
-	   host->host_no, (u_int)megaBase, megaIrq);
-    
+    host = scsi_register (pHostTmpl, sizeof (mega_host_config));
+    megaCfg = (mega_host_config *) host->hostdata;
+    memset (megaCfg, 0, sizeof (mega_host_config));
+
+    printk (KERN_INFO " scsi%d: Found a MegaRAID controller at 0x%x, IRQ: %d" CRLFSTR,
+	    host->host_no, (u_int) megaBase, megaIrq);
+
     /* Copy resource info into structure */
-    megaCfg->flag            = flag;
-    megaCfg->host            = host;
-    megaCfg->base            = megaBase;
-    megaCfg->host->irq       = megaIrq;
-    megaCfg->host->io_port   = megaBase;
+    megaCfg->flag = flag;
+    megaCfg->host = host;
+    megaCfg->base = megaBase;
+    megaCfg->host->irq = megaIrq;
+    megaCfg->host->io_port = megaBase;
     megaCfg->host->n_io_port = 16;
     megaCfg->host->unique_id = (pciBus << 8) | pciDevFun;
-    megaCtlrs[numCtlrs++]    = megaCfg;
+    megaCtlrs[numCtlrs++] = megaCfg;
 
     if (flag != BOARD_QUARTZ) {
       /* Request our IO Range */
-      if (check_region(megaBase, 16)) {
-	printk(KERN_WARNING "megaraid: Couldn't register I/O range!" CRLFSTR);
-	scsi_unregister(host);
+      if (check_region (megaBase, 16)) {
+	printk (KERN_WARNING "megaraid: Couldn't register I/O range!" CRLFSTR);
+	scsi_unregister (host);
 	continue;
       }
-      request_region(megaBase, 16, "megaraid");
+      request_region (megaBase, 16, "megaraid");
     }
 
     /* Request our IRQ */
-    if (request_irq(megaIrq, megaraid_isr, SA_SHIRQ, 
-		    "megaraid", megaCfg)) {
-      printk(KERN_WARNING "megaraid: Couldn't register IRQ %d!" CRLFSTR,
-	     megaIrq);
-      scsi_unregister(host);
+    if (request_irq (megaIrq, megaraid_isr, SA_SHIRQ,
+		     "megaraid", megaCfg)) {
+      printk (KERN_WARNING "megaraid: Couldn't register IRQ %d!" CRLFSTR,
+	      megaIrq);
+      scsi_unregister (host);
       continue;
     }
 
-    mega_register_mailbox(megaCfg, virt_to_bus((void*)&megaCfg->mailbox));
-    mega_i_query_adapter(megaCfg);
+    mega_register_mailbox (megaCfg, virt_to_bus ((void *) &megaCfg->mailbox));
+    mega_i_query_adapter (megaCfg);
 
     /* Initialize SCBs */
-    initSCB(megaCfg);
+    initSCB (megaCfg);
 
   }
   return pciIdx;
@@ -1082,23 +1203,22 @@
 /*---------------------------------------------------------
  * Detects if a megaraid controller exists in this system
  *---------------------------------------------------------*/
-int megaraid_detect(Scsi_Host_Template *pHostTmpl)
+int megaraid_detect (Scsi_Host_Template * pHostTmpl)
 {
   int count = 0;
 
   pHostTmpl->proc_dir = &proc_scsi_megaraid;
 
 #if LINUX_VERSION_CODE < 0x20100
-  if (!pcibios_present()) 
-    {
-      printk(KERN_WARNING "megaraid: PCI bios not present." CRLFSTR);
-      return 0;
-    }
+  if (!pcibios_present ()) {
+    printk (KERN_WARNING "megaraid: PCI bios not present." CRLFSTR);
+    return 0;
+  }
 #endif
 
-  count += findCard(pHostTmpl, 0x101E, 0x9010, 0);
-  count += findCard(pHostTmpl, 0x101E, 0x9060, 0);
-  count += findCard(pHostTmpl, 0x8086, 0x1960, BOARD_QUARTZ);
+  count += findCard (pHostTmpl, 0x101E, 0x9010, 0);
+  count += findCard (pHostTmpl, 0x101E, 0x9060, 0);
+  count += findCard (pHostTmpl, 0x8086, 0x1960, BOARD_QUARTZ);
 
   return count;
 }
@@ -1106,33 +1226,40 @@
 /*---------------------------------------------------------------------
  * Release the controller's resources
  *---------------------------------------------------------------------*/
-int megaraid_release(struct Scsi_Host *pSHost)
+int megaraid_release (struct Scsi_Host *pSHost)
 {
   mega_host_config *megaCfg;
-  mega_mailbox         *mbox;
-  u_char                mboxData[16];
+  mega_mailbox *mbox;
+  u_char mboxData[16];
+  int i;
 
-  megaCfg = (mega_host_config*)pSHost->hostdata;
-  mbox    = (mega_mailbox *)mboxData;
+  megaCfg = (mega_host_config *) pSHost->hostdata;
+  mbox = (mega_mailbox *) mboxData;
 
   /* Flush cache to disk */
-  memset(mbox, 0, 16);
+  memset (mbox, 0, 16);
   mboxData[0] = 0xA;
 
   /* Issue a blocking (interrupts disabled) command to the card */
-  MegaIssueCmd(megaCfg, mboxData, NULL, 0);
+  MegaIssueCmd (megaCfg, mboxData, NULL, 0);
 
-  schedule();
+  schedule ();
 
   /* Free our resources */
   if (megaCfg->flag & BOARD_QUARTZ) {
-      iounmap((void *)megaCfg->base);
-  } else {
-      release_region(megaCfg->host->io_port, 16);
-  }
-  free_irq(megaCfg->host->irq, megaCfg); /* Must be freed first, otherwise
-                                            extra interrupt is generated */
-  scsi_unregister(pSHost);
+    iounmap ((void *) megaCfg->base);
+  }
+  else {
+    release_region (megaCfg->host->io_port, 16);
+  }
+  free_irq (megaCfg->host->irq, megaCfg);	/* Must be freed first, otherwise
+
+						   extra interrupt is generated */
+  for (i = 0; i < megaCfg->max_cmds; i++) {
+    if (megaCfg->scbList[i].sgList)
+      kfree (megaCfg->scbList[i].sgList);	/* free sgList */
+  }
+  scsi_unregister (pSHost);
 
   return 0;
 }
@@ -1140,20 +1267,20 @@
 /*----------------------------------------------
  * Get information about the card/driver 
  *----------------------------------------------*/
-const char *megaraid_info(struct Scsi_Host *pSHost)
+const char * megaraid_info (struct Scsi_Host *pSHost)
 {
-  static char           buffer[512];
-  mega_host_config  *megaCfg;
-  mega_RAIDINQ          *adapterInfo;
+  static char buffer[512];
+  mega_host_config *megaCfg;
+  mega_RAIDINQ *adapterInfo;
 
-  megaCfg     = (mega_host_config *)pSHost->hostdata;
-  adapterInfo = (mega_RAIDINQ *)megaCfg->mega_buffer;
+  megaCfg = (mega_host_config *) pSHost->hostdata;
+  adapterInfo = (mega_RAIDINQ *) megaCfg->mega_buffer;
 
-  sprintf(buffer, "AMI MegaRAID %s %d commands %d targs %d chans",
-	  megaCfg->fwVer,
-	  adapterInfo->AdpInfo.MaxConcCmds,
-	  megaCfg->host->max_id,
-	  megaCfg->host->max_channel);
+  sprintf (buffer, "AMI MegaRAID %s %d commands %d targs %d chans",
+	   megaCfg->fwVer,
+	   adapterInfo->AdpInfo.MaxConcCmds,
+	   megaCfg->host->max_id,
+	   megaCfg->host->max_channel);
   return buffer;
 }
 
@@ -1172,29 +1299,29 @@
  *   10 01 numstatus byte
  *   11 01 status byte 
  *-----------------------------------------------------------------*/
-int megaraid_queue(Scsi_Cmnd *SCpnt, void (*pktComp)(Scsi_Cmnd *))
+int megaraid_queue (Scsi_Cmnd * SCpnt, void (*pktComp) (Scsi_Cmnd *))
 {
   mega_host_config *megaCfg;
-  mega_scb         *pScb;
+  mega_scb *pScb;
 
-  megaCfg = (mega_host_config *)SCpnt->host->hostdata;
+  megaCfg = (mega_host_config *) SCpnt->host->hostdata;
 
   if (!(megaCfg->flag & (1L << SCpnt->channel))) {
-    printk(KERN_INFO "scsi%d: scanning channel %c for devices.\n",
-	   megaCfg->host->host_no,
-	   SCpnt->channel + 'A');
+    printk (KERN_INFO "scsi%d: scanning channel %c for devices.\n",
+	    megaCfg->host->host_no,
+	    SCpnt->channel + 'A');
     megaCfg->flag |= (1L << SCpnt->channel);
   }
 
   SCpnt->scsi_done = pktComp;
 
   /* Allocate and build a SCB request */
-  if ((pScb = mega_build_cmd(megaCfg, SCpnt)) != NULL) {
+  if ((pScb = mega_build_cmd (megaCfg, SCpnt)) != NULL) {
     /* Add SCB to the head of the pending queue */
-    ENQUEUE(pScb, mega_scb, qPending, next);
+    ENQUEUE (pScb, mega_scb, qPending, next);
 
     /* Issue the command to the card */
-      mega_runque(NULL);
+    mega_runque (NULL);
   }
 
   return 0;
@@ -1202,37 +1329,44 @@
 
 /*----------------------------------------------------------------------
  * Issue a blocking command to the controller
- *
- * Note - this isnt 2.0.x SMP safe
  *----------------------------------------------------------------------*/
-volatile static int internal_done_flag    = 0;
+volatile static int internal_done_flag = 0;
 volatile static int internal_done_errcode = 0;
 
-static void internal_done(Scsi_Cmnd *SCpnt)
+static void internal_done (Scsi_Cmnd * SCpnt)
 {
   internal_done_errcode = SCpnt->result;
   internal_done_flag++;
 }
 
 /*
- *	This seems dangerous in an SMP environment because 
- *	while spinning on internal_done_flag in 2.0.x SMP
- *	no IRQ's will be taken, including those that might
- *	be needed to clear this.
- *
- *	I think this should be using a wait queue ?
- *				-- AC
+ *      This seems dangerous in an SMP environment because
+ *      while spinning on internal_done_flag in 2.0.x SMP
+ *      no IRQ's will be taken, including those that might
+ *      be needed to clear this.
+ *
+ *      I think this should be using a wait queue ?
+ *                              -- AC
+ */      
+
+/*
+ *      I'll probably fix this in the next version, but
+ *      megaraid_command() will never get called since can_queue is set,
+ *      except maybe in a *really* old kernel in which case it's very
+ *      unlikely they'd be using SMP anyway.  Really this function is
+ *      just here for completeness.
+ *                              - JLJ
  */
- 
-int megaraid_command(Scsi_Cmnd *SCpnt)
+
+int megaraid_command (Scsi_Cmnd * SCpnt)
 {
   internal_done_flag = 0;
 
   /* Queue command, and wait until it has completed */
-  megaraid_queue(SCpnt, internal_done);
+  megaraid_queue (SCpnt, internal_done);
 
-  while(!internal_done_flag)
-    barrier();
+  while (!internal_done_flag)
+    barrier ();
 
   return internal_done_errcode;
 }
@@ -1240,67 +1374,67 @@
 /*---------------------------------------------------------------------
  * Abort a previous SCSI request
  *---------------------------------------------------------------------*/
-int megaraid_abort(Scsi_Cmnd *SCpnt)
+int megaraid_abort (Scsi_Cmnd * SCpnt)
 {
   mega_host_config *megaCfg;
-  int       idx;
-  long      flags;
+  int idx;
+  long flags;
 
-  spin_lock_irqsave(&mega_lock,flags);
+  spin_lock_irqsave (&mega_lock, flags);
 
-  megaCfg = (mega_host_config *)SCpnt->host->hostdata;
+  megaCfg = (mega_host_config *) SCpnt->host->hostdata;
 
-  TRACE(("ABORT!!! %.08lx %.02x <%d.%d.%d>\n",
-	 SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel, SCpnt->target, 
-	 SCpnt->lun));
+  TRACE (("ABORT!!! %.08lx %.02x <%d.%d.%d>\n",
+	SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel, SCpnt->target,
+	  SCpnt->lun));
   /*
    * Walk list of SCBs for any that are still outstanding
    */
-  for(idx=0; idx<megaCfg->max_cmds; idx++) {
+  for (idx = 0; idx < megaCfg->max_cmds; idx++) {
     if (megaCfg->scbList[idx].idx >= 0) {
       if (megaCfg->scbList[idx].SCpnt == SCpnt) {
-	freeSCB(&megaCfg->scbList[idx]);
+	freeSCB (&megaCfg->scbList[idx]);
 
-	SCpnt->result = (DID_RESET << 16) | (SUGGEST_RETRY<<24);
-	callDone(SCpnt);
+	SCpnt->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24);
+	callDone (SCpnt);
       }
     }
   }
-  spin_unlock_irqrestore(&mega_lock,flags);
+  spin_unlock_irqrestore (&mega_lock, flags);
   return SCSI_ABORT_SNOOZE;
 }
 
 /*---------------------------------------------------------------------
  * Reset a previous SCSI request
  *---------------------------------------------------------------------*/
-int megaraid_reset(Scsi_Cmnd *SCpnt, unsigned int rstflags)
+int megaraid_reset (Scsi_Cmnd * SCpnt, unsigned int rstflags)
 {
   mega_host_config *megaCfg;
-  int       idx;
-  long      flags;
+  int idx;
+  long flags;
 
-  spin_lock_irqsave(&mega_lock,flags);
+  spin_lock_irqsave (&mega_lock, flags);
 
-  megaCfg = (mega_host_config *)SCpnt->host->hostdata;
+  megaCfg = (mega_host_config *) SCpnt->host->hostdata;
 
-  TRACE(("RESET: %.08lx %.02x <%d.%d.%d>\n",
-	 SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel, SCpnt->target, 
-	 SCpnt->lun));
+  TRACE (("RESET: %.08lx %.02x <%d.%d.%d>\n",
+	SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel, SCpnt->target,
+	  SCpnt->lun));
 
   /*
    * Walk list of SCBs for any that are still outstanding
    */
-  for(idx=0; idx<megaCfg->max_cmds; idx++) {
+  for (idx = 0; idx < megaCfg->max_cmds; idx++) {
     if (megaCfg->scbList[idx].idx >= 0) {
       SCpnt = megaCfg->scbList[idx].SCpnt;
-      freeSCB(&megaCfg->scbList[idx]);
-      SCpnt->result = (DID_RESET << 16) | (SUGGEST_RETRY<<24);
-      callDone(SCpnt);
+      freeSCB (&megaCfg->scbList[idx]);
+      SCpnt->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24);
+      callDone (SCpnt);
     }
   }
-  spin_unlock_irqrestore(&mega_lock,flags);
+  spin_unlock_irqrestore (&mega_lock, flags);
   return SCSI_RESET_PUNT;
-} 
+}
 
 /*-------------------------------------------------------------
  * Return the disk geometry for a particular disk
@@ -1312,23 +1446,23 @@
  *     geom[1] = sectors
  *     geom[2] = cylinders
  *-------------------------------------------------------------*/
-int megaraid_biosparam(Disk *disk, kdev_t dev, int *geom)
+int megaraid_biosparam (Disk * disk, kdev_t dev, int *geom)
 {
-  int                   heads, sectors, cylinders;
+  int heads, sectors, cylinders;
   mega_host_config *megaCfg;
 
   /* Get pointer to host config structure */
-  megaCfg = (mega_host_config *)disk->device->host->hostdata;
+  megaCfg = (mega_host_config *) disk->device->host->hostdata;
 
   /* Default heads (64) & sectors (32) */
-  heads     = 64;
-  sectors   = 32;
+  heads = 64;
+  sectors = 32;
   cylinders = disk->capacity / (heads * sectors);
 
   /* Handle extended translation size for logical drives > 1Gb */
   if (disk->capacity >= 0x200000) {
-    heads     = 255;
-    sectors   = 63;
+    heads = 255;
+    sectors = 63;
     cylinders = disk->capacity / (heads * sectors);
   }
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/megaraid.h linux/drivers/scsi/megaraid.h
--- linux.vanilla/drivers/scsi/megaraid.h	Sun Dec  6 00:14:43 1998
+++ linux/drivers/scsi/megaraid.h	Mon Mar 22 00:54:37 1999
@@ -1,6 +1,10 @@
 #ifndef __MEGARAID_H__
 #define __MEGARAID_H__
 
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+
 #define IN_ISR                  0x80000000L
 #define NO_INTR                 0x40000000L
 #define IN_TIMEOUT              0x20000000L
@@ -18,7 +22,7 @@
 
 #define MEGA_CMD_TIMEOUT        10
 
-#define MAX_SGLIST              20
+#define MAX_SGLIST              17
 #define MAX_COMMANDS            254
 
 #define MAX_LOGICAL_DRIVES      8
@@ -94,6 +98,8 @@
 
 #define PCI_CONF_BASE_ADDR_OFFSET  0x10
 #define PCI_CONF_IRQ_OFFSET        0x3c
+#define PCI_CONF_AMISIG            0xa0
+#define AMI_SIGNATURE              0x11223344
 
 #if LINUX_VERSION_CODE < 0x20100
 #define MEGARAID \
@@ -111,14 +117,14 @@
     megaraid_reset,                     /* Reset Command Function    */\
     NULL,                               /* Slave Attach Function     */\
     megaraid_biosparam,                 /* Disk BIOS Parameters      */\
-    1,                                  /* # of cmds that can be\
+    254,                                /* # of cmds that can be\
                                            outstanding at any time */\
     7,                                  /* HBA Target ID             */\
     MAX_SGLIST,                         /* Scatter/Gather Table Size */\
-    1,                                  /* SCSI Commands per LUN     */\
+    64,                                 /* SCSI Commands per LUN     */\
     0,                                  /* Present                   */\
     0,                                  /* Default Unchecked ISA DMA */\
-    ENABLE_CLUSTERING }                 /* Enable Clustering         */
+    ENABLE_CLUSTERING }		/* Enable Clustering         */
 #else
 #define MEGARAID \
   {\
@@ -132,10 +138,10 @@
     abort:            megaraid_abort,          /* Abort Command Function    */\
     reset:            megaraid_reset,          /* Reset Command Function    */\
     bios_param:       megaraid_biosparam,      /* Disk BIOS Parameters      */\
-    can_queue:        255,                     /* Can Queue                 */\
+    can_queue:        254,                     /* Can Queue                 */\
     this_id:          7,                       /* HBA Target ID             */\
     sg_tablesize:     MAX_SGLIST,              /* Scatter/Gather Table Size */\
-    cmd_per_lun:      1,                       /* SCSI Commands per LUN     */\
+    cmd_per_lun:      64,                      /* SCSI Commands per LUN     */\
     present:          0,                       /* Present                   */\
     unchecked_isa_dma:0,                       /* Default Unchecked ISA DMA */\
     use_clustering:   ENABLE_CLUSTERING       /* Enable Clustering         */\
@@ -143,140 +149,150 @@
 #endif
 
 /* Structures */
-typedef struct _mega_ADP_INFO
-{
-  u_char    MaxConcCmds;
-  u_char    RbldRate;
-  u_char    MaxTargPerChan;
-  u_char    ChanPresent;
-  u_char    FwVer[4];
-  u_short   AgeOfFlash;
-  u_char    ChipSet;
-  u_char    DRAMSize;
-  u_char    CacheFlushInterval;
-  u_char    BiosVer[4];
-  u_char    resvd[7];
+typedef struct _mega_ADP_INFO {
+    u_char MaxConcCmds;
+    u_char RbldRate;
+    u_char MaxTargPerChan;
+    u_char ChanPresent;
+    u_char FwVer[4];
+    u_short AgeOfFlash;
+    u_char ChipSet;
+    u_char DRAMSize;
+    u_char CacheFlushInterval;
+    u_char BiosVer[4];
+    u_char resvd[7];
 } mega_ADP_INFO;
 
-typedef struct _mega_LDRV_INFO
-{
-  u_char   NumLDrv;
-  u_char   resvd[3];
-  u_long   LDrvSize[MAX_LOGICAL_DRIVES];
-  u_char   LDrvProp[MAX_LOGICAL_DRIVES];
-  u_char   LDrvState[MAX_LOGICAL_DRIVES];
+typedef struct _mega_LDRV_INFO {
+    u_char NumLDrv;
+    u_char resvd[3];
+    u_long LDrvSize[MAX_LOGICAL_DRIVES];
+    u_char LDrvProp[MAX_LOGICAL_DRIVES];
+    u_char LDrvState[MAX_LOGICAL_DRIVES];
 } mega_LDRV_INFO;
 
-typedef struct _mega_PDRV_INFO
-{
-  u_char   PDrvState[MAX_PHYSICAL_DRIVES];
-  u_char   resvd;
+typedef struct _mega_PDRV_INFO {
+    u_char PDrvState[MAX_PHYSICAL_DRIVES];
+    u_char resvd;
 } mega_PDRV_INFO;
 
 // RAID inquiry: Mailbox command 0x5
-typedef struct _mega_RAIDINQ
-{
-  mega_ADP_INFO    AdpInfo;
-  mega_LDRV_INFO   LogdrvInfo;
-  mega_PDRV_INFO   PhysdrvInfo;
+typedef struct _mega_RAIDINQ {
+    mega_ADP_INFO AdpInfo;
+    mega_LDRV_INFO LogdrvInfo;
+    mega_PDRV_INFO PhysdrvInfo;
 } mega_RAIDINQ;
 
 // Passthrough command: Mailbox command 0x3
-typedef struct mega_passthru
-{
-  u_char            timeout:3;              /* 0=6sec/1=60sec/2=10min/3=3hrs */
-  u_char            ars:1;
-  u_char            reserved:3;
-  u_char            islogical:1;
-  u_char            logdrv;                 /* if islogical == 1 */
-  u_char            channel;                /* if islogical == 0 */
-  u_char            target;                 /* if islogical == 0 */
-  u_char            queuetag;               /* unused */
-  u_char            queueaction;            /* unused */
-  u_char            cdb[MAX_CDB_LEN];
-  u_char            cdblen;
-  u_char            reqsenselen;
-  u_char            reqsensearea[MAX_REQ_SENSE_LEN];
-  u_char            numsgelements;
-  u_char            scsistatus;
-  u_long            dataxferaddr;
-  u_long            dataxferlen;
+typedef struct mega_passthru {
+    u_char timeout:3;		/* 0=6sec/1=60sec/2=10min/3=3hrs */
+    u_char ars:1;
+    u_char reserved:3;
+    u_char islogical:1;
+    u_char logdrv;		/* if islogical == 1 */
+    u_char channel;		/* if islogical == 0 */
+    u_char target;		/* if islogical == 0 */
+    u_char queuetag;		/* unused */
+    u_char queueaction;		/* unused */
+    u_char cdb[MAX_CDB_LEN];
+    u_char cdblen;
+    u_char reqsenselen;
+    u_char reqsensearea[MAX_REQ_SENSE_LEN];
+    u_char numsgelements;
+    u_char scsistatus;
+    u_long dataxferaddr;
+    u_long dataxferlen;
 } mega_passthru;
 
-typedef struct _mega_mailbox
-{
-  /* 0x0 */ u_char    cmd;
-  /* 0x1 */ u_char    cmdid;
-  /* 0x2 */ u_short   numsectors;
-  /* 0x4 */ u_long    lba;
-  /* 0x8 */ u_long    xferaddr;
-  /* 0xC */ u_char    logdrv;
-  /* 0xD */ u_char    numsgelements;
-  /* 0xE */ u_char    resvd;
-  /* 0xF */ u_char    busy;
-  /* 0x10*/ u_char    numstatus;
-  /* 0x11*/ u_char    status;
-  /* 0x12*/ u_char    completed[46];
-            u_char    mraid_poll;
-            u_char    mraid_ack;
-            u_char    pad[16];
+typedef struct _mega_mailbox {
+    /* 0x0 */ u_char cmd;
+    /* 0x1 */ u_char cmdid;
+    /* 0x2 */ u_short numsectors;
+    /* 0x4 */ u_long lba;
+    /* 0x8 */ u_long xferaddr;
+    /* 0xC */ u_char logdrv;
+    /* 0xD */ u_char numsgelements;
+    /* 0xE */ u_char resvd;
+    /* 0xF */ u_char busy;
+    /* 0x10 */ u_char numstatus;
+    /* 0x11 */ u_char status;
+    /* 0x12 */ u_char completed[46];
+    u_char mraid_poll;
+    u_char mraid_ack;
+    u_char pad[16];
 } mega_mailbox;
 
-typedef struct _mega_sglist
-{
-  u_long     address;
-  u_long     length;
+typedef struct _mega_ioctl_mbox {
+    /* 0x0 */ u_char cmd;
+    /* 0x1 */ u_char cmdid;
+    /* 0x2 */ u_char channel;
+    /* 0x3 */ u_char param;
+    /* 0x4 */ u_char pad[4];
+    /* 0x8 */ u_long xferaddr;
+    /* 0xC */ u_char logdrv;
+    /* 0xD */ u_char numsgelements;
+    /* 0xE */ u_char resvd;
+    /* 0xF */ u_char busy;
+    /* 0x10 */ u_char numstatus;
+    /* 0x11 */ u_char status;
+    /* 0x12 */ u_char completed[46];
+    u_char mraid_poll;
+    u_char mraid_ack;
+    u_char malign[16];
+} mega_ioctl_mbox;
+
+typedef struct _mega_sglist {
+    u_long address;
+    u_long length;
 } mega_sglist;
 
 /* Queued command data */
 typedef struct _mega_scb mega_scb;
 
-struct _mega_scb
-{
-  int             idx;
-  u_long          flag;
-  Scsi_Cmnd      *SCpnt;
-  u_char          mboxData[16];
-  mega_passthru   pthru;
-  mega_sglist    *sgList;
-  mega_scb       *next;
+struct _mega_scb {
+    int idx;
+    u_long flag;
+    Scsi_Cmnd *SCpnt;
+    u_char mboxData[16];
+    mega_passthru pthru;
+    mega_sglist *sgList;
+    mega_scb *next;
 };
 
 /* Per-controller data */
-typedef struct _mega_host_config
-{
-  u_char               numldrv;
-  u_long               flag;
-  u_long               base;
-
-  struct tq_struct     megaTq;
-
-  /* Host adapter parameters */
-  u_char               fwVer[7];
-  u_char               biosVer[7];
-
-  struct Scsi_Host     *host;
-
-  /* The following must be DMA-able!! */
-  volatile mega_mailbox *mbox;
-  volatile mega_mailbox mailbox;
-  volatile u_char       mega_buffer[2*1024L];
+typedef struct _mega_host_config {
+    u_char numldrv;
+    u_long flag;
+    u_long base;
+
+    struct tq_struct megaTq;
+
+    /* Host adapter parameters */
+    u_char fwVer[7];
+    u_char biosVer[7];
+
+    struct Scsi_Host *host;
+
+    /* The following must be DMA-able!! */
+    volatile mega_mailbox *mbox;
+    volatile mega_mailbox mailbox;
+    volatile u_char mega_buffer[2 * 1024L];
 
-  u_char                max_cmds;
-  mega_scb              scbList[MAX_COMMANDS];
+    u_char max_cmds;
+    mega_scb scbList[MAX_COMMANDS];
 } mega_host_config;
 
 extern struct proc_dir_entry proc_scsi_megaraid;
 
-const char *megaraid_info( struct Scsi_Host * );
-int        megaraid_detect( Scsi_Host_Template * );
-int        megaraid_release(struct Scsi_Host *);
-int        megaraid_command( Scsi_Cmnd * );
-int        megaraid_abort( Scsi_Cmnd * );
-int        megaraid_reset( Scsi_Cmnd *, unsigned int); 
-int        megaraid_queue( Scsi_Cmnd *, void (*done)(Scsi_Cmnd *) );
-int        megaraid_biosparam( Disk *, kdev_t, int * );
-int        megaraid_proc_info( char *buffer, char **start, off_t offset,
-			       int length, int hostno, int inout );
+const char *megaraid_info(struct Scsi_Host *);
+int megaraid_detect(Scsi_Host_Template *);
+int megaraid_release(struct Scsi_Host *);
+int megaraid_command(Scsi_Cmnd *);
+int megaraid_abort(Scsi_Cmnd *);
+int megaraid_reset(Scsi_Cmnd *, unsigned int);
+int megaraid_queue(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
+int megaraid_biosparam(Disk *, kdev_t, int *);
+int megaraid_proc_info(char *buffer, char **start, off_t offset,
+		       int length, int hostno, int inout);
 
 #endif
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/ncr53c8xx.c linux/drivers/scsi/ncr53c8xx.c
--- linux.vanilla/drivers/scsi/ncr53c8xx.c	Sun Dec  6 00:14:43 1998
+++ linux/drivers/scsi/ncr53c8xx.c	Mon Dec 14 18:11:09 1998
@@ -63,11 +63,17 @@
 **  August 18 1997 by Cort <cort@cs.nmt.edu>:
 **     Support for Power/PC (Big Endian).
 **
+**  June 20 1998 by Gerard Roudier <groudier@club-internet.fr>:
+**     Support for up to 64 tags per lun.
+**     O(1) everywhere (C and SCRIPTS) for normal cases.
+**     Low PCI traffic for command handling when on-chip RAM is present.
+**     Aggressive SCSI SCRIPTS optimizations.
+**
 *******************************************************************************
 */
 
 /*
-**	30 January 1998, version 2.5f.1
+**	December 14 1998, version 3.1e
 **
 **	Supported SCSI-II features:
 **	    Synchronous negotiation
@@ -92,9 +98,7 @@
 **		Shared IRQ (since linux-1.3.72)
 */
 
-#define SCSI_NCR_DEBUG_FLAGS	(0)		
-
-#define NCR_GETCC_WITHMSG
+#define SCSI_NCR_DEBUG_FLAGS	(0)
 
 /*==========================================================
 **
@@ -112,11 +116,13 @@
 #include <asm/dma.h>
 #include <asm/io.h>
 #include <asm/system.h>
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93)
+#include <asm/spinlock.h>
+#endif
 #include <linux/delay.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
 #include <linux/errno.h>
-#include <linux/bios32.h>
 #include <linux/pci.h>
 #include <linux/string.h>
 #include <linux/malloc.h>
@@ -127,11 +133,7 @@
 #include <linux/stat.h>
 
 #include <linux/version.h>
-#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
 #include <linux/blk.h>
-#else
-#include "../block/blk.h"
-#endif
 
 #if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,35)
 #include <linux/init.h>
@@ -144,6 +146,10 @@
 #endif
 #endif
 
+#if LINUX_VERSION_CODE <= LinuxVersionCode(2,1,92)
+#include <linux/bios32.h>
+#endif
+
 #include "scsi.h"
 #include "hosts.h"
 #include "constants.h"
@@ -152,14 +158,180 @@
 #include <linux/types.h>
 
 /*
-**	Define the BSD style u_int32 type
+**	Define BITS_PER_LONG for earlier linux versions.
+*/
+#ifndef	BITS_PER_LONG
+#if (~0UL) == 0xffffffffUL
+#define	BITS_PER_LONG	32
+#else
+#define	BITS_PER_LONG	64
+#endif
+#endif
+
+/*
+**	Define the BSD style u_int32 and u_int64 type.
+**	Are in fact u_int32_t and u_int64_t :-)
 */
 typedef u32 u_int32;
+typedef u64 u_int64;
 
 #include "ncr53c8xx.h"
 
 /*==========================================================
 **
+**	A la VMS/CAM-3 queue management.
+**	Implemented from linux list management.
+**
+**==========================================================
+*/
+
+typedef struct xpt_quehead {
+	struct xpt_quehead *flink;	/* Forward  pointer */
+	struct xpt_quehead *blink;	/* Backward pointer */
+} XPT_QUEHEAD;
+
+#define xpt_que_init(ptr) do { \
+	(ptr)->flink = (ptr); (ptr)->blink = (ptr); \
+} while (0)
+
+static inline void __xpt_que_add(struct xpt_quehead * new,
+	struct xpt_quehead * blink,
+	struct xpt_quehead * flink)
+{
+	flink->blink	= new;
+	new->flink	= flink;
+	new->blink	= blink;
+	blink->flink	= new;
+}
+
+static inline void __xpt_que_del(struct xpt_quehead * blink,
+	struct xpt_quehead * flink)
+{
+	flink->blink = blink;
+	blink->flink = flink;
+}
+
+static inline int xpt_que_empty(struct xpt_quehead *head)
+{
+	return head->flink == head;
+}
+
+static inline void xpt_que_splice(struct xpt_quehead *list,
+	struct xpt_quehead *head)
+{
+	struct xpt_quehead *first = list->flink;
+
+	if (first != list) {
+		struct xpt_quehead *last = list->blink;
+		struct xpt_quehead *at   = head->flink;
+
+		first->blink = head;
+		head->flink  = first;
+
+		last->flink = at;
+		at->blink   = last;
+	}
+}
+
+#define xpt_que_entry(ptr, type, member) \
+	((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+
+#define xpt_insque(new, pos)		__xpt_que_add(new, pos, (pos)->flink)
+
+#define xpt_remque(el)			__xpt_que_del((el)->blink, (el)->flink)
+
+#define xpt_insque_head(new, head)	__xpt_que_add(new, head, (head)->flink)
+
+static inline struct xpt_quehead *xpt_remque_head(struct xpt_quehead *head)
+{
+	struct xpt_quehead *elem = head->flink;
+
+	if (elem != head)
+		__xpt_que_del(head, elem->flink);
+	else
+		elem = 0;
+	return elem;
+}
+
+#define xpt_insque_tail(new, head)	__xpt_que_add(new, (head)->blink, head)
+
+static inline struct xpt_quehead *xpt_remque_tail(struct xpt_quehead *head)
+{
+	struct xpt_quehead *elem = head->blink;
+
+	if (elem != head)
+		__xpt_que_del(elem->blink, head);
+	else
+		elem = 0;
+	return elem;
+}
+
+/*==========================================================
+**
+**	The CCB done queue uses an array of CCB virtual 
+**	addresses. Empty entries are flagged using the bogus 
+**	virtual address 0xffffffff.
+**
+**	Since PCI ensures that only aligned DWORDs are accessed 
+**	atomically, 64 bit little-endian architecture requires 
+**	to test the high order DWORD of the entry to determine 
+**	if it is empty or valid.
+**
+**	BTW, I will make things differently as soon as I will 
+**	have a better idea, but this is simple and should work.
+**
+**==========================================================
+*/
+ 
+#define SCSI_NCR_CCB_DONE_SUPPORT
+#ifdef  SCSI_NCR_CCB_DONE_SUPPORT
+
+#define MAX_DONE 24
+#define CCB_DONE_EMPTY 0xffffffffUL
+
+/* All 32 bit architectures */
+#if BITS_PER_LONG == 32
+#define CCB_DONE_VALID(cp)  (((u_long) cp) != CCB_DONE_EMPTY)
+
+/* All > 32 bit (64 bit) architectures regardless endian-ness */
+#else
+#define CCB_DONE_VALID(cp)  \
+	((((u_long) cp) & 0xffffffff00000000ul) && 	\
+	 (((u_long) cp) & 0xfffffffful) != CCB_DONE_EMPTY)
+#endif
+
+#endif /* SCSI_NCR_CCB_DONE_SUPPORT */
+
+/*==========================================================
+**
+**	On x86 architecture, write buffers management does 
+**	not reorder writes to memory. So, using compiler 
+**	optimization barriers is enough to guarantee some 
+**	ordering when the CPU is writing data accessed by 
+**	the NCR.
+**	On Alpha architecture, explicit memory barriers have 
+**	to be used.
+**	Other architectures are defaulted to mb() macro if  
+**	defined, otherwise use compiler barrier.
+**
+**==========================================================
+*/
+
+#if defined(__i386__)
+#define MEMORY_BARRIER()	barrier()
+#elif defined(__alpha__)
+#define MEMORY_BARRIER()	mb()
+#else
+#  ifdef mb
+#  define MEMORY_BARRIER()	mb()
+#  else
+#  define MEMORY_BARRIER()	barrier()
+#  endif
+#endif
+
+/*==========================================================
+**
 **	Configuration and Debugging
 **
 **==========================================================
@@ -181,13 +353,33 @@
 */
 
 #ifndef SCSI_NCR_MAX_TAGS
-#define SCSI_NCR_MAX_TAGS    (4)
+#define SCSI_NCR_MAX_TAGS    (8)
+#endif
+
+/*
+**    TAGS are actually limited to 64 tags/lun.
+**    We need to deal with power of 2, for alignment constraints.
+*/
+#if	SCSI_NCR_MAX_TAGS > 64
+#undef	SCSI_NCR_MAX_TAGS
+#define	SCSI_NCR_MAX_TAGS (64)
+#endif
+
+#define NO_TAG	(255)
+
+/*
+**	Choose appropriate type for tag bitmap.
+*/
+#if	SCSI_NCR_MAX_TAGS > 32
+typedef u_int64 tagmap_t;
+#else
+typedef u_int32 tagmap_t;
 #endif
 
 /*
 **    Number of targets supported by the driver.
 **    n permits target numbers 0..n-1.
-**    Default is 7, meaning targets #0..#6.
+**    Default is 16, meaning targets #0..#15.
 **    #7 .. is myself.
 */
 
@@ -233,10 +425,23 @@
 
 /*
 **    The maximum number of segments a transfer is split into.
+**    We support up to 127 segments for both read and write.
+**    The data scripts are broken into 2 sub-scripts.
+**    80 (MAX_SCATTERL) segments are moved from a sub-script 
+**    in on-chip RAM. This makes data transfers shorter than 
+**    80k (assuming 1k fs) as fast as possible.
 */
 
 #define MAX_SCATTER (SCSI_NCR_MAX_SCATTER)
 
+#if (MAX_SCATTER > 80)
+#define MAX_SCATTERL	80
+#define	MAX_SCATTERH	(MAX_SCATTER - MAX_SCATTERL)
+#else
+#define MAX_SCATTERL	(MAX_SCATTER-1)
+#define	MAX_SCATTERH	1
+#endif
+
 /*
 **    Io mapped or memory mapped.
 */
@@ -266,7 +471,6 @@
  **	Obvious definitions
  */
 
-#define printf		printk
 #define u_char		unsigned char
 #define u_short		unsigned short
 #define u_int		unsigned int
@@ -275,27 +479,83 @@
 typedef	u_long		vm_offset_t;
 typedef	int		vm_size_t;
 
+#ifndef bcopy
 #define bcopy(s, d, n)	memcpy((d), (s), (n))
+#endif
+#ifndef bzero
 #define bzero(d, n)	memset((d), 0, (n))
-
+#endif
+ 
 #ifndef offsetof
 #define offsetof(t, m)	((size_t) (&((t *)0)->m))
 #endif
 
 /*
+**	SMP threading.
+**
+**	Assuming that SMP systems are generally high end systems and may 
+**	use several SCSI adapters, we are using one lock per controller 
+**	instead of some global one. For the moment (linux-2.1.95), driver's 
+**	entry points are called with the 'io_request_lock' lock held, so:
+**	- We are uselessly loosing a couple of micro-seconds to lock the 
+**	  controller data structure.
+**	- But the driver is not broken by design for SMP and so can be 
+**	  more resistant to bugs or bad changes in the IO sub-system code.
+**	- A small advantage could be that the interrupt code is grained as 
+**	  wished (e.g.: threaded by controller).
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93)
+
+#if 0	/* not yet needed */
+static spinlock_t driver_lock;
+#define	NCR_LOCK_DRIVER(flags)     spin_lock_irqsave(&driver_lock, flags)
+#define	NCR_UNLOCK_DRIVER(flags)   spin_unlock_irqrestore(&driver_lock, flags)
+#endif
+
+#define NCR_INIT_LOCK_NCB(np)      spin_lock_init(&np->smp_lock);
+#define	NCR_LOCK_NCB(np, flags)    spin_lock_irqsave(&np->smp_lock, flags)
+#define	NCR_UNLOCK_NCB(np, flags)  spin_unlock_irqrestore(&np->smp_lock, flags)
+
+#	if LINUX_VERSION_CODE < LinuxVersionCode(2,3,99)
+
+#	define	NCR_LOCK_SCSI_DONE(np, flags) \
+		spin_lock_irqsave(&io_request_lock, flags)
+#	define	NCR_UNLOCK_SCSI_DONE(np, flags) \
+		spin_unlock_irqrestore(&io_request_lock, flags)
+
+#	else
+
+#	define	NCR_LOCK_SCSI_DONE(np, flags)    do {;} while (0)
+#	define	NCR_UNLOCK_SCSI_DONE(np, flags)  do {;} while (0)
+
+#	endif
+
+#else
+
+#if 0	/* not yet needed */
+#define	NCR_LOCK_DRIVER(flags)     do {;} while (0)
+#define	NCR_UNLOCK_DRIVER(flags)   do {;} while (0)
+#endif
+
+#define	NCR_INIT_LOCK_NCB(np)      do { } while (0)
+#define	NCR_LOCK_NCB(np, flags)    do { save_flags(flags); cli(); } while (0)
+#define	NCR_UNLOCK_NCB(np, flags)  do { restore_flags(flags); } while (0)
+
+#define	NCR_LOCK_SCSI_DONE(np, flags)    do {;} while (0)
+#define	NCR_UNLOCK_SCSI_DONE(np, flags)  do {;} while (0)
+
+#endif
+
+/*
 **	Address translation
 **
-**	On Linux 1.3.X, virt_to_bus() must be used to translate
-**	virtual memory addresses of the kernel data segment into
-**	IO bus adresses.
-**	On i386 architecture, IO bus addresses match the physical
-**	addresses. But on other architectures they can be different.
-**	In the original Bsd driver, vtophys() is called to translate
-**	data addresses to IO bus addresses. In order to minimize
-**	change, I decide to define vtophys() as virt_to_bus().
+**	The driver has to provide physical memory addresses to 
+**	the script processor. Because some architectures use 
+**	different physical addresses from the PCI BUS, we must 
+**	use virt_to_bus instead of virt_to_phys.
 */
 
-#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
 #define vtophys(p)	virt_to_bus(p)
 
 /*
@@ -309,20 +569,29 @@
 **	architecture.
 */
 
-#ifndef NCR_IOMAPPED
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,0)
+#define ioremap vremap
+#define iounmap vfree
+#endif
+
+#if defined (__sparc__)
+#include <asm/irq.h>
+#elif defined (__alpha__)
+#define bus_dvma_to_mem(p)		((p) & 0xfffffffful)
+#else
+#define bus_dvma_to_mem(p)		(p)
+#endif
+
+#if defined(__i386__) || !defined(NCR_IOMAPPED)
 __initfunc(
 static vm_offset_t remap_pci_mem(u_long base, u_long size)
 )
 {
 	u_long page_base	= ((u_long) base) & PAGE_MASK;
 	u_long page_offs	= ((u_long) base) - page_base;
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
 	u_long page_remapped	= (u_long) ioremap(page_base, page_offs+size);
-#else
-	u_long page_remapped	= (u_long) vremap(page_base, page_offs+size);
-#endif
 
-	return (vm_offset_t) (page_remapped ? (page_remapped + page_offs) : 0UL);
+	return (vm_offset_t) (page_remapped? (page_remapped + page_offs) : 0UL);
 }
 
 __initfunc(
@@ -330,51 +599,40 @@
 )
 {
 	if (vaddr)
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
 		iounmap((void *) (vaddr & PAGE_MASK));
-#else
-		vfree((void *) (vaddr & PAGE_MASK));
-#endif
 }
-#endif	/* !NCR_IOMAPPED */
-
-#else /* linux-1.2.13 */
+#endif	/* __i386__ || !NCR_IOMAPPED */
 
 /*
-**	Linux 1.2.X assumes that addresses (virtual, physical, bus)
-**	are the same.
-**
-**	I have not found how to do MMIO. It seems that only processes can
-**	map high physical pages to virtual (Xservers can do MMIO).
-*/
-
-#define vtophys(p)	((u_long) (p))
+**	Insert a delay in micro-seconds and milli-seconds.
+**	-------------------------------------------------
+**	Under Linux, udelay() is restricted to delay < 1 milli-second.
+**	In fact, it generally works for up to 1 second delay.
+**	Since 2.1.105, the mdelay() function is provided for delays 
+**	in milli-seconds.
+**	Under 2.0 kernels, udelay() is an inline function that is very 
+**	inaccurate on Pentium processors.
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,105)
+#define UDELAY udelay
+#define MDELAY mdelay
+#else
+static void UDELAY(long us) { udelay(us); }
+static void MDELAY(long ms) { while (ms--) UDELAY(1000); }
 #endif
 
 /*
-**	Insert a delay in micro-seconds.
-*/
-
-static void DELAY(long us)
-{
-	for (;us>1000;us-=1000) udelay(1000);
-	if (us) udelay(us);
-}
-
-/*
 **	Internal data structure allocation.
 **
 **	Linux scsi memory poor pool is adjusted for the need of
 **	middle-level scsi driver.
 **	We allocate our control blocks in the kernel memory pool
 **	to avoid scsi pool shortage.
-**	I notice that kmalloc() returns NULL during host attach under
-**	Linux 1.2.13. But this ncr driver is reliable enough to
-**	accomodate with this joke.
 **
-**	kmalloc() only ensure 8 bytes boundary alignment.
+**	kmalloc() only ensures 8 bytes boundary alignment.
 **	The NCR need better alignment for cache line bursting.
-**	The global header is moved betewen the NCB and CCBs and need 
+**	The global header is moved between the NCB and CCBs and needs 
 **	origin and destination addresses to have same lower four bits.
 **
 **	We use 32 boundary alignment for NCB and CCBs and offset multiple 
@@ -384,17 +642,9 @@
 #define ALIGN_SIZE(shift)	(1UL << shift)
 #define ALIGN_MASK(shift)	(~(ALIGN_SIZE(shift)-1))
 
-#define NCB_ALIGN_SHIFT		5
-#define CCB_ALIGN_SHIFT		5
-#define LCB_ALIGN_SHIFT		5
-#define SCR_ALIGN_SHIFT		5
-
-#define NCB_ALIGN_SIZE		ALIGN_SIZE(NCB_ALIGN_SHIFT)
-#define NCB_ALIGN_MASK		ALIGN_MASK(NCB_ALIGN_SHIFT)
-#define CCB_ALIGN_SIZE		ALIGN_SIZE(CCB_ALIGN_SHIFT)
-#define CCB_ALIGN_MASK		ALIGN_MASK(CCB_ALIGN_SHIFT)
-#define SCR_ALIGN_SIZE		ALIGN_SIZE(SCR_ALIGN_SHIFT)
-#define SCR_ALIGN_MASK		ALIGN_MASK(SCR_ALIGN_SHIFT)
+#define CACHE_LINE_SHIFT	5
+#define CACHE_LINE_SIZE		ALIGN_SIZE(CACHE_LINE_SHIFT)
+#define CACHE_LINE_MASK		ALIGN_MASK(CACHE_LINE_SHIFT)
 
 static void *m_alloc(int size, int a_shift)
 {
@@ -448,11 +698,8 @@
 **	be able to transfer data in the direction choosen by the target. 
 */
 
-#define XferNone	0
-#define XferIn		1
-#define XferOut		2
-#define XferBoth	3
-static int guess_xfer_direction(int opcode);
+#define XFER_IN		(1)
+#define XFER_OUT	(2)
 
 /*
 **	Head of list of NCR boards
@@ -470,45 +717,30 @@
 **	/proc directory entry and proc_info function
 */
 
-#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
 struct proc_dir_entry proc_scsi_ncr53c8xx = {
     PROC_SCSI_NCR53C8XX, 9, "ncr53c8xx",
     S_IFDIR | S_IRUGO | S_IXUGO, 2
 };
-# ifdef SCSI_NCR_PROC_INFO_SUPPORT
+#ifdef SCSI_NCR_PROC_INFO_SUPPORT
 int ncr53c8xx_proc_info(char *buffer, char **start, off_t offset,
 			int length, int hostno, int func);
-# endif
 #endif
 
 /*
-**	Table of target capabilities.
-**
-**	This bitmap is anded with the byte 7 of inquiry data on completion of
-**	INQUIRY command.
-** 	The driver never see zeroed bits and will ignore the corresponding
-**	capabilities of the target.
-*/
-
-static struct {
-	unsigned char and_map[MAX_TARGET];
-} target_capabilities[SCSI_NCR_MAX_HOST] = { NCR53C8XX_TARGET_CAPABILITIES };
-
-/*
 **	Driver setup.
 **
 **	This structure is initialized from linux config options.
 **	It can be overridden at boot-up by the boot command line.
 */
 struct ncr_driver_setup {
-	unsigned master_parity	: 1;
-	unsigned scsi_parity	: 1;
-	unsigned disconnection	: 1;
-	unsigned special_features : 2;
-	unsigned ultra_scsi	: 2;
-	unsigned force_sync_nego: 1;
-	unsigned reverse_probe: 1;
-	unsigned pci_fix_up: 4;
+	u_char	master_parity;
+	u_char	scsi_parity;
+	u_char	disconnection;
+	u_char	special_features;
+	u_char	ultra_scsi;
+	u_char	force_sync_nego;
+	u_char	reverse_probe;
+	u_char	pci_fix_up;
 	u_char	use_nvram;
 	u_char	verbose;
 	u_char	default_tags;
@@ -521,6 +753,7 @@
 	u_char	diff_support;
 	u_char	irqm;
 	u_char	bus_check;
+	char	tag_ctrl[100];
 };
 
 static struct ncr_driver_setup
@@ -529,9 +762,12 @@
 #ifdef	SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
 static struct ncr_driver_setup
 	driver_safe_setup __initdata	= SCSI_NCR_DRIVER_SAFE_SETUP;
-#ifdef	MODULE
+# ifdef	MODULE
 char *ncr53c8xx = 0;	/* command line passed by insmod */
-#endif
+#  if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,30)
+MODULE_PARM(ncr53c8xx, "s");
+#  endif
+# endif
 #endif
 
 /*
@@ -540,16 +776,9 @@
 
 #define ScsiResult(host_code, scsi_code) (((host_code) << 16) + ((scsi_code) & 0x7f))
 
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0)
-static void ncr53c8xx_select_queue_depths(struct Scsi_Host *host, struct scsi_device *devlist);
-#endif
-
-#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
+static void ncr53c8xx_select_queue_depths(
+	struct Scsi_Host *host, struct scsi_device *devlist);
 static void ncr53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs);
-#else
-static void ncr53c8xx_intr(int irq, struct pt_regs * regs);
-#endif
-
 static void ncr53c8xx_timeout(unsigned long np);
 
 #define initverbose (driver_setup.verbose)
@@ -576,6 +805,7 @@
 #define SYMBIOS_SCAM_ENABLE	(1)
 #define SYMBIOS_PARITY_ENABLE	(1<<1)
 #define SYMBIOS_VERBOSE_MSGS	(1<<2)
+#define SYMBIOS_CHS_MAPPING	(1<<3)
 	u_short	flags1;
 #define SYMBIOS_SCAN_HI_LO	(1)
 	u_short	word10;		/* 0x00 */
@@ -666,13 +896,13 @@
 typedef struct {
 	int	bus;
 	u_char	device_fn;
-	u_int	base;
-	u_int	base_2;
-	u_int	io_port;
+	u_long	base;
+	u_long	base_2;
+	u_long	io_port;
 	int	irq;
 /* port and reg fields to use INB, OUTB macros */
-	u_int	port;
-	volatile struct	ncr_reg	*reg;
+	u_long	port;
+	volatile struct ncr_reg	*reg;
 } ncr_slot;
 
 typedef struct {
@@ -745,7 +975,7 @@
 
 #define	assert(expression) { \
 	if (!(expression)) { \
-		(void)printf(\
+		(void)printk(KERN_ERR \
 			"assertion \"%s\" failed: file \"%s\", line %d\n", \
 			#expression, \
 			__FILE__, __LINE__); \
@@ -808,7 +1038,12 @@
 **
 **	Access to the controller chip.
 **
-**	If NCR_IOMAPPED is defined, only IO are used by the driver.
+**	If NCR_IOMAPPED is defined, the driver will use 
+**	normal IOs instead of the MEMORY MAPPED IO method  
+**	recommended by PCI specifications.
+**	If all PCI bridges, host brigdes and architectures 
+**	would have been correctly designed for PCI, this 
+**	option would be useless.
 **
 **==========================================================
 */
@@ -928,15 +1163,31 @@
 #define HS_NEGOTIATE	(2)	/* sync/wide data transfer*/
 #define HS_DISCONNECT	(3)	/* Disconnected by target */
 
-#define HS_COMPLETE	(4)
-#define HS_SEL_TIMEOUT	(5)	/* Selection timeout      */
-#define HS_RESET	(6)	/* SCSI reset	     */
-#define HS_ABORTED	(7)	/* Transfer aborted       */
-#define HS_TIMEOUT	(8)	/* Software timeout       */
-#define HS_FAIL		(9)	/* SCSI or PCI bus errors */
-#define HS_UNEXPECTED	(10)	/* Unexpected disconnect  */
+#define HS_DONEMASK	(0x80)
+#define HS_COMPLETE	(4|HS_DONEMASK)
+#define HS_SEL_TIMEOUT	(5|HS_DONEMASK)	/* Selection timeout      */
+#define HS_RESET	(6|HS_DONEMASK)	/* SCSI reset	          */
+#define HS_ABORTED	(7|HS_DONEMASK)	/* Transfer aborted       */
+#define HS_TIMEOUT	(8|HS_DONEMASK)	/* Software timeout       */
+#define HS_FAIL		(9|HS_DONEMASK)	/* SCSI or PCI bus errors */
+#define HS_UNEXPECTED	(10|HS_DONEMASK)/* Unexpected disconnect  */
 
-#define HS_DONEMASK	(0xfc)
+/*
+**	Invalid host status values used by the SCRIPTS processor 
+**	when the nexus is not fully identified.
+**	Shall never appear in a CCB.
+*/
+
+#define HS_INVALMASK	(0x40)
+#define	HS_SELECTING	(0|HS_INVALMASK)
+#define	HS_IN_RESELECT	(1|HS_INVALMASK)
+#define	HS_STARTING	(2|HS_INVALMASK)
+
+/*
+**	Flags set by the SCRIPT processor for commands 
+**	that have been skipped.
+*/
+#define HS_SKIPMASK	(0x20)
 
 /*==========================================================
 **
@@ -945,21 +1196,24 @@
 **==========================================================
 */
 
-#define	SIR_SENSE_RESTART	(1)
-#define	SIR_SENSE_FAILED	(2)
-#define	SIR_STALL_RESTART	(3)
-#define	SIR_STALL_QUEUE		(4)
-#define	SIR_NEGO_SYNC		(5)
-#define	SIR_NEGO_WIDE		(6)
-#define	SIR_NEGO_FAILED		(7)
-#define	SIR_NEGO_PROTO		(8)
-#define	SIR_REJECT_RECEIVED	(9)
-#define	SIR_REJECT_SENT		(10)
-#define	SIR_IGN_RESIDUE		(11)
-#define	SIR_MISSING_SAVE	(12)
-#define	SIR_DATA_IO_IS_OUT	(13)
-#define	SIR_DATA_IO_IS_IN	(14)
-#define	SIR_MAX			(14)
+#define	SIR_BAD_STATUS		(1)
+#define	SIR_XXXXXXXXXX		(2)
+#define	SIR_NEGO_SYNC		(3)
+#define	SIR_NEGO_WIDE		(4)
+#define	SIR_NEGO_FAILED		(5)
+#define	SIR_NEGO_PROTO		(6)
+#define	SIR_REJECT_RECEIVED	(7)
+#define	SIR_REJECT_SENT		(8)
+#define	SIR_IGN_RESIDUE		(9)
+#define	SIR_MISSING_SAVE	(10)
+#define	SIR_RESEL_NO_MSG_IN	(11)
+#define	SIR_RESEL_NO_IDENTIFY	(12)
+#define	SIR_RESEL_BAD_LUN	(13)
+#define	SIR_RESEL_BAD_TARGET	(14)
+#define	SIR_RESEL_BAD_I_T_L	(15)
+#define	SIR_RESEL_BAD_I_T_L_Q	(16)
+#define	SIR_DONE_OVERFLOW	(17)
+#define	SIR_MAX			(17)
 
 /*==========================================================
 **
@@ -997,7 +1251,6 @@
 #define	QUIRK_NOMSG	(0x02)
 #define QUIRK_NOSYNC	(0x10)
 #define QUIRK_NOWIDE16	(0x20)
-#define	QUIRK_UPDATE	(0x80)
 
 /*==========================================================
 **
@@ -1056,10 +1309,7 @@
 #define UC_SETWIDE	14
 #define UC_SETFLAG	15
 #define UC_CLEARPROF	16
-
-#ifdef	SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
-#define UC_DEBUG_ERROR_RECOVERY 17
-#endif
+#define UC_SETVERBOSE	17
 
 #define	UF_TRACE	(0x01)
 #define	UF_NODISC	(0x02)
@@ -1072,10 +1322,11 @@
 **---------------------------------------
 */
 
+#ifdef SCSI_NCR_PROFILE_SUPPORT
+
 struct tstamp {
 	u_long start;
 	u_long end;
-	u_long select;
 	u_long command;
 	u_long status;
 	u_long disconnect;
@@ -1099,263 +1350,257 @@
 	u_long	ms_disc;
 	u_long	ms_post;
 };
+#endif
 
-/*==========================================================
+/*========================================================================
 **
 **	Declaration of structs:		target control block
 **
-**==========================================================
+**========================================================================
 */
-
 struct tcb {
-	/*
-	**	during reselection the ncr jumps to this point
-	**	with SFBR set to the encoded target number
-	**	with bit 7 set.
+	/*----------------------------------------------------------------
+	**	During reselection the ncr jumps to this point with SFBR 
+	**	set to the encoded target number with bit 7 set.
 	**	if it's not this target, jump to the next.
 	**
-	**	JUMP  IF (SFBR != #target#)
-	**	@(next tcb)
+	**	JUMP  IF (SFBR != #target#), @(next tcb)
+	**----------------------------------------------------------------
 	*/
-
 	struct link   jump_tcb;
 
-	/*
-	**	load the actual values for the sxfer and the scntl3
+	/*----------------------------------------------------------------
+	**	Load the actual values for the sxfer and the scntl3
 	**	register (sync/wide mode).
 	**
-	**	SCR_COPY (1);
-	**	@(sval field of this tcb)
-	**	@(sxfer register)
-	**	SCR_COPY (1);
-	**	@(wval field of this tcb)
-	**	@(scntl3 register)
+	**	SCR_COPY (1), @(sval field of this tcb), @(sxfer  register)
+	**	SCR_COPY (1), @(wval field of this tcb), @(scntl3 register)
+	**----------------------------------------------------------------
 	*/
-
 	ncrcmd	getscr[6];
 
-	/*
-	**	if next message is "identify"
-	**	then load the message to SFBR,
-	**	else load 0 to SFBR.
+	/*----------------------------------------------------------------
+	**	Get the IDENTIFY message and load the LUN to SFBR.
 	**
-	**	CALL
-	**	<RESEL_LUN>
+	**	CALL, <RESEL_LUN>
+	**----------------------------------------------------------------
 	*/
-
 	struct link   call_lun;
 
-	/*
-	**	now look for the right lun.
+	/*----------------------------------------------------------------
+	**	Now look for the right lun.
 	**
-	**	JUMP
-	**	@(first ccb of this lun)
-	*/
-
-	struct link   jump_lcb;
-
-	/*
-	**	pointer to interrupted getcc ccb
-	*/
-
-	ccb_p   hold_cp;
-
-	/*
-	**	pointer to ccb used for negotiating.
-	**	Avoid to start a nego for all queued commands 
+	**	For i = 0 to 3
+	**		SCR_JUMP ^ IFTRUE(MASK(i, 3)), @(first lcb mod. i)
+	**
+	**	Recent chips will prefetch the 4 JUMPS using only 1 burst.
+	**	It is kind of hashcoding.
+	**----------------------------------------------------------------
+	*/
+	struct link     jump_lcb[4];	/* JUMPs for reselection	*/
+	lcb_p		lp[MAX_LUN];	/* The lcb's of this tcb	*/
+	u_char		inq_done;	/* Target capabilities received	*/
+	u_char		inq_byte7;	/* Contains these capabilities	*/
+
+	/*----------------------------------------------------------------
+	**	Pointer to the ccb used for negotiation.
+	**	Prevent from starting a negotiation for all queued commands 
 	**	when tagged command queuing is enabled.
+	**----------------------------------------------------------------
 	*/
-
 	ccb_p   nego_cp;
 
-	/*
+	/*----------------------------------------------------------------
 	**	statistical data
+	**----------------------------------------------------------------
 	*/
-
 	u_long	transfers;
 	u_long	bytes;
 
-	/*
-	**	user settable limits for sync transfer
-	**	and tagged commands.
-	**	These limits are read from the NVRAM if present.
-	*/
-
-	u_char	usrsync;
-	u_char	usrwide;
-	u_char	usrtags;
-	u_char	usrflag;
-
-	u_char	numtags;
-	u_char	maxtags;
-	u_short	num_good;
-
-	/*
-	**	negotiation of wide and synch transfer.
-	**	device quirks.
+	/*----------------------------------------------------------------
+	**	negotiation of wide and synch transfer and device quirks.
+	**----------------------------------------------------------------
 	*/
-
 /*0*/	u_char	minsync;
 /*1*/	u_char	sval;
 /*2*/	u_short	period;
 /*0*/	u_char	maxoffs;
-
 /*1*/	u_char	quirks;
-
 /*2*/	u_char	widedone;
 /*3*/	u_char	wval;
-	/*
-	**	inquire data
-	*/
-#define MAX_INQUIRE 36
-	u_char	inqdata[MAX_INQUIRE];
 
-	/*
-	**	the lcb's of this tcb
+	/*----------------------------------------------------------------
+	**	User settable limits and options.
+	**	These limits are read from the NVRAM if present.
+	**----------------------------------------------------------------
 	*/
-
-	lcb_p   lp[MAX_LUN];
+	u_char	usrsync;
+	u_char	usrwide;
+	u_char	usrtags;
+	u_char	usrflag;
 };
 
-/*==========================================================
+/*========================================================================
 **
 **	Declaration of structs:		lun control block
 **
-**==========================================================
+**========================================================================
 */
-
 struct lcb {
-	/*
-	**	during reselection the ncr jumps to this point
+	/*----------------------------------------------------------------
+	**	During reselection the ncr jumps to this point
 	**	with SFBR set to the "Identify" message.
 	**	if it's not this lun, jump to the next.
 	**
-	**	JUMP  IF (SFBR != #lun#)
-	**	@(next lcb of this target)
-	*/
-
-	struct link	jump_lcb;
-
-	/*
-	**	if next message is "simple tag",
-	**	then load the tag to SFBR,
-	**	else load 0 to SFBR.
+	**	JUMP  IF (SFBR != #lun#), @(next lcb of this target)
 	**
-	**	CALL
-	**	<RESEL_TAG>
-	*/
-
-	struct link	call_tag;
-
-	/*
-	**	now look for the right ccb.
+	**	It is this lun. Load TEMP with the nexus jumps table 
+	**	address and jump to RESEL_TAG (or RESEL_NOTAG).
 	**
-	**	JUMP
-	**	@(first ccb of this lun)
-	*/
-
-	struct link	jump_ccb;
-
-	/*
-	**	start of the ccb chain
-	*/
-
-	ccb_p	next_ccb;
-
-	/*
-	**	Control of tagged queueing
-	*/
-
-	u_char		reqccbs;
-	u_char		actccbs;
-	u_char		reqlink;
-	u_char		actlink;
-	u_char		usetags;
-	u_char		lasttag;
-
-	/*
-	**	Linux specific fields:
-	**	Number of active commands and current credit.
-	**	Should be managed by the generic scsi driver
+	**		SCR_COPY (4), p_jump_ccb, TEMP,
+	**		SCR_JUMP, <RESEL_TAG>
+	**----------------------------------------------------------------
 	*/
+	struct link	jump_lcb;
+	ncrcmd		load_jump_ccb[3];
+	struct link	jump_tag;
+	ncrcmd		p_jump_ccb;	/* Jump table bus address	*/
+
+	/*----------------------------------------------------------------
+	**	Jump table used by the script processor to directly jump 
+	**	to the CCB corresponding to the reselected nexus.
+	**	Address is allocated on 256 bytes boundary in order to 
+	**	allow 8 bit calculation of the tag jump entry for up to 
+	**	64 possible tags.
+	**----------------------------------------------------------------
+	*/
+	u_int32		jump_ccb_0;	/* Default table if no tags	*/
+	u_int32		*jump_ccb;	/* Virtual address		*/
+
+	/*----------------------------------------------------------------
+	**	CCB queue management.
+	**----------------------------------------------------------------
+	*/
+	XPT_QUEHEAD	free_ccbq;	/* Queue of available CCBs	*/
+	XPT_QUEHEAD	busy_ccbq;	/* Queue of busy CCBs		*/
+	XPT_QUEHEAD	wait_ccbq;	/* Queue of waiting for IO CCBs	*/
+	XPT_QUEHEAD	skip_ccbq;	/* Queue of skipped CCBs	*/
+	u_char		actccbs;	/* Number of allocated CCBs	*/
+	u_char		busyccbs;	/* CCBs busy for this lun	*/
+	u_char		queuedccbs;	/* CCBs queued to the controller*/
+	u_char		queuedepth;	/* Queue depth for this lun	*/
+	u_char		scdev_depth;	/* SCSI device queue depth	*/
+	u_char		maxnxs;		/* Max possible nexuses		*/
+
+	/*----------------------------------------------------------------
+	**	Control of tagged command queuing.
+	**	Tags allocation is performed using a circular buffer.
+	**	This avoids using a loop for tag allocation.
+	**----------------------------------------------------------------
+	*/
+	u_char		ia_tag;		/* Allocation index		*/
+	u_char		if_tag;		/* Freeing index		*/
+	u_char cb_tags[SCSI_NCR_MAX_TAGS];	/* Circular tags buffer	*/
+	u_char		usetags;	/* Command queuing is active	*/
+	u_char		maxtags;	/* Max nr of tags asked by user	*/
+	u_char		numtags;	/* Current number of tags	*/
+	u_char		inq_byte7;	/* Store unit CmdQ capabitility	*/
+
+	/*----------------------------------------------------------------
+	**	QUEUE FULL control and ORDERED tag control.
+	**----------------------------------------------------------------
+	*/
+	/*----------------------------------------------------------------
+	**	QUEUE FULL and ORDERED tag control.
+	**----------------------------------------------------------------
+	*/
+	u_short		num_good;	/* Nr of GOOD since QUEUE FULL	*/
+	tagmap_t	tags_umap;	/* Used tags bitmap		*/
+	tagmap_t	tags_smap;	/* Tags in use at 'tag_stime'	*/
+	u_long		tags_stime;	/* Last time we set smap=umap	*/
+	ccb_p		held_ccb;	/* CCB held for QUEUE FULL	*/
+};
 
-	u_char		active;
-	u_char		opennings;
-
-	/*-----------------------------------------------
-	**	Flag to force M_ORDERED_TAG on next command
-	**	in order to avoid spurious timeout when
-	**	M_SIMPLE_TAG is used for all operations.
-	**-----------------------------------------------
+/*========================================================================
+**
+**      Declaration of structs:     the launch script.
+**
+**========================================================================
+**
+**	It is part of the CCB and is called by the scripts processor to 
+**	start or restart the data structure (nexus).
+**	This 6 DWORDs mini script makes use of prefetching.
+**
+**------------------------------------------------------------------------
+*/
+struct launch {
+	/*----------------------------------------------------------------
+	**	SCR_COPY(4),	@(p_phys), @(dsa register)
+	**	SCR_JUMP,	@(scheduler_point)
+	**----------------------------------------------------------------
 	*/
-	u_char	force_ordered_tag;
-#define NCR_TIMEOUT_INCREASE	(5*HZ)
+	ncrcmd		setup_dsa[3];	/* Copy 'phys' address to dsa	*/
+	struct link	schedule;	/* Jump to scheduler point	*/
+	ncrcmd		p_phys;		/* 'phys' header bus address	*/
 };
 
-/*==========================================================
+/*========================================================================
 **
-**      Declaration of structs:     COMMAND control block
+**      Declaration of structs:     global HEADER.
 **
-**==========================================================
+**========================================================================
 **
-**	This substructure is copied from the ccb to a
-**	global address after selection (or reselection)
-**	and copied back before disconnect.
+**	This substructure is copied from the ccb to a global address after 
+**	selection (or reselection) and copied back before disconnect.
 **
 **	These fields are accessible to the script processor.
 **
-**----------------------------------------------------------
+**------------------------------------------------------------------------
 */
 
 struct head {
-	/*
-	**	Execution of a ccb starts at this point.
-	**	It's a jump to the "SELECT" label
-	**	of the script.
-	**
-	**	After successful selection the script
-	**	processor overwrites it with a jump to
-	**	the IDLE label of the script.
-	*/
-
-	struct link	launch;
-
-	/*
+	/*----------------------------------------------------------------
 	**	Saved data pointer.
-	**	Points to the position in the script
-	**	responsible for the actual transfer
-	**	of data.
-	**	It's written after reception of a
-	**	"SAVE_DATA_POINTER" message.
-	**	The goalpointer points after
-	**	the last transfer command.
+	**	Points to the position in the script responsible for the
+	**	actual transfer transfer of data.
+	**	It's written after reception of a SAVE_DATA_POINTER message.
+	**	The goalpointer points after the last transfer command.
+	**----------------------------------------------------------------
 	*/
-
 	u_int32		savep;
 	u_int32		lastp;
 	u_int32		goalp;
 
-	/*
-	**	The virtual address of the ccb
-	**	containing this header.
+	/*----------------------------------------------------------------
+	**	Alternate data pointer.
+	**	They are copied back to savep/lastp/goalp by the SCRIPTS 
+	**	when the direction is unknown and the device claims data out.
+	**----------------------------------------------------------------
+	*/
+	u_int32		wlastp;
+	u_int32		wgoalp;
+
+	/*----------------------------------------------------------------
+	**	The virtual address of the ccb containing this header.
+	**----------------------------------------------------------------
 	*/
-
 	ccb_p	cp;
 
-	/*
-	**	space for some timestamps to gather
-	**	profiling data about devices and this driver.
+#ifdef SCSI_NCR_PROFILE_SUPPORT
+	/*----------------------------------------------------------------
+	**	Space for some timestamps to gather profiling data.
+	**----------------------------------------------------------------
 	*/
-
 	struct tstamp	stamp;
+#endif
 
-	/*
-	**	status fields.
-	*/
-
-	u_char		scr_st[4];	/* script status */
-	u_char		status[4];	/* host status. Must be the last */
-					/* DWORD of the CCB header */
+	/*----------------------------------------------------------------
+	**	Status fields.
+	**----------------------------------------------------------------
+	*/
+	u_char		scr_st[4];	/* script status		*/
+	u_char		status[4];	/* host status. must be the 	*/
+					/*  last DWORD of the header.	*/
 };
 
 /*
@@ -1388,6 +1633,7 @@
 #define  HS_REG	scr1
 #define  HS_PRT	nc_scr1
 #define  SS_REG	scr2
+#define  SS_PRT	nc_scr2
 #define  PS_REG	scr3
 
 /*
@@ -1410,9 +1656,12 @@
 **	First four bytes (host)
 */
 #define  xerr_status   phys.xerr_st
-#define  sync_status   phys.sync_st
 #define  nego_status   phys.nego_st
+
+#if 0
+#define  sync_status   phys.sync_st
 #define  wide_status   phys.wide_st
+#endif
 
 /*==========================================================
 **
@@ -1434,9 +1683,6 @@
 
 	/*
 	**	Header.
-	**	Has to be the first entry,
-	**	because it's jumped to by the
-	**	script processor
 	*/
 
 	struct head	header;
@@ -1447,403 +1693,276 @@
 
 	struct scr_tblsel  select;
 	struct scr_tblmove smsg  ;
-	struct scr_tblmove smsg2 ;
 	struct scr_tblmove cmd   ;
-	struct scr_tblmove scmd  ;
 	struct scr_tblmove sense ;
 	struct scr_tblmove data [MAX_SCATTER];
 };
 
-/*==========================================================
+
+/*========================================================================
 **
 **      Declaration of structs:     Command control block.
 **
-**==========================================================
-**
-**	During execution of a ccb by the script processor,
-**	the DSA (data structure address) register points
-**	to this substructure of the ccb.
-**	This substructure contains the header with
-**	the script-processor-changable data and then
-**	data blocks for the indirect move commands.
-**
-**----------------------------------------------------------
+**========================================================================
 */
-
-
 struct ccb {
-	/*
-	**	This field forces 32 bytes alignement for phys.header,
-	**	in order to use cache line bursting when copying it 
-	**	to the ncb.
-	*/
-
-	struct link		filler[2];
-
-	/*
-	**	during reselection the ncr jumps to this point.
-	**	If a "SIMPLE_TAG" message was received,
-	**	then SFBR is set to the tag.
-	**	else SFBR is set to 0
-	**	If looking for another tag, jump to the next ccb.
-	**
-	**	JUMP  IF (SFBR != #TAG#)
-	**	@(next ccb of this lun)
-	*/
-
-	struct link		jump_ccb;
-
-	/*
-	**	After execution of this call, the return address
-	**	(in  the TEMP register) points to the following
-	**	data structure block.
-	**	So copy it to the DSA register, and start
-	**	processing of this data structure.
-	**
-	**	CALL
-	**	<RESEL_TMP>
-	*/
-
-	struct link		call_tmp;
-
-	/*
-	**	This is the data structure which is
-	**	to be executed by the script processor.
+	/*----------------------------------------------------------------
+	**	This is the data structure which is pointed by the DSA 
+	**	register when it is executed by the script processor.
+	**	It must be the first entry because it contains the header 
+	**	as first entry that must be cache line aligned.
+	**----------------------------------------------------------------
+	*/
+	struct dsb	phys;
+
+	/*----------------------------------------------------------------
+	**	Mini-script used at CCB execution start-up.
+	**	Load the DSA with the data structure address (phys) and 
+	**	jump to SELECT. Jump to CANCEL if CCB is to be canceled.
+	**----------------------------------------------------------------
+	*/
+	struct launch	start;
+
+	/*----------------------------------------------------------------
+	**	Mini-script used at CCB relection to restart the nexus.
+	**	Load the DSA with the data structure address (phys) and 
+	**	jump to RESEL_DSA. Jump to ABORT if CCB is to be aborted.
+	**----------------------------------------------------------------
 	*/
+	struct launch	restart;
 
-	struct dsb		phys;
-
-	/*
+	/*----------------------------------------------------------------
 	**	If a data transfer phase is terminated too early
 	**	(after reception of a message (i.e. DISCONNECT)),
 	**	we have to prepare a mini script to transfer
 	**	the rest of the data.
+	**----------------------------------------------------------------
 	*/
+	ncrcmd		patch[8];
 
-	ncrcmd			patch[8];
-
-	/*
+	/*----------------------------------------------------------------
 	**	The general SCSI driver provides a
 	**	pointer to a control block.
+	**----------------------------------------------------------------
 	*/
-
-	Scsi_Cmnd	*cmd;
-	int data_len;
-
-	/*
-	**	We prepare a message to be sent after selection,
-	**	and a second one to be sent after getcc selection.
+	Scsi_Cmnd	*cmd;		/* SCSI command 		*/
+	u_long		tlimit;		/* Deadline for this job	*/
+	int		data_len;	/* Total data length		*/
+
+	/*----------------------------------------------------------------
+	**	Message areas.
+	**	We prepare a message to be sent after selection.
+	**	We may use a second one if the command is rescheduled 
+	**	due to GETCC or QFULL.
 	**      Contents are IDENTIFY and SIMPLE_TAG.
 	**	While negotiating sync or wide transfer,
-	**	a SDTM or WDTM message is appended.
-	*/
-
-	u_char			scsi_smsg [8];
-	u_char			scsi_smsg2[8];
-
-	/*
-	**	Lock this ccb.
-	**	Flag is used while looking for a free ccb.
-	*/
-
-	u_long		magic;
-
-	/*
-	**	Physical address of this instance of ccb
-	*/
-
-	u_long		p_ccb;
-
-	/*
-	**	Completion time out for this job.
-	**	It's set to time of start + allowed number of seconds.
-	*/
-
-	u_long		tlimit;
-
-	/*
-	**	All ccbs of one hostadapter are chained.
-	*/
-
-	ccb_p		link_ccb;
-
-	/*
-	**	All ccbs of one target/lun are chained.
+	**	a SDTR or WDTR message is appended.
+	**----------------------------------------------------------------
 	*/
+	u_char		scsi_smsg [8];
+	u_char		scsi_smsg2[8];
 
-	ccb_p		next_ccb;
-
-	/*
-	**	Sense command
-	*/
-
-	u_char		sensecmd[6];
-
-	/*
-	**	Tag for this transfer.
-	**	It's patched into jump_ccb.
-	**	If it's not zero, a SIMPLE_TAG
-	**	message is included in smsg.
-	*/
-
-	u_char			tag;
-
-	/*
-	**	Number of segments of the scatter list.
-	**	Used for recalculation of savep/goalp/lastp on 
-	**	SIR_DATA_IO_IS_OUT interrupt.
-	*/
-	
-	u_char			segments;
+	/*----------------------------------------------------------------
+	**	Other fields.
+	**----------------------------------------------------------------
+	*/
+	u_long		p_ccb;		/* BUS address of this CCB	*/
+	u_char		sensecmd[6];	/* Sense command		*/
+	u_char		tag;		/* Tag for this transfer	*/
+					/*  255 means no tag		*/
+	u_char		target;
+	u_char		lun;
+	u_char		queued;
+	u_char		auto_sense;
+	ccb_p		link_ccb;	/* Host adapter CCB chain	*/
+	XPT_QUEHEAD	link_ccbq;	/* Link to unit CCB queue	*/
+	u_int32		startp;		/* Initial data pointer		*/
+	u_long		magic;		/* Free / busy  CCB flag	*/
 };
 
 #define CCB_PHYS(cp,lbl)	(cp->p_ccb + offsetof(struct ccb, lbl))
 
-/*==========================================================
+
+/*========================================================================
 **
 **      Declaration of structs:     NCR device descriptor
 **
-**==========================================================
+**========================================================================
 */
-
 struct ncb {
-	/*
+	/*----------------------------------------------------------------
 	**	The global header.
-	**	Accessible to both the host and the
-	**	script-processor.
-	**	Is 32 bytes aligned since ncb is, in order to 
-	**	allow cache line bursting when copying it from or 
-	**	to ccbs.
+	**	It is accessible to both the host and the script processor.
+	**	Must be cache line size aligned (32 for x86) in order to 
+	**	allow cache line bursting when it is copied to/from CCB.
+	**----------------------------------------------------------------
 	*/
 	struct head     header;
 
-	/*-----------------------------------------------
-	**	Specific Linux fields
-	**-----------------------------------------------
-	*/
-	int    unit;			/* Unit number                       */
-	char   chip_name[8];		/* Chip name                         */
-	char   inst_name[16];		/* Instance name                     */
-	struct timer_list timer;	/* Timer link header                 */
-	int	ncr_cache;		/* Cache test variable               */
-	Scsi_Cmnd *waiting_list;	/* Waiting list header for commands  */
-					/* that we can't put into the squeue */
-	u_long	settle_time;		/* Reset in progess		     */
-	u_char	release_stage;		/* Synchronisation stage on release  */
-	u_char	verbose;		/* Boot verbosity for this controller*/
-#ifdef	SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
-	u_char	debug_error_recovery;
-	u_char	stalling;
-	u_char	assert_atn;
-#endif
-
-	/*-----------------------------------------------
-	**	Added field to support differences
-	**	between ncr chips.
-	**	sv_xxx are some io register bit value at start-up and
-	**	so assumed to have been set by the sdms bios.
-	**	rv_xxx are the bit fields of io register that will keep 
-	**	the features used by the driver.
-	**-----------------------------------------------
-	*/
-	u_short	device_id;
-	u_char	revision_id;
-
-	u_char	sv_scntl0;
-	u_char	sv_scntl3;
-	u_char	sv_dmode;
-	u_char	sv_dcntl;
-	u_char	sv_ctest3;
-	u_char	sv_ctest4;
-	u_char	sv_ctest5;
-	u_char	sv_gpcntl;
-	u_char	sv_stest2;
-	u_char	sv_stest4;
-
-	u_char	rv_scntl0;
-	u_char	rv_scntl3;
-	u_char	rv_dmode;
-	u_char	rv_dcntl;
-	u_char	rv_ctest3;
-	u_char	rv_ctest4;
-	u_char	rv_ctest5;
-	u_char	rv_stest2;
-
-	u_char	scsi_mode;
-
-	/*-----------------------------------------------
-	**	Scripts ..
-	**-----------------------------------------------
-	**
-	**	During reselection the ncr jumps to this point.
+	/*----------------------------------------------------------------
+	**	CCBs management queues.
+	**----------------------------------------------------------------
+	*/
+	Scsi_Cmnd	*waiting_list;	/* Commands waiting for a CCB	*/
+					/*  when lcb is not allocated.	*/
+	Scsi_Cmnd	*done_list;	/* Commands waiting for done()  */
+					/* callback to be invoked.      */ 
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93)
+	spinlock_t	smp_lock;	/* Lock for SMP threading       */
+#endif
+
+	/*----------------------------------------------------------------
+	**	Chip and controller indentification.
+	**----------------------------------------------------------------
+	*/
+	int		unit;		/* Unit number			*/
+	char		chip_name[8];	/* Chip name			*/
+	char		inst_name[16];	/* ncb instance name		*/
+
+	/*----------------------------------------------------------------
+	**	Initial value of some IO register bits.
+	**	These values are assumed to have been set by BIOS, and may 
+	**	be used for probing adapter implementation differences.
+	**----------------------------------------------------------------
+	*/
+	u_char	sv_scntl0, sv_scntl3, sv_dmode, sv_dcntl, sv_ctest3, sv_ctest4,
+		sv_ctest5, sv_gpcntl, sv_stest2, sv_stest4;
+
+	/*----------------------------------------------------------------
+	**	Actual initial value of IO register bits used by the 
+	**	driver. They are loaded at initialisation according to  
+	**	features that are to be enabled.
+	**----------------------------------------------------------------
+	*/
+	u_char	rv_scntl0, rv_scntl3, rv_dmode, rv_dcntl, rv_ctest3, rv_ctest4, 
+		rv_ctest5, rv_stest2;
+
+	/*----------------------------------------------------------------
+	**	Targets management.
+	**	During reselection the ncr jumps to jump_tcb.
 	**	The SFBR register is loaded with the encoded target id.
+	**	For i = 0 to 3
+	**		SCR_JUMP ^ IFTRUE(MASK(i, 3)), @(next tcb mod. i)
 	**
-	**	Jump to the first target.
-	**
-	**	JUMP
-	**	@(next tcb)
-	*/
-	struct link     jump_tcb;
-
-	/*-----------------------------------------------
-	**	Configuration ..
-	**-----------------------------------------------
-	**
-	**	virtual and physical addresses
-	**	of the 53c810 chip.
-	*/
-	vm_offset_t     vaddr;
-	vm_offset_t     paddr;
-
-	vm_offset_t     vaddr2;
-	vm_offset_t     paddr2;
-
-	/*
-	**	pointer to the chip's registers.
-	*/
-	volatile
-	struct ncr_reg* reg;
-
-	/*
-	**	A copy of the scripts, relocated for this ncb.
-	*/
-	struct script	*script0;
-	struct scripth	*scripth0;
-
-	/*
-	**	Scripts instance virtual address.
-	*/
-	struct script	*script;
-	struct scripth	*scripth;
-
-	/*
-	**	Scripts instance physical address.
-	*/
-	u_long		p_script;
-	u_long		p_scripth;
-
-	/*
-	**	The SCSI address of the host adapter.
-	*/
-	u_char	  myaddr;
-
-	/*
-	**	Max dwords burst supported by the adapter.
-	*/
+	**	Recent chips will prefetch the 4 JUMPS using only 1 burst.
+	**	It is kind of hashcoding.
+	**----------------------------------------------------------------
+	*/
+	struct link     jump_tcb[4];	/* JUMPs for reselection	*/
+	struct tcb  target[MAX_TARGET];	/* Target data			*/
+
+	/*----------------------------------------------------------------
+	**	Virtual and physical bus addresses of the chip.
+	**----------------------------------------------------------------
+	*/
+	vm_offset_t	vaddr;		/* Virtual and bus address of	*/
+	vm_offset_t     paddr;		/*  chip's IO registers.	*/
+	vm_offset_t     paddr2;		/* On-chip RAM bus address.	*/
+	volatile			/* Pointer to volatile for 	*/
+	struct ncr_reg	*reg;		/*  memory mapped IO.		*/
+
+	/*----------------------------------------------------------------
+	**	SCRIPTS virtual and physical bus addresses.
+	**	'script'  is loaded in the on-chip RAM if present.
+	**	'scripth' stays in main memory.
+	**----------------------------------------------------------------
+	*/
+	struct script	*script0;	/* Copies of script and scripth	*/
+	struct scripth	*scripth0;	/*  relocated for this ncb.	*/
+	struct scripth	*scripth;	/* Actual scripth virt. address	*/
+	u_long		p_script;	/* Actual script and scripth	*/
+	u_long		p_scripth;	/*  bus addresses.		*/
+
+	/*----------------------------------------------------------------
+	**	General controller parameters and configuration.
+	**----------------------------------------------------------------
+	*/
+	u_short		device_id;	/* PCI device id		*/
+	u_char		revision_id;	/* PCI device revision id	*/
+	u_long		port;		/* IO space base address	*/
+	u_int		irq;		/* IRQ level			*/
+	u_int		features;	/* Chip features map		*/
+	u_char		myaddr;		/* SCSI id of the adapter	*/
 	u_char		maxburst;	/* log base 2 of dwords burst	*/
-
-	/*
-	**	timing parameters
-	*/
+	u_char		maxwide;	/* Maximum transfer width	*/
 	u_char		minsync;	/* Minimum sync period factor	*/
 	u_char		maxsync;	/* Maximum sync period factor	*/
 	u_char		maxoffs;	/* Max scsi offset		*/
 	u_char		multiplier;	/* Clock multiplier (1,2,4)	*/
 	u_char		clock_divn;	/* Number of clock divisors	*/
 	u_long		clock_khz;	/* SCSI clock frequency in KHz	*/
-	u_int		features;	/* Chip features map		*/
-
 
-	/*-----------------------------------------------
-	**	Link to the generic SCSI driver
-	**-----------------------------------------------
+	/*----------------------------------------------------------------
+	**	Start queue management.
+	**	It is filled up by the host processor and accessed by the 
+	**	SCRIPTS processor in order to start SCSI commands.
+	**----------------------------------------------------------------
+	*/
+	u_short		squeueput;	/* Next free slot of the queue	*/
+	u_short		actccbs;	/* Number of allocated CCBs	*/
+	u_short		queuedccbs;	/* Number of CCBs in start queue*/
+	u_short		queuedepth;	/* Start queue depth		*/
+
+	/*----------------------------------------------------------------
+	**	Timeout handler.
+	**----------------------------------------------------------------
 	*/
-
-	/* struct scsi_link	sc_link; */
-
-	/*-----------------------------------------------
-	**	Job control
-	**-----------------------------------------------
-	**
-	**	Commands from user
-	*/
-	struct usrcmd	user;
-	u_char		order;
-
-	/*
-	**	Target data
-	*/
-	struct tcb	target[MAX_TARGET];
-
-	/*
-	**	Start queue.
-	*/
-	u_int32		squeue [MAX_START];
-	u_short		squeueput;
-	u_short		actccbs;
-
-	/*
-	**	Timeout handler
-	*/
-#if 0
-	u_long		heartbeat;
-	u_short		ticks;
-	u_short		latetime;
-#endif
+	struct timer_list timer;	/* Timer handler link header	*/
 	u_long		lasttime;
+	u_long		settle_time;	/* Resetting the SCSI BUS	*/
 
-	/*-----------------------------------------------
-	**	Debug and profiling
-	**-----------------------------------------------
-	**
-	**	register dump
-	*/
-	struct ncr_reg	regdump;
-	u_long		regtime;
-
-	/*
-	**	Profiling data
-	*/
-	struct profile	profile;
-	u_long		disc_phys;
-	u_long		disc_ref;
-
-	/*
-	**	The global control block.
-	**	It's used only during the configuration phase.
-	**	A target control block will be created
-	**	after the first successful transfer.
-	*/
-	struct ccb      *ccb;
-
-	/*
-	**	message buffers.
-	**	Should be longword aligned,
-	**	because they're written with a
-	**	COPY script command.
-	*/
-	u_char		msgout[8];
-	u_char		msgin [8];
-	u_int32		lastmsg;
-
-	/*
-	**	Buffer for STATUS_IN phase.
-	*/
-	u_char		scratch;
-
-	/*
-	**	controller chip dependent maximal transfer width.
-	*/
-	u_char		maxwide;
-
-	/*
-	**	option for M_IDENTIFY message: enables disconnecting
-	*/
-	u_char		disc;
-
-	/*
-	**	address of the ncr control registers in io space
-	*/
-	u_int		port;
-
-	/*
-	**	irq level
+	/*----------------------------------------------------------------
+	**	Debugging and profiling.
+	**----------------------------------------------------------------
 	*/
-	u_short		irq;
+	struct ncr_reg	regdump;	/* Register dump		*/
+	u_long		regtime;	/* Time it has been done	*/
+#ifdef SCSI_NCR_PROFILE_SUPPORT
+	struct profile	profile;	/* Profiling data		*/
+	u_int		disc_phys;	/* Disconnection counters 	*/
+	u_int		disc_ref;
+#endif
+
+	/*----------------------------------------------------------------
+	**	Miscellaneous buffers accessed by the scripts-processor.
+	**	They shall be DWORD aligned, because they may be read or 
+	**	written with a SCR_COPY script command.
+	**----------------------------------------------------------------
+	*/
+	u_char		msgout[8];	/* Buffer for MESSAGE OUT 	*/
+	u_char		msgin [8];	/* Buffer for MESSAGE IN	*/
+	u_int32		lastmsg;	/* Last SCSI message sent	*/
+	u_char		scratch;	/* Scratch for SCSI receive	*/
+
+	/*----------------------------------------------------------------
+	**	Miscellaneous configuration and status parameters.
+	**----------------------------------------------------------------
+	*/
+	u_char		disc;		/* Diconnection allowed		*/
+	u_char		scsi_mode;	/* Current SCSI BUS mode	*/
+	u_char		order;		/* Tag order to use		*/
+	u_char		verbose;	/* Verbosity for this controller*/
+	int		ncr_cache;	/* Used for cache test at init.	*/
+
+	/*----------------------------------------------------------------
+	**	Command completion handling.
+	**----------------------------------------------------------------
+	*/
+#ifdef SCSI_NCR_CCB_DONE_SUPPORT
+	struct ccb	*(ccb_done[MAX_DONE]);
+	int		ccb_done_ic;
+#endif
+	/*----------------------------------------------------------------
+	**	Fields that should be removed or changed.
+	**----------------------------------------------------------------
+	*/
+	struct ccb	*ccb;		/* Global CCB			*/
+	struct usrcmd	user;		/* Command from user		*/
+	u_char		release_stage;	/* Synchronisation stage on release  */
 };
 
 #define NCB_SCRIPT_PHYS(np,lbl)	 (np->p_script  + offsetof (struct script, lbl))
-#define NCB_SCRIPTH_PHYS(np,lbl) (np->p_scripth + offsetof (struct scripth, lbl))
+#define NCB_SCRIPTH_PHYS(np,lbl) (np->p_scripth + offsetof (struct scripth,lbl))
 
 /*==========================================================
 **
@@ -1861,84 +1980,129 @@
 **	we reach them (for forward jumps).
 **	Therefore we declare a struct here.
 **	If you make changes inside the script,
-**	DON'T FORGET TO CHANGE THE LENGTHS HERE!
+**	DONT FORGET TO CHANGE THE LENGTHS HERE!
 **
 **----------------------------------------------------------
 */
 
 /*
-**	Script fragments which are loaded into the on-board RAM 
+**	Script fragments which are loaded into the on-chip RAM 
 **	of 825A, 875 and 895 chips.
 */
 struct script {
-	ncrcmd	start		[  4];
-	ncrcmd	start0		[  2];
-	ncrcmd	start1		[  3];
+	ncrcmd	start		[  5];
 	ncrcmd  startpos	[  1];
-	ncrcmd  trysel		[  8];
-	ncrcmd	skip		[  8];
-	ncrcmd	skip2		[  3];
-	ncrcmd  idle		[  2];
-	ncrcmd	select		[ 22];
-	ncrcmd	prepare		[  4];
-	ncrcmd	loadpos		[ 14];
-	ncrcmd	prepare2	[ 24];
-	ncrcmd	setmsg		[  5];
-	ncrcmd  clrack		[  2];
-	ncrcmd  dispatch	[ 38];
+	ncrcmd	select		[  6];
+	ncrcmd	select2		[  9];
+	ncrcmd	loadpos		[  4];
+	ncrcmd	send_ident	[  9];
+	ncrcmd	prepare		[  6];
+	ncrcmd	prepare2	[  7];
+#ifdef SCSI_NCR_PROFILE_SUPPORT
+	ncrcmd  command		[  9];
+#else
+	ncrcmd  command		[  6];
+#endif
+	ncrcmd  dispatch	[ 32];
+	ncrcmd  clrack		[  4];
 	ncrcmd	no_data		[ 17];
-	ncrcmd  checkatn	[ 10];
-	ncrcmd  command		[ 15];
-	ncrcmd  status		[ 27];
-	ncrcmd  msg_in		[ 26];
-	ncrcmd  msg_bad		[  6];
-	ncrcmd  complete	[ 13];
-	ncrcmd	cleanup		[ 12];
-	ncrcmd	cleanup0	[ 11];
-	ncrcmd	signal		[ 10];
-	ncrcmd  save_dp		[  5];
+#ifdef SCSI_NCR_PROFILE_SUPPORT
+	ncrcmd  status		[ 11];
+#else
+	ncrcmd  status		[  8];
+#endif
+	ncrcmd  msg_in		[  2];
+	ncrcmd  msg_in2		[ 16];
+	ncrcmd  msg_bad		[  4];
+	ncrcmd	setmsg		[  7];
+	ncrcmd	cleanup		[  6];
+	ncrcmd  complete	[  9];
+	ncrcmd	cleanup_ok	[  8];
+	ncrcmd	cleanup0	[  1];
+#ifndef SCSI_NCR_CCB_DONE_SUPPORT
+	ncrcmd	signal		[ 12];
+#else
+	ncrcmd	signal		[  9];
+	ncrcmd	done_pos	[  1];
+	ncrcmd	done_plug	[  2];
+	ncrcmd	done_end	[  7];
+#endif
+	ncrcmd  save_dp		[  7];
 	ncrcmd  restore_dp	[  5];
-	ncrcmd  disconnect	[ 12];
-	ncrcmd  disconnect0	[  5];
-	ncrcmd  disconnect1	[ 23];
+#ifdef SCSI_NCR_PROFILE_SUPPORT
+	ncrcmd  disconnect	[ 28];
+#else
+	ncrcmd  disconnect	[ 17];
+#endif
 	ncrcmd	msg_out		[  9];
 	ncrcmd	msg_out_done	[  7];
-	ncrcmd  badgetcc	[  6];
+	ncrcmd  idle		[  2];
 	ncrcmd	reselect	[  8];
-	ncrcmd	reselect1	[  8];
-	ncrcmd	reselect2	[  8];
-	ncrcmd	resel_tmp	[  5];
-	ncrcmd  resel_lun	[ 18];
-	ncrcmd	resel_tag	[ 24];
-	ncrcmd  data_io		[  6];
-	ncrcmd  data_in		[MAX_SCATTER * 4 + 4];
+	ncrcmd	reselected	[  8];
+	ncrcmd	resel_dsa	[  6];
+#ifdef SCSI_NCR_PROFILE_SUPPORT
+	ncrcmd	loadpos1	[  7];
+#else
+	ncrcmd	loadpos1	[  4];
+#endif
+	ncrcmd  resel_lun	[  6];
+	ncrcmd	resel_tag	[  6];
+	ncrcmd	jump_to_nexus	[  4];
+	ncrcmd	nexus_indirect	[  4];
+	ncrcmd	resel_notag	[  4];
+	ncrcmd  data_in		[MAX_SCATTERL * 4];
+	ncrcmd  data_in2	[  4];
+	ncrcmd  data_out	[MAX_SCATTERL * 4];
+	ncrcmd  data_out2	[  4];
 };
 
 /*
 **	Script fragments which stay in main memory for all chips.
 */
 struct scripth {
-	ncrcmd  tryloop		[MAX_START*5+2];
-	ncrcmd  msg_parity	[  6];
+	ncrcmd  tryloop		[MAX_START*2];
+	ncrcmd  tryloop2	[  2];
+#ifdef SCSI_NCR_CCB_DONE_SUPPORT
+	ncrcmd  done_queue	[MAX_DONE*5];
+	ncrcmd  done_queue2	[  2];
+#endif
+	ncrcmd	select_no_atn	[  8];
+	ncrcmd	cancel		[  4];
+	ncrcmd	skip		[  9];
+	ncrcmd	skip2		[ 19];
+	ncrcmd	par_err_data_in	[  6];
+	ncrcmd	par_err_other	[  4];
 	ncrcmd	msg_reject	[  8];
-	ncrcmd	msg_ign_residue	[ 32];
-	ncrcmd  msg_extended	[ 18];
-	ncrcmd  msg_ext_2	[ 18];
-	ncrcmd	msg_wdtr	[ 27];
-	ncrcmd  msg_ext_3	[ 18];
-	ncrcmd	msg_sdtr	[ 27];
+	ncrcmd	msg_ign_residue	[ 24];
+	ncrcmd  msg_extended	[ 10];
+	ncrcmd  msg_ext_2	[ 10];
+	ncrcmd	msg_wdtr	[ 14];
+	ncrcmd	send_wdtr	[  7];
+	ncrcmd  msg_ext_3	[ 10];
+	ncrcmd	msg_sdtr	[ 14];
+	ncrcmd	send_sdtr	[  7];
 	ncrcmd	msg_out_abort	[ 10];
-	ncrcmd  getcc		[  4];
-	ncrcmd  getcc1		[  5];
-#ifdef NCR_GETCC_WITHMSG
-	ncrcmd	getcc2		[ 33];
-#else
-	ncrcmd	getcc2		[ 14];
-#endif
-	ncrcmd	getcc3		[ 10];
-	ncrcmd  data_out	[MAX_SCATTER * 4 + 4];
+	ncrcmd  hdata_in	[MAX_SCATTERH * 4];
+	ncrcmd  hdata_in2	[  2];
+	ncrcmd  hdata_out	[MAX_SCATTERH * 4];
+	ncrcmd  hdata_out2	[  2];
+	ncrcmd	reset		[  4];
 	ncrcmd	aborttag	[  4];
-	ncrcmd	abort		[ 22];
+	ncrcmd	abort		[  2];
+	ncrcmd	abort_resel	[ 20];
+	ncrcmd	resend_ident	[  4];
+	ncrcmd	clratn_go_on	[  3];
+	ncrcmd	nxtdsp_go_on	[  1];
+	ncrcmd	sdata_in	[  8];
+	ncrcmd  data_io		[ 18];
+	ncrcmd	bad_identify	[ 12];
+	ncrcmd	bad_i_t_l	[  4];
+	ncrcmd	bad_i_t_l_q	[  4];
+	ncrcmd	bad_target	[  8];
+	ncrcmd	bad_status	[  8];
+	ncrcmd	start_ram	[  4];
+	ncrcmd	start_ram0	[  4];
+	ncrcmd	sto_restart	[  5];
 	ncrcmd	snooptest	[  9];
 	ncrcmd	snoopend	[  2];
 };
@@ -1952,13 +2116,18 @@
 **==========================================================
 */
 
-static	void	ncr_alloc_ccb	(ncb_p np, u_long t, u_long l);
+static	void	ncr_alloc_ccb	(ncb_p np, u_char tn, u_char ln);
 static	void	ncr_complete	(ncb_p np, ccb_p cp);
 static	void	ncr_exception	(ncb_p np);
-static	void	ncr_free_ccb	(ncb_p np, ccb_p cp, u_long t, u_long l);
+static	void	ncr_free_ccb	(ncb_p np, ccb_p cp);
+static	void	ncr_init_ccb	(ncb_p np, ccb_p cp);
+static	void	ncr_init_tcb	(ncb_p np, u_char tn);
+static	lcb_p	ncr_alloc_lcb	(ncb_p np, u_char tn, u_char ln);
+static	lcb_p	ncr_setup_lcb	(ncb_p np, u_char tn, u_char ln,
+				 u_char *inq_data);
 static	void	ncr_getclock	(ncb_p np, int mult);
 static	void	ncr_selectclock	(ncb_p np, u_char scntl3);
-static	ccb_p	ncr_get_ccb	(ncb_p np, u_long t,u_long l);
+static	ccb_p	ncr_get_ccb	(ncb_p np, u_char tn, u_char ln);
 static	void	ncr_init	(ncb_p np, int reset, char * msg, u_long code);
 static	int	ncr_int_sbmc	(ncb_p np);
 static	int	ncr_int_par	(ncb_p np);
@@ -1967,7 +2136,6 @@
 static  void    ncr_int_sto     (ncb_p np);
 static	u_long	ncr_lookup	(char* id);
 static	void	ncr_negotiate	(struct ncb* np, struct tcb* tp);
-static	void	ncr_opennings	(ncb_p np, lcb_p lp, Scsi_Cmnd * xp);
 
 #ifdef SCSI_NCR_PROFILE_SUPPORT
 static	void	ncb_profile	(ncb_p np, ccb_p cp);
@@ -1977,16 +2145,18 @@
 				(ncb_p np, ncrcmd *src, ncrcmd *dst, int len);
 static  void    ncr_script_fill (struct script * scr, struct scripth * scripth);
 static	int	ncr_scatter	(ccb_p cp, Scsi_Cmnd *cmd);
-static	void	ncr_setmaxtags	(ncb_p np, tcb_p tp, u_long numtags);
 static	void	ncr_getsync	(ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p);
 static	void	ncr_setsync	(ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer);
-static	void	ncr_settags     (tcb_p tp, lcb_p lp);
+static	void	ncr_setup_tags	(ncb_p np, u_char tn, u_char ln);
 static	void	ncr_setwide	(ncb_p np, ccb_p cp, u_char wide, u_char ack);
 static	int	ncr_show_msg	(u_char * msg);
 static	int	ncr_snooptest	(ncb_p np);
 static	void	ncr_timeout	(ncb_p np);
 static  void    ncr_wakeup      (ncb_p np, u_long code);
-static	void	ncr_start_reset	(ncb_p np, int settle_delay);
+static  void    ncr_wakeup_done (ncb_p np);
+static	void	ncr_start_next_ccb (ncb_p np, lcb_p lp, int maxn);
+static	void	ncr_put_start_queue(ncb_p np, ccb_p cp);
+static	void	ncr_start_reset	(ncb_p np);
 static	int	ncr_reset_scsi_bus (ncb_p np, int enab_int, int settle_delay);
 
 #ifdef SCSI_NCR_USER_COMMAND_SUPPORT
@@ -2075,38 +2245,17 @@
 
 static	struct script script0 __initdata = {
 /*--------------------------< START >-----------------------*/ {
-#if 0
 	/*
-	**	Claim to be still alive ...
+	**	This NOP will be patched with LED ON
+	**	SCR_REG_REG (gpreg, SCR_AND, 0xfe)
 	*/
-	SCR_COPY (sizeof (((struct ncb *)0)->heartbeat)),
-		KVAR(SCRIPT_KVAR_JIFFIES),
-		NADDR (heartbeat),
-#endif
+	SCR_NO_OP,
+		0,
 	/*
-	**      Make data structure address invalid.
-	**      clear SIGP.
+	**      Clear SIGP.
 	*/
-	SCR_LOAD_REG (dsa, 0xff),
-		0,
 	SCR_FROM_REG (ctest2),
 		0,
-}/*-------------------------< START0 >----------------------*/,{
-	/*
-	**	Hook for interrupted GetConditionCode.
-	**	Will be patched to ... IFTRUE by
-	**	the interrupt handler.
-	*/
-	SCR_INT ^ IFFALSE (0),
-		SIR_SENSE_RESTART,
-
-}/*-------------------------< START1 >----------------------*/,{
-	/*
-	**	Hook for stalled start queue.
-	**	Will be patched to IFTRUE by the interrupt handler.
-	*/
-	SCR_INT ^ IFFALSE (0),
-		SIR_STALL_RESTART,
 	/*
 	**	Then jump to a certain point in tryloop.
 	**	Due to the lack of indirect addressing the code
@@ -2115,76 +2264,6 @@
 	SCR_JUMP,
 }/*-------------------------< STARTPOS >--------------------*/,{
 		PADDRH(tryloop),
-}/*-------------------------< TRYSEL >----------------------*/,{
-	/*
-	**	Now:
-	**	DSA: Address of a Data Structure
-	**	or   Address of the IDLE-Label.
-	**
-	**	TEMP:	Address of a script, which tries to
-	**		start the NEXT entry.
-	**
-	**	Save the TEMP register into the SCRATCHA register.
-	**	Then copy the DSA to TEMP and RETURN.
-	**	This is kind of an indirect jump.
-	**	(The script processor has NO stack, so the
-	**	CALL is actually a jump and link, and the
-	**	RETURN is an indirect jump.)
-	**
-	**	If the slot was empty, DSA contains the address
-	**	of the IDLE part of this script. The processor
-	**	jumps to IDLE and waits for a reselect.
-	**	It will wake up and try the same slot again
-	**	after the SIGP bit becomes set by the host.
-	**
-	**	If the slot was not empty, DSA contains
-	**	the address of the phys-part of a ccb.
-	**	The processor jumps to this address.
-	**	phys starts with head,
-	**	head starts with launch,
-	**	so actually the processor jumps to
-	**	the lauch part.
-	**	If the entry is scheduled for execution,
-	**	then launch contains a jump to SELECT.
-	**	If it's not scheduled, it contains a jump to IDLE.
-	*/
-	SCR_COPY (4),
-		RADDR (temp),
-		RADDR (scratcha),
-	SCR_COPY (4),
-		RADDR (dsa),
-		RADDR (temp),
-	SCR_RETURN,
-		0
-
-}/*-------------------------< SKIP >------------------------*/,{
-	/*
-	**	This entry has been canceled.
-	**	Next time use the next slot.
-	*/
-	SCR_COPY (4),
-		RADDR (scratcha),
-		PADDR (startpos),
-	/*
-	**	patch the launch field.
-	**	should look like an idle process.
-	*/
-	SCR_COPY_F (4),
-		RADDR (dsa),
-		PADDR (skip2),
-	SCR_COPY (8),
-		PADDR (idle),
-}/*-------------------------< SKIP2 >-----------------------*/,{
-		0,
-	SCR_JUMP,
-		PADDR(start),
-}/*-------------------------< IDLE >------------------------*/,{
-	/*
-	**	Nothing to do?
-	**	Wait for reselect.
-	*/
-	SCR_JUMP,
-		PADDR(reselect),
 
 }/*-------------------------< SELECT >----------------------*/,{
 	/*
@@ -2201,7 +2280,7 @@
 
 	SCR_CLR (SCR_TRG),
 		0,
-	SCR_LOAD_REG (HS_REG, 0xff),
+	SCR_LOAD_REG (HS_REG, HS_SELECTING),
 		0,
 
 	/*
@@ -2210,6 +2289,7 @@
 	SCR_SEL_TBL_ATN ^ offsetof (struct dsb, select),
 		PADDR (reselect),
 
+}/*-------------------------< SELECT2 >----------------------*/,{
 	/*
 	**	Now there are 4 possibilities:
 	**
@@ -2222,50 +2302,31 @@
 	**	Then the script processor takes the jump
 	**	to the RESELECT label.
 	**
-	**	(3) The ncr completes the selection.
-	**	Then it will execute the next statement.
-	**
-	**	(4) There is a selection timeout.
-	**	Then the ncr should interrupt the host and stop.
-	**	Unfortunately, it seems to continue execution
-	**	of the script. But it will fail with an
-	**	IID-interrupt on the next WHEN.
-	*/
-
-	SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_IN)),
-		0,
-
-	/*
-	**	Save target id to ctest0 register
+	**	(3) The ncr wins arbitration.
+	**	Then it will execute SCRIPTS instruction until 
+	**	the next instruction that checks SCSI phase.
+	**	Then will stop and wait for selection to be 
+	**	complete or selection time-out to occur.
+	**	As a result the SCRIPTS instructions until 
+	**	LOADPOS + 2 should be executed in parallel with 
+	**	the SCSI core performing selection.
 	*/
 
-	SCR_FROM_REG (sdid),
-		0,
-	SCR_TO_REG (ctest0),
-		0,
 	/*
-	**	Send the IDENTIFY and SIMPLE_TAG messages
-	**	(and the M_X_SYNC_REQ message)
+	**	The M_REJECT problem seems to be due to a selection 
+	**	timing problem.
+	**	Wait immediately for the selection to complete. 
+	**	(2.5x behaves so)
 	*/
-	SCR_MOVE_TBL ^ SCR_MSG_OUT,
-		offsetof (struct dsb, smsg),
-#ifdef undef /* XXX better fail than try to deal with this ... */
-	SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_OUT)),
-		-16,
-#endif
-	SCR_CLR (SCR_ATN),
+	SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_OUT)),
 		0,
-	SCR_COPY (1),
-		RADDR (sfbr),
-		NADDR (lastmsg),
+
 	/*
-	**	Selection complete.
 	**	Next time use the next slot.
 	*/
 	SCR_COPY (4),
-		RADDR (scratcha),
+		RADDR (temp),
 		PADDR (startpos),
-}/*-------------------------< PREPARE >----------------------*/,{
 	/*
 	**      The ncr doesn't have an indirect load
 	**	or store command. So we have to
@@ -2285,22 +2346,32 @@
 	/*
 	**	continued after the next label ...
 	*/
-
 }/*-------------------------< LOADPOS >---------------------*/,{
 		0,
 		NADDR (header),
 	/*
-	**      Mark this ccb as not scheduled.
+	**	Wait for the next phase or the selection
+	**	to complete or time-out.
 	*/
-	SCR_COPY (8),
-		PADDR (idle),
-		NADDR (header.launch),
+	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+		PADDR (prepare),
+
+}/*-------------------------< SEND_IDENT >----------------------*/,{
 	/*
-	**      Set a time stamp for this selection
+	**	Selection complete.
+	**	Send the IDENTIFY and SIMPLE_TAG messages
+	**	(and the M_X_SYNC_REQ message)
 	*/
-	SCR_COPY (sizeof (u_long)),
-		KVAR(SCRIPT_KVAR_JIFFIES),
-		NADDR (header.stamp.select),
+	SCR_MOVE_TBL ^ SCR_MSG_OUT,
+		offsetof (struct dsb, smsg),
+	SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)),
+		PADDRH (resend_ident),
+	SCR_LOAD_REG (scratcha, 0x80),
+		0,
+	SCR_COPY (1),
+		RADDR (scratcha),
+		NADDR (lastmsg),
+}/*-------------------------< PREPARE >----------------------*/,{
 	/*
 	**      load the savep (saved pointer) into
 	**      the TEMP register (actual pointer)
@@ -2314,74 +2385,61 @@
 	SCR_COPY (4),
 		NADDR (header.status),
 		RADDR (scr0),
-
 }/*-------------------------< PREPARE2 >---------------------*/,{
 	/*
-	**      Load the synchronous mode register
-	*/
-	SCR_COPY (1),
-		NADDR (sync_st),
-		RADDR (sxfer),
-	/*
-	**      Load the wide mode and timing register
-	*/
-	SCR_COPY (1),
-		NADDR (wide_st),
-		RADDR (scntl3),
-	/*
-	**	Initialize the msgout buffer with a NOOP message.
+	**	Initialize the msgout buffer with a NOOP message.
 	*/
 	SCR_LOAD_REG (scratcha, M_NOOP),
 		0,
 	SCR_COPY (1),
 		RADDR (scratcha),
 		NADDR (msgout),
+#if 0
 	SCR_COPY (1),
 		RADDR (scratcha),
 		NADDR (msgin),
+#endif
 	/*
-	**	Message in phase ?
+	**	Anticipate the COMMAND phase.
+	**	This is the normal case for initial selection.
 	*/
-	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+	SCR_JUMP ^ IFFALSE (WHEN (SCR_COMMAND)),
 		PADDR (dispatch),
+
+}/*-------------------------< COMMAND >--------------------*/,{
+#ifdef SCSI_NCR_PROFILE_SUPPORT
 	/*
-	**	Extended or reject message ?
+	**	... set a timestamp ...
 	*/
-	SCR_FROM_REG (sbdl),
-		0,
-	SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)),
-		PADDR (msg_in),
-	SCR_JUMP ^ IFTRUE (DATA (M_REJECT)),
-		PADDRH (msg_reject),
+	SCR_COPY (sizeof (u_long)),
+		KVAR(SCRIPT_KVAR_JIFFIES),
+		NADDR (header.stamp.command),
+#endif
 	/*
-	**	normal processing
+	**	... and send the command
 	*/
-	SCR_JUMP,
-		PADDR (dispatch),
-}/*-------------------------< SETMSG >----------------------*/,{
-	SCR_COPY (1),
-		RADDR (scratcha),
-		NADDR (msgout),
-	SCR_SET (SCR_ATN),
-		0,
-}/*-------------------------< CLRACK >----------------------*/,{
+	SCR_MOVE_TBL ^ SCR_COMMAND,
+		offsetof (struct dsb, cmd),
 	/*
-	**	Terminate possible pending message phase.
+	**	If status is still HS_NEGOTIATE, negotiation failed.
+	**	We check this here, since we want to do that 
+	**	only once.
 	*/
-	SCR_CLR (SCR_ACK),
-		0,
-
-}/*-----------------------< DISPATCH >----------------------*/,{
 	SCR_FROM_REG (HS_REG),
 		0,
 	SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)),
 		SIR_NEGO_FAILED,
+
+}/*-----------------------< DISPATCH >----------------------*/,{
 	/*
-	**	remove bogus output signals
+	**	MSG_IN is the only phase that shall be 
+	**	entered at least once for each (re)selection.
+	**	So we test it first.
 	*/
-	SCR_REG_REG (socl, SCR_AND, CACK|CATN),
-		0,
-	SCR_RETURN ^ IFTRUE (WHEN (SCR_DATA_OUT)),
+	SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)),
+		PADDR (msg_in),
+
+	SCR_RETURN ^ IFTRUE (IF (SCR_DATA_OUT)),
 		0,
 	/*
 	**	DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 4.
@@ -2396,16 +2454,13 @@
 		RADDR (scratcha),
 		RADDR (scratcha),
 	SCR_RETURN,
-		0,
-
-	SCR_JUMP ^ IFTRUE (IF (SCR_MSG_OUT)),
-		PADDR (msg_out),
-	SCR_JUMP ^ IFTRUE (IF (SCR_MSG_IN)),
-		PADDR (msg_in),
-	SCR_JUMP ^ IFTRUE (IF (SCR_COMMAND)),
-		PADDR (command),
+ 		0,
 	SCR_JUMP ^ IFTRUE (IF (SCR_STATUS)),
 		PADDR (status),
+	SCR_JUMP ^ IFTRUE (IF (SCR_COMMAND)),
+		PADDR (command),
+	SCR_JUMP ^ IFTRUE (IF (SCR_MSG_OUT)),
+		PADDR (msg_out),
 	/*
 	**      Discard one illegal phase byte, if required.
 	*/
@@ -2425,6 +2480,15 @@
 	SCR_JUMP,
 		PADDR (dispatch),
 
+}/*-------------------------< CLRACK >----------------------*/,{
+	/*
+	**	Terminate possible pending message phase.
+	*/
+	SCR_CLR (SCR_ACK),
+		0,
+	SCR_JUMP,
+		PADDR (dispatch),
+
 }/*-------------------------< NO_DATA >--------------------*/,{
 	/*
 	**	The target wants to tranfer too much data
@@ -2454,116 +2518,31 @@
 		PADDR (dispatch),
 	SCR_JUMP,
 		PADDR (no_data),
-}/*-------------------------< CHECKATN >--------------------*/,{
-	/*
-	**	If AAP (bit 1 of scntl0 register) is set
-	**	and a parity error is detected,
-	**	the script processor asserts ATN.
-	**
-	**	The target should switch to a MSG_OUT phase
-	**	to get the message.
-	*/
-	SCR_FROM_REG (socl),
-		0,
-	SCR_JUMP ^ IFFALSE (MASK (CATN, CATN)),
-		PADDR (dispatch),
-	/*
-	**	count it
-	*/
-	SCR_REG_REG (PS_REG, SCR_ADD, 1),
-		0,
-	/*
-	**	Prepare a M_ID_ERROR message
-	**	(initiator detected error).
-	**	The target should retry the transfer.
-	*/
-	SCR_LOAD_REG (scratcha, M_ID_ERROR),
-		0,
-	SCR_JUMP,
-		PADDR (setmsg),
-
-}/*-------------------------< COMMAND >--------------------*/,{
-	/*
-	**	If this is not a GETCC transfer ...
-	*/
-	SCR_FROM_REG (SS_REG),
-		0,
-/*<<<*/	SCR_JUMPR ^ IFTRUE (DATA (S_CHECK_COND)),
-		28,
-	/*
-	**	... set a timestamp ...
-	*/
-	SCR_COPY (sizeof (u_long)),
-		KVAR(SCRIPT_KVAR_JIFFIES),
-		NADDR (header.stamp.command),
-	/*
-	**	... and send the command
-	*/
-	SCR_MOVE_TBL ^ SCR_COMMAND,
-		offsetof (struct dsb, cmd),
-	SCR_JUMP,
-		PADDR (dispatch),
-	/*
-	**	Send the GETCC command
-	*/
-/*>>>*/	SCR_MOVE_TBL ^ SCR_COMMAND,
-		offsetof (struct dsb, scmd),
-	SCR_JUMP,
-		PADDR (dispatch),
 
 }/*-------------------------< STATUS >--------------------*/,{
+#ifdef SCSI_NCR_PROFILE_SUPPORT
 	/*
 	**	set the timestamp.
 	*/
 	SCR_COPY (sizeof (u_long)),
 		KVAR(SCRIPT_KVAR_JIFFIES),
 		NADDR (header.stamp.status),
-	/*
-	**	If this is a GETCC transfer,
-	*/
-	SCR_FROM_REG (SS_REG),
-		0,
-/*<<<*/	SCR_JUMPR ^ IFFALSE (DATA (S_CHECK_COND)),
-		40,
+#endif
 	/*
 	**	get the status
 	*/
 	SCR_MOVE_ABS (1) ^ SCR_STATUS,
 		NADDR (scratch),
 	/*
-	**	Save status to scsi_status.
-	**	Mark as complete.
-	**	And wait for disconnect.
-	*/
-	SCR_TO_REG (SS_REG),
-		0,
-	SCR_REG_REG (SS_REG, SCR_OR, S_SENSE),
-		0,
-	SCR_LOAD_REG (HS_REG, HS_COMPLETE),
-		0,
-	SCR_JUMP,
-		PADDR (checkatn),
-	/*
-	**	If it was no GETCC transfer,
-	**	save the status to scsi_status.
+	**	save status to scsi_status.
+	**	mark as complete.
 	*/
-/*>>>*/	SCR_MOVE_ABS (1) ^ SCR_STATUS,
-		NADDR (scratch),
 	SCR_TO_REG (SS_REG),
 		0,
-	/*
-	**	if it was no check condition ...
-	*/
-	SCR_JUMP ^ IFTRUE (DATA (S_CHECK_COND)),
-		PADDR (checkatn),
-	/*
-	**	... mark as complete.
-	*/
 	SCR_LOAD_REG (HS_REG, HS_COMPLETE),
 		0,
 	SCR_JUMP,
-		PADDR (checkatn),
-
+		PADDR (dispatch),
 }/*-------------------------< MSG_IN >--------------------*/,{
 	/*
 	**	Get the first byte of the message
@@ -2574,28 +2553,18 @@
 	*/
 	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
 		NADDR (msgin[0]),
+}/*-------------------------< MSG_IN2 >--------------------*/,{
 	/*
-	**	Check for message parity error.
-	*/
-	SCR_TO_REG (scratcha),
-		0,
-	SCR_FROM_REG (socl),
-		0,
-	SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
-		PADDRH (msg_parity),
-	SCR_FROM_REG (scratcha),
-		0,
-	/*
-	**	Parity was ok, handle this message.
+	**	Handle this message.
 	*/
 	SCR_JUMP ^ IFTRUE (DATA (M_COMPLETE)),
 		PADDR (complete),
+	SCR_JUMP ^ IFTRUE (DATA (M_DISCONNECT)),
+		PADDR (disconnect),
 	SCR_JUMP ^ IFTRUE (DATA (M_SAVE_DP)),
 		PADDR (save_dp),
 	SCR_JUMP ^ IFTRUE (DATA (M_RESTORE_DP)),
 		PADDR (restore_dp),
-	SCR_JUMP ^ IFTRUE (DATA (M_DISCONNECT)),
-		PADDR (disconnect),
 	SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)),
 		PADDRH (msg_extended),
 	SCR_JUMP ^ IFTRUE (DATA (M_NOOP)),
@@ -2619,24 +2588,42 @@
 		SIR_REJECT_SENT,
 	SCR_LOAD_REG (scratcha, M_REJECT),
 		0,
+}/*-------------------------< SETMSG >----------------------*/,{
+	SCR_COPY (1),
+		RADDR (scratcha),
+		NADDR (msgout),
+	SCR_SET (SCR_ATN),
+		0,
+	SCR_JUMP,
+		PADDR (clrack),
+}/*-------------------------< CLEANUP >-------------------*/,{
+	/*
+	**      dsa:    Pointer to ccb
+	**	      or xxxxxxFF (no ccb)
+	**
+	**      HS_REG:   Host-Status (<>0!)
+	*/
+	SCR_FROM_REG (dsa),
+		0,
+	SCR_JUMP ^ IFTRUE (DATA (0xff)),
+		PADDR (start),
+	/*
+	**      dsa is valid.
+	**	complete the cleanup.
+	*/
 	SCR_JUMP,
-		PADDR (setmsg),
+		PADDR (cleanup_ok),
 
 }/*-------------------------< COMPLETE >-----------------*/,{
 	/*
 	**	Complete message.
 	**
-	**	If it's not the get condition code,
-	**	copy TEMP register to LASTP in header.
+	**	Copy TEMP register to LASTP in header.
 	*/
-	SCR_FROM_REG (SS_REG),
-		0,
-/*<<<*/	SCR_JUMPR ^ IFTRUE (MASK (S_SENSE, S_SENSE)),
-		12,
 	SCR_COPY (4),
 		RADDR (temp),
 		NADDR (header.lastp),
-/*>>>*/	/*
+	/*
 	**	When we terminate the cycle by clearing ACK,
 	**	the target may disconnect immediately.
 	**
@@ -2656,20 +2643,9 @@
 	*/
 	SCR_WAIT_DISC,
 		0,
-}/*-------------------------< CLEANUP >-------------------*/,{
-	/*
-	**      dsa:    Pointer to ccb
-	**	      or xxxxxxFF (no ccb)
-	**
-	**      HS_REG:   Host-Status (<>0!)
-	*/
-	SCR_FROM_REG (dsa),
-		0,
-	SCR_JUMP ^ IFTRUE (DATA (0xff)),
-		PADDR (signal),
+}/*-------------------------< CLEANUP_OK >----------------*/,{
 	/*
-	**      dsa is valid.
-	**	save the status registers
+	**	Save host status to header.
 	*/
 	SCR_COPY (4),
 		RADDR (scr0),
@@ -2684,43 +2660,32 @@
 		NADDR (header),
 }/*-------------------------< CLEANUP0 >--------------------*/,{
 		0,
-
+}/*-------------------------< SIGNAL >----------------------*/,{
 	/*
-	**	If command resulted in "check condition"
-	**	status and is not yet completed,
-	**	try to get the condition code.
+	**	if job not completed ...
 	*/
 	SCR_FROM_REG (HS_REG),
 		0,
-/*<<<*/	SCR_JUMPR ^ IFFALSE (MASK (0, HS_DONEMASK)),
-		16,
-	SCR_FROM_REG (SS_REG),
-		0,
-	SCR_JUMP ^ IFTRUE (DATA (S_CHECK_COND)),
-		PADDRH(getcc2),
 	/*
-	**	And make the DSA register invalid.
+	**	... start the next command.
 	*/
-/*>>>*/	SCR_LOAD_REG (dsa, 0xff), /* invalid */
-		0,
-}/*-------------------------< SIGNAL >----------------------*/,{
+	SCR_JUMP ^ IFTRUE (MASK (0, (HS_DONEMASK|HS_SKIPMASK))),
+		PADDR(start),
 	/*
-	**	if status = queue full,
-	**	reinsert in startqueue and stall queue.
+	**	If command resulted in not GOOD status,
+	**	call the C code if needed.
 	*/
 	SCR_FROM_REG (SS_REG),
 		0,
-	SCR_INT ^ IFTRUE (DATA (S_QUEUE_FULL)),
-		SIR_STALL_QUEUE,
-	/*
-	**	if job completed ...
-	*/
-	SCR_FROM_REG (HS_REG),
-		0,
+	SCR_CALL ^ IFFALSE (DATA (S_GOOD)),
+		PADDRH (bad_status),
+
+#ifndef	SCSI_NCR_CCB_DONE_SUPPORT
+
 	/*
 	**	... signal completion to the host
 	*/
-	SCR_INT_FLY ^ IFFALSE (MASK (0, HS_DONEMASK)),
+	SCR_INT_FLY,
 		0,
 	/*
 	**	Auf zu neuen Schandtaten!
@@ -2728,6 +2693,28 @@
 	SCR_JUMP,
 		PADDR(start),
 
+#else	/* defined SCSI_NCR_CCB_DONE_SUPPORT */
+
+	/*
+	**	... signal completion to the host
+	*/
+	SCR_JUMP,
+}/*------------------------< DONE_POS >---------------------*/,{
+		PADDRH (done_queue),
+}/*------------------------< DONE_PLUG >--------------------*/,{
+	SCR_INT,
+		SIR_DONE_OVERFLOW,
+}/*------------------------< DONE_END >---------------------*/,{
+	SCR_INT_FLY,
+		0,
+	SCR_COPY (4),
+		RADDR (temp),
+		PADDR (done_pos),
+	SCR_JUMP,
+		PADDR (start),
+
+#endif	/* SCSI_NCR_CCB_DONE_SUPPORT */
+
 }/*-------------------------< SAVE_DP >------------------*/,{
 	/*
 	**	SAVE_DP message:
@@ -2736,8 +2723,10 @@
 	SCR_COPY (4),
 		RADDR (temp),
 		NADDR (header.savep),
+	SCR_CLR (SCR_ACK),
+		0,
 	SCR_JUMP,
-		PADDR (clrack),
+		PADDR (dispatch),
 }/*-------------------------< RESTORE_DP >---------------*/,{
 	/*
 	**	RESTORE_DP message:
@@ -2751,56 +2740,6 @@
 
 }/*-------------------------< DISCONNECT >---------------*/,{
 	/*
-	**	If QUIRK_AUTOSAVE is set,
-	**	do an "save pointer" operation.
-	*/
-	SCR_FROM_REG (QU_REG),
-		0,
-/*<<<*/	SCR_JUMPR ^ IFFALSE (MASK (QUIRK_AUTOSAVE, QUIRK_AUTOSAVE)),
-		12,
-	/*
-	**	like SAVE_DP message:
-	**	Copy TEMP register to SAVEP in header.
-	*/
-	SCR_COPY (4),
-		RADDR (temp),
-		NADDR (header.savep),
-/*>>>*/	/*
-	**	Check if temp==savep or temp==goalp:
-	**	if not, log a missing save pointer message.
-	**	In fact, it's a comparison mod 256.
-	**
-	**	Hmmm, I hadn't thought that I would be urged to
-	**	write this kind of ugly self modifying code.
-	**
-	**	It's unbelievable, but the ncr53c8xx isn't able
-	**	to subtract one register from another.
-	*/
-	SCR_FROM_REG (temp),
-		0,
-	/*
-	**	You are not expected to understand this ..
-	**
-	**	CAUTION: only little endian architectures supported! XXX
-	*/
-	SCR_COPY_F (1),
-		NADDR (header.savep),
-		PADDR (disconnect0),
-}/*-------------------------< DISCONNECT0 >--------------*/,{
-/*<<<*/	SCR_JUMPR ^ IFTRUE (DATA (1)),
-		20,
-	/*
-	**	neither this
-	*/
-	SCR_COPY_F (1),
-		NADDR (header.goalp),
-		PADDR (disconnect1),
-}/*-------------------------< DISCONNECT1 >--------------*/,{
-	SCR_INT ^ IFFALSE (DATA (1)),
-		SIR_MISSING_SAVE,
-/*>>>*/
-
-	/*
 	**	DISCONNECTing  ...
 	**
 	**	disable the "unexpected disconnect" feature,
@@ -2815,6 +2754,7 @@
 	*/
 	SCR_WAIT_DISC,
 		0,
+#ifdef SCSI_NCR_PROFILE_SUPPORT
 	/*
 	**	Profiling:
 	**	Set a time stamp,
@@ -2825,19 +2765,35 @@
 		NADDR (header.stamp.disconnect),
 	SCR_COPY (4),
 		NADDR (disc_phys),
-		RADDR (temp),
-	SCR_REG_REG (temp, SCR_ADD, 0x01),
+		RADDR (scratcha),
+	SCR_REG_REG (scratcha, SCR_ADD, 0x01),
 		0,
 	SCR_COPY (4),
-		RADDR (temp),
+		RADDR (scratcha),
 		NADDR (disc_phys),
+#endif
 	/*
 	**	Status is: DISCONNECTED.
 	*/
 	SCR_LOAD_REG (HS_REG, HS_DISCONNECT),
 		0,
+	/*
+	**	If QUIRK_AUTOSAVE is set,
+	**	do an "save pointer" operation.
+	*/
+	SCR_FROM_REG (QU_REG),
+		0,
+	SCR_JUMP ^ IFFALSE (MASK (QUIRK_AUTOSAVE, QUIRK_AUTOSAVE)),
+		PADDR (cleanup_ok),
+	/*
+	**	like SAVE_DP message:
+	**	Copy TEMP register to SAVEP in header.
+	*/
+	SCR_COPY (4),
+		RADDR (temp),
+		NADDR (header.savep),
 	SCR_JUMP,
-		PADDR (cleanup),
+		PADDR (cleanup_ok),
 
 }/*-------------------------< MSG_OUT >-------------------*/,{
 	/*
@@ -2846,7 +2802,7 @@
 	SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
 		NADDR (msgout),
 	SCR_COPY (1),
-		RADDR (sfbr),
+		NADDR (msgout),
 		NADDR (lastmsg),
 	/*
 	**	If it was no ABORT message ...
@@ -2873,23 +2829,16 @@
 	*/
 	SCR_JUMP,
 		PADDR (dispatch),
-}/*------------------------< BADGETCC >---------------------*/,{
-	/*
-	**	If SIGP was set, clear it and try again.
-	*/
-	SCR_FROM_REG (ctest2),
-		0,
-	SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)),
-		PADDRH (getcc2),
-	SCR_INT,
-		SIR_SENSE_FAILED,
-}/*-------------------------< RESELECT >--------------------*/,{
+}/*-------------------------< IDLE >------------------------*/,{
 	/*
+	**	Nothing to do?
+	**	Wait for reselect.
 	**	This NOP will be patched with LED OFF
 	**	SCR_REG_REG (gpreg, SCR_OR, 0x01)
 	*/
 	SCR_NO_OP,
 		0,
+}/*-------------------------< RESELECT >--------------------*/,{
 	/*
 	**	make the DSA invalid.
 	*/
@@ -2897,6 +2846,8 @@
 		0,
 	SCR_CLR (SCR_TRG),
 		0,
+	SCR_LOAD_REG (HS_REG, HS_IN_RESELECT),
+		0,
 	/*
 	**	Sleep waiting for a reselection.
 	**	If SIGP is set, special treatment.
@@ -2904,8 +2855,8 @@
 	**	Zu allem bereit ..
 	*/
 	SCR_WAIT_RESEL,
-		PADDR(reselect2),
-}/*-------------------------< RESELECT1 >--------------------*/,{
+		PADDR(start),
+}/*-------------------------< RESELECTED >------------------*/,{
 	/*
 	**	This NOP will be patched with LED ON
 	**	SCR_REG_REG (gpreg, SCR_AND, 0xfe)
@@ -2927,38 +2878,51 @@
 	*/
 	SCR_REG_SFBR (ssid, SCR_AND, 0x8F),
 		0,
-	SCR_TO_REG (ctest0),
+	SCR_TO_REG (sdid),
 		0,
 	SCR_JUMP,
 		NADDR (jump_tcb),
-}/*-------------------------< RESELECT2 >-------------------*/,{
+
+}/*-------------------------< RESEL_DSA >-------------------*/,{
 	/*
-	**	This NOP will be patched with LED ON
-	**	SCR_REG_REG (gpreg, SCR_AND, 0xfe)
+	**	Ack the IDENTIFY or TAG previously received.
 	*/
-	SCR_NO_OP,
+	SCR_CLR (SCR_ACK),
 		0,
 	/*
-	**	If it's not connected :(
-	**	-> interrupted by SIGP bit.
-	**	Jump to start.
+	**      The ncr doesn't have an indirect load
+	**	or store command. So we have to
+	**	copy part of the control block to a
+	**	fixed place, where we can access it.
+	**
+	**	We patch the address part of a
+	**	COPY command with the DSA-register.
+	*/
+	SCR_COPY_F (4),
+		RADDR (dsa),
+		PADDR (loadpos1),
+	/*
+	**	then we do the actual copy.
+	*/
+	SCR_COPY (sizeof (struct head)),
+	/*
+	**	continued after the next label ...
 	*/
-	SCR_FROM_REG (ctest2),
-		0,
-	SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)),
-		PADDR (start),
-	SCR_JUMP,
-		PADDR (reselect),
 
-}/*-------------------------< RESEL_TMP >-------------------*/,{
+}/*-------------------------< LOADPOS1 >-------------------*/,{
+		0,
+		NADDR (header),
+#ifdef SCSI_NCR_PROFILE_SUPPORT
 	/*
-	**	The return address in TEMP
-	**	is in fact the data structure address,
-	**	so copy it to the DSA register.
+	**      Set a time stamp for this reselection
+	*/
+	SCR_COPY (sizeof (u_long)),
+		KVAR(SCRIPT_KVAR_JIFFIES),
+		NADDR (header.stamp.reselect),
+#endif
+	/*
+	**	The DSA contains the data structure address.
 	*/
-	SCR_COPY (4),
-		RADDR (temp),
-		RADDR (dsa),
 	SCR_JUMP,
 		PADDR (prepare),
 
@@ -2968,133 +2932,111 @@
 	**	to get an IDENTIFY message
 	**	Wait for a msg_in phase.
 	*/
-/*<<<*/	SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
-		48,
+	SCR_INT ^ IFFALSE (WHEN (SCR_MSG_IN)),
+		SIR_RESEL_NO_MSG_IN,
 	/*
-	**	message phase
-	**	It's not a sony, it's a trick:
-	**	read the data without acknowledging it.
+	**	message phase.
+	**	Read the data directly from the BUS DATA lines.
+	**	This helps to support very old SCSI devices that 
+	**	may reselect without sending an IDENTIFY.
 	*/
 	SCR_FROM_REG (sbdl),
 		0,
-/*<<<*/	SCR_JUMPR ^ IFFALSE (MASK (M_IDENTIFY, 0x98)),
-		32,
-	/*
-	**	It WAS an Identify message.
-	**	get it and ack it!
-	*/
-	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
-		NADDR (msgin),
-	SCR_CLR (SCR_ACK),
-		0,
 	/*
-	**	Mask out the lun.
+	**	It should be an Identify message.
 	*/
-	SCR_REG_REG (sfbr, SCR_AND, 0x07),
-		0,
-	SCR_RETURN,
-		0,
-	/*
-	**	No message phase or no IDENTIFY message:
-	**	return 0.
-	*/
-/*>>>*/	SCR_LOAD_SFBR (0),
-		0,
 	SCR_RETURN,
 		0,
-
 }/*-------------------------< RESEL_TAG >-------------------*/,{
 	/*
-	**	come back to this point
-	**	to get a SIMPLE_TAG message
-	**	Wait for a MSG_IN phase.
+	**	Read IDENTIFY + SIMPLE + TAG using a single MOVE.
+	**	Agressive optimization, is'nt it?
+	**	No need to test the SIMPLE TAG message, since the 
+	**	driver only supports conformant devices for tags. ;-)
 	*/
-/*<<<*/	SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
-		64,
+	SCR_MOVE_ABS (3) ^ SCR_MSG_IN,
+		NADDR (msgin),
 	/*
-	**	message phase
-	**	It's a trick - read the data
-	**	without acknowledging it.
+	**	Read the TAG from the SIDL.
+	**	Still an aggressive optimization. ;-)
+	**	Compute the CCB indirect jump address which 
+	**	is (#TAG*2 & 0xfc) due to tag numbering using 
+	**	1,3,5..MAXTAGS*2+1 actual values.
 	*/
-	SCR_FROM_REG (sbdl),
+	SCR_REG_SFBR (sidl, SCR_SHL, 0),
 		0,
-/*<<<*/	SCR_JUMPR ^ IFFALSE (DATA (M_SIMPLE_TAG)),
-		48,
-	/*
-	**	It WAS a SIMPLE_TAG message.
-	**	get it and ack it!
-	*/
-	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
-		NADDR (msgin),
-	SCR_CLR (SCR_ACK),
+	SCR_SFBR_REG (temp, SCR_AND, 0xfc),
 		0,
-	/*
-	**	Wait for the second byte (the tag)
-	*/
-/*<<<*/	SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
-		24,
-	/*
-	**	Get it and ack it!
-	*/
-	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
-		NADDR (msgin),
-	SCR_CLR (SCR_ACK|SCR_CARRY),
+}/*-------------------------< JUMP_TO_NEXUS >-------------------*/,{
+	SCR_COPY_F (4),
+		RADDR (temp),
+		PADDR (nexus_indirect),
+	SCR_COPY (4),
+}/*-------------------------< NEXUS_INDIRECT >-------------------*/,{
 		0,
+		RADDR (temp),
 	SCR_RETURN,
 		0,
+}/*-------------------------< RESEL_NOTAG >-------------------*/,{
 	/*
-	**	No message phase or no SIMPLE_TAG message
-	**	or no second byte: return 0.
+	**	No tag expected.
+	**	Read an throw away the IDENTIFY.
 	*/
-/*>>>*/	SCR_LOAD_SFBR (0),
-		0,
-	SCR_SET (SCR_CARRY),
-		0,
-	SCR_RETURN,
-		0,
-
-}/*-------------------------< DATA_IO >--------------------*/,{
-/*
-**	Because Linux does not provide xfer data direction 
-**	to low-level scsi drivers, we must trust the target 
-**	for actual data direction when we cannot guess it.
-**	The programmed interrupt patches savep, lastp, goalp,
-**	etc.., and restarts the scsi script at data_out/in.
-*/
-	SCR_INT ^ IFTRUE (WHEN (SCR_DATA_OUT)),
-		SIR_DATA_IO_IS_OUT,
-	SCR_INT ^ IFTRUE (WHEN (SCR_DATA_IN)),
-		SIR_DATA_IO_IS_IN,
+	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+		NADDR (msgin),
 	SCR_JUMP,
-		PADDR (no_data),
-
+		PADDR (jump_to_nexus),
 }/*-------------------------< DATA_IN >--------------------*/,{
 /*
 **	Because the size depends on the
-**	#define MAX_SCATTER parameter,
+**	#define MAX_SCATTERL parameter,
 **	it is filled in at runtime.
 **
-**  ##===========< i=0; i<MAX_SCATTER >=========
+**  ##===========< i=0; i<MAX_SCATTERL >=========
 **  ||	SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)),
-**  ||		PADDR (checkatn),
+**  ||		PADDR (dispatch),
 **  ||	SCR_MOVE_TBL ^ SCR_DATA_IN,
 **  ||		offsetof (struct dsb, data[ i]),
 **  ##==========================================
 **
-**	SCR_CALL,
-**		PADDR (checkatn),
-**	SCR_JUMP,
-**		PADDR (no_data),
+**---------------------------------------------------------
 */
 0
+}/*-------------------------< DATA_IN2 >-------------------*/,{
+	SCR_CALL,
+		PADDR (dispatch),
+	SCR_JUMP,
+		PADDR (no_data),
+}/*-------------------------< DATA_OUT >--------------------*/,{
+/*
+**	Because the size depends on the
+**	#define MAX_SCATTERL parameter,
+**	it is filled in at runtime.
+**
+**  ##===========< i=0; i<MAX_SCATTERL >=========
+**  ||	SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)),
+**  ||		PADDR (dispatch),
+**  ||	SCR_MOVE_TBL ^ SCR_DATA_OUT,
+**  ||		offsetof (struct dsb, data[ i]),
+**  ##==========================================
+**
+**---------------------------------------------------------
+*/
+0
+}/*-------------------------< DATA_OUT2 >-------------------*/,{
+	SCR_CALL,
+		PADDR (dispatch),
+	SCR_JUMP,
+		PADDR (no_data),
 }/*--------------------------------------------------------*/
 };
 
 static	struct scripth scripth0 __initdata = {
 /*-------------------------< TRYLOOP >---------------------*/{
 /*
-**	Load an entry of the start queue into dsa
-**	and try to start it by jumping to TRYSEL.
+**	Start the next entry.
+**	Called addresses point to the launch script in the CCB.
+**	They are patched by the main processor.
 **
 **	Because the size depends on the
 **	#define MAX_START parameter, it is filled
@@ -3103,32 +3045,142 @@
 **-----------------------------------------------------------
 **
 **  ##===========< I=0; i<MAX_START >===========
-**  ||	SCR_COPY (4),
-**  ||		NADDR (squeue[i]),
-**  ||		RADDR (dsa),
 **  ||	SCR_CALL,
-**  ||		PADDR (trysel),
+**  ||		PADDR (idle),
 **  ##==========================================
 **
-**	SCR_JUMP,
-**		PADDRH(tryloop),
+**-----------------------------------------------------------
+*/
+0
+}/*------------------------< TRYLOOP2 >---------------------*/,{
+	SCR_JUMP,
+		PADDRH(tryloop),
+
+#ifdef SCSI_NCR_CCB_DONE_SUPPORT
+
+}/*------------------------< DONE_QUEUE >-------------------*/,{
+/*
+**	Copy the CCB address to the next done entry.
+**	Because the size depends on the
+**	#define MAX_DONE parameter, it is filled
+**	in at runtime.
+**
+**-----------------------------------------------------------
+**
+**  ##===========< I=0; i<MAX_DONE >===========
+**  ||	SCR_COPY (sizeof(ccb_p)),
+**  ||		NADDR (header.cp),
+**  ||		NADDR (ccb_done[i]),
+**  ||	SCR_CALL,
+**  ||		PADDR (done_end),
+**  ##==========================================
 **
 **-----------------------------------------------------------
 */
 0
-},/*-------------------------< MSG_PARITY >---------------*/{
+}/*------------------------< DONE_QUEUE2 >------------------*/,{
+	SCR_JUMP,
+		PADDRH (done_queue),
+
+#endif /* SCSI_NCR_CCB_DONE_SUPPORT */
+}/*------------------------< SELECT_NO_ATN >-----------------*/,{
 	/*
-	**	count it
+	**	Set Initiator mode.
+	**      And try to select this target without ATN.
 	*/
-	SCR_REG_REG (PS_REG, SCR_ADD, 0x01),
+
+	SCR_CLR (SCR_TRG),
+		0,
+	SCR_LOAD_REG (HS_REG, HS_SELECTING),
+		0,
+	SCR_SEL_TBL ^ offsetof (struct dsb, select),
+		PADDR (reselect),
+	SCR_JUMP,
+		PADDR (select2),
+
+}/*-------------------------< CANCEL >------------------------*/,{
+
+	SCR_LOAD_REG (scratcha, HS_ABORTED),
+		0,
+	SCR_JUMPR,
+		8,
+}/*-------------------------< SKIP >------------------------*/,{
+	SCR_LOAD_REG (scratcha, 0),
+		0,
+	/*
+	**	This entry has been canceled.
+	**	Next time use the next slot.
+	*/
+	SCR_COPY (4),
+		RADDR (temp),
+		PADDR (startpos),
+	/*
+	**      The ncr doesn't have an indirect load
+	**	or store command. So we have to
+	**	copy part of the control block to a
+	**	fixed place, where we can access it.
+	**
+	**	We patch the address part of a
+	**	COPY command with the DSA-register.
+	*/
+	SCR_COPY_F (4),
+		RADDR (dsa),
+		PADDRH (skip2),
+	/*
+	**	then we do the actual copy.
+	*/
+	SCR_COPY (sizeof (struct head)),
+	/*
+	**	continued after the next label ...
+	*/
+}/*-------------------------< SKIP2 >---------------------*/,{
+		0,
+		NADDR (header),
+	/*
+	**      Initialize the status registers
+	*/
+	SCR_COPY (4),
+		NADDR (header.status),
+		RADDR (scr0),
+	/*
+	**	Force host status.
+	*/
+	SCR_FROM_REG (scratcha),
+		0,
+	SCR_JUMPR ^ IFFALSE (MASK (0, HS_DONEMASK)),
+		16,
+	SCR_REG_REG (HS_REG, SCR_OR, HS_SKIPMASK),
+		0,
+	SCR_JUMPR,
+		8,
+	SCR_TO_REG (HS_REG),
+		0,
+	SCR_LOAD_REG (SS_REG, S_GOOD),
 		0,
+	SCR_JUMP,
+		PADDR (cleanup_ok),
+
+},/*-------------------------< PAR_ERR_DATA_IN >---------------*/{
+	/*
+	**	Ignore all data in byte, until next phase
+	*/
+	SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
+		PADDRH (par_err_other),
+	SCR_MOVE_ABS (1) ^ SCR_DATA_IN,
+		NADDR (scratch),
+	SCR_JUMPR,
+		-24,
+},/*-------------------------< PAR_ERR_OTHER >------------------*/{
 	/*
-	**	send a "message parity error" message.
+	**	count it.
 	*/
-	SCR_LOAD_REG (scratcha, M_PARITY),
+	SCR_REG_REG (PS_REG, SCR_ADD, 0x01),
 		0,
+	/*
+	**	jump to dispatcher.
+	*/
 	SCR_JUMP,
-		PADDR (setmsg),
+		PADDR (dispatch),
 }/*-------------------------< MSG_REJECT >---------------*/,{
 	/*
 	**	If a negotiation was in progress,
@@ -3160,17 +3212,6 @@
 	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
 		NADDR (msgin[1]),
 	/*
-	**	Check for message parity error.
-	*/
-	SCR_TO_REG (scratcha),
-		0,
-	SCR_FROM_REG (socl),
-		0,
-	SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
-		PADDRH (msg_parity),
-	SCR_FROM_REG (scratcha),
-		0,
-	/*
 	**	Size is 0 .. ignore message.
 	*/
 	SCR_JUMP ^ IFTRUE (DATA (0)),
@@ -3178,14 +3219,14 @@
 	/*
 	**	Size is not 1 .. have to interrupt.
 	*/
-/*<<<*/	SCR_JUMPR ^ IFFALSE (DATA (1)),
+	SCR_JUMPR ^ IFFALSE (DATA (1)),
 		40,
 	/*
 	**	Check for residue byte in swide register
 	*/
 	SCR_FROM_REG (scntl2),
 		0,
-/*<<<*/	SCR_JUMPR ^ IFFALSE (MASK (WSR, WSR)),
+	SCR_JUMPR ^ IFFALSE (MASK (WSR, WSR)),
 		16,
 	/*
 	**	There IS data in the swide register.
@@ -3198,9 +3239,9 @@
 	/*
 	**	Load again the size to the sfbr register.
 	*/
-/*>>>*/	SCR_FROM_REG (scratcha),
+	SCR_FROM_REG (scratcha),
 		0,
-/*>>>*/	SCR_INT,
+	SCR_INT,
 		SIR_IGN_RESIDUE,
 	SCR_JUMP,
 		PADDR (clrack),
@@ -3219,17 +3260,6 @@
 	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
 		NADDR (msgin[1]),
 	/*
-	**	Check for message parity error.
-	*/
-	SCR_TO_REG (scratcha),
-		0,
-	SCR_FROM_REG (socl),
-		0,
-	SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
-		PADDRH (msg_parity),
-	SCR_FROM_REG (scratcha),
-		0,
-	/*
 	*/
 	SCR_JUMP ^ IFTRUE (DATA (3)),
 		PADDRH (msg_ext_3),
@@ -3245,17 +3275,6 @@
 	*/
 	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
 		NADDR (msgin[2]),
-	/*
-	**	Check for message parity error.
-	*/
-	SCR_TO_REG (scratcha),
-		0,
-	SCR_FROM_REG (socl),
-		0,
-	SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
-		PADDRH (msg_parity),
-	SCR_FROM_REG (scratcha),
-		0,
 	SCR_JUMP ^ IFTRUE (DATA (M_X_WIDE_REQ)),
 		PADDRH (msg_wdtr),
 	/*
@@ -3273,10 +3292,6 @@
 	*/
 	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
 		NADDR (msgin[3]),
-	SCR_FROM_REG (socl),
-		0,
-	SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
-		PADDRH (msg_parity),
 	/*
 	**	let the host do the real work.
 	*/
@@ -3290,17 +3305,17 @@
 	SCR_CLR (SCR_ACK),
 		0,
 
+/* CHECK THE SOURCE FOR 'send_wdtr' IF YOU INTEND TO CHANGE SOMETHING HERE */
 	SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)),
 		SIR_NEGO_PROTO,
+}/*-------------------------< SEND_WDTR >----------------*/,{
 	/*
 	**	Send the M_X_WIDE_REQ
 	*/
 	SCR_MOVE_ABS (4) ^ SCR_MSG_OUT,
 		NADDR (msgout),
-	SCR_CLR (SCR_ATN),
-		0,
 	SCR_COPY (1),
-		RADDR (sfbr),
+		NADDR (msgout),
 		NADDR (lastmsg),
 	SCR_JUMP,
 		PADDR (msg_out_done),
@@ -3315,17 +3330,6 @@
 	*/
 	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
 		NADDR (msgin[2]),
-	/*
-	**	Check for message parity error.
-	*/
-	SCR_TO_REG (scratcha),
-		0,
-	SCR_FROM_REG (socl),
-		0,
-	SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
-		PADDRH (msg_parity),
-	SCR_FROM_REG (scratcha),
-		0,
 	SCR_JUMP ^ IFTRUE (DATA (M_X_SYNC_REQ)),
 		PADDRH (msg_sdtr),
 	/*
@@ -3344,10 +3348,6 @@
 	*/
 	SCR_MOVE_ABS (2) ^ SCR_MSG_IN,
 		NADDR (msgin[3]),
-	SCR_FROM_REG (socl),
-		0,
-	SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
-		PADDRH (msg_parity),
 	/*
 	**	let the host do the real work.
 	*/
@@ -3361,17 +3361,17 @@
 	SCR_CLR (SCR_ACK),
 		0,
 
+/* CHECK THE SOURCE FOR 'send_sdtr' IF YOU INTEND TO CHANGE SOMETHING HERE */
 	SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)),
 		SIR_NEGO_PROTO,
+}/*-------------------------< SEND_SDTR >-------------*/,{
 	/*
 	**	Send the M_X_SYNC_REQ
 	*/
 	SCR_MOVE_ABS (5) ^ SCR_MSG_OUT,
 		NADDR (msgout),
-	SCR_CLR (SCR_ATN),
-		0,
 	SCR_COPY (1),
-		RADDR (sfbr),
+		NADDR (msgout),
 		NADDR (lastmsg),
 	SCR_JUMP,
 		PADDR (msg_out_done),
@@ -3396,203 +3396,259 @@
 	SCR_JUMP,
 		PADDR (cleanup),
 
-}/*-------------------------< GETCC >-----------------------*/,{
+}/*-------------------------< HDATA_IN >-------------------*/,{
+/*
+**	Because the size depends on the
+**	#define MAX_SCATTERH parameter,
+**	it is filled in at runtime.
+**
+**  ##==< i=MAX_SCATTERL; i<MAX_SCATTERL+MAX_SCATTERH >==
+**  ||	SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)),
+**  ||		PADDR (dispatch),
+**  ||	SCR_MOVE_TBL ^ SCR_DATA_IN,
+**  ||		offsetof (struct dsb, data[ i]),
+**  ##===================================================
+**
+**---------------------------------------------------------
+*/
+0
+}/*-------------------------< HDATA_IN2 >------------------*/,{
+	SCR_JUMP,
+		PADDR (data_in),
+
+}/*-------------------------< HDATA_OUT >-------------------*/,{
+/*
+**	Because the size depends on the
+**	#define MAX_SCATTERH parameter,
+**	it is filled in at runtime.
+**
+**  ##==< i=MAX_SCATTERL; i<MAX_SCATTERL+MAX_SCATTERH >==
+**  ||	SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)),
+**  ||		PADDR (dispatch),
+**  ||	SCR_MOVE_TBL ^ SCR_DATA_OUT,
+**  ||		offsetof (struct dsb, data[ i]),
+**  ##===================================================
+**
+**---------------------------------------------------------
+*/
+0
+}/*-------------------------< HDATA_OUT2 >------------------*/,{
+	SCR_JUMP,
+		PADDR (data_out),
+
+}/*-------------------------< RESET >----------------------*/,{
 	/*
-	**	The ncr doesn't have an indirect load
-	**	or store command. So we have to
-	**	copy part of the control block to a
-	**	fixed place, where we can modify it.
-	**
-	**	We patch the address part of a COPY command
-	**	with the address of the dsa register ...
+	**      Send a M_RESET message if bad IDENTIFY 
+	**	received on reselection.
 	*/
-	SCR_COPY_F (4),
-		RADDR (dsa),
-		PADDRH (getcc1),
+	SCR_LOAD_REG (scratcha, M_ABORT_TAG),
+		0,
+	SCR_JUMP,
+		PADDRH (abort_resel),
+}/*-------------------------< ABORTTAG >-------------------*/,{
 	/*
-	**	... then we do the actual copy.
+	**      Abort a wrong tag received on reselection.
 	*/
-	SCR_COPY (sizeof (struct head)),
-}/*-------------------------< GETCC1 >----------------------*/,{
+	SCR_LOAD_REG (scratcha, M_ABORT_TAG),
 		0,
-		NADDR (header),
+	SCR_JUMP,
+		PADDRH (abort_resel),
+}/*-------------------------< ABORT >----------------------*/,{
 	/*
-	**	Initialize the status registers
+	**      Abort a reselection when no active CCB.
 	*/
-	SCR_COPY (4),
-		NADDR (header.status),
-		RADDR (scr0),
-}/*-------------------------< GETCC2 >----------------------*/,{
+	SCR_LOAD_REG (scratcha, M_ABORT),
+		0,
+}/*-------------------------< ABORT_RESEL >----------------*/,{
+	SCR_COPY (1),
+		RADDR (scratcha),
+		NADDR (msgout),
+	SCR_SET (SCR_ATN),
+		0,
+	SCR_CLR (SCR_ACK),
+		0,
 	/*
-	**	Get the condition code from a target.
-	**
-	**	DSA points to a data structure.
-	**	Set TEMP to the script location
-	**	that receives the condition code.
-	**
-	**	Because there is no script command
-	**	to load a longword into a register,
-	**	we use a CALL command.
+	**	and send it.
+	**	we expect an immediate disconnect
 	*/
-/*<<<*/	SCR_CALLR,
-		24,
+	SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+		0,
+	SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
+		NADDR (msgout),
+	SCR_COPY (1),
+		NADDR (msgout),
+		NADDR (lastmsg),
+	SCR_CLR (SCR_ACK|SCR_ATN),
+		0,
+	SCR_WAIT_DISC,
+		0,
+	SCR_JUMP,
+		PADDR (start),
+}/*-------------------------< RESEND_IDENT >-------------------*/,{
 	/*
-	**	Get the condition code.
+	**	The target stays in MSG OUT phase after having acked 
+	**	Identify [+ Tag [+ Extended message ]]. Targets shall
+	**	behave this way on parity error.
+	**	We must send it again all the messages.
 	*/
+	SCR_SET (SCR_ATN), /* Shall be asserted 2 deskew delays before the  */
+		0,         /* 1rst ACK = 90 ns. Hope the NCR is'nt too fast */
+	SCR_JUMP,
+		PADDR (send_ident),
+}/*-------------------------< CLRATN_GO_ON >-------------------*/,{
+	SCR_CLR (SCR_ATN),
+		0,
+	SCR_JUMP,
+}/*-------------------------< NXTDSP_GO_ON >-------------------*/,{
+		0,
+}/*-------------------------< SDATA_IN >-------------------*/,{
+	SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)),
+		PADDR (dispatch),
 	SCR_MOVE_TBL ^ SCR_DATA_IN,
 		offsetof (struct dsb, sense),
-	/*
-	**	No data phase may follow!
-	*/
 	SCR_CALL,
-		PADDR (checkatn),
+		PADDR (dispatch),
 	SCR_JUMP,
 		PADDR (no_data),
-/*>>>*/
-
+}/*-------------------------< DATA_IO >--------------------*/,{
 	/*
-	**	The CALL jumps to this point.
-	**	Prepare for a RESTORE_POINTER message.
-	**	Save the TEMP register into the saved pointer.
+	**	We jump here if the data direction was unknown at the 
+	**	time we had to queue the command to the scripts processor.
+	**	Pointers had been set as follow in this situation:
+	**	  savep   -->   DATA_IO
+	**	  lastp   -->   start pointer when DATA_IN
+	**	  goalp   -->   goal  pointer when DATA_IN
+	**	  wlastp  -->   start pointer when DATA_OUT
+	**	  wgoalp  -->   goal  pointer when DATA_OUT
+	**	This script sets savep/lastp/goalp according to the 
+	**	direction chosen by the target.
 	*/
-	SCR_COPY (4),
-		RADDR (temp),
-		NADDR (header.savep),
+	SCR_JUMPR ^ IFTRUE (WHEN (SCR_DATA_OUT)),
+		32,
 	/*
-	**	Load scratcha, because in case of a selection timeout,
-	**	the host will expect a new value for startpos in
-	**	the scratcha register.
+	**	Direction is DATA IN.
+	**	Warning: we jump here, even when phase is DATA OUT.
 	*/
 	SCR_COPY (4),
-		PADDR (startpos),
-		RADDR (scratcha),
-#ifdef NCR_GETCC_WITHMSG
-	/*
-	**	If QUIRK_NOMSG is set, select without ATN.
-	**	and don't send a message.
-	*/
-	SCR_FROM_REG (QU_REG),
-		0,
-	SCR_JUMP ^ IFTRUE (MASK (QUIRK_NOMSG, QUIRK_NOMSG)),
-		PADDRH(getcc3),
-	/*
-	**	Then try to connect to the target.
-	**	If we are reselected, special treatment
-	**	of the current job is required before
-	**	accepting the reselection.
-	*/
-	SCR_SEL_TBL_ATN ^ offsetof (struct dsb, select),
-		PADDR(badgetcc),
+		NADDR (header.lastp),
+		NADDR (header.savep),
+
 	/*
-	**	save target id.
+	**	Jump to the SCRIPTS according to actual direction.
 	*/
-	SCR_FROM_REG (sdid),
-		0,
-	SCR_TO_REG (ctest0),
+	SCR_COPY (4),
+		NADDR (header.savep),
+		RADDR (temp),
+	SCR_RETURN,
 		0,
 	/*
-	**	Send the IDENTIFY message.
-	**	In case of short transfer, remove ATN.
+	**	Direction is DATA OUT.
 	*/
-	SCR_MOVE_TBL ^ SCR_MSG_OUT,
-		offsetof (struct dsb, smsg2),
-	SCR_CLR (SCR_ATN),
-		0,
-	/*
-	**	save the first byte of the message.
+	SCR_COPY (4),
+		NADDR (header.wlastp),
+		NADDR (header.lastp),
+	SCR_COPY (4),
+		NADDR (header.wgoalp),
+		NADDR (header.goalp),
+	SCR_JUMPR,
+		-64,
+}/*-------------------------< BAD_IDENTIFY >---------------*/,{
+	/*
+	**	If message phase but not an IDENTIFY,
+	**	get some help from the C code.
+	**	Old SCSI device may behave so.
 	*/
-	SCR_COPY (1),
-		RADDR (sfbr),
-		NADDR (lastmsg),
+	SCR_JUMPR ^ IFTRUE (MASK (0x80, 0x80)),
+		16,
+	SCR_INT,
+		SIR_RESEL_NO_IDENTIFY,
 	SCR_JUMP,
-		PADDR (prepare2),
-
-#endif
-}/*-------------------------< GETCC3 >----------------------*/,{
+		PADDRH (reset),
 	/*
-	**	Try to connect to the target.
-	**	If we are reselected, special treatment
-	**	of the current job is required before
-	**	accepting the reselection.
-	**
-	**	Silly target won't accept a message.
-	**	Select without ATN.
+	**	Message is an IDENTIFY, but lun is unknown.
+	**	Read the message, since we got it directly 
+	**	from the SCSI BUS data lines.
+	**	Signal problem to C code for logging the event.
+	**	Send a M_ABORT to clear all pending tasks.
 	*/
-	SCR_SEL_TBL ^ offsetof (struct dsb, select),
-		PADDR(badgetcc),
+	SCR_INT,
+		SIR_RESEL_BAD_LUN,
+	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+		NADDR (msgin),
+	SCR_JUMP,
+		PADDRH (abort),
+}/*-------------------------< BAD_I_T_L >------------------*/,{
 	/*
-	**	save target id.
+	**	We donnot have a task for that I_T_L.
+	**	Signal problem to C code for logging the event.
+	**	Send a M_ABORT message.
 	*/
-	SCR_FROM_REG (sdid),
-		0,
-	SCR_TO_REG (ctest0),
-		0,
+	SCR_INT,
+		SIR_RESEL_BAD_I_T_L,
+	SCR_JUMP,
+		PADDRH (abort),
+}/*-------------------------< BAD_I_T_L_Q >----------------*/,{
 	/*
-	**	Force error if selection timeout
+	**	We donnot have a task that matches the tag.
+	**	Signal problem to C code for logging the event.
+	**	Send a M_ABORTTAG message.
 	*/
-	SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_IN)),
-		0,
+	SCR_INT,
+		SIR_RESEL_BAD_I_T_L_Q,
+	SCR_JUMP,
+		PADDRH (aborttag),
+}/*-------------------------< BAD_TARGET >-----------------*/,{
 	/*
-	**	don't negotiate.
+	**	We donnot know the target that reselected us.
+	**	Grab the first message if any (IDENTIFY).
+	**	Signal problem to C code for logging the event.
+	**	M_RESET message.
 	*/
+	SCR_INT,
+		SIR_RESEL_BAD_TARGET,
+	SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
+		8,
+	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+		NADDR (msgin),
 	SCR_JUMP,
-		PADDR (prepare2),
-
-}/*-------------------------< DATA_OUT >-------------------*/,{
-/*
-**	Because the size depends on the
-**	#define MAX_SCATTER parameter,
-**	it is filled in at runtime.
-**
-**  ##===========< i=0; i<MAX_SCATTER >=========
-**  ||	SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)),
-**  ||		PADDR (dispatch),
-**  ||	SCR_MOVE_TBL ^ SCR_DATA_OUT,
-**  ||		offsetof (struct dsb, data[ i]),
-**  ##==========================================
-**
-**	SCR_CALL,
-**		PADDR (dispatch),
-**	SCR_JUMP,
-**		PADDR (no_data),
-**
-**---------------------------------------------------------
-*/
-0
-}/*-------------------------< ABORTTAG >-------------------*/,{
+		PADDRH (reset),
+}/*-------------------------< BAD_STATUS >-----------------*/,{
 	/*
-	**      Abort a bad reselection.
-	**	Set the message to ABORT vs. ABORT_TAG
+	**	If command resulted in either QUEUE FULL,
+	**	CHECK CONDITION or COMMAND TERMINATED,
+	**	call the C code.
 	*/
-	SCR_LOAD_REG (scratcha, M_ABORT_TAG),
-		0,
-	SCR_JUMPR ^ IFFALSE (CARRYSET),
-		8,
-}/*-------------------------< ABORT >----------------------*/,{
-	SCR_LOAD_REG (scratcha, M_ABORT),
+	SCR_INT ^ IFTRUE (DATA (S_QUEUE_FULL)),
+		SIR_BAD_STATUS,
+	SCR_INT ^ IFTRUE (DATA (S_CHECK_COND)),
+		SIR_BAD_STATUS,
+	SCR_INT ^ IFTRUE (DATA (S_TERMINATED)),
+		SIR_BAD_STATUS,
+	SCR_RETURN,
 		0,
-	SCR_COPY (1),
+}/*-------------------------< START_RAM >-------------------*/,{
+	/*
+	**	Load the script into on-chip RAM, 
+	**	and jump to start point.
+	*/
+	SCR_COPY_F (4),
 		RADDR (scratcha),
-		NADDR (msgout),
-	SCR_SET (SCR_ATN),
-		0,
-	SCR_CLR (SCR_ACK),
+		PADDRH (start_ram0),
+	SCR_COPY (sizeof (struct script)),
+}/*-------------------------< START_RAM0 >--------------------*/,{
 		0,
+		PADDR (start),
+	SCR_JUMP,
+		PADDR (start),
+}/*-------------------------< STO_RESTART >-------------------*/,{
 	/*
-	**	and send it.
-	**	we expect an immediate disconnect
+	**
+	**	Repair start queue (e.g. next time use the next slot) 
+	**	and jump to start point.
 	*/
-	SCR_REG_REG (scntl2, SCR_AND, 0x7f),
-		0,
-	SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
-		NADDR (msgout),
-	SCR_COPY (1),
-		RADDR (sfbr),
-		NADDR (lastmsg),
-	SCR_CLR (SCR_ACK|SCR_ATN),
-		0,
-	SCR_WAIT_DISC,
-		0,
+	SCR_COPY (4),
+		RADDR (temp),
+		PADDR (startpos),
 	SCR_JUMP,
 		PADDR (start),
 }/*-------------------------< SNOOPTEST >-------------------*/,{
@@ -3641,48 +3697,63 @@
 
 	p = scrh->tryloop;
 	for (i=0; i<MAX_START; i++) {
-		*p++ =SCR_COPY (4);
-		*p++ =NADDR (squeue[i]);
-		*p++ =RADDR (dsa);
 		*p++ =SCR_CALL;
-		*p++ =PADDR (trysel);
+		*p++ =PADDR (idle);
 	};
-	*p++ =SCR_JUMP;
-	*p++ =PADDRH(tryloop);
 
 	assert ((u_long)p == (u_long)&scrh->tryloop + sizeof (scrh->tryloop));
 
-	p = scr->data_in;
+#ifdef SCSI_NCR_CCB_DONE_SUPPORT
 
-	for (i=0; i<MAX_SCATTER; i++) {
+	p = scrh->done_queue;
+	for (i = 0; i<MAX_DONE; i++) {
+		*p++ =SCR_COPY (sizeof(ccb_p));
+		*p++ =NADDR (header.cp);
+		*p++ =NADDR (ccb_done[i]);
+		*p++ =SCR_CALL;
+		*p++ =PADDR (done_end);
+	}
+
+	assert ((u_long)p ==(u_long)&scrh->done_queue+sizeof(scrh->done_queue));
+
+#endif /* SCSI_NCR_CCB_DONE_SUPPORT */
+
+	p = scrh->hdata_in;
+	for (i=0; i<MAX_SCATTERH; i++) {
 		*p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN));
-		*p++ =PADDR (checkatn);
+		*p++ =PADDR (dispatch);
 		*p++ =SCR_MOVE_TBL ^ SCR_DATA_IN;
 		*p++ =offsetof (struct dsb, data[i]);
 	};
+	assert ((u_long)p == (u_long)&scrh->hdata_in + sizeof (scrh->hdata_in));
 
-	*p++ =SCR_CALL;
-	*p++ =PADDR (checkatn);
-	*p++ =SCR_JUMP;
-	*p++ =PADDR (no_data);
-
+	p = scr->data_in;
+	for (i=MAX_SCATTERH; i<MAX_SCATTERH+MAX_SCATTERL; i++) {
+		*p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN));
+		*p++ =PADDR (dispatch);
+		*p++ =SCR_MOVE_TBL ^ SCR_DATA_IN;
+		*p++ =offsetof (struct dsb, data[i]);
+	};
 	assert ((u_long)p == (u_long)&scr->data_in + sizeof (scr->data_in));
 
-	p = scrh->data_out;
-
-	for (i=0; i<MAX_SCATTER; i++) {
+	p = scrh->hdata_out;
+	for (i=0; i<MAX_SCATTERH; i++) {
 		*p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT));
 		*p++ =PADDR (dispatch);
 		*p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT;
 		*p++ =offsetof (struct dsb, data[i]);
 	};
+	assert ((u_long)p==(u_long)&scrh->hdata_out + sizeof (scrh->hdata_out));
 
-	*p++ =SCR_CALL;
-	*p++ =PADDR (dispatch);
-	*p++ =SCR_JUMP;
-	*p++ =PADDR (no_data);
+	p = scr->data_out;
+	for (i=MAX_SCATTERH; i<MAX_SCATTERH+MAX_SCATTERL; i++) {
+		*p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT));
+		*p++ =PADDR (dispatch);
+		*p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT;
+		*p++ =offsetof (struct dsb, data[i]);
+	};
 
-	assert ((u_long)p == (u_long)&scrh->data_out + sizeof (scrh->data_out));
+	assert ((u_long)p == (u_long)&scr->data_out + sizeof (scr->data_out));
 }
 
 /*==========================================================
@@ -3719,13 +3790,13 @@
 		*/
 
 		if (opcode == 0) {
-			printf ("%s: ERROR0 IN SCRIPT at %d.\n",
+			printk (KERN_ERR "%s: ERROR0 IN SCRIPT at %d.\n",
 				ncr_name(np), (int) (src-start-1));
-			DELAY (1000000);
+			MDELAY (1000);
 		};
 
 		if (DEBUG_FLAGS & DEBUG_SCRIPT)
-			printf ("%p:  <%x>\n",
+			printk (KERN_DEBUG "%p:  <%x>\n",
 				(src-1), (unsigned)opcode);
 
 		/*
@@ -3745,9 +3816,9 @@
 			if ((tmp2 & RELOC_MASK) == RELOC_KVAR)
 				tmp2 = 0;
 			if ((tmp1 ^ tmp2) & 3) {
-				printf ("%s: ERROR1 IN SCRIPT at %d.\n",
+				printk (KERN_ERR"%s: ERROR1 IN SCRIPT at %d.\n",
 					ncr_name(np), (int) (src-start-1));
-				DELAY (1000000);
+				MDELAY (1000);
 			}
 			/*
 			**	If PREFETCH feature not enabled, remove 
@@ -3769,7 +3840,7 @@
 		case 0x8:
 			/*
 			**	JUMP / CALL
-			**	don't relocate if relative :-)
+			**	dont't relocate if relative :-)
 			*/
 			if (opcode & 0x00800000)
 				relocs = 0;
@@ -3795,7 +3866,8 @@
 
 				switch (old & RELOC_MASK) {
 				case RELOC_REGISTER:
-					new = (old & ~RELOC_MASK) + np->paddr;
+					new = (old & ~RELOC_MASK)
+						+ bus_dvma_to_mem(np->paddr);
 					break;
 				case RELOC_LABEL:
 					new = (old & ~RELOC_MASK) + np->p_script;
@@ -3833,9 +3905,6 @@
 			*dst++ = cpu_to_scr(*src++);
 
 	};
-	if (bootverbose > 1 && opchanged)
-		printf("%s: NO FLUSH bit removed from %d script instructions\n",
-			ncr_name(np), opchanged); 
 }
 
 /*==========================================================
@@ -3858,31 +3927,37 @@
 struct host_data {
      struct ncb *ncb;
 
-     char ncb_align[NCB_ALIGN_SIZE-1];	/* Filler for alignment */
+     char ncb_align[CACHE_LINE_SIZE-1]; /* Filler for alignment */
      struct ncb _ncb_data;
 
-     char ccb_align[CCB_ALIGN_SIZE-1];	/* Filler for alignment */
+     char ccb_align[CACHE_LINE_SIZE-1]; /* Filler for alignment */
      struct ccb _ccb_data;
 
-     char scr_align[SCR_ALIGN_SIZE-1];	/* Filler for alignment */
+     char scr_align[CACHE_LINE_SIZE-1]; /* Filler for alignment */
      struct script script_data;
 
      struct scripth scripth_data;
 };
 
 /*
-**	Print something which allow to retrieve the controler type, unit,
+**	Print something which allows to retrieve the controler type, unit,
 **	target, lun concerned by a kernel message.
 */
 
-#define PRINT_LUN(np, target, lun) \
-printf(KERN_INFO "%s-<%d,%d>: ", ncr_name(np), (int) (target), (int) (lun))
+static void PRINT_TARGET(ncb_p np, int target)
+{
+	printk(KERN_INFO "%s-<%d,*>: ", ncr_name(np), target);
+}
+
+static void PRINT_LUN(ncb_p np, int target, int lun)
+{
+	printk(KERN_INFO "%s-<%d,%d>: ", ncr_name(np), target, lun);
+}
 
 static void PRINT_ADDR(Scsi_Cmnd *cmd)
 {
 	struct host_data *host_data = (struct host_data *) cmd->host->hostdata;
-	ncb_p np                    = host_data->ncb;
-	if (np) PRINT_LUN(np, cmd->target, cmd->lun);
+	PRINT_LUN(host_data->ncb, cmd->target, cmd->lun);
 }
 
 /*==========================================================
@@ -3989,8 +4064,6 @@
 
 	if (tn->flags & TEKRAM_TAGGED_COMMANDS) {
 		tp->usrtags = 2 << nvram->max_tags_index;
-		if (tp->usrtags > SCSI_NCR_MAX_TAGS)
-			tp->usrtags = SCSI_NCR_MAX_TAGS;
 	}
 
 	if (!(tn->flags & TEKRAM_DISCONNECT_ENABLE))
@@ -4053,8 +4126,7 @@
 	 * Divisor to be used for async (timer pre-scaler).
 	 */
 	i = np->clock_divn - 1;
-	while (i >= 0) {
-		--i;
+	while (--i >= 0) {
 		if (10ul * SCSI_NCR_MIN_ASYNC * np->clock_khz > div_10M[i]) {
 			++i;
 			break;
@@ -4246,7 +4318,7 @@
 #endif
 			tp->usrsync = driver_setup.default_sync;
 			tp->usrwide = driver_setup.max_wide;
-			tp->usrtags = driver_setup.default_tags;
+			tp->usrtags = SCSI_NCR_MAX_TAGS;
 			if (!driver_setup.disconnection)
 				np->target[i].usrflag = UF_NODISC;
 		}
@@ -4257,7 +4329,7 @@
 	*/
 
 	i = nvram ? nvram->type : 0;
-	printf(KERN_INFO "%s: %sID %d, Fast-%d%s%s\n", ncr_name(np),
+	printk(KERN_INFO "%s: %sID %d, Fast-%d%s%s\n", ncr_name(np),
 		i  == SCSI_NCR_SYMBIOS_NVRAM ? "Symbios format NVRAM, " :
 		(i == SCSI_NCR_TEKRAM_NVRAM  ? "Tekram format NVRAM, " : ""),
 		np->myaddr,
@@ -4266,19 +4338,19 @@
 		(np->rv_stest2 & 0x20)	? ", Differential"	: "");
 
 	if (bootverbose > 1) {
-		printf ("%s: initial SCNTL3/DMODE/DCNTL/CTEST3/4/5 = "
+		printk (KERN_INFO "%s: initial SCNTL3/DMODE/DCNTL/CTEST3/4/5 = "
 			"(hex) %02x/%02x/%02x/%02x/%02x/%02x\n",
 			ncr_name(np), np->sv_scntl3, np->sv_dmode, np->sv_dcntl,
 			np->sv_ctest3, np->sv_ctest4, np->sv_ctest5);
 
-		printf ("%s: final   SCNTL3/DMODE/DCNTL/CTEST3/4/5 = "
+		printk (KERN_INFO "%s: final   SCNTL3/DMODE/DCNTL/CTEST3/4/5 = "
 			"(hex) %02x/%02x/%02x/%02x/%02x/%02x\n",
 			ncr_name(np), np->rv_scntl3, np->rv_dmode, np->rv_dcntl,
 			np->rv_ctest3, np->rv_ctest4, np->rv_ctest5);
 	}
 
 	if (bootverbose && np->paddr2)
-		printf (KERN_INFO "%s: on-board RAM at 0x%lx\n",
+		printk (KERN_INFO "%s: on-chip RAM at 0x%lx\n",
 			ncr_name(np), np->paddr2);
 
 	return 0;
@@ -4294,17 +4366,18 @@
 	int i;
 
 	/* display Symbios nvram host data */
-	printf("%s: HOST ID=%d%s%s%s%s\n",
+	printk(KERN_DEBUG "%s: HOST ID=%d%s%s%s%s%s\n",
 		ncr_name(np), nvram->host_id & 0x0f,
 		(nvram->flags  & SYMBIOS_SCAM_ENABLE)	? " SCAM"	:"",
 		(nvram->flags  & SYMBIOS_PARITY_ENABLE)	? " PARITY"	:"",
-		(nvram->flags  & SYMBIOS_VERBOSE_MSGS)	? " VERSBOSE"	:"", 
+		(nvram->flags  & SYMBIOS_VERBOSE_MSGS)	? " VERBOSE"	:"", 
+		(nvram->flags  & SYMBIOS_CHS_MAPPING)	? " CHS_ALT"	:"", 
 		(nvram->flags1 & SYMBIOS_SCAN_HI_LO)	? " HI_LO"	:"");
 
 	/* display Symbios nvram drive data */
 	for (i = 0 ; i < 15 ; i++) {
 		struct Symbios_target *tn = &nvram->target[i];
-		printf("%s-%d:%s%s%s%s WIDTH=%d SYNC=%d TMO=%d\n",
+		printk(KERN_DEBUG "%s-%d:%s%s%s%s WIDTH=%d SYNC=%d TMO=%d\n",
 		ncr_name(np), i,
 		(tn->flags & SYMBIOS_DISCONNECT_ENABLE)	? " DISC"	: "",
 		(tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME)	? " SCAN_BOOT"	: "",
@@ -4337,7 +4410,8 @@
 	case 2: rem = " REMOVABLE=all";		break;
 	}
 
-	printf("%s: HOST ID=%d%s%s%s%s%s%s%s%s%s BOOT DELAY=%d tags=%d\n",
+	printk(KERN_DEBUG
+		"%s: HOST ID=%d%s%s%s%s%s%s%s%s%s BOOT DELAY=%d tags=%d\n",
 		ncr_name(np), nvram->host_id & 0x0f,
 		(nvram->flags1 & SYMBIOS_SCAM_ENABLE)	? " SCAM"	:"",
 		(nvram->flags & TEKRAM_MORE_THAN_2_DRIVES) ? " >2DRIVES"	:"",
@@ -4355,7 +4429,7 @@
 		struct Tekram_target *tn = &nvram->target[i];
 		j = tn->sync_index & 0xf;
 		sync = j < 12 ? Tekram_sync[j] : 255;
-		printf("%s-%d:%s%s%s%s%s%s PERIOD=%d\n",
+		printk(KERN_DEBUG "%s-%d:%s%s%s%s%s%s PERIOD=%d\n",
 		ncr_name(np), i,
 		(tn->flags & TEKRAM_PARITY_CHECK)	? " PARITY"	: "",
 		(tn->flags & TEKRAM_SYNC_NEGO)		? " SYNC"	: "",
@@ -4387,11 +4461,18 @@
         struct Scsi_Host *instance = 0;
 	u_long flags = 0;
 	ncr_nvram *nvram = device->nvram;
+	int i;
 
-printf(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%x, io_port=0x%x, irq=%d\n",
+#ifdef __sparc__
+printk(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=%s\n",
+	device->chip.name, unit, device->chip.revision_id, device->slot.base,
+	device->slot.io_port, __irq_itoa(device->slot.irq));
+#else
+printk(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=%d\n",
 	device->chip.name, unit, device->chip.revision_id, device->slot.base,
 	device->slot.io_port, device->slot.irq);
-
+#endif
+ 
 	/*
 	**	Allocate host_data structure
 	*/
@@ -4402,17 +4483,16 @@
 	**	Initialize structure.
 	*/
 	host_data = (struct host_data *) instance->hostdata;
+	bzero (host_data, sizeof(*host_data));
 
 	/*
 	**	Align np and first ccb to 32 boundary for cache line 
 	**	bursting when copying the global header.
 	*/
-	np        = (ncb_p) (((u_long) &host_data->_ncb_data) & NCB_ALIGN_MASK);
+	np = (ncb_p) (((u_long) &host_data->_ncb_data) & CACHE_LINE_MASK);
+	NCR_INIT_LOCK_NCB(np);
 	host_data->ncb = np;
-	bzero (np, sizeof (*np));
-
-	np->ccb   = (ccb_p) (((u_long) &host_data->_ccb_data) & CCB_ALIGN_MASK);
-	bzero (np->ccb, sizeof (*np->ccb));
+	np->ccb = (ccb_p) (((u_long) &host_data->_ccb_data) & CACHE_LINE_MASK);
 
 	/*
 	**	Store input informations in the host data structure.
@@ -4428,8 +4508,8 @@
 	np->maxoffs	= device->chip.offset_max;
 	np->maxburst	= device->chip.burst_max;
 
-	np->script0  =
-	(struct script *) (((u_long) &host_data->script_data) & SCR_ALIGN_MASK);
+	np->script0  = (struct script *)
+		(((u_long) &host_data->script_data) & CACHE_LINE_MASK);
 	np->scripth0 = &host_data->scripth_data;
 
 	/*
@@ -4451,12 +4531,14 @@
 #ifndef NCR_IOMAPPED
 	np->vaddr = remap_pci_mem((u_long) np->paddr, (u_long) 128);
 	if (!np->vaddr) {
-		printf("%s: can't map memory mapped IO region\n", ncr_name(np));
+		printk(KERN_ERR
+			"%s: can't map memory mapped IO region\n",ncr_name(np));
 		goto attach_error;
 	}
 	else
 		if (bootverbose > 1)
-			printf("%s: using memory mapped IO at virtual address 0x%lx\n", ncr_name(np), (u_long) np->vaddr);
+			printk(KERN_INFO
+				"%s: using memory mapped IO at virtual address 0x%lx\n", ncr_name(np), (u_long) np->vaddr);
 
 	/*
 	**	Make the controller's registers available.
@@ -4491,7 +4573,7 @@
 		default:
 			nvram = 0;
 #ifdef SCSI_NCR_DEBUG_NVRAM
-			printf("%s: NVRAM: None or invalid data.\n", ncr_name(np));
+			printk(KERN_DEBUG "%s: NVRAM: None or invalid data.\n", ncr_name(np));
 #endif
 		}
 	}
@@ -4502,37 +4584,27 @@
 	*/
 	(void)ncr_prepare_setting(np, nvram);
 
-#ifndef NCR_IOMAPPED
-	if (np->paddr2 && sizeof(struct script) <= 4096) {
-		np->vaddr2 = remap_pci_mem((u_long) np->paddr2, (u_long) 4096);
-		if (!np->vaddr2) {
-			printf("%s: can't map memory mapped IO region\n", ncr_name(np));
-			goto attach_error;
-		}
-		else
-			if (bootverbose > 1)
-				printf("%s: on-board ram mapped at virtual address 0x%lx\n", ncr_name(np), (u_long) np->vaddr2);
+	if (np->paddr2 && sizeof(struct script) > 4096) {
+		np->paddr2 = 0;
+		printk(KERN_WARNING "%s: script too large, NOT using on chip RAM.\n",
+			ncr_name(np));
 	}
-#endif /* !defined NCR_IOMAPPED */
 
 	/*
 	**	Fill Linux host instance structure
 	*/
-#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
 	instance->max_channel	= 0;
 	instance->max_id	= np->maxwide ? 16 : 8;
 	instance->max_lun	= SCSI_NCR_MAX_LUN;
-#endif
 #ifndef NCR_IOMAPPED
 	instance->base		= (char *) np->reg;
 #endif
 	instance->irq		= device->slot.irq;
+	instance->unique_id	= device->slot.io_port;
 	instance->io_port	= device->slot.io_port;
 	instance->n_io_port	= 128;
 	instance->dma_channel	= 0;
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0)
 	instance->select_queue_depths = ncr53c8xx_select_queue_depths;
-#endif
 
 	/*
 	**	Patch script to physical addresses
@@ -4542,8 +4614,7 @@
 	np->scripth	= np->scripth0;
 	np->p_scripth	= vtophys(np->scripth);
 
-	np->script	= (np->vaddr2) ? (struct script *) np->vaddr2 : np->script0;
-	np->p_script	= (np->vaddr2) ? np->paddr2 : vtophys(np->script0);
+	np->p_script	= (np->paddr2) ? bus_dvma_to_mem(np->paddr2) : vtophys(np->script0);
 
 	ncr_script_copy_and_bind (np, (ncrcmd *) &script0, (ncrcmd *) np->script0, sizeof(struct script));
 	ncr_script_copy_and_bind (np, (ncrcmd *) &scripth0, (ncrcmd *) np->scripth0, sizeof(struct scripth));
@@ -4554,27 +4625,32 @@
 	*/
 
 	if (np->features & FE_LED0) {
-		np->script0->reselect[0]  =
+		np->script0->idle[0]  =
 				cpu_to_scr(SCR_REG_REG(gpreg, SCR_OR,  0x01));
-		np->script0->reselect1[0] =
+		np->script0->reselected[0] =
 				cpu_to_scr(SCR_REG_REG(gpreg, SCR_AND, 0xfe));
-		np->script0->reselect2[0] =
+		np->script0->start[0] =
 				cpu_to_scr(SCR_REG_REG(gpreg, SCR_AND, 0xfe));
 	}
 
 	/*
-	**	init data structure
-	*/
-
-	np->jump_tcb.l_cmd	= cpu_to_scr(SCR_JUMP);
-	np->jump_tcb.l_paddr	= cpu_to_scr(NCB_SCRIPTH_PHYS (np, abort));
+	**	Look for the target control block of this nexus.
+	**	For i = 0 to 3
+	**		JUMP ^ IFTRUE (MASK (i, 3)), @(next_lcb)
+	*/
+	for (i = 0 ; i < 4 ; i++) {
+		np->jump_tcb[i].l_cmd   =
+				cpu_to_scr((SCR_JUMP ^ IFTRUE (MASK (i, 3))));
+		np->jump_tcb[i].l_paddr =
+				cpu_to_scr(NCB_SCRIPTH_PHYS (np, bad_target));
+	}
 
 	/*
 	**	Reset chip.
 	*/
 
 	OUTB (nc_istat,  SRST);
-	DELAY (1000);
+	UDELAY (100);
 	OUTB (nc_istat,  0   );
 
 	/*
@@ -4582,34 +4658,45 @@
 	*/
 
 	if (ncr_snooptest (np)) {
-		printf ("CACHE INCORRECTLY CONFIGURED.\n");
+		printk (KERN_ERR "CACHE INCORRECTLY CONFIGURED.\n");
 		goto attach_error;
 	};
 
 	/*
 	**	Install the interrupt handler.
 	*/
-#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
-#ifdef SCSI_NCR_SHARE_IRQ
+#ifdef	SCSI_NCR_SHARE_IRQ
+#define	NCR_SA_INTERRUPT_FLAGS (SA_INTERRUPT | SA_SHIRQ)
 	if (bootverbose > 1)
-		printf("%s: requesting shared irq %d (dev_id=0x%lx)\n",
-		        ncr_name(np), device->slot.irq, (u_long) np);
-	if (request_irq(device->slot.irq, ncr53c8xx_intr,
-			SA_INTERRUPT|SA_SHIRQ, "ncr53c8xx", np)) {
+#ifdef __sparc__
+		printk(KERN_INFO "%s: requesting shared irq %s (dev_id=0x%lx)\n",
+		        ncr_name(np), __irq_itoa(device->slot.irq), (u_long) np);
 #else
-	if (request_irq(device->slot.irq, ncr53c8xx_intr,
-			SA_INTERRUPT, "ncr53c8xx", np)) {
+		printk(KERN_INFO "%s: requesting shared irq %d (dev_id=0x%lx)\n",
+		        ncr_name(np), device->slot.irq, (u_long) np);
 #endif
 #else
+#define	NCR_SA_INTERRUPT_FLAGS SA_INTERRUPT
+#endif
 	if (request_irq(device->slot.irq, ncr53c8xx_intr,
-			SA_INTERRUPT, "ncr53c8xx")) {
+			NCR_SA_INTERRUPT_FLAGS, "ncr53c8xx", np)) {
+#ifdef __sparc__
+		printk(KERN_ERR "%s: request irq %s failure\n",
+			ncr_name(np), __irq_itoa(device->slot.irq));
+#else
+		printk(KERN_ERR "%s: request irq %d failure\n",
+			ncr_name(np), device->slot.irq);
 #endif
-		printf("%s: request irq %d failure\n", ncr_name(np), device->slot.irq);
 		goto attach_error;
 	}
 	np->irq = device->slot.irq;
 
 	/*
+	**	Initialize the fixed part of the default ccb.
+	*/
+	ncr_init_ccb(np, np->ccb);
+
+	/*
 	**	After SCSI devices have been opened, we cannot
 	**	reset the bus safely, so we do it here.
 	**	Interrupt handler does the real work.
@@ -4617,26 +4704,26 @@
 	**	if interrupts are not enabled yet.
 	**	Then enable disconnects.
 	*/
-	save_flags(flags); cli();
+	NCR_LOCK_NCB(np, flags);
 	if (ncr_reset_scsi_bus(np, 0, driver_setup.settle_delay) != 0) {
-		printf("%s: FATAL ERROR: CHECK SCSI BUS - CABLES, TERMINATION, DEVICE POWER etc.!\n", ncr_name(np));
-		restore_flags(flags);
+		printk(KERN_ERR "%s: FATAL ERROR: CHECK SCSI BUS - CABLES, TERMINATION, DEVICE POWER etc.!\n", ncr_name(np));
+
+		NCR_UNLOCK_NCB(np, flags);
 		goto attach_error;
 	}
 	ncr_exception (np);
-	restore_flags(flags);
 
 	np->disc = 1;
 
 	/*
 	**	The middle-level SCSI driver does not
-	**	wait devices to settle.
+	**	wait for devices to settle.
 	**	Wait synchronously if more than 2 seconds.
 	*/
 	if (driver_setup.settle_delay > 2) {
-		printf("%s: waiting %d seconds for scsi devices to settle...\n",
+		printk(KERN_INFO "%s: waiting %d seconds for scsi devices to settle...\n",
 			ncr_name(np), driver_setup.settle_delay);
-		DELAY(1000000UL * driver_setup.settle_delay);
+		MDELAY (1000 * driver_setup.settle_delay);
 	}
 
 	/*
@@ -4665,46 +4752,81 @@
         	first_host = instance;
 	}
 
+	NCR_UNLOCK_NCB(np, flags);
+
 	return 0;
 
 attach_error:
 	if (!instance) return -1;
-	printf("%s: detaching...\n", ncr_name(np));
+	printk(KERN_INFO "%s: detaching...\n", ncr_name(np));
 #ifndef NCR_IOMAPPED
 	if (np->vaddr) {
 #ifdef DEBUG_NCR53C8XX
-		printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr, 128);
+		printk(KERN_DEBUG "%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr, 128);
 #endif
 		unmap_pci_mem((vm_offset_t) np->vaddr, (u_long) 128);
 	}
-	if (np->vaddr2) {
-#ifdef DEBUG_NCR53C8XX
-		printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr2, 4096);
-#endif
-		unmap_pci_mem((vm_offset_t) np->vaddr2, (u_long) 4096);
-	}
-#endif
+#endif /* !NCR_IOMAPPED */
 	if (np->port) {
 #ifdef DEBUG_NCR53C8XX
-		printf("%s: releasing IO region %x[%d]\n", ncr_name(np), np->port, 128);
+		printk(KERN_DEBUG "%s: releasing IO region %x[%d]\n", ncr_name(np), np->port, 128);
 #endif
 		release_region(np->port, 128);
 	}
 	if (np->irq) {
 #ifdef DEBUG_NCR53C8XX
-		printf("%s: freeing irq %d\n", ncr_name(np), np->irq);
-#endif
-#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
-		free_irq(np->irq, np);
+#ifdef __sparc__
+	printk(KERN_INFO "%s: freeing irq %s\n", ncr_name(np),
+	       __irq_itoa(np->irq));
 #else
-		free_irq(np->irq);
+	printk(KERN_INFO "%s: freeing irq %d\n", ncr_name(np), np->irq);
+#endif
 #endif
+		free_irq(np->irq, np);
 	}
 	scsi_unregister(instance);
 
         return -1;
  }
 
+
+/*==========================================================
+**
+**
+**	Done SCSI commands list management.
+**
+**	We donnot enter the scsi_done() callback immediately 
+**	after a command has been seen as completed but we 
+**	insert it into a list which is flushed outside any kind 
+**	of driver critical section.
+**	This allows to do minimal stuff under interrupt and 
+**	inside critical sections and to also avoid locking up 
+**	on recursive calls to driver entry points under SMP.
+**	In fact, the only kernel point which is entered by the 
+**	driver with a driver lock set is kmalloc(GFP_ATOMIC) 
+**	that shall not reenter the driver under any circumstances,
+**	AFAIK.
+**
+**==========================================================
+*/
+static inline void ncr_queue_done_cmd(ncb_p np, Scsi_Cmnd *cmd)
+{
+	cmd->host_scribble = (char *) np->done_list;
+	np->done_list = cmd;
+}
+
+static inline void ncr_flush_done_cmds(Scsi_Cmnd *lcmd)
+{
+	Scsi_Cmnd *cmd;
+
+	while (lcmd) {
+		cmd = lcmd;
+		lcmd = (Scsi_Cmnd *) cmd->host_scribble;
+		cmd->scsi_done(cmd);
+	}
+}
+
+
 /*==========================================================
 **
 **
@@ -4714,27 +4836,18 @@
 **
 **==========================================================
 */
-int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
+int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd)
 {
-        struct Scsi_Host   *host      = cmd->host;
 /*	Scsi_Device        *device    = cmd->device; */
-	struct host_data   *host_data = (struct host_data *) host->hostdata;
-	ncb_p np                      = host_data->ncb;
 	tcb_p tp                      = &np->target[cmd->target];
-
+	lcb_p lp		      = tp->lp[cmd->lun];
 	ccb_p cp;
-	lcb_p lp;
 
 	int	segments;
-	u_char	qidx, nego, idmsg, *msgptr;
-	u_int  msglen, msglen2;
-	u_long	flags;
-	int	xfer_direction;
-
-	cmd->scsi_done     = done;
-	cmd->host_scribble = NULL;
-	cmd->SCp.ptr       = NULL;
-	cmd->SCp.buffer    = NULL;
+	u_char	nego, idmsg, *msgptr;
+	u_int  msglen;
+	int	direction;
+	u_int32	lastp, goalp;
 
 	/*---------------------------------------------
 	**
@@ -4764,7 +4877,7 @@
 
 	if (DEBUG_FLAGS & DEBUG_TINY) {
 		PRINT_ADDR(cmd);
-		printf ("CMD=%x ", cmd->cmnd[0]);
+		printk ("CMD=%x ", cmd->cmnd[0]);
 	}
 
 	/*---------------------------------------------------
@@ -4777,8 +4890,6 @@
 	**
 	**----------------------------------------------------
 	*/
-	save_flags(flags); cli();
-
 	if (np->settle_time && cmd->timeout_per_command >= HZ &&
 		np->settle_time > jiffies + cmd->timeout_per_command - HZ) {
 		np->settle_time = jiffies + cmd->timeout_per_command - HZ;
@@ -4786,7 +4897,6 @@
 
         if (np->settle_time || !(cp=ncr_get_ccb (np, cmd->target, cmd->lun))) {
 		insert_into_waiting_list(np, cmd);
-		restore_flags(flags);
 		return(DID_OK);
 	}
 	cp->cmd = cmd;
@@ -4797,10 +4907,12 @@
 	**
 	**----------------------------------------------------
 	*/
-	if (!tp->usrtags && cmd->device && cmd->device->tagged_queue) {
-		tp->usrtags = SCSI_NCR_MAX_TAGS;
-		ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS);
+#if 0	/* This stuff was only usefull for linux-1.2.13 */
+	if (lp && !lp->numtags && cmd->device && cmd->device->tagged_queue) {
+		lp->numtags = tp->usrtags;
+		ncr_setup_tags (np, cmd->target, cmd->lun);
 	}
+#endif
 
 	/*---------------------------------------------------
 	**
@@ -4813,47 +4925,23 @@
 	cp->phys.header.stamp.start = jiffies;
 #endif
 
-	/*----------------------------------------------------
-	**
-	**	Get device quirks from a speciality table.
-	**
-	**	@GENSCSI@
-	**	This should be a part of the device table
-	**	in "scsi_conf.c".
-	**
-	**----------------------------------------------------
-	*/
-	if (tp->quirks & QUIRK_UPDATE) {
-		tp->quirks = ncr_lookup ((char*) &tp->inqdata[0]);
-#ifndef NCR_GETCC_WITHMSG
-		if (tp->quirks) {
-			PRINT_ADDR(cmd);
-			printf ("quirks=%x.\n", tp->quirks);
-		}
-#endif
-	}
-
 	/*---------------------------------------------------
 	**
 	**	negotiation required?
 	**
-	**	Only SCSI-II devices.
-	**	To negotiate with SCSI-I devices is dangerous, since
-	**	Synchronous Negotiation protocol is optional, and
-	**	INQUIRY data do not contains capabilities in byte 7.
-	**----------------------------------------------------
+	**---------------------------------------------------
 	*/
 
 	nego = 0;
 
-	if (cmd->lun == 0 && !tp->nego_cp && 
-	    (tp->inqdata[2] & 0x7) >= 2 && tp->inqdata[7]) {
+	if ((!tp->widedone || !tp->period) && !tp->nego_cp && tp->inq_done && lp) {
+
 		/*
 		**	negotiate wide transfers ?
 		*/
 
 		if (!tp->widedone) {
-			if (tp->inqdata[7] & INQ7_WIDE16) {
+			if (tp->inq_byte7 & INQ7_WIDE16) {
 				nego = NS_WIDE;
 			} else
 				tp->widedone=1;
@@ -4864,17 +4952,13 @@
 		*/
 
 		if (!nego && !tp->period) {
-			if ( 1
-#if defined (CDROM_ASYNC)
-			    && ((tp->inqdata[0] & 0x1f) != 5)
-#endif
-			    && (tp->inqdata[7] & INQ7_SYNC)) {
+			if (tp->inq_byte7 & INQ7_SYNC) {
 				nego = NS_SYNC;
 			} else {
 				tp->period  =0xffff;
 				tp->sval = 0xe0;
-				PRINT_ADDR(cmd);
-				printf ("asynchronous.\n");
+				PRINT_TARGET(np, cmd->target);
+				printk ("SYNC transfers not supported.\n");
 			};
 		};
 
@@ -4888,33 +4972,6 @@
 			tp->nego_cp = cp;
 	};
 
-	/*---------------------------------------------------
-	**
-	**	choose a new tag ...
-	**
-	**----------------------------------------------------
-	*/
-
-	if ((lp = tp->lp[cmd->lun]) && (lp->usetags)) {
-		/*
-		**	assign a tag to this ccb!
-		*/
-		while (!cp->tag) {
-			ccb_p cp2 = lp->next_ccb;
-			lp->lasttag = lp->lasttag % 255 + 1;
-			while (cp2 && cp2->tag != lp->lasttag)
-				cp2 = cp2->next_ccb;
-			if (cp2) continue;
-			cp->tag=lp->lasttag;
-			if (DEBUG_FLAGS & DEBUG_TAGS) {
-				PRINT_ADDR(cmd);
-				printf ("using tag #%d.\n", cp->tag);
-			}
-		}
-	} else {
-		cp->tag=0;
-	}
-
 	/*----------------------------------------------------
 	**
 	**	Build the identify / tag / sdtr message
@@ -4924,44 +4981,54 @@
 
 	idmsg = M_IDENTIFY | cmd->lun;
 
-	if (cp != np->ccb && ((np->disc && !(tp->usrflag & UF_NODISC)) || cp->tag))
+	if (cp ->tag != NO_TAG ||
+		(cp != np->ccb && np->disc && !(tp->usrflag & UF_NODISC)))
 		idmsg |= 0x40;
 
 	msgptr = cp->scsi_smsg;
 	msglen = 0;
 	msgptr[msglen++] = idmsg;
 
-	if (cp->tag) {
-	    char tag;
+	if (cp->tag != NO_TAG) {
+		char order = np->order;
 
-	    tag = np->order;
-	    if (tag == 0) {
 		/*
-		**	Ordered write ops, unordered read ops.
+		**	Force ordered tag if necessary to avoid timeouts 
+		**	and to preserve interactivity.
 		*/
-		switch (cmd->cmnd[0]) {
-		case 0x08:  /* READ_SMALL (6) */
-		case 0x28:  /* READ_BIG  (10) */
-		case 0xa8:  /* READ_HUGE (12) */
-		    tag = M_SIMPLE_TAG;
-		    break;
-		default:
-		    tag = M_ORDERED_TAG;
+		if (lp && lp->tags_stime + (3*HZ) <= jiffies) {
+			if (lp->tags_smap) {
+				order = M_ORDERED_TAG;
+				if ((DEBUG_FLAGS & DEBUG_TAGS)||bootverbose>2){ 
+					PRINT_ADDR(cmd);
+					printk("ordered tag forced.\n");
+				}
+			}
+			lp->tags_stime = jiffies;
+			lp->tags_smap = lp->tags_umap;
 		}
-	    }
-	    /*
-	    **	Have to force ordered tag to avoid timeouts
-	    */
-	    if ((lp = tp->lp[cmd->lun]) && (lp->force_ordered_tag)) {
-		tag = M_ORDERED_TAG;
-		lp->force_ordered_tag = 0;
-		if (DEBUG_FLAGS & DEBUG_TAGS) {
-			PRINT_ADDR(cmd);
-			printf ("Ordered Queue Tag forced\n");
+
+		if (order == 0) {
+			/*
+			**	Ordered write ops, unordered read ops.
+			*/
+			switch (cmd->cmnd[0]) {
+			case 0x08:  /* READ_SMALL (6) */
+			case 0x28:  /* READ_BIG  (10) */
+			case 0xa8:  /* READ_HUGE (12) */
+				order = M_SIMPLE_TAG;
+				break;
+			default:
+				order = M_ORDERED_TAG;
+			}
 		}
-	    }
-	    msgptr[msglen++] = tag;
-	    msgptr[msglen++] = cp -> tag;
+		msgptr[msglen++] = order;
+		/*
+		**	Actual tags are numbered 1,3,5,..2*MAXTAGS+1,
+		**	since we may have to deal with devices that have 
+		**	problems with #TAG 0 or too great #TAG numbers.
+		*/
+		msgptr[msglen++] = (cp->tag << 1) + 1;
 	}
 
 	switch (nego) {
@@ -4973,9 +5040,9 @@
 		msgptr[msglen++] = tp->maxoffs;
 		if (DEBUG_FLAGS & DEBUG_NEGO) {
 			PRINT_ADDR(cp->cmd);
-			printf ("sync msgout: ");
+			printk ("sync msgout: ");
 			ncr_show_msg (&cp->scsi_smsg [msglen-5]);
-			printf (".\n");
+			printk (".\n");
 		};
 		break;
 	case NS_WIDE:
@@ -4985,25 +5052,15 @@
 		msgptr[msglen++] = tp->usrwide;
 		if (DEBUG_FLAGS & DEBUG_NEGO) {
 			PRINT_ADDR(cp->cmd);
-			printf ("wide msgout: ");
+			printk ("wide msgout: ");
 			ncr_show_msg (&cp->scsi_smsg [msglen-4]);
-			printf (".\n");
+			printk (".\n");
 		};
 		break;
 	};
 
 	/*----------------------------------------------------
 	**
-	**	Build the identify message for getcc.
-	**
-	**----------------------------------------------------
-	*/
-
-	cp -> scsi_smsg2 [0] = idmsg;
-	msglen2 = 1;
-
-	/*----------------------------------------------------
-	**
 	**	Build the data descriptors
 	**
 	**----------------------------------------------------
@@ -5012,8 +5069,7 @@
 	segments = ncr_scatter (cp, cp->cmd);
 
 	if (segments < 0) {
-		ncr_free_ccb(np, cp, cmd->target, cmd->lun);
-		restore_flags(flags);
+		ncr_free_ccb(np, cp);
 		return(DID_ERROR);
 	}
 
@@ -5024,20 +5080,24 @@
 	**
 	**----------------------------------------------------
 	*/
-	switch((int) cmd->cmnd[0]) {
-	case 0x08:  /*	READ(6)				08 */
-	case 0x28:  /*	READ(10)			28 */
-	case 0xA8:  /*	READ(12)			A8 */
-		xfer_direction = XferIn;
-		break;
-	case 0x0A:  /*	WRITE(6)			0A */
-	case 0x2A:  /*	WRITE(10)			2A */
-	case 0xAA:  /*	WRITE(12)			AA */
-		xfer_direction = XferOut;
-		break;
-	default:
-		xfer_direction = guess_xfer_direction((int) cmd->cmnd[0]);
-		break;
+	if (!cp->data_len)
+		direction = 0;
+	else {
+		switch((int) cmd->cmnd[0]) {
+		case 0x08:  /*	READ(6)				08 */
+		case 0x28:  /*	READ(10)			28 */
+		case 0xA8:  /*	READ(12)			A8 */
+			direction = XFER_IN;
+			break;
+		case 0x0A:  /*	WRITE(6)			0A */
+		case 0x2A:  /*	WRITE(10)			2A */
+		case 0xAA:  /*	WRITE(12)			AA */
+			direction = XFER_OUT;
+			break;
+		default:
+			direction = (XFER_IN|XFER_OUT);
+			break;
+		}
 	}
 
 	/*----------------------------------------------------
@@ -5047,36 +5107,64 @@
 	**----------------------------------------------------
 	*/
 
-	cp->segments = segments;
-	if (!cp->data_len)
-		xfer_direction = XferNone;
+	/*
+	**	Default to no data transfer.
+	*/
+	lastp = goalp = NCB_SCRIPT_PHYS (np, no_data);
 
-	switch (xfer_direction) {
-		u_long endp;
-	default:
-	case XferBoth:
-		cp->phys.header.savep =
-			cpu_to_scr(NCB_SCRIPT_PHYS (np, data_io));
-		cp->phys.header.goalp = cp->phys.header.savep;
-		break;
-	case XferIn:
-		endp = NCB_SCRIPT_PHYS (np, data_in) + MAX_SCATTER*16;
-		cp->phys.header.goalp = cpu_to_scr(endp + 8);
-		cp->phys.header.savep = cpu_to_scr(endp - segments*16);
-		break;
-	case XferOut:
-		endp = NCB_SCRIPTH_PHYS (np, data_out) + MAX_SCATTER*16;
-		cp->phys.header.goalp = cpu_to_scr(endp + 8);
-		cp->phys.header.savep = cpu_to_scr(endp - segments*16);
-		break;
-	case XferNone:
-		cp->phys.header.savep =
-			cpu_to_scr(NCB_SCRIPT_PHYS (np, no_data));
-		cp->phys.header.goalp = cp->phys.header.savep;
-		break;
+	/*
+	**	Compute data out pointers, if needed.
+	*/
+	if (direction & XFER_OUT) {
+		goalp = NCB_SCRIPT_PHYS (np, data_out2) + 8;
+		if (segments <= MAX_SCATTERL)
+			lastp = goalp - 8 - (segments * 16);
+		else {
+			lastp = NCB_SCRIPTH_PHYS (np, hdata_out2);
+			lastp -= (segments - MAX_SCATTERL) * 16;
+		}
+		/*
+		**	If actual data direction is unknown, save pointers 
+		**	in header. The SCRIPTS will swap them to current 
+		**	if target decision will be data out.
+		*/
+		if (direction & XFER_IN) {
+			cp->phys.header.wgoalp	= cpu_to_scr(goalp);
+			cp->phys.header.wlastp	= cpu_to_scr(lastp);
+		}
 	}
 
-	cp->phys.header.lastp = cp->phys.header.savep;
+	/*
+	**	Compute data in pointers, if needed.
+	*/
+	if (direction & XFER_IN) {
+		goalp = NCB_SCRIPT_PHYS (np, data_in2) + 8;
+		if (segments <= MAX_SCATTERL)
+			lastp = goalp - 8 - (segments * 16);
+		else {
+			lastp = NCB_SCRIPTH_PHYS (np, hdata_in2);
+			lastp -= (segments - MAX_SCATTERL) * 16;
+		}
+	}
+
+	/*
+	**	Set all pointers values needed by SCRIPTS.
+	**	If direction is unknown, start at data_io.
+	*/
+	cp->phys.header.lastp = cpu_to_scr(lastp);
+	cp->phys.header.goalp = cpu_to_scr(goalp);
+
+	if ((direction & (XFER_IN|XFER_OUT)) == (XFER_IN|XFER_OUT))
+		cp->phys.header.savep = 
+			cpu_to_scr(NCB_SCRIPTH_PHYS (np, data_io));
+	else
+		cp->phys.header.savep= cpu_to_scr(lastp);
+
+	/*
+	**	Save the initial data pointer in order to be able 
+	**	to redo the command.
+	*/
+	cp->startp = cp->phys.header.savep;
 
 	/*----------------------------------------------------
 	**
@@ -5088,13 +5176,12 @@
 	**	physical -> virtual backlink
 	**	Generic SCSI command
 	*/
-	cp->phys.header.cp		= cp;
+
 	/*
 	**	Startqueue
 	*/
-	cp->phys.header.launch.l_paddr	=
-			cpu_to_scr(NCB_SCRIPT_PHYS (np, select));
-	cp->phys.header.launch.l_cmd	= cpu_to_scr(SCR_JUMP);
+	cp->start.schedule.l_paddr   = cpu_to_scr(NCB_SCRIPT_PHYS (np, select));
+	cp->restart.schedule.l_paddr = cpu_to_scr(NCB_SCRIPT_PHYS (np, resel_dsa));
 	/*
 	**	select
 	*/
@@ -5107,30 +5194,12 @@
 	cp->phys.smsg.addr		= cpu_to_scr(CCB_PHYS (cp, scsi_smsg));
 	cp->phys.smsg.size		= cpu_to_scr(msglen);
 
-	cp->phys.smsg2.addr		= cpu_to_scr(CCB_PHYS (cp, scsi_smsg2));
-	cp->phys.smsg2.size		= cpu_to_scr(msglen2);
-	/*
-	**	command
-	*/
-	cp->phys.cmd.addr		= cpu_to_scr(vtophys (&cmd->cmnd[0]));
-	cp->phys.cmd.size		= cpu_to_scr(cmd->cmd_len);
-	/*
-	**	sense command
-	*/
-	cp->phys.scmd.addr		= cpu_to_scr(CCB_PHYS (cp, sensecmd));
-	cp->phys.scmd.size		= cpu_to_scr(6);
-	/*
-	**	patch requested size into sense command
-	*/
-	cp->sensecmd[0]			= 0x03;
-	cp->sensecmd[1]			= cmd->lun << 5;
-	cp->sensecmd[4]			= sizeof(cmd->sense_buffer);
 	/*
-	**	sense data
+	**	command
 	*/
-	cp->phys.sense.addr		=
-			cpu_to_scr(vtophys (&cmd->sense_buffer[0]));
-	cp->phys.sense.size		= cpu_to_scr(sizeof(cmd->sense_buffer));
+	cp->phys.cmd.addr		= cpu_to_scr(vtophys (&cmd->cmnd[0]));
+	cp->phys.cmd.size		= cpu_to_scr(cmd->cmd_len);
+
 	/*
 	**	status
 	*/
@@ -5140,9 +5209,11 @@
 	cp->parity_status		= 0;
 
 	cp->xerr_status			= XE_OK;
-	cp->sync_status			= tp->sval;
 	cp->nego_status			= nego;
+#if 0
+	cp->sync_status			= tp->sval;
 	cp->wide_status			= tp->wval;
+#endif
 
 	/*----------------------------------------------------
 	**
@@ -5152,56 +5223,96 @@
 	*/
 
 	/*
-	**	reselect pattern and activate this job.
+	**	activate this job.
 	*/
 
-	cp->jump_ccb.l_cmd	=
-		cpu_to_scr((SCR_JUMP ^ IFFALSE (DATA (cp->tag))));
-
 	/* Compute a time limit greater than the middle-level driver one */
 	if (cmd->timeout_per_command > 0)
-		cp->tlimit	= jiffies + cmd->timeout_per_command + NCR_TIMEOUT_INCREASE;
+		cp->tlimit	= jiffies + cmd->timeout_per_command + HZ;
 	else
-		cp->tlimit	= jiffies + 3600 * HZ; /* No timeout=one hour */
+		cp->tlimit	= jiffies + 86400 * HZ;/* No timeout=24 hours */
 	cp->magic		= CCB_MAGIC;
 
 	/*
+	**	insert next CCBs into start queue.
+	**	2 max at a time is enough to flush the CCB wait queue.
+	*/
+	cp->auto_sense = 0;
+	if (lp)
+		ncr_start_next_ccb(np, lp, 2);
+	else
+		ncr_put_start_queue(np, cp);
+
+	/*
+	**	Command is successfully queued.
+	*/
+
+	return(DID_OK);
+}
+
+
+/*==========================================================
+**
+**
+**	Insert a CCB into the start queue and wake up the 
+**	SCRIPTS processor.
+**
+**
+**==========================================================
+*/
+
+static void ncr_start_next_ccb(ncb_p np, lcb_p lp, int maxn)
+{
+	XPT_QUEHEAD *qp;
+	ccb_p cp;
+
+	if (lp->held_ccb)
+		return;
+
+	while (maxn-- && lp->queuedccbs < lp->queuedepth) {
+		qp = xpt_remque_head(&lp->wait_ccbq);
+		if (!qp)
+			break;
+		++lp->queuedccbs;
+		cp = xpt_que_entry(qp, struct ccb, link_ccbq);
+		xpt_insque_tail(qp, &lp->busy_ccbq);
+		lp->jump_ccb[cp->tag == NO_TAG ? 0 : cp->tag] =
+			cpu_to_scr(CCB_PHYS (cp, restart));
+		ncr_put_start_queue(np, cp);
+	}
+}
+
+static void ncr_put_start_queue(ncb_p np, ccb_p cp)
+{
+	u_short	qidx;
+
+	/*
 	**	insert into start queue.
 	*/
+	if (!np->squeueput) np->squeueput = 1;
+	qidx = np->squeueput + 2;
+	if (qidx >= MAX_START + MAX_START) qidx = 1;
+
+	np->scripth->tryloop [qidx] = cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
+	MEMORY_BARRIER();
+	np->scripth->tryloop [np->squeueput] = cpu_to_scr(CCB_PHYS (cp, start));
 
-	qidx = np->squeueput + 1;
-	if (qidx >= MAX_START) qidx=0;
-	np->squeue [qidx	 ] = cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
-	np->squeue [np->squeueput] = cpu_to_scr(CCB_PHYS (cp, phys));
 	np->squeueput = qidx;
+	++np->queuedccbs;
+	cp->queued = 1;
 
-	if(DEBUG_FLAGS & DEBUG_QUEUE)
-		printf ("%s: queuepos=%d tryoffset=%d.\n", ncr_name (np),
-		np->squeueput,
-		(unsigned)(scr_to_cpu(np->script->startpos[0]) - 
-			   (NCB_SCRIPTH_PHYS (np, tryloop))));
+	if (DEBUG_FLAGS & DEBUG_QUEUE)
+		printk ("%s: queuepos=%d.\n", ncr_name (np), np->squeueput);
 
 	/*
 	**	Script processor may be waiting for reselect.
 	**	Wake it up.
 	*/
-#ifdef	SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
-	if (!np->stalling)
-#endif
+	MEMORY_BARRIER();
 	OUTB (nc_istat, SIGP);
-
-	/*
-	**	and reenable interrupts
-	*/
-	restore_flags(flags);
-
-	/*
-	**	Command is successfully queued.
-	*/
-
-	return(DID_OK);
 }
 
+
 /*==========================================================
 **
 **
@@ -5214,18 +5325,13 @@
 **
 **==========================================================
 */
-static void ncr_start_reset(ncb_p np, int settle_delay)
+static void ncr_start_reset(ncb_p np)
 {
-	u_long flags;
-
-	save_flags(flags); cli();
-
 	if (!np->settle_time) {
-		(void) ncr_reset_scsi_bus(np, 1, settle_delay);
-	}
-	restore_flags(flags);
-}
-
+		(void) ncr_reset_scsi_bus(np, 1, driver_setup.settle_delay);
+ 	}
+ }
+ 
 static int ncr_reset_scsi_bus(ncb_p np, int enab_int, int settle_delay)
 {
 	u_int32 term;
@@ -5234,13 +5340,14 @@
 	np->settle_time	= jiffies + settle_delay * HZ;
 
 	if (bootverbose > 1)
-		printf("%s: resetting, "
+		printk("%s: resetting, "
 			"command processing suspended for %d seconds\n",
 			ncr_name(np), settle_delay);
 
 	OUTB (nc_istat, SRST);
-	DELAY (1000);
+	UDELAY (100);
 	OUTB (nc_istat, 0);
+	UDELAY (2000);	/* The 895 needs time for the bus mode to settle */
 	if (enab_int)
 		OUTW (nc_sien, RST);
 	/*
@@ -5250,7 +5357,7 @@
 	OUTB (nc_stest3, TE);
 	OUTB (nc_dcntl, (np->rv_dcntl & IRQM));
 	OUTB (nc_scntl1, CRST);
-	DELAY (100);
+	UDELAY (200);
 
 	if (!driver_setup.bus_check)
 		goto out;
@@ -5270,9 +5377,9 @@
 		term &= 0x3ffff;
 
 	if (term != (2<<7)) {
-		printf("%s: suspicious SCSI data while resetting the BUS.\n",
+		printk("%s: suspicious SCSI data while resetting the BUS.\n",
 			ncr_name(np));
-		printf("%s: %sdp0,d7-0,rst,req,ack,bsy,sel,atn,msg,c/d,i/o = "
+		printk("%s: %sdp0,d7-0,rst,req,ack,bsy,sel,atn,msg,c/d,i/o = "
 			"0x%lx, expecting 0x%lx\n",
 			ncr_name(np),
 			(np->features & FE_WIDE) ? "dp1,d15-8," : "",
@@ -5294,27 +5401,16 @@
 **
 **==========================================================
 */
-int ncr_reset_bus (Scsi_Cmnd *cmd, int sync_reset)
+int ncr_reset_bus (ncb_p np, Scsi_Cmnd *cmd, int sync_reset)
 {
-        struct Scsi_Host   *host      = cmd->host;
 /*	Scsi_Device        *device    = cmd->device; */
-	struct host_data   *host_data = (struct host_data *) host->hostdata;
-	ncb_p np                      = host_data->ncb;
 	ccb_p cp;
-	u_long flags;
 	int found;
 
-#ifdef	SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
-	if (np->stalling)
-		np->stalling = 0;
-#endif
-
-	save_flags(flags); cli();
 /*
  * Return immediately if reset is in progress.
  */
 	if (np->settle_time) {
-		restore_flags(flags);
 		return SCSI_RESET_PUNT;
 	}
 /*
@@ -5323,7 +5419,7 @@
  * Commands will now be queued in the waiting list until a settle 
  * delay of 2 seconds will be completed.
  */
-	ncr_start_reset(np, driver_setup.settle_delay);
+	ncr_start_reset(np);
 /*
  * First, look in the wakeup list
  */
@@ -5358,11 +5454,9 @@
  */
 	if (!found && sync_reset && !retrieve_from_waiting_list(0, np, cmd)) {
 		cmd->result = ScsiResult(DID_RESET, 0);
-		cmd->scsi_done(cmd);
+		ncr_queue_done_cmd(np, cmd);
 	}
 
-	restore_flags(flags);
-
 	return SCSI_RESET_SUCCESS;
 }
 
@@ -5375,30 +5469,19 @@
 **
 **==========================================================
 */
-static int ncr_abort_command (Scsi_Cmnd *cmd)
+static int ncr_abort_command (ncb_p np, Scsi_Cmnd *cmd)
 {
-        struct Scsi_Host   *host      = cmd->host;
 /*	Scsi_Device        *device    = cmd->device; */
-	struct host_data   *host_data = (struct host_data *) host->hostdata;
-	ncb_p np                      = host_data->ncb;
 	ccb_p cp;
-	u_long flags;
 	int found;
 	int retv;
 
-#ifdef	SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
-	if (np->stalling == 2)
-		np->stalling = 0;
-#endif
-
-	save_flags(flags); cli();
 /*
  * First, look for the scsi command in the waiting list
  */
 	if (remove_from_waiting_list(np, cmd)) {
 		cmd->result = ScsiResult(DID_ABORT, 0);
-		cmd->scsi_done(cmd);
-		restore_flags(flags);
+		ncr_queue_done_cmd(np, cmd);
 		return SCSI_ABORT_SUCCESS;
 	}
 
@@ -5417,45 +5500,45 @@
 	}
 
 	if (!found) {
-		restore_flags(flags);
 		return SCSI_ABORT_NOT_RUNNING;
 	}
 
 	if (np->settle_time) {
-		restore_flags(flags);
 		return SCSI_ABORT_SNOOZE;
 	}
 
 	/*
-	**	Disable reselect.
-	**      Remove it from startqueue.
-	**	Set cp->tlimit to 0. The ncr_timeout() handler will use 
-	**	this condition in order to complete the canceled command 
-	**	after the script skipped the ccb, if necessary.
-	*/
-	cp->jump_ccb.l_cmd = cpu_to_scr(SCR_JUMP);
-	if (cp->phys.header.launch.l_paddr ==
-		cpu_to_scr(NCB_SCRIPT_PHYS (np, select))) {
-		printf ("%s: abort ccb=%p (skip)\n", ncr_name (np), cp);
-		cp->phys.header.launch.l_paddr =
-			cpu_to_scr(NCB_SCRIPT_PHYS (np, skip));
-	}
+	**	If the CCB is active, patch schedule jumps for the 
+	**	script to abort the command.
+	*/
 
 	cp->tlimit = 0;
-	retv = SCSI_ABORT_PENDING;
+	switch(cp->host_status) {
+	case HS_BUSY:
+	case HS_NEGOTIATE:
+		printk ("%s: abort ccb=%p (cancel)\n", ncr_name (np), cp);
+			cp->start.schedule.l_paddr =
+				cpu_to_scr(NCB_SCRIPTH_PHYS (np, cancel));
+		retv = SCSI_ABORT_PENDING;
+		break;
+	case HS_DISCONNECT:
+		cp->restart.schedule.l_paddr =
+				cpu_to_scr(NCB_SCRIPTH_PHYS (np, abort));
+		retv = SCSI_ABORT_PENDING;
+		break;
+	default:
+		retv = SCSI_ABORT_NOT_RUNNING;
+		break;
+
+	}
 
 	/*
 	**      If there are no requests, the script
 	**      processor will sleep on SEL_WAIT_RESEL.
 	**      Let's wake it up, since it may have to work.
 	*/
-#ifdef	SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
-	if (!np->stalling)
-#endif
 	OUTB (nc_istat, SIGP);
 
-	restore_flags(flags);
-
 	return retv;
 }
 
@@ -5479,7 +5562,7 @@
 	int target, lun;
 	int i;
 
-	printf("%s: releasing host resources\n", ncr_name(np));
+	printk("%s: releasing host resources\n", ncr_name(np));
 
 /*
 **	Stop the ncr_timeout process
@@ -5487,12 +5570,12 @@
 */
 
 #ifdef DEBUG_NCR53C8XX
-	printf("%s: stopping the timer\n", ncr_name(np));
+	printk("%s: stopping the timer\n", ncr_name(np));
 #endif
 	np->release_stage = 1;
-	for (i = 50 ; i && np->release_stage != 2 ; i--) DELAY(100000);
+	for (i = 50 ; i && np->release_stage != 2 ; i--) MDELAY (100);
 	if (np->release_stage != 2)
-		printf("%s: the timer seems to be already stopped\n", ncr_name(np));
+		printk("%s: the timer seems to be already stopped\n", ncr_name(np));
 	else np->release_stage = 2;
 
 /*
@@ -5500,7 +5583,7 @@
 */
 
 #ifdef DEBUG_NCR53C8XX
-	printf("%s: disabling chip interrupts\n", ncr_name(np));
+	printk("%s: disabling chip interrupts\n", ncr_name(np));
 #endif
 	OUTW (nc_sien , 0);
 	OUTB (nc_dien , 0);
@@ -5510,22 +5593,22 @@
 */
 
 #ifdef DEBUG_NCR53C8XX
-	printf("%s: freeing irq %d\n", ncr_name(np), np->irq);
-#endif
-#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
-	free_irq(np->irq, np);
+#ifdef __sparc__
+	printk("%s: freeing irq %s\n", ncr_name(np), __irq_itoa(np->irq));
 #else
-	free_irq(np->irq);
+	printk("%s: freeing irq %d\n", ncr_name(np), np->irq);
 #endif
+#endif
+	free_irq(np->irq, np);
 
 	/*
 	**	Reset NCR chip
 	**	Restore bios setting for automatic clock detection.
 	*/
 
-	printf("%s: resetting chip\n", ncr_name(np));
+	printk("%s: resetting chip\n", ncr_name(np));
 	OUTB (nc_istat,  SRST);
-	DELAY (1000);
+	UDELAY (100);
 	OUTB (nc_istat,  0   );
 
 	OUTB(nc_dmode,	np->sv_dmode);
@@ -5544,17 +5627,13 @@
 
 #ifndef NCR_IOMAPPED
 #ifdef DEBUG_NCR53C8XX
-	printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr, 128);
+	printk("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr, 128);
 #endif
 	unmap_pci_mem((vm_offset_t) np->vaddr, (u_long) 128);
-#ifdef DEBUG_NCR53C8XX
-	printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr2, 4096);
-#endif
-	unmap_pci_mem((vm_offset_t) np->vaddr2, (u_long) 4096);
-#endif
+#endif /* !NCR_IOMAPPED */
 
 #ifdef DEBUG_NCR53C8XX
-	printf("%s: releasing IO region %x[%d]\n", ncr_name(np), np->port, 128);
+	printk("%s: releasing IO region %x[%d]\n", ncr_name(np), np->port, 128);
 #endif
 	release_region(np->port, 128);
 
@@ -5565,11 +5644,11 @@
 	while ((cp=np->ccb->link_ccb) != NULL) {
 		np->ccb->link_ccb = cp->link_ccb;
 		if (cp->host_status) {
-		printf("%s: shall free an active ccb (host_status=%d)\n",
+		printk("%s: shall free an active ccb (host_status=%d)\n",
 			ncr_name(np), cp->host_status);
 		}
 #ifdef DEBUG_NCR53C8XX
-	printf("%s: freeing ccb (%lx)\n", ncr_name(np), (u_long) cp);
+	printk("%s: freeing ccb (%lx)\n", ncr_name(np), (u_long) cp);
 #endif
 		m_free(cp, sizeof(*cp));
 	}
@@ -5584,14 +5663,16 @@
 			lp = tp->lp[lun];
 			if (lp) {
 #ifdef DEBUG_NCR53C8XX
-	printf("%s: freeing lp (%lx)\n", ncr_name(np), (u_long) lp);
+	printk("%s: freeing lp (%lx)\n", ncr_name(np), (u_long) lp);
 #endif
+				if (lp->jump_ccb != &lp->jump_ccb_0)
+					m_free(lp->jump_ccb, 256);
 				m_free(lp, sizeof(*lp));
 			}
 		}
 	}
 
-	printf("%s: host resources successfully released\n", ncr_name(np));
+	printk("%s: host resources successfully released\n", ncr_name(np));
 
 	return 1;
 }
@@ -5617,20 +5698,8 @@
 	**	Sanity check
 	*/
 
-	if (!cp || (cp->magic!=CCB_MAGIC) || !cp->cmd) return;
-	cp->magic = 1;
-	cp->tlimit= 0;
-	cmd = cp->cmd;
-
-	/*
-	**	No Reselect anymore.
-	*/
-	cp->jump_ccb.l_cmd = cpu_to_scr(SCR_JUMP);
-
-	/*
-	**	No starting.
-	*/
-	cp->phys.header.launch.l_paddr = cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
+	if (!cp || cp->magic != CCB_MAGIC || !cp->cmd)
+		return;
 
 	/*
 	**	timestamp
@@ -5641,9 +5710,13 @@
 #endif
 
 	if (DEBUG_FLAGS & DEBUG_TINY)
-		printf ("CCB=%lx STAT=%x/%x\n", (unsigned long)cp & 0xfff,
+		printk ("CCB=%lx STAT=%x/%x\n", (unsigned long)cp & 0xfff,
 			cp->host_status,cp->scsi_status);
 
+	/*
+	**	Get command, target and lun pointers.
+	*/
+
 	cmd = cp->cmd;
 	cp->cmd = NULL;
 	tp = &np->target[cmd->target];
@@ -5659,17 +5732,32 @@
 		tp->nego_cp = 0;
 
 	/*
+	**	If auto-sense performed, change scsi status.
+	*/
+	if (cp->auto_sense) {
+		cp->scsi_status = cp->auto_sense;
+	}
+
+	/*
+	**	If we were recovering from queue full or performing 
+	**	auto-sense, requeue skipped CCBs to the wait queue.
+	*/
+
+	if (lp && lp->held_ccb) {
+		if (cp == lp->held_ccb) {
+			xpt_que_splice(&lp->skip_ccbq, &lp->wait_ccbq);
+			xpt_que_init(&lp->skip_ccbq);
+			lp->held_ccb = 0;
+		}
+	}
+
+	/*
 	**	Check for parity errors.
 	*/
 
-	if (cp->parity_status) {
+	if (cp->parity_status > 1) {
 		PRINT_ADDR(cmd);
-		printf ("%d parity error(s), fallback.\n", cp->parity_status);
-		/*
-		**	fallback to asynch transfer.
-		*/
-		tp->usrsync=255;
-		tp->period =  0;
+		printk ("%d parity error(s).\n",cp->parity_status);
 	}
 
 	/*
@@ -5680,13 +5768,13 @@
 		PRINT_ADDR(cmd);
 		switch (cp->xerr_status) {
 		case XE_EXTRA_DATA:
-			printf ("extraneous data discarded.\n");
+			printk ("extraneous data discarded.\n");
 			break;
 		case XE_BAD_PHASE:
-			printf ("illegal scsi phase (4/5).\n");
+			printk ("illegal scsi phase (4/5).\n");
 			break;
 		default:
-			printf ("extended error %d.\n", cp->xerr_status);
+			printk ("extended error %d.\n", cp->xerr_status);
 			break;
 		}
 		if (cp->host_status==HS_COMPLETE)
@@ -5694,95 +5782,70 @@
 	}
 
 	/*
+	**	Print out any error for debugging purpose.
+	*/
+	if (DEBUG_FLAGS & (DEBUG_RESULT|DEBUG_TINY)) {
+		if (cp->host_status!=HS_COMPLETE || cp->scsi_status!=S_GOOD) {
+			PRINT_ADDR(cmd);
+			printk ("ERROR: cmd=%x host_status=%x scsi_status=%x\n",
+				cmd->cmnd[0], cp->host_status, cp->scsi_status);
+		}
+	}
+
+	/*
 	**	Check the status.
 	*/
 	if (   (cp->host_status == HS_COMPLETE)
 		&& (cp->scsi_status == S_GOOD ||
 		    cp->scsi_status == S_COND_MET)) {
-		/*
+                /*
 		**	All went well (GOOD status).
 		**	CONDITION MET status is returned on 
-		**	`Pre-Fetch' or `Search data' success.
-		*/
+                **	`Pre-Fetch' or `Search data' success.
+                */
 		cmd->result = ScsiResult(DID_OK, cp->scsi_status);
 
 		/*
-		** if (cp->phys.header.lastp != cp->phys.header.goalp)...
-		**
 		**	@RESID@
 		**	Could dig out the correct value for resid,
 		**	but it would be quite complicated.
-		**
-		**	The ah1542.c driver sets it to 0 too ...
 		*/
+		/* if (cp->phys.header.lastp != cp->phys.header.goalp) */
 
 		/*
-		**	Try to assign a ccb to this nexus
+		**	Allocate the lcb if not yet.
 		*/
-		ncr_alloc_ccb (np, cmd->target, cmd->lun);
+		if (!lp)
+			ncr_alloc_lcb (np, cmd->target, cmd->lun);
 
 		/*
-		**	On inquire cmd (0x12) save some data.
-		**	Clear questionnable capacities.
+		**	On standard INQUIRY response (EVPD and CmDt 
+		**	not set), setup logical unit according to 
+		**	announced capabilities (we need the 1rst 7 bytes).
 		*/
-		if (cmd->lun == 0 && cmd->cmnd[0] == 0x12) {
-			if (np->unit < SCSI_NCR_MAX_HOST) {
-				if (driver_setup.force_sync_nego)
-					((char *) cmd->request_buffer)[7] |= INQ7_SYNC;
-				else
-					((char *) cmd->request_buffer)[7] &=
-					(target_capabilities[np->unit].and_map[cmd->target]);
-			}
-			bcopy (	cmd->request_buffer,
-				&tp->inqdata,
-				sizeof (tp->inqdata));
-
-			/*
-			**	set number of tags
-			*/
-				ncr_setmaxtags (np, tp, driver_setup.default_tags);
-			/*
-			**	prepare negotiation of synch and wide.
-			*/
-			ncr_negotiate (np, tp);
-
-			/*
-			**	force quirks update before next command start
-			*/
-			tp->quirks |= QUIRK_UPDATE;
+		if (cmd->cmnd[0] == 0x12 && !(cmd->cmnd[1] & 0x3) &&
+		    cmd->cmnd[4] >= 7) {
+			ncr_setup_lcb (np, cmd->target, cmd->lun,
+				       (char *) cmd->request_buffer);
 		}
 
-		/*
-		**	Announce changes to the generic driver.
-		*/
-		if (lp) {
-			ncr_settags (tp, lp);
-			if (lp->reqlink != lp->actlink)
-				ncr_opennings (np, lp, cmd);
-		};
-
 		tp->bytes     += cp->data_len;
 		tp->transfers ++;
 
 		/*
 		**	If tags was reduced due to queue full,
-		**	increase tags if 100 good status received.
+		**	increase tags if 1000 good status received.
 		*/
-		if (tp->numtags < tp->maxtags) {
-			++tp->num_good;
-			if (tp->num_good >= 100) {
-				tp->num_good = 0;
-				++tp->numtags;
-				if (tp->numtags == 1) {
-					PRINT_ADDR(cmd);
-					printf("tagged command queueing resumed\n");
-				}
+		if (lp && lp->usetags && lp->numtags < lp->maxtags) {
+			++lp->num_good;
+			if (lp->num_good >= 1000) {
+				lp->num_good = 0;
+				++lp->numtags;
+				ncr_setup_tags (np, cmd->target, cmd->lun);
 			}
 		}
 	} else if ((cp->host_status == HS_COMPLETE)
-		&& (cp->scsi_status == (S_SENSE|S_GOOD) ||
-		    cp->scsi_status == (S_SENSE|S_CHECK_COND))) {
-
+		&& (cp->scsi_status == S_CHECK_COND)) {
 		/*
 		**   Check condition code
 		*/
@@ -5791,43 +5854,21 @@
 		if (DEBUG_FLAGS & (DEBUG_RESULT|DEBUG_TINY)) {
 			u_char * p = (u_char*) & cmd->sense_buffer;
 			int i;
-			printf ("\n%s: sense data:", ncr_name (np));
-			for (i=0; i<14; i++) printf (" %x", *p++);
-			printf (".\n");
+			PRINT_ADDR(cmd);
+			printk ("sense data:");
+			for (i=0; i<14; i++) printk (" %x", *p++);
+			printk (".\n");
 		}
 
 	} else if ((cp->host_status == HS_COMPLETE)
 		&& (cp->scsi_status == S_BUSY ||
-		    cp->scsi_status == S_CONFLICT)) {
+		    cp->scsi_status == S_QUEUE_FULL)) {
 
 		/*
 		**   Target is busy.
 		*/
 		cmd->result = ScsiResult(DID_OK, cp->scsi_status);
 
-	} else if ((cp->host_status == HS_COMPLETE)
-		&& (cp->scsi_status == S_QUEUE_FULL)) {
-
-		/*
-		**   Target is stuffed.
-		*/
-		cmd->result = ScsiResult(DID_OK, cp->scsi_status);
-
-		/*
-		**  Suspend tagged queuing and start good status counter.
-		**  Announce changes to the generic driver.
-		*/
-		if (tp->numtags) {
-			PRINT_ADDR(cmd);
-			printf("QUEUE FULL! suspending tagged command queueing\n");
-			tp->numtags	= 0;
-			tp->num_good	= 0;
-			if (lp) {
-				ncr_settags (tp, lp);
-				if (lp->reqlink != lp->actlink)
-					ncr_opennings (np, lp, cmd);
-			};
-		}
 	} else if ((cp->host_status == HS_SEL_TIMEOUT)
 		|| (cp->host_status == HS_TIMEOUT)) {
 
@@ -5856,7 +5897,7 @@
 		**  Other protocol messes
 		*/
 		PRINT_ADDR(cmd);
-		printf ("COMMAND FAILED (%x %x) @%p.\n",
+		printk ("COMMAND FAILED (%x %x) @%p.\n",
 			cp->host_status, cp->scsi_status, cp);
 
 		cmd->result = ScsiResult(DID_ERROR, cp->scsi_status);
@@ -5870,43 +5911,51 @@
 		u_char * p;
 		int i;
 		PRINT_ADDR(cmd);
-		printf (" CMD:");
+		printk (" CMD:");
 		p = (u_char*) &cmd->cmnd[0];
-		for (i=0; i<cmd->cmd_len; i++) printf (" %x", *p++);
+		for (i=0; i<cmd->cmd_len; i++) printk (" %x", *p++);
 
 		if (cp->host_status==HS_COMPLETE) {
 			switch (cp->scsi_status) {
 			case S_GOOD:
-				printf ("  GOOD");
+				printk ("  GOOD");
 				break;
 			case S_CHECK_COND:
-				printf ("  SENSE:");
+				printk ("  SENSE:");
 				p = (u_char*) &cmd->sense_buffer;
 				for (i=0; i<14; i++)
-					printf (" %x", *p++);
+					printk (" %x", *p++);
 				break;
 			default:
-				printf ("  STAT: %x\n", cp->scsi_status);
+				printk ("  STAT: %x\n", cp->scsi_status);
 				break;
 			}
-		} else printf ("  HOSTERROR: %x", cp->host_status);
-		printf ("\n");
+		} else printk ("  HOSTERROR: %x", cp->host_status);
+		printk ("\n");
 	}
 
 	/*
 	**	Free this ccb
 	*/
-	ncr_free_ccb (np, cp, cmd->target, cmd->lun);
+	ncr_free_ccb (np, cp);
+
+	/*
+	**	requeue awaiting scsi commands for this lun.
+	*/
+	if (lp && lp->queuedccbs < lp->queuedepth &&
+	    !xpt_que_empty(&lp->wait_ccbq))
+		ncr_start_next_ccb(np, lp, 2);
 
 	/*
-	**	requeue awaiting scsi commands
+	**	requeue awaiting scsi commands for this controller.
 	*/
-	if (np->waiting_list) requeue_waiting_list(np);
+	if (np->waiting_list)
+		requeue_waiting_list(np);
 
 	/*
 	**	signal completion to generic driver.
 	*/
-	cmd->scsi_done (cmd);
+	ncr_queue_done_cmd(np, cmd);
 }
 
 /*==========================================================
@@ -5918,41 +5967,92 @@
 **==========================================================
 */
 
-void ncr_wakeup (ncb_p np, u_long code)
+/*
+**	This CCB has been skipped by the NCR.
+**	Queue it in the correponding unit queue.
+*/
+void ncr_ccb_skipped(ncb_p np, ccb_p cp)
 {
-	/*
-	**	Starting at the default ccb and following
-	**	the links, complete all jobs with a
-	**	host_status greater than "disconnect".
-	**
-	**	If the "code" parameter is not zero,
-	**	complete all jobs that are not IDLE.
-	*/
+	tcb_p tp = &np->target[cp->target];
+	lcb_p lp = tp->lp[cp->lun];
 
-	ccb_p cp = np->ccb;
-	while (cp) {
-		switch (cp->host_status) {
+	if (lp && cp != np->ccb) {
+		cp->host_status &= ~HS_SKIPMASK;
+		cp->start.schedule.l_paddr = 
+			cpu_to_scr(NCB_SCRIPT_PHYS (np, select));
+		xpt_remque(&cp->link_ccbq);
+		xpt_insque_tail(&cp->link_ccbq, &lp->skip_ccbq);
+		if (cp->queued) {
+			--lp->queuedccbs;
+		}
+	}
+	if (cp->queued) {
+		--np->queuedccbs;
+		cp->queued = 0;
+	}
+}
+
+/*
+**	The NCR has completed CCBs.
+**	Look at the DONE QUEUE if enabled, otherwise scan all CCBs
+*/
+void ncr_wakeup_done (ncb_p np)
+{
+	ccb_p cp;
+#ifdef SCSI_NCR_CCB_DONE_SUPPORT
+	int i, j;
 
-		case HS_IDLE:
+	i = np->ccb_done_ic;
+	while (1) {
+		j = i+1;
+		if (j >= MAX_DONE)
+			j = 0;
+
+		cp = np->ccb_done[j];
+		if (!CCB_DONE_VALID(cp))
 			break;
 
-		case HS_DISCONNECT:
-			if(DEBUG_FLAGS & DEBUG_TINY) printf ("D");
-			/* fall through */
-
-		case HS_BUSY:
-		case HS_NEGOTIATE:
-			if (!code) break;
-			cp->host_status = code;
+		np->ccb_done[j] = (ccb_p) CCB_DONE_EMPTY;
+		np->scripth->done_queue[5*j + 4] =
+				cpu_to_scr(NCB_SCRIPT_PHYS (np, done_plug));
+		MEMORY_BARRIER();
+		np->scripth->done_queue[5*i + 4] =
+				cpu_to_scr(NCB_SCRIPT_PHYS (np, done_end));
 
-			/* fall through */
+		if (cp->host_status & HS_DONEMASK)
+			ncr_complete (np, cp);
+		else if (cp->host_status & HS_SKIPMASK)
+			ncr_ccb_skipped (np, cp);
 
-		default:
+		i = j;
+	}
+	np->ccb_done_ic = i;
+#else
+	cp = np->ccb;
+	while (cp) {
+		if (cp->host_status & HS_DONEMASK)
 			ncr_complete (np, cp);
-			break;
-		};
-		cp = cp -> link_ccb;
-	};
+		else if (cp->host_status & HS_SKIPMASK)
+			ncr_ccb_skipped (np, cp);
+		cp = cp->link_ccb;
+	}
+#endif
+}
+
+/*
+**	Complete all active CCBs.
+*/
+void ncr_wakeup (ncb_p np, u_long code)
+{
+	ccb_p cp = np->ccb;
+
+	while (cp) {
+		if (cp->host_status != HS_IDLE) {
+			cp->host_status = code;
+			ncr_complete (np, cp);
+		}
+		cp = cp->link_ccb;
+	}
 }
 
 /*==========================================================
@@ -5966,38 +6066,57 @@
 
 void ncr_init (ncb_p np, int reset, char * msg, u_long code)
 {
-	int	i;
+ 	int	i;
 
-	/*
+ 	/*
 	**	Reset chip if asked, otherwise just clear fifos.
-	*/
+ 	*/
+
 	if (reset) {
 		OUTB (nc_istat,  SRST);
-		DELAY (10000);
+		UDELAY (100);
 	}
 	else {
 		OUTB (nc_stest3, TE|CSF);
 		OUTONB (nc_ctest3, CLF);
 	}
-
+ 
 	/*
 	**	Message.
 	*/
 
-	if (msg) printf (KERN_INFO "%s: restart (%s).\n", ncr_name (np), msg);
+	if (msg) printk (KERN_INFO "%s: restart (%s).\n", ncr_name (np), msg);
 
 	/*
 	**	Clear Start Queue
 	*/
-	for (i=0;i<MAX_START;i++)
-		np -> squeue [i] = cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
+	np->queuedepth = MAX_START - 1;	/* 1 entry needed as end marker */
+	for (i = 1; i < MAX_START + MAX_START; i += 2)
+		np->scripth0->tryloop[i] =
+				cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
 
 	/*
 	**	Start at first entry.
 	*/
 	np->squeueput = 0;
 	np->script0->startpos[0] = cpu_to_scr(NCB_SCRIPTH_PHYS (np, tryloop));
-	np->script0->start0  [0] = cpu_to_scr(SCR_INT ^ IFFALSE (0));
+
+	/*
+	**	Clear Done Queue
+	*/
+	for (i = 0; i < MAX_DONE; i++) {
+		np->ccb_done[i] = (ccb_p) CCB_DONE_EMPTY;
+		np->scripth0->done_queue[5*i + 4] =
+			cpu_to_scr(NCB_SCRIPT_PHYS (np, done_end));
+	}
+
+	/*
+	**	Start at first entry.
+	*/
+	np->script0->done_pos[0] = cpu_to_scr(NCB_SCRIPTH_PHYS (np,done_queue));
+	np->ccb_done_ic = MAX_DONE-1;
+	np->scripth0->done_queue[5*(MAX_DONE-1) + 4] =
+			cpu_to_scr(NCB_SCRIPT_PHYS (np, done_plug));
 
 	/*
 	**	Wakeup all pending jobs.
@@ -6009,6 +6128,8 @@
 	*/
 
 	OUTB (nc_istat,  0x00   );	/*  Remove Reset, abort */
+	UDELAY (2000);	/* The 895 needs time for the bus mode to settle */
+
 	OUTB (nc_scntl0, np->rv_scntl0 | 0xc0);
 					/*  full arb., ena parity, par->ATN  */
 	OUTB (nc_scntl1, 0x00);		/*  odd parity, and remove CRST!! */
@@ -6027,7 +6148,7 @@
 
 	OUTB (nc_stest2, EXT|np->rv_stest2);	/* Extended Sreq/Sack filtering */
 	OUTB (nc_stest3, TE);			/* TolerANT enable */
-	OUTB (nc_stime0, 0x0d	);		/* HTH disabled  STO 0.4 sec. */
+	OUTB (nc_stime0, 0x0c	);		/* HTH disabled  STO 0.25 sec */
 
 	/*
 	**	Disable disconnects.
@@ -6044,23 +6165,10 @@
 	}
 
 	/*
-	**    Upload the script into on-board RAM
-	*/
-	if (np->vaddr2) {
-		if (bootverbose)
-			printf ("%s: copying script fragments into the on-board RAM ...\n", ncr_name(np));
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0)
-		memcpy_toio(np->script, np->script0, sizeof(struct script));
-#else
-		memcpy(np->script, np->script0, sizeof(struct script));
-#endif
-	}
-
-	/*
 	**      enable ints
 	*/
 
-	OUTW (nc_sien , STO|HTH|MA|SGE|UDC|RST);
+	OUTW (nc_sien , STO|HTH|MA|SGE|UDC|RST|PAR);
 	OUTB (nc_dien , MDPE|BF|ABRT|SSI|SIR|IID);
 
 	/*
@@ -6072,6 +6180,18 @@
 	}
 
 	/*
+	**	DEL 441 - 53C876 Rev 5 - Part Number 609-0392787/2788 - ITEM 2.
+	**	Disable overlapped arbitration.
+	**	The 896 Rev 1 is also affected by this errata.
+	*/
+	if (np->device_id == PCI_DEVICE_ID_NCR_53C875 &&
+	    np->revision_id >= 0x10 && np->revision_id <= 0x15)
+		OUTB (nc_ctest0, (1<<5));
+	else if (np->device_id == PCI_DEVICE_ID_NCR_53C896 &&
+	         np->revision_id <= 0x1)
+		OUTB (nc_ccntl0, DPR);
+
+	/*
 	**	Fill in target structure.
 	**	Reinitialize usrsync.
 	**	Reinitialize usrwide.
@@ -6103,8 +6223,16 @@
 	/*
 	**    Start script processor.
 	*/
-
-	OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, start));
+	MEMORY_BARRIER();
+	if (np->paddr2) {
+		if (bootverbose)
+			printk ("%s: Downloading SCSI SCRIPTS.\n",
+				ncr_name(np));
+		OUTL (nc_scratcha, vtophys(np->script0));
+		OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, start_ram));
+	}
+	else
+		OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, start));
 }
 
 /*==========================================================
@@ -6132,14 +6260,6 @@
 	}
 
 	/*
-	**	if not scsi 2
-	**	don't believe FAST!
-	*/
-
-	if ((minsync < 50) && (tp->inqdata[2] & 0x0f) < 2)
-		minsync=50;
-
-	/*
 	**	our limit ..
 	*/
 
@@ -6215,7 +6335,7 @@
 	/*
 	**	Why not to try the immediate lower divisor and to choose 
 	**	the one that allows the fastest output speed ?
-	**	We don't want input speed too much greater than output speed.
+	**	We dont want input speed too much greater than output speed.
 	*/
 	if (div >= 1 && fak < 8) {
 		u_long fak2, per2;
@@ -6266,8 +6386,12 @@
 	for (cp = np->ccb; cp; cp = cp->link_ccb) {
 		if (!cp->cmd) continue;
 		if (cp->cmd->target != target) continue;
+#if 0
 		cp->sync_status = tp->sval;
 		cp->wide_status = tp->wval;
+#endif
+		cp->phys.select.sel_scntl3 = tp->wval;
+		cp->phys.select.sel_sxfer  = tp->sval;
 	};
 }
 
@@ -6282,15 +6406,15 @@
 {
 	Scsi_Cmnd *cmd;
 	tcb_p tp;
-	u_char target = INB (nc_ctest0) & 0x0f;
+	u_char target = INB (nc_sdid) & 0x0f;
 	u_char idiv;
 
-	assert (cp);
+	assert (cp && cp->cmd);
 	if (!cp) return;
 
 	cmd = cp->cmd;
-	assert (cmd);
 	if (!cmd) return;
+
 	assert (target == (cmd->target & 0xf));
 
 	tp = &np->target[target];
@@ -6320,7 +6444,7 @@
 	/*
 	**	Bells and whistles   ;-)
 	*/
-	PRINT_ADDR(cmd);
+	PRINT_TARGET(np, target);
 	if (sxfer & 0x01f) {
 		unsigned f10 = 100000 << (tp->widedone ? tp->widedone -1 : 0);
 		unsigned mb10 = (f10 + tp->period/2) / tp->period;
@@ -6339,11 +6463,11 @@
 		else if	(tp->period < 2000)	scsi = "FAST-10";
 		else				scsi = "FAST-5";
 
-		printf ("%s %sSCSI %d.%d MB/s (%d ns, offset %d)\n", scsi,
+		printk ("%s %sSCSI %d.%d MB/s (%d ns, offset %d)\n", scsi,
 			tp->widedone > 1 ? "WIDE " : "",
 			mb10 / 10, mb10 % 10, tp->period / 10, sxfer & 0x1f);
 	} else
-		printf ("%sasynchronous.\n", tp->widedone > 1 ? "wide " : "");
+		printk ("%sasynchronous.\n", tp->widedone > 1 ? "wide " : "");
 
 	/*
 	**	set actual value and sync_status
@@ -6365,17 +6489,17 @@
 static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide, u_char ack)
 {
 	Scsi_Cmnd *cmd;
-	u_short target = INB (nc_ctest0) & 0x0f;
+	u_short target = INB (nc_sdid) & 0x0f;
 	tcb_p tp;
 	u_char	scntl3;
 	u_char	sxfer;
 
-	assert (cp);
+	assert (cp && cp->cmd);
 	if (!cp) return;
 
 	cmd = cp->cmd;
-	assert (cmd);
 	if (!cmd) return;
+
 	assert (target == (cmd->target & 0xf));
 
 	tp = &np->target[target];
@@ -6395,11 +6519,11 @@
 	**	Bells and whistles   ;-)
 	*/
 	if (bootverbose >= 2) {
-		PRINT_ADDR(cmd);
+		PRINT_TARGET(np, target);
 		if (scntl3 & EWS)
-			printf ("WIDE SCSI (16 bit) enabled.\n");
+			printk ("WIDE SCSI (16 bit) enabled.\n");
 		else
-			printf ("WIDE SCSI disabled.\n");
+			printk ("WIDE SCSI disabled.\n");
 	}
 
 	/*
@@ -6416,73 +6540,91 @@
 **==========================================================
 */
 
-static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long numtags)
+static void ncr_setup_tags (ncb_p np, u_char tn, u_char ln)
 {
-	int l;
-	if (numtags > tp->usrtags)
-		numtags = tp->usrtags;
-	tp->numtags = numtags;
-	tp->maxtags = numtags;
-
-	for (l=0; l<MAX_LUN; l++) {
-		lcb_p lp;
-		u_char wastags;
+	tcb_p tp = &np->target[tn];
+	lcb_p lp = tp->lp[ln];
+	u_char   reqtags, maxdepth;
 
-		if (!tp) break;
-		lp=tp->lp[l];
-		if (!lp) continue;
-
-		wastags = lp->usetags;
-		ncr_settags (tp, lp);
-
-		if (numtags > 1 && lp->reqccbs > 1) {
-			PRINT_LUN(np, tp - np->target, l);
-			printf("using tagged command queueing, up to %ld cmds/lun\n", numtags);
-		}
-		else if (numtags <= 1 && wastags) {
-			PRINT_LUN(np, tp - np->target, l);
-			printf("disabling tagged command queueing\n");
-		}
-	};
-}
+	/*
+	**	Just in case ...
+	*/
+	if ((!tp) || (!lp))
+		return;
 
-static void ncr_settags (tcb_p tp, lcb_p lp)
-{
-	u_char reqtags, tmp;
+	/*
+	**	If SCSI device queue depth is not yet set, leave here.
+	*/
+	if (!lp->scdev_depth)
+		return;
 
-	if ((!tp) || (!lp)) return;
+	/*
+	**	Donnot allow more tags than the SCSI driver can queue 
+	**	for this device.
+	**	Donnot allow more tags than we can handle.
+	*/
+	maxdepth = lp->scdev_depth;
+	if (maxdepth > lp->maxnxs)	maxdepth    = lp->maxnxs;
+	if (lp->maxtags > maxdepth)	lp->maxtags = maxdepth;
+	if (lp->numtags > maxdepth)	lp->numtags = maxdepth;
 
 	/*
 	**	only devices conformant to ANSI Version >= 2
-	**	only devices capable of tagges commands
-	**	only disk devices
+	**	only devices capable of tagged commands
 	**	only if enabled by user ..
 	*/
-	if ((  tp->inqdata[2] & 0x7) >= 2 &&
-	    (  tp->inqdata[7] & INQ7_QUEUE) && ((tp->inqdata[0] & 0x1f)==0x00)
-		&& tp->numtags > 1) {
-		reqtags = tp->numtags;
-		if (lp->actlink <= 1)
-			lp->usetags=reqtags;
-	} else {
-		reqtags = 1;
-		if (lp->actlink <= 1)
-			lp->usetags=0;
-	};
+	if ((lp->inq_byte7 & INQ7_QUEUE) && lp->numtags > 1) {
+		reqtags = lp->numtags;
+	} else {
+		reqtags = 1;
+	};
+
+	/*
+	**	Update max number of tags
+	*/
+	lp->numtags = reqtags;
+	if (lp->numtags > lp->maxtags)
+		lp->maxtags = lp->numtags;
+
+	/*
+	**	If we want to switch tag mode, we must wait 
+	**	for no CCB to be active.
+	*/
+	if	(reqtags > 1 && lp->usetags) {	 /* Stay in tagged mode    */
+		if (lp->queuedepth == reqtags)	 /* Already announced	   */
+			return;
+		lp->queuedepth	= reqtags;
+	}
+	else if	(reqtags <= 1 && !lp->usetags) { /* Stay in untagged mode  */
+		lp->queuedepth	= reqtags;
+		return;
+	}
+	else {					 /* Want to switch tag mode */
+		if (lp->busyccbs)		 /* If not yet safe, return */
+			return;
+		lp->queuedepth	= reqtags;
+		lp->usetags	= reqtags > 1 ? 1 : 0;
+	}
 
 	/*
-	**	don't announce more than available.
+	**	Patch the lun mini-script, according to tag mode.
 	*/
-	tmp = lp->actccbs;
-	if (tmp > reqtags) tmp = reqtags;
-	lp->reqlink = tmp;
+	lp->jump_tag.l_paddr = lp->usetags?
+			cpu_to_scr(NCB_SCRIPT_PHYS(np, resel_tag)) :
+			cpu_to_scr(NCB_SCRIPT_PHYS(np, resel_notag));
 
 	/*
-	**	don't discard if announced.
+	**	Announce change to user.
 	*/
-	tmp = lp->actlink;
-	if (tmp < reqtags) tmp = reqtags;
-	lp->reqccbs = tmp;
+	if (bootverbose) {
+		PRINT_LUN(np, tn, ln);
+		if (lp->usetags) {
+			printk("tagged command queue depth set to %d\n", reqtags);
+		}
+		else {
+			printk("tagged command queueing disabled\n");
+		}
+	}
 }
 
 /*----------------------------------------------------
@@ -6513,13 +6655,18 @@
 		break;
 
 	case UC_SETTAGS:
-		if (np->user.data > SCSI_NCR_MAX_TAGS)
-			np->user.data = SCSI_NCR_MAX_TAGS;
 		for (t=0; t<MAX_TARGET; t++) {
+			int ln;
 			if (!((np->user.target>>t)&1)) continue;
 			np->target[t].usrtags = np->user.data;
-			ncr_setmaxtags (np, &np->target[t], np->user.data);
-		};
+			for (ln = 0; ln < MAX_LUN; ln++) {
+				lcb_p lp = np->target[t].lp[ln];
+				if (!lp)
+					continue;
+				lp->maxtags = lp->numtags = np->user.data;
+				ncr_setup_tags (np, t, ln);
+			}
+ 		};
 		break;
 
 	case UC_SETDEBUG:
@@ -6532,6 +6679,10 @@
 		np->order = np->user.data;
 		break;
 
+	case UC_SETVERBOSE:
+		np->verbose = np->user.data;
+		break;
+
 	case UC_SETWIDE:
 		for (t=0; t<MAX_TARGET; t++) {
 			u_long size;
@@ -6552,110 +6703,16 @@
 		};
 		break;
 
+#ifdef SCSI_NCR_PROFILE_SUPPORT
 	case UC_CLEARPROF:
 		bzero(&np->profile, sizeof(np->profile));
 		break;
-#ifdef	UC_DEBUG_ERROR_RECOVERY
-	case UC_DEBUG_ERROR_RECOVERY:
-		np->debug_error_recovery = np->user.data;
-		break;
 #endif
 	}
 	np->user.cmd=0;
 }
 #endif
 
-
-/*=====================================================================
-**
-**    Embedded error recovery debugging code.
-**
-**=====================================================================
-**
-**    This code is conditionned by SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT.
-**    It only can be enabled after boot-up with a control command.
-**
-**    Every 30 seconds the timer handler of the driver decides to 
-**    change the behaviour of the driver in order to trigger errors.
-**
-**    If last command was "debug_error_recovery sge", the driver 
-**    sets sync offset of all targets that use sync transfers to 2, 
-**    and so hopes a SCSI gross error at the next read operation.
-**
-**    If last command was "debug_error_recovery abort", the driver 
-**    does not signal new scsi commands to the script processor, until 
-**    it is asked to abort or reset a command by the mid-level driver.
-**
-**    If last command was "debug_error_recovery reset", the driver 
-**    does not signal new scsi commands to the script processor, until 
-**    it is asked to reset a command by the mid-level driver.
-**
-**    If last command was "debug_error_recovery parity", the driver 
-**    will assert ATN on the next DATA IN phase mismatch, and so will 
-**    behave as if a parity error had been detected.
-**
-**    The command "debug_error_recovery none" makes the driver behave 
-**    normaly.
-**
-**=====================================================================
-*/
-
-#ifdef	SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
-static void ncr_trigger_errors (ncb_p np)
-{
-	/*
-	** 	If np->debug_error_recovery is not zero, we want to 
-	**	simulate common errors in order to test error recovery.
-	*/
-	do {
-		static u_long last = 0l;
-
-		if (!np->debug_error_recovery)
-			break;
-		if (!last)
-			last = jiffies;
-		else if (jiffies < last + 30*HZ)
-			break;
-		last = jiffies;
-		/*
-		 * This one triggers SCSI gross errors.
-		 */
-		if (np->debug_error_recovery == 1) {
-			int i;
-			printf("%s: testing error recovery from SCSI gross error...\n", ncr_name(np));
-			for (i = 0 ; i < MAX_TARGET ; i++) {
-				if (np->target[i].sval & 0x1f) {
-					np->target[i].sval &= ~0x1f;
-					np->target[i].sval += 2;
-				}
-			}
-		}
-		/*
-		 * This one triggers abort from the mid-level driver.
-		 */
-		else if (np->debug_error_recovery == 2) {
-			printf("%s: testing error recovery from mid-level driver abort()...\n", ncr_name(np));
-			np->stalling = 2;
-		}
-		/*
-		 * This one triggers reset from the mid-level driver.
-		 */
-		else if (np->debug_error_recovery == 3) {
-			printf("%s: testing error recovery from mid-level driver reset()...\n", ncr_name(np));
-			np->stalling = 3;
-		}
-		/*
-		 * This one set ATN on phase mismatch in DATA IN phase and so 
-		 * will behave as on scsi parity error detected.
-		 */
-		else if (np->debug_error_recovery == 4) {
-			printf("%s: testing data in parity error...\n", ncr_name(np));
-			np->assert_atn = 1;
-		}
-	} while (0);
-}
-#endif
-
 /*==========================================================
 **
 **
@@ -6673,9 +6730,6 @@
 static void ncr_timeout (ncb_p np)
 {
 	u_long	thistime = jiffies;
-	u_long	count = 0;
-	ccb_p cp;
-	u_long flags;
 
 	/*
 	**	If release process in progress, let's go
@@ -6688,18 +6742,9 @@
 		return;
 	}
 
-	np->timer.expires =
-#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
-			jiffies +
-#endif
-			SCSI_NCR_TIMER_INTERVAL;
-
+	np->timer.expires = jiffies + SCSI_NCR_TIMER_INTERVAL;
 	add_timer(&np->timer);
 
-#ifdef	SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
-	ncr_trigger_errors (np);
-#endif
-
 	/*
 	**	If we are resetting the ncr, wait for settle_time before 
 	**	clearing it. Then command processing will be resumed.
@@ -6707,12 +6752,10 @@
 	if (np->settle_time) {
 		if (np->settle_time <= thistime) {
 			if (bootverbose > 1)
-				printf("%s: command processing resumed\n", ncr_name(np));
-			save_flags(flags); cli();
+				printk("%s: command processing resumed\n", ncr_name(np));
 			np->settle_time	= 0;
 			np->disc	= 1;
 			requeue_waiting_list(np);
-			restore_flags(flags);
 		}
 		return;
 	}
@@ -6722,99 +6765,20 @@
 	**	to perform abort of a command, we must look at ccbs about 
 	**	every 0.25 second.
 	*/
-	if (np->lasttime + (HZ>>2) <= thistime) {
+	if (np->lasttime + 4*HZ < thistime) {
 		/*
 		**	block ncr interrupts
 		*/
-		save_flags(flags); cli();
-
 		np->lasttime = thistime;
 
+#ifdef SCSI_NCR_PROFILE_SUPPORT
 		/*
 		**	Reset profile data to avoid ugly overflow
 		**	(Limited to 1024 GB for 32 bit architecture)
 		*/
 		if (np->profile.num_kbytes > (~0UL >> 2))
 			bzero(&np->profile, sizeof(np->profile));
-
-		/*----------------------------------------------------
-		**
-		**	handle ncr chip timeouts
-		**
-		**	Assumption:
-		**	We have a chance to arbitrate for the
-		**	SCSI bus at least every 10 seconds.
-		**
-		**----------------------------------------------------
-		*/
-#if 0
-		if (thistime < np->heartbeat + HZ + HZ)
-			np->latetime = 0;
-		else
-			np->latetime++;
 #endif
-
-		/*----------------------------------------------------
-		**
-		**	handle ccb timeouts
-		**
-		**----------------------------------------------------
-		*/
-
-		for (cp=np->ccb; cp; cp=cp->link_ccb) {
-			/*
-			**	look for timed out ccbs.
-			*/
-			if (!cp->host_status) continue;
-			count++;
-			/*
-			**	Have to force ordered tag to avoid timeouts
-			*/
-			if (cp->cmd && cp->tlimit && cp->tlimit <= 
-				thistime + NCR_TIMEOUT_INCREASE + SCSI_NCR_TIMEOUT_ALERT) {
-				lcb_p lp;
-				lp = np->target[cp->cmd->target].lp[cp->cmd->lun];
-				if (lp && !lp->force_ordered_tag) {
-					lp->force_ordered_tag = 1;
-				}
-			}
-			/*
-			**	ncr_abort_command() cannot complete canceled 
-			**	commands immediately. It sets tlimit to zero 
-			**	and ask the script to skip the scsi process if 
-			**	necessary. We have to complete this work here.
-			*/
-
-			if (cp->tlimit) continue;
-
-			switch (cp->host_status) {
-
-			case HS_BUSY:
-			case HS_NEGOTIATE:
-				/*
-				** still in start queue ?
-				*/
-				if (cp->phys.header.launch.l_paddr ==
-					cpu_to_scr(NCB_SCRIPT_PHYS (np, skip)))
-					continue;
-
-				/* fall through */
-			case HS_DISCONNECT:
-				cp->host_status=HS_ABORTED;
-			};
-			cp->tag = 0;
-
-			/*
-			**	wakeup this ccb.
-			*/
-			ncr_complete (np, cp);
-
-#ifdef	SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
-			if (!np->stalling)
-#endif
-			OUTB (nc_istat, SIGP);
-		}
-		restore_flags(flags);
 	}
 
 #ifdef SCSI_NCR_BROKEN_INTR
@@ -6823,11 +6787,9 @@
 		/*
 		**	Process pending interrupts.
 		*/
-		save_flags(flags); cli();
-		if (DEBUG_FLAGS & DEBUG_TINY) printf ("{");
+		if (DEBUG_FLAGS & DEBUG_TINY) printk ("{");
 		ncr_exception (np);
-		if (DEBUG_FLAGS & DEBUG_TINY) printf ("}");
-		restore_flags(flags);
+		if (DEBUG_FLAGS & DEBUG_TINY) printk ("}");
 	}
 #endif /* SCSI_NCR_BROKEN_INTR */
 }
@@ -6876,14 +6838,14 @@
 	if (dsp > np->p_script && dsp <= np->p_script + sizeof(struct script)) {
 		script_ofs	= dsp - np->p_script;
 		script_size	= sizeof(struct script);
-		script_base	= (u_char *) np->script;
+		script_base	= (u_char *) np->script0;
 		script_name	= "script";
 	}
 	else if (np->p_scripth < dsp && 
 		 dsp <= np->p_scripth + sizeof(struct scripth)) {
 		script_ofs	= dsp - np->p_scripth;
 		script_size	= sizeof(struct scripth);
-		script_base	= (u_char *) np->scripth;
+		script_base	= (u_char *) np->scripth0;
 		script_name	= "scripth";
 	} else {
 		script_ofs	= dsp;
@@ -6892,22 +6854,22 @@
 		script_name	= "mem";
 	}
 
-	printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ (%s %x:%08x).\n",
-		ncr_name (np), (unsigned)INB (nc_ctest0)&0x0f, dstat, sist,
+	printk ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ (%s %x:%08x).\n",
+		ncr_name (np), (unsigned)INB (nc_sdid)&0x0f, dstat, sist,
 		(unsigned)INB (nc_socl), (unsigned)INB (nc_sbcl), (unsigned)INB (nc_sbdl),
 		(unsigned)INB (nc_sxfer),(unsigned)INB (nc_scntl3), script_name, script_ofs,
 		(unsigned)INL (nc_dbc));
 
 	if (((script_ofs & 3) == 0) &&
 	    (unsigned)script_ofs < script_size) {
-		printf ("%s: script cmd = %08x\n", ncr_name(np),
-			(int) *(ncrcmd *)(script_base + script_ofs));
+		printk ("%s: script cmd = %08x\n", ncr_name(np),
+			scr_to_cpu((int) *(ncrcmd *)(script_base + script_ofs)));
 	}
 
-        printf ("%s: regdump:", ncr_name(np));
+        printk ("%s: regdump:", ncr_name(np));
         for (i=0; i<16;i++)
-            printf (" %02x", (unsigned)INB_OFF(i));
-        printf (".\n");
+            printk (" %02x", (unsigned)INB_OFF(i));
+        printk (".\n");
 }
 
 /*============================================================
@@ -6953,23 +6915,25 @@
 	**	Since the global header may be copied back to a CCB 
 	**	using a posted PCI memory write, the last operation on 
 	**	the istat register is a READ in order to flush posted 
-	**	PCI commands (Btw, the 'do' loop is probably useless).
+	**	PCI write commands.
 	*/
 	istat = INB (nc_istat);
 	if (istat & INTF) {
-		do {
-			OUTB (nc_istat, (istat & SIGP) | INTF);
-			istat = INB (nc_istat);
-		} while (istat & INTF);
-		if (DEBUG_FLAGS & DEBUG_TINY) printf ("F ");
+		OUTB (nc_istat, (istat & SIGP) | INTF);
+		istat = INB (nc_istat);
+		if (DEBUG_FLAGS & DEBUG_TINY) printk ("F ");
+#ifdef SCSI_NCR_PROFILE_SUPPORT
 		np->profile.num_fly++;
-		ncr_wakeup (np, 0);
+#endif
+		ncr_wakeup_done (np);
 	};
 
 	if (!(istat & (SIP|DIP)))
 		return;
 
+#ifdef SCSI_NCR_PROFILE_SUPPORT
 	np->profile.num_int++;
+#endif
 
 	if (istat & CABRT)
 		OUTB (nc_istat, CABRT);
@@ -6983,7 +6947,7 @@
 	dstat = (istat & DIP) ? INB (nc_dstat) : 0;
 
 	if (DEBUG_FLAGS & DEBUG_TINY)
-		printf ("<%d|%x:%x|%x:%x>",
+		printk ("<%d|%x:%x|%x:%x>",
 			(int)INB(nc_scr0),
 			dstat,sist,
 			(unsigned)INL(nc_dsp),
@@ -7020,12 +6984,11 @@
 		**  DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 2.
 		*/
 		if (!(sist & (SBMC|PAR)) && !(dstat & SSI)) {
-			printf(	"%s: unknown interrupt(s) ignored, "
+			printk(	"%s: unknown interrupt(s) ignored, "
 				"ISTAT=%x DSTAT=%x SIST=%x\n",
 				ncr_name(np), istat, dstat, sist);
 			return;
 		}
-
 		OUTONB (nc_dcntl, (STD|NOCOM));
 		return;
 	};
@@ -7083,29 +7046,26 @@
 
 	ncr_log_hard_error(np, sist, dstat);
 
-	printf ("%s: have to clear fifos.\n", ncr_name (np));
+	printk ("%s: have to clear fifos.\n", ncr_name (np));
 	OUTB (nc_stest3, TE|CSF);
 	OUTONB (nc_ctest3, CLF);
 
 	if ((sist & (SGE)) ||
 		(dstat & (MDPE|BF|ABORT|IID))) {
-		ncr_start_reset(np, driver_setup.settle_delay);
+		ncr_start_reset(np);
 		return;
 	};
 
 	if (sist & HTH) {
-		printf ("%s: handshake timeout\n", ncr_name(np));
-		ncr_start_reset(np, driver_setup.settle_delay);
+		printk ("%s: handshake timeout\n", ncr_name(np));
+		ncr_start_reset(np);
 		return;
 	};
 
 	if (sist & UDC) {
-		printf ("%s: unexpected disconnect\n", ncr_name(np));
-		if (INB (nc_scr1) != 0xff) {
-			OUTB (nc_scr1, HS_UNEXPECTED);
-			OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup));
-		};
-		ncr_start_reset(np, driver_setup.settle_delay);
+		printk ("%s: unexpected disconnect\n", ncr_name(np));
+		OUTB (HS_PRT, HS_UNEXPECTED);
+		OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup));
 		return;
 	};
 
@@ -7114,7 +7074,7 @@
 	**	Print a message. The timeout will do the real work.
 	**=========================================================
 	*/
-	printf ("%s: unknown interrupt\n", ncr_name(np));
+	printk ("%s: unknown interrupt\n", ncr_name(np));
 }
 
 /*==========================================================
@@ -7135,9 +7095,9 @@
 
 void ncr_int_sto (ncb_p np)
 {
-	u_long dsa, scratcha, diff;
+	u_long dsa;
 	ccb_p cp;
-	if (DEBUG_FLAGS & DEBUG_TINY) printf ("T");
+	if (DEBUG_FLAGS & DEBUG_TINY) printk ("T");
 
 	/*
 	**	look for ccb and set the status.
@@ -7154,21 +7114,11 @@
 	};
 
 	/*
-	**	repair start queue
+	**	repair start queue and jump to start point.
 	*/
 
-	scratcha = INL (nc_scratcha);
-	diff = scratcha - NCB_SCRIPTH_PHYS (np, tryloop);
-
-/*	assert ((diff <= MAX_START * 20) && !(diff % 20));*/
-
-	if ((diff <= MAX_START * 20) && !(diff % 20)) {
-		np->script->startpos[0] = cpu_to_scr(scratcha);
-		OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, start));
-		return;
-	};
-	ncr_init (np, 1, "selection timeout", HS_FAIL);
-	np->disc = 1;
+	OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, sto_restart));
+	return;
 }
 
 /*==========================================================
@@ -7192,19 +7142,22 @@
 {
 	u_char scsi_mode = INB (nc_stest4) & SMODE;
 
-	printf("%s: SCSI bus mode change from %x to %x.\n",
-		ncr_name(np), np->scsi_mode, scsi_mode);
+	if (scsi_mode != np->scsi_mode) {
+		printk("%s: SCSI bus mode change from %x to %x.\n",
+			ncr_name(np), np->scsi_mode, scsi_mode);
 
-	np->scsi_mode = scsi_mode;
+		np->scsi_mode = scsi_mode;
 
-	/*
-	**	Suspend command processing for 1 second and 
-	**	reinitialize all except the chip.
-	*/
-	np->settle_time	= jiffies + HZ;
-	ncr_init (np, 0, bootverbose ? "scsi mode change" : NULL, HS_RESET);
 
-	return 1;
+		/*
+		**	Suspend command processing for 1 second and 
+		**	reinitialize all except the chip.
+		*/
+		np->settle_time	= jiffies + HZ;
+		ncr_init (np, 0, bootverbose ? "scsi mode change" : NULL, HS_RESET);
+		return 1;
+	}
+	return 0;
 }
 
 /*==========================================================
@@ -7213,16 +7166,73 @@
 **
 **==========================================================
 **
-**	SCSI parity errors are handled by the SCSI script.
-**	So, we just print some message.
 **
 **----------------------------------------------------------
 */
 
 static int ncr_int_par (ncb_p np)
 {
-	printf("%s: SCSI parity error detected\n", ncr_name(np));
-	return 0;
+	u_char	hsts	= INB (HS_PRT);
+	u_int32	dbc	= INL (nc_dbc);
+	u_char	sstat1	= INB (nc_sstat1);
+	int phase	= -1;
+	int msg		= -1;
+	u_int32 jmp;
+
+	printk("%s: SCSI parity error detected: SCR1=%d DBC=%x SSTAT1=%x\n",
+		ncr_name(np), hsts, dbc, sstat1);
+
+	/*
+	 *	Ignore the interrupt if the NCR is not connected 
+	 *	to the SCSI bus, since the right work should have  
+	 *	been done on unexpected disconnection handling.
+	 */
+	if (!(INB (nc_scntl1) & ISCON))
+		return 0;
+
+	/*
+	 *	If the nexus is not clearly identified, reset the bus.
+	 *	We will try to do better later.
+	 */
+	if (hsts & HS_INVALMASK)
+		goto reset_all;
+
+	/*
+	 *	If the SCSI parity error occurs in MSG IN phase, prepare a 
+	 *	MSG PARITY message. Otherwise, prepare a INITIATOR DETECTED 
+	 *	ERROR message and let the device decide to retry the command 
+	 *	or to terminate with check condition. If we were in MSG IN 
+	 *	phase waiting for the response of a negotiation, we will 
+	 *	get SIR_NEGO_FAILED at dispatch.
+	 */
+	if (!(dbc & 0xc0000000))
+		phase = (dbc >> 24) & 7;
+	if (phase == 7)
+		msg = M_PARITY;
+	else
+		msg = M_ID_ERROR;
+
+	/*
+	 *	If the NCR stopped on a MOVE ^ DATA_IN, we jump to a 
+	 *	script that will ignore all data in bytes until phase 
+	 *	change, since we are not sure the chip will wait the phase 
+	 *	change prior to delivering the interrupt.
+	 */
+	if (phase == 1)
+		jmp = NCB_SCRIPTH_PHYS (np, par_err_data_in);
+	else
+		jmp = NCB_SCRIPTH_PHYS (np, par_err_other);
+
+	OUTONB (nc_ctest3, CLF );	/* clear dma fifo  */
+	OUTB (nc_stest3, TE|CSF);	/* clear scsi fifo */
+
+	np->msgout[0] = msg;
+	OUTL (nc_dsp, jmp);
+	return 1;
+
+reset_all:
+	ncr_start_reset(np);
+	return 1;
 }
 
 /*==========================================================
@@ -7292,72 +7302,89 @@
 			if (ss2 & ORF1) rest++;
 		};
 
-		OUTONB (nc_ctest3, CLF );	/* clear dma fifo  */
-		OUTB (nc_stest3, TE|CSF);	/* clear scsi fifo */
-
 		if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_PHASE))
-			printf ("P%x%x RL=%d D=%d SS0=%x ", cmd&7, sbcl&7,
+			printk ("P%x%x RL=%d D=%d SS0=%x ", cmd&7, sbcl&7,
 				(unsigned) rest, (unsigned) delta, ss0);
 
 	} else	{
 		if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_PHASE))
-			printf ("P%x%x RL=%d ", cmd&7, sbcl&7, rest);
-		if ((cmd & 7) != 1) {
-			OUTONB (nc_ctest3, CLF );
-			OUTB (nc_stest3, TE|CSF);
-		}
+			printk ("P%x%x RL=%d ", cmd&7, sbcl&7, rest);
 	}
 
 	/*
-	**	locate matching cp
+	**	Clear fifos.
 	*/
-	dsa = INL (nc_dsa);
-	cp  = np->ccb;
-	while (cp && (CCB_PHYS (cp, phys) != dsa))
-		cp = cp->link_ccb;
+	OUTONB (nc_ctest3, CLF );	/* clear dma fifo  */
+	OUTB (nc_stest3, TE|CSF);	/* clear scsi fifo */
 
-	if (!cp) {
-	    printf ("%s: SCSI phase error fixup: CCB already dequeued (0x%08lx)\n", 
-		    ncr_name (np), (u_long) np->header.cp);
-	    return;
-	}
-	if (cp != np->header.cp) {
-	    printf ("%s: SCSI phase error fixup: CCB address mismatch (0x%08lx != 0x%08lx)\n", 
-		    ncr_name (np), (u_long) cp, (u_long) np->header.cp);
-/*	    return;*/
+	/*
+	**	locate matching cp.
+	**	if the interrupted phase is DATA IN or DATA OUT,
+	**	trust the global header.
+	*/
+	dsa = INL (nc_dsa);
+	if (!(cmd & 6)) {
+		cp = np->header.cp;
+		if (CCB_PHYS(cp, phys) != dsa)
+			cp = 0;
+	} else {
+		cp  = np->ccb;
+		while (cp && (CCB_PHYS (cp, phys) != dsa))
+			cp = cp->link_ccb;
 	}
 
 	/*
-	**	find the interrupted script command,
+	**	try to find the interrupted script command,
 	**	and the address at which to continue.
 	*/
-
-	if (dsp == vtophys (&cp->patch[2])) {
-		vdsp = &cp->patch[0];
-		nxtdsp = vdsp[3];
-	} else if (dsp == vtophys (&cp->patch[6])) {
-		vdsp = &cp->patch[4];
-		nxtdsp = vdsp[3];
-	} else if (dsp > np->p_script && dsp <= np->p_script + sizeof(struct script)) {
-		vdsp = (u_int32 *) ((char*)np->script - np->p_script + dsp -8);
+	vdsp	= 0;
+	nxtdsp	= 0;
+	if	(dsp >  np->p_script &&
+		 dsp <= np->p_script + sizeof(struct script)) {
+		vdsp = (u_int32 *)((char*)np->script0 + (dsp-np->p_script-8));
 		nxtdsp = dsp;
-	} else {
-		vdsp = (u_int32 *) ((char*)np->scripth - np->p_scripth + dsp -8);
+	}
+	else if	(dsp >  np->p_scripth &&
+		 dsp <= np->p_scripth + sizeof(struct scripth)) {
+		vdsp = (u_int32 *)((char*)np->scripth0 + (dsp-np->p_scripth-8));
 		nxtdsp = dsp;
-	};
+	}
+	else if (cp) {
+		if	(dsp == vtophys (&cp->patch[2])) {
+			vdsp = &cp->patch[0];
+			nxtdsp = scr_to_cpu(vdsp[3]);
+		}
+		else if (dsp == vtophys (&cp->patch[6])) {
+			vdsp = &cp->patch[4];
+			nxtdsp = scr_to_cpu(vdsp[3]);
+		}
+	}
 
 	/*
 	**	log the information
 	*/
 
 	if (DEBUG_FLAGS & DEBUG_PHASE) {
-		printf ("\nCP=%p CP2=%p DSP=%x NXT=%x VDSP=%p CMD=%x ",
+		printk ("\nCP=%p CP2=%p DSP=%x NXT=%x VDSP=%p CMD=%x ",
 			cp, np->header.cp,
 			(unsigned)dsp,
 			(unsigned)nxtdsp, vdsp, cmd);
 	};
 
 	/*
+	**	cp=0 means that the DSA does not point to a valid control 
+	**	block. This should not happen since we donnot use multi-byte 
+	**	move while we are being reselected ot after command complete.
+	**	We are not able to recover from such a phase error.
+	*/
+	if (!cp) {
+		printk ("%s: SCSI phase error fixup: "
+			"CCB already dequeued (0x%08lx)\n", 
+			ncr_name (np), (u_long) np->header.cp);
+		goto reset_all;
+	}
+
+	/*
 	**	get old startaddress and old length.
 	*/
 
@@ -7373,7 +7400,7 @@
 	};
 
 	if (DEBUG_FLAGS & DEBUG_PHASE) {
-		printf ("OCMD=%x\nTBLP=%p OLEN=%x OADR=%x\n",
+		printk ("OCMD=%x\nTBLP=%p OLEN=%x OADR=%x\n",
 			(unsigned) (scr_to_cpu(vdsp[0]) >> 24),
 			tblp,
 			(unsigned) olen,
@@ -7386,18 +7413,23 @@
 
 	if (cmd != (scr_to_cpu(vdsp[0]) >> 24)) {
 		PRINT_ADDR(cp->cmd);
-		printf ("internal error: cmd=%02x != %02x=(vdsp[0] >> 24)\n",
+		printk ("internal error: cmd=%02x != %02x=(vdsp[0] >> 24)\n",
 			(unsigned)cmd, (unsigned)scr_to_cpu(vdsp[0]) >> 24);
-		
-		return;
+
+		goto reset_all;
 	}
 
-#ifdef	SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
-	if ((cmd & 7) == 1 && np->assert_atn) {
-		np->assert_atn = 0;
-		OUTONB(nc_socl, CATN);
+	/*
+	**	cp != np->header.cp means that the header of the CCB 
+	**	currently being processed has not yet been copied to 
+	**	the global header area. That may happen if the device did 
+	**	not accept all our messages after having been selected.
+	*/
+	if (cp != np->header.cp) {
+		printk ("%s: SCSI phase error fixup: "
+			"CCB address mismatch (0x%08lx != 0x%08lx)\n", 
+			ncr_name (np), (u_long) cp, (u_long) np->header.cp);
 	}
-#endif
 
 	/*
 	**	if old phase not dataphase, leave here.
@@ -7405,52 +7437,261 @@
 
 	if (cmd & 0x06) {
 		PRINT_ADDR(cp->cmd);
-		printf ("phase change %x-%x %d@%08x resid=%d.\n",
+		printk ("phase change %x-%x %d@%08x resid=%d.\n",
 			cmd&7, sbcl&7, (unsigned)olen,
 			(unsigned)oadr, (unsigned)rest);
+		goto unexpected_phase;
+	};
+
+	/*
+	**	choose the correct patch area.
+	**	if savep points to one, choose the other.
+	*/
+
+	newcmd = cp->patch;
+	if (cp->phys.header.savep == cpu_to_scr(vtophys (newcmd))) newcmd+=4;
+
+	/*
+	**	fillin the commands
+	*/
+
+	newcmd[0] = cpu_to_scr(((cmd & 0x0f) << 24) | rest);
+	newcmd[1] = cpu_to_scr(oadr + olen - rest);
+	newcmd[2] = cpu_to_scr(SCR_JUMP);
+	newcmd[3] = cpu_to_scr(nxtdsp);
+
+	if (DEBUG_FLAGS & DEBUG_PHASE) {
+		PRINT_ADDR(cp->cmd);
+		printk ("newcmd[%d] %x %x %x %x.\n",
+			(int) (newcmd - cp->patch),
+			(unsigned)scr_to_cpu(newcmd[0]),
+			(unsigned)scr_to_cpu(newcmd[1]),
+			(unsigned)scr_to_cpu(newcmd[2]),
+			(unsigned)scr_to_cpu(newcmd[3]));
+	}
+	/*
+	**	fake the return address (to the patch).
+	**	and restart script processor at dispatcher.
+	*/
+#ifdef SCSI_NCR_PROFILE_SUPPORT
+	np->profile.num_break++;
+#endif
+	OUTL (nc_temp, vtophys (newcmd));
+	OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, dispatch));
+	return;
+
+	/*
+	**	Unexpected phase changes that occurs when the current phase 
+	**	is not a DATA IN or DATA OUT phase are due to error conditions.
+	**	Such event may only happen when the SCRIPTS is using a 
+	**	multibyte SCSI MOVE.
+	**
+	**	Phase change		Some possible cause
+	**
+	**	COMMAND  --> MSG IN	SCSI parity error detected by target.
+	**	COMMAND  --> STATUS	Bad command or refused by target.
+	**	MSG OUT  --> MSG IN     Message rejected by target.
+	**	MSG OUT  --> COMMAND    Bogus target that discards extended
+	**				negotiation messages.
+	**
+	**	The code below does not care of the new phase and so 
+	**	trusts the target. Why to annoy it ?
+	**	If the interrupted phase is COMMAND phase, we restart at
+	**	dispatcher.
+	**	If a target does not get all the messages after selection, 
+	**	the code assumes blindly that the target discards extended 
+	**	messages and clears the negotiation status.
+	**	If the target does not want all our response to negotiation,
+	**	we force a SIR_NEGO_PROTO interrupt (it is a hack that avoids 
+	**	bloat for such a should_not_happen situation).
+	**	In all other situation, we reset the BUS.
+	**	Are these assumptions reasonnable ? (Wait and see ...)
+	*/
+unexpected_phase:
+	dsp -= 8;
+	nxtdsp = 0;
+
+	switch (cmd & 7) {
+	case 2:	/* COMMAND phase */
+		nxtdsp = NCB_SCRIPT_PHYS (np, dispatch);
+		break;
+#if 0
+	case 3:	/* STATUS  phase */
+		nxtdsp = NCB_SCRIPT_PHYS (np, dispatch);
+		break;
+#endif
+	case 6:	/* MSG OUT phase */
+		np->scripth->nxtdsp_go_on[0] = cpu_to_scr(dsp + 8);
+		if	(dsp == NCB_SCRIPT_PHYS (np, send_ident)) {
+			cp->host_status = HS_BUSY;
+			nxtdsp = NCB_SCRIPTH_PHYS (np, clratn_go_on);
+		}
+		else if	(dsp == NCB_SCRIPTH_PHYS (np, send_wdtr) ||
+			 dsp == NCB_SCRIPTH_PHYS (np, send_sdtr)) {
+			nxtdsp = dsp - 8; /* Should raise SIR_NEGO_PROTO */
+		}
+		break;
+#if 0
+	case 7:	/* MSG IN  phase */
+		nxtdsp = NCB_SCRIPT_PHYS (np, clrack);
+		break;
+#endif
+	}
+
+	if (nxtdsp) {
+		OUTL (nc_dsp, nxtdsp);
+		return;
+	}
+
+reset_all:
+	ncr_start_reset(np);
+}
+
+
+static void ncr_sir_to_redo(ncb_p np, int num, ccb_p cp)
+{
+	Scsi_Cmnd *cmd	= cp->cmd;
+	tcb_p tp	= &np->target[cmd->target];
+	lcb_p lp	= tp->lp[cmd->lun];
+	XPT_QUEHEAD	*qp;
+	ccb_p		cp2;
+	int		disc_cnt = 0;
+	int		busy_cnt = 0;
+	u_int32		startp;
+	u_char		s_status = INB (SS_PRT);
+
+	/*
+	**	Let the SCRIPTS processor skip all not yet started CCBs,
+	**	and count disconnected CCBs. Since the busy queue is in 
+	**	the same order as the chip start queue, disconnected CCBs 
+	**	are before cp and busy ones after.
+	*/
+	if (lp) {
+		qp = lp->busy_ccbq.blink;
+		while (qp != &lp->busy_ccbq) {
+			cp2 = xpt_que_entry(qp, struct ccb, link_ccbq);
+			qp  = qp->blink;
+			++busy_cnt;
+			if (cp2 == cp)
+				break;
+			cp2->start.schedule.l_paddr =
+			cpu_to_scr(NCB_SCRIPTH_PHYS (np, skip));
+		}
+		lp->held_ccb = cp;	/* Requeue when this one completes */
+		disc_cnt = lp->queuedccbs - busy_cnt;
+	}
+
+	switch(s_status) {
+	default:	/* Just for safety, should never happen */
+	case S_QUEUE_FULL:
+		/*
+		**	Decrease number of tags to the number of 
+		**	disconnected commands.
+		*/
+		if (!lp)
+			goto out;
+		if (bootverbose >= 1) {
+			PRINT_ADDR(cmd);
+			printk ("QUEUE FULL! %d busy, %d disconnected CCBs\n",
+				busy_cnt, disc_cnt);
+		}
+		if (disc_cnt < lp->numtags) {
+			lp->numtags	= disc_cnt > 2 ? disc_cnt : 2;
+			lp->num_good	= 0;
+			ncr_setup_tags (np, cmd->target, cmd->lun);
+		}
+		/*
+		**	Requeue the command to the start queue.
+		**	If any disconnected commands,
+		**		Clear SIGP.
+		**		Jump to reselect.
+		*/
+		cp->phys.header.savep = cp->startp;
+		cp->host_status = HS_BUSY;
+		cp->scsi_status = S_ILLEGAL;
+
+		ncr_put_start_queue(np, cp);
+		if (disc_cnt)
+			INB (nc_ctest2);		/* Clear SIGP */
+		OUTL (nc_dsp,	NCB_SCRIPT_PHYS (np, reselect));
+		return;
+	case S_TERMINATED:
+	case S_CHECK_COND:
+		/*
+		**	If we were requesting sense, give up.
+		*/
+		if (cp->auto_sense)
+			goto out;
+
+		/*
+		**	Device returned CHECK CONDITION status.
+		**	Prepare all needed data strutures for getting 
+		**	sense data.
+		**
+		**	identify message
+		*/
+		cp->scsi_smsg2[0]	= M_IDENTIFY | cmd->lun;
+		cp->phys.smsg.addr	= cpu_to_scr(CCB_PHYS (cp, scsi_smsg2));
+		cp->phys.smsg.size	= cpu_to_scr(1);
+
+		/*
+		**	sense command
+		*/
+		cp->phys.cmd.addr	= cpu_to_scr(CCB_PHYS (cp, sensecmd));
+		cp->phys.cmd.size	= cpu_to_scr(6);
+
+		/*
+		**	patch requested size into sense command
+		*/
+		cp->sensecmd[0]		= 0x03;
+		cp->sensecmd[1]		= cmd->lun << 5;
+		cp->sensecmd[4]		= sizeof(cmd->sense_buffer);
+
+		/*
+		**	sense data
+		*/
+		cp->phys.sense.addr	=
+				cpu_to_scr(vtophys (&cmd->sense_buffer[0]));
+		cp->phys.sense.size	=
+				cpu_to_scr(sizeof(cmd->sense_buffer));
 
-		OUTONB (nc_dcntl, (STD|NOCOM));
-		return;
-	};
+		/*
+		**	requeue the command.
+		*/
+		startp = cpu_to_scr(NCB_SCRIPTH_PHYS (np, sdata_in));
 
-	/*
-	**	choose the correct patch area.
-	**	if savep points to one, choose the other.
-	*/
+		cp->phys.header.savep	= startp;
+		cp->phys.header.goalp	= startp + 24;
+		cp->phys.header.lastp	= startp;
+		cp->phys.header.wgoalp	= startp + 24;
+		cp->phys.header.wlastp	= startp;
 
-	newcmd = cp->patch;
-	if (cp->phys.header.savep == cpu_to_scr(vtophys (newcmd))) newcmd+=4;
+		cp->host_status = HS_BUSY;
+		cp->scsi_status = S_ILLEGAL;
+		cp->auto_sense	= s_status;
 
-	/*
-	**	fillin the commands
-	*/
+		cp->start.schedule.l_paddr =
+			cpu_to_scr(NCB_SCRIPT_PHYS (np, select));
 
-	newcmd[0] = cpu_to_scr(((cmd & 0x0f) << 24) | rest);
-	newcmd[1] = cpu_to_scr(oadr + olen - rest);
-	newcmd[2] = cpu_to_scr(SCR_JUMP);
-	newcmd[3] = cpu_to_scr(nxtdsp);
+		/*
+		**	Select without ATN for quirky devices.
+		*/
+		if (tp->quirks & QUIRK_NOMSG)
+			cp->start.schedule.l_paddr =
+			cpu_to_scr(NCB_SCRIPTH_PHYS (np, select_no_atn));
 
-	if (DEBUG_FLAGS & DEBUG_PHASE) {
-		PRINT_ADDR(cp->cmd);
-		printf ("newcmd[%d] %x %x %x %x.\n",
-			(int) (newcmd - cp->patch),
-			(unsigned)scr_to_cpu(newcmd[0]),
-			(unsigned)scr_to_cpu(newcmd[1]),
-			(unsigned)scr_to_cpu(newcmd[2]),
-			(unsigned)scr_to_cpu(newcmd[3]));
+		ncr_put_start_queue(np, cp);
+
+		OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, start));
+		return;
 	}
-	/*
-	**	fake the return address (to the patch).
-	**	and restart script processor at dispatcher.
-	*/
-	np->profile.num_break++;
-	OUTL (nc_temp, vtophys (newcmd));
-	if ((cmd & 7) == 0)
-		OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, dispatch));
-	else
-		OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, checkatn));
+
+out:
+	OUTONB (nc_dcntl, (STD|NOCOM));
+	return;
 }
 
+
 /*==========================================================
 **
 **
@@ -7463,159 +7704,79 @@
 static int ncr_show_msg (u_char * msg)
 {
 	u_char i;
-	printf ("%x",*msg);
+	printk ("%x",*msg);
 	if (*msg==M_EXTENDED) {
 		for (i=1;i<8;i++) {
 			if (i-1>msg[1]) break;
-			printf ("-%x",msg[i]);
+			printk ("-%x",msg[i]);
 		};
 		return (i+1);
 	} else if ((*msg & 0xf0) == 0x20) {
-		printf ("-%x",msg[1]);
+		printk ("-%x",msg[1]);
 		return (2);
 	};
 	return (1);
 }
 
+
 void ncr_int_sir (ncb_p np)
 {
 	u_char scntl3;
 	u_char chg, ofs, per, fak, wide;
 	u_char num = INB (nc_dsps);
 	ccb_p	cp=0;
-	u_long	dsa;
-	u_char	target = INB (nc_ctest0) & 0x0f;
+	u_long	dsa    = INL (nc_dsa);
+	u_char	target = INB (nc_sdid) & 0x0f;
 	tcb_p	tp     = &np->target[target];
-	int     i;
-	if (DEBUG_FLAGS & DEBUG_TINY) printf ("I#%d", num);
+
+	if (DEBUG_FLAGS & DEBUG_TINY) printk ("I#%d", num);
 
 	switch (num) {
-	case SIR_SENSE_RESTART:
-	case SIR_STALL_RESTART:
-		break;
-	case SIR_STALL_QUEUE:	/* Ignore, just restart the script */
+	case SIR_RESEL_NO_MSG_IN:
+	case SIR_RESEL_NO_IDENTIFY:
+		/*
+		**	If devices reselecting without sending an IDENTIFY 
+		**	message still exist, this should help.
+		**	We just assume lun=0, 1 CCB, no tag.
+		*/
+		if (tp->lp[0]) { 
+			OUTL (nc_dsp, scr_to_cpu(tp->lp[0]->jump_ccb[0]));
+			return;
+		}
+	case SIR_RESEL_BAD_TARGET:	/* Will send a TARGET RESET message */
+	case SIR_RESEL_BAD_LUN:		/* Will send a TARGET RESET message */
+	case SIR_RESEL_BAD_I_T_L_Q:	/* Will send an ABORT TAG message   */
+	case SIR_RESEL_BAD_I_T_L:	/* Will send an ABORT message	    */
+		printk ("%s:%d: SIR %d, "
+			"incorrect nexus identification on reselection\n",
+			ncr_name (np), target, num);
 		goto out;
-
+	case SIR_DONE_OVERFLOW:
+		printk ("%s:%d: SIR %d, "
+			"CCB done queue overflow\n",
+			ncr_name (np), target, num);
+		goto out;
+	case SIR_BAD_STATUS:
+		cp = np->header.cp;
+		if (!cp || CCB_PHYS (cp, phys) != dsa)
+			goto out;
+		ncr_sir_to_redo(np, num, cp);
+		return;
 	default:
 		/*
 		**	lookup the ccb
 		*/
-		dsa = INL (nc_dsa);
 		cp = np->ccb;
 		while (cp && (CCB_PHYS (cp, phys) != dsa))
 			cp = cp->link_ccb;
 
-		assert (cp);
-		if (!cp)
-			goto out;
-		assert (cp == np->header.cp);
-		if (cp != np->header.cp)
+		assert (cp && cp == np->header.cp);
+
+		if (!cp || cp != np->header.cp)
 			goto out;
 	}
 
 	switch (num) {
-		u_long endp;
-	case SIR_DATA_IO_IS_OUT:
-	case SIR_DATA_IO_IS_IN:
-/*
-**	We did not guess the direction of transfer. We have to wait for 
-**	actual data direction driven by the target before setting 
-**	pointers. We must patch the global header too.
-*/
-		if (num == SIR_DATA_IO_IS_OUT) {
-			endp = NCB_SCRIPTH_PHYS (np, data_out) + MAX_SCATTER*16;
-			cp->phys.header.goalp = cpu_to_scr(endp + 8);
-			cp->phys.header.savep =
-				cpu_to_scr(endp - cp->segments*16);
-		} else {
-			endp = NCB_SCRIPT_PHYS (np, data_in)  + MAX_SCATTER*16;
-			cp->phys.header.goalp = cpu_to_scr(endp + 8);
-			cp->phys.header.savep =
-				cpu_to_scr(endp - cp->segments*16);
-		}
-
-		cp->phys.header.lastp	= cp->phys.header.savep;
-		np->header.savep	= cp->phys.header.savep;
-		np->header.goalp	= cp->phys.header.goalp;
-		np->header.lastp	= cp->phys.header.lastp;
-
-		OUTL (nc_temp,	scr_to_cpu(np->header.savep));
-		OUTL (nc_dsp,	scr_to_cpu(np->header.savep));
-		return;
-		/* break; */
-
-/*--------------------------------------------------------------------
-**
-**	Processing of interrupted getcc selects
-**
-**--------------------------------------------------------------------
-*/
-
-	case SIR_SENSE_RESTART:
-		/*------------------------------------------
-		**	Script processor is idle.
-		**	Look for interrupted "check cond"
-		**------------------------------------------
-		*/
-
-		if (DEBUG_FLAGS & DEBUG_RESTART)
-			printf ("%s: int#%d",ncr_name (np),num);
-		cp = (ccb_p) 0;
-		for (i=0; i<MAX_TARGET; i++) {
-			if (DEBUG_FLAGS & DEBUG_RESTART) printf (" t%d", i);
-			tp = &np->target[i];
-			if (DEBUG_FLAGS & DEBUG_RESTART) printf ("+");
-			cp = tp->hold_cp;
-			if (!cp) continue;
-			if (DEBUG_FLAGS & DEBUG_RESTART) printf ("+");
-			if ((cp->host_status==HS_BUSY) &&
-				(cp->scsi_status==S_CHECK_COND))
-				break;
-			if (DEBUG_FLAGS & DEBUG_RESTART) printf ("- (remove)");
-			tp->hold_cp = cp = (ccb_p) 0;
-		};
-
-		if (cp) {
-			if (DEBUG_FLAGS & DEBUG_RESTART)
-				printf ("+ restart job ..\n");
-			OUTL (nc_dsa, CCB_PHYS (cp, phys));
-			OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, getcc));
-			return;
-		};
-
-		/*
-		**	no job, resume normal processing
-		*/
-		if (DEBUG_FLAGS & DEBUG_RESTART) printf (" -- remove trap\n");
-		np->script->start0[0] =  cpu_to_scr(SCR_INT ^ IFFALSE (0));
-		break;
-
-	case SIR_SENSE_FAILED:
-		/*-------------------------------------------
-		**	While trying to select for
-		**	getting the condition code,
-		**	a target reselected us.
-		**-------------------------------------------
-		*/
-		if (DEBUG_FLAGS & DEBUG_RESTART) {
-			PRINT_ADDR(cp->cmd);
-			printf ("in getcc reselect by t%d.\n",
-				(int)INB(nc_ssid) & 0x0f);
-		}
-
-		/*
-		**	Mark this job
-		*/
-		cp->host_status = HS_BUSY;
-		cp->scsi_status = S_CHECK_COND;
-		np->target[cp->cmd->target].hold_cp = cp;
-
-		/*
-		**	And patch code to restart it.
-		*/
-		np->script->start0[0] =  cpu_to_scr(SCR_INT);
-		break;
-
 /*-----------------------------------------------------------------------------
 **
 **	Was Sie schon immer ueber transfermode negotiation wissen wollten ...
@@ -7699,7 +7860,7 @@
 
 		if (DEBUG_FLAGS & DEBUG_NEGO) {
 			PRINT_ADDR(cp->cmd);
-			printf ("negotiation failed sir=%x status=%x.\n",
+			printk ("negotiation failed sir=%x status=%x.\n",
 				num, cp->nego_status);
 		};
 
@@ -7722,7 +7883,8 @@
 		np->msgout[0] = M_NOOP;
 		cp->nego_status = 0;
 		OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, dispatch));
-		break;
+		return;
+/*		break;	*/
 
 	case SIR_NEGO_SYNC:
 		/*
@@ -7731,9 +7893,9 @@
 
 		if (DEBUG_FLAGS & DEBUG_NEGO) {
 			PRINT_ADDR(cp->cmd);
-			printf ("sync msgin: ");
+			printk ("sync msgin: ");
 			(void) ncr_show_msg (np->msgin);
-			printf (".\n");
+			printk (".\n");
 		};
 
 		/*
@@ -7751,7 +7913,7 @@
 		*/
 
 		if (ofs)
-			tp->inqdata[7] |= INQ7_SYNC;
+			tp->inq_byte7 |= INQ7_SYNC;
 
 		/*
 		**	check values against driver limits.
@@ -7785,7 +7947,7 @@
 
 		if (DEBUG_FLAGS & DEBUG_NEGO) {
 			PRINT_ADDR(cp->cmd);
-			printf ("sync: per=%d scntl3=0x%x ofs=%d fak=%d chg=%d.\n",
+			printk ("sync: per=%d scntl3=0x%x ofs=%d fak=%d chg=%d.\n",
 				per, scntl3, ofs, fak, chg);
 		}
 
@@ -7819,20 +7981,6 @@
 		};
 
 		/*
-		**	It was a request.
-		**      Check against the table of target capabilities.
-		**      If target not capable force M_REJECT and asynchronous.
-		*/
-		if (np->unit < SCSI_NCR_MAX_HOST) {
-			tp->inqdata[7] &=
-				(target_capabilities[np->unit].and_map[target]);
-			if (!(tp->inqdata[7] & INQ7_SYNC)) {
-				ofs = 0;
-				fak = 7;
-			}
-		}
-
-		/*
 		**	It was a request. Set value and
 		**      prepare an answer message
 		*/
@@ -7849,9 +7997,9 @@
 
 		if (DEBUG_FLAGS & DEBUG_NEGO) {
 			PRINT_ADDR(cp->cmd);
-			printf ("sync msgout: ");
+			printk ("sync msgout: ");
 			(void) ncr_show_msg (np->msgout);
-			printf (".\n");
+			printk (".\n");
 		}
 
 		if (!ofs) {
@@ -7868,9 +8016,9 @@
 		*/
 		if (DEBUG_FLAGS & DEBUG_NEGO) {
 			PRINT_ADDR(cp->cmd);
-			printf ("wide msgin: ");
+			printk ("wide msgin: ");
 			(void) ncr_show_msg (np->msgin);
-			printf (".\n");
+			printk (".\n");
 		};
 
 		/*
@@ -7886,7 +8034,7 @@
 		*/
 
 		if (wide)
-			tp->inqdata[7] |= INQ7_WIDE16;
+			tp->inq_byte7 |= INQ7_WIDE16;
 
 		/*
 		**	check values against driver limits.
@@ -7897,7 +8045,7 @@
 
 		if (DEBUG_FLAGS & DEBUG_NEGO) {
 			PRINT_ADDR(cp->cmd);
-			printf ("wide: wide=%d chg=%d.\n", wide, chg);
+			printk ("wide: wide=%d chg=%d.\n", wide, chg);
 		}
 
 		if (INB (HS_PRT) == HS_NEGOTIATE) {
@@ -7947,9 +8095,9 @@
 
 		if (DEBUG_FLAGS & DEBUG_NEGO) {
 			PRINT_ADDR(cp->cmd);
-			printf ("wide msgout: ");
+			printk ("wide msgout: ");
 			(void) ncr_show_msg (np->msgin);
-			printf (".\n");
+			printk (".\n");
 		}
 		break;
 
@@ -7969,7 +8117,7 @@
 		*/
 
 		PRINT_ADDR(cp->cmd);
-		printf ("M_REJECT received (%x:%x).\n",
+		printk ("M_REJECT received (%x:%x).\n",
 			(unsigned)scr_to_cpu(np->lastmsg), np->msgout[0]);
 		break;
 
@@ -7982,9 +8130,9 @@
 		*/
 
 		PRINT_ADDR(cp->cmd);
-		printf ("M_REJECT sent for ");
+		printk ("M_REJECT sent for ");
 		(void) ncr_show_msg (np->msgin);
-		printf (".\n");
+		printk (".\n");
 		break;
 
 /*--------------------------------------------------------------------
@@ -8004,9 +8152,9 @@
 		*/
 
 		PRINT_ADDR(cp->cmd);
-		printf ("M_IGN_RESIDUE received, but not yet implemented.\n");
+		printk ("M_IGN_RESIDUE received, but not yet implemented.\n");
 		break;
-
+#if 0
 	case SIR_MISSING_SAVE:
 		/*-----------------------------------------------
 		**
@@ -8017,92 +8165,13 @@
 		*/
 
 		PRINT_ADDR(cp->cmd);
-		printf ("M_DISCONNECT received, but datapointer not saved: "
+		printk ("M_DISCONNECT received, but datapointer not saved: "
 			"data=%x save=%x goal=%x.\n",
 			(unsigned) INL (nc_temp),
 			(unsigned) scr_to_cpu(np->header.savep),
 			(unsigned) scr_to_cpu(np->header.goalp));
 		break;
-
-#if 0   /* This stuff does not work */
-/*--------------------------------------------------------------------
-**
-**	Processing of a "S_QUEUE_FULL" status.
-**
-**	The current command has been rejected,
-**	because there are too many in the command queue.
-**	We have started too many commands for that target.
-**
-**	If possible, reinsert at head of queue.
-**	Stall queue until there are no disconnected jobs
-**	(ncr is REALLY idle). Then restart processing.
-**
-**	We should restart the current job after the controller
-**	has become idle. But this is not yet implemented.
-**
-**--------------------------------------------------------------------
-*/
-	case SIR_STALL_QUEUE:
-		/*-----------------------------------------------
-		**
-		**	Stall the start queue.
-		**
-		**-----------------------------------------------
-		*/
-		PRINT_ADDR(cp->cmd);
-		printf ("queue full.\n");
-
-		np->script->start1[0] =  cpu_to_scr(SCR_INT);
-
-		/*
-		**	Try to disable tagged transfers.
-		*/
-		ncr_setmaxtags (np, &np->target[target], 0);
-
-		/*
-		** @QUEUE@
-		**
-		**	Should update the launch field of the
-		**	current job to be able to restart it.
-		**	Then prepend it to the start queue.
-		*/
-
-		/* fall through */
-
-	case SIR_STALL_RESTART:
-		/*-----------------------------------------------
-		**
-		**	Enable selecting again,
-		**	if NO disconnected jobs.
-		**
-		**-----------------------------------------------
-		*/
-		/*
-		**	Look for a disconnected job.
-		*/
-		cp = np->ccb;
-		while (cp && cp->host_status != HS_DISCONNECT)
-			cp = cp->link_ccb;
-
-		/*
-		**	if there is one, ...
-		*/
-		if (cp) {
-			/*
-			**	wait for reselection
-			*/
-			OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, reselect));
-			return;
-		};
-
-		/*
-		**	else remove the interrupt.
-		*/
-
-		printf ("%s: queue empty.\n", ncr_name (np));
-		np->script->start1[0] =  cpu_to_scr(SCR_INT ^ IFFALSE (0));
-		break;
-#endif   /* This stuff does not work */
+#endif
 	};
 
 out:
@@ -8118,48 +8187,71 @@
 **==========================================================
 */
 
-static	ccb_p ncr_get_ccb
-	(ncb_p np, u_long target, u_long lun)
+static	ccb_p ncr_get_ccb (ncb_p np, u_char tn, u_char ln)
 {
-	lcb_p lp;
+	tcb_p tp = &np->target[tn];
+	lcb_p lp = tp->lp[ln];
+	u_char tag = NO_TAG;
 	ccb_p cp = (ccb_p) 0;
 
 	/*
 	**	Lun structure available ?
 	*/
+	if (lp) {
+		XPT_QUEHEAD *qp;
+		/*
+		**	Keep from using more tags than we can handle.
+		*/
+		if (lp->usetags && lp->busyccbs >= lp->maxnxs)
+			return (ccb_p) 0;
 
-	lp = np->target[target].lp[lun];
-
-	if (lp && lp->opennings && (!lp->active || lp->active < lp->reqlink)) {
-
-		cp = lp->next_ccb;
+		/*
+		**	Allocate a new CCB if needed.
+		*/
+		if (xpt_que_empty(&lp->free_ccbq))
+			ncr_alloc_ccb(np, tn, ln);
 
 		/*
+		**	Tune tag mode if asked by user.
+		*/
+		if (lp->queuedepth != lp->numtags) {
+			ncr_setup_tags(np, tn, ln);
+		}
+			
+		/*
 		**	Look for free CCB
 		*/
-
-		while (cp && cp->magic) cp = cp->next_ccb;
+		qp = xpt_remque_head(&lp->free_ccbq);
+		if (qp) {
+			cp = xpt_que_entry(qp, struct ccb, link_ccbq);
+			if (cp->magic) {
+				PRINT_LUN(np, tn, ln);
+				printk ("ccb free list corrupted (@%p)\n", cp);
+				cp = 0;
+			}
+			else {
+				xpt_insque_tail(qp, &lp->wait_ccbq);
+				++lp->busyccbs;
+			}
+		}
 
 		/*
-		**	Increment active commands and decrement credit.
+		**	If a CCB is available,
+		**	Get a tag for this nexus if required.
 		*/
-
 		if (cp) {
-			++lp->active;
-			--lp->opennings;
+			if (lp->usetags)
+				tag = lp->cb_tags[lp->ia_tag];
 		}
+		else if (lp->actccbs > 0)
+			return (ccb_p) 0;
 	}
 
 	/*
 	**	if nothing available, take the default.
-	**	DANGEROUS, because this ccb is not suitable for
-	**	reselection.
-	**	If lp->actccbs > 0 wait for a suitable ccb to be free.
 	*/
-	if ((!cp) && lp && lp->actccbs > 0)
-		return ((ccb_p) 0);
-
-	if (!cp) cp = np->ccb;
+	if (!cp)
+		cp = np->ccb;
 
 	/*
 	**	Wait until available.
@@ -8176,7 +8268,32 @@
 		return ((ccb_p) 0);
 
 	cp->magic = 1;
-	return (cp);
+
+	/*
+	**	Move to next available tag if tag used.
+	*/
+	if (lp) {
+		if (tag != NO_TAG) {
+			++lp->ia_tag;
+			if (lp->ia_tag == SCSI_NCR_MAX_TAGS)
+				lp->ia_tag = 0;
+			lp->tags_umap |= (((tagmap_t) 1) << tag);
+		}
+	}
+
+	/*
+	**	Remember all informations needed to free this CCB.
+	*/
+	cp->tag	   = tag;
+	cp->target = tn;
+	cp->lun    = ln;
+
+	if (DEBUG_FLAGS & DEBUG_TAGS) {
+		PRINT_LUN(np, tn, ln);
+		printk ("ccb @%p using tag %d.\n", cp, tag);
+	}
+
+	return cp;
 }
 
 /*==========================================================
@@ -8188,252 +8305,417 @@
 **==========================================================
 */
 
-void ncr_free_ccb (ncb_p np, ccb_p cp, u_long target, u_long lun)
+static void ncr_free_ccb (ncb_p np, ccb_p cp)
 {
-	lcb_p lp;
+	tcb_p tp = &np->target[cp->target];
+	lcb_p lp = tp->lp[cp->lun];
+
+	if (DEBUG_FLAGS & DEBUG_TAGS) {
+		PRINT_LUN(np, cp->target, cp->lun);
+		printk ("ccb @%p freeing tag %d.\n", cp, cp->tag);
+	}
 
 	/*
-	**    sanity
+	**	If lun control block available,
+	**	decrement active commands and increment credit, 
+	**	free the tag if any and remove the JUMP for reselect.
 	*/
-
-	assert (cp != NULL);
+	if (lp) {
+		if (cp->tag != NO_TAG) {
+			lp->cb_tags[lp->if_tag++] = cp->tag;
+			if (lp->if_tag == SCSI_NCR_MAX_TAGS)
+				lp->if_tag = 0;
+			lp->tags_umap &= ~(((tagmap_t) 1) << cp->tag);
+			lp->tags_smap &= lp->tags_umap;
+			lp->jump_ccb[cp->tag] =
+				cpu_to_scr(NCB_SCRIPTH_PHYS(np, bad_i_t_l_q));
+		} else {
+			lp->jump_ccb[0] =
+				cpu_to_scr(NCB_SCRIPTH_PHYS(np, bad_i_t_l));
+		}
+	}
 
 	/*
-	**	Decrement active commands and increment credit.
+	**	Make this CCB available.
 	*/
 
-	lp = np->target[target].lp[lun];
 	if (lp) {
-			--lp->active;
-			++lp->opennings;
+		if (cp != np->ccb) {
+			xpt_remque(&cp->link_ccbq);
+			xpt_insque_head(&cp->link_ccbq, &lp->free_ccbq);
+		}
+		--lp->busyccbs;
+		if (cp->queued) {
+			--lp->queuedccbs;
+		}
 	}
-
 	cp -> host_status = HS_IDLE;
 	cp -> magic = 0;
+	if (cp->queued) {
+		--np->queuedccbs;
+		cp->queued = 0;
+	}
+
 #if 0
 	if (cp == np->ccb)
 		wakeup ((caddr_t) cp);
 #endif
 }
 
-/*==========================================================
-**
-**
-**      Allocation of resources for Targets/Luns/Tags.
-**
-**
-**==========================================================
-*/
 
-static	void ncr_alloc_ccb (ncb_p np, u_long target, u_long lun)
+#define ncr_reg_bus_addr(r) \
+	(bus_dvma_to_mem(np->paddr) + offsetof (struct ncr_reg, r))
+
+/*------------------------------------------------------------------------
+**	Initialize the fixed part of a CCB structure.
+**------------------------------------------------------------------------
+**------------------------------------------------------------------------
+*/
+static void ncr_init_ccb(ncb_p np, ccb_p cp)
 {
-	tcb_p tp;
-	lcb_p lp;
-	ccb_p cp;
+	ncrcmd copy_4 = np->features & FE_PFEN ? SCR_COPY(4) : SCR_COPY_F(4);
 
-	assert (np != NULL);
+	/*
+	**	Remember virtual and bus address of this ccb.
+	*/
+	cp->p_ccb 	   = vtophys(cp);
+	cp->phys.header.cp = cp;
 
-	if (target>=MAX_TARGET) return;
-	if (lun   >=MAX_LUN   ) return;
+	/*
+	**	This allows xpt_remque to work for the default ccb.
+	*/
+	xpt_que_init(&cp->link_ccbq);
 
-	tp=&np->target[target];
+	/*
+	**	Initialyze the start and restart launch script.
+	**
+	**	COPY(4) @(...p_phys), @(dsa)
+	**	JUMP @(sched_point)
+	*/
+	cp->start.setup_dsa[0]	 = cpu_to_scr(copy_4);
+	cp->start.setup_dsa[1]	 = cpu_to_scr(vtophys(&cp->start.p_phys));
+	cp->start.setup_dsa[2]	 = cpu_to_scr(ncr_reg_bus_addr(nc_dsa));
+	cp->start.schedule.l_cmd = cpu_to_scr(SCR_JUMP);
+	cp->start.p_phys	 = cpu_to_scr(vtophys(&cp->phys));
 
-	if (!tp->jump_tcb.l_cmd) {
-		/*
-		**	initialize it.
-		*/
-		tp->jump_tcb.l_cmd   =
-			cpu_to_scr((SCR_JUMP^IFFALSE (DATA (0x80 + target))));
-		tp->jump_tcb.l_paddr = np->jump_tcb.l_paddr;
+	bcopy(&cp->start, &cp->restart, sizeof(cp->restart));
 
-		tp->getscr[0] =	(np->features & FE_PFEN) ?
-			cpu_to_scr(SCR_COPY(1)):cpu_to_scr(SCR_COPY_F(1));
-		tp->getscr[1] = cpu_to_scr(vtophys (&tp->sval));
-		tp->getscr[2] =
-		cpu_to_scr(np->paddr + offsetof (struct ncr_reg, nc_sxfer));
+	cp->start.schedule.l_paddr   = cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
+	cp->restart.schedule.l_paddr = cpu_to_scr(NCB_SCRIPTH_PHYS (np, abort));
+}
 
-		tp->getscr[3] =	(np->features & FE_PFEN) ?
-			cpu_to_scr(SCR_COPY(1)):cpu_to_scr(SCR_COPY_F(1));
-		tp->getscr[4] = cpu_to_scr(vtophys (&tp->wval));
-		tp->getscr[5] =
-		cpu_to_scr(np->paddr + offsetof (struct ncr_reg, nc_scntl3));
 
-		assert (( (offsetof(struct ncr_reg, nc_sxfer) ^
-			offsetof(struct tcb    , sval    )) &3) == 0);
-		assert (( (offsetof(struct ncr_reg, nc_scntl3) ^
-			offsetof(struct tcb    , wval    )) &3) == 0);
+/*------------------------------------------------------------------------
+**	Allocate a CCB and initialize its fixed part.
+**------------------------------------------------------------------------
+**------------------------------------------------------------------------
+*/
+static void ncr_alloc_ccb(ncb_p np, u_char tn, u_char ln)
+{
+	tcb_p tp = &np->target[tn];
+	lcb_p lp = tp->lp[ln];
+	ccb_p cp = 0;
 
-		tp->call_lun.l_cmd   = cpu_to_scr(SCR_CALL);
-		tp->call_lun.l_paddr =
-			cpu_to_scr(NCB_SCRIPT_PHYS (np, resel_lun));
+	/*
+	**	Allocate memory for this CCB.
+	*/
+	cp = m_alloc(sizeof(struct ccb), 5);
+	if (!cp)
+		return;
 
-		tp->jump_lcb.l_cmd   = cpu_to_scr(SCR_JUMP);
-		tp->jump_lcb.l_paddr = cpu_to_scr(NCB_SCRIPTH_PHYS (np, abort));
-		np->jump_tcb.l_paddr = cpu_to_scr(vtophys (&tp->jump_tcb));
+	if (DEBUG_FLAGS & DEBUG_ALLOC) {
+		PRINT_LUN(np, tn, ln);
+		printk ("new ccb @%p.\n", cp);
 	}
 
 	/*
-	**	Logic unit control block
+	**	Count it and initialyze it.
 	*/
-	lp = tp->lp[lun];
-	if (!lp) {
-		/*
-		**	Allocate a lcb
-		*/
-		lp = (lcb_p) m_alloc (sizeof (struct lcb), LCB_ALIGN_SHIFT);
-		if (!lp) return;
+	lp->actccbs++;
+	np->actccbs++;
+	bzero (cp, sizeof (*cp));
+	ncr_init_ccb(np, cp);
 
-		if (DEBUG_FLAGS & DEBUG_ALLOC) {
-			PRINT_LUN(np, target, lun);
-			printf ("new lcb @%p.\n", lp);
-		}
+	/*
+	**	Chain into wakeup list and free ccb queue and take it 
+	**	into account for tagged commands.
+	*/
+	cp->link_ccb      = np->ccb->link_ccb;
+	np->ccb->link_ccb = cp;
 
-		/*
-		**	Initialize it
-		*/
-		bzero (lp, sizeof (*lp));
-		lp->jump_lcb.l_cmd   =
-			cpu_to_scr(SCR_JUMP ^ IFFALSE (DATA (lun)));
-		lp->jump_lcb.l_paddr = tp->jump_lcb.l_paddr;
+	xpt_insque_head(&cp->link_ccbq, &lp->free_ccbq);
+	ncr_setup_tags (np, tn, ln);
+}
+
+/*==========================================================
+**
+**
+**      Allocation of resources for Targets/Luns/Tags.
+**
+**
+**==========================================================
+*/
 
-		lp->call_tag.l_cmd   = cpu_to_scr(SCR_CALL);
-		lp->call_tag.l_paddr =
-			cpu_to_scr(NCB_SCRIPT_PHYS (np, resel_tag));
 
-		lp->jump_ccb.l_cmd   = cpu_to_scr(SCR_JUMP);
-		lp->jump_ccb.l_paddr =
-			cpu_to_scr(NCB_SCRIPTH_PHYS (np, aborttag));
+/*------------------------------------------------------------------------
+**	Target control block initialisation.
+**------------------------------------------------------------------------
+**	This data structure is fully initialized after a SCSI command 
+**	has been successfully completed for this target.
+**	It contains a SCRIPT that is called on target reselection.
+**------------------------------------------------------------------------
+*/
+static void ncr_init_tcb (ncb_p np, u_char tn)
+{
+	tcb_p tp = &np->target[tn];
+	ncrcmd copy_1 = np->features & FE_PFEN ? SCR_COPY(1) : SCR_COPY_F(1);
+	int th = tn & 3;
+	int i;
 
-		lp->actlink = 1;
+	/*
+	**	Jump to next tcb if SFBR does not match this target.
+	**	JUMP  IF (SFBR != #target#), @(next tcb)
+	*/
+	tp->jump_tcb.l_cmd   =
+		cpu_to_scr((SCR_JUMP ^ IFFALSE (DATA (0x80 + tn))));
+	tp->jump_tcb.l_paddr = np->jump_tcb[th].l_paddr;
 
-		lp->active  = 1;
+	/*
+	**	Load the synchronous transfer register.
+	**	COPY @(tp->sval), @(sxfer)
+	*/
+	tp->getscr[0] =	cpu_to_scr(copy_1);
+	tp->getscr[1] = cpu_to_scr(vtophys (&tp->sval));
+	tp->getscr[2] = cpu_to_scr(ncr_reg_bus_addr(nc_sxfer));
+  
+	/*
+	**	Load the timing register.
+	**	COPY @(tp->wval), @(scntl3)
+	*/
+	tp->getscr[3] =	cpu_to_scr(copy_1);
+	tp->getscr[4] = cpu_to_scr(vtophys (&tp->wval));
+	tp->getscr[5] = cpu_to_scr(ncr_reg_bus_addr(nc_scntl3));
 
-		/*
-		**   Chain into LUN list
-		*/
-		tp->jump_lcb.l_paddr = cpu_to_scr(vtophys (&lp->jump_lcb));
-		tp->lp[lun] = lp;
+	/*
+	**	Get the IDENTIFY message and the lun.
+	**	CALL @script(resel_lun)
+	*/
+	tp->call_lun.l_cmd   = cpu_to_scr(SCR_CALL);
+	tp->call_lun.l_paddr = cpu_to_scr(NCB_SCRIPT_PHYS (np, resel_lun));
 
-		ncr_setmaxtags (np, tp, driver_setup.default_tags);
+	/*
+	**	Look for the lun control block of this nexus.
+	**	For i = 0 to 3
+	**		JUMP ^ IFTRUE (MASK (i, 3)), @(next_lcb)
+	*/
+	for (i = 0 ; i < 4 ; i++) {
+		tp->jump_lcb[i].l_cmd   =
+				cpu_to_scr((SCR_JUMP ^ IFTRUE (MASK (i, 3))));
+		tp->jump_lcb[i].l_paddr =
+				cpu_to_scr(NCB_SCRIPTH_PHYS (np, bad_identify));
 	}
 
 	/*
-	**	Allocate ccbs up to lp->reqccbs.
+	**	Link this target control block to the JUMP chain.
 	*/
+	np->jump_tcb[th].l_paddr = cpu_to_scr(vtophys (&tp->jump_tcb));
 
 	/*
-	**	Limit possible number of ccbs.
-	**
-	**	If tagged command queueing is enabled,
-	**	can use more than one ccb.
+	**	These assert's should be moved at driver initialisations.
 	*/
-	if (np->actccbs >= MAX_START-2) return;
-	if (lp->actccbs && (lp->actccbs >= lp->reqccbs))
-		return;
+	assert (( (offsetof(struct ncr_reg, nc_sxfer) ^
+		offsetof(struct tcb    , sval    )) &3) == 0);
+	assert (( (offsetof(struct ncr_reg, nc_scntl3) ^
+		offsetof(struct tcb    , wval    )) &3) == 0);
+}
+
+
+/*------------------------------------------------------------------------
+**	Lun control block allocation and initialization.
+**------------------------------------------------------------------------
+**	This data structure is allocated and initialized after a SCSI 
+**	command has been successfully completed for this target/lun.
+**------------------------------------------------------------------------
+*/
+static lcb_p ncr_alloc_lcb (ncb_p np, u_char tn, u_char ln)
+{
+	tcb_p tp = &np->target[tn];
+	lcb_p lp = tp->lp[ln];
+	ncrcmd copy_4 = np->features & FE_PFEN ? SCR_COPY(4) : SCR_COPY_F(4);
+	int lh = ln & 3;
 
 	/*
-	**	Allocate a ccb
+	**	Already done, return.
 	*/
-	cp = (ccb_p) m_alloc (sizeof (struct ccb), CCB_ALIGN_SHIFT);
-	if (!cp)
-		return;
+	if (lp)
+		return lp;
+
+	/*
+	**	Allocate the lcb.
+	*/
+	lp = m_alloc(sizeof(struct lcb), 3);
+	if (!lp)
+		goto fail;
+	bzero(lp, sizeof(*lp));
+	tp->lp[ln] = lp;
 
 	if (DEBUG_FLAGS & DEBUG_ALLOC) {
-		PRINT_LUN(np, target, lun);
-		printf ("new ccb @%p.\n", cp);
+		PRINT_LUN(np, tn, ln);
+		printk ("new lcb @%p.\n", lp);
 	}
 
 	/*
-	**	Count it
+	**	Initialize the target control block if not yet.
 	*/
-	lp->actccbs++;
-	np->actccbs++;
+	if (!tp->jump_tcb.l_cmd)
+		ncr_init_tcb(np, tn);
 
 	/*
-	**	Initialize it
+	**	Initialize the CCB queue headers.
 	*/
-	bzero (cp, sizeof (*cp));
+	xpt_que_init(&lp->free_ccbq);
+	xpt_que_init(&lp->busy_ccbq);
+	xpt_que_init(&lp->wait_ccbq);
+	xpt_que_init(&lp->skip_ccbq);
 
 	/*
-	**	Fill in physical addresses
+	**	Set max CCBs to 1 and use the default 1 entry 
+	**	jump table by default.
 	*/
-
-	cp->p_ccb	     = vtophys (cp);
+	lp->maxnxs	= 1;
+	lp->jump_ccb	= &lp->jump_ccb_0;
+	lp->p_jump_ccb	= cpu_to_scr(vtophys(lp->jump_ccb));
 
 	/*
-	**	Chain into reselect list
+	**	Initilialyze the reselect script:
+	**
+	**	Jump to next lcb if SFBR does not match this lun.
+	**	Load TEMP with the CCB direct jump table bus address.
+	**	Get the SIMPLE TAG message and the tag.
+	**
+	**	JUMP  IF (SFBR != #lun#), @(next lcb)
+	**	COPY @(lp->p_jump_ccb),	  @(temp)
+	**	JUMP @script(resel_notag)
 	*/
-	cp->jump_ccb.l_cmd   = cpu_to_scr(SCR_JUMP);
-	cp->jump_ccb.l_paddr = lp->jump_ccb.l_paddr;
-	lp->jump_ccb.l_paddr = cpu_to_scr(CCB_PHYS (cp, jump_ccb));
-	cp->call_tmp.l_cmd   = cpu_to_scr(SCR_CALL);
-	cp->call_tmp.l_paddr = cpu_to_scr(NCB_SCRIPT_PHYS (np, resel_tmp));
+	lp->jump_lcb.l_cmd   =
+		cpu_to_scr((SCR_JUMP ^ IFFALSE (MASK (0x80+ln, 0xff))));
+	lp->jump_lcb.l_paddr = tp->jump_lcb[lh].l_paddr;
+
+	lp->load_jump_ccb[0] = cpu_to_scr(copy_4);
+	lp->load_jump_ccb[1] = cpu_to_scr(vtophys (&lp->p_jump_ccb));
+	lp->load_jump_ccb[2] = cpu_to_scr(ncr_reg_bus_addr(nc_temp));
+
+	lp->jump_tag.l_cmd   = cpu_to_scr(SCR_JUMP);
+	lp->jump_tag.l_paddr = cpu_to_scr(NCB_SCRIPT_PHYS (np, resel_notag));
 
 	/*
-	**	Chain into wakeup list
+	**	Link this lun control block to the JUMP chain.
 	*/
-	cp->link_ccb      = np->ccb->link_ccb;
-	np->ccb->link_ccb = cp;
+	tp->jump_lcb[lh].l_paddr = cpu_to_scr(vtophys (&lp->jump_lcb));
 
 	/*
-	**	Chain into CCB list
+	**	Initialize command queuing control.
 	*/
-	cp->next_ccb	= lp->next_ccb;
-	lp->next_ccb	= cp;
+	lp->busyccbs	= 1;
+	lp->queuedccbs	= 1;
+	lp->queuedepth	= 1;
+fail:
+	return lp;
 }
 
-/*==========================================================
-**
-**
-**	Announce the number of ccbs/tags to the scsi driver.
-**
-**
-**==========================================================
-*/
 
-static void ncr_opennings (ncb_p np, lcb_p lp, Scsi_Cmnd * cmd)
+/*------------------------------------------------------------------------
+**	Lun control block setup on INQUIRY data received.
+**------------------------------------------------------------------------
+**	We only support WIDE, SYNC for targets and CMDQ for logical units.
+**	This setup is done on each INQUIRY since we are expecting user 
+**	will play with CHANGE DEFINITION commands. :-)
+**------------------------------------------------------------------------
+*/
+static lcb_p ncr_setup_lcb (ncb_p np, u_char tn, u_char ln, u_char *inq_data)
 {
+	tcb_p tp = &np->target[tn];
+	lcb_p lp = tp->lp[ln];
+	u_char inq_byte7;
+
 	/*
-	**	want to reduce the number ...
+	**	If no lcb, try to allocate it.
 	*/
-	if (lp->actlink > lp->reqlink) {
+	if (!lp && !(lp = ncr_alloc_lcb(np, tn, ln)))
+		goto fail;
 
-		/*
-		**	Try to  reduce the count.
-		**	We assume to run at splbio ..
-		*/
-		u_char diff = lp->actlink - lp->reqlink;
+	/*
+	**	Get device quirks from a speciality table.
+	*/
+	tp->quirks = ncr_lookup (inq_data);
+	if (tp->quirks && bootverbose) {
+		PRINT_LUN(np, tn, ln);
+		printk ("quirks=%x.\n", tp->quirks);
+	}
 
-		if (!diff) return;
+	/*
+	**	Evaluate trustable target/unit capabilities.
+	**	We only believe device version >= SCSI-2 that 
+	**	use appropriate response data format (2).
+	*/
+	inq_byte7 = 0;
+	if ((inq_data[2] & 0x7) >= 2 && (inq_data[3] & 0xf) == 2)
+		inq_byte7 = inq_data[7];
 
-		if (diff > lp->opennings)
-			diff = lp->opennings;
+	/*
+	**	Throw away announced LUN capabilities if we are told 
+	**	that there is no real device supported by the logical unit.
+	*/
+	if ((inq_data[0] & 0xe0) > 0x20 || (inq_data[0] & 0x1f) == 0x1f)
+		inq_byte7 &= (INQ7_SYNC | INQ7_WIDE16);
 
-		lp->opennings	-= diff;
+	/*
+	**	If user is wanting SYNC, force this feature.
+	*/
+	if (driver_setup.force_sync_nego)
+		inq_byte7 |= INQ7_SYNC;
 
-		lp->actlink	-= diff;
-		if (DEBUG_FLAGS & DEBUG_TAGS)
-			printf ("%s: actlink: diff=%d, new=%d, req=%d\n",
-				ncr_name(np), diff, lp->actlink, lp->reqlink);
-		return;
-	};
+	/*
+	**	Prepare negotiation if SIP capabilities have changed.
+	*/
+	tp->inq_done = 1;
+	if ((inq_byte7 ^ tp->inq_byte7) & (INQ7_SYNC | INQ7_WIDE16)) {
+		tp->inq_byte7 = inq_byte7;
+		ncr_negotiate(np, tp);
+	}
 
 	/*
-	**	want to increase the number ?
+	**	If unit supports tagged commands, allocate the 
+	**	CCB JUMP table if not yet.
 	*/
-	if (lp->reqlink > lp->actlink) {
-		u_char diff = lp->reqlink - lp->actlink;
+	if ((inq_byte7 & INQ7_QUEUE) && lp->jump_ccb == &lp->jump_ccb_0) {
+		int i;
+		lp->jump_ccb = m_alloc(256, 8);
+		if (!lp->jump_ccb) {
+			lp->jump_ccb = &lp->jump_ccb_0;
+			goto fail;
+		}
+		lp->p_jump_ccb = cpu_to_scr(vtophys(lp->jump_ccb));
+		for (i = 0 ; i < 64 ; i++)
+			lp->jump_ccb[i] =
+				cpu_to_scr(NCB_SCRIPTH_PHYS (np, bad_i_t_l_q));
+		for (i = 0 ; i < SCSI_NCR_MAX_TAGS ; i++)
+			lp->cb_tags[i] = i;
+		lp->maxnxs = SCSI_NCR_MAX_TAGS;
+		lp->tags_stime = jiffies;
+	}
 
-		lp->opennings	+= diff;
+	/*
+	**	Adjust tagged queueing status if needed.
+	*/
+	if ((inq_byte7 ^ lp->inq_byte7) & INQ7_QUEUE) {
+		lp->inq_byte7 = inq_byte7;
+		lp->numtags   = lp->maxtags;
+		ncr_setup_tags (np, tn, ln);
+	}
 
-		lp->actlink	+= diff;
-#if 0
-		wakeup ((caddr_t) xp->sc_link);
-#endif
-		if (DEBUG_FLAGS & DEBUG_TAGS)
-			printf ("%s: actlink: diff=%d, new=%d, req=%d\n",
-				ncr_name(np), diff, lp->actlink, lp->reqlink);
-	};
+fail:
+	return lp;
 }
 
 /*==========================================================
@@ -8471,9 +8753,6 @@
 	int segment	= 0;
 	int use_sg	= (int) cmd->use_sg;
 
-#if 0
-	bzero (cp->phys.data, sizeof (cp->phys.data));
-#endif
 	data		= cp->phys.data;
 	cp->data_len	= 0;
 
@@ -8522,7 +8801,7 @@
 static int ncr_regtest (struct ncb* np)
 )
 {
-	register volatile u_long data;
+	register volatile u_int32 data;
 	/*
 	**	ncr registers may NOT be cached.
 	**	write 0xffffffff to a read only register area,
@@ -8536,7 +8815,7 @@
 #else
 	if ((data & 0xe2f0fffd) != 0x02000080) {
 #endif
-		printf ("CACHE TEST FAILED: reg dstat-sstat2 readback %x.\n",
+		printk ("CACHE TEST FAILED: reg dstat-sstat2 readback %x.\n",
 			(unsigned) data);
 		return (0x10);
 	};
@@ -8548,8 +8827,8 @@
 static int ncr_snooptest (struct ncb* np)
 )
 {
-	u_long	ncr_rd, ncr_wr, ncr_bk, host_rd, host_wr, pc, err=0;
-	int	i;
+	u_int32	ncr_rd, ncr_wr, ncr_bk, host_rd, host_wr, pc;
+	int	i, err=0;
 #ifndef NCR_IOMAPPED
 	if (np->reg) {
             err |= ncr_regtest (np);
@@ -8591,22 +8870,22 @@
 	**	Reset ncr chip
 	*/
 	OUTB (nc_istat,  SRST);
-	DELAY (1000);
+	UDELAY (100);
 	OUTB (nc_istat,  0   );
 	/*
 	**	check for timeout
 	*/
 	if (i>=NCR_SNOOP_TIMEOUT) {
-		printf ("CACHE TEST FAILED: timeout.\n");
+		printk ("CACHE TEST FAILED: timeout.\n");
 		return (0x20);
 	};
 	/*
 	**	Check termination position.
 	*/
 	if (pc != NCB_SCRIPTH_PHYS (np, snoopend)+8) {
-		printf ("CACHE TEST FAILED: script execution failed.\n");
-		printf ("start=%08lx, pc=%08lx, end=%08lx\n", 
-			(u_long) NCB_SCRIPTH_PHYS (np, snooptest), pc,
+		printk ("CACHE TEST FAILED: script execution failed.\n");
+		printk ("start=%08lx, pc=%08lx, end=%08lx\n", 
+			(u_long) NCB_SCRIPTH_PHYS (np, snooptest), (u_long) pc,
 			(u_long) NCB_SCRIPTH_PHYS (np, snoopend) +8);
 		return (0x40);
 	};
@@ -8614,17 +8893,17 @@
 	**	Show results.
 	*/
 	if (host_wr != ncr_rd) {
-		printf ("CACHE TEST FAILED: host wrote %d, ncr read %d.\n",
+		printk ("CACHE TEST FAILED: host wrote %d, ncr read %d.\n",
 			(int) host_wr, (int) ncr_rd);
 		err |= 1;
 	};
 	if (host_rd != ncr_wr) {
-		printf ("CACHE TEST FAILED: ncr wrote %d, host read %d.\n",
+		printk ("CACHE TEST FAILED: ncr wrote %d, host read %d.\n",
 			(int) ncr_wr, (int) host_rd);
 		err |= 2;
 	};
 	if (ncr_bk != ncr_wr) {
-		printf ("CACHE TEST FAILED: ncr wrote %d, read back %d.\n",
+		printk ("CACHE TEST FAILED: ncr wrote %d, read back %d.\n",
 			(int) ncr_wr, (int) ncr_bk);
 		err |= 4;
 	};
@@ -8652,8 +8931,8 @@
 #define PROFILE  cp->phys.header.stamp
 static	void ncb_profile (ncb_p np, ccb_p cp)
 {
-	int co, st, en, di, se, post,work,disc;
-	u_long diff;
+	long co, st, en, di, re, post, work, disc;
+	u_int diff;
 
 	PROFILE.end = jiffies;
 
@@ -8665,18 +8944,18 @@
 
 	en = ncr_delta (PROFILE.start,PROFILE.end),
 	di = ncr_delta (PROFILE.start,PROFILE.disconnect),
-	se = ncr_delta (PROFILE.start,PROFILE.select);
+	re = ncr_delta (PROFILE.start,PROFILE.reselect);
 	post = en - st;
 
 	/*
 	**	@PROFILE@  Disconnect time invalid if multiple disconnects
 	*/
 
-	if (di>=0) disc = se-di; else  disc = 0;
+	if (di>=0) disc = re - di; else  disc = 0;
 
 	work = (st - co) - disc;
 
-	diff = (np->disc_phys - np->disc_ref) & 0xff;
+	diff = (scr_to_cpu(np->disc_phys) - np->disc_ref) & 0xff;
 	np->disc_ref += diff;
 
 	np->profile.num_trans	+= 1;
@@ -8718,13 +8997,13 @@
 
 static struct table_entry device_tab[] =
 {
-#ifdef NCR_GETCC_WITHMSG
+#if 0
 	{"", "", "", QUIRK_NOMSG},
+#endif
 	{"SONY", "SDT-5000", "3.17", QUIRK_NOMSG},
 	{"WangDAT", "Model 2600", "01.7", QUIRK_NOMSG},
 	{"WangDAT", "Model 3200", "02.2", QUIRK_NOMSG},
 	{"WangDAT", "Model 1300", "02.4", QUIRK_NOMSG},
-#endif
 	{"", "", "", 0} /* catch all: must be last entry. */
 };
 
@@ -8788,21 +9067,21 @@
 	}
 
 	if (bootverbose >= 2)
-		printf ("%s: enabling clock multiplier\n", ncr_name(np));
+		printk ("%s: enabling clock multiplier\n", ncr_name(np));
 
 	OUTB(nc_stest1, DBLEN);	   /* Enable clock multiplier		  */
 	if (np->multiplier > 2) {  /* Poll bit 5 of stest4 for quadrupler */
 		int i = 20;
 		while (!(INB(nc_stest4) & LCKFRQ) && --i > 0)
-			DELAY(20);
+			UDELAY (20);
 		if (!i)
-			printf("%s: the chip cannot lock the frequency\n", ncr_name(np));
+			printk("%s: the chip cannot lock the frequency\n", ncr_name(np));
 	} else			/* Wait 20 micro-seconds for doubler	*/
-		DELAY(20);
+		UDELAY (20);
 	OUTB(nc_stest3, HSC);		/* Halt the scsi clock		*/
 	OUTB(nc_scntl3,	scntl3);
 	OUTB(nc_stest1, (DBLEN|DBLSEL));/* Select clock multiplier	*/
-	OUTB(nc_stest3, 0x00|TE);		/* Restart scsi clock 		*/
+	OUTB(nc_stest3, 0x00);		/* Restart scsi clock 		*/
 }
 
 
@@ -8839,7 +9118,7 @@
 	OUTB (nc_stime1, 0);	/* disable general purpose timer */
 	OUTB (nc_stime1, gen);	/* set to nominal delay of 1<<gen * 125us */
 	while (!(INW(nc_sist) & GEN) && ms++ < 100000)
-		DELAY(1000);	/* count ms */
+		UDELAY (1000);	/* count ms */
 	OUTB (nc_stime1, 0);	/* disable general purpose timer */
  	/*
  	 * set prescaler to divide by whatever 0 means
@@ -8849,7 +9128,7 @@
  	OUTB (nc_scntl3, 0);
 
 	if (bootverbose >= 2)
-		printf ("%s: Delay (GEN=%d): %u msec\n", ncr_name(np), gen, ms);
+		printk ("%s: Delay (GEN=%d): %u msec\n", ncr_name(np), gen, ms);
   	/*
  	 * adjust for prescaler, and convert into KHz 
   	 */
@@ -8875,7 +9154,7 @@
 	*/
 	if (mult > 1 && (stest1 & (DBLEN+DBLSEL)) == DBLEN+DBLSEL) {
 		if (bootverbose >= 2)
-			printf ("%s: clock multiplier found\n", ncr_name(np));
+			printk ("%s: clock multiplier found\n", ncr_name(np));
 		np->multiplier = mult;
 	}
 
@@ -8887,14 +9166,14 @@
 	if (np->multiplier != mult || (scntl3 & 7) < 3 || !(scntl3 & 1)) {
 		unsigned f2;
 
-		OUTB(nc_istat, SRST); DELAY(5); OUTB(nc_istat, 0);
+		OUTB(nc_istat, SRST); UDELAY (5); OUTB(nc_istat, 0);
 
 		(void) ncrgetfreq (np, 11);	/* throw away first result */
 		f1 = ncrgetfreq (np, 11);
 		f2 = ncrgetfreq (np, 11);
 
 		if (bootverbose)
-			printf ("%s: NCR clock is %uKHz, %uKHz\n", ncr_name(np), f1, f2);
+			printk ("%s: NCR clock is %uKHz, %uKHz\n", ncr_name(np), f1, f2);
 
 		if (f1 > f2) f1 = f2;		/* trust lower result	*/
 
@@ -8904,7 +9183,7 @@
 
 		if (f1 < 80000 && mult > 1) {
 			if (bootverbose >= 2)
-				printf ("%s: clock multiplier assumed\n", ncr_name(np));
+				printk ("%s: clock multiplier assumed\n", ncr_name(np));
 			np->multiplier	= mult;
 		}
 	} else {
@@ -8943,6 +9222,12 @@
 ** ---------------------------------------------------------------------
 */
 
+#ifdef MODULE
+#define	ARG_SEP	' '
+#else
+#define	ARG_SEP	','
+#endif
+
 __initfunc(
 void ncr53c8xx_setup(char *str, int *ints)
 )
@@ -8955,33 +9240,33 @@
 	int c;
 
 	while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
+		char *pe;
+
 		val = 0;
 		pv = pc;
 		c = *++pv;
+
 		if	(c == 'n')
 			val = 0;
 		else if	(c == 'y')
 			val = 1;
 		else {
 			base = 0;
-#if 0
-			if	(c == '0') {
-				c = *pv++;
-				base = 8;
-			}
-			if	(c == 'x') {
-				++pv;
-				base = 16;
+			val = (int) simple_strtoul(pv, &pe, base);
+		}
+		if	(!strncmp(cur, "tags:", 5)) {
+			int i;
+			driver_setup.default_tags = val;
+			if (pe && *pe == '/') {
+				i = 0;
+				while (*pe && *pe != ARG_SEP && 
+					i < sizeof(driver_setup.tag_ctrl)-1) {
+					driver_setup.tag_ctrl[i++] = *pe++;
+				}
+				driver_setup.tag_ctrl[i] = '\0';
 			}
-			else if (c >= '0' && c <= '9')
-				base = 10;
-			else
-				break;
-#endif
-			val = (int) simple_strtoul(pv, NULL, base);
 		}
-
-		if	(!strncmp(cur, "mpar:", 5))
+		else if	(!strncmp(cur, "mpar:", 5))
 			driver_setup.master_parity	= val;
 		else if	(!strncmp(cur, "spar:", 5))
 			driver_setup.scsi_parity	= val;
@@ -8995,11 +9280,6 @@
 			driver_setup.force_sync_nego	= val;
 		else if	(!strncmp(cur, "revprob:", 8))
 			driver_setup.reverse_probe	= val;
-		else if	(!strncmp(cur, "tags:", 5)) {
-			if (val > SCSI_NCR_MAX_TAGS)
-				val = SCSI_NCR_MAX_TAGS;
-			driver_setup.default_tags	= val;
-			}
 		else if	(!strncmp(cur, "sync:", 5))
 			driver_setup.default_sync	= val;
 		else if	(!strncmp(cur, "verb:", 5))
@@ -9030,13 +9310,9 @@
 		else if	(!strncmp(cur, "safe:", 5) && val)
 			memcpy(&driver_setup, &driver_safe_setup, sizeof(driver_setup));
 		else
-			printf("ncr53c8xx_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur);
+			printk("ncr53c8xx_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur);
 
-#ifdef MODULE
-		if ((cur = strchr(cur, ' ')) != NULL)
-#else
-		if ((cur = strchr(cur, ',')) != NULL)
-#endif
+		if ((cur = strchr(cur, ARG_SEP)) != NULL)
 			++cur;
 	}
 #endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */
@@ -9065,24 +9341,31 @@
 )
 {
 #define YesNo(y)	y ? 'y' : 'n'
-	printk("ncr53c8xx: setup=disc:%c,specf:%d,ultra:%c,tags:%d,sync:%d,burst:%d,wide:%c,diff:%d\n",
-			YesNo(driver_setup.disconnection),
-			driver_setup.special_features,
-			YesNo(driver_setup.ultra_scsi),
-			driver_setup.default_tags,
-			driver_setup.default_sync,
-			driver_setup.burst_max,
-			YesNo(driver_setup.max_wide),
-			driver_setup.diff_support);
-	printk("ncr53c8xx: setup=mpar:%c,spar:%c,fsn=%c,verb:%d,debug:0x%x,led:%c,settle:%d,irqm:%d\n",
-			YesNo(driver_setup.master_parity),
-			YesNo(driver_setup.scsi_parity),
-			YesNo(driver_setup.force_sync_nego),
-			driver_setup.verbose,
-			driver_setup.debug,
-			YesNo(driver_setup.led_pin),
-			driver_setup.settle_delay,
-			driver_setup.irqm);
+	printk ("ncr53c8xx: setup=disc:%c,specf:%d,ultra:%d,tags:%d,sync:%d,"
+		"burst:%d,wide:%c,diff:%d,revprob:%c,buschk:0x%x\n",
+		YesNo(driver_setup.disconnection),
+		driver_setup.special_features,
+		driver_setup.ultra_scsi,
+		driver_setup.default_tags,
+		driver_setup.default_sync,
+		driver_setup.burst_max,
+		YesNo(driver_setup.max_wide),
+		driver_setup.diff_support,
+		YesNo(driver_setup.reverse_probe),
+		driver_setup.bus_check);
+
+	printk ("ncr53c8xx: setup=mpar:%c,spar:%c,fsn=%c,verb:%d,debug:0x%x,"
+		"led:%c,settle:%d,irqm:%d,nvram:0x%x,pcifix:0x%x\n",
+		YesNo(driver_setup.master_parity),
+		YesNo(driver_setup.scsi_parity),
+		YesNo(driver_setup.force_sync_nego),
+		driver_setup.verbose,
+		driver_setup.debug,
+		YesNo(driver_setup.led_pin),
+		driver_setup.settle_delay,
+		driver_setup.irqm,
+		driver_setup.use_nvram,
+		driver_setup.pci_fix_up);
 #undef YesNo
 }
 
@@ -9102,7 +9385,7 @@
 	int i, j;
 	int attach_count = 0;
 	ncr_nvram  *nvram;
-	ncr_device *devp;
+	ncr_device *devp = 0;	/* to shut up gcc */
 
 	if (!nvram_index)
 		return 0;
@@ -9117,13 +9400,13 @@
 			if (nvram_index == -1)
 				nvram_index = i;
 #ifdef SCSI_NCR_DEBUG_NVRAM
-			printf("ncr53c8xx: NVRAM: Symbios format Boot Block, 53c%s, PCI bus %d, device %d, function %d\n",
+			printk("ncr53c8xx: NVRAM: Symbios format Boot Block, 53c%s, PCI bus %d, device %d, function %d\n",
 				devp->chip.name, devp->slot.bus, 
 				(int) (devp->slot.device_fn & 0xf8) >> 3, 
 				(int) devp->slot.device_fn & 7);
 			for (j = 0 ; j < 4 ; j++) {
 				Symbios_host *h = &nvram->data.Symbios.host[j];
-			printf("ncr53c8xx: BOOT[%d] device_id=%04x vendor_id=%04x device_fn=%02x io_port=%04x %s\n",
+			printk("ncr53c8xx: BOOT[%d] device_id=%04x vendor_id=%04x device_fn=%02x io_port=%04x %s\n",
 				j,		h->device_id,	h->vendor_id,
 				h->device_fn,	h->io_port,
 				(h->flags & SYMBIOS_INIT_SCAN_AT_BOOT) ? "SCAN AT BOOT" : "");
@@ -9131,7 +9414,7 @@
 		}
 		else if (nvram->type == SCSI_NCR_TEKRAM_NVRAM) {
 			/* display Tekram nvram data */
-			printf("ncr53c8xx: NVRAM: Tekram format data, 53c%s, PCI bus %d, device %d, function %d\n",
+			printk("ncr53c8xx: NVRAM: Tekram format data, 53c%s, PCI bus %d, device %d, function %d\n",
 				devp->chip.name, devp->slot.bus, 
 				(int) (devp->slot.device_fn & 0xf8) >> 3, 
 				(int) devp->slot.device_fn & 7);
@@ -9198,18 +9481,14 @@
 #ifdef SCSI_NCR_NVRAM_SUPPORT
 	int nvram_index = 0;
 #endif
-	if (initverbose >= 2)
-		ncr_print_driver_setup();
 
 #ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
 	ncr_debug = driver_setup.debug;
 #endif
 
-#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
      tpnt->proc_dir = &proc_scsi_ncr53c8xx;
-# ifdef SCSI_NCR_PROC_INFO_SUPPORT
+#ifdef SCSI_NCR_PROC_INFO_SUPPORT
      tpnt->proc_info = ncr53c8xx_proc_info;
-# endif
 #endif
 
 #if	defined(SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT) && defined(MODULE)
@@ -9217,6 +9496,9 @@
 	ncr53c8xx_setup(ncr53c8xx, (int *) 0);
 #endif
 
+	if (initverbose >= 2)
+		ncr_print_driver_setup();
+
 	/* 
 	** Detect all 53c8xx hosts and then attach them.
 	**
@@ -9228,7 +9510,11 @@
 	** the order they are detected.
 	*/
 
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,92)
+	if (!pci_present())
+#else
 	if (!pcibios_present())
+#endif
 		return 0;
 
 	chips	= sizeof(ncr_chip_ids)	/ sizeof(ncr_chip_ids[0]);
@@ -9276,7 +9562,7 @@
 				}
 			}
 #endif
-			printf(KERN_INFO "ncr53c8xx: 53c%s detected %s\n",
+			printk(KERN_INFO "ncr53c8xx: 53c%s detected %s\n",
 				device[count].chip.name, msg);
 			++count;
 		}
@@ -9292,7 +9578,7 @@
 	for (i= 0; i < count; i++) {
 		if (!device[i].attach_done && 
 		    !ncr_attach (tpnt, attach_count, &device[i])) {
- 			attach_count++;
+			attach_count++;
 		}
 	}
 
@@ -9312,11 +9598,14 @@
 {
 	ushort vendor_id, device_id, command;
 	uchar cache_line_size, latency_timer;
-	uchar irq, revision;
-#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
-	uint base, base_2, io_port; 
+	uchar revision;
+#if LINUX_VERSION_CODE > LinuxVersionCode(2,1,92)
+	struct pci_dev *pdev;
+	ulong base, base_2, io_port; 
+	uint irq;
 #else
-	ulong base, base_2; 
+	uchar irq;
+	uint base, base_2, io_port; 
 #endif
 	int i;
 
@@ -9325,8 +9614,6 @@
 #endif
 	ncr_chip *chip;
 
-	printk(KERN_INFO "ncr53c8xx: at PCI bus %d, device %d, function %d\n",
-		bus, (int) (device_fn & 0xf8) >> 3, (int) device_fn & 7);
 	/*
 	 * Read info from the PCI config space.
 	 * pcibios_read_config_xxx() functions are assumed to be used for 
@@ -9340,16 +9627,29 @@
 					PCI_DEVICE_ID, &device_id);
 	(void) pcibios_read_config_word(bus, device_fn,
 					PCI_COMMAND, &command);
+#if LINUX_VERSION_CODE > LinuxVersionCode(2,1,92)
+	pdev = pci_find_slot(bus, device_fn);
+	io_port = pdev->base_address[0];
+	base = pdev->base_address[1];
+	base_2 = pdev->base_address[2];
+	irq = pdev->irq;
+#else
 	(void) pcibios_read_config_dword(bus, device_fn,
 					PCI_BASE_ADDRESS_0, &io_port);	
 	(void) pcibios_read_config_dword(bus, device_fn,
 					PCI_BASE_ADDRESS_1, &base);
 	(void) pcibios_read_config_dword(bus, device_fn,
 					PCI_BASE_ADDRESS_2, &base_2);
-	(void) pcibios_read_config_byte(bus, device_fn,
-					PCI_CLASS_REVISION,&revision);	
+
+	/* Handle 64bit base adresses for 53C896. */
+	if ((base & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == PCI_BASE_ADDRESS_MEM_TYPE_64)
+		(void) pcibios_read_config_dword(bus, device_fn,
+						 PCI_BASE_ADDRESS_3, &base_2);
 	(void) pcibios_read_config_byte(bus, device_fn,
 					PCI_INTERRUPT_LINE, &irq);
+#endif
+	(void) pcibios_read_config_byte(bus, device_fn,
+					PCI_CLASS_REVISION,&revision);	
 	(void) pcibios_read_config_byte(bus, device_fn,
 					PCI_CACHE_LINE_SIZE, &cache_line_size);
 	(void) pcibios_read_config_byte(bus, device_fn,
@@ -9369,6 +9669,34 @@
 		chip->revision_id = revision;
 		break;
 	}
+
+#if defined(__i386__)
+	/*
+	 *	Ignore Symbios chips controlled by SISL RAID controller.
+	 */
+	if (chip && (base_2 & PCI_BASE_ADDRESS_MEM_MASK)) {
+		unsigned int ScriptsSize, MagicValue;
+		vm_offset_t ScriptsRAM;
+
+		if (chip->features & FE_RAM8K)
+			ScriptsSize = 8192;
+		else
+			ScriptsSize = 4096;
+
+		ScriptsRAM = remap_pci_mem(base_2 & PCI_BASE_ADDRESS_MEM_MASK,
+					   ScriptsSize);
+		if (ScriptsRAM) {
+			MagicValue = readl(ScriptsRAM + ScriptsSize - 16);
+			unmap_pci_mem(ScriptsRAM, ScriptsSize);
+			if (MagicValue == 0x52414944)
+				return -1;
+		}
+	}
+#endif
+
+	printk(KERN_INFO "ncr53c8xx: at PCI bus %d, device %d, function %d\n",
+		bus, (int) (device_fn & 0xf8) >> 3, (int) device_fn & 7);
+
 	if (!chip) {
 		printk("ncr53c8xx: not initializing, device not supported\n");
 		return -1;
@@ -9376,25 +9704,111 @@
 
 #ifdef __powerpc__
 	/*
-	 *	Severall fix-up for power/pc.
+	 *	Several fix-up for power/pc.
 	 *	Should not be performed by the driver.
 	 */
-	if ((command &
-		(PCI_COMMAND_MASTER|PCI_COMMAND_IO|PCI_COMMAND_MEMORY)) !=
-		(PCI_COMMAND_MASTER|PCI_COMMAND_IO|PCI_COMMAND_MEMORY)) {
-		printk("ncr53c8xx : setting PCI master/io/command bit\n");
-		command |= PCI_COMMAND_MASTER|PCI_COMMAND_IO|PCI_COMMAND_MEMORY;
+	if (!(command & PCI_COMMAND_MASTER)) {
+		printk("ncr53c8xx: attempting to force PCI_COMMAND_MASTER...");
+		command |= PCI_COMMAND_MASTER;
 		pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
+		pcibios_read_config_word(bus, device_fn, PCI_COMMAND, &command);
+		if (!(command & PCI_COMMAND_MASTER)) {
+			printk("failed!\n");
+		} else {
+			printk("succeeded.\n");
+		}
 	}
-	if (io_port >= 0x10000000) {
-		io_port = (io_port & 0x00FFFFFF) | 0x01000000;
-		pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0, io_port);
-	}
-	if (base >= 0x10000000) {
-		base = (base & 0x00FFFFFF) | 0x01000000;
-		pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, base);
+
+	if (!(command & PCI_COMMAND_IO)) {
+		printk("ncr53c8xx: attempting to force PCI_COMMAND_IO...");
+		command |= PCI_COMMAND_IO;
+		pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
+		pcibios_read_config_word(bus, device_fn, PCI_COMMAND, &command);
+		if (!(command & PCI_COMMAND_IO)) {
+			printk("failed!\n");
+		} else {
+			printk("succeeded.\n");
+		}
 	}
-#endif
+
+	if (!(command & PCI_COMMAND_MEMORY)) {
+		printk("ncr53c8xx: attempting to force PCI_COMMAND_MEMORY...");
+		command |= PCI_COMMAND_MEMORY;
+		pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
+		pcibios_read_config_word(bus, device_fn, PCI_COMMAND, &command);
+		if (!(command & PCI_COMMAND_MEMORY)) {
+			printk("failed!\n");
+		} else {
+			printk("succeeded.\n");
+		}
+	}
+	
+	if ( is_prep ) {
+		if (io_port >= 0x10000000) {
+			printk("ncr53c8xx: reallocating io_port (Wacky IBM)");
+			io_port = (io_port & 0x00FFFFFF) | 0x01000000;
+			pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0, io_port);
+		}
+		if (base >= 0x10000000) {
+			printk("ncr53c8xx: reallocating base (Wacky IBM)");
+			base = (base & 0x00FFFFFF) | 0x01000000;
+			pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, base);
+		}
+		if (base_2 >= 0x10000000) {
+			printk("ncr53c8xx: reallocating base2 (Wacky IBM)");
+			base_2 = (base_2 & 0x00FFFFFF) | 0x01000000;
+			pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_2, base_2);
+		}
+	}
+#endif	/* __powerpc__ */
+
+#ifdef __sparc__
+	/*
+	 *	Severall fix-ups for sparc.
+	 *
+	 *	Should not be performed by the driver, but how can OBP know
+	 *	each and every PCI card, if they don't use Fcode?
+	 */
+
+	base = __pa(base);
+	base_2 = __pa(base_2);
+
+	if (!(command & PCI_COMMAND_MASTER)) {
+		if (initverbose >= 2)
+			printk("ncr53c8xx: setting PCI_COMMAND_MASTER bit (fixup)\n");
+		command |= PCI_COMMAND_MASTER;
+		pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
+		pcibios_read_config_word(bus, device_fn, PCI_COMMAND, &command);
+	}
+
+	if ((chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
+		if (initverbose >= 2)
+			printk("ncr53c8xx: setting PCI_COMMAND_INVALIDATE bit (fixup)\n");
+		command |= PCI_COMMAND_INVALIDATE;
+		pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
+		pcibios_read_config_word(bus, device_fn, PCI_COMMAND, &command);
+	}
+
+	if ((chip->features & FE_CLSE) && !cache_line_size) {
+		cache_line_size = CACHE_LINE_SIZE;
+		if (initverbose >= 2)
+			printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fixup)\n", cache_line_size);
+		pcibios_write_config_byte(bus, device_fn,
+					  PCI_CACHE_LINE_SIZE, cache_line_size);
+		pcibios_read_config_byte(bus, device_fn,
+					 PCI_CACHE_LINE_SIZE, &cache_line_size);
+	}
+
+	if (!latency_timer) {
+		latency_timer = 248;
+		if (initverbose >= 2)
+			printk("ncr53c8xx: setting PCI_LATENCY_TIMER to %d bus clocks (fixup)\n", latency_timer);
+		pcibios_write_config_byte(bus, device_fn,
+					  PCI_LATENCY_TIMER, latency_timer);
+		pcibios_read_config_byte(bus, device_fn,
+					 PCI_LATENCY_TIMER, &latency_timer);
+	}
+#endif	/* __sparc__ */
 
 	/*
 	 * Check availability of IO space, memory space and master capability.
@@ -9431,8 +9845,13 @@
 	base_2 &= PCI_BASE_ADDRESS_MEM_MASK;
 
 	if (io_port && check_region (io_port, 128)) {
+#ifdef __sparc__
+		printk("ncr53c8xx: IO region 0x%lx to 0x%lx is in use\n",
+			io_port, (io_port + 127));
+#else
 		printk("ncr53c8xx: IO region 0x%x to 0x%x is in use\n",
 			(int) io_port, (int) (io_port + 127));
+#endif
 		return -1;
 	}
 	
@@ -9465,7 +9884,7 @@
 	/*
 	 * Try to fix up PCI config according to wished features.
 	 */
-#if defined(__i386) && !defined(MODULE)
+#if defined(__i386__) && !defined(MODULE)
 	if ((driver_setup.pci_fix_up & 1) &&
 	    (chip->features & FE_CLSE) && cache_line_size == 0) {
 #if LINUX_VERSION_CODE < LinuxVersionCode(2,1,75)
@@ -9475,6 +9894,7 @@
 		switch(boot_cpu_data.x86) {
 #endif
 		case 4:	cache_line_size = 4; break;
+		case 6:
 		case 5:	cache_line_size = 8; break;
 		}
 		if (cache_line_size)
@@ -9610,35 +10030,111 @@
 	return 0;     
 }
 
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0)
 /*
 **   Linux select queue depths function
 */
+
+#define DEF_DEPTH	(driver_setup.default_tags)
+#define ALL_TARGETS	-2
+#define NO_TARGET	-1
+#define ALL_LUNS	-2
+#define NO_LUN		-1
+
+static int device_queue_depth(ncb_p np, int target, int lun)
+{
+	int c, h, t, u, v;
+	char *p = driver_setup.tag_ctrl;
+	char *ep;
+
+	h = -1;
+	t = NO_TARGET;
+	u = NO_LUN;
+	while ((c = *p++) != 0) {
+		v = simple_strtoul(p, &ep, 0);
+		switch(c) {
+		case '/':
+			++h;
+			t = ALL_TARGETS;
+			u = ALL_LUNS;
+			break;
+		case 't':
+			if (t != target)
+				t = (target == v) ? v : NO_TARGET;
+			u = ALL_LUNS;
+			break;
+		case 'u':
+			if (u != lun)
+				u = (lun == v) ? v : NO_LUN;
+			break;
+		case 'q':
+			if (h == np->unit &&
+				(t == ALL_TARGETS || t == target) &&
+				(u == ALL_LUNS    || u == lun))
+				return v;
+			break;
+		case '-':
+			t = ALL_TARGETS;
+			u = ALL_LUNS;
+			break;
+		default:
+			break;
+		}
+		p = ep;
+	}
+	return DEF_DEPTH;
+}
+
 static void ncr53c8xx_select_queue_depths(struct Scsi_Host *host, struct scsi_device *devlist)
 {
 	struct scsi_device *device;
 
 	for (device = devlist; device; device = device->next) {
-		if (device->host == host) {
-#if SCSI_NCR_MAX_TAGS > 1
-			if (device->tagged_supported) {
-				device->queue_depth = SCSI_NCR_MAX_TAGS;
-			}
-			else {
-				device->queue_depth = 2;
-			}
-#else
-			device->queue_depth = 1;
-#endif
+		ncb_p np;
+		tcb_p tp;
+		lcb_p lp;
+		int numtags;
+
+		if (device->host != host)
+			continue;
+
+		np = ((struct host_data *) host->hostdata)->ncb;
+		tp = &np->target[device->id];
+		lp = tp->lp[device->lun];
+
+		/*
+		**	Select queue depth from driver setup.
+		**	Donnot use more than configured by user.
+		**	Use at least 2.
+		**	Donnot use more than our maximum.
+		*/
+		numtags = device_queue_depth(np, device->id, device->lun);
+		if (numtags > tp->usrtags)
+			numtags = tp->usrtags;
+		if (!device->tagged_supported)
+			numtags = 1;
+		device->queue_depth = numtags;
+		if (device->queue_depth < 2)
+			device->queue_depth = 2;
+		if (device->queue_depth > SCSI_NCR_MAX_TAGS)
+			device->queue_depth = SCSI_NCR_MAX_TAGS;
+
+		/*
+		**	Since the queue depth is not tunable under Linux,
+		**	we need to know this value in order not to 
+		**	announce stupid things to user.
+		*/
+		if (lp) {
+			lp->numtags = lp->maxtags = numtags;
+			lp->scdev_depth = device->queue_depth;
+		}
+		ncr_setup_tags (np, device->id, device->lun);
 
 #ifdef DEBUG_NCR53C8XX
-printk("ncr53c8xx_select_queue_depth: id=%d, lun=%d, queue_depth=%d\n",
-	device->id, device->lun, device->queue_depth);
+printk("ncr53c8xx_select_queue_depth: host=%d, id=%d, lun=%d, depth=%d\n",
+	np->unit, device->id, device->lun, device->queue_depth);
 #endif
-		}
 	}
 }
-#endif
 
 /*
 **   Linux entry point of queuecommand() function
@@ -9646,69 +10142,96 @@
 
 int ncr53c8xx_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
 {
+     ncb_p np = ((struct host_data *) cmd->host->hostdata)->ncb;
+     unsigned long flags;
      int sts;
+
 #ifdef DEBUG_NCR53C8XX
 printk("ncr53c8xx_queue_command\n");
 #endif
 
-     if ((sts = ncr_queue_command(cmd, done)) != DID_OK) {
+     cmd->scsi_done     = done;
+     cmd->host_scribble = NULL;
+     cmd->SCp.ptr       = NULL;
+     cmd->SCp.buffer    = NULL;
+
+     NCR_LOCK_NCB(np, flags);
+
+     if ((sts = ncr_queue_command(np, cmd)) != DID_OK) {
 	  cmd->result = ScsiResult(sts, 0);
-	  done(cmd);
 #ifdef DEBUG_NCR53C8XX
 printk("ncr53c8xx : command not queued - result=%d\n", sts);
 #endif
-          return sts;
      }
 #ifdef DEBUG_NCR53C8XX
+     else
 printk("ncr53c8xx : command successfully queued\n");
 #endif
+
+     NCR_UNLOCK_NCB(np, flags);
+
+     if (sts != DID_OK)
+          done(cmd);
+
      return sts;
 }
 
 /*
 **   Linux entry point of the interrupt handler.
-**   Fort linux versions > 1.3.70, we trust the kernel for 
+**   Since linux versions > 1.3.70, we trust the kernel for 
 **   passing the internal host descriptor as 'dev_id'.
 **   Otherwise, we scan the host list and call the interrupt 
 **   routine for each host that uses this IRQ.
 */
 
-#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
 static void ncr53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs)
 {
+     unsigned long flags;
+     ncb_p np = (ncb_p) dev_id;
+     Scsi_Cmnd *done_list;
+
 #ifdef DEBUG_NCR53C8XX
      printk("ncr53c8xx : interrupt received\n");
 #endif
 
-     if (DEBUG_FLAGS & DEBUG_TINY) printf ("[");
-     ncr_exception((ncb_p) dev_id);
-     if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n");
-}
-
-#else
-static void ncr53c8xx_intr(int irq, struct pt_regs * regs)
-{
-     struct Scsi_Host *host;
-     struct host_data *host_data;
+     if (DEBUG_FLAGS & DEBUG_TINY) printk ("[");
 
-     for (host = first_host; host; host = host->next) {
-	  if (host->hostt == the_template && host->irq == irq) {
-	       host_data = (struct host_data *) host->hostdata;
-               if (DEBUG_FLAGS & DEBUG_TINY) printf ("[");
-               ncr_exception(host_data->ncb);
-               if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n");
-	  }
+     NCR_LOCK_NCB(np, flags);
+     ncr_exception(np);
+     done_list     = np->done_list;
+     np->done_list = 0;
+     NCR_UNLOCK_NCB(np, flags);
+
+     if (DEBUG_FLAGS & DEBUG_TINY) printk ("]\n");
+
+     if (done_list) {
+          NCR_LOCK_SCSI_DONE(np, flags);
+          ncr_flush_done_cmds(done_list);
+          NCR_UNLOCK_SCSI_DONE(np, flags);
      }
 }
-#endif
 
 /*
 **   Linux entry point of the timer handler
 */
 
-static void ncr53c8xx_timeout(unsigned long np)
+static void ncr53c8xx_timeout(unsigned long npref)
 {
+     ncb_p np = (ncb_p) npref;
+     unsigned long flags;
+     Scsi_Cmnd *done_list;
+
+     NCR_LOCK_NCB(np, flags);
      ncr_timeout((ncb_p) np);
+     done_list     = np->done_list;
+     np->done_list = 0;
+     NCR_UNLOCK_NCB(np, flags);
+
+     if (done_list) {
+          NCR_LOCK_SCSI_DONE(np, flags);
+          ncr_flush_done_cmds(done_list);
+          NCR_UNLOCK_SCSI_DONE(np, flags);
+     }
 }
 
 /*
@@ -9716,16 +10239,24 @@
 */
 
 #if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS
-
 int ncr53c8xx_reset(Scsi_Cmnd *cmd, unsigned int reset_flags)
+#else
+int ncr53c8xx_reset(Scsi_Cmnd *cmd)
+#endif
 {
+	ncb_p np = ((struct host_data *) cmd->host->hostdata)->ncb;
 	int sts;
 	unsigned long flags;
+	Scsi_Cmnd *done_list;
 
+#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS
 	printk("ncr53c8xx_reset: pid=%lu reset_flags=%x serial_number=%ld serial_number_at_timeout=%ld\n",
 		cmd->pid, reset_flags, cmd->serial_number, cmd->serial_number_at_timeout);
+#else
+	printk("ncr53c8xx_reset: command pid %lu\n", cmd->pid);
+#endif
 
-	save_flags(flags); cli();
+	NCR_LOCK_NCB(np, flags);
 
 	/*
 	 * We have to just ignore reset requests in some situations.
@@ -9743,8 +10274,13 @@
 	 * before returning SCSI_RESET_SUCCESS.
 	 */
 
-	sts = ncr_reset_bus(cmd,
+#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS
+	sts = ncr_reset_bus(np, cmd,
 	(reset_flags & (SCSI_RESET_SYNCHRONOUS | SCSI_RESET_ASYNCHRONOUS)) == SCSI_RESET_SYNCHRONOUS);
+#else
+	sts = ncr_reset_bus(np, cmd, 0);
+#endif
+
 	/*
 	 * Since we always reset the controller, when we return success, 
 	 * we add this information to the return code.
@@ -9755,33 +10291,36 @@
 #endif
 
 out:
-	restore_flags(flags);
+	done_list     = np->done_list;
+	np->done_list = 0;
+	NCR_UNLOCK_NCB(np, flags);
+
+	ncr_flush_done_cmds(done_list);
+
 	return sts;
 }
-#else
-int ncr53c8xx_reset(Scsi_Cmnd *cmd)
-{
-	printk("ncr53c8xx_reset: command pid %lu\n", cmd->pid);
-	return ncr_reset_bus(cmd, 1);
-}
-#endif
 
 /*
 **   Linux entry point of abort() function
 */
 
-#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS
-
 int ncr53c8xx_abort(Scsi_Cmnd *cmd)
 {
+	ncb_p np = ((struct host_data *) cmd->host->hostdata)->ncb;
 	int sts;
 	unsigned long flags;
+	Scsi_Cmnd *done_list;
 
+#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS
 	printk("ncr53c8xx_abort: pid=%lu serial_number=%ld serial_number_at_timeout=%ld\n",
 		cmd->pid, cmd->serial_number, cmd->serial_number_at_timeout);
+#else
+	printk("ncr53c8xx_abort: command pid %lu\n", cmd->pid);
+#endif
 
-	save_flags(flags); cli();
+	NCR_LOCK_NCB(np, flags);
 
+#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS
 	/*
 	 * We have to just ignore abort requests in some situations.
 	 */
@@ -9789,19 +10328,19 @@
 		sts = SCSI_ABORT_NOT_RUNNING;
 		goto out;
 	}
+#endif
 
-	sts = ncr_abort_command(cmd);
+	sts = ncr_abort_command(np, cmd);
 out:
-	restore_flags(flags);
+	done_list     = np->done_list;
+	np->done_list = 0;
+	NCR_UNLOCK_NCB(np, flags);
+
+	ncr_flush_done_cmds(done_list);
+
 	return sts;
 }
-#else
-int ncr53c8xx_abort(Scsi_Cmnd *cmd)
-{
-	printk("ncr53c8xx_abort: command pid %lu\n", cmd->pid);
-	return ncr_abort_command(cmd);
-}
-#endif
+
 
 #ifdef MODULE
 int ncr53c8xx_release(struct Scsi_Host *host)
@@ -9836,7 +10375,7 @@
 	Scsi_Cmnd *wcmd;
 
 #ifdef DEBUG_WAITING_LIST
-	printf("%s: cmd %lx inserted into waiting list\n", ncr_name(np), (u_long) cmd);
+	printk("%s: cmd %lx inserted into waiting list\n", ncr_name(np), (u_long) cmd);
 #endif
 	cmd->next_wcmd = 0;
 	if (!(wcmd = np->waiting_list)) np->waiting_list = cmd;
@@ -9859,7 +10398,7 @@
 				cmd->next_wcmd = 0;
 			}
 #ifdef DEBUG_WAITING_LIST
-	printf("%s: cmd %lx retrieved from waiting list\n", ncr_name(np), (u_long) cmd);
+	printk("%s: cmd %lx retrieved from waiting list\n", ncr_name(np), (u_long) cmd);
 #endif
 			return cmd;
 		}
@@ -9875,70 +10414,29 @@
 	np->waiting_list = 0;
 
 #ifdef DEBUG_WAITING_LIST
-	if (waiting_list) printf("%s: waiting_list=%lx processing sts=%d\n", ncr_name(np), (u_long) waiting_list, sts);
+	if (waiting_list) printk("%s: waiting_list=%lx processing sts=%d\n", ncr_name(np), (u_long) waiting_list, sts);
 #endif
 	while ((wcmd = waiting_list) != 0) {
 		waiting_list = (Scsi_Cmnd *) wcmd->next_wcmd;
 		wcmd->next_wcmd = 0;
 		if (sts == DID_OK) {
 #ifdef DEBUG_WAITING_LIST
-	printf("%s: cmd %lx trying to requeue\n", ncr_name(np), (u_long) wcmd);
+	printk("%s: cmd %lx trying to requeue\n", ncr_name(np), (u_long) wcmd);
 #endif
-			sts = ncr_queue_command(wcmd, wcmd->scsi_done);
+			sts = ncr_queue_command(np, wcmd);
 		}
 		if (sts != DID_OK) {
 #ifdef DEBUG_WAITING_LIST
-	printf("%s: cmd %lx done forced sts=%d\n", ncr_name(np), (u_long) wcmd, sts);
+	printk("%s: cmd %lx done forced sts=%d\n", ncr_name(np), (u_long) wcmd, sts);
 #endif
 			wcmd->result = ScsiResult(sts, 0);
-			wcmd->scsi_done(wcmd);
+			ncr_queue_done_cmd(np, wcmd);
 		}
 	}
 }
 
 #undef next_wcmd
 
-/*
-**	Returns data transfer direction for common op-codes.
-*/
-
-static int guess_xfer_direction(int opcode)
-{
-	int d;
-
-	switch(opcode) {
-	case 0x12:  /*	INQUIRY				12 */
-	case 0x4D:  /*	LOG SENSE			4D */
-	case 0x5A:  /*	MODE SENSE(10)			5A */
-	case 0x1A:  /*	MODE SENSE(6)			1A */
-	case 0x3C:  /*	READ BUFFER			3C */
-	case 0x1C:  /*	RECEIVE DIAGNOSTIC RESULTS	1C */
-	case 0x03:  /*	REQUEST SENSE			03 */
-		d = XferIn;
-		break;
-	case 0x39:  /*	COMPARE				39 */
-	case 0x3A:  /*	COPY AND VERIFY			3A */
-	case 0x18:  /*	COPY				18 */
-	case 0x4C:  /*	LOG SELECT			4C */
-	case 0x55:  /*	MODE SELECT(10)			55 */
-	case 0x3B:  /*	WRITE BUFFER			3B */
-	case 0x1D:  /*	SEND DIAGNOSTIC			1D */
-	case 0x40:  /*	CHANGE DEFINITION		40 */
-	case 0x15:  /*	MODE SELECT(6)			15 */
-		d = XferOut;
-		break;
-	case 0x00:  /*	TEST UNIT READY			00 */
-		d = XferNone;
-		break;
-	default:
-		d = XferBoth;
-		break;
-	}
-
-	return d;
-}
-
-
 #ifdef SCSI_NCR_PROC_INFO_SUPPORT
 
 /*=========================================================================
@@ -10026,6 +10524,8 @@
 		uc->cmd = UC_SETTAGS;
 	else if	((arg_len = is_keyword(ptr, len, "setorder")) != 0)
 		uc->cmd = UC_SETORDER;
+	else if	((arg_len = is_keyword(ptr, len, "setverbose")) != 0)
+		uc->cmd = UC_SETVERBOSE;
 	else if	((arg_len = is_keyword(ptr, len, "setwide")) != 0)
 		uc->cmd = UC_SETWIDE;
 	else if	((arg_len = is_keyword(ptr, len, "setdebug")) != 0)
@@ -10034,15 +10534,11 @@
 		uc->cmd = UC_SETFLAG;
 	else if	((arg_len = is_keyword(ptr, len, "clearprof")) != 0)
 		uc->cmd = UC_CLEARPROF;
-#ifdef	UC_DEBUG_ERROR_RECOVERY
-	else if	((arg_len = is_keyword(ptr, len, "debug_error_recovery")) != 0)
-		uc->cmd = UC_DEBUG_ERROR_RECOVERY;
-#endif
 	else
 		arg_len = 0;
 
 #ifdef DEBUG_PROC_INFO
-printf("ncr_user_command: arg_len=%d, cmd=%ld\n", arg_len, uc->cmd);
+printk("ncr_user_command: arg_len=%d, cmd=%ld\n", arg_len, uc->cmd);
 #endif
 
 	if (!arg_len)
@@ -10062,20 +10558,21 @@
 			GET_INT_ARG(target);
 			uc->target = (1<<target);
 #ifdef DEBUG_PROC_INFO
-printf("ncr_user_command: target=%ld\n", target);
+printk("ncr_user_command: target=%ld\n", target);
 #endif
 		}
 		break;
 	}
 
 	switch(uc->cmd) {
+	case UC_SETVERBOSE:
 	case UC_SETSYNC:
 	case UC_SETTAGS:
 	case UC_SETWIDE:
 		SKIP_SPACES(1);
 		GET_INT_ARG(uc->data);
 #ifdef DEBUG_PROC_INFO
-printf("ncr_user_command: data=%ld\n", uc->data);
+printk("ncr_user_command: data=%ld\n", uc->data);
 #endif
 		break;
 	case UC_SETORDER:
@@ -10123,7 +10620,7 @@
 			ptr += arg_len; len -= arg_len;
 		}
 #ifdef DEBUG_PROC_INFO
-printf("ncr_user_command: data=%ld\n", uc->data);
+printk("ncr_user_command: data=%ld\n", uc->data);
 #endif
 		break;
 	case UC_SETFLAG:
@@ -10138,24 +10635,6 @@
 			ptr += arg_len; len -= arg_len;
 		}
 		break;
-#ifdef	UC_DEBUG_ERROR_RECOVERY
-	case UC_DEBUG_ERROR_RECOVERY:
-		SKIP_SPACES(1);
-		if	((arg_len = is_keyword(ptr, len, "sge")))
-			uc->data = 1;
-		else if	((arg_len = is_keyword(ptr, len, "abort")))
-			uc->data = 2;
-		else if	((arg_len = is_keyword(ptr, len, "reset")))
-			uc->data = 3;
-		else if	((arg_len = is_keyword(ptr, len, "parity")))
-			uc->data = 4;
-		else if	((arg_len = is_keyword(ptr, len, "none")))
-			uc->data = 0;
-		else
-			return -EINVAL;
-		ptr += arg_len; len -= arg_len;
-		break;
-#endif
 	default:
 		break;
 	}
@@ -10165,9 +10644,9 @@
 	else {
 		long flags;
 
-		save_flags(flags); cli();
+		NCR_LOCK_NCB(np, flags);
 		ncr_usercmd (np);
-		restore_flags(flags);
+		NCR_UNLOCK_NCB(np, flags);
 	}
 	return length;
 }
@@ -10239,7 +10718,11 @@
 	copy_info(&info, "revision id 0x%x\n",	np->revision_id);
 
 	copy_info(&info, "  IO port address 0x%lx, ", (u_long) np->port);
+#ifdef __sparc__
+	copy_info(&info, "IRQ number %s\n", __irq_itoa(np->irq));
+#else
 	copy_info(&info, "IRQ number %d\n", (int) np->irq);
+#endif
 
 #ifndef NCR_IOMAPPED
 	if (np->reg)
@@ -10288,7 +10771,7 @@
 	int retv;
 
 #ifdef DEBUG_PROC_INFO
-printf("ncr53c8xx_proc_info: hostno=%d, func=%d\n", hostno, func);
+printk("ncr53c8xx_proc_info: hostno=%d, func=%d\n", hostno, func);
 #endif
 
 	for (host = first_host; host; host = host->next) {
@@ -10426,7 +10909,7 @@
 	nvram_stop(np, &gpreg);
 	
 #ifdef SCSI_NCR_DEBUG_NVRAM
-printf("ncr53c8xx: NvRAM marker=%x trailer=%x %x %x %x %x %x byte_count=%d/%d checksum=%x/%x\n",
+printk("ncr53c8xx: NvRAM marker=%x trailer=%x %x %x %x %x %x byte_count=%d/%d checksum=%x/%x\n",
 	nvram->start_marker,
 	nvram->trailer[0], nvram->trailer[1], nvram->trailer[2],
 	nvram->trailer[3], nvram->trailer[4], nvram->trailer[5],
@@ -10576,7 +11059,7 @@
 static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode)
 )
 {
-	DELAY(5);
+	UDELAY (5);
 	switch (bit_mode){
 	case SET_BIT:
 		*gpreg |= write_bit;
@@ -10593,7 +11076,7 @@
 
 	}
 	OUTB (nc_gpreg, *gpreg);
-	DELAY(5);
+	UDELAY (5);
 }
 
 #undef SET_BIT 0
@@ -10730,7 +11213,7 @@
 static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg)
 )
 {
-	DELAY(2);
+	UDELAY (2);
 	Tnvram_Clk(np, gpreg);
 	*read_bit = INB (nc_gpreg);
 }
@@ -10750,7 +11233,7 @@
 	*gpreg |= 0x10;
 		
 	OUTB (nc_gpreg, *gpreg);
-	DELAY(2);
+	UDELAY (2);
 
 	Tnvram_Clk(np, gpreg);
 }
@@ -10764,7 +11247,7 @@
 {
 	*gpreg &= 0xef;
 	OUTB (nc_gpreg, *gpreg);
-	DELAY(2);
+	UDELAY (2);
 
 	Tnvram_Clk(np, gpreg);
 }
@@ -10777,7 +11260,7 @@
 )
 {
 	OUTB (nc_gpreg, *gpreg | 0x04);
-	DELAY(2);
+	UDELAY (2);
 	OUTB (nc_gpreg, *gpreg);
 }
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/ncr53c8xx.h linux/drivers/scsi/ncr53c8xx.h
--- linux.vanilla/drivers/scsi/ncr53c8xx.h	Sun Jun 21 18:41:16 1998
+++ linux/drivers/scsi/ncr53c8xx.h	Fri Mar 26 17:14:47 1999
@@ -45,7 +45,7 @@
 /*
 **	Name and revision of the driver
 */
-#define SCSI_NCR_DRIVER_NAME		"ncr53c8xx - revision 2.5f.1"
+#define SCSI_NCR_DRIVER_NAME		"ncr53c8xx - revision 3.1e"
 
 /*
 **	Check supported Linux versions
@@ -56,51 +56,34 @@
 #endif
 #include <linux/config.h>
 
-/*
-**	During make dep of linux-1.2.13, LINUX_VERSION_CODE is undefined
-**	Under linux-1.3.X, all seems to be OK.
-**	So, we have only to define it under 1.2.13
-*/
-
 #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
 
-#if !defined(LINUX_VERSION_CODE)
-#define LINUX_VERSION_CODE LinuxVersionCode(1,2,13)
+/*
+ *	No more an option, enabled by default.
+ */
+#ifndef CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT
+#define CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT
 #endif
 
 /*
-**	Normal IO or memory mapped IO.
-**
-**	Memory mapped IO only works with linux-1.3.X
-**	If your motherboard does not work with memory mapped IO,
-**	define SCSI_NCR_IOMAPPED for PATCHLEVEL 3 too.
+**	These options are not tunable from 'make config'
 */
-
-#if	LINUX_VERSION_CODE < LinuxVersionCode(1,3,0)
-#	define	SCSI_NCR_IOMAPPED
-#endif
-
-#if	LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
-#	define	SCSI_NCR_PROC_INFO_SUPPORT
-#endif
-
-#if	LINUX_VERSION_CODE >= LinuxVersionCode(1,3,72)
-#	define SCSI_NCR_SHARE_IRQ
-#endif
+#define	SCSI_NCR_PROC_INFO_SUPPORT
+#define SCSI_NCR_SHARE_IRQ
 
 /*
 **	If you want a driver as small as possible, donnot define the 
 **	following options.
 */
-
 #define SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
 #define SCSI_NCR_DEBUG_INFO_SUPPORT
 #define SCSI_NCR_PCI_FIX_UP_SUPPORT
 #ifdef	SCSI_NCR_PROC_INFO_SUPPORT
-#	define	SCSI_NCR_PROFILE_SUPPORT
+#	ifdef	CONFIG_SCSI_NCR53C8XX_PROFILE
+#		define	SCSI_NCR_PROFILE_SUPPORT
+#	endif
 #	define	SCSI_NCR_USER_COMMAND_SUPPORT
 #	define	SCSI_NCR_USER_INFO_SUPPORT
-/* #	define	SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT */
 #endif
 
 /*==========================================================
@@ -130,25 +113,27 @@
 #define SCSI_NCR_MAX_SYNC			(40)
 
 /*
- * Allow tags from 2 to 12, default 4
+ * Allow tags from 2 to 64, default 8
  */
 #ifdef	CONFIG_SCSI_NCR53C8XX_MAX_TAGS
 #if	CONFIG_SCSI_NCR53C8XX_MAX_TAGS < 2
 #define SCSI_NCR_MAX_TAGS	(2)
-#elif	CONFIG_SCSI_NCR53C8XX_MAX_TAGS > 12
-#define SCSI_NCR_MAX_TAGS	(12)
+#elif	CONFIG_SCSI_NCR53C8XX_MAX_TAGS > 64
+#define SCSI_NCR_MAX_TAGS	(64)
 #else
 #define	SCSI_NCR_MAX_TAGS	CONFIG_SCSI_NCR53C8XX_MAX_TAGS
 #endif
 #else
-#define SCSI_NCR_MAX_TAGS	(4)
+#define SCSI_NCR_MAX_TAGS	(8)
 #endif
 
 /*
  * Allow tagged command queuing support if configured with default number 
  * of tags set to max (see above).
  */
-#ifdef	CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE
+#ifdef	CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS
+#define	SCSI_NCR_SETUP_DEFAULT_TAGS	CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS
+#elif	defined CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE
 #define	SCSI_NCR_SETUP_DEFAULT_TAGS	SCSI_NCR_MAX_TAGS
 #else
 #define	SCSI_NCR_SETUP_DEFAULT_TAGS	(0)
@@ -161,16 +146,19 @@
 #define	SCSI_NCR_IOMAPPED
 #elif defined(__alpha__) || defined(__powerpc__)
 #define	SCSI_NCR_IOMAPPED
+#elif defined(__sparc__)
+#undef SCSI_NCR_IOMAPPED
 #endif
 
 /*
  * Sync transfer frequency at startup.
- * Allow from 5Mhz to 40Mhz default 10 Mhz.
+ * Allow from 5Mhz to 40Mhz default 20 Mhz.
  */
 #ifndef	CONFIG_SCSI_NCR53C8XX_SYNC
-#define	CONFIG_SCSI_NCR53C8XX_SYNC	(5)
+#define	CONFIG_SCSI_NCR53C8XX_SYNC	(20)
 #elif	CONFIG_SCSI_NCR53C8XX_SYNC > SCSI_NCR_MAX_SYNC
-#define	SCSI_NCR_SETUP_DEFAULT_SYNC	SCSI_NCR_MAX_SYNC
+#undef	CONFIG_SCSI_NCR53C8XX_SYNC
+#define	CONFIG_SCSI_NCR53C8XX_SYNC	SCSI_NCR_MAX_SYNC
 #endif
 
 #if	CONFIG_SCSI_NCR53C8XX_SYNC == 0
@@ -245,14 +233,18 @@
 #define SCSI_NCR_ALWAYS_SIMPLE_TAG
 #define SCSI_NCR_MAX_SCATTER	(127)
 #define SCSI_NCR_MAX_TARGET	(16)
-#define SCSI_NCR_MAX_HOST	(2)
-#define SCSI_NCR_TIMEOUT_ALERT	(3*HZ)
 
+/* No need to use a too large adapter queue */
+#if SCSI_NCR_MAX_TAGS <= 32
 #define SCSI_NCR_CAN_QUEUE	(7*SCSI_NCR_MAX_TAGS)
+#else
+#define SCSI_NCR_CAN_QUEUE	(250)
+#endif
+
 #define SCSI_NCR_CMD_PER_LUN	(SCSI_NCR_MAX_TAGS)
 #define SCSI_NCR_SG_TABLESIZE	(SCSI_NCR_MAX_SCATTER)
 
-#define SCSI_NCR_TIMER_INTERVAL	((HZ+5-1)/5)
+#define SCSI_NCR_TIMER_INTERVAL	(HZ)
 
 #if 1 /* defined CONFIG_SCSI_MULTI_LUN */
 #define SCSI_NCR_MAX_LUN	(8)
@@ -268,21 +260,12 @@
 
 #if defined(HOSTS_C) || defined(MODULE)
 
-#if	LINUX_VERSION_CODE >= LinuxVersionCode(1,3,98)
 #include <scsi/scsicam.h>
-#else
-#include <linux/scsicam.h>
-#endif
 
 int ncr53c8xx_abort(Scsi_Cmnd *);
 int ncr53c8xx_detect(Scsi_Host_Template *tpnt);
 int ncr53c8xx_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
-
-#if	LINUX_VERSION_CODE >= LinuxVersionCode(1,3,98)
 int ncr53c8xx_reset(Scsi_Cmnd *, unsigned int);
-#else
-int ncr53c8xx_reset(Scsi_Cmnd *);
-#endif
 
 #ifdef MODULE
 int ncr53c8xx_release(struct Scsi_Host *);
@@ -304,34 +287,22 @@
 			sg_tablesize:   SCSI_NCR_SG_TABLESIZE,	\
 			cmd_per_lun:    SCSI_NCR_CMD_PER_LUN,	\
 			use_clustering: DISABLE_CLUSTERING} 
-  
-#elif	LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
-
-#define NCR53C8XX {	NULL, NULL, NULL, NULL,				\
-			SCSI_NCR_DRIVER_NAME,	ncr53c8xx_detect,	\
-    			ncr53c8xx_release,	NULL,	NULL,		\
-			ncr53c8xx_queue_command,ncr53c8xx_abort,	\
-			ncr53c8xx_reset, NULL,	scsicam_bios_param,	\
-			SCSI_NCR_CAN_QUEUE,	7,			\
-			SCSI_NCR_SG_TABLESIZE,	SCSI_NCR_CMD_PER_LUN,	\
-			0,	0,	DISABLE_CLUSTERING} 
 
 #else
 
-#define NCR53C8XX {	NULL, NULL,					\
+#define NCR53C8XX {	NULL, NULL, NULL, NULL,				\
 			SCSI_NCR_DRIVER_NAME,	ncr53c8xx_detect,	\
-			ncr53c8xx_release,	NULL, 	NULL, 		\
+			ncr53c8xx_release,	NULL,	NULL,		\
 			ncr53c8xx_queue_command,ncr53c8xx_abort,	\
 			ncr53c8xx_reset, NULL,	scsicam_bios_param,	\
 			SCSI_NCR_CAN_QUEUE,	7,			\
 			SCSI_NCR_SG_TABLESIZE,	SCSI_NCR_CMD_PER_LUN,	\
 			0,	0,	DISABLE_CLUSTERING} 
-
+ 
 #endif /* LINUX_VERSION_CODE */
 
 #endif /* defined(HOSTS_C) || defined(MODULE) */ 
 
-
 #ifndef HOSTS_C
 
 /*
@@ -347,13 +318,18 @@
 #error	"BIG ENDIAN byte ordering needs kernel version >= 2.1.0"
 #endif
 
-#ifdef	__powerpc__
+#if defined(__powerpc__)
 #define	inw_l2b		inw
 #define	inl_l2b		inl
 #define	outw_b2l	outw
 #define	outl_b2l	outl
+#elif defined(__sparc__)
+#define	readw_l2b	readw
+#define	readl_l2b	readl
+#define	writew_b2l	writew
+#define	writel_b2l	writel
 #else
-#error	"Support for BIG ENDIAN is only available for the PowerPC"
+#error	"Support for BIG ENDIAN is only available for PowerPC and SPARC"
 #endif
 
 #else	/* Assumed x86 or alpha */
@@ -448,6 +424,10 @@
 #define FE_LDSTR	(1<<13)
 #define FE_RAM		(1<<14)
 #define FE_CLK80	(1<<15)
+#define FE_RAM8K	(1<<16)
+#define FE_64BIT	(1<<17)
+#define FE_IO256	(1<<18)
+#define FE_NOPM		(1<<19)
 #define FE_CACHE_SET	(FE_ERL|FE_CLSE|FE_WRIE|FE_ERMP)
 #define FE_SCSI_SET	(FE_WIDE|FE_ULTRA|FE_ULTRA2|FE_DBLR|FE_QUAD|F_CLK80)
 #define FE_SPECIAL_SET	(FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM)
@@ -492,7 +472,10 @@
  {PCI_DEVICE_ID_NCR_53C875, 0x01, "875",  6, 16, 5,			\
  FE_WIDE|FE_ULTRA|FE_CLK80|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
  ,									\
- {PCI_DEVICE_ID_NCR_53C875, 0xff, "875",  6, 16, 5,			\
+ {PCI_DEVICE_ID_NCR_53C875, 0x0f, "875",  6, 16, 5,			\
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ ,									\
+ {PCI_DEVICE_ID_NCR_53C875, 0xff, "876",  6, 16, 5,			\
  FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
  ,									\
  {PCI_DEVICE_ID_NCR_53C875J,0xff, "875J", 6, 16, 5,			\
@@ -505,7 +488,8 @@
  FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
  ,									\
  {PCI_DEVICE_ID_NCR_53C896, 0xff, "896",  7, 31, 7,			\
- FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM|\
+ FE_RAM8K|FE_64BIT|FE_IO256|FE_NOPM}\
 }
 
 /*
@@ -582,94 +566,6 @@
 	1					\
 }
 
-/*
-**	Define the table of target capabilities by host and target
-**
-**	If you have problems with a scsi device, note the host unit and the
-**	corresponding target number.
-**
-**	Edit the corresponding entry of the table below and try successively:
-**		NQ7_Questionnable
-**		NQ7_IdeLike
-**
-**	This bitmap is anded with the byte 7 of inquiry data on completion of
-**	INQUIRY command.
-**	The driver never see the zeroed bits and will ignore the corresponding
-**	capabilities of the target.
-*/
-
-#define INQ7_SftRe	1
-#define INQ7_CmdQueue	(1<<1)		/* Tagged Command */
-#define INQ7_Reserved	(1<<2)
-#define INQ7_Linked	(1<<3)
-#define INQ7_Sync	(1<<4)		/* Synchronous Negotiation */
-#define INQ7_WBus16	(1<<5)
-#define INQ7_WBus32	(1<<6)
-#define INQ7_RelAdr	(1<<7)
-
-#define INQ7_IdeLike		0
-#define INQ7_Scsi1Like		INQ7_IdeLike
-#define INQ7_Perfect		0xff
-#define INQ7_Questionnable	~(INQ7_CmdQueue|INQ7_Sync)
-#define INQ7_VeryQuestionnable	\
-			~(INQ7_CmdQueue|INQ7_Sync|INQ7_WBus16|INQ7_WBus32)
-
-#define INQ7_Default		INQ7_Perfect
-
-#define NCR53C8XX_TARGET_CAPABILITIES					\
-/* Host 0 */								\
-{									\
-	{								\
-	/* Target  0 */		INQ7_Default,				\
-	/* Target  1 */		INQ7_Default,				\
-	/* Target  2 */		INQ7_Default,				\
-	/* Target  3 */		INQ7_Default,				\
-	/* Target  4 */		INQ7_Default,				\
-	/* Target  5 */		INQ7_Default,				\
-	/* Target  6 */		INQ7_Default,				\
-	/* Target  7 */		INQ7_Default,				\
-	/* Target  8 */		INQ7_Default,				\
-	/* Target  9 */		INQ7_Default,				\
-	/* Target 10 */		INQ7_Default,				\
-	/* Target 11 */		INQ7_Default,				\
-	/* Target 12 */		INQ7_Default,				\
-	/* Target 13 */		INQ7_Default,				\
-	/* Target 14 */		INQ7_Default,				\
-	/* Target 15 */		INQ7_Default,				\
-	}								\
-},									\
-/* Host 1 */								\
-{									\
-	{								\
-	/* Target  0 */		INQ7_Default,				\
-	/* Target  1 */		INQ7_Default,				\
-	/* Target  2 */		INQ7_Default,				\
-	/* Target  3 */		INQ7_Default,				\
-	/* Target  4 */		INQ7_Default,				\
-	/* Target  5 */		INQ7_Default,				\
-	/* Target  6 */		INQ7_Default,				\
-	/* Target  7 */		INQ7_Default,				\
-	/* Target  8 */		INQ7_Default,				\
-	/* Target  9 */		INQ7_Default,				\
-	/* Target 10 */		INQ7_Default,				\
-	/* Target 11 */		INQ7_Default,				\
-	/* Target 12 */		INQ7_Default,				\
-	/* Target 13 */		INQ7_Default,				\
-	/* Target 14 */		INQ7_Default,				\
-	/* Target 15 */		INQ7_Default,				\
-	}								\
-}
-
-/*
-**	Replace the proc_dir_entry of the standard ncr driver.
-*/
-
-#if	LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
-#if	defined(CONFIG_SCSI_NCR53C7xx) || !defined(CONFIG_SCSI_NCR53C8XX)
-#define PROC_SCSI_NCR53C8XX	PROC_SCSI_NCR53C7xx
-#endif
-#endif
-
 /**************** ORIGINAL CONTENT of ncrreg.h from FreeBSD ******************/
 
 /*-----------------------------------------------------------------
@@ -794,7 +690,11 @@
 /*28*/  u_int32    nc_dnad;	/* ### Next command register        */
 /*2c*/  u_int32    nc_dsp;	/* --> Script Pointer               */
 /*30*/  u_int32    nc_dsps;	/* --> Script pointer save/opcode#2 */
-/*34*/  u_int32    nc_scratcha;  /* ??? Temporary register a         */
+
+/*34*/  u_char     nc_scratcha;  /* Temporary register a            */
+/*35*/  u_char     nc_scratcha1;
+/*36*/  u_char     nc_scratcha2;
+/*37*/  u_char     nc_scratcha3;
 
 /*38*/  u_char    nc_dmode;
 	#define   BL_2    0x80  /* mod: burst length shift value +2 */
@@ -868,7 +768,20 @@
 
 /*53*/	u_char    nc_53_;
 /*54*/  u_short   nc_sodl;	/* Lowlevel: data out to scsi data  */
-/*56*/  u_short   nc_56_;
+/*56*/	u_char    nc_ccntl0;	/* Chip Control 0 (896)             */
+	#define   ENPMJ  0x80	/* Enable Phase Mismatch Jump       */
+	#define   PMJCTL 0x40	/* Phase Mismatch Jump Control      */
+	#define   ENNDJ  0x20	/* Enable Non Data PM Jump          */
+	#define   DISFC  0x10	/* Disable Auto FIFO Clear          */
+	#define   DILS   0x02	/* Disable Internal Load/Store      */
+	#define   DPR    0x01	/* Disable Pipe Req                 */
+
+/*57*/	u_char    nc_ccntl1;	/* Chip Control 1 (896)             */
+	#define   ZMOD   0x80	/* High Impedance Mode              */
+	#define   DDAC   0x08	/* Disable Dual Address Cycle       */
+	#define   XTIMOD 0x04	/* 64-bit Table Ind. Indexing Mode  */
+	#define   EXTIBMV 0x02	/* Enable 64-bit Table Ind. BMOV    */
+	#define   EXDBMV 0x01	/* Enable 64-bit Direct BMOV        */
 /*58*/  u_short   nc_sbdl;	/* Lowlevel: data from scsi data    */
 /*5a*/  u_short   nc_5a_;
 /*5c*/  u_char    nc_scr0;	/* Working register B               */
@@ -1073,10 +986,10 @@
 
 /*-----------------------------------------------------------
 **
-**	FROM_REG (reg)		  reg  = SFBR
+**	FROM_REG (reg)		  SFBR = reg
 **	<< 0 >>
 **
-**	TO_REG	 (reg)		  SFBR = reg
+**	TO_REG	 (reg)		  reg  = SFBR
 **	<< 0 >>
 **
 **	LOAD_REG (reg, data)	  reg  = <data>
@@ -1102,6 +1015,42 @@
 
 /*-----------------------------------------------------------
 **
+**	LOAD  from memory   to register.
+**	STORE from register to memory.
+**
+**-----------------------------------------------------------
+**
+**	LOAD_ABS (LEN)
+**	<<start address>>
+**
+**	LOAD_REL (LEN)        (DSA relative)
+**	<<dsa_offset>>
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_NO_FLUSH2	0x02000000
+#define SCR_DSA_REL2	0x10000000
+
+#define SCR_LOAD_R(reg, how, n) \
+        (0xe1000000 | how | (SCR_REG_OFS(REG(reg))) | (n))
+
+#define SCR_STORE_R(reg, how, n) \
+        (0xe0000000 | how | (SCR_REG_OFS(REG(reg))) | (n))
+
+#define SCR_LOAD_ABS(reg, n)	SCR_LOAD_R(reg, SCR_NO_FLUSH2, n)
+#define SCR_LOAD_REL(reg, n)	SCR_LOAD_R(reg, SCR_NO_FLUSH2|SCR_DSA_REL2, n)
+#define SCR_LOAD_ABS_F(reg, n)	SCR_LOAD_R(reg, 0, n)
+#define SCR_LOAD_REL_F(reg, n)	SCR_LOAD_R(reg, SCR_DSA_REL2, n)
+
+#define SCR_STORE_ABS(reg, n)	SCR_STORE_R(reg, SCR_NO_FLUSH2, n)
+#define SCR_STORE_REL(reg, n)	SCR_STORE_R(reg, SCR_NO_FLUSH2|SCR_DSA_REL2,n)
+#define SCR_STORE_ABS_F(reg, n)	SCR_STORE_R(reg, 0, n)
+#define SCR_STORE_REL_F(reg, n)	SCR_STORE_R(reg, SCR_DSA_REL2, n)
+
+
+/*-----------------------------------------------------------
+**
 **	Waiting for Disconnect or Reselect
 **
 **-----------------------------------------------------------
@@ -1136,7 +1085,7 @@
 **-----------------------------------------------------------
 */
 
-#define SCR_NO_OP        0x80000000
+#define SCR_NO_OP       0x80000000
 #define SCR_JUMP        0x80080000
 #define SCR_JUMPR       0x80880000
 #define SCR_CALL        0x88080000
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/pci2000.c linux/drivers/scsi/pci2000.c
--- linux.vanilla/drivers/scsi/pci2000.c	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/pci2000.c	Mon Mar 22 01:13:49 1999
@@ -0,0 +1,716 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:		pci2000i.c
+ *
+ *	Revisions	1.10	Jan-21-1999
+ *
+ *		- Fixed sign on message to reflect proper controller name.
+ *		- Added support for RAID status monitoring and control.
+ *
+ *-M*************************************************************************/
+#define PCI2000_VERSION		"1.10"
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "pci2000.h"
+#include "psi_roy.h"
+
+#include<linux/stat.h>
+
+struct proc_dir_entry Proc_Scsi_Pci2000 =
+	{ PROC_SCSI_PCI2000, 7, "pci2000", S_IFDIR | S_IRUGO | S_IXUGO, 2 };
+
+//#define DEBUG 1
+
+#ifdef DEBUG
+#define DEB(x) x
+#define STOP_HERE	{int st;for(st=0;st<100;st++){st=1;}}
+#else
+#define DEB(x)
+#define STOP_HERE
+#endif
+
+typedef struct
+	{
+	ULONG		address;
+	ULONG		length;
+	}	SCATGATH, *PSCATGATH;
+
+typedef struct
+	{
+	Scsi_Cmnd	*SCpnt;
+	SCATGATH	 scatGath[16];
+	UCHAR		 tag;
+	}	DEV2000, *PDEV2000;
+
+typedef struct
+	{
+	USHORT		 basePort;
+	USHORT		 mb0;
+	USHORT		 mb1;
+	USHORT		 mb2;
+	USHORT		 mb3;
+	USHORT		 mb4;
+	USHORT		 cmd;
+	USHORT		 tag;
+	DEV2000	 	 dev[MAX_BUS][MAX_UNITS];
+	}	ADAPTER2000, *PADAPTER2000;
+
+#define HOSTDATA(host) ((PADAPTER2000)&host->hostdata)
+
+
+static struct	Scsi_Host 	   *PsiHost[MAXADAPTER] = {NULL,};  // One for each adapter
+static			int				NumAdapters = 0;
+/****************************************************************
+ *	Name:			WaitReady	:LOCAL
+ *
+ *	Description:	Wait for controller ready.
+ *
+ *	Parameters:		padapter - Pointer adapter data structure.
+ *
+ *	Returns:		TRUE on not ready.
+ *
+ ****************************************************************/
+static int WaitReady (PADAPTER2000 padapter)
+	{
+	ULONG	timer;
+
+	timer = jiffies + TIMEOUT_COMMAND;								// calculate the timeout value
+	do	{
+		if ( !inb_p (padapter->cmd) )
+			return FALSE;
+		}	while ( timer > jiffies );									// test for timeout
+	return TRUE;
+	}
+/****************************************************************
+ *	Name:	OpDone	:LOCAL
+ *
+ *	Description:	Clean up operation and issue done to caller.
+ *
+ *	Parameters:		SCpnt	- Pointer to SCSI command structure.
+ *					status	- Caller status.
+ *
+ *	Returns:		Nothing.
+ *
+ ****************************************************************/
+static void OpDone (Scsi_Cmnd *SCpnt, ULONG status)
+	{
+	SCpnt->result = status;
+	SCpnt->scsi_done (SCpnt);
+	}
+/****************************************************************
+ *	Name:	Command		:LOCAL
+ *
+ *	Description:	Issue queued command to the PCI-2000.
+ *
+ *	Parameters:		padapter - Pointer to adapter information structure.
+ *					cmd		 - PCI-2000 command byte.
+ *
+ *	Returns:		Non-zero command tag if operation is accepted.
+ *
+ ****************************************************************/
+static UCHAR Command (PADAPTER2000 padapter, UCHAR cmd)
+	{
+	outb_p (cmd, padapter->cmd);
+	if ( WaitReady (padapter) )
+		return 0;
+
+	if ( inw_p (padapter->mb0) )
+		return 0;
+
+	return inb_p (padapter->mb1);
+	}
+/****************************************************************
+ *	Name:	BuildSgList		:LOCAL
+ *
+ *	Description:	Build the scatter gather list for controller.
+ *
+ *	Parameters:		SCpnt	 - Pointer to SCSI command structure.
+ *					padapter - Pointer to adapter information structure.
+ *					pdev	 - Pointer to adapter device structure.
+ *
+ *	Returns:		Non-zero in not scatter gather.
+ *
+ ****************************************************************/
+static int BuildSgList (Scsi_Cmnd *SCpnt, PADAPTER2000 padapter, PDEV2000 pdev)
+	{
+	int	z;
+
+	if ( SCpnt->use_sg )
+		{
+		for ( z = 0;  z < SCpnt->use_sg;  z++ )
+			{
+			pdev->scatGath[z].address = virt_to_bus (((struct scatterlist *)SCpnt->request_buffer)[z].address);
+			pdev->scatGath[z].length = ((struct scatterlist *)SCpnt->request_buffer)[z].length;
+			}
+		outl (virt_to_bus (pdev->scatGath), padapter->mb2);
+		outl ((SCpnt->use_sg << 24) | SCpnt->request_bufflen, padapter->mb3);
+		return FALSE;
+		}
+	outl (virt_to_bus (SCpnt->request_buffer), padapter->mb2);
+	outl (SCpnt->request_bufflen, padapter->mb3);
+	return TRUE;
+	}
+/*********************************************************************
+ *	Name:	PsiRaidCmd
+ *
+ *	Description:	Execute a simple command.
+ *
+ *	Parameters:		padapter - Pointer to adapter control structure.
+ *					cmd		 - Roy command byte.
+ *
+ *	Returns:		Return error status.
+ *
+ ********************************************************************/
+static int PsiRaidCmd (PADAPTER2000 padapter, char cmd)
+	{
+	if ( WaitReady (padapter) )						// test for command register ready
+		return DID_TIME_OUT;
+	outb_p (cmd, padapter->cmd);					// issue command
+	if ( WaitReady (padapter) )						// wait for adapter ready
+		return DID_TIME_OUT;
+	return DID_OK;
+	}
+/****************************************************************
+ *	Name:	Irq_Handler	:LOCAL
+ *
+ *	Description:	Interrupt handler.
+ *
+ *	Parameters:		irq		- Hardware IRQ number.
+ *					dev_id	-
+ *					regs	-
+ *
+ *	Returns:		TRUE if drive is not ready in time.
+ *
+ ****************************************************************/
+static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs)
+	{
+	struct Scsi_Host   *shost = NULL;	// Pointer to host data block
+	PADAPTER2000		padapter;		// Pointer to adapter control structure
+	PDEV2000			pdev;
+	Scsi_Cmnd		   *SCpnt;
+	UCHAR				tag = 0;
+	UCHAR				tag0;
+	ULONG				error;
+	int					pun;
+	int					bus;
+	int					z;
+
+	DEB(printk ("\npci2000 recieved interrupt "));
+	for ( z = 0; z < NumAdapters;  z++ )										// scan for interrupt to process
+		{
+		if ( PsiHost[z]->irq == (UCHAR)(irq & 0xFF) )
+			{
+			tag = inb_p (HOSTDATA(PsiHost[z])->tag);
+			if (  tag )
+				{
+				shost = PsiHost[z];
+				break;
+				}
+			}
+		}
+
+	if ( !shost )
+		{
+		DEB (printk ("\npci2000: not my interrupt"));
+		return;
+		}
+
+	padapter = HOSTDATA(shost);
+
+	tag0 = tag & 0x7F;															// mask off the error bit
+	for ( bus = 0;  bus < MAX_BUS;  bus++ )										// scan the busses
+    	{
+		for ( pun = 0;  pun < MAX_UNITS;  pun++ )								// scan the targets
+    		{
+			pdev = &padapter->dev[bus][pun];
+			if ( !pdev->tag )
+    			continue;
+			if ( pdev->tag == tag0 )											// is this it?
+				{
+				pdev->tag = 0;
+				SCpnt = pdev->SCpnt;
+				goto irqProceed;
+    			}
+			}
+    	}
+
+	outb_p (0xFF, padapter->tag);												// clear the op interrupt
+	outb_p (CMD_DONE, padapter->cmd);											// complete the op
+	return;																		// done, but, with what?
+
+irqProceed:;
+	if ( tag & ERR08_TAGGED )												// is there an error here?
+		{
+		if ( WaitReady (padapter) )
+			{
+			OpDone (SCpnt, DID_TIME_OUT << 16);
+			return;
+			}
+
+		outb_p (tag0, padapter->mb0);										// get real error code
+		outb_p (CMD_ERROR, padapter->cmd);
+		if ( WaitReady (padapter) )											// wait for controller to suck up the op
+			{
+			OpDone (SCpnt, DID_TIME_OUT << 16);
+			return;
+			}
+
+		error = inl (padapter->mb0);										// get error data
+		outb_p (0xFF, padapter->tag);										// clear the op interrupt
+		outb_p (CMD_DONE, padapter->cmd);									// complete the op
+
+		DEB (printk ("status: %lX ", error));
+		if ( error == 0x00020002 )											// is this error a check condition?
+			{
+			if ( bus )														// are we doint SCSI commands?
+				{
+				OpDone (SCpnt, (DID_OK << 16) | 2);
+				return;
+				}
+			if ( *SCpnt->cmnd == SCSIOP_TEST_UNIT_READY )
+				OpDone (SCpnt, (DRIVER_SENSE << 24) | (DID_OK << 16) | 2);	// test caller we have sense data too
+			else
+				OpDone (SCpnt, DID_ERROR << 16);
+			return;
+			}
+		OpDone (SCpnt, DID_ERROR << 16);
+		return;
+		}
+
+	outb_p (0xFF, padapter->tag);											// clear the op interrupt
+	outb_p (CMD_DONE, padapter->cmd);										// complete the op
+	OpDone (SCpnt, DID_OK << 16);
+	}
+/****************************************************************
+ *	Name:	Pci2220i_QueueCommand
+ *
+ *	Description:	Process a queued command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *					done  - Pointer to done function to call.
+ *
+ *	Returns:		Status code.
+ *
+ ****************************************************************/
+int Pci2000_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+	{
+	UCHAR		   *cdb = (UCHAR *)SCpnt->cmnd;					// Pointer to SCSI CDB
+	PADAPTER2000	padapter = HOSTDATA(SCpnt->host);			// Pointer to adapter control structure
+	int				rc		 = -1;								// command return code
+	UCHAR			bus		 = SCpnt->channel;
+	UCHAR			pun		 = SCpnt->target;
+	UCHAR			lun		 = SCpnt->lun;
+	UCHAR			cmd;
+	PDEV2000		pdev	 = &padapter->dev[bus][pun];
+
+	if ( !done )
+		{
+		printk("pci2000_queuecommand: %02X: done can't be NULL\n", *cdb);
+		return 0;
+		}
+
+	SCpnt->scsi_done = done;
+	pdev->SCpnt = SCpnt;  									// Save this command data
+
+	if ( WaitReady (padapter) )
+		{
+		rc = DID_ERROR;
+		goto finished;
+		}
+
+	outw_p (pun | (lun << 8), padapter->mb0);
+
+	if ( bus )
+		{
+		DEB (if(*cdb) printk ("\nCDB: %X-  %X %X %X %X %X %X %X %X %X %X ", SCpnt->cmd_len, cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9]));
+		DEB (if(*cdb) printk ("\ntimeout_per_command: %d, timeout_total: %d, timeout: %d, internal_timout: %d", SCpnt->timeout_per_command,
+							  SCpnt->timeout_total, SCpnt->timeout, SCpnt->internal_timeout));
+		outl (SCpnt->timeout_per_command, padapter->mb1);
+		outb_p (CMD_SCSI_TIMEOUT, padapter->cmd);
+		if ( WaitReady (padapter) )
+			{
+			rc = DID_ERROR;
+			goto finished;
+			}
+
+		outw_p (pun | (lun << 8), padapter->mb0);
+		outw_p (SCpnt->cmd_len << 8, padapter->mb0 + 2);
+		outl (virt_to_bus (cdb), padapter->mb1);
+		if ( BuildSgList (SCpnt, padapter, pdev) )
+			cmd = CMD_SCSI_THRU;
+		else
+			cmd = CMD_SCSI_THRU_SG;
+		if ( (pdev->tag = Command (padapter, cmd)) == 0 )
+			rc = DID_TIME_OUT;
+		goto finished;
+		}
+	else
+		{
+		if ( lun )
+			{
+			rc = DID_BAD_TARGET;
+			goto finished;
+			}
+		}
+
+	switch ( *cdb )
+		{
+		case SCSIOP_INQUIRY:   					// inquiry CDB
+			if ( cdb[2] == SC_MY_RAID )
+				{
+				switch ( cdb[3] ) 
+					{
+					case MY_SCSI_REBUILD:
+						OpDone (SCpnt, PsiRaidCmd (padapter, CMD_RAID_REBUILD) << 16);
+						return 0;
+					case MY_SCSI_ALARMMUTE:
+						OpDone (SCpnt, PsiRaidCmd (padapter, CMD_RAID_MUTE) << 16);
+						return 0;
+					case MY_SCSI_DEMOFAIL:
+						OpDone (SCpnt, PsiRaidCmd (padapter, CMD_RAID_FAIL) << 16);
+						return 0;
+					default:
+						if ( SCpnt->use_sg )
+							{
+							rc = DID_ERROR;
+							goto finished;
+							}
+						else
+							outl (virt_to_bus (SCpnt->request_buffer), padapter->mb2);
+						outl (cdb[5], padapter->mb0);
+						outl (cdb[3], padapter->mb3);
+						cmd = CMD_DASD_RAID_RQ;
+						break;
+					}
+				break;
+				}
+			
+			if ( SCpnt->use_sg )
+				{
+				outl (virt_to_bus (((struct scatterlist *)(SCpnt->request_buffer))->address), padapter->mb2);
+				}
+			else
+				{
+				outl (virt_to_bus (SCpnt->request_buffer), padapter->mb2);
+				}
+			outl (SCpnt->request_bufflen, padapter->mb3);
+			cmd = CMD_DASD_SCSI_INQ;
+			break;
+
+		case SCSIOP_TEST_UNIT_READY:			// test unit ready CDB
+			outl (virt_to_bus (SCpnt->sense_buffer), padapter->mb2);
+			outl (sizeof (SCpnt->sense_buffer), padapter->mb3);
+			cmd = CMD_TEST_READY;
+			break;
+
+		case SCSIOP_READ_CAPACITY:			  	// read capctiy CDB
+			if ( SCpnt->use_sg )
+				{
+				outl (virt_to_bus (((struct scatterlist *)(SCpnt->request_buffer))->address), padapter->mb2);
+				}
+			else
+				{
+				outl (virt_to_bus (SCpnt->request_buffer), padapter->mb2);
+				}
+			outl (8, padapter->mb3);
+			cmd = CMD_DASD_CAP;
+			break;
+		case SCSIOP_VERIFY:						// verify CDB
+			outw_p ((USHORT)cdb[8] | ((USHORT)cdb[7] << 8), padapter->mb0 + 2);
+			outl (XSCSI2LONG (&cdb[2]), padapter->mb1);
+			cmd = CMD_READ_SG;
+			break;
+		case SCSIOP_READ:						// read10 CDB
+			outw_p ((USHORT)cdb[8] | ((USHORT)cdb[7] << 8), padapter->mb0 + 2);
+			outl (XSCSI2LONG (&cdb[2]), padapter->mb1);
+			if ( BuildSgList (SCpnt, padapter, pdev) )
+				cmd = CMD_READ;
+			else
+				cmd = CMD_READ_SG;
+			break;
+		case SCSIOP_READ6:						// read6  CDB
+			outw_p (cdb[4], padapter->mb0 + 2);
+			outl ((SCSI2LONG (&cdb[1])) & 0x001FFFFF, padapter->mb1);
+			if ( BuildSgList (SCpnt, padapter, pdev) )
+				cmd = CMD_READ;
+			else
+				cmd = CMD_READ_SG;
+			break;
+		case SCSIOP_WRITE:						// write10 CDB
+			outw_p ((USHORT)cdb[8] | ((USHORT)cdb[7] << 8), padapter->mb0 + 2);
+			outl (XSCSI2LONG (&cdb[2]), padapter->mb1);
+			if ( BuildSgList (SCpnt, padapter, pdev) )
+				cmd = CMD_WRITE;
+			else
+				cmd = CMD_WRITE_SG;
+			break;
+		case SCSIOP_WRITE6:						// write6  CDB
+			outw_p (cdb[4], padapter->mb0 + 2);
+			outl ((SCSI2LONG (&cdb[1])) & 0x001FFFFF, padapter->mb1);
+			if ( BuildSgList (SCpnt, padapter, pdev) )
+				cmd = CMD_WRITE;
+			else
+				cmd = CMD_WRITE_SG;
+			break;
+		case SCSIOP_START_STOP_UNIT:
+			cmd = CMD_EJECT_MEDIA;
+			break;
+		case SCSIOP_MEDIUM_REMOVAL:
+			switch ( cdb[4] )
+				{
+				case 0:
+					cmd = CMD_UNLOCK_DOOR;
+					break;
+				case 1:
+					cmd = CMD_LOCK_DOOR;
+					break;
+				default:
+					cmd = 0;
+					break;
+				}
+			if ( cmd )
+				break;
+		default:
+			DEB (printk ("pci2220i_queuecommand: Unsupported command %02X\n", *cdb));
+			OpDone (SCpnt, DID_ERROR << 16);
+			return 0;
+		}
+
+	if ( (pdev->tag = Command (padapter, cmd)) == 0 )
+		rc = DID_TIME_OUT;
+finished:;
+	if ( rc != -1 )
+		OpDone (SCpnt, rc << 16);
+	return 0;
+	}
+/****************************************************************
+ *	Name:	internal_done :LOCAL
+ *
+ *	Description:	Done handler for non-queued commands
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *
+ *	Returns:		Nothing.
+ *
+ ****************************************************************/
+static void internal_done (Scsi_Cmnd * SCpnt)
+	{
+	SCpnt->SCp.Status++;
+	}
+/****************************************************************
+ *	Name:	Pci2220i_Command
+ *
+ *	Description:	Process a command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *
+ *	Returns:		Status code.
+ *
+ ****************************************************************/
+int Pci2000_Command (Scsi_Cmnd *SCpnt)
+	{
+	DEB(printk("pci2000_command: ..calling pci2000_queuecommand\n"));
+
+	Pci2000_QueueCommand (SCpnt, internal_done);
+
+    SCpnt->SCp.Status = 0;
+	while (!SCpnt->SCp.Status)
+		barrier ();
+	return SCpnt->result;
+	}
+/****************************************************************
+ *	Name:	Pci2220i_Detect
+ *
+ *	Description:	Detect and initialize our boards.
+ *
+ *	Parameters:		tpnt - Pointer to SCSI host template structure.
+ *
+ *	Returns:		Number of adapters found.
+ *
+ ****************************************************************/
+int Pci2000_Detect (Scsi_Host_Template *tpnt)
+	{
+	int					pci_index = 0;
+	struct Scsi_Host   *pshost;
+	PADAPTER2000	    padapter;
+	int					z, zz;
+	int					setirq;
+
+	if ( pcibios_present () )
+		{
+		for ( pci_index = 0;  pci_index <= MAXADAPTER;  ++pci_index )
+			{
+			UCHAR	pci_bus, pci_device_fn;
+
+			if ( pcibios_find_device (VENDOR_PSI, DEVICE_ROY_1, pci_index, &pci_bus, &pci_device_fn) != 0 )
+				break;
+
+			pshost = scsi_register (tpnt, sizeof(ADAPTER2000));
+			padapter = HOSTDATA(pshost);
+
+			pcibios_read_config_word (pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &padapter->basePort);
+			padapter->basePort &= 0xFFFE;
+			DEB (printk ("\nBase Regs = %#04X", padapter->basePort));			// get the base I/O port address
+			padapter->mb0	= padapter->basePort + RTR_MAILBOX;		   			// get the 32 bit mail boxes
+			padapter->mb1	= padapter->basePort + RTR_MAILBOX + 4;
+			padapter->mb2	= padapter->basePort + RTR_MAILBOX + 8;
+			padapter->mb3	= padapter->basePort + RTR_MAILBOX + 12;
+			padapter->mb4	= padapter->basePort + RTR_MAILBOX + 16;
+			padapter->cmd	= padapter->basePort + RTR_LOCAL_DOORBELL;			// command register
+			padapter->tag	= padapter->basePort + RTR_PCI_DOORBELL;			// tag/response register
+
+			if ( WaitReady (padapter) )
+				goto unregister;
+			outb_p (0x84, padapter->mb0);
+			outb_p (CMD_SPECIFY, padapter->cmd);
+			if ( WaitReady (padapter) )
+				goto unregister;
+
+			pcibios_read_config_byte (pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pshost->irq);
+			setirq = 1;
+			for ( z = 0;  z < pci_index;  z++ )									// scan for shared interrupts
+				{
+				if ( PsiHost[z]->irq == pshost->irq )							// if shared then, don't posses
+					setirq = 0;
+				}
+			if ( setirq )														// if not shared, posses
+				{
+				if ( request_irq (pshost->irq, Irq_Handler, 0, "pci2000", NULL) )
+					{
+					printk ("Unable to allocate IRQ for PSI-2000 controller.\n");
+					goto unregister;
+					}
+				}
+			PsiHost[pci_index]	= pshost;										// save SCSI_HOST pointer
+
+			pshost->unique_id	= padapter->basePort;
+			pshost->max_id		= 16;
+			pshost->max_channel	= 1;
+
+			for ( zz = 0;  zz < MAX_BUS;  zz++ )
+				for ( z = 0; z < MAX_UNITS;  z++ )
+					padapter->dev[zz][z].tag = 0;
+			
+			printk("\nPSI-2000 Intelligent Storage SCSI CONTROLLER: at I/O = %X  IRQ = %d\n", padapter->basePort, pshost->irq);
+			printk("Version %s, Compiled %s %s\n\n", PCI2000_VERSION,  __DATE__, __TIME__);
+			continue;
+unregister:;
+			scsi_unregister (pshost);
+			}
+		}
+	NumAdapters = pci_index;
+	return pci_index;
+	}
+/****************************************************************
+ *	Name:	Pci2220i_Abort
+ *
+ *	Description:	Process the Abort command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *
+ *	Returns:		Allways snooze.
+ *
+ ****************************************************************/
+int Pci2000_Abort (Scsi_Cmnd *SCpnt)
+	{
+	DEB (printk ("pci2000_abort\n"));
+	return SCSI_ABORT_SNOOZE;
+	}
+/****************************************************************
+ *	Name:	Pci2220i_Reset
+ *
+ *	Description:	Process the Reset command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *					flags - Flags about the reset command
+ *
+ *	Returns:		No active command at this time, so this means
+ *					that each time we got some kind of response the
+ *					last time through.  Tell the mid-level code to
+ *					request sense information in order to decide what
+ *					to do next.
+ *
+ ****************************************************************/
+int Pci2000_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+	{
+	return SCSI_RESET_PUNT;
+	}
+
+#include "sd.h"
+
+/****************************************************************
+ *	Name:	Pci2220i_BiosParam
+ *
+ *	Description:	Process the biosparam request from the SCSI manager to
+ *					return C/H/S data.
+ *
+ *	Parameters:		disk - Pointer to SCSI disk structure.
+ *					dev	 - Major/minor number from kernel.
+ *					geom - Pointer to integer array to place geometry data.
+ *
+ *	Returns:		zero.
+ *
+ ****************************************************************/
+int Pci2000_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[])
+	{
+	PADAPTER2000	    padapter;
+
+	padapter = HOSTDATA(disk->device->host);
+
+	if ( WaitReady (padapter) )
+		return 0;
+	outb_p (disk->device->id, padapter->mb0);
+	outb_p (CMD_GET_PARMS, padapter->cmd);
+	if ( WaitReady (padapter) )
+		return 0;
+
+	geom[0] = inb_p (padapter->mb2 + 3);
+	geom[1] = inb_p (padapter->mb2 + 2);
+	geom[2] = inw_p (padapter->mb2);
+	return 0;
+	}
+
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = PCI2000;
+
+#include "scsi_module.c"
+#endif
+
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/pci2000.h linux/drivers/scsi/pci2000.h
--- linux.vanilla/drivers/scsi/pci2000.h	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/pci2000.h	Thu Mar  4 13:17:54 1999
@@ -0,0 +1,226 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:		pci2000.h
+ *
+ *	Description:	Header file for the SCSI driver for the PCI-2000
+ *					interface card.
+ *
+ *-M*************************************************************************/
+#ifndef _PCI2000_H
+#define _PCI2000_H
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+#ifndef	PSI_EIDE_SCSIOP
+#define	PSI_EIDE_SCSIOP	1
+
+/************************************************/
+/*		definition of standard data types		*/
+/************************************************/
+#define	CHAR	char
+#define	UCHAR	unsigned char
+#define	SHORT	short
+#define	USHORT	unsigned short
+#define	BOOL	long
+#define	LONG	long
+#define	ULONG	unsigned long
+#define	VOID	void
+
+typedef	CHAR	*PCHAR;
+typedef	UCHAR	*PUCHAR;
+typedef	SHORT	*PSHORT;
+typedef	USHORT	*PUSHORT;
+typedef	BOOL	*PBOOL;
+typedef	LONG	*PLONG;
+typedef	ULONG	*PULONG;
+typedef	VOID	*PVOID;
+
+
+/************************************************/
+/*		Misc. macros			 				*/
+/************************************************/
+#define ANY2SCSI(up, p)					\
+((UCHAR *)up)[0] = (((ULONG)(p)) >> 8);	\
+((UCHAR *)up)[1] = ((ULONG)(p));
+
+#define SCSI2LONG(up)					\
+( (((long)*(((UCHAR *)up))) << 16)		\
++ (((long)(((UCHAR *)up)[1])) << 8)		\
++ ((long)(((UCHAR *)up)[2])) )
+
+#define XANY2SCSI(up, p)				\
+((UCHAR *)up)[0] = ((long)(p)) >> 24;	\
+((UCHAR *)up)[1] = ((long)(p)) >> 16;	\
+((UCHAR *)up)[2] = ((long)(p)) >> 8;	\
+((UCHAR *)up)[3] = ((long)(p));
+
+#define XSCSI2LONG(up)					\
+( (((long)(((UCHAR *)up)[0])) << 24)	\
++ (((long)(((UCHAR *)up)[1])) << 16)	\
++ (((long)(((UCHAR *)up)[2])) <<  8)	\
++ ((long)(((UCHAR *)up)[3])) )
+
+/************************************************/
+/*		SCSI CDB operation codes 				*/
+/************************************************/
+#define SCSIOP_TEST_UNIT_READY		0x00
+#define SCSIOP_REZERO_UNIT			0x01
+#define SCSIOP_REWIND				0x01
+#define SCSIOP_REQUEST_BLOCK_ADDR	0x02
+#define SCSIOP_REQUEST_SENSE		0x03
+#define SCSIOP_FORMAT_UNIT			0x04
+#define SCSIOP_READ_BLOCK_LIMITS	0x05
+#define SCSIOP_REASSIGN_BLOCKS		0x07
+#define SCSIOP_READ6				0x08
+#define SCSIOP_RECEIVE				0x08
+#define SCSIOP_WRITE6				0x0A
+#define SCSIOP_PRINT				0x0A
+#define SCSIOP_SEND					0x0A
+#define SCSIOP_SEEK6				0x0B
+#define SCSIOP_TRACK_SELECT			0x0B
+#define SCSIOP_SLEW_PRINT			0x0B
+#define SCSIOP_SEEK_BLOCK			0x0C
+#define SCSIOP_PARTITION			0x0D
+#define SCSIOP_READ_REVERSE			0x0F
+#define SCSIOP_WRITE_FILEMARKS		0x10
+#define SCSIOP_FLUSH_BUFFER			0x10
+#define SCSIOP_SPACE				0x11
+#define SCSIOP_INQUIRY				0x12
+#define SCSIOP_VERIFY6				0x13
+#define SCSIOP_RECOVER_BUF_DATA		0x14
+#define SCSIOP_MODE_SELECT			0x15
+#define SCSIOP_RESERVE_UNIT			0x16
+#define SCSIOP_RELEASE_UNIT			0x17
+#define SCSIOP_COPY					0x18
+#define SCSIOP_ERASE				0x19
+#define SCSIOP_MODE_SENSE			0x1A
+#define SCSIOP_START_STOP_UNIT		0x1B
+#define SCSIOP_STOP_PRINT			0x1B
+#define SCSIOP_LOAD_UNLOAD			0x1B
+#define SCSIOP_RECEIVE_DIAGNOSTIC	0x1C
+#define SCSIOP_SEND_DIAGNOSTIC		0x1D
+#define SCSIOP_MEDIUM_REMOVAL		0x1E
+#define SCSIOP_READ_CAPACITY		0x25
+#define SCSIOP_READ					0x28
+#define SCSIOP_WRITE				0x2A
+#define SCSIOP_SEEK					0x2B
+#define SCSIOP_LOCATE				0x2B
+#define SCSIOP_WRITE_VERIFY			0x2E
+#define SCSIOP_VERIFY				0x2F
+#define SCSIOP_SEARCH_DATA_HIGH		0x30
+#define SCSIOP_SEARCH_DATA_EQUAL	0x31
+#define SCSIOP_SEARCH_DATA_LOW		0x32
+#define SCSIOP_SET_LIMITS			0x33
+#define SCSIOP_READ_POSITION		0x34
+#define SCSIOP_SYNCHRONIZE_CACHE	0x35
+#define SCSIOP_COMPARE				0x39
+#define SCSIOP_COPY_COMPARE			0x3A
+#define SCSIOP_WRITE_DATA_BUFF		0x3B
+#define SCSIOP_READ_DATA_BUFF		0x3C
+#define SCSIOP_CHANGE_DEFINITION	0x40
+#define SCSIOP_READ_SUB_CHANNEL		0x42
+#define SCSIOP_READ_TOC				0x43
+#define SCSIOP_READ_HEADER			0x44
+#define SCSIOP_PLAY_AUDIO			0x45
+#define SCSIOP_PLAY_AUDIO_MSF		0x47
+#define SCSIOP_PLAY_TRACK_INDEX		0x48
+#define SCSIOP_PLAY_TRACK_RELATIVE	0x49
+#define SCSIOP_PAUSE_RESUME			0x4B
+#define SCSIOP_LOG_SELECT			0x4C
+#define SCSIOP_LOG_SENSE			0x4D
+#define SCSIOP_MODE_SELECT10		0x55
+#define SCSIOP_MODE_SENSE10			0x5A
+#define SCSIOP_LOAD_UNLOAD_SLOT		0xA6
+#define SCSIOP_MECHANISM_STATUS		0xBD
+#define SCSIOP_READ_CD				0xBE
+
+// SCSI read capacity structure
+typedef	struct _READ_CAPACITY_DATA
+	{
+	ULONG blks;				/* total blocks (converted to little endian) */
+	ULONG blksiz;			/* size of each (converted to little endian) */
+	}	READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA;
+
+// SCSI inquiry data
+typedef struct _INQUIRYDATA
+	{
+	UCHAR DeviceType			:5;
+	UCHAR DeviceTypeQualifier	:3;
+	UCHAR DeviceTypeModifier	:7;
+	UCHAR RemovableMedia		:1;
+    UCHAR Versions;
+    UCHAR ResponseDataFormat;
+    UCHAR AdditionalLength;
+    UCHAR Reserved[2];
+	UCHAR SoftReset				:1;
+	UCHAR CommandQueue			:1;
+	UCHAR Reserved2				:1;
+	UCHAR LinkedCommands		:1;
+	UCHAR Synchronous			:1;
+	UCHAR Wide16Bit				:1;
+	UCHAR Wide32Bit				:1;
+	UCHAR RelativeAddressing	:1;
+    UCHAR VendorId[8];
+    UCHAR ProductId[16];
+    UCHAR ProductRevisionLevel[4];
+    UCHAR VendorSpecific[20];
+    UCHAR Reserved3[40];
+	}	INQUIRYDATA, *PINQUIRYDATA;
+
+#endif
+
+// function prototypes
+int Pci2000_Detect			(Scsi_Host_Template *tpnt);
+int Pci2000_Command			(Scsi_Cmnd *SCpnt);
+int Pci2000_QueueCommand	(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *));
+int Pci2000_Abort			(Scsi_Cmnd *SCpnt);
+int Pci2000_Reset			(Scsi_Cmnd *SCpnt, unsigned int flags);
+int Pci2000_BiosParam		(Disk *disk, kdev_t dev, int geom[]);
+
+#ifndef NULL
+	#define NULL 0
+#endif
+
+extern struct proc_dir_entry Proc_Scsi_Pci2000;
+
+#define PCI2000 { NULL, NULL,							\
+			&Proc_Scsi_Pci2000,/* proc_dir_entry */		\
+			NULL,		                				\
+			"PCI-2000 SCSI Intelligent Disk Controller",\
+			Pci2000_Detect,								\
+			NULL,										\
+			NULL,	 									\
+			Pci2000_Command,							\
+			Pci2000_QueueCommand,						\
+			Pci2000_Abort,								\
+			Pci2000_Reset,								\
+			NULL,										\
+			Pci2000_BiosParam,                 			\
+			16, 										\
+			-1, 										\
+			16,		 									\
+			1, 											\
+			0, 											\
+			0, 											\
+			DISABLE_CLUSTERING }
+
+#endif
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/pci2220i.c linux/drivers/scsi/pci2220i.c
--- linux.vanilla/drivers/scsi/pci2220i.c	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/pci2220i.c	Mon Mar 22 01:18:20 1999
@@ -0,0 +1,819 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:		pci2220i.c
+ *
+ *	Description:	SCSI driver for the PCI2220I EIDE interface card.
+ *
+ *-M*************************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "pci2220i.h"
+#include "psi_dale.h"
+
+#include<linux/stat.h>
+
+struct proc_dir_entry Proc_Scsi_Pci2220i =
+	{ PROC_SCSI_PCI2220I, 7, "pci2220i", S_IFDIR | S_IRUGO | S_IXUGO, 2 };
+
+//#define DEBUG 1
+
+#ifdef DEBUG
+#define DEB(x) x
+#define STOP_HERE	{int st;for(st=0;st<100;st++){st=1;}}
+#else
+#define DEB(x)
+#define STOP_HERE
+#endif
+
+#define MAXADAPTER 4	/* Increase this and the sizes of the arrays below, if you need more. */
+
+#define	MAX_BUS_MASTER_BLOCKS	1		// This is the maximum we can bus master for (1024 bytes)
+
+#define	PORT_DATA				0
+#define	PORT_ERROR				1
+#define	PORT_SECTOR_COUNT		2
+#define	PORT_LBA_0				3
+#define	PORT_LBA_8				4
+#define	PORT_LBA_16				5
+#define	PORT_LBA_24				6
+#define	PORT_STAT_CMD			7
+#define	PORT_STAT_SEL			8
+#define	PORT_FAIL				9
+#define	PORT_ALT_STAT		   	10
+
+typedef struct
+	{
+	UCHAR		   	device;				// device code
+	UCHAR			byte6;				// device select register image
+	UCHAR			spigot;				// spigot number
+	UCHAR			sparebyte;			// placeholder
+	USHORT			sectors;			// number of sectors per track
+	USHORT			heads;				// number of heads
+	USHORT			cylinders;			// number of cylinders for this device
+	USHORT			spareword;			// placeholder
+	ULONG			blocks;				// number of blocks on device
+	}	OUR_DEVICE, *POUR_DEVICE;
+
+typedef struct
+	{
+	USHORT		 ports[12];
+	USHORT		 regDmaDesc;					// address of the DMA discriptor register for direction of transfer
+	USHORT		 regDmaCmdStat;					// Byte #1 of DMA command status register
+	USHORT		 regDmaAddrPci;					// 32 bit register for PCI address of DMA
+	USHORT		 regDmaAddrLoc;					// 32 bit register for local bus address of DMA
+	USHORT		 regDmaCount;					// 32 bit register for DMA transfer count
+	USHORT		 regDmaMode;						// 32 bit register for DMA mode control
+	USHORT		 regRemap;						// 32 bit local space remap
+	USHORT		 regDesc;						// 32 bit local region descriptor
+	USHORT		 regRange;						// 32 bit local range
+	USHORT		 regIrqControl;					// 16 bit Interrupt enable/disable and status
+	USHORT		 regScratchPad;					// scratch pad I/O base address
+	USHORT		 regBase;						// Base I/O register for data space
+	USHORT		 basePort;						// PLX base I/O port
+	USHORT		 timingMode;					// timing mode currently set for adapter
+	ULONG		 timingAddress;					// address to use on adapter for current timing mode
+	OUR_DEVICE	 device[4];
+	IDE_STRUCT	 ide;
+	ULONG		 startSector;
+	USHORT		 sectorCount;
+	Scsi_Cmnd	*SCpnt;
+	VOID		*buffer;
+	USHORT		 expectingIRQ;
+	USHORT		 readPhase;
+	}	ADAPTER2220I, *PADAPTER2220I;
+
+#define HOSTDATA(host) ((PADAPTER2220I)&host->hostdata)
+
+
+static struct	Scsi_Host 	   *PsiHost[MAXADAPTER] = {NULL,};  // One for each adapter
+static			int				NumAdapters = 0;
+static			IDENTIFY_DATA	identifyData;
+static			SETUP			DaleSetup;
+
+/****************************************************************
+ *	Name:	WriteData	:LOCAL
+ *
+ *	Description:	Write data to device.
+ *
+ *	Parameters:		padapter - Pointer adapter data structure.
+ *
+ *	Returns:		TRUE if drive does not assert DRQ in time.
+ *
+ ****************************************************************/
+static int WriteData (PADAPTER2220I padapter)
+	{
+	ULONG	timer;
+	USHORT *pports = padapter->ports;
+
+	timer = jiffies + TIMEOUT_DRQ;								// calculate the timeout value
+	do  {
+		if ( inb_p (pports[PORT_STAT_CMD]) & IDE_STATUS_DRQ )
+			{
+			outb_p (0, padapter->regDmaDesc);							// write operation
+			outl (padapter->timingAddress, padapter->regDmaAddrLoc);
+			outl (virt_to_bus (padapter->buffer), padapter->regDmaAddrPci);
+			outl ((ULONG)padapter->ide.ide.ide[2] * (ULONG)512, padapter->regDmaCount);
+			outb_p (1, padapter->regDmaMode);							// interrupts off
+			outb_p (0x03, padapter->regDmaCmdStat);						// kick the DMA engine in gear
+			return 0;
+			}
+		}	while ( timer > jiffies );									// test for timeout
+
+	padapter->ide.ide.ides.cmd = 0;									// null out the command byte
+	return 1;
+	}
+/****************************************************************
+ *	Name:	IdeCmd	:LOCAL
+ *
+ *	Description:	Process a queued command from the SCSI manager.
+ *
+ *	Parameters:		padapter - Pointer adapter data structure.
+ *
+ *	Returns:		Zero if no error or status register contents on error.
+ *
+ ****************************************************************/
+static UCHAR IdeCmd (PADAPTER2220I padapter)
+	{
+	ULONG	timer;
+	USHORT *pports = padapter->ports;
+	UCHAR	status;
+
+	outb_p (padapter->ide.ide.ides.spigot, pports[PORT_STAT_SEL]);	// select the spigot
+	outb_p (padapter->ide.ide.ide[6], pports[PORT_LBA_24]);			// select the drive
+	timer = jiffies + TIMEOUT_READY;							// calculate the timeout value
+	DEB(printk ("\npci2220i Issueing new command: 0x%X",padapter->ide.ide.ides.cmd));
+	do  {
+		status = inb_p (padapter->ports[PORT_STAT_CMD]);
+		if ( status & IDE_STATUS_DRDY )
+			{
+			outb_p (padapter->ide.ide.ide[2], pports[PORT_SECTOR_COUNT]);
+			outb_p (padapter->ide.ide.ide[3], pports[PORT_LBA_0]);
+			outb_p (padapter->ide.ide.ide[4], pports[PORT_LBA_8]);
+			outb_p (padapter->ide.ide.ide[5], pports[PORT_LBA_16]);
+			padapter->expectingIRQ = 1;
+			outb_p (padapter->ide.ide.ide[7], pports[PORT_STAT_CMD]);
+
+			if ( padapter->ide.ide.ides.cmd == IDE_CMD_WRITE_MULTIPLE )
+				return (WriteData (padapter));
+			return 0;
+			}
+		}	while ( timer > jiffies );									// test for timeout
+
+	padapter->ide.ide.ides.cmd = 0;									// null out the command byte
+	return status;
+	}
+/****************************************************************
+ *	Name:	SetupTransfer	:LOCAL
+ *
+ *	Description:	Setup a data transfer command.
+ *
+ *	Parameters:		padapter - Pointer adapter data structure.
+ *					drive	 - Drive/head register upper nibble only.
+ *
+ *	Returns:		TRUE if no data to transfer.
+ *
+ ****************************************************************/
+static int SetupTransfer (PADAPTER2220I padapter, UCHAR drive)
+	{
+	if ( padapter->sectorCount )
+		{
+		*(ULONG *)padapter->ide.ide.ides.lba = padapter->startSector;
+		padapter->ide.ide.ide[6] |= drive;
+//		padapter->ide.ide.ides.sectors = ( padapter->sectorCount > SECTORSXFER ) ? SECTORSXFER : padapter->sectorCount;
+		padapter->ide.ide.ides.sectors = ( padapter->sectorCount > MAX_BUS_MASTER_BLOCKS ) ? MAX_BUS_MASTER_BLOCKS : padapter->sectorCount;
+		padapter->sectorCount -= padapter->ide.ide.ides.sectors;	// bump the start and count for next xfer
+		padapter->startSector += padapter->ide.ide.ides.sectors;
+		return 0;
+		}
+	else
+		{
+		padapter->ide.ide.ides.cmd = 0;								// null out the command byte
+		padapter->SCpnt = NULL;
+		return 1;
+		}
+	}
+/****************************************************************
+ *	Name:	DecodeError	:LOCAL
+ *
+ *	Description:	Decode and process device errors.
+ *
+ *	Parameters:		pshost - Pointer to host data block.
+ *					status - Status register code.
+ *
+ *	Returns:		The driver status code.
+ *
+ ****************************************************************/
+static ULONG DecodeError (struct Scsi_Host *pshost, UCHAR status)
+	{
+	PADAPTER2220I	padapter = HOSTDATA(pshost);
+	UCHAR			error;
+
+	padapter->expectingIRQ = 0;
+	padapter->SCpnt = NULL;
+	if ( status & IDE_STATUS_WRITE_FAULT )
+		{
+		return DID_PARITY << 16;
+		}
+	if ( status & IDE_STATUS_BUSY )
+		return DID_BUS_BUSY << 16;
+
+	error = inb_p (padapter->ports[PORT_ERROR]);
+	DEB(printk ("\npci2220i error register: %x", error));
+	switch ( error )
+		{
+		case IDE_ERROR_AMNF:
+		case IDE_ERROR_TKONF:
+		case IDE_ERROR_ABRT:
+		case IDE_ERROR_IDFN:
+		case IDE_ERROR_UNC:
+		case IDE_ERROR_BBK:
+		default:
+			return DID_ERROR << 16;
+		}
+	return DID_ERROR << 16;
+	}
+/****************************************************************
+ *	Name:	Irq_Handler	:LOCAL
+ *
+ *	Description:	Interrupt handler.
+ *
+ *	Parameters:		irq		- Hardware IRQ number.
+ *					dev_id	-
+ *					regs	-
+ *
+ *	Returns:		TRUE if drive is not ready in time.
+ *
+ ****************************************************************/
+static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs)
+	{
+	struct Scsi_Host   *shost = NULL;	// Pointer to host data block
+	PADAPTER2220I		padapter;		// Pointer to adapter control structure
+	USHORT		 	   *pports;			// I/O port array
+	Scsi_Cmnd		   *SCpnt;
+	UCHAR				status;
+	int					z;
+
+//	DEB(printk ("\npci2220i recieved interrupt\n"));
+
+	for ( z = 0; z < NumAdapters;  z++ )								// scan for interrupt to process
+		{
+		if ( PsiHost[z]->irq == (UCHAR)(irq & 0xFF) )
+			{
+			if ( inw_p (HOSTDATA(PsiHost[z])->regIrqControl) & 0x8000 )
+				{
+				shost = PsiHost[z];
+				break;
+				}
+			}
+		}
+
+	if ( !shost )
+		{
+		DEB (printk ("\npci2220i: not my interrupt"));
+		return;
+		}
+
+	padapter = HOSTDATA(shost);
+	pports = padapter->ports;
+	SCpnt = padapter->SCpnt;
+
+	if ( !padapter->expectingIRQ )
+		{
+		DEB(printk ("\npci2220i Unsolicited interrupt\n"));
+		return;
+		}
+	padapter->expectingIRQ = 0;
+
+	status = inb_p (padapter->ports[PORT_STAT_CMD]);					// read the device status
+	if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) )
+		goto irqerror;
+
+	switch ( padapter->ide.ide.ides.cmd )								// decide how to handle the interrupt
+		{
+		case IDE_CMD_READ_MULTIPLE:
+			if ( padapter->readPhase == 1 )								// is this a bus master channel complete?
+				{
+				DEB(printk ("\npci2220i processing read interrupt cleanup"));
+				outb_p (0x08, padapter->regDmaCmdStat);					// cancel interrupt from DMA engine
+				padapter->buffer += padapter->ide.ide.ides.sectors * 512;
+				if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) )
+					{
+					SCpnt->result = DID_OK << 16;
+					padapter->SCpnt = NULL;
+					SCpnt->scsi_done (SCpnt);
+					return;
+					}
+				padapter->readPhase = 0;
+				if ( !(status = IdeCmd (padapter)) )
+					{
+					DEB (printk ("\npci2220i interrupt complete, waiting for another"));
+					return;
+					}
+				}
+			if ( status & IDE_STATUS_DRQ )
+				{
+				DEB(printk ("\npci2220i processing read interrupt start bus master cycle"));
+				outb_p (8, padapter->regDmaDesc); 				   		// read operation
+				padapter->readPhase = 1;
+				padapter->expectingIRQ = 1;
+				outl   (padapter->timingAddress, padapter->regDmaAddrLoc);
+				outl   (virt_to_bus (padapter->buffer), padapter->regDmaAddrPci);
+				outl   ((ULONG)padapter->ide.ide.ides.sectors * (ULONG)512, padapter->regDmaCount);
+				outb_p (5, padapter->regDmaMode);				   		// interrupt enable/disable
+				outb_p (0x03, padapter->regDmaCmdStat);			   		// kick the DMA engine in gear
+				return;
+				}
+			break;
+
+		case IDE_CMD_WRITE_MULTIPLE:
+			DEB(printk ("\npci2220i processing write interrupt cleanup"));
+			padapter->buffer += padapter->ide.ide.ides.sectors * 512;
+			if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) )
+				{
+				SCpnt->result = DID_OK << 16;
+				padapter->SCpnt = NULL;
+				SCpnt->scsi_done (SCpnt);
+				return;
+				}
+			if ( !(status = IdeCmd (padapter)) )
+				{
+				DEB (printk ("\npci2220i interrupt complete, waiting for another"));
+				return;
+				}
+			break;
+
+		case IDE_COMMAND_IDENTIFY:
+			{
+			PINQUIRYDATA	pinquiryData  = SCpnt->request_buffer;
+
+			DEB(printk ("\npci2220i processing verify interrupt cleanup"));
+			if ( status & IDE_STATUS_DRQ )
+				{
+				insw (pports[PORT_DATA], &identifyData, sizeof (identifyData) >> 1);
+
+				memset (pinquiryData, 0, SCpnt->request_bufflen);		// Zero INQUIRY data structure.
+				pinquiryData->DeviceType = 0;
+				pinquiryData->Versions = 2;
+				pinquiryData->AdditionalLength = 35 - 4;
+
+				// Fill in vendor identification fields.
+				for ( z = 0;  z < 20;  z += 2 )
+					{
+					pinquiryData->VendorId[z]	  = ((UCHAR *)identifyData.ModelNumber)[z + 1];
+					pinquiryData->VendorId[z + 1] = ((UCHAR *)identifyData.ModelNumber)[z];
+					}
+
+				// Initialize unused portion of product id.
+				for ( z = 0;  z < 4;  z++ )
+					pinquiryData->ProductId[12 + z] = ' ';
+
+				// Move firmware revision from IDENTIFY data to
+				// product revision in INQUIRY data.
+				for ( z = 0;  z < 4;  z += 2 )
+					{
+					pinquiryData->ProductRevisionLevel[z]	 = ((UCHAR *)identifyData.FirmwareRevision)[z + 1];
+					pinquiryData->ProductRevisionLevel[z + 1] = ((UCHAR *)identifyData.FirmwareRevision)[z];
+					}
+
+				SCpnt->result = DID_OK << 16;
+				padapter->SCpnt = NULL;
+				SCpnt->scsi_done (SCpnt);
+				return;
+				}
+			break;
+			}
+
+		default:
+			DEB(printk ("\npci2220i no real process here!"));
+			SCpnt->result = DID_OK << 16;
+			padapter->SCpnt = NULL;
+			SCpnt->scsi_done (SCpnt);
+			return;
+		}
+
+irqerror:;
+	DEB(printk ("\npci2220i error  Device Status: %X\n", status));
+	SCpnt->result = DecodeError (shost, status);
+	SCpnt->scsi_done (SCpnt);
+	}
+/****************************************************************
+ *	Name:	Pci2220i_QueueCommand
+ *
+ *	Description:	Process a queued command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *					done  - Pointer to done function to call.
+ *
+ *	Returns:		Status code.
+ *
+ ****************************************************************/
+int Pci2220i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+	{
+	UCHAR		   *cdb = (UCHAR *)SCpnt->cmnd;					// Pointer to SCSI CDB
+	PADAPTER2220I	padapter = HOSTDATA(SCpnt->host);			// Pointer to adapter control structure
+	POUR_DEVICE		pdev	 = &padapter->device[SCpnt->target];// Pointer to device information
+	UCHAR			rc;											// command return code
+
+	SCpnt->scsi_done = done;
+	padapter->ide.ide.ides.spigot = pdev->spigot;
+	padapter->buffer = SCpnt->request_buffer;
+	if (done)
+		{
+		if ( !pdev->device || SCpnt->lun )
+			{
+			SCpnt->result = DID_BAD_TARGET << 16;
+			done (SCpnt);
+			return 0;
+			}
+		}
+	else
+		{
+		printk("pci2220i_queuecommand: %02X: done can't be NULL\n", *cdb);
+		return 0;
+		}
+
+	DEB (if(*cdb) printk ("\nCDB: %X-  %X %X %X %X %X %X %X %X %X %X ", SCpnt->cmd_len, cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9]));
+	switch ( *cdb )
+		{
+		case SCSIOP_INQUIRY:   					// inquiry CDB
+			{
+			padapter->ide.ide.ide[6] = pdev->byte6;
+			padapter->ide.ide.ides.cmd = IDE_COMMAND_IDENTIFY;
+			break;
+			}
+
+		case SCSIOP_TEST_UNIT_READY:			// test unit ready CDB
+			SCpnt->result = DID_OK << 16;
+			done (SCpnt);
+			return 0;
+
+		case SCSIOP_READ_CAPACITY:			  	// read capctiy CDB
+			{
+			PREAD_CAPACITY_DATA	pdata = (PREAD_CAPACITY_DATA)SCpnt->request_buffer;
+
+			pdata->blksiz = 0x20000;
+			XANY2SCSI ((UCHAR *)&pdata->blks, pdev->blocks);
+			SCpnt->result = DID_OK << 16;
+			done (SCpnt);
+			return 0;
+			}
+
+		case SCSIOP_VERIFY:						// verify CDB
+			*(ULONG *)padapter->ide.ide.ides.lba = XSCSI2LONG (&cdb[2]);
+			padapter->ide.ide.ide[6] |= pdev->byte6;
+			padapter->ide.ide.ide[2] = (UCHAR)((USHORT)cdb[8] | ((USHORT)cdb[7] << 8));
+			padapter->ide.ide.ides.cmd = IDE_COMMAND_VERIFY;
+			break;
+
+		case SCSIOP_READ:						// read10 CDB
+			padapter->startSector = XSCSI2LONG (&cdb[2]);
+			padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8);
+			SetupTransfer (padapter, pdev->byte6);
+			padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE;
+			padapter->readPhase = 0;
+			break;
+
+		case SCSIOP_READ6:						// read6  CDB
+			padapter->startSector = SCSI2LONG (&cdb[1]);
+			padapter->sectorCount = cdb[4];
+			SetupTransfer (padapter, pdev->byte6);
+			padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE;
+			padapter->readPhase = 0;
+			break;
+
+		case SCSIOP_WRITE:						// write10 CDB
+			padapter->startSector = XSCSI2LONG (&cdb[2]);
+			padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8);
+			SetupTransfer (padapter, pdev->byte6);
+			padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE;
+			break;
+		case SCSIOP_WRITE6:						// write6  CDB
+			padapter->startSector = SCSI2LONG (&cdb[1]);
+			padapter->sectorCount = cdb[4];
+			SetupTransfer (padapter, pdev->byte6);
+			padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE;
+			break;
+
+		default:
+			DEB (printk ("pci2220i_queuecommand: Unsupported command %02X\n", *cdb));
+			SCpnt->result = DID_ERROR << 16;
+			done (SCpnt);
+			return 0;
+		}
+
+	padapter->SCpnt = SCpnt;  									// Save this command data
+
+	rc = IdeCmd (padapter);
+	if ( rc )
+		{
+		padapter->expectingIRQ = 0;
+		DEB (printk ("pci2220i_queuecommand: %02X, %02X: Device failed to respond for command\n", *cdb, padapter->ide.ide.ides.cmd));
+		SCpnt->result = DID_ERROR << 16;
+		done (SCpnt);
+		return 0;
+		}
+	if ( padapter->ide.ide.ides.cmd == IDE_CMD_WRITE_MULTIPLE )
+		{
+		if ( WriteData (padapter) )
+			{
+			padapter->expectingIRQ = 0;
+			DEB (printk ("pci2220i_queuecommand: %02X, %02X: Device failed to accept data\n", *cdb, padapter->ide.ide.ides.cmd));
+			SCpnt->result = DID_ERROR << 16;
+			done (SCpnt);
+			return 0;
+			}
+		}
+	DEB (printk("  now waiting for initial interrupt "));
+	return 0;
+	}
+
+static void internal_done(Scsi_Cmnd * SCpnt)
+	{
+	SCpnt->SCp.Status++;
+	}
+/****************************************************************
+ *	Name:	Pci2220i_Command
+ *
+ *	Description:	Process a command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *
+ *	Returns:		Status code.
+ *
+ ****************************************************************/
+int Pci2220i_Command (Scsi_Cmnd *SCpnt)
+	{
+	DEB(printk("pci2220i_command: ..calling pci2220i_queuecommand\n"));
+
+	Pci2220i_QueueCommand (SCpnt, internal_done);
+
+    SCpnt->SCp.Status = 0;
+	while (!SCpnt->SCp.Status)
+		barrier ();
+	return SCpnt->result;
+	}
+/****************************************************************
+ *	Name:			ReadFlash
+ *
+ *	Description:	Read information from controller Flash memory.
+ *
+ *	Parameters:		hostdata - Pointer to host interface data structure.
+ *					pdata	 - Pointer to data structures.
+ *					base	 - base address in Flash.
+ *					length	 - lenght of data space in bytes.
+ *
+ *	Returns:		Nothing.
+ *
+ ****************************************************************/
+VOID ReadFlash (PADAPTER2220I hostdata, VOID *pdata, ULONG base, ULONG length)
+	{
+	ULONG	 oldremap;
+	UCHAR	 olddesc;
+	ULONG	 z;
+	UCHAR	*pd = (UCHAR *)pdata;
+
+	oldremap = inl (hostdata->regRemap);									// save values to restore later
+	olddesc  = inb_p (hostdata->regDesc);
+
+	outl (base | 1, hostdata->regRemap);									// remap to Flash space as specified
+	outb_p (0x40, hostdata->regDesc);										// describe remap region as 8 bit
+	for ( z = 0;  z < length;  z++)											// get "length" data count
+		*pd++ = inb_p (hostdata->regBase + z);								// read in the data
+
+	outl (oldremap, hostdata->regRemap);									// restore remap register values
+	outb_p (olddesc, hostdata->regDesc);
+	}
+
+/****************************************************************
+ *	Name:	Pci2220i_Detect
+ *
+ *	Description:	Detect and initialize our boards.
+ *
+ *	Parameters:		tpnt - Pointer to SCSI host template structure.
+ *
+ *	Returns:		Number of adapters found.
+ *
+ ****************************************************************/
+int Pci2220i_Detect (Scsi_Host_Template *tpnt)
+	{
+	int					pci_index = 0;
+	struct Scsi_Host   *pshost;
+	PADAPTER2220I	    hostdata;
+	ULONG				modearray[] = {DALE_DATA_MODE2, DALE_DATA_MODE3, DALE_DATA_MODE4, DALE_DATA_MODE4P};
+	int					unit;
+	int					z;
+	int					setirq;
+
+	if ( pcibios_present () )
+		{
+		for ( pci_index = 0;  pci_index <= MAXADAPTER;  ++pci_index )
+			{
+			UCHAR	pci_bus, pci_device_fn;
+
+			if ( pcibios_find_device (VENDOR_PSI, DEVICE_DALE_1, pci_index, &pci_bus, &pci_device_fn) != 0 )
+				break;
+
+			pshost = scsi_register (tpnt, sizeof(ADAPTER2220I));
+			hostdata = HOSTDATA(pshost);
+
+			pcibios_read_config_word (pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &hostdata->basePort);
+			hostdata->basePort &= 0xFFFE;
+			DEB (printk ("\nBase Regs = %#04X", hostdata->basePort));
+			hostdata->regRemap		= hostdata->basePort + RTR_LOCAL_REMAP;				// 32 bit local space remap
+			DEB (printk (" %#04X", hostdata->regRemap));
+			hostdata->regDesc		= hostdata->basePort + RTR_REGIONS;	  				// 32 bit local region descriptor
+			DEB (printk (" %#04X", hostdata->regDesc));
+			hostdata->regRange		= hostdata->basePort + RTR_LOCAL_RANGE;				// 32 bit local range
+			DEB (printk (" %#04X", hostdata->regRange));
+			hostdata->regIrqControl	= hostdata->basePort + RTR_INT_CONTROL_STATUS;		// 16 bit interupt control and status
+			DEB (printk (" %#04X", hostdata->regIrqControl));
+			hostdata->regScratchPad	= hostdata->basePort + RTR_MAILBOX;	  				// 16 byte scratchpad I/O base address
+			DEB (printk (" %#04X", hostdata->regScratchPad));
+
+			pcibios_read_config_word (pci_bus, pci_device_fn, PCI_BASE_ADDRESS_2, &hostdata->regBase);
+			hostdata->regBase &= 0xFFFE;
+			for ( z = 0;  z < 9;  z++ )													// build regester address array
+				hostdata->ports[z] = hostdata->regBase + 0x80 + (z * 4);
+			hostdata->ports[PORT_FAIL] = hostdata->regBase + REG_FAIL;
+			hostdata->ports[PORT_ALT_STAT] = hostdata->regBase + REG_ALT_STAT;
+			DEB (printk ("\nPorts ="));
+			DEB (for (z=0;z<11;z++) printk(" %#04X", hostdata->ports[z]););
+
+			hostdata->regDmaDesc	= hostdata->regBase + RTL_DMA1_DESC_PTR;			// address of the DMA discriptor register for direction of transfer
+			DEB (printk ("\nDMA Regs = %#04X", hostdata->regDmaDesc));
+			hostdata->regDmaCmdStat	= hostdata->regBase + RTL_DMA_COMMAND_STATUS + 1;	// Byte #1 of DMA command status register
+			DEB (printk (" %#04X", hostdata->regDmaCmdStat));
+			hostdata->regDmaAddrPci	= hostdata->regBase + RTL_DMA1_PCI_ADDR;			// 32 bit register for PCI address of DMA
+			DEB (printk (" %#04X", hostdata->regDmaAddrPci));
+			hostdata->regDmaAddrLoc	= hostdata->regBase + RTL_DMA1_LOCAL_ADDR;			// 32 bit register for local bus address of DMA
+			DEB (printk (" %#04X", hostdata->regDmaAddrLoc));
+			hostdata->regDmaCount	= hostdata->regBase + RTL_DMA1_COUNT;				// 32 bit register for DMA transfer count
+			DEB (printk (" %#04X", hostdata->regDmaCount));
+			hostdata->regDmaMode	= hostdata->regBase + RTL_DMA1_MODE + 1;			// 32 bit register for DMA mode control
+			DEB (printk (" %#04X", hostdata->regDmaMode));
+
+			if ( !inb_p (hostdata->regScratchPad + DALE_NUM_DRIVES) )					// if no devices on this board
+				goto unregister;
+
+			pcibios_read_config_byte (pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pshost->irq);
+			setirq = 1;
+			for ( z = 0;  z < pci_index;  z++ )											// scan for shared interrupts
+				{
+				if ( PsiHost[z]->irq == pshost->irq )						// if shared then, don't posses
+					setirq = 0;
+				}
+			if ( setirq )																// if not shared, posses
+				{
+				if ( request_irq (pshost->irq, Irq_Handler, 0, "pci2220i", NULL) )
+					{
+					printk ("Unable to allocate IRQ for PSI-2220I controller.\n");
+					goto unregister;
+					}
+				}
+			PsiHost[pci_index]	= pshost;												// save SCSI_HOST pointer
+
+			pshost->unique_id	= hostdata->regBase;
+			pshost->max_id		= 4;
+
+			outb_p (0x01, hostdata->regRange);											// fix our range register because other drivers want to tromp on it
+
+			hostdata->timingMode	= inb_p (hostdata->regScratchPad + DALE_TIMING_MODE);
+			hostdata->timingAddress	= modearray[hostdata->timingMode - 2];
+			ReadFlash (hostdata, &DaleSetup, DALE_FLASH_SETUP, sizeof (SETUP));
+
+			for ( z = 0;  z < inb_p (hostdata->regScratchPad + DALE_NUM_DRIVES);  ++z )
+				{
+				unit = inb_p (hostdata->regScratchPad + DALE_CHANNEL_DEVICE_0 + z) & 0x0F;
+				hostdata->device[unit].device	 = inb_p (hostdata->regScratchPad + DALE_SCRATH_DEVICE_0 + unit);
+				hostdata->device[unit].byte6	 = (UCHAR)(((unit & 1) << 4) | 0xE0);
+				hostdata->device[unit].spigot	 = (UCHAR)(1 << (unit >> 1));
+				hostdata->device[unit].sectors	 = DaleSetup.setupDevice[unit].sectors;
+				hostdata->device[unit].heads	 = DaleSetup.setupDevice[unit].heads;
+				hostdata->device[unit].cylinders = DaleSetup.setupDevice[unit].cylinders;
+				hostdata->device[unit].blocks	 = DaleSetup.setupDevice[unit].blocks;
+				DEB (printk ("\nHOSTDATA->device    = %X", hostdata->device[unit].device));
+				DEB (printk ("\n          byte6     = %X", hostdata->device[unit].byte6));
+				DEB (printk ("\n          spigot    = %X", hostdata->device[unit].spigot));
+				DEB (printk ("\n          sectors   = %X", hostdata->device[unit].sectors));
+				DEB (printk ("\n          heads     = %X", hostdata->device[unit].heads));
+				DEB (printk ("\n          cylinders = %X", hostdata->device[unit].cylinders));
+				DEB (printk ("\n          blocks    = %lX", hostdata->device[unit].blocks));
+				}
+
+			printk("\nPSI-2220I EIDE CONTROLLER: at I/O = %X/%X  IRQ = %d\n", hostdata->basePort, hostdata->regBase, pshost->irq);
+			printk("(C) 1997 Perceptive Solutions, Inc. All rights reserved\n\n");
+
+			NumAdapters++;
+			continue;
+unregister:;
+			scsi_unregister (pshost);
+			}
+		}
+	return NumAdapters;
+	}
+/****************************************************************
+ *	Name:	Pci2220i_Abort
+ *
+ *	Description:	Process the Abort command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *
+ *	Returns:		Allways snooze.
+ *
+ ****************************************************************/
+int Pci2220i_Abort (Scsi_Cmnd *SCpnt)
+	{
+	DEB (printk ("pci2220i_abort\n"));
+	return SCSI_ABORT_SNOOZE;
+	}
+/****************************************************************
+ *	Name:	Pci2220i_Reset
+ *
+ *	Description:	Process the Reset command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *					flags - Flags about the reset command
+ *
+ *	Returns:		No active command at this time, so this means
+ *					that each time we got some kind of response the
+ *					last time through.  Tell the mid-level code to
+ *					request sense information in order to decide what
+ *					to do next.
+ *
+ ****************************************************************/
+int Pci2220i_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+	{
+	return SCSI_RESET_PUNT;
+	}
+
+#include "sd.h"
+
+/****************************************************************
+ *	Name:	Pci2220i_BiosParam
+ *
+ *	Description:	Process the biosparam request from the SCSI manager to
+ *					return C/H/S data.
+ *
+ *	Parameters:		disk - Pointer to SCSI disk structure.
+ *					dev	 - Major/minor number from kernel.
+ *					geom - Pointer to integer array to place geometry data.
+ *
+ *	Returns:		zero.
+ *
+ ****************************************************************/
+int Pci2220i_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[])
+	{
+	POUR_DEVICE	pdev;
+
+	pdev = &(HOSTDATA(disk->device->host)->device[disk->device->id]);
+
+	geom[0] = pdev->heads;
+	geom[1] = pdev->sectors;
+	geom[2] = pdev->cylinders;
+	return 0;
+	}
+
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = PCI2220I;
+
+#include "scsi_module.c"
+#endif
+
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/pci2220i.h linux/drivers/scsi/pci2220i.h
--- linux.vanilla/drivers/scsi/pci2220i.h	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/pci2220i.h	Thu Mar  4 13:13:10 1999
@@ -0,0 +1,345 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:		pci2220i.h
+ *
+ *	Description:	Header file for the SCSI driver for the PCI2220I
+ *					EIDE interface card.
+ *
+ *-M*************************************************************************/
+
+#ifndef _PCI2220I_H
+#define _PCI2220I_H
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+#ifndef	PSI_EIDE_SCSIOP
+#define	PSI_EIDE_SCSIOP	1
+
+/************************************************/
+/*		Some defines that we like 				*/
+/************************************************/
+#define	CHAR		char
+#define	UCHAR		unsigned char
+#define	SHORT		short
+#define	USHORT		unsigned short
+#define	BOOL		unsigned short
+#define	LONG		long
+#define	ULONG		unsigned long
+#define	VOID		void
+
+/************************************************/
+/*		Timeout konstants		 				*/
+/************************************************/
+#define	TIMEOUT_READY				10		// 100 mSec
+#define	TIMEOUT_DRQ					40		// 400 mSec
+
+/************************************************/
+/*		Misc. macros			 				*/
+/************************************************/
+#define ANY2SCSI(up, p)					\
+((UCHAR *)up)[0] = (((ULONG)(p)) >> 8);	\
+((UCHAR *)up)[1] = ((ULONG)(p));
+
+#define SCSI2LONG(up)					\
+( (((long)*(((UCHAR *)up))) << 16)		\
++ (((long)(((UCHAR *)up)[1])) << 8)		\
++ ((long)(((UCHAR *)up)[2])) )
+
+#define XANY2SCSI(up, p)				\
+((UCHAR *)up)[0] = ((long)(p)) >> 24;	\
+((UCHAR *)up)[1] = ((long)(p)) >> 16;	\
+((UCHAR *)up)[2] = ((long)(p)) >> 8;	\
+((UCHAR *)up)[3] = ((long)(p));
+
+#define XSCSI2LONG(up)					\
+( (((long)(((UCHAR *)up)[0])) << 24)	\
++ (((long)(((UCHAR *)up)[1])) << 16)	\
++ (((long)(((UCHAR *)up)[2])) <<  8)	\
++ ((long)(((UCHAR *)up)[3])) )
+
+/************************************************/
+/*		SCSI CDB operation codes 				*/
+/************************************************/
+#define SCSIOP_TEST_UNIT_READY		0x00
+#define SCSIOP_REZERO_UNIT			0x01
+#define SCSIOP_REWIND				0x01
+#define SCSIOP_REQUEST_BLOCK_ADDR	0x02
+#define SCSIOP_REQUEST_SENSE		0x03
+#define SCSIOP_FORMAT_UNIT			0x04
+#define SCSIOP_READ_BLOCK_LIMITS	0x05
+#define SCSIOP_REASSIGN_BLOCKS		0x07
+#define SCSIOP_READ6				0x08
+#define SCSIOP_RECEIVE				0x08
+#define SCSIOP_WRITE6				0x0A
+#define SCSIOP_PRINT				0x0A
+#define SCSIOP_SEND					0x0A
+#define SCSIOP_SEEK6				0x0B
+#define SCSIOP_TRACK_SELECT			0x0B
+#define SCSIOP_SLEW_PRINT			0x0B
+#define SCSIOP_SEEK_BLOCK			0x0C
+#define SCSIOP_PARTITION			0x0D
+#define SCSIOP_READ_REVERSE			0x0F
+#define SCSIOP_WRITE_FILEMARKS		0x10
+#define SCSIOP_FLUSH_BUFFER			0x10
+#define SCSIOP_SPACE				0x11
+#define SCSIOP_INQUIRY				0x12
+#define SCSIOP_VERIFY6				0x13
+#define SCSIOP_RECOVER_BUF_DATA		0x14
+#define SCSIOP_MODE_SELECT			0x15
+#define SCSIOP_RESERVE_UNIT			0x16
+#define SCSIOP_RELEASE_UNIT			0x17
+#define SCSIOP_COPY					0x18
+#define SCSIOP_ERASE				0x19
+#define SCSIOP_MODE_SENSE			0x1A
+#define SCSIOP_START_STOP_UNIT		0x1B
+#define SCSIOP_STOP_PRINT			0x1B
+#define SCSIOP_LOAD_UNLOAD			0x1B
+#define SCSIOP_RECEIVE_DIAGNOSTIC	0x1C
+#define SCSIOP_SEND_DIAGNOSTIC		0x1D
+#define SCSIOP_MEDIUM_REMOVAL		0x1E
+#define SCSIOP_READ_CAPACITY		0x25
+#define SCSIOP_READ					0x28
+#define SCSIOP_WRITE				0x2A
+#define SCSIOP_SEEK					0x2B
+#define SCSIOP_LOCATE				0x2B
+#define SCSIOP_WRITE_VERIFY			0x2E
+#define SCSIOP_VERIFY				0x2F
+#define SCSIOP_SEARCH_DATA_HIGH		0x30
+#define SCSIOP_SEARCH_DATA_EQUAL	0x31
+#define SCSIOP_SEARCH_DATA_LOW		0x32
+#define SCSIOP_SET_LIMITS			0x33
+#define SCSIOP_READ_POSITION		0x34
+#define SCSIOP_SYNCHRONIZE_CACHE	0x35
+#define SCSIOP_COMPARE				0x39
+#define SCSIOP_COPY_COMPARE			0x3A
+#define SCSIOP_WRITE_DATA_BUFF		0x3B
+#define SCSIOP_READ_DATA_BUFF		0x3C
+#define SCSIOP_CHANGE_DEFINITION	0x40
+#define SCSIOP_READ_SUB_CHANNEL		0x42
+#define SCSIOP_READ_TOC				0x43
+#define SCSIOP_READ_HEADER			0x44
+#define SCSIOP_PLAY_AUDIO			0x45
+#define SCSIOP_PLAY_AUDIO_MSF		0x47
+#define SCSIOP_PLAY_TRACK_INDEX		0x48
+#define SCSIOP_PLAY_TRACK_RELATIVE	0x49
+#define SCSIOP_PAUSE_RESUME			0x4B
+#define SCSIOP_LOG_SELECT			0x4C
+#define SCSIOP_LOG_SENSE			0x4D
+#define SCSIOP_MODE_SELECT10		0x55
+#define SCSIOP_MODE_SENSE10			0x5A
+#define SCSIOP_LOAD_UNLOAD_SLOT		0xA6
+#define SCSIOP_MECHANISM_STATUS		0xBD
+#define SCSIOP_READ_CD				0xBE
+
+// IDE command definitions
+#define IDE_COMMAND_ATAPI_RESET		0x08
+#define IDE_COMMAND_READ			0x20
+#define IDE_COMMAND_WRITE			0x30
+#define IDE_COMMAND_RECALIBRATE		0x10
+#define IDE_COMMAND_SEEK			0x70
+#define IDE_COMMAND_SET_PARAMETERS	0x91
+#define IDE_COMMAND_VERIFY			0x40
+#define IDE_COMMAND_ATAPI_PACKET	0xA0
+#define IDE_COMMAND_ATAPI_IDENTIFY	0xA1
+#define	IDE_CMD_READ_MULTIPLE		0xC4
+#define	IDE_CMD_WRITE_MULTIPLE		0xC5
+#define	IDE_CMD_SET_MULTIPLE		0xC6
+#define IDE_COMMAND_WRITE_DMA		0xCA
+#define IDE_COMMAND_READ_DMA		0xC8
+#define IDE_COMMAND_IDENTIFY		0xEC
+
+// IDE status definitions
+#define IDE_STATUS_ERROR			0x01
+#define IDE_STATUS_INDEX			0x02
+#define IDE_STATUS_CORRECTED_ERROR	0x04
+#define IDE_STATUS_DRQ				0x08
+#define IDE_STATUS_DSC				0x10
+#define	IDE_STATUS_WRITE_FAULT		0x20
+#define IDE_STATUS_DRDY				0x40
+#define IDE_STATUS_BUSY				0x80
+
+// IDE error definitions
+#define	IDE_ERROR_AMNF				0x01
+#define	IDE_ERROR_TKONF				0x02
+#define	IDE_ERROR_ABRT				0x04
+#define	IDE_ERROR_MCR				0x08
+#define	IDE_ERROR_IDFN				0x10
+#define	IDE_ERROR_MC				0x20
+#define	IDE_ERROR_UNC				0x40
+#define	IDE_ERROR_BBK				0x80
+
+//	IDE interface structure
+typedef struct _IDE_STRUCT
+	{
+	union
+		{
+		UCHAR	ide[9];
+		struct
+			{
+			USHORT	data;
+			UCHAR	sectors;
+			UCHAR	lba[4];
+			UCHAR	cmd;
+			UCHAR	spigot;
+			}	ides;
+		} ide;
+	}	IDE_STRUCT;
+
+// SCSI read capacity structure
+typedef	struct _READ_CAPACITY_DATA
+	{
+	ULONG blks;				/* total blocks (converted to little endian) */
+	ULONG blksiz;			/* size of each (converted to little endian) */
+	}	READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA;
+
+// SCSI inquiry data
+typedef struct _INQUIRYDATA
+	{
+	UCHAR DeviceType			:5;
+	UCHAR DeviceTypeQualifier	:3;
+	UCHAR DeviceTypeModifier	:7;
+	UCHAR RemovableMedia		:1;
+    UCHAR Versions;
+    UCHAR ResponseDataFormat;
+    UCHAR AdditionalLength;
+    UCHAR Reserved[2];
+	UCHAR SoftReset				:1;
+	UCHAR CommandQueue			:1;
+	UCHAR Reserved2				:1;
+	UCHAR LinkedCommands		:1;
+	UCHAR Synchronous			:1;
+	UCHAR Wide16Bit				:1;
+	UCHAR Wide32Bit				:1;
+	UCHAR RelativeAddressing	:1;
+    UCHAR VendorId[8];
+    UCHAR ProductId[16];
+    UCHAR ProductRevisionLevel[4];
+    UCHAR VendorSpecific[20];
+    UCHAR Reserved3[40];
+	}	INQUIRYDATA, *PINQUIRYDATA;
+
+// IDE IDENTIFY data
+typedef struct _IDENTIFY_DATA
+	{
+    USHORT GeneralConfiguration;            // 00
+    USHORT NumberOfCylinders;               // 02
+    USHORT Reserved1;                       // 04
+    USHORT NumberOfHeads;                   // 06
+    USHORT UnformattedBytesPerTrack;        // 08
+    USHORT UnformattedBytesPerSector;       // 0A
+    USHORT SectorsPerTrack;                 // 0C
+    USHORT VendorUnique1[3];                // 0E
+    USHORT SerialNumber[10];                // 14
+    USHORT BufferType;                      // 28
+    USHORT BufferSectorSize;                // 2A
+    USHORT NumberOfEccBytes;                // 2C
+    USHORT FirmwareRevision[4];             // 2E
+    USHORT ModelNumber[20];                 // 36
+    UCHAR  MaximumBlockTransfer;            // 5E
+    UCHAR  VendorUnique2;                   // 5F
+    USHORT DoubleWordIo;                    // 60
+    USHORT Capabilities;                    // 62
+    USHORT Reserved2;                       // 64
+    UCHAR  VendorUnique3;                   // 66
+    UCHAR  PioCycleTimingMode;              // 67
+    UCHAR  VendorUnique4;                   // 68
+    UCHAR  DmaCycleTimingMode;              // 69
+    USHORT TranslationFieldsValid:1;        // 6A
+    USHORT Reserved3:15;
+    USHORT NumberOfCurrentCylinders;        // 6C
+    USHORT NumberOfCurrentHeads;            // 6E
+    USHORT CurrentSectorsPerTrack;          // 70
+    ULONG  CurrentSectorCapacity;           // 72
+    USHORT Reserved4[197];                  // 76
+	}	IDENTIFY_DATA, *PIDENTIFY_DATA;
+
+// Identify data without the Reserved4.
+typedef struct _IDENTIFY_DATA2 {
+    USHORT GeneralConfiguration;            // 00
+    USHORT NumberOfCylinders;               // 02
+    USHORT Reserved1;                       // 04
+    USHORT NumberOfHeads;                   // 06
+    USHORT UnformattedBytesPerTrack;        // 08
+    USHORT UnformattedBytesPerSector;       // 0A
+    USHORT SectorsPerTrack;                 // 0C
+    USHORT VendorUnique1[3];                // 0E
+    USHORT SerialNumber[10];                // 14
+    USHORT BufferType;                      // 28
+    USHORT BufferSectorSize;                // 2A
+    USHORT NumberOfEccBytes;                // 2C
+    USHORT FirmwareRevision[4];             // 2E
+    USHORT ModelNumber[20];                 // 36
+    UCHAR  MaximumBlockTransfer;            // 5E
+    UCHAR  VendorUnique2;                   // 5F
+    USHORT DoubleWordIo;                    // 60
+    USHORT Capabilities;                    // 62
+    USHORT Reserved2;                       // 64
+    UCHAR  VendorUnique3;                   // 66
+    UCHAR  PioCycleTimingMode;              // 67
+    UCHAR  VendorUnique4;                   // 68
+    UCHAR  DmaCycleTimingMode;              // 69
+	USHORT TranslationFieldsValid:1;     	// 6A
+    USHORT Reserved3:15;
+    USHORT NumberOfCurrentCylinders;        // 6C
+    USHORT NumberOfCurrentHeads;            // 6E
+    USHORT CurrentSectorsPerTrack;          // 70
+    ULONG  CurrentSectorCapacity;           // 72
+	}	IDENTIFY_DATA2, *PIDENTIFY_DATA2;
+
+#endif	// PSI_EIDE_SCSIOP
+
+// function prototypes
+int Pci2220i_Detect			(Scsi_Host_Template *tpnt);
+int Pci2220i_Command			(Scsi_Cmnd *SCpnt);
+int Pci2220i_QueueCommand	(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *));
+int Pci2220i_Abort			(Scsi_Cmnd *SCpnt);
+int Pci2220i_Reset			(Scsi_Cmnd *SCpnt, unsigned int flags);
+int Pci2220i_BiosParam		(Disk *disk, kdev_t dev, int geom[]);
+
+#ifndef NULL
+	#define NULL 0
+#endif
+
+extern struct proc_dir_entry Proc_Scsi_Pci2220i;
+
+#define PCI2220I { NULL, NULL,						\
+			&Proc_Scsi_Pci2220i,/* proc_dir_entry */	\
+			NULL,		                			\
+			"PCI-2220I EIDE Disk Controller",		\
+			Pci2220i_Detect,							\
+			NULL,									\
+			NULL,	 								\
+			Pci2220i_Command,						\
+			Pci2220i_QueueCommand,					\
+			Pci2220i_Abort,							\
+			Pci2220i_Reset,							\
+			NULL,									\
+			Pci2220i_BiosParam,                 		\
+			1, 										\
+			-1, 									\
+			SG_NONE,		 						\
+			1, 										\
+			0, 										\
+			0, 										\
+			DISABLE_CLUSTERING }
+
+#endif
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/psi240i.c linux/drivers/scsi/psi240i.c
--- linux.vanilla/drivers/scsi/psi240i.c	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/psi240i.c	Thu Mar  4 13:15:50 1999
@@ -0,0 +1,717 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PSI-240I device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:		psi240i.c
+ *
+ *	Description:	SCSI driver for the PSI240I EIDE interface card.
+ *
+ *-M*************************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "psi240i.h"
+#include "psi_chip.h"
+
+#include<linux/stat.h>
+
+struct proc_dir_entry Proc_Scsi_Psi240i =
+	{ PROC_SCSI_PSI240I, 7, "psi240i", S_IFDIR | S_IRUGO | S_IXUGO, 2 };
+
+//#define DEBUG 1
+
+#ifdef DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+#define MAXBOARDS 2	/* Increase this and the sizes of the arrays below, if you need more. */
+
+#define	PORT_DATA				0
+#define	PORT_ERROR				1
+#define	PORT_SECTOR_COUNT		2
+#define	PORT_LBA_0				3
+#define	PORT_LBA_8				4
+#define	PORT_LBA_16				5
+#define	PORT_LBA_24				6
+#define	PORT_STAT_CMD			7
+#define	PORT_SEL_FAIL			8
+#define	PORT_IRQ_STATUS			9
+#define	PORT_ADDRESS			10
+#define	PORT_FAIL				11
+#define	PORT_ALT_STAT		   	12
+
+typedef struct
+	{
+	UCHAR		   	device;				// device code
+	UCHAR			byte6;				// device select register image
+	UCHAR			spigot;				// spigot number
+	UCHAR			expectingIRQ;		// flag for expecting and interrupt
+	USHORT			sectors;			// number of sectors per track
+	USHORT			heads;				// number of heads
+	USHORT			cylinders;			// number of cylinders for this device
+	USHORT			spareword;			// placeholder
+	ULONG			blocks;				// number of blocks on device
+	}	OUR_DEVICE, *POUR_DEVICE;
+
+typedef struct
+	{
+	USHORT		 ports[13];
+	OUR_DEVICE	 device[8];
+	Scsi_Cmnd	*pSCmnd;
+	IDE_STRUCT	 ide;
+	ULONG		 startSector;
+	USHORT		 sectorCount;
+	Scsi_Cmnd	*SCpnt;
+	VOID		*buffer;
+	USHORT		 expectingIRQ;
+	}	ADAPTER240I, *PADAPTER240I;
+
+#define HOSTDATA(host) ((PADAPTER240I)&host->hostdata)
+
+static struct	Scsi_Host *PsiHost[6] = {NULL,};  /* One for each IRQ level (10-15) */
+static			IDENTIFY_DATA	identifyData;
+static			SETUP			ChipSetup;
+
+static	USHORT	portAddr[6] = {CHIP_ADRS_0, CHIP_ADRS_1, CHIP_ADRS_2, CHIP_ADRS_3, CHIP_ADRS_4, CHIP_ADRS_5};
+
+/****************************************************************
+ *	Name:	WriteData	:LOCAL
+ *
+ *	Description:	Write data to device.
+ *
+ *	Parameters:		padapter - Pointer adapter data structure.
+ *
+ *	Returns:		TRUE if drive does not assert DRQ in time.
+ *
+ ****************************************************************/
+static int WriteData (PADAPTER240I padapter)
+	{
+	ULONG	timer;
+	USHORT *pports = padapter->ports;
+
+	timer = jiffies + TIMEOUT_DRQ;								// calculate the timeout value
+	do  {
+		if ( inb_p (pports[PORT_STAT_CMD]) & IDE_STATUS_DRQ )
+			{
+			outsw (pports[PORT_DATA], padapter->buffer, (USHORT)padapter->ide.ide.ide[2] * 256);
+			return 0;
+			}
+		}	while ( timer > jiffies );									// test for timeout
+
+	padapter->ide.ide.ides.cmd = 0;									// null out the command byte
+	return 1;
+	}
+/****************************************************************
+ *	Name:	IdeCmd	:LOCAL
+ *
+ *	Description:	Process a queued command from the SCSI manager.
+ *
+ *	Parameters:		padapter - Pointer adapter data structure.
+ *
+ *	Returns:		Zero if no error or status register contents on error.
+ *
+ ****************************************************************/
+static UCHAR IdeCmd (PADAPTER240I padapter)
+	{
+	ULONG	timer;
+	USHORT *pports = padapter->ports;
+	UCHAR	status;
+
+	outb_p (padapter->ide.ide.ides.spigot, pports[PORT_SEL_FAIL]);	// select the spigot
+	outb_p (padapter->ide.ide.ide[6], pports[PORT_LBA_24]);			// select the drive
+	timer = jiffies + TIMEOUT_READY;							// calculate the timeout value
+	do  {
+		status = inb_p (padapter->ports[PORT_STAT_CMD]);
+		if ( status & IDE_STATUS_DRDY )
+			{
+			outb_p (padapter->ide.ide.ide[2], pports[PORT_SECTOR_COUNT]);
+			outb_p (padapter->ide.ide.ide[3], pports[PORT_LBA_0]);
+			outb_p (padapter->ide.ide.ide[4], pports[PORT_LBA_8]);
+			outb_p (padapter->ide.ide.ide[5], pports[PORT_LBA_16]);
+			padapter->expectingIRQ = 1;
+			outb_p (padapter->ide.ide.ide[7], pports[PORT_STAT_CMD]);
+
+			if ( padapter->ide.ide.ides.cmd == IDE_CMD_WRITE_MULTIPLE )
+				return (WriteData (padapter));
+
+			return 0;
+			}
+		}	while ( timer > jiffies );									// test for timeout
+
+	padapter->ide.ide.ides.cmd = 0;									// null out the command byte
+	return status;
+	}
+/****************************************************************
+ *	Name:	SetupTransfer	:LOCAL
+ *
+ *	Description:	Setup a data transfer command.
+ *
+ *	Parameters:		padapter - Pointer adapter data structure.
+ *					drive	 - Drive/head register upper nibble only.
+ *
+ *	Returns:		TRUE if no data to transfer.
+ *
+ ****************************************************************/
+static int SetupTransfer (PADAPTER240I padapter, UCHAR drive)
+	{
+	if ( padapter->sectorCount )
+		{
+		*(ULONG *)padapter->ide.ide.ides.lba = padapter->startSector;
+		padapter->ide.ide.ide[6] |= drive;
+		padapter->ide.ide.ides.sectors = ( padapter->sectorCount > SECTORSXFER ) ? SECTORSXFER : padapter->sectorCount;
+		padapter->sectorCount -= padapter->ide.ide.ides.sectors;	// bump the start and count for next xfer
+		padapter->startSector += padapter->ide.ide.ides.sectors;
+		return 0;
+		}
+	else
+		{
+		padapter->ide.ide.ides.cmd = 0;								// null out the command byte
+		padapter->SCpnt = NULL;
+		return 1;
+		}
+	}
+/****************************************************************
+ *	Name:	DecodeError	:LOCAL
+ *
+ *	Description:	Decode and process device errors.
+ *
+ *	Parameters:		pshost - Pointer to host data block.
+ *					status - Status register code.
+ *
+ *	Returns:		The driver status code.
+ *
+ ****************************************************************/
+static ULONG DecodeError (struct Scsi_Host *pshost, UCHAR status)
+	{
+	PADAPTER240I	padapter = HOSTDATA(pshost);
+	UCHAR			error;
+
+	padapter->expectingIRQ = 0;
+	padapter->SCpnt = NULL;
+	if ( status & IDE_STATUS_WRITE_FAULT )
+		{
+		return DID_PARITY << 16;
+		}
+	if ( status & IDE_STATUS_BUSY )
+		return DID_BUS_BUSY << 16;
+
+	error = inb_p (padapter->ports[PORT_ERROR]);
+	DEB(printk ("\npsi240i error register: %x", error));
+	switch ( error )
+		{
+		case IDE_ERROR_AMNF:
+		case IDE_ERROR_TKONF:
+		case IDE_ERROR_ABRT:
+		case IDE_ERROR_IDFN:
+		case IDE_ERROR_UNC:
+		case IDE_ERROR_BBK:
+		default:
+			return DID_ERROR << 16;
+		}
+	return DID_ERROR << 16;
+	}
+/****************************************************************
+ *	Name:	Irq_Handler	:LOCAL
+ *
+ *	Description:	Interrupt handler.
+ *
+ *	Parameters:		irq		- Hardware IRQ number.
+ *					dev_id	-
+ *					regs	-
+ *
+ *	Returns:		TRUE if drive is not ready in time.
+ *
+ ****************************************************************/
+static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs)
+	{
+	struct Scsi_Host   *shost;			// Pointer to host data block
+	PADAPTER240I		padapter;		// Pointer to adapter control structure
+	USHORT		 	   *pports;			// I/O port array
+	Scsi_Cmnd		   *SCpnt;
+	UCHAR				status;
+	int					z;
+
+	DEB(printk ("\npsi240i recieved interrupt\n"));
+
+	shost = PsiHost[irq - 10];
+	if ( !shost )
+		panic ("Splunge!");
+
+	padapter = HOSTDATA(shost);
+	pports = padapter->ports;
+	SCpnt = padapter->SCpnt;
+
+	if ( !padapter->expectingIRQ )
+		{
+		DEB(printk ("\npsi240i Unsolicited interrupt\n"));
+		return;
+		}
+	padapter->expectingIRQ = 0;
+
+	status = inb_p (padapter->ports[PORT_STAT_CMD]);			// read the device status
+	if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) )
+		goto irqerror;
+
+	DEB(printk ("\npsi240i processing interrupt"));
+	switch ( padapter->ide.ide.ides.cmd )							// decide how to handle the interrupt
+		{
+		case IDE_CMD_READ_MULTIPLE:
+			if ( status & IDE_STATUS_DRQ )
+				{
+				insw (pports[PORT_DATA], padapter->buffer, (USHORT)padapter->ide.ide.ides.sectors * 256);
+				padapter->buffer += padapter->ide.ide.ides.sectors * 512;
+				if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) )
+					{
+					SCpnt->result = DID_OK << 16;
+					padapter->SCpnt = NULL;
+					SCpnt->scsi_done (SCpnt);
+					return;
+					}
+				if ( !(status = IdeCmd (padapter)) )
+					return;
+				}
+			break;
+
+		case IDE_CMD_WRITE_MULTIPLE:
+			padapter->buffer += padapter->ide.ide.ides.sectors * 512;
+			if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) )
+				{
+				SCpnt->result = DID_OK << 16;
+				padapter->SCpnt = NULL;
+				SCpnt->scsi_done (SCpnt);
+				return;
+				}
+			if ( !(status = IdeCmd (padapter)) )
+				return;
+			break;
+
+		case IDE_COMMAND_IDENTIFY:
+			{
+			PINQUIRYDATA	pinquiryData  = SCpnt->request_buffer;
+
+			if ( status & IDE_STATUS_DRQ )
+				{
+				insw (pports[PORT_DATA], &identifyData, sizeof (identifyData) >> 1);
+
+				memset (pinquiryData, 0, SCpnt->request_bufflen);		// Zero INQUIRY data structure.
+				pinquiryData->DeviceType = 0;
+				pinquiryData->Versions = 2;
+				pinquiryData->AdditionalLength = 35 - 4;
+
+				// Fill in vendor identification fields.
+				for ( z = 0;  z < 20;  z += 2 )
+					{
+					pinquiryData->VendorId[z]	  = ((UCHAR *)identifyData.ModelNumber)[z + 1];
+					pinquiryData->VendorId[z + 1] = ((UCHAR *)identifyData.ModelNumber)[z];
+					}
+
+				// Initialize unused portion of product id.
+				for ( z = 0;  z < 4;  z++ )
+					pinquiryData->ProductId[12 + z] = ' ';
+
+				// Move firmware revision from IDENTIFY data to
+				// product revision in INQUIRY data.
+				for ( z = 0;  z < 4;  z += 2 )
+					{
+					pinquiryData->ProductRevisionLevel[z]	 = ((UCHAR *)identifyData.FirmwareRevision)[z + 1];
+					pinquiryData->ProductRevisionLevel[z + 1] = ((UCHAR *)identifyData.FirmwareRevision)[z];
+					}
+
+				SCpnt->result = DID_OK << 16;
+				padapter->SCpnt = NULL;
+				SCpnt->scsi_done (SCpnt);
+				return;
+				}
+			break;
+			}
+
+		default:
+			SCpnt->result = DID_OK << 16;
+			padapter->SCpnt = NULL;
+			SCpnt->scsi_done (SCpnt);
+			return;
+		}
+
+irqerror:;
+	DEB(printk ("\npsi240i error  Device Status: %X\n", status));
+	SCpnt->result = DecodeError (shost, status);
+	SCpnt->scsi_done (SCpnt);
+	}
+/****************************************************************
+ *	Name:	Psi240i_QueueCommand
+ *
+ *	Description:	Process a queued command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *					done  - Pointer to done function to call.
+ *
+ *	Returns:		Status code.
+ *
+ ****************************************************************/
+int Psi240i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+	{
+	UCHAR		   *cdb = (UCHAR *)SCpnt->cmnd;					// Pointer to SCSI CDB
+	PADAPTER240I	padapter = HOSTDATA(SCpnt->host);			// Pointer to adapter control structure
+	POUR_DEVICE		pdev	 = &padapter->device[SCpnt->target];// Pointer to device information
+	UCHAR			rc;											// command return code
+
+	SCpnt->scsi_done = done;
+	padapter->ide.ide.ides.spigot = pdev->spigot;
+	padapter->buffer = SCpnt->request_buffer;
+	if (done)
+		{
+		if ( !pdev->device || SCpnt->lun )
+			{
+			SCpnt->result = DID_BAD_TARGET << 16;
+			done (SCpnt);
+			return 0;
+			}
+		}
+	else
+		{
+		printk("psi240i_queuecommand: %02X: done can't be NULL\n", *cdb);
+		return 0;
+		}
+
+	switch ( *cdb )
+		{
+		case SCSIOP_INQUIRY:   					// inquiry CDB
+			{
+			padapter->ide.ide.ide[6] = pdev->byte6;
+			padapter->ide.ide.ides.cmd = IDE_COMMAND_IDENTIFY;
+			break;
+			}
+
+		case SCSIOP_TEST_UNIT_READY:			// test unit ready CDB
+			SCpnt->result = DID_OK << 16;
+			done (SCpnt);
+			return 0;
+
+		case SCSIOP_READ_CAPACITY:			  	// read capctiy CDB
+			{
+			PREAD_CAPACITY_DATA	pdata = (PREAD_CAPACITY_DATA)SCpnt->request_buffer;
+
+			pdata->blksiz = 0x20000;
+			XANY2SCSI ((UCHAR *)&pdata->blks, pdev->blocks);
+			SCpnt->result = DID_OK << 16;
+			done (SCpnt);
+			return 0;
+			}
+
+		case SCSIOP_VERIFY:						// verify CDB
+			*(ULONG *)padapter->ide.ide.ides.lba = XSCSI2LONG (&cdb[2]);
+			padapter->ide.ide.ide[6] |= pdev->byte6;
+			padapter->ide.ide.ide[2] = (UCHAR)((USHORT)cdb[8] | ((USHORT)cdb[7] << 8));
+			padapter->ide.ide.ides.cmd = IDE_COMMAND_VERIFY;
+			break;
+
+		case SCSIOP_READ:						// read10 CDB
+			padapter->startSector = XSCSI2LONG (&cdb[2]);
+			padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8);
+			SetupTransfer (padapter, pdev->byte6);
+			padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE;
+			break;
+
+		case SCSIOP_READ6:						// read6  CDB
+			padapter->startSector = SCSI2LONG (&cdb[1]);
+			padapter->sectorCount = cdb[4];
+			SetupTransfer (padapter, pdev->byte6);
+			padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE;
+			break;
+
+		case SCSIOP_WRITE:						// write10 CDB
+			padapter->startSector = XSCSI2LONG (&cdb[2]);
+			padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8);
+			SetupTransfer (padapter, pdev->byte6);
+			padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE;
+			break;
+		case SCSIOP_WRITE6:						// write6  CDB
+			padapter->startSector = SCSI2LONG (&cdb[1]);
+			padapter->sectorCount = cdb[4];
+			SetupTransfer (padapter, pdev->byte6);
+			padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE;
+			break;
+
+		default:
+			DEB (printk ("psi240i_queuecommand: Unsupported command %02X\n", *cdb));
+			SCpnt->result = DID_ERROR << 16;
+			done (SCpnt);
+			return 0;
+		}
+
+	padapter->SCpnt = SCpnt;  									// Save this command data
+
+	rc = IdeCmd (padapter);
+	if ( rc )
+		{
+		padapter->expectingIRQ = 0;
+		DEB (printk ("psi240i_queuecommand: %02X, %02X: Device failed to respond for command\n", *cdb, padapter->ide.ide.ides.cmd));
+		SCpnt->result = DID_ERROR << 16;
+		done (SCpnt);
+		return 0;
+		}
+	DEB (printk("psi240i_queuecommand: %02X, %02X now waiting for interrupt ", *cdb, padapter->ide.ide.ides.cmd));
+	return 0;
+	}
+
+static void internal_done(Scsi_Cmnd * SCpnt)
+	{
+	SCpnt->SCp.Status++;
+	}
+/****************************************************************
+ *	Name:	Psi240i_Command
+ *
+ *	Description:	Process a command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *
+ *	Returns:		Status code.
+ *
+ ****************************************************************/
+int Psi240i_Command (Scsi_Cmnd *SCpnt)
+	{
+	DEB(printk("psi240i_command: ..calling psi240i_queuecommand\n"));
+
+	Psi240i_QueueCommand (SCpnt, internal_done);
+
+    SCpnt->SCp.Status = 0;
+	while (!SCpnt->SCp.Status)
+		barrier ();
+	return SCpnt->result;
+	}
+/***************************************************************************
+ *	Name:			ReadChipMemory
+ *
+ *	Description:	Read information from controller memory.
+ *
+ *	Parameters:		psetup	- Pointer to memory image of setup information.
+ *					base	- base address of memory.
+ *					length	- lenght of data space in bytes.
+ *					port	- I/O address of data port.
+ *
+ *	Returns:		Nothing.
+ *
+ **************************************************************************/
+void ReadChipMemory (void *pdata, USHORT base, USHORT length, USHORT port)
+	{
+	USHORT	z, zz;
+	UCHAR	*pd = (UCHAR *)pdata;
+	outb_p (SEL_NONE, port + REG_SEL_FAIL);				// setup data port
+	zz = 0;
+	while ( zz < length )
+		{
+		outw_p (base, port + REG_ADDRESS);				// setup address
+
+		for ( z = 0;  z < 8;  z++ )
+			{
+			if ( (zz + z) < length )
+			*pd++ = inb_p (port + z);	// read data byte
+			}
+		zz += 8;
+		base += 8;
+		}
+	}
+/****************************************************************
+ *	Name:	Psi240i_Detect
+ *
+ *	Description:	Detect and initialize our boards.
+ *
+ *	Parameters:		tpnt - Pointer to SCSI host template structure.
+ *
+ *	Returns:		Number of adapters found.
+ *
+ ****************************************************************/
+int Psi240i_Detect (Scsi_Host_Template *tpnt)
+	{
+	int					board;
+	int					count = 0;
+	int					unit;
+	int					z;
+	USHORT				port;
+	CHIP_CONFIG_N		chipConfig;
+	CHIP_DEVICE_N		chipDevice[8];
+	struct Scsi_Host   *pshost;
+	ULONG				flags;
+
+	for ( board = 0;  board < 6;  board++ )					// scan for I/O ports
+		{
+		port = portAddr[board];								// get base address to test
+		if ( check_region (port, 16) )						// test for I/O addresses available
+			continue;										//   nope
+		if ( inb_p (port + REG_FAIL) != CHIP_ID )			// do the first test for likley hood that it is us
+			continue;
+		outb_p (SEL_NONE, port + REG_SEL_FAIL);				// setup EEPROM/RAM access
+		outw (0, port + REG_ADDRESS);						// setup EEPROM address zero
+		if ( inb_p (port) != 0x55 )							// test 1st byte
+			continue;										//   nope
+		if ( inb_p (port + 1) != 0xAA )						// test 2nd byte
+			continue;										//   nope
+
+		// at this point our board is found and can be accessed.  Now we need to initialize
+		// our informatation and register with the kernel.
+
+
+		ReadChipMemory (&chipConfig, CHIP_CONFIG, sizeof (chipConfig), port);
+		ReadChipMemory (&chipDevice, CHIP_DEVICE, sizeof (chipDevice), port);
+		ReadChipMemory (&ChipSetup, CHIP_EEPROM_DATA, sizeof (ChipSetup), port);
+
+		if ( !chipConfig.numDrives )						// if no devices on this board
+			continue;
+
+		pshost = scsi_register (tpnt, sizeof(ADAPTER240I));
+
+		save_flags (flags);
+		cli ();
+		if ( request_irq (chipConfig.irq, Irq_Handler, 0, "psi240i", NULL) )
+			{
+			printk ("Unable to allocate IRQ for PSI-240I controller.\n");
+			restore_flags (flags);
+			goto unregister;
+			}
+
+		PsiHost[chipConfig.irq - 10] = pshost;
+		pshost->unique_id = port;
+		pshost->io_port = port;
+		pshost->n_io_port = 16;  /* Number of bytes of I/O space used */
+		pshost->irq = chipConfig.irq;
+
+		for ( z = 0;  z < 11;  z++ )						// build regester address array
+			HOSTDATA(pshost)->ports[z] = port + z;
+		HOSTDATA(pshost)->ports[11] = port + REG_FAIL;
+		HOSTDATA(pshost)->ports[12] = port + REG_ALT_STAT;
+		DEB (printk ("\nPorts ="));
+		DEB (for (z=0;z<13;z++) printk(" %#04X",HOSTDATA(pshost)->ports[z]););
+
+		for ( z = 0;  z < chipConfig.numDrives;  ++z )
+			{
+			unit = chipDevice[z].channel & 0x0F;
+			HOSTDATA(pshost)->device[unit].device	 = ChipSetup.setupDevice[unit].device;
+			HOSTDATA(pshost)->device[unit].byte6	 = (UCHAR)(((unit & 1) << 4) | 0xE0);
+			HOSTDATA(pshost)->device[unit].spigot	 = (UCHAR)(1 << (unit >> 1));
+			HOSTDATA(pshost)->device[unit].sectors	 = ChipSetup.setupDevice[unit].sectors;
+			HOSTDATA(pshost)->device[unit].heads	 = ChipSetup.setupDevice[unit].heads;
+			HOSTDATA(pshost)->device[unit].cylinders = ChipSetup.setupDevice[unit].cylinders;
+			HOSTDATA(pshost)->device[unit].blocks	 = ChipSetup.setupDevice[unit].blocks;
+			DEB (printk ("\nHOSTDATA->device    = %X", HOSTDATA(pshost)->device[unit].device));
+			DEB (printk ("\n          byte6     = %X", HOSTDATA(pshost)->device[unit].byte6));
+			DEB (printk ("\n          spigot    = %X", HOSTDATA(pshost)->device[unit].spigot));
+			DEB (printk ("\n          sectors   = %X", HOSTDATA(pshost)->device[unit].sectors));
+			DEB (printk ("\n          heads     = %X", HOSTDATA(pshost)->device[unit].heads));
+			DEB (printk ("\n          cylinders = %X", HOSTDATA(pshost)->device[unit].cylinders));
+			DEB (printk ("\n          blocks    = %lX", HOSTDATA(pshost)->device[unit].blocks));
+			}
+
+		restore_flags (flags);
+		printk("\nPSI-240I EIDE CONTROLLER: at I/O = %x  IRQ = %d\n", port, chipConfig.irq);
+		printk("(C) 1997 Perceptive Solutions, Inc. All rights reserved\n\n");
+		count++;
+		continue;
+
+unregister:;
+		scsi_unregister (pshost);
+		}
+	return count;
+	}
+/****************************************************************
+ *	Name:	Psi240i_Abort
+ *
+ *	Description:	Process the Abort command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *
+ *	Returns:		Allways snooze.
+ *
+ ****************************************************************/
+int Psi240i_Abort (Scsi_Cmnd *SCpnt)
+	{
+	DEB (printk ("psi240i_abort\n"));
+	return SCSI_ABORT_SNOOZE;
+	}
+/****************************************************************
+ *	Name:	Psi240i_Reset
+ *
+ *	Description:	Process the Reset command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *					flags - Flags about the reset command
+ *
+ *	Returns:		No active command at this time, so this means
+ *					that each time we got some kind of response the
+ *					last time through.  Tell the mid-level code to
+ *					request sense information in order to decide what
+ *					to do next.
+ *
+ ****************************************************************/
+int Psi240i_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+	{
+	return SCSI_RESET_PUNT;
+	}
+
+#include "sd.h"
+
+/****************************************************************
+ *	Name:	Psi240i_BiosParam
+ *
+ *	Description:	Process the biosparam request from the SCSI manager to
+ *					return C/H/S data.
+ *
+ *	Parameters:		disk - Pointer to SCSI disk structure.
+ *					dev	 - Major/minor number from kernel.
+ *					geom - Pointer to integer array to place geometry data.
+ *
+ *	Returns:		zero.
+ *
+ ****************************************************************/
+int Psi240i_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[])
+	{
+	POUR_DEVICE	pdev;
+
+	pdev = &(HOSTDATA(disk->device->host)->device[disk->device->id]);
+
+	geom[0] = pdev->heads;
+	geom[1] = pdev->sectors;
+	geom[2] = pdev->cylinders;
+	return 0;
+	}
+
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = PSI240I;
+
+#include "scsi_module.c"
+#endif
+
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/psi240i.h linux/drivers/scsi/psi240i.h
--- linux.vanilla/drivers/scsi/psi240i.h	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/psi240i.h	Thu Mar  4 13:16:00 1999
@@ -0,0 +1,344 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PSI-240I device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:		psi240i.h
+ *
+ *	Description:	Header file for the SCSI driver for the PSI240I
+ *					EIDE interface card.
+ *
+ *-M*************************************************************************/
+#ifndef _PSI240I_H
+#define _PSI240I_H
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+#ifndef	PSI_EIDE_SCSIOP
+#define	PSI_EIDE_SCSIOP	1
+
+/************************************************/
+/*		Some defines that we like 				*/
+/************************************************/
+#define	CHAR		char
+#define	UCHAR		unsigned char
+#define	SHORT		short
+#define	USHORT		unsigned short
+#define	BOOL		unsigned short
+#define	LONG		long
+#define	ULONG		unsigned long
+#define	VOID		void
+
+/************************************************/
+/*		Timeout konstants		 				*/
+/************************************************/
+#define	TIMEOUT_READY				10		// 100 mSec
+#define	TIMEOUT_DRQ					40		// 400 mSec
+
+/************************************************/
+/*		Misc. macros			 				*/
+/************************************************/
+#define ANY2SCSI(up, p)					\
+((UCHAR *)up)[0] = (((ULONG)(p)) >> 8);	\
+((UCHAR *)up)[1] = ((ULONG)(p));
+
+#define SCSI2LONG(up)					\
+( (((long)*(((UCHAR *)up))) << 16)		\
++ (((long)(((UCHAR *)up)[1])) << 8)		\
++ ((long)(((UCHAR *)up)[2])) )
+
+#define XANY2SCSI(up, p)				\
+((UCHAR *)up)[0] = ((long)(p)) >> 24;	\
+((UCHAR *)up)[1] = ((long)(p)) >> 16;	\
+((UCHAR *)up)[2] = ((long)(p)) >> 8;	\
+((UCHAR *)up)[3] = ((long)(p));
+
+#define XSCSI2LONG(up)					\
+( (((long)(((UCHAR *)up)[0])) << 24)	\
++ (((long)(((UCHAR *)up)[1])) << 16)	\
++ (((long)(((UCHAR *)up)[2])) <<  8)	\
++ ((long)(((UCHAR *)up)[3])) )
+
+/************************************************/
+/*		SCSI CDB operation codes 				*/
+/************************************************/
+#define SCSIOP_TEST_UNIT_READY		0x00
+#define SCSIOP_REZERO_UNIT			0x01
+#define SCSIOP_REWIND				0x01
+#define SCSIOP_REQUEST_BLOCK_ADDR	0x02
+#define SCSIOP_REQUEST_SENSE		0x03
+#define SCSIOP_FORMAT_UNIT			0x04
+#define SCSIOP_READ_BLOCK_LIMITS	0x05
+#define SCSIOP_REASSIGN_BLOCKS		0x07
+#define SCSIOP_READ6				0x08
+#define SCSIOP_RECEIVE				0x08
+#define SCSIOP_WRITE6				0x0A
+#define SCSIOP_PRINT				0x0A
+#define SCSIOP_SEND					0x0A
+#define SCSIOP_SEEK6				0x0B
+#define SCSIOP_TRACK_SELECT			0x0B
+#define SCSIOP_SLEW_PRINT			0x0B
+#define SCSIOP_SEEK_BLOCK			0x0C
+#define SCSIOP_PARTITION			0x0D
+#define SCSIOP_READ_REVERSE			0x0F
+#define SCSIOP_WRITE_FILEMARKS		0x10
+#define SCSIOP_FLUSH_BUFFER			0x10
+#define SCSIOP_SPACE				0x11
+#define SCSIOP_INQUIRY				0x12
+#define SCSIOP_VERIFY6				0x13
+#define SCSIOP_RECOVER_BUF_DATA		0x14
+#define SCSIOP_MODE_SELECT			0x15
+#define SCSIOP_RESERVE_UNIT			0x16
+#define SCSIOP_RELEASE_UNIT			0x17
+#define SCSIOP_COPY					0x18
+#define SCSIOP_ERASE				0x19
+#define SCSIOP_MODE_SENSE			0x1A
+#define SCSIOP_START_STOP_UNIT		0x1B
+#define SCSIOP_STOP_PRINT			0x1B
+#define SCSIOP_LOAD_UNLOAD			0x1B
+#define SCSIOP_RECEIVE_DIAGNOSTIC	0x1C
+#define SCSIOP_SEND_DIAGNOSTIC		0x1D
+#define SCSIOP_MEDIUM_REMOVAL		0x1E
+#define SCSIOP_READ_CAPACITY		0x25
+#define SCSIOP_READ					0x28
+#define SCSIOP_WRITE				0x2A
+#define SCSIOP_SEEK					0x2B
+#define SCSIOP_LOCATE				0x2B
+#define SCSIOP_WRITE_VERIFY			0x2E
+#define SCSIOP_VERIFY				0x2F
+#define SCSIOP_SEARCH_DATA_HIGH		0x30
+#define SCSIOP_SEARCH_DATA_EQUAL	0x31
+#define SCSIOP_SEARCH_DATA_LOW		0x32
+#define SCSIOP_SET_LIMITS			0x33
+#define SCSIOP_READ_POSITION		0x34
+#define SCSIOP_SYNCHRONIZE_CACHE	0x35
+#define SCSIOP_COMPARE				0x39
+#define SCSIOP_COPY_COMPARE			0x3A
+#define SCSIOP_WRITE_DATA_BUFF		0x3B
+#define SCSIOP_READ_DATA_BUFF		0x3C
+#define SCSIOP_CHANGE_DEFINITION	0x40
+#define SCSIOP_READ_SUB_CHANNEL		0x42
+#define SCSIOP_READ_TOC				0x43
+#define SCSIOP_READ_HEADER			0x44
+#define SCSIOP_PLAY_AUDIO			0x45
+#define SCSIOP_PLAY_AUDIO_MSF		0x47
+#define SCSIOP_PLAY_TRACK_INDEX		0x48
+#define SCSIOP_PLAY_TRACK_RELATIVE	0x49
+#define SCSIOP_PAUSE_RESUME			0x4B
+#define SCSIOP_LOG_SELECT			0x4C
+#define SCSIOP_LOG_SENSE			0x4D
+#define SCSIOP_MODE_SELECT10		0x55
+#define SCSIOP_MODE_SENSE10			0x5A
+#define SCSIOP_LOAD_UNLOAD_SLOT		0xA6
+#define SCSIOP_MECHANISM_STATUS		0xBD
+#define SCSIOP_READ_CD				0xBE
+
+// IDE command definitions
+#define IDE_COMMAND_ATAPI_RESET		0x08
+#define IDE_COMMAND_READ			0x20
+#define IDE_COMMAND_WRITE			0x30
+#define IDE_COMMAND_RECALIBRATE		0x10
+#define IDE_COMMAND_SEEK			0x70
+#define IDE_COMMAND_SET_PARAMETERS	0x91
+#define IDE_COMMAND_VERIFY			0x40
+#define IDE_COMMAND_ATAPI_PACKET	0xA0
+#define IDE_COMMAND_ATAPI_IDENTIFY	0xA1
+#define	IDE_CMD_READ_MULTIPLE		0xC4
+#define	IDE_CMD_WRITE_MULTIPLE		0xC5
+#define	IDE_CMD_SET_MULTIPLE		0xC6
+#define IDE_COMMAND_WRITE_DMA		0xCA
+#define IDE_COMMAND_READ_DMA		0xC8
+#define IDE_COMMAND_IDENTIFY		0xEC
+
+// IDE status definitions
+#define IDE_STATUS_ERROR			0x01
+#define IDE_STATUS_INDEX			0x02
+#define IDE_STATUS_CORRECTED_ERROR	0x04
+#define IDE_STATUS_DRQ				0x08
+#define IDE_STATUS_DSC				0x10
+#define	IDE_STATUS_WRITE_FAULT		0x20
+#define IDE_STATUS_DRDY				0x40
+#define IDE_STATUS_BUSY				0x80
+
+// IDE error definitions
+#define	IDE_ERROR_AMNF				0x01
+#define	IDE_ERROR_TKONF				0x02
+#define	IDE_ERROR_ABRT				0x04
+#define	IDE_ERROR_MCR				0x08
+#define	IDE_ERROR_IDFN				0x10
+#define	IDE_ERROR_MC				0x20
+#define	IDE_ERROR_UNC				0x40
+#define	IDE_ERROR_BBK				0x80
+
+//	IDE interface structure
+typedef struct _IDE_STRUCT
+	{
+	union
+		{
+		UCHAR	ide[9];
+		struct
+			{
+			USHORT	data;
+			UCHAR	sectors;
+			UCHAR	lba[4];
+			UCHAR	cmd;
+			UCHAR	spigot;
+			}	ides;
+		} ide;
+	}	IDE_STRUCT;
+
+// SCSI read capacity structure
+typedef	struct _READ_CAPACITY_DATA
+	{
+	ULONG blks;				/* total blocks (converted to little endian) */
+	ULONG blksiz;			/* size of each (converted to little endian) */
+	}	READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA;
+
+// SCSI inquiry data
+typedef struct _INQUIRYDATA
+	{
+	UCHAR DeviceType			:5;
+	UCHAR DeviceTypeQualifier	:3;
+	UCHAR DeviceTypeModifier	:7;
+	UCHAR RemovableMedia		:1;
+    UCHAR Versions;
+    UCHAR ResponseDataFormat;
+    UCHAR AdditionalLength;
+    UCHAR Reserved[2];
+	UCHAR SoftReset				:1;
+	UCHAR CommandQueue			:1;
+	UCHAR Reserved2				:1;
+	UCHAR LinkedCommands		:1;
+	UCHAR Synchronous			:1;
+	UCHAR Wide16Bit				:1;
+	UCHAR Wide32Bit				:1;
+	UCHAR RelativeAddressing	:1;
+    UCHAR VendorId[8];
+    UCHAR ProductId[16];
+    UCHAR ProductRevisionLevel[4];
+    UCHAR VendorSpecific[20];
+    UCHAR Reserved3[40];
+	}	INQUIRYDATA, *PINQUIRYDATA;
+
+// IDE IDENTIFY data
+typedef struct _IDENTIFY_DATA
+	{
+    USHORT GeneralConfiguration;            // 00
+    USHORT NumberOfCylinders;               // 02
+    USHORT Reserved1;                       // 04
+    USHORT NumberOfHeads;                   // 06
+    USHORT UnformattedBytesPerTrack;        // 08
+    USHORT UnformattedBytesPerSector;       // 0A
+    USHORT SectorsPerTrack;                 // 0C
+    USHORT VendorUnique1[3];                // 0E
+    USHORT SerialNumber[10];                // 14
+    USHORT BufferType;                      // 28
+    USHORT BufferSectorSize;                // 2A
+    USHORT NumberOfEccBytes;                // 2C
+    USHORT FirmwareRevision[4];             // 2E
+    USHORT ModelNumber[20];                 // 36
+    UCHAR  MaximumBlockTransfer;            // 5E
+    UCHAR  VendorUnique2;                   // 5F
+    USHORT DoubleWordIo;                    // 60
+    USHORT Capabilities;                    // 62
+    USHORT Reserved2;                       // 64
+    UCHAR  VendorUnique3;                   // 66
+    UCHAR  PioCycleTimingMode;              // 67
+    UCHAR  VendorUnique4;                   // 68
+    UCHAR  DmaCycleTimingMode;              // 69
+    USHORT TranslationFieldsValid:1;        // 6A
+    USHORT Reserved3:15;
+    USHORT NumberOfCurrentCylinders;        // 6C
+    USHORT NumberOfCurrentHeads;            // 6E
+    USHORT CurrentSectorsPerTrack;          // 70
+    ULONG  CurrentSectorCapacity;           // 72
+    USHORT Reserved4[197];                  // 76
+	}	IDENTIFY_DATA, *PIDENTIFY_DATA;
+
+// Identify data without the Reserved4.
+typedef struct _IDENTIFY_DATA2 {
+    USHORT GeneralConfiguration;            // 00
+    USHORT NumberOfCylinders;               // 02
+    USHORT Reserved1;                       // 04
+    USHORT NumberOfHeads;                   // 06
+    USHORT UnformattedBytesPerTrack;        // 08
+    USHORT UnformattedBytesPerSector;       // 0A
+    USHORT SectorsPerTrack;                 // 0C
+    USHORT VendorUnique1[3];                // 0E
+    USHORT SerialNumber[10];                // 14
+    USHORT BufferType;                      // 28
+    USHORT BufferSectorSize;                // 2A
+    USHORT NumberOfEccBytes;                // 2C
+    USHORT FirmwareRevision[4];             // 2E
+    USHORT ModelNumber[20];                 // 36
+    UCHAR  MaximumBlockTransfer;            // 5E
+    UCHAR  VendorUnique2;                   // 5F
+    USHORT DoubleWordIo;                    // 60
+    USHORT Capabilities;                    // 62
+    USHORT Reserved2;                       // 64
+    UCHAR  VendorUnique3;                   // 66
+    UCHAR  PioCycleTimingMode;              // 67
+    UCHAR  VendorUnique4;                   // 68
+    UCHAR  DmaCycleTimingMode;              // 69
+	USHORT TranslationFieldsValid:1;     	// 6A
+    USHORT Reserved3:15;
+    USHORT NumberOfCurrentCylinders;        // 6C
+    USHORT NumberOfCurrentHeads;            // 6E
+    USHORT CurrentSectorsPerTrack;          // 70
+    ULONG  CurrentSectorCapacity;           // 72
+	}	IDENTIFY_DATA2, *PIDENTIFY_DATA2;
+
+#endif	// PSI_EIDE_SCSIOP
+
+// function prototypes
+int Psi240i_Detect			(Scsi_Host_Template *tpnt);
+int Psi240i_Command			(Scsi_Cmnd *SCpnt);
+int Psi240i_QueueCommand	(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *));
+int Psi240i_Abort			(Scsi_Cmnd *SCpnt);
+int Psi240i_Reset			(Scsi_Cmnd *SCpnt, unsigned int flags);
+int Psi240i_BiosParam		(Disk *disk, kdev_t dev, int geom[]);
+
+#ifndef NULL
+	#define NULL 0
+#endif
+
+extern struct proc_dir_entry Proc_Scsi_Psi240i;
+
+#define PSI240I { NULL, NULL,						\
+			&Proc_Scsi_Psi240i,/* proc_dir_entry */	\
+			NULL,		                			\
+			"PSI-240I EIDE Disk Controller",		\
+			Psi240i_Detect,							\
+			NULL,									\
+			NULL,	 								\
+			Psi240i_Command,						\
+			Psi240i_QueueCommand,					\
+			Psi240i_Abort,							\
+			Psi240i_Reset,							\
+			NULL,									\
+			Psi240i_BiosParam,                 		\
+			1, 										\
+			-1, 									\
+			SG_NONE,		 						\
+			1, 										\
+			0, 										\
+			0, 										\
+			DISABLE_CLUSTERING }
+
+#endif
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/psi_chip.h linux/drivers/scsi/psi_chip.h
--- linux.vanilla/drivers/scsi/psi_chip.h	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/psi_chip.h	Thu Mar  4 13:16:00 1999
@@ -0,0 +1,194 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PSI-240I device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:	psi_chip.h
+ *
+ *	Description:	This file contains the interface defines and
+ *					error codes.
+ *
+ *-M*************************************************************************/
+#ifndef PSI_CHIP
+#define PSI_CHIP
+
+/************************************************/
+/*		Misc konstants							*/
+/************************************************/
+#define	CHIP_MAXDRIVES			8
+
+/************************************************/
+/*		Chip I/O addresses						*/
+/************************************************/
+#define	CHIP_ADRS_0				0x0130
+#define	CHIP_ADRS_1				0x0150
+#define	CHIP_ADRS_2				0x0190
+#define	CHIP_ADRS_3				0x0210
+#define	CHIP_ADRS_4				0x0230
+#define	CHIP_ADRS_5				0x0250
+
+/************************************************/
+/*		EEPROM locations		*/
+/************************************************/
+#define	CHIP_EEPROM_BIOS		0x0000		// BIOS base address
+#define	CHIP_EEPROM_DATA		0x2000	   	// SETUP data base address
+#define	CHIP_EEPROM_FACTORY		0x2400	   	// FACTORY data base address
+#define	CHIP_EEPROM_SETUP		0x3000	   	// SETUP PROGRAM base address
+
+#define	CHIP_EEPROM_SIZE		32768U	   	// size of the entire EEPROM
+#define	CHIP_EEPROM_BIOS_SIZE	8192	   	// size of the BIOS in bytes
+#define	CHIP_EEPROM_DATA_SIZE	4096	   	// size of factory, setup, log data block in bytes
+#define	CHIP_EEPROM_SETUP_SIZE	20480U	   	// size of the setup program in bytes
+
+/************************************************/
+/*		Chip Interrupts							*/
+/************************************************/
+#define	CHIP_IRQ_10				0x72
+#define	CHIP_IRQ_11				0x73
+#define	CHIP_IRQ_12				0x74
+
+/************************************************/
+/*		Chip Setup addresses		*/
+/************************************************/
+#define	CHIP_SETUP_BASE			0x0000C000L
+
+/************************************************/
+/*		Chip Register address offsets	*/
+/************************************************/
+#define	REG_DATA				0x00
+#define	REG_ERROR				0x01
+#define	REG_SECTOR_COUNT		0x02
+#define	REG_LBA_0				0x03
+#define	REG_LBA_8				0x04
+#define	REG_LBA_16				0x05
+#define	REG_LBA_24				0x06
+#define	REG_STAT_CMD			0x07
+#define	REG_SEL_FAIL			0x08
+#define	REG_IRQ_STATUS			0x09
+#define	REG_ADDRESS				0x0A
+#define	REG_FAIL				0x0C
+#define	REG_ALT_STAT		   	0x0E
+#define	REG_DRIVE_ADRS			0x0F
+
+/************************************************/
+/*		Chip RAM locations		*/
+/************************************************/
+#define	CHIP_DEVICE				0x8000
+#define	CHIP_DEVICE_0			0x8000
+#define CHIP_DEVICE_1			0x8008
+#define	CHIP_DEVICE_2			0x8010
+#define	CHIP_DEVICE_3			0x8018
+#define	CHIP_DEVICE_4			0x8020
+#define	CHIP_DEVICE_5			0x8028
+#define	CHIP_DEVICE_6			0x8030
+#define	CHIP_DEVICE_7			0x8038
+typedef struct
+	{
+	UCHAR	channel;		// channel of this device (0-8).
+	UCHAR	spt;			// Sectors Per Track.
+	ULONG	spc;			// Sectors Per Cylinder.
+	}	CHIP_DEVICE_N;
+
+#define	CHIP_CONFIG				0x8100		// address of boards configuration.
+typedef struct
+	{
+	UCHAR		irq;			// interrupt request channel number
+	UCHAR		numDrives;		// Number of accessable drives
+	UCHAR		fastFormat;	 	// Boolean for fast format enable
+	}	CHIP_CONFIG_N;
+
+#define	CHIP_MAP				0x8108 		// eight byte device type map.
+
+
+#define	CHIP_RAID				0x8120 		// array of RAID signature structures and LBA
+#define	CHIP_RAID_1				0x8120
+#define CHIP_RAID_2				0x8130
+#define	CHIP_RAID_3				0x8140
+#define	CHIP_RAID_4				0x8150
+
+/************************************************/
+/*		Chip Register Masks		*/
+/************************************************/
+#define	CHIP_ID					0x7B
+#define	SEL_RAM					0x8000
+#define	MASK_FAIL				0x80
+
+/************************************************/
+/*		Chip cable select bits		*/
+/************************************************/
+#define	SECTORSXFER				8
+
+/************************************************/
+/*		Chip cable select bits		*/
+/************************************************/
+#define	SEL_NONE				0x00
+#define	SEL_1					0x01
+#define	SEL_2					0x02
+#define	SEL_3					0x04
+#define	SEL_4					0x08
+
+/************************************************/
+/*		Programmable Interrupt Controller*/
+/************************************************/
+#define	PIC1					0x20		// first 8259 base port address
+#define	PIC2					0xA0		// second 8259 base port address
+#define	INT_OCW1				1			// Operation Control Word 1: IRQ mask
+#define	EOI						0x20		// non-specific end-of-interrupt
+
+/************************************************/
+/*		Device/Geometry controls				*/
+/************************************************/
+#define GEOMETRY_NONE		 	0x0			// No device
+#define GEOMETRY_AUTO			0x1			// Geometry set automatically
+#define GEOMETRY_USER		 	0x2			// User supplied geometry
+
+#define	DEVICE_NONE				0x0			// No device present
+#define	DEVICE_INACTIVE			0x1			// device present but not registered active
+#define	DEVICE_ATAPI			0x2			// ATAPI device (CD_ROM, Tape, Etc...)
+#define	DEVICE_DASD_NONLBA		0x3			// Non LBA incompatible device
+#define	DEVICE_DASD_LBA			0x4			// LBA compatible device
+
+/************************************************/
+/*		Setup Structure Definitions	*/
+/************************************************/
+typedef struct							// device setup parameters
+	{
+	UCHAR			geometryControl;	// geometry control flags
+	UCHAR		   	device;				// device code
+	USHORT			sectors;			// number of sectors per track
+	USHORT			heads;				// number of heads
+	USHORT			cylinders;			// number of cylinders for this device
+	ULONG			blocks;				// number of blocks on device
+	USHORT			spare1;
+	USHORT			spare2;
+	} SETUP_DEVICE, *PSETUP_DEVICE;
+
+typedef struct		// master setup structure
+	{
+	USHORT 			startupDelay;
+	USHORT 			promptBIOS;
+	USHORT 			fastFormat;
+	USHORT			spare2;
+	USHORT			spare3;
+	USHORT			spare4;
+	USHORT			spare5;
+	USHORT			spare6;
+	SETUP_DEVICE	setupDevice[8];
+	}	SETUP, *PSETUP;
+
+#endif
\ No newline at end of file
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/psi_dale.h linux/drivers/scsi/psi_dale.h
--- linux.vanilla/drivers/scsi/psi_dale.h	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/psi_dale.h	Thu Mar  4 13:13:10 1999
@@ -0,0 +1,187 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:		psi_dale.h
+ *
+ *	Description:	This file contains the interface defines and
+ *					error codes.
+ *
+ *-M*************************************************************************/
+
+#ifndef PSI_DALE
+#define PSI_DALE
+
+/************************************************/
+/*		Dale PCI setup							*/
+/************************************************/
+#define	VENDOR_PSI			0x1256
+#define	DEVICE_DALE_1		0x4401		/* 'D1' */
+
+/************************************************/
+/*		Misc konstants							*/
+/************************************************/
+#define	DALE_MAXDRIVES			4
+#define	SECTORSXFER				8
+#define	BYTES_PER_SECTOR		512
+#define	DEFAULT_TIMING_MODE		5
+
+/************************************************/
+/*		EEPROM locations						*/
+/************************************************/
+#define	DALE_FLASH_PAGE_SIZE	128				// number of bytes per page
+#define	DALE_FLASH_SIZE			65536L
+
+#define	DALE_FLASH_BIOS			0x00080000L		// BIOS base address
+#define	DALE_FLASH_SETUP		0x00088000L		// SETUP PROGRAM base address offset from BIOS
+#define	DALE_FLASH_RAID			0x00088400L		// RAID signature storage
+#define	DALE_FLASH_FACTORY		0x00089000L		// FACTORY data base address offset from BIOS
+
+#define	DALE_FLASH_BIOS_SIZE	32768U			// size of FLASH BIOS REGION
+
+/************************************************/
+/*		DALE Register address offsets			*/
+/************************************************/
+#define	REG_DATA				0x80
+#define	REG_ERROR				0x84
+#define	REG_SECTOR_COUNT		0x88
+#define	REG_LBA_0				0x8C
+#define	REG_LBA_8				0x90
+#define	REG_LBA_16				0x94
+#define	REG_LBA_24				0x98
+#define	REG_STAT_CMD			0x9C
+#define	REG_STAT_SEL			0xA0
+#define	REG_FAIL				0xB0
+#define	REG_ALT_STAT			0xB8
+#define	REG_DRIVE_ADRS			0xBC
+
+#define	DALE_DATA_SLOW			0x00040000L
+#define	DALE_DATA_MODE2			0x00040000L
+#define	DALE_DATA_MODE3			0x00050000L
+#define	DALE_DATA_MODE4			0x00060000L
+#define	DALE_DATA_MODE4P		0x00070000L
+
+#define RTR_LOCAL_RANGE					0x000
+#define RTR_LOCAL_REMAP					0x004
+#define RTR_EXP_RANGE					0x010
+#define RTR_EXP_REMAP					0x014
+#define RTR_REGIONS						0x018
+#define RTR_DM_MASK						0x01C
+#define RTR_DM_LOCAL_BASE				0x020
+#define RTR_DM_IO_BASE					0x024
+#define RTR_DM_PCI_REMAP				0x028
+#define RTR_DM_IO_CONFIG				0x02C
+#define RTR_MAILBOX						0x040
+#define RTR_LOCAL_DOORBELL				0x060
+#define RTR_PCI_DOORBELL				0x064
+#define RTR_INT_CONTROL_STATUS 			0x068
+#define RTR_EEPROM_CONTROL_STATUS		0x06C
+
+#define RTL_DMA0_MODE					0x00
+#define RTL_DMA0_PCI_ADDR				0x04
+#define RTL_DMA0_LOCAL_ADDR				0x08
+#define RTL_DMA0_COUNT					0x0C
+#define RTL_DMA0_DESC_PTR				0x10
+#define RTL_DMA1_MODE					0x14
+#define RTL_DMA1_PCI_ADDR				0x18
+#define RTL_DMA1_LOCAL_ADDR				0x1C
+#define RTL_DMA1_COUNT					0x20
+#define RTL_DMA1_DESC_PTR				0x24
+#define RTL_DMA_COMMAND_STATUS			0x28
+#define RTL_DMA_ARB0					0x2C
+#define RTL_DMA_ARB1					0x30
+
+/************************************************/
+/*		Dale Scratchpad locations				*/
+/************************************************/
+#define	DALE_CHANNEL_DEVICE_0	0		// device channel locations
+#define	DALE_CHANNEL_DEVICE_1	1
+#define	DALE_CHANNEL_DEVICE_2	2
+#define	DALE_CHANNEL_DEVICE_3	3
+
+#define	DALE_SCRATH_DEVICE_0	4		// device type codes
+#define	DALE_SCRATH_DEVICE_1	5
+#define DALE_SCRATH_DEVICE_2	6
+#define	DALE_SCRATH_DEVICE_3	7
+
+#define	DALE_RAID_0_STATUS		8
+#define DALE_RAID_1_STATUS		9
+
+#define	DALE_TIMING_MODE		12		// bus master timing mode (2, 3, 4, 5)
+#define	DALE_NUM_DRIVES			13		// number of addressable drives on this board
+#define	DALE_RAID_ON			14 		// RAID status On
+#define	DALE_LAST_ERROR			15		// Last error code from BIOS
+
+/************************************************/
+/*		Dale cable select bits					*/
+/************************************************/
+#define	SEL_NONE				0x00
+#define	SEL_1					0x01
+#define	SEL_2					0x02
+
+/************************************************/
+/*		Programmable Interrupt Controller		*/
+/************************************************/
+#define	PIC1					0x20				// first 8259 base port address
+#define	PIC2					0xA0				// second 8259 base port address
+#define	INT_OCW1				1					// Operation Control Word 1: IRQ mask
+#define	EOI						0x20				// non-specific end-of-interrupt
+
+/************************************************/
+/*		Device/Geometry controls				*/
+/************************************************/
+#define GEOMETRY_NONE	 	0x0			// No device
+#define GEOMETRY_SET		0x1			// Geometry set
+#define	GEOMETRY_LBA		0x2			// Geometry set in default LBA mode
+#define	GEOMETRY_PHOENIX	0x3			// Geometry set in Pheonix BIOS compatibility mode
+
+#define	DEVICE_NONE			0x0			// No device present
+#define	DEVICE_INACTIVE		0x1			// device present but not registered active
+#define	DEVICE_ATAPI		0x2			// ATAPI device (CD_ROM, Tape, Etc...)
+#define	DEVICE_DASD_NONLBA	0x3			// Non LBA incompatible device
+#define	DEVICE_DASD_LBA		0x4			// LBA compatible device
+
+/************************************************/
+/*		Setup Structure Definitions				*/
+/************************************************/
+typedef struct		// device setup parameters
+	{
+	UCHAR	geometryControl;	// geometry control flags
+	UCHAR	device;				// device code
+	USHORT	sectors;			// number of sectors per track
+	USHORT	heads;				// number of heads
+	USHORT	cylinders;			// number of cylinders for this device
+	ULONG	blocks;				// number of blocks on device
+	ULONG	realCapacity;		// number of real blocks on this device for drive changed testing
+	} SETUP_DEVICE, *PSETUP_DEVICE;
+
+typedef struct		// master setup structure
+	{
+	USHORT			startupDelay;
+	BOOL			promptBIOS;
+	BOOL			fastFormat;
+	BOOL			shareInterrupt;
+	BOOL			rebootRebuil;
+	USHORT			timingMode;
+	USHORT			spare5;
+	USHORT			spare6;
+	SETUP_DEVICE	setupDevice[4];
+	}	SETUP, *PSETUP;
+
+#endif
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/psi_roy.h linux/drivers/scsi/psi_roy.h
--- linux.vanilla/drivers/scsi/psi_roy.h	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/psi_roy.h	Thu Mar  4 13:17:54 1999
@@ -0,0 +1,337 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:	psi_roy.h
+ *
+ *	Description:	This file contains the host interface command and
+ *					error codes.
+ *
+ *-M*************************************************************************/
+
+#ifndef	ROY_HOST
+#define	ROY_HOST
+
+/************************************************/
+/*		PCI setup								*/
+/************************************************/
+#define	VENDOR_PSI			0x1256
+#define	DEVICE_ROY_1		0x5201		/* 'R1' */
+
+/************************************************/
+/*		controller constants					*/
+/************************************************/
+#define MAXADAPTER			4			// Increase this and the sizes of the arrays below, if you need more.
+#define	MAX_BUS				2
+#define	MAX_UNITS			16
+#define	TIMEOUT_COMMAND		30 			// number of jiffies for command busy timeout
+
+/************************************************/
+/*		I/O address offsets						*/
+/************************************************/
+#define RTR_MAILBOX						0x040
+#define RTR_LOCAL_DOORBELL				0x060
+#define RTR_PCI_DOORBELL				0x064
+
+/************************************************/
+/*												*/
+/*			Host command codes					*/
+/*												*/
+/************************************************/
+#define	CMD_READ_CHS		0x01		/* read sectors as specified (CHS mode) */
+#define	CMD_READ			0x02		/* read sectors as specified (RBA mode) */
+#define	CMD_READ_SG			0x03		/* read sectors using scatter/gather list */
+#define	CMD_WRITE_CHS		0x04		/* write sectors as specified (CHS mode) */
+#define	CMD_WRITE			0x05		/* write sectors as specified (RBA mode) */
+#define	CMD_WRITE_SG		0x06		/* write sectors using scatter/gather list (LBA mode) */
+#define	CMD_READ_CHS_SG		0x07		/* read sectors using scatter/gather list (CHS mode) */
+#define	CMD_WRITE_CHS_SG	0x08		/* write sectors using scatter/gather list (CHS mode) */
+#define	CMD_VERIFY_CHS		0x09		/* verify data on sectors as specified (CHS mode) */
+#define	CMD_VERIFY			0x0A		/* verify data on sectors as specified (RBA mode) */
+#define	CMD_DASD_CDB		0x0B		/* process CDB for a DASD device */
+#define	CMD_DASD_CDB_SG		0x0C		/* process CDB for a DASD device with scatter/gather */
+
+#define	CMD_READ_ABS		0x10		/* read absolute disk */
+#define	CMD_WRITE_ABS		0x11		/* write absolute disk */
+#define	CMD_VERIFY_ABS		0x12		/* verify absolute disk */
+#define	CMD_TEST_READY		0x13		/* test unit ready and return status code */
+#define	CMD_LOCK_DOOR		0x14		/* lock device door */
+#define	CMD_UNLOCK_DOOR		0x15		/* unlock device door */
+#define	CMD_EJECT_MEDIA		0x16		/* eject the media */
+#define	CMD_UPDATE_CAP		0x17		/* update capacity information */
+#define	CMD_TEST_PRIV		0x18		/* test and setup private format media */
+
+
+#define	CMD_SCSI_THRU		0x30		/* SCSI pass through CDB */
+#define	CMD_SCSI_THRU_SG	0x31		/* SCSI pass through CDB with scatter/gather */
+#define	CMD_SCSI_REQ_SENSE	0x32		/* SCSI pass through request sense after check condition */
+
+#define	CMD_DASD_RAID_RQ	0x35		/* request DASD RAID drive data */
+#define	CMD_DASD_RAID_RQ0	0x31			/* byte 1 subcommand to query for RAID 0 informatation */
+#define	CMD_DASD_RAID_RQ1	0x32			/* byte 1 subcommand to query for RAID 1 informatation */
+#define	CMD_DASD_RAID_RQ5	0x33			/* byte 1 subcommand to query for RAID 5 informatation */
+
+#define	CMD_DASD_SCSI_INQ	0x36		/* do DASD inquire and return in SCSI format */
+#define	CMD_DASD_CAP		0x37		/* read DASD capacity */
+#define	CMD_DASD_INQ		0x38		/* do DASD inquire for type data and return SCSI/EIDE inquiry */
+#define	CMD_SCSI_INQ		0x39		/* do SCSI inquire */
+#define	CMD_READ_SETUP		0x3A		/* Get setup structures from controller */
+#define	CMD_WRITE_SETUP		0x3B		/* Put setup structures in controller and burn in flash */
+#define	CMD_READ_CONFIG		0x3C		/* Get the entire configuration and setup structures */
+#define	CMD_WRITE_CONFIG	0x3D		/* Put the entire configuration and setup structures in flash */
+
+#define	CMD_TEXT_DEVICE		0x3E		/* obtain device text */
+#define	CMD_TEXT_SIGNON		0x3F		/* get sign on banner */
+
+#define	CMD_QUEUE			0x40		/* any command below this generates a queue tag interrupt to host*/
+
+#define	CMD_PREFETCH		0x40		/* prefetch sectors as specified */
+#define	CMD_TEST_WRITE		0x41		/* Test a device for write protect */
+#define	CMD_LAST_STATUS		0x42		/* get last command status and error data*/
+#define	CMD_ABORT			0x43		/* abort command as specified */
+#define	CMD_ERROR			0x44		/* fetch error code from a tagged op */
+#define	CMD_DONE			0x45		/* done with operation */
+#define	CMD_DIAGNOSTICS		0x46		/* execute controller diagnostics and wait for results */
+#define	CMD_FEATURE_MODE	0x47		/* feature mode control word */
+#define	CMD_DASD_INQUIRE	0x48		/* inquire as to DASD SCSI device (32 possible) */
+#define	CMD_FEATURE_QUERY	0x49		/* query the feature control word */
+#define	CMD_DASD_EJECT		0x4A		/* Eject removable media for DASD type */
+#define	CMD_DASD_LOCK		0x4B		/* Lock removable media for DASD type */
+#define	CMD_DASD_TYPE		0x4C		/* obtain DASD device type */
+#define	CMD_NUM_DEV			0x4D		/* obtain the number of devices connected to the controller */
+#define	CMD_GET_PARMS		0x4E		/* obtain device parameters */
+#define	CMD_SPECIFY			0x4F		/* specify operating system for scatter/gather operations */
+
+#define	CMD_RAID_GET_DEV	0x50		/* read RAID device geometry */
+#define CMD_RAID_READ		0x51		/* read RAID 1 parameter block */
+#define	CMD_RAID_WRITE		0x52		/* write RAID 1 parameter block */
+#define	CMD_RAID_LITEUP		0x53		/* Light up the drive light for identification */
+#define	CMD_RAID_REBUILD	0x54		/* issue a RAID 1 pair rebuild */
+#define	CMD_RAID_MUTE		0x55		/* mute RAID failure alarm */
+#define	CMD_RAID_FAIL		0x56		/* induce a RAID failure */
+#define	CMD_RAID_STATUS		0x57		/* get status of RAID pair */
+#define	CMD_RAID_STOP		0x58		/* stop any reconstruct in progress */
+#define CMD_RAID_START		0x59		/* start reconstruct */
+#define	CMD_RAID0_READ		0x5A		/* read RAID 0 parameter block */
+#define	CMD_RAID0_WRITE		0x5B		/* write RAID 0 parameter block */
+#define	CMD_RAID5_READ		0x5C		/* read RAID 5 parameter block */
+#define	CMD_RAID5_WRITE		0x5D		/* write RAID 5 parameter block */
+
+#define	CMD_ERASE_TABLES	0x5F		/* erase partition table and RAID signatutures */
+
+#define	CMD_SCSI_GET		0x60		/* get SCSI pass through devices */
+#define	CMD_SCSI_TIMEOUT	0x61		/* set SCSI pass through timeout */
+#define	CMD_SCSI_ERROR		0x62		/* get SCSI pass through request sense length and residual data count */
+#define	CMD_GET_SPARMS		0x63		/* get SCSI bus and user parms */
+#define	CMD_SCSI_ABORT		0x64		/* abort by setting time-out to zero */
+
+#define	CMD_CHIRP_CHIRP		0x77		/* make a chirp chirp sound */
+#define	CMD_GET_LAST_DONE	0x78		/* get tag of last done in progress */
+#define	CMD_GET_FEATURES	0x79		/* get feature code and ESN */
+#define CMD_CLEAR_CACHE		0x7A		/* Clear cache on specified device */
+#define	CMD_BIOS_TEST		0x7B		/* Test whether or not to load BIOS */
+#define	CMD_WAIT_FLUSH		0x7C		/* wait for cache flushed and invalidate read cache */
+#define	CMD_RESET_BUS		0x7D		/* reset the SCSI bus */
+#define	CMD_STARTUP_QRY		0x7E		/* startup in progress query */
+#define	CMD_RESET			0x7F		/* reset the controller */
+
+#define	CMD_RESTART_RESET	0x80		/* reload and restart the controller at any reset issued */
+#define	CMD_SOFT_RESET		0x81		/* do a soft reset NOW! */
+
+/************************************************/
+/*												*/
+/*				Host return errors				*/
+/*												*/
+/************************************************/
+#define	ERR08_TAGGED		0x80		/* doorbell error ored with tag */
+
+#define	ERR16_NONE			0x0000		/* no errors */
+#define	ERR16_SC_COND_MET	0x0004		/* SCSI status - Condition Met */
+#define	ERR16_CMD			0x0101		/* command error */
+#define	ERR16_SC_CHECK_COND	0x0002		/* SCSI status - Check Condition */
+#define	ERR16_CMD_NOT		0x0201		/* command not supported */
+#define ERR16_NO_DEVICE     0x0301		/* invalid device selection */
+#define	ERR16_SECTOR		0x0202		/* bad sector */
+#define	ERR16_PROTECT		0x0303		/* write protected */
+#define	ERR16_NOSECTOR		0x0404		/* sector not found */
+#define	ERR16_MEDIA			0x0C0C		/* invalid media */
+#define	ERR16_CONTROL		0x2020		/* controller error */
+#define	ERR16_CONTROL_DMA	0x2120		/* controller DMA engine error */
+#define	ERR16_NO_ALARM		0x2220		/* alarm is not active */
+#define	ERR16_OP_BUSY		0x2320		/* operation busy */
+#define	ERR16_SEEK			0x4040		/* seek failure */
+#define	ERR16_DEVICE_FAIL	0x4140		/* device has failed */
+#define ERR16_TIMEOUT		0x8080		/* timeout error */
+#define	ERR16_DEV_NOT_READY	0xAAAA		/* drive not ready */
+#define	ERR16_UNDEFINED		0xBBBB		/* undefined error */
+#define	ERR16_WRITE_FAULT	0xCCCC		/* write fault */
+#define ERR16_INVALID_DEV	0x4001		/* invalid device access */
+#define	ERR16_DEVICE_BUSY	0x4002		/* device is busy */
+#define	ERR16_MEMORY		0x4003		/* device pass thru requires too much memory */
+#define	ERR16_NO_FEATURE	0x40FA		/* feature no implemented */
+#define	ERR16_NOTAG			0x40FD		/* no tag space available */
+#define	ERR16_NOT_READY		0x40FE		/* controller not ready error */
+#define	ERR16_SETUP_FLASH	0x5050		/* error when writing setup to flash memory */
+#define	ERR16_SETUP_SIZE	0x5051		/* setup block size error */
+#define	ERR16_SENSE			0xFFFF		/* sense opereration failed */
+#define	ERR16_SC_BUSY		0x0008		/* SCSI status - Busy */
+#define	ERR16_SC_RES_CONFL	0x0018		/* SCSI status - Reservation Conflict */
+#define	ERR16_SC_CMD_TERM	0x0022		/* SCSI status - Command Terminated */
+#define	ERR16_SC_OTHER		0x00FF		/* SCSI status - not recognized (any value masked) */
+#define	ERR16_MEDIA_CHANGED	0x8001		/* devices media has been changed */
+
+#define	ERR32_NONE			0x00000000	/* no errors */
+#define	ERR32_SC_COND_MET	0x00000004	/* SCSI status - Condition Met */
+#define	ERR32_CMD			0x00010101	/* command error */
+#define	ERR32_SC_CHECK_COND	0x00020002	/* SCSI status - Check Condition */
+#define	ERR32_CMD_NOT		0x00030201	/* command not supported */
+#define ERR32_NO_DEVICE     0x00040301	/* invalid device selection */
+#define	ERR32_SECTOR		0x00050202	/* bad sector */
+#define	ERR32_PROTECT		0x00060303	/* write protected */
+#define	ERR32_NOSECTOR		0x00070404	/* sector not found */
+#define	ERR32_MEDIA			0x00080C0C	/* invalid media */
+#define	ERR32_CONTROL		0x00092020	/* controller error */
+#define	ERR32_CONTROL_DMA	0x000A2120	/* Controller DMA error */
+#define	ERR32_NO_ALARM		0x000B2220 	/* alarm is not active */
+#define	ERR32_OP_BUSY		0x000C2320	/* operation busy */
+#define	ERR32_SEEK			0x000D4040	/* seek failure */
+#define	ERR32_DEVICE_FAIL	0x000E4140	/* device has failed */
+#define ERR32_TIMEOUT		0x000F8080	/* timeout error */
+#define	ERR32_DEV_NOT_READY	0x0010AAAA	/* drive not ready */
+#define	ERR32_UNDEFINED		0x0011BBBB	/* undefined error */
+#define	ERR32_WRITE_FAULT	0x0012CCCC	/* write fault */
+#define ERR32_INVALID_DEV	0x00134001	/* invalid device access */
+#define	ERR32_DEVICE_BUSY	0x00144002	/* device is busy */
+#define	ERR32_MEMORY		0x00154003	/* device pass thru requires too much memory */
+#define	ERR32_NO_FEATURE	0x001640FA	/* feature no implemented */
+#define	ERR32_NOTAG			0x001740FD	/* no tag space available */
+#define	ERR32_NOT_READY		0x001840FE	/* controller not ready error */
+#define	ERR32_SETUP_FLASH	0x00195050	/* error when writing setup to flash memory */
+#define	ERR32_SETUP_SIZE	0x001A5051	/* setup block size error */
+#define	ERR32_SENSE			0x001BFFFF	/* sense opereration failed */
+#define	ERR32_SC_BUSY		0x001C0008	/* SCSI status - Busy */
+#define	ERR32_SC_RES_CONFL	0x001D0018	/* SCSI status - Reservation Conflict */
+#define	ERR32_SC_CMD_TERM	0x001E0022	/* SCSI status - Command Terminated */
+#define	ERR32_SC_OTHER		0x001F00FF	/* SCSI status - not recognized (any value masked) */
+#define	ERR32_MEDIA_CHANGED	0x00208001	/* devices media has been changed */
+
+/************************************************/
+/*												*/
+/*	Host Operating System specification codes	*/
+/*												*/
+/************************************************/
+#define	SPEC_INTERRUPT		0x80		/* specification requires host interrupt */
+#define	SPEC_BACKWARD_SG	0x40		/* specification requires scatter/gather items reversed */
+#define	SPEC_DOS_BLOCK		0x01		/* DOS DASD blocking on pass through */
+#define	SPEC_OS2_V3			0x02		/* OS/2 Warp */
+#define	SPCE_SCO_3242		0x04		/* SCO 3.4.2.2 */
+#define	SPEC_QNX_4X			0x05		/* QNX 4.XX */
+#define	SPEC_NOVELL_NWPA	0x08		/* Novell NWPA scatter/gather support */
+
+/************************************************/
+/*												*/
+/*	Inquire structures							*/
+/*												*/
+/************************************************/
+typedef	struct	_CNT_SCSI_INQ
+	{
+	UCHAR	devt;						/* 00: device type */
+	UCHAR	devtm;						/* 01: device type modifier */
+	UCHAR	svers;						/* 02: SCSI version */
+	UCHAR	rfmt;						/* 03: response data format */
+	UCHAR	adlen;						/* 04: additional length of data */
+	UCHAR	res1;						/* 05: */
+	UCHAR	res2;						/* 06: */
+	UCHAR	fncs;						/* 07: functional capabilities */
+	UCHAR	vid[8];						/* 08: vendor ID */
+	UCHAR	pid[16];					/* 10: product ID */
+	UCHAR	rev[4];						/* 20: product revision */
+	}	CNT_SCSI_INQ;
+
+typedef	struct	_CNT_IDE_INQ
+	{
+	USHORT	GeneralConfiguration;		/* 00 */
+	USHORT	NumberOfCylinders;			/* 02 */
+	USHORT	Reserved1;					/* 04 */
+	USHORT	NumberOfHeads;				/* 06 */
+	USHORT	UnformattedBytesPerTrack;	/* 08 */
+	USHORT	UnformattedBytesPerSector;	/* 0A */
+	USHORT	SectorsPerTrack;			/* 0C */
+	USHORT	VendorUnique1[3];			/* 0E */
+	USHORT	SerialNumber[10];			/* 14 */
+	USHORT	BufferType;					/* 28 */
+	USHORT	BufferSectorSize;			/* 2A */
+	USHORT	NumberOfEccBytes;			/* 2C */
+	USHORT	FirmwareRevision[4];		/* 2E */
+	USHORT	ModelNumber[20];			/* 36 */
+	UCHAR	MaximumBlockTransfer;		/* 5E */
+	UCHAR	VendorUnique2;				/* 5F */
+	USHORT	DoubleWordIo;				/* 60 */
+	USHORT	Capabilities;				/* 62 */
+	USHORT	Reserved2;					/* 64 */
+	UCHAR	VendorUnique3;				/* 66 */
+	UCHAR	PioCycleTimingMode;			/* 67 */
+	UCHAR	VendorUnique4;				/* 68 */
+	UCHAR	DmaCycleTimingMode;			/* 69 */
+	USHORT	TranslationFieldsValid;		/* 6A */
+	USHORT	NumberOfCurrentCylinders;	/* 6C */
+	USHORT	NumberOfCurrentHeads;		/* 6E */
+	USHORT	CurrentSectorsPerTrack;		/* 70 */
+	ULONG	CurrentSectorCapacity;		/* 72 */
+	}	CNT_IDE_INQ;
+
+typedef struct	_DASD_INQUIRE
+	{
+	ULONG	type;						/* 0 = SCSI, 1 = IDE */
+	union
+		{
+		CNT_SCSI_INQ	scsi;			/* SCSI inquire data */
+		CNT_IDE_INQ		ide;			/* IDE inquire data */
+		}	inq;
+	}	DASD_INQUIRE;
+
+/************************************************/
+/*												*/
+/*	Device Codes								*/
+/*												*/
+/************************************************/
+#define DEVC_DASD			0x00		/* Direct-access Storage Device */
+#define DEVC_SEQACESS		0x01		/* Sequential-access device */
+#define DEVC_PRINTER		0x02		/* Printer device */
+#define DEVC_PROCESSOR		0x03		/* Processor device */
+#define DEVC_WRITEONCE		0x04		/* Write-once device */
+#define DEVC_CDROM			0x05		/* CD-ROM device */
+#define DEVC_SCANNER		0x06		/* Scanner device */
+#define DEVC_OPTICAL		0x07		/* Optical memory device */
+#define DEVC_MEDCHGR		0x08		/* Medium changer device */
+#define	DEVC_DASD_REMOVABLE	0x80		/* Direct-access storage device, Removable */
+#define	DEVC_NONE			0xFF		/* no device */
+
+// SCSI controls for RAID
+#define	SC_MY_RAID			0xBF			// our special CDB command byte for Win95... interface
+#define	MY_SCSI_QUERY0		0x31			// byte 1 subcommand to query driver for RAID 0 informatation
+#define	MY_SCSI_QUERY1		0x32			// byte 1 subcommand to query driver for RAID 1 informatation
+#define	MY_SCSI_QUERY5		0x33			// byte 1 subcommand to query driver for RAID 5 informatation
+#define	MY_SCSI_REBUILD		0x40			// byte 1 subcommand to reconstruct a mirrored pair
+#define MY_SCSI_DEMOFAIL	0x54			// byte 1 subcommand for RAID failure demonstration
+#define	MY_SCSI_ALARMMUTE	0x60			// byte 1 subcommand to mute any alarm currently on
+
+
+#endif
+
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- linux.vanilla/drivers/scsi/scsi.c	Sun Dec  6 00:14:44 1998
+++ linux/drivers/scsi/scsi.c	Fri Mar 26 17:03:17 1999
@@ -288,12 +288,14 @@
 {"PIONEER","CD-ROM DRM-600","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"PIONEER","CD-ROM DRM-602X","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"PIONEER","CD-ROM DRM-604X","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"PIONEER","CD-ROM DRM-1804X","*", 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 */
 {"nCipher","Fastness Crypto","*", BLIST_FORCELUN},
+{"iomega","jaz 1GB","J.86", BLIST_NOTQ | BLIST_NOLUN},
 /*
  * Must be at end of list...
  */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/scsi_syms.c linux/drivers/scsi/scsi_syms.c
--- linux.vanilla/drivers/scsi/scsi_syms.c	Sun Jun 21 18:41:14 1998
+++ linux/drivers/scsi/scsi_syms.c	Fri Mar 26 17:03:24 1999
@@ -26,13 +26,11 @@
 #include "constants.h"
 
 #include "sd.h"
+#include <scsi/scsicam.h>
 /*
  * This source file contains the symbol table used by scsi loadable
  * modules.
  */
-extern int scsicam_bios_param (Disk * disk,
-                               int dev,	int *ip	); 
-
 
 extern void print_command (unsigned char *command);
 extern void print_sense(const char * devclass, Scsi_Cmnd * SCpnt);
@@ -47,6 +45,7 @@
     X(scsi_register),
     X(scsi_unregister),
     X(scsicam_bios_param),
+    X(scsi_partsize),
     X(allocate_device),
     X(scsi_do_cmd),
     X(scsi_command_size),
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/scsicam.c linux/drivers/scsi/scsicam.c
--- linux.vanilla/drivers/scsi/scsicam.c	Sun Dec  6 00:14:45 1998
+++ linux/drivers/scsi/scsicam.c	Fri Mar 26 17:03:30 1999
@@ -26,9 +26,8 @@
 #include "scsi.h"
 #include "hosts.h"
 #include "sd.h"
+#include <scsi/scsicam.h>
 
-static int partsize(struct buffer_head *bh, unsigned long capacity,
-    unsigned int  *cyls, unsigned int *hds, unsigned int *secs);
 static int setsize(unsigned long capacity,unsigned int *cyls,unsigned int *hds,
     unsigned int *secs);
 
@@ -56,7 +55,7 @@
 	return -1;
 
     /* try to infer mapping from partition table */
-    ret_code = partsize (bh, (unsigned long) size, (unsigned int *) ip + 2, 
+    ret_code = scsi_partsize (bh, (unsigned long) size, (unsigned int *) ip + 2, 
 	(unsigned int *) ip + 0, (unsigned int *) ip + 1);
     brelse (bh);
 
@@ -85,7 +84,7 @@
 }
 
 /*
- * Function : static int partsize(struct buffer_head *bh, unsigned long 
+ * Function : static int scsi_partsize(struct buffer_head *bh, unsigned long 
  *     capacity,unsigned int *cyls, unsigned int *hds, unsigned int *secs);
  *
  * Purpose : to determine the BIOS mapping used to create the partition
@@ -95,7 +94,7 @@
  *
  */
 
-static int partsize(struct buffer_head *bh, unsigned long capacity,
+int scsi_partsize(struct buffer_head *bh, unsigned long capacity,
     unsigned int  *cyls, unsigned int *hds, unsigned int *secs) {
     struct partition *p, *largest = NULL;
     int i, largest_cyl;
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/scsiiom.c linux/drivers/scsi/scsiiom.c
--- linux.vanilla/drivers/scsi/scsiiom.c	Sun Jun 21 18:41:18 1998
+++ linux/drivers/scsi/scsiiom.c	Sun Dec 27 20:31:40 1998
@@ -4,203 +4,264 @@
  *	Description: Device Driver for Tekram DC-390 (T) PCI SCSI      *
  *		     Bus Master Host Adapter			       *
  ***********************************************************************/
+/* $Id: scsiiom.c,v 2.15 1998/12/25 17:33:27 garloff Exp $ */
 
-
-static USHORT
-DC390_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB )
+UCHAR
+dc390_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB )
 {
-    USHORT ioport, rc;
-    UCHAR  bval, bval1, i, cnt;
-    PUCHAR ptr;
-    ULONG  wlval;
+    USHORT wlval;
+    UCHAR  bval, bval1;
 
     pSRB->TagNumber = 31;
-    ioport = pACB->IOPortBase;
-    bval = pDCB->UnitSCSIID;
-    outb(bval,ioport+Scsi_Dest_ID);
-    bval = pDCB->SyncPeriod;
-    outb(bval,ioport+Sync_Period);
-    bval = pDCB->SyncOffset;
-    outb(bval,ioport+Sync_Offset);
-    bval = pDCB->CtrlR1;
-    outb(bval,ioport+CtrlReg1);
-    bval = pDCB->CtrlR3;
-    outb(bval,ioport+CtrlReg3);
-    bval = pDCB->CtrlR4;
-    outb(bval,ioport+CtrlReg4);
-    bval = CLEAR_FIFO_CMD;	   /* Flush FIFO */
-    outb(bval,ioport+ScsiCmd);
-
+    DC390_write8 (Scsi_Dest_ID, pDCB->UnitSCSIID);
+    DC390_write8 (Sync_Period, pDCB->SyncPeriod);
+    DC390_write8 (Sync_Offset, pDCB->SyncOffset);
+    DC390_write8 (CtrlReg1, pDCB->CtrlR1);
+    DC390_write8 (CtrlReg3, pDCB->CtrlR3);
+    DC390_write8 (CtrlReg4, pDCB->CtrlR4);
+    DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);		/* Flush FIFO */
+    DEBUG1(printk (KERN_INFO "DC390: Start SCSI command: %02x (Sync:%02x)\n",\
+	    pSRB->CmdBlock[0], pDCB->SyncMode);)
     pSRB->ScsiPhase = SCSI_NOP0;
+    //pSRB->MsgOutBuf[0] = MSG_NOP;
+    //pSRB->MsgCnt = 0;
     bval = pDCB->IdentifyMsg;
-    if( !(pDCB->SyncMode & EN_ATN_STOP) )
+    if( !(pDCB->SyncMode & EN_ATN_STOP) )	/* Don't always try send Extended messages on arbitration */
     {
 	if( (pSRB->CmdBlock[0] == INQUIRY) ||
 	    (pSRB->CmdBlock[0] == REQUEST_SENSE) ||
 	    (pSRB->SRBFlag & AUTO_REQSENSE) )
 	{
-	    bval &= 0xBF;	/* NO disconnection */
-	    outb(bval,ioport+ScsiFifo);
-	    bval1 = SELECT_W_ATN;
+	    bval &= 0xBF;	/* No DisConn */
+	    DC390_write8 (ScsiFifo, bval);
+	    bval1 = SEL_W_ATN;
 	    pSRB->SRBState = SRB_START_;
+	    DEBUG1(printk (KERN_DEBUG "DC390: No DisCn, No TagQ (%02x, %02x)\n", bval, bval1);)
 	    if( pDCB->SyncMode & SYNC_ENABLE )
-	    {
-		if( !(pDCB->IdentifyMsg & 7) ||
+		{
+		if( !(pDCB->IdentifyMsg & 7) ||		/* LUN == 0 || Cmd != INQUIRY */
 		    (pSRB->CmdBlock[0] != INQUIRY) )
 		{
-		    bval1 = SEL_W_ATN_STOP;
+		    bval1 = SEL_W_ATN_STOP;	/* Try to establish SYNC nego */
 		    pSRB->SRBState = SRB_MSGOUT;
 		}
 	    }
 	}
-	else
+	else	/* TagQ ? */
 	{
-	    if(pDCB->SyncMode & EN_TAG_QUEUING)
+	    DC390_write8 (ScsiFifo, bval);
+	    if(pDCB->SyncMode & EN_TAG_QUEUEING)
 	    {
-		outb(bval,ioport+ScsiFifo);
-		bval = MSG_SIMPLE_QTAG;
-		outb(bval,ioport+ScsiFifo);
-		wlval = 1;
-		bval = 0;
-		while( wlval & pDCB->TagMask )
-		{
-		    wlval = wlval << 1;
-		    bval++;
-		}
-		outb(bval,ioport+ScsiFifo);
+	        DC390_write8 (ScsiFifo, MSG_SIMPLE_QTAG);
+		DEBUG1(printk (KERN_DEBUG "DC390: %sDisCn, TagQ (%02x, %02x, %08lx)\n", (bval&0x40?"":"No "), bval, SEL_W_ATN3, pDCB->TagMask);)
+	        bval = 0; wlval = 1;
+	        while (wlval & pDCB->TagMask)
+		  { bval++; wlval <<= 1; };
 		pDCB->TagMask |= wlval;
+	        DC390_write8 (ScsiFifo, bval);
 		pSRB->TagNumber = bval;
-		bval1 = SEL_W_ATN2;
+		DEBUG1(printk (KERN_DEBUG "DC390: SRB %p (Cmd %li), Tag %02x queued\n", pSRB, pSRB->pcmd->pid, bval);)
+		bval1 = SEL_W_ATN3;
 		pSRB->SRBState = SRB_START_;
 	    }
-	    else
+	    else	/* No TagQ */
 	    {
-		outb(bval,ioport+ScsiFifo);
-		bval1 = SELECT_W_ATN;
+		bval1 = SEL_W_ATN;
+		DEBUG1(printk (KERN_DEBUG "DC390: %sDisCn, No TagQ (%02x, %02x, %08lx)\n", (bval&0x40?"":"No "), bval, bval1, pDCB->TagMask);)
 		pSRB->SRBState = SRB_START_;
 	    }
 	}
 
-	if( pSRB->SRBFlag & AUTO_REQSENSE )
-	{
-	    bval = REQUEST_SENSE;
-	    outb(bval,ioport+ScsiFifo);
-	    bval = pDCB->IdentifyMsg << 5;
-	    outb(bval,ioport+ScsiFifo);
-	    bval = 0;
-	    outb(bval,ioport+ScsiFifo);
-	    outb(bval,ioport+ScsiFifo);
-	    bval = sizeof(pSRB->pcmd->sense_buffer);
-	    outb(bval,ioport+ScsiFifo);
-	    bval = 0;
-	    outb(bval,ioport+ScsiFifo);
-	}
-	else
-	{
-	    cnt = pSRB->ScsiCmdLen;
-	    ptr = (PUCHAR) pSRB->CmdBlock;
-	    for(i=0; i<cnt; i++)
-	    {
-		bval = *ptr++;
-		outb(bval,ioport+ScsiFifo);
-	    }
-	}
     }
-    else	/* ATN_STOP */
+    else	/* ATN_STOP: Always try to establish Sync nego */
     {
 	if( (pSRB->CmdBlock[0] == INQUIRY) ||
 	    (pSRB->CmdBlock[0] == REQUEST_SENSE) ||
 	    (pSRB->SRBFlag & AUTO_REQSENSE) )
 	{
-	    bval &= 0xBF;
-	    outb(bval,ioport+ScsiFifo);
-	    bval1 = SELECT_W_ATN;
+	    bval &= 0xBF;	/* No DisConn */
+	    DC390_write8 (ScsiFifo, bval);
+	    bval1 = SEL_W_ATN;
+	    DEBUG1(printk (KERN_DEBUG "DC390: No DisCn, No TagQ (%02x, %02x)\n", bval, bval1);)
 	    pSRB->SRBState = SRB_START_;
+	      /* ??? */
 	    if( pDCB->SyncMode & SYNC_ENABLE )
 	    {
-		if( !(pDCB->IdentifyMsg & 7) ||
+		if( !(pDCB->IdentifyMsg & 7) ||		/* LUN == 0 || Cmd != INQUIRY */
 		    (pSRB->CmdBlock[0] != INQUIRY) )
 		{
-		    bval1 = SEL_W_ATN_STOP;
+		    bval1 = SEL_W_ATN_STOP;	/* Try to establish Sync nego */
 		    pSRB->SRBState = SRB_MSGOUT;
 		}
 	    }
 	}
-	else
+	else	/* TagQ ? */
 	{
-	    if(pDCB->SyncMode & EN_TAG_QUEUING)
+	    DC390_write8 (ScsiFifo, bval);
+	    if(pDCB->SyncMode & EN_TAG_QUEUEING)
 	    {
-		outb(bval,ioport+ScsiFifo);
 		pSRB->MsgOutBuf[0] = MSG_SIMPLE_QTAG;
-		wlval = 1;
-		bval = 0;
-		while( wlval & pDCB->TagMask )
-		{
-		    wlval = wlval << 1;
-		    bval++;
-		}
+		DEBUG1(printk (KERN_DEBUG "DC390: %sDisCn, TagQ (%02x, %02x, %08lx)\n", (bval&0x40?"":"No "), bval, SEL_W_ATN_STOP, pDCB->TagMask);)
+		bval = 0; wlval = 1;
+	        while (wlval & pDCB->TagMask)
+		  { bval++; wlval <<= 1; };
 		pDCB->TagMask |= wlval;
 		pSRB->TagNumber = bval;
+		DEBUG1(printk (KERN_DEBUG "DC390: SRB %p (Cmd %li), Tag %02x queued\n", pSRB, pSRB->pcmd->pid, bval);)
 		pSRB->MsgOutBuf[1] = bval;
 		pSRB->MsgCnt = 2;
 		bval1 = SEL_W_ATN_STOP;
-		pSRB->SRBState = SRB_START_;
+		pSRB->SRBState = SRB_START_;	/* ?? */
 	    }
-	    else
+	    else	/* No TagQ */
 	    {
-		outb(bval,ioport+ScsiFifo);
 		pSRB->MsgOutBuf[0] = MSG_NOP;
 		pSRB->MsgCnt = 1;
 		pSRB->SRBState = SRB_START_;
 		bval1 = SEL_W_ATN_STOP;
-	    }
+		DEBUG1(printk (KERN_DEBUG "DC390: %sDisCn, No TagQ (%02x, %02x, %08lx)\n", (bval&0x40?"":"No "), bval, bval1, pDCB->TagMask);)
+	    };
 	}
     }
-    bval = inb( ioport+Scsi_Status );
-    if( bval & INTERRUPT )
+    if (bval1 != SEL_W_ATN_STOP)
+      {	/* Command is written in CommandPhase, if SEL_W_ATN_STOP ... */
+	if( pSRB->SRBFlag & AUTO_REQSENSE )
+	  {
+	    bval = 0;
+	    DC390_write8 (ScsiFifo, REQUEST_SENSE);
+	    DC390_write8 (ScsiFifo, pDCB->IdentifyMsg << 5);
+	    DC390_write8 (ScsiFifo, bval);
+	    DC390_write8 (ScsiFifo, bval);
+	    DC390_write8 (ScsiFifo, sizeof(pSRB->pcmd->sense_buffer));
+	    DC390_write8 (ScsiFifo, bval);
+	    DEBUG1(printk (KERN_DEBUG "DC390: AutoReqSense !\n");)
+	  }
+	else	/* write cmnd to bus */ 
+	  {
+	    PUCHAR ptr; UCHAR i;
+	    ptr = (PUCHAR) pSRB->CmdBlock;
+	    for (i=0; i<pSRB->ScsiCmdLen; i++)
+	      DC390_write8 (ScsiFifo, *(ptr++));
+	  };
+      }
+
+    /* Check if we can't win arbitration */
+    if (DC390_read8 (Scsi_Status) & INTERRUPT)
     {
 	pSRB->SRBState = SRB_READY;
 	pDCB->TagMask &= ~( 1 << pSRB->TagNumber );
-	rc = 1;
+	DEBUG0(printk (KERN_WARNING "DC390: Interrupt during StartSCSI!\n");)
+	return 1;
     }
     else
     {
 	pSRB->ScsiPhase = SCSI_NOP1;
+	DEBUG0(if (pACB->pActiveDCB)	\
+		    printk (KERN_WARNING "DC390: ActiveDCB != 0\n");)
+	DEBUG0(if (pDCB->pActiveSRB)	\
+		    printk (KERN_WARNING "DC390: ActiveSRB != 0\n");)
 	pACB->pActiveDCB = pDCB;
 	pDCB->pActiveSRB = pSRB;
-	rc = 0;
-	outb(bval1,ioport+ScsiCmd);
+	//DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
+	DC390_write8 (ScsiCmd, bval1);
+	return 0;
     }
-    return( rc );
 }
 
+//#define DMA_INT EN_DMA_INT /*| EN_PAGE_INT*/
+#define DMA_INT 0
+
+#if DMA_INT
+/* This is similar to AM53C974.c ... */
+static UCHAR 
+dc390_dma_intr (PACB pACB)
+{
+  PSRB pSRB;
+  UCHAR dstate;
+  DEBUG0(USHORT pstate;PDEVDECL1;)
+  
+  DEBUG0(PDEVSET1;)
+  DEBUG0(PCI_READ_CONFIG_WORD (PDEV, PCI_STATUS, &pstate);)
+  DEBUG0(if (pstate & (PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY))\
+	{ printk(KERN_WARNING "DC390: PCI state = %04x!\n", pstate); \
+	  PCI_WRITE_CONFIG_WORD (PDEV, PCI_STATUS, (PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY));};)
+
+  dstate = DC390_read8 (DMA_Status); 
+
+  if (! pACB->pActiveDCB || ! pACB->pActiveDCB->pActiveSRB) return dstate;
+  else pSRB  = pACB->pActiveDCB->pActiveSRB;
+  
+  if (dstate & (DMA_XFER_ABORT | DMA_XFER_ERROR | POWER_DOWN | PCI_MS_ABORT))
+    {
+	printk (KERN_ERR "DC390: DMA error (%02x)!\n", dstate);
+	return dstate;
+    };
+  if (dstate & DMA_XFER_DONE)
+    {
+	ULONG residual, xferCnt; int ctr = 5000000;
+	if (! (DC390_read8 (DMA_Cmd) & READ_DIRECTION))
+	  {
+	    do
+	      {
+		DEBUG1(printk (KERN_DEBUG "DC390: read residual bytes ... \n");)
+		dstate = DC390_read8 (DMA_Status);
+		residual = DC390_read8 (CtcReg_Low) | DC390_read8 (CtcReg_Mid) << 8 |
+		  DC390_read8 (CtcReg_High) << 16;
+		residual += DC390_read8 (Current_Fifo) & 0x1f;
+	      } while (residual && ! (dstate & SCSI_INTERRUPT) && --ctr);
+	    if (!ctr) printk (KERN_CRIT "DC390: dma_intr: DMA aborted unfinished: %06x bytes remain!!\n", DC390_read32 (DMA_Wk_ByteCntr));
+	    /* residual =  ... */
+	  }
+	else
+	    residual = 0;
+	
+	/* ??? */
+	
+	xferCnt = pSRB->SGToBeXferLen - residual;
+	pSRB->SGBusAddr += xferCnt;
+	pSRB->TotalXferredLen += xferCnt;
+	pSRB->SGToBeXferLen = residual;
+# ifdef DC390_DEBUG0
+	printk (KERN_INFO "DC390: DMA: residual = %i, xfer = %i\n", 
+		(unsigned int)residual, (unsigned int)xferCnt);
+# endif
+	
+	DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
+    }
+  dc390_laststatus &= ~0xff000000; dc390_laststatus |= dstate << 24;
+  return dstate;
+};
+#endif
 
-#ifndef  VERSION_ELF_1_2_13
-static void
+void __inline__
 DC390_Interrupt( int irq, void *dev_id, struct pt_regs *regs)
-#else
-static void
-DC390_Interrupt( int irq, struct pt_regs *regs)
-#endif
 {
     PACB   pACB;
     PDCB   pDCB;
     PSRB   pSRB;
-    USHORT ioport = 0;
-    USHORT phase, i;
+    UCHAR  sstatus=0;
+    UCHAR  phase, i;
     void   (*stateV)( PACB, PSRB, PUCHAR );
-    UCHAR  istate = 0;
-    UCHAR  sstatus=0, istatus;
+    UCHAR  istate, istatus;
+#if DMA_INT
+    UCHAR  dstatus;
+#endif
+    DC390_AFLAGS DC390_IFLAGS DC390_DFLAGS
 
-    pACB = pACB_start;
-    if( pACB == NULL )
+    pACB = dc390_pACB_start;
+
+    if (pACB == 0)
+    {
+	printk(KERN_WARNING "DC390: Interrupt on uninitialized adapter!\n");
 	return;
-    for( i=0; i < adapterCnt; i++ )
+    }
+    DC390_LOCK_DRV;
+
+    for( i=0; i < dc390_adapterCnt; i++ )
     {
 	if( pACB->IRQLevel == (UCHAR) irq )
 	{
-	     ioport = pACB->IOPortBase;
-	     sstatus = inb( ioport+Scsi_Status );
+	     sstatus = DC390_read8 (Scsi_Status);
 	     if( sstatus & INTERRUPT )
 		 break;
 	     else
@@ -212,90 +273,133 @@
 	}
     }
 
-#ifdef DC390_DEBUG1
-	printk("sstatus=%2x,",sstatus);
-#endif
-
-    if( pACB == (PACB )-1 )
-    {
-	printk("DC390: Spurious interrupt detected!\n");
-	return;
-    }
+    DEBUG1(printk (KERN_DEBUG "sstatus=%02x,", sstatus);)
 
-    istate = inb( ioport+Intern_State );
-    istatus = inb( ioport+INT_Status );
+    if( !pACB ) { DC390_UNLOCK_DRV; return; };
 
-#ifdef DC390_DEBUG1
-	printk("Istatus=%2x,",istatus);
+#if DMA_INT
+    DC390_LOCK_IO;
+    DC390_LOCK_ACB;
+    dstatus = dc390_dma_intr (pACB);
+    DC390_UNLOCK_ACB;
+    DC390_UNLOCK_IO;
+
+    DEBUG1(printk (KERN_DEBUG "dstatus=%02x,", dstatus);)
+    if (! (dstatus & SCSI_INTERRUPT))
+      {
+	DEBUG0(printk (KERN_WARNING "DC390 Int w/o SCSI actions (only DMA?)\n");)
+	DC390_UNLOCK_DRV;
+	return;
+      };
+#else
+    //DC390_write32 (DMA_ScsiBusCtrl, WRT_ERASE_DMA_STAT | EN_INT_ON_PCI_ABORT);
+    //dstatus = DC390_read8 (DMA_Status);
+    //DC390_write32 (DMA_ScsiBusCtrl, EN_INT_ON_PCI_ABORT);
 #endif
 
+    DC390_LOCK_IO;
+    DC390_LOCK_ACB;
+    DC390_UNLOCK_DRV_NI; /* Allow _other_ CPUs to process IRQ (useful for shared IRQs) */
+
+    istate = DC390_read8 (Intern_State);
+    istatus = DC390_read8 (INT_Status); /* This clears Scsi_Status, Intern_State and INT_Status ! */
+
+    DEBUG1(printk (KERN_INFO "Istatus(Res,Inv,Dis,Serv,Succ,ReS,SelA,Sel)=%02x,",istatus);)
+    dc390_laststatus &= ~0x00ffffff;
+    dc390_laststatus |= /* dstatus<<24 | */ sstatus<<16 | istate<<8 | istatus;
+
+    if (sstatus & ILLEGAL_OP_ERR)
+    {
+	printk ("DC390: Illegal Operation detected (%08lx)!\n", dc390_laststatus);
+	dc390_dumpinfo (pACB, pACB->pActiveDCB, pACB->pActiveDCB->pActiveSRB);
+    };
+	
     if(istatus &  DISCONNECTED)
     {
-	DC390_Disconnect( pACB );
-	return;
+	dc390_Disconnect( pACB );
+	goto unlock;
     }
 
     if(istatus &  RESELECTED)
     {
-	DC390_Reselect( pACB );
-	return;
+	dc390_Reselect( pACB );
+	goto unlock;
     }
 
-    if(istatus &  INVALID_CMD)
+    if( istatus & (SUCCESSFUL_OP|SERVICE_REQUEST) )
     {
-	DC390_InvalidCmd( pACB );
-	return;
+	pDCB = pACB->pActiveDCB;
+	if (!pDCB)
+	{
+		printk (KERN_ERR "DC390: Suc. op/ Serv. req: pActiveDCB = 0!\n");
+		goto unlock;
+	};
+	pSRB = pDCB->pActiveSRB;
+	if( pDCB->DCBFlag & ABORT_DEV_ )
+	  dc390_EnableMsgOut_Abort (pACB, pSRB);
+
+	phase = pSRB->ScsiPhase;
+	DEBUG1(printk (KERN_INFO "DC390: [%i]%s(0) (%02x)\n", phase, dc390_p0_str[phase], sstatus);)
+	stateV = (void *) dc390_phase0[phase];
+	( *stateV )( pACB, pSRB, &sstatus );
+
+	pSRB->ScsiPhase = sstatus & 7;
+	phase = (UCHAR) sstatus & 7;
+	DEBUG1(printk (KERN_INFO "DC390: [%i]%s(1) (%02x)\n", phase, dc390_p1_str[phase], sstatus);)
+	stateV = (void *) dc390_phase1[phase];
+	( *stateV )( pACB, pSRB, &sstatus );
+	goto unlock;
     }
 
-    if(istatus &  SCSI_RESET)
+    if(istatus &  INVALID_CMD)
     {
-	DC390_ScsiRstDetect( pACB );
-	return;
+	dc390_InvalidCmd( pACB );
+	goto unlock;
     }
 
-    if( istatus & (SUCCESSFUL_OP+SERVICE_REQUEST) )
+    if(istatus &  SCSI_RESET)
     {
-	pDCB = pACB->pActiveDCB;
-	pSRB = pDCB->pActiveSRB;
-	if( pDCB )
-	{
-	    if( pDCB->DCBFlag & ABORT_DEV_ )
-		EnableMsgOut( pACB, pSRB );
-	}
-
-	phase = (USHORT) pSRB->ScsiPhase;
-	stateV = (void *) DC390_phase0[phase];
-	stateV( pACB, pSRB, &sstatus );
-
-	pSRB->ScsiPhase = sstatus & 7;
-	phase = (USHORT) sstatus & 7;
-	stateV = (void *) DC390_phase1[phase];
-	stateV( pACB, pSRB, &sstatus );
+	dc390_ScsiRstDetect( pACB );
+	goto unlock;
     }
+
+ unlock:
+    DC390_LOCK_DRV_NI;
+    DC390_UNLOCK_ACB;
+    DC390_UNLOCK_IO;
+    DC390_UNLOCK_DRV; /* Restore initial flags */
 }
 
+void
+do_DC390_Interrupt( int irq, void *dev_id, struct pt_regs *regs)
+{
+    DEBUG1(printk (KERN_INFO "DC390: Irq (%i) caught: ", irq);)
+    /* Locking is done in DC390_Interrupt */
+    DC390_Interrupt(irq, dev_id, regs);
+    DEBUG1(printk (".. IRQ returned\n");)
+}
 
-static void
-DC390_DataOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+void
+dc390_DataOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
 {
-    UCHAR   sstatus, bval;
-    USHORT  ioport;
+    UCHAR   sstatus;
     PSGL    psgl;
     ULONG   ResidCnt, xferCnt;
+    UCHAR   dstate = 0;
 
-    ioport = pACB->IOPortBase;
     sstatus = *psstatus;
 
     if( !(pSRB->SRBState & SRB_XFERPAD) )
     {
-	if( sstatus & PARITY_ERR )
+	if( sstatus & (PARITY_ERR | ILLEGAL_OP_ERR) )
 	    pSRB->SRBStatus |= PARITY_ERROR;
 
 	if( sstatus & COUNT_2_ZERO )
 	{
-	    bval = inb(ioport+DMA_Status);
-	    while( !(bval & DMA_XFER_DONE) )
-		bval = inb(ioport+DMA_Status);
+	    int ctr = 5000000; /* only try for about a tenth of a second */
+	    while( --ctr && !((dstate = DC390_read8 (DMA_Status)) & DMA_XFER_DONE) && pSRB->SGToBeXferLen );
+	    if (!ctr) printk (KERN_CRIT "DC390: Deadlock in DataOut_0: DMA aborted unfinished: %06x bytes remain!!\n", DC390_read32 (DMA_Wk_ByteCntr));
+	    dc390_laststatus &= ~0xff000000; dc390_laststatus |= dstate << 24;
 	    pSRB->TotalXferredLen += pSRB->SGToBeXferLen;
 	    pSRB->SGIndex++;
 	    if( pSRB->SGIndex < pSRB->SGcount )
@@ -303,11 +407,7 @@
 		pSRB->pSegmentList++;
 		psgl = pSRB->pSegmentList;
 
-#ifndef VERSION_ELF_1_2_13
-		pSRB->SGPhysAddr = virt_to_phys( psgl->address );
-#else
-		pSRB->SGPhysAddr = (ULONG) psgl->address;
-#endif
+		pSRB->SGBusAddr = virt_to_bus( psgl->address );
 		pSRB->SGToBeXferLen = (ULONG) psgl->length;
 	    }
 	    else
@@ -315,51 +415,49 @@
 	}
 	else
 	{
-	    bval = inb( ioport+Current_Fifo );
-	    bval &= 0x1f;
-	    ResidCnt = (ULONG) inb(ioport+CtcReg_High);
-	    ResidCnt = ResidCnt << 8;
-	    ResidCnt |= (ULONG) inb(ioport+CtcReg_Mid);
-	    ResidCnt = ResidCnt << 8;
-	    ResidCnt |= (ULONG) inb(ioport+CtcReg_Low);
-	    ResidCnt += (ULONG) bval;
+	    ResidCnt = (ULONG) DC390_read8 (Current_Fifo) & 0x1f;
+	    ResidCnt |= (ULONG) DC390_read8 (CtcReg_High) << 16;
+	    ResidCnt |= (ULONG) DC390_read8 (CtcReg_Mid) << 8; 
+	    ResidCnt += (ULONG) DC390_read8 (CtcReg_Low);
 
 	    xferCnt = pSRB->SGToBeXferLen - ResidCnt;
-	    pSRB->SGPhysAddr += xferCnt;
+	    pSRB->SGBusAddr += xferCnt;
 	    pSRB->TotalXferredLen += xferCnt;
 	    pSRB->SGToBeXferLen = ResidCnt;
 	}
     }
-    bval = WRITE_DIRECTION+DMA_IDLE_CMD;
-    outb( bval, ioport+DMA_Cmd);
+    DC390_write8 (DMA_Cmd, WRITE_DIRECTION+DMA_IDLE_CMD); /* | DMA_INT */
 }
 
-static void
-DC390_DataIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+void
+dc390_DataIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
 {
-    UCHAR   sstatus, bval;
-    USHORT  i, ioport, residual;
+    UCHAR   sstatus, residual, bval;
     PSGL    psgl;
-    ULONG   ResidCnt, xferCnt;
+    ULONG   ResidCnt, xferCnt, i;
     PUCHAR  ptr;
 
-
-    ioport = pACB->IOPortBase;
     sstatus = *psstatus;
 
     if( !(pSRB->SRBState & SRB_XFERPAD) )
     {
-	if( sstatus & PARITY_ERR )
+	if( sstatus & (PARITY_ERR | ILLEGAL_OP_ERR))
 	    pSRB->SRBStatus |= PARITY_ERROR;
 
 	if( sstatus & COUNT_2_ZERO )
 	{
-	    bval = inb(ioport+DMA_Status);
-	    while( !(bval & DMA_XFER_DONE) )
-		bval = inb(ioport+DMA_Status);
+	    int ctr = 5000000; /* only try for about a tenth of a second */
+	    int dstate = 0;
+	    while( --ctr && !((dstate = DC390_read8 (DMA_Status)) & DMA_XFER_DONE) && pSRB->SGToBeXferLen );
+	    if (!ctr) printk (KERN_CRIT "DC390: Deadlock in DataIn_0: DMA aborted unfinished: %06x bytes remain!!\n", DC390_read32 (DMA_Wk_ByteCntr));
+	    if (!ctr) printk (KERN_CRIT "DC390: DataIn_0: DMA State: %i\n", dstate);
+	    dc390_laststatus &= ~0xff000000; dc390_laststatus |= dstate << 24;
+	    DEBUG1(ResidCnt = ((ULONG) DC390_read8 (CtcReg_High) << 16)	\
+		+ ((ULONG) DC390_read8 (CtcReg_Mid) << 8)		\
+		+ ((ULONG) DC390_read8 (CtcReg_Low));)
+	    DEBUG1(printk (KERN_DEBUG "Count_2_Zero (ResidCnt=%li,ToBeXfer=%li),", ResidCnt, pSRB->SGToBeXferLen);)
 
-	    bval = READ_DIRECTION+DMA_IDLE_CMD;
-	    outb( bval, ioport+DMA_Cmd);
+	    DC390_write8 (DMA_Cmd, READ_DIRECTION+DMA_IDLE_CMD); /* | DMA_INT */
 
 	    pSRB->TotalXferredLen += pSRB->SGToBeXferLen;
 	    pSRB->SGIndex++;
@@ -368,11 +466,7 @@
 		pSRB->pSegmentList++;
 		psgl = pSRB->pSegmentList;
 
-#ifndef VERSION_ELF_1_2_13
-		pSRB->SGPhysAddr = virt_to_phys( psgl->address );
-#else
-		pSRB->SGPhysAddr = (ULONG) psgl->address;
-#endif
+		pSRB->SGBusAddr = virt_to_bus( psgl->address );
 		pSRB->SGToBeXferLen = (ULONG) psgl->length;
 	    }
 	    else
@@ -381,14 +475,15 @@
 	else	/* phase changed */
 	{
 	    residual = 0;
-	    bval = inb(ioport+Current_Fifo);
+	    bval = DC390_read8 (Current_Fifo);
 	    while( bval & 0x1f )
 	    {
+		DEBUG1(printk (KERN_DEBUG "Check for residuals,");)
 		if( (bval & 0x1f) == 1 )
 		{
-		    for(i=0; i< 0x100; i++)
+		    for(i=0; i < 0x100; i++)
 		    {
-			bval = inb(ioport+Current_Fifo);
+			bval = DC390_read8 (Current_Fifo);
 			if( !(bval & 0x1f) )
 			    goto din_1;
 			else if( i == 0x0ff )
@@ -399,283 +494,383 @@
 		    }
 		}
 		else
-		    bval = inb(ioport+Current_Fifo);
+		    bval = DC390_read8 (Current_Fifo);
 	    }
 din_1:
-	    bval = READ_DIRECTION+DMA_BLAST_CMD;
-	    outb(bval, ioport+DMA_Cmd);
-	    for(i=0; i<0x8000; i++)
+	    DC390_write8 (DMA_Cmd, READ_DIRECTION+DMA_BLAST_CMD);
+	    for (i = 0xa000; i; i--)
 	    {
-		bval = inb(ioport+DMA_Status);
-		if(bval & BLAST_COMPLETE)
+		bval = DC390_read8 (DMA_Status);
+		if (bval & BLAST_COMPLETE)
 		    break;
 	    }
-	    bval = READ_DIRECTION+DMA_IDLE_CMD;
-	    outb(bval, ioport+DMA_Cmd);
-
-	    ResidCnt = (ULONG) inb(ioport+CtcReg_High);
-	    ResidCnt = ResidCnt << 8;
-	    ResidCnt |= (ULONG) inb(ioport+CtcReg_Mid);
-	    ResidCnt = ResidCnt << 8;
-	    ResidCnt |= (ULONG) inb(ioport+CtcReg_Low);
+	    /* It seems a DMA Blast abort isn't that bad ... */
+	    if (!i) printk (KERN_ERR "DC390: DMA Blast aborted unfinished!\n");
+	    //DC390_write8 (DMA_Cmd, READ_DIRECTION+DMA_IDLE_CMD); /* | DMA_INT */
+	    dc390_laststatus &= ~0xff000000; dc390_laststatus |= bval << 24;
+
+	    DEBUG1(printk (KERN_DEBUG "Blast: Read %li times DMA_Status %02x", 0xa000-i, bval);)
+	    ResidCnt = (ULONG) DC390_read8 (CtcReg_High);
+	    ResidCnt <<= 8;
+	    ResidCnt |= (ULONG) DC390_read8 (CtcReg_Mid);
+	    ResidCnt <<= 8;
+	    ResidCnt |= (ULONG) DC390_read8 (CtcReg_Low);
 
 	    xferCnt = pSRB->SGToBeXferLen - ResidCnt;
-	    pSRB->SGPhysAddr += xferCnt;
+	    pSRB->SGBusAddr += xferCnt;
 	    pSRB->TotalXferredLen += xferCnt;
 	    pSRB->SGToBeXferLen = ResidCnt;
 
 	    if( residual )
 	    {
-		bval = inb(ioport+ScsiFifo);	    /* get residual byte */
-#ifndef VERSION_ELF_1_2_13
-		ptr = (PUCHAR) phys_to_virt( pSRB->SGPhysAddr );
-#else
-		ptr = (PUCHAR) pSRB->SGPhysAddr;
-#endif
+		bval = DC390_read8 (ScsiFifo);	    /* get one residual byte */
+		ptr = (PUCHAR) bus_to_virt( pSRB->SGBusAddr );
 		*ptr = bval;
-		pSRB->SGPhysAddr++;
+		pSRB->SGBusAddr++; xferCnt++;
 		pSRB->TotalXferredLen++;
 		pSRB->SGToBeXferLen--;
 	    }
+	    DEBUG1(printk (KERN_DEBUG "Xfered: %li, Total: %li, Remaining: %li\n", xferCnt,\
+			   pSRB->TotalXferredLen, pSRB->SGToBeXferLen);)
+
 	}
     }
 }
 
 static void
-DC390_Command_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+dc390_Command_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
 {
 }
 
 static void
-DC390_Status_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+dc390_Status_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
 {
-    UCHAR  bval;
-    USHORT ioport;
 
-    ioport = pACB->IOPortBase;
-    bval = inb(ioport+ScsiFifo);
-    pSRB->TargetStatus = bval;
-    bval++;
-    bval = inb(ioport+ScsiFifo);	/* get message */
-    pSRB->EndMessage = bval;
+    pSRB->TargetStatus = DC390_read8 (ScsiFifo);
+    //udelay (1);
+    pSRB->EndMessage = DC390_read8 (ScsiFifo);	/* get message */
 
     *psstatus = SCSI_NOP0;
     pSRB->SRBState = SRB_COMPLETED;
-    bval = MSG_ACCEPTED_CMD;
-    outb(bval, ioport+ScsiCmd);
+    DC390_write8 (ScsiCmd, MSG_ACCEPTED_CMD);
 }
 
 static void
-DC390_MsgOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+dc390_MsgOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
 {
     if( pSRB->SRBState & (SRB_UNEXPECT_RESEL+SRB_ABORT_SENT) )
 	*psstatus = SCSI_NOP0;
+    //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
 }
 
+
+static void __inline__
+dc390_reprog (PACB pACB, PDCB pDCB)
+{
+  DC390_write8 (Sync_Period, pDCB->SyncPeriod);
+  DC390_write8 (Sync_Offset, pDCB->SyncOffset);
+  DC390_write8 (CtrlReg3, pDCB->CtrlR3);
+  DC390_write8 (CtrlReg4, pDCB->CtrlR4);
+  dc390_SetXferRate (pACB, pDCB);
+};
+
+
+#ifdef DC390_DEBUG0
 static void
-DC390_MsgIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+dc390_printMsg (UCHAR *MsgBuf, UCHAR len)
 {
-    UCHAR  bval;
-    USHORT ioport, wval, wval1;
-    PDCB   pDCB;
-    PSRB   psrb;
+  int i;
+  printk (" %02x", MsgBuf[0]);
+  for (i = 1; i < len; i++)
+    printk (" %02x", MsgBuf[i]);
+  printk ("\n");
+};
+#endif
 
-    ioport = pACB->IOPortBase;
-    pDCB = pACB->pActiveDCB;
+#define DC390_ENABLE_MSGOUT DC390_write8 (ScsiCmd, SET_ATN_CMD)
 
-    bval = inb( ioport+ScsiFifo );
-    if( !(pSRB->SRBState & SRB_MSGIN_MULTI) )
+/* reject_msg */
+static void __inline__
+dc390_MsgIn_reject (PACB pACB, PSRB pSRB)
+{
+  pSRB->MsgOutBuf[0] = MSG_REJECT_;
+  pSRB->MsgCnt = 1; DC390_ENABLE_MSGOUT;
+  DEBUG0 (printk (KERN_INFO "DC390: Reject message\n");)
+}
+
+/* abort command */
+static void __inline__
+dc390_EnableMsgOut_Abort ( PACB pACB, PSRB pSRB )
+{
+    pSRB->MsgOutBuf[0] = MSG_ABORT; 
+    pSRB->MsgCnt = 1; DC390_ENABLE_MSGOUT;
+    pSRB->pSRBDCB->DCBFlag &= ~ABORT_DEV_;
+}
+
+static PSRB
+dc390_MsgIn_QTag (PACB pACB, PDCB pDCB, UCHAR tag)
+{
+  PSRB lastSRB = pDCB->pGoingLast;
+  PSRB pSRB = pDCB->pGoingSRB;
+
+  if (pSRB)
     {
-	if(bval == MSG_DISCONNECT)
-	{
-	    pSRB->SRBState = SRB_DISCONNECT;
-	}
-	else if( bval == MSG_SAVE_PTR )
-	    goto  min6;
-	else if( (bval == MSG_EXTENDED) || ((bval >= MSG_SIMPLE_QTAG) &&
-					    (bval <= MSG_ORDER_QTAG)) )
+      for( ;pSRB ; )
 	{
-	    pSRB->SRBState |= SRB_MSGIN_MULTI;
-	    pSRB->MsgInBuf[0] = bval;
-	    pSRB->MsgCnt = 1;
-	    pSRB->pMsgPtr = &pSRB->MsgInBuf[1];
+	  if (pSRB->TagNumber == tag) break;
+	  if (pSRB == lastSRB) goto mingx0;
+	  pSRB = pSRB->pNextSRB;
 	}
-	else if(bval == MSG_REJECT_)
+
+      if( pDCB->DCBFlag & ABORT_DEV_ )
 	{
-	    bval = RESET_ATN_CMD;
-	    outb(bval, ioport+ScsiCmd);
-	    if( pSRB->SRBState & DO_SYNC_NEGO)
-		goto  set_async;
+	  pSRB->SRBState = SRB_ABORT_SENT;
+	  dc390_EnableMsgOut_Abort( pACB, pSRB );
 	}
-	else if( bval == MSG_RESTORE_PTR)
-	    goto  min6;
-	else
-	    goto  min6;
+
+      if( !(pSRB->SRBState & SRB_DISCONNECT) )
+	goto  mingx0;
+
+      pDCB->pActiveSRB = pSRB;
+      pSRB->SRBState = SRB_DATA_XFER;
     }
-    else
-    {	/* minx: */
+  else
+    {
+    mingx0:
+      pSRB = pACB->pTmpSRB;
+      pSRB->SRBState = SRB_UNEXPECT_RESEL;
+      pDCB->pActiveSRB = pSRB;
+      pSRB->MsgOutBuf[0] = MSG_ABORT_TAG;
+      pSRB->MsgCnt = 1; DC390_ENABLE_MSGOUT;
+    }
+  return pSRB;
+}
 
-	*pSRB->pMsgPtr = bval;
-	pSRB->MsgCnt++;
-	pSRB->pMsgPtr++;
-	if( (pSRB->MsgInBuf[0] >= MSG_SIMPLE_QTAG) &&
-	    (pSRB->MsgInBuf[0] <= MSG_ORDER_QTAG) )
-	{
-	    if( pSRB->MsgCnt == 2)
-	    {
-		pSRB->SRBState = 0;
-		bval = pSRB->MsgInBuf[1];
-		pSRB = pDCB->pGoingSRB;
-		psrb = pDCB->pGoingLast;
-		if( pSRB )
-		{
-		    for( ;; )
-		    {
-			if(pSRB->TagNumber != bval)
-			{
-			    if( pSRB == psrb )
-				goto  mingx0;
-			    pSRB = pSRB->pNextSRB;
-			}
-			else
-			    break;
-		    }
-		    if( pDCB->DCBFlag & ABORT_DEV_ )
-		    {
-			pSRB->SRBState = SRB_ABORT_SENT;
-			EnableMsgOut( pACB, pSRB );
-		    }
-		    if( !(pSRB->SRBState & SRB_DISCONNECT) )
-			goto  mingx0;
-		    pDCB->pActiveSRB = pSRB;
-		    pSRB->SRBState = SRB_DATA_XFER;
-		}
-		else
-		{
-mingx0:
-		    pSRB = pACB->pTmpSRB;
-		    pSRB->SRBState = SRB_UNEXPECT_RESEL;
-		    pDCB->pActiveSRB = pSRB;
-		    pSRB->MsgOutBuf[0] = MSG_ABORT_TAG;
-		    EnableMsgOut2( pACB, pSRB );
-		}
-	    }
+
+/* set async transfer mode */
+static void 
+dc390_MsgIn_set_async (PACB pACB, PSRB pSRB)
+{
+  PDCB pDCB = pSRB->pSRBDCB;
+  if (!(pSRB->SRBState & DO_SYNC_NEGO)) 
+    printk ("DC390: Target %i initiates Non-Sync?\n", pDCB->UnitSCSIID);
+  pSRB->SRBState &= ~DO_SYNC_NEGO;
+  pDCB->SyncMode &= ~(SYNC_ENABLE+SYNC_NEGO_DONE);
+  pDCB->SyncPeriod = 0;
+  pDCB->SyncOffset = 0;
+  //pDCB->NegoPeriod = 50; /* 200ns <=> 5 MHz */
+  pDCB->CtrlR3 = FAST_CLK;	/* fast clock / normal scsi */
+  pDCB->CtrlR4 &= 0x3f;
+  pDCB->CtrlR4 |= pACB->glitch_cfg;	/* glitch eater */
+  dc390_reprog (pACB, pDCB);
+}
+
+/* set sync transfer mode */
+static void
+dc390_MsgIn_set_sync (PACB pACB, PSRB pSRB)
+{
+  UCHAR bval;
+  USHORT wval, wval1;
+  PDCB pDCB = pSRB->pSRBDCB;
+  UCHAR oldsyncperiod = pDCB->SyncPeriod;
+  UCHAR oldsyncoffset = pDCB->SyncOffset;
+  
+  if (!(pSRB->SRBState & DO_SYNC_NEGO))
+    {
+      printk ("DC390: Target %i initiates Sync: %ins %i ... answer ...\n", 
+	      pDCB->UnitSCSIID, pSRB->MsgInBuf[3]<<2, pSRB->MsgInBuf[4]);
+
+      /* reject */
+      //dc390_MsgIn_reject (pACB, pSRB);
+      //return dc390_MsgIn_set_async (pACB, pSRB);
+
+      /* Reply with corrected SDTR Message */
+      if (pSRB->MsgInBuf[4] > 15)
+	{ 
+	  printk ("DC390: Lower Sync Offset to 15\n");
+	  pSRB->MsgInBuf[4] = 15;
 	}
-	else if( (pSRB->MsgInBuf[0] == MSG_EXTENDED) && (pSRB->MsgCnt == 5) )
+      if (pSRB->MsgInBuf[3] < pDCB->NegoPeriod)
 	{
-	    pSRB->SRBState &= ~(SRB_MSGIN_MULTI+DO_SYNC_NEGO);
-	    if( (pSRB->MsgInBuf[1] != 3) || (pSRB->MsgInBuf[2] != 1) )
-	    {	/* reject_msg: */
-		pSRB->MsgCnt = 1;
-		pSRB->MsgInBuf[0] = MSG_REJECT_;
-		bval = SET_ATN_CMD;
-		outb(bval, ioport+ScsiCmd);
-	    }
-	    else if( !(pSRB->MsgInBuf[3]) || !(pSRB->MsgInBuf[4]) )
-	    {
-set_async:
-		pDCB = pSRB->pSRBDCB;
-		pDCB->SyncMode &= ~(SYNC_ENABLE+SYNC_NEGO_DONE);
-		pDCB->SyncPeriod = 0;
-		pDCB->SyncOffset = 0;
-		pDCB->CtrlR3 = FAST_CLK;     /* ;non_fast */
-		pDCB->CtrlR4 &= 0x3f;
-		pDCB->CtrlR4 |= EATER_25NS;  /* ; 25ns glitch eater */
-		goto  re_prog;
-	    }
-	    else
-	    {	/* set_sync: */
+	  printk ("DC390: Set sync nego period to %ins\n", pDCB->NegoPeriod << 2);
+	  pSRB->MsgInBuf[3] = pDCB->NegoPeriod;
+	};
+      memcpy (pSRB->MsgOutBuf, pSRB->MsgInBuf, 5);
+      pSRB->MsgCnt = 5;
+      DC390_ENABLE_MSGOUT;
+    };
 
-		pDCB = pSRB->pSRBDCB;
-		pDCB->SyncMode |= SYNC_ENABLE+SYNC_NEGO_DONE;
-		pDCB->SyncOffset &= 0x0f0;
-		pDCB->SyncOffset |= pSRB->MsgInBuf[4];
-		pDCB->NegoPeriod = pSRB->MsgInBuf[3];
-		wval = (USHORT) pSRB->MsgInBuf[3];
-		wval = wval << 2;
-		wval--;
-		wval1 = wval / 25;
-		if( (wval1 * 25) != wval)
-		    wval1++;
-		bval = FAST_CLK+FAST_SCSI;
-		pDCB->CtrlR4 &= 0x3f;
-		if(wval1 >= 8)
-		{
-		    wval1--;
-		    bval = FAST_CLK;	    /* ;fast clock/normal scsi */
-		    pDCB->CtrlR4 |= EATER_25NS;   /* ;25 ns glitch eater */
-		}
-		pDCB->CtrlR3 = bval;
-		pDCB->SyncPeriod = (UCHAR)wval1;
-re_prog:
-		bval = pDCB->SyncPeriod;
-		outb(bval, ioport+Sync_Period);
-		bval = pDCB->SyncOffset;
-		outb(bval, ioport+Sync_Offset);
-		bval = pDCB->CtrlR3;
-		outb(bval, ioport+CtrlReg3);
-		bval = pDCB->CtrlR4;
-		outb(bval, ioport+CtrlReg4);
-		SetXferRate( pACB, pDCB);
-	    }
-	}
+  pSRB->SRBState &= ~DO_SYNC_NEGO;
+  pDCB->SyncMode |= SYNC_ENABLE+SYNC_NEGO_DONE;
+  pDCB->SyncOffset &= 0x0f0;
+  pDCB->SyncOffset |= pSRB->MsgInBuf[4];
+  pDCB->NegoPeriod = pSRB->MsgInBuf[3];
+
+  wval = (USHORT) pSRB->MsgInBuf[3];
+  wval = wval << 2; wval -= 3; wval1 = wval / 25;	/* compute speed */
+  if( (wval1 * 25) != wval) wval1++;
+  bval = FAST_CLK+FAST_SCSI;	/* fast clock / fast scsi */
+
+  pDCB->CtrlR4 &= 0x3f;		/* Glitch eater: 12ns less than normal */
+  if (pACB->glitch_cfg != NS_TO_GLITCH(0))
+    pDCB->CtrlR4 |= NS_TO_GLITCH(((GLITCH_TO_NS(pACB->glitch_cfg)) - 1));
+  else
+    pDCB->CtrlR4 |= NS_TO_GLITCH(0);
+  if (wval1 < 4) pDCB->CtrlR4 |= NS_TO_GLITCH(0); /* Ultra */
+
+  if (wval1 >= 8)
+    {
+      wval1--;	/* Timing computation differs by 1 from FAST_SCSI */
+      bval = FAST_CLK;		/* fast clock / normal scsi */
+      pDCB->CtrlR4 |= pACB->glitch_cfg; 	/* glitch eater */
+    }
+
+  pDCB->CtrlR3 = bval;
+  pDCB->SyncPeriod = (UCHAR)wval1;
+  
+  if ((oldsyncperiod != wval1 || oldsyncoffset != pDCB->SyncOffset) && pDCB->UnitSCSILUN == 0)
+    {
+      if (! (bval & FAST_SCSI)) wval1++;
+      printk ("DC390: Target %i: Sync transfer %i.%1i MHz, Offset %i\n", pDCB->UnitSCSIID, 
+	      40/wval1, ((40%wval1)*10+wval1/2)/wval1, pDCB->SyncOffset & 0x0f);
+    }
+  
+  dc390_reprog (pACB, pDCB);
+};
+
+
+/* According to the docs, the AM53C974 reads the message and 
+ * generates a Succesful Operation IRQ before asserting ACK for
+ * the last byte (how does it know whether it's the last ?) */
+/* The old code handled it in another way, indicating, that on
+ * every message byte an IRQ is generated and every byte has to
+ * be manually ACKed. Hmmm ?  (KG, 98/11/28) */
+/* The old implementation was correct. Sigh! */
+
+/* Check if the message is complete */
+static UCHAR __inline__
+dc390_MsgIn_complete (UCHAR *msgbuf, ULONG len)
+{ 
+  if (*msgbuf == MSG_EXTENDED)
+    {
+      if (len < 2) return 0;
+      if (len < msgbuf[1] + 2) return 0;
     }
-min6:
+  else if (*msgbuf >= 0x20 && *msgbuf <= 0x2f) // two byte messages
+    if (len < 2) return 0;
+  return 1;
+}
+
+
+
+/* read and eval received messages */
+void
+dc390_MsgIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+    PDCB   pDCB = pACB->pActiveDCB;
+
+    /* Read the msg */
+
+    pSRB->MsgInBuf[pACB->MsgLen++] = DC390_read8 (ScsiFifo);
+    //pSRB->SRBState = 0;
+
+    /* Msg complete ? */
+    if (dc390_MsgIn_complete (pSRB->MsgInBuf, pACB->MsgLen))
+      {
+	DEBUG0 (printk (KERN_INFO "DC390: MsgIn:"); dc390_printMsg (pSRB->MsgInBuf, pACB->MsgLen);)
+	/* Now eval the msg */
+	switch (pSRB->MsgInBuf[0]) 
+	  {
+	  case MSG_DISCONNECT: 
+	    pSRB->SRBState = SRB_DISCONNECT; break;
+	    
+	  case MSG_SIMPLE_QTAG:
+	  case MSG_HEAD_QTAG:
+	  case MSG_ORDER_QTAG:
+	    pSRB = dc390_MsgIn_QTag (pACB, pDCB, pSRB->MsgInBuf[1]);
+	    break;
+	    
+	  case MSG_REJECT_: 
+	    DC390_write8 (ScsiCmd, RESET_ATN_CMD);
+	    pDCB->NegoPeriod = 50; /* 200ns <=> 5 MHz */
+	    if( pSRB->SRBState & DO_SYNC_NEGO)
+	      dc390_MsgIn_set_async (pACB, pSRB);
+	    break;
+	    
+	  case MSG_EXTENDED:
+	    /* reject every extended msg but SDTR */
+	    if (pSRB->MsgInBuf[1] != 3 || pSRB->MsgInBuf[2] != EXTENDED_SDTR)
+	      dc390_MsgIn_reject (pACB, pSRB);
+	    else
+	      {
+		if (pSRB->MsgInBuf[3] == 0 || pSRB->MsgInBuf[4] == 0)
+		  dc390_MsgIn_set_async (pACB, pSRB);
+		else
+		  dc390_MsgIn_set_sync (pACB, pSRB);
+	      };
+	    
+	    // nothing has to be done
+	  case MSG_COMPLETE: break;
+	    
+	    // SAVE POINTER my be ignored as we have the PSRB associated with the
+	    // scsi command. Thanks, Gerard, for pointing it out.
+	  case MSG_SAVE_PTR: break;
+	    // The device might want to restart transfer with a RESTORE
+	  case MSG_RESTORE_PTR: 
+	    printk ("DC390: RESTORE POINTER message received ... reject\n");
+	    // fall through
+
+	    // reject unknown messages
+	  default: dc390_MsgIn_reject (pACB, pSRB);
+	  }
+	
+	/* Clear counter and MsgIn state */
+	pSRB->SRBState &= ~SRB_MSGIN;
+	pACB->MsgLen = 0;
+      };
+
     *psstatus = SCSI_NOP0;
-    bval = MSG_ACCEPTED_CMD;
-    outb(bval, ioport+ScsiCmd);
+    DC390_write8 (ScsiCmd, MSG_ACCEPTED_CMD);
+    //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
 }
 
-static void
-DataIO_Comm( PACB pACB, PSRB pSRB, UCHAR ioDir)
+
+void
+dc390_DataIO_Comm( PACB pACB, PSRB pSRB, UCHAR ioDir)
 {
     PSGL   psgl;
-    UCHAR  bval;
-    USHORT ioport;
     ULONG  lval;
 
-
-    ioport = pACB->IOPortBase;
     if( pSRB->SGIndex < pSRB->SGcount )
     {
-	bval = DMA_IDLE_CMD | ioDir;   /* ;+EN_DMA_INT */
-	outb( bval, ioport+DMA_Cmd);
+	DC390_write8 (DMA_Cmd, DMA_IDLE_CMD | ioDir /* | DMA_INT */);
 	if( !pSRB->SGToBeXferLen )
 	{
 	    psgl = pSRB->pSegmentList;
-#ifndef VERSION_ELF_1_2_13
-	    pSRB->SGPhysAddr = virt_to_phys( psgl->address );
-#else
-	    pSRB->SGPhysAddr = (ULONG) psgl->address;
-#endif
+	    pSRB->SGBusAddr = virt_to_bus( psgl->address );
 	    pSRB->SGToBeXferLen = (ULONG) psgl->length;
+	    DEBUG1(printk (KERN_DEBUG " DC390: Next SG segment.");)
 	}
 	lval = pSRB->SGToBeXferLen;
-	bval = (UCHAR) lval;
-	outb(bval,ioport+CtcReg_Low);
-	lval = lval >> 8;
-	bval = (UCHAR) lval;
-	outb(bval,ioport+CtcReg_Mid);
-	lval = lval >> 8;
-	bval = (UCHAR) lval;
-	outb(bval,ioport+CtcReg_High);
+	DEBUG1(printk (KERN_DEBUG " DC390: Transfer %li bytes (address %08lx)\n", lval, pSRB->SGBusAddr);)
+	DC390_write8 (CtcReg_Low, (UCHAR) lval);
+	lval >>= 8;
+	DC390_write8 (CtcReg_Mid, (UCHAR) lval);
+	lval >>= 8;
+	DC390_write8 (CtcReg_High, (UCHAR) lval);
 
-	lval = pSRB->SGToBeXferLen;
-	outl(lval, ioport+DMA_XferCnt);
-
-	lval = pSRB->SGPhysAddr;
-	outl( lval, ioport+DMA_XferAddr);
-
-	bval = DMA_COMMAND+INFO_XFER_CMD;
-	outb(bval, ioport+ScsiCmd);
+	DC390_write32 (DMA_XferCnt, pSRB->SGToBeXferLen);
+	DC390_write32 (DMA_XferAddr, pSRB->SGBusAddr);
 
+	//DC390_write8 (DMA_Cmd, DMA_IDLE_CMD | ioDir); /* | DMA_INT; */
 	pSRB->SRBState = SRB_DATA_XFER;
 
-	bval = DMA_IDLE_CMD | ioDir;	/* ;+EN_DMA_INT */
-	outb(bval, ioport+DMA_Cmd);
+	DC390_write8 (ScsiCmd, DMA_COMMAND+INFO_XFER_CMD);
 
-	bval = DMA_START_CMD | ioDir;	/* ;+EN_DMA_INT */
-	outb(bval, ioport+DMA_Cmd);
+	DC390_write8 (DMA_Cmd, DMA_START_CMD | ioDir | DMA_INT);
+	//DEBUG1(DC390_write32 (DMA_ScsiBusCtrl, WRT_ERASE_DMA_STAT | EN_INT_ON_PCI_ABORT);)
+	//DEBUG1(printk (KERN_DEBUG "DC390: DMA_Status: %02x\n", DC390_read8 (DMA_Status));)
+	//DEBUG1(DC390_write32 (DMA_ScsiBusCtrl, EN_INT_ON_PCI_ABORT);)
     }
     else    /* xfer pad */
     {
@@ -683,112 +878,83 @@
 	{
 	    pSRB->AdaptStatus = H_OVER_UNDER_RUN;
 	    pSRB->SRBStatus |= OVER_RUN;
+	    DEBUG0(printk (KERN_WARNING " DC390: Overrun -");)
 	}
-	bval = 0;
-	outb(bval,ioport+CtcReg_Low);
-	outb(bval,ioport+CtcReg_Mid);
-	outb(bval,ioport+CtcReg_High);
+	DEBUG0(printk (KERN_WARNING " Clear transfer pad \n");)
+	DC390_write8 (CtcReg_Low, 0);
+	DC390_write8 (CtcReg_Mid, 0);
+	DC390_write8 (CtcReg_High, 0);
 
 	pSRB->SRBState |= SRB_XFERPAD;
-	bval = DMA_COMMAND+XFER_PAD_BYTE;
-	outb(bval, ioport+ScsiCmd);
+	DC390_write8 (ScsiCmd, DMA_COMMAND+XFER_PAD_BYTE);
 /*
-	bval = DMA_IDLE_CMD | ioDir;	;+EN_DMA_INT
-	outb(bval, ioport+DMA_Cmd);
-	bval = DMA_START_CMD | ioDir;	;+EN_DMA_INT
-	outb(bval, ioport+DMA_Cmd);
+	DC390_write8 (DMA_Cmd, DMA_IDLE_CMD | ioDir); // | DMA_INT;
+	DC390_write8 (DMA_Cmd, DMA_START_CMD | ioDir | DMA_INT);
 */
     }
 }
 
 
 static void
-DC390_DataOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+dc390_DataOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
 {
-    UCHAR  ioDir;
-
-    ioDir = WRITE_DIRECTION;
-    DataIO_Comm( pACB, pSRB, ioDir);
+    dc390_DataIO_Comm (pACB, pSRB, WRITE_DIRECTION);
 }
 
 static void
-DC390_DataInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+dc390_DataInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
 {
-    UCHAR  ioDir;
-
-    ioDir = READ_DIRECTION;
-    DataIO_Comm( pACB, pSRB, ioDir);
+    dc390_DataIO_Comm (pACB, pSRB, READ_DIRECTION);
 }
 
-static void
-DC390_CommandPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+void
+dc390_CommandPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
 {
     PDCB   pDCB;
-    UCHAR  bval;
+    UCHAR  i, cnt;
     PUCHAR ptr;
-    USHORT ioport, i,  cnt;
-
 
-    ioport = pACB->IOPortBase;
-    bval = RESET_ATN_CMD;
-    outb(bval, ioport+ScsiCmd);
-    bval = CLEAR_FIFO_CMD;
-    outb(bval, ioport+ScsiCmd);
+    DC390_write8 (ScsiCmd, RESET_ATN_CMD);
+    DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
     if( !(pSRB->SRBFlag & AUTO_REQSENSE) )
     {
-	cnt = (USHORT) pSRB->ScsiCmdLen;
+	cnt = (UCHAR) pSRB->ScsiCmdLen;
 	ptr = (PUCHAR) pSRB->CmdBlock;
 	for(i=0; i < cnt; i++)
-	{
-	    outb(*ptr, ioport+ScsiFifo);
-	    ptr++;
-	}
+	    DC390_write8 (ScsiFifo, *(ptr++));
     }
     else
     {
-	bval = REQUEST_SENSE;
-	outb(bval, ioport+ScsiFifo);
+	UCHAR bval = 0;
+	DC390_write8 (ScsiFifo, REQUEST_SENSE);
 	pDCB = pACB->pActiveDCB;
-	bval = pDCB->IdentifyMsg << 5;
-	outb(bval, ioport+ScsiFifo);
-	bval = 0;
-	outb(bval, ioport+ScsiFifo);
-	outb(bval, ioport+ScsiFifo);
-	bval = sizeof(pSRB->pcmd->sense_buffer);
-	outb(bval, ioport+ScsiFifo);
-	bval = 0;
-	outb(bval, ioport+ScsiFifo);
+	DC390_write8 (ScsiFifo, pDCB->IdentifyMsg << 5);
+	DC390_write8 (ScsiFifo, bval);
+	DC390_write8 (ScsiFifo, bval);
+	DC390_write8 (ScsiFifo, sizeof(pSRB->pcmd->sense_buffer));
+	DC390_write8 (ScsiFifo, bval);
     }
     pSRB->SRBState = SRB_COMMAND;
-    bval = INFO_XFER_CMD;
-    outb(bval, ioport+ScsiCmd);
+    DC390_write8 (ScsiCmd, INFO_XFER_CMD);
 }
 
 static void
-DC390_StatusPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+dc390_StatusPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
 {
-    UCHAR  bval;
-    USHORT ioport;
-
-    ioport = pACB->IOPortBase;
-    bval = CLEAR_FIFO_CMD;
-    outb(bval, ioport+ScsiCmd);
+    DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
     pSRB->SRBState = SRB_STATUS;
-    bval = INITIATOR_CMD_CMPLTE;
-    outb(bval, ioport+ScsiCmd);
+    DC390_write8 (ScsiCmd, INITIATOR_CMD_CMPLTE);
+    //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
 }
 
-static void
-DC390_MsgOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+void
+dc390_MsgOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
 {
-    UCHAR   bval;
-    USHORT  ioport, i, cnt;
+    UCHAR   bval, i, cnt;
     PUCHAR  ptr;
     PDCB    pDCB;
 
-    ioport = pACB->IOPortBase;
-    bval = CLEAR_FIFO_CMD;
-    outb(bval, ioport+ScsiCmd);
+    DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
     pDCB = pACB->pActiveDCB;
     if( !(pSRB->SRBState & SRB_MSGOUT) )
     {
@@ -797,10 +963,7 @@
 	{
 	    ptr = (PUCHAR) pSRB->MsgOutBuf;
 	    for(i=0; i < cnt; i++)
-	    {
-		outb(*ptr, ioport+ScsiFifo);
-		ptr++;
-	    }
+		DC390_write8 (ScsiFifo, *(ptr++));
 	    pSRB->MsgCnt = 0;
 	    if( (pDCB->DCBFlag & ABORT_DEV_) &&
 		(pSRB->MsgOutBuf[0] == MSG_ABORT) )
@@ -816,76 +979,67 @@
 		if( pDCB->SyncMode & SYNC_ENABLE )
 		    goto  mop1;
 	    }
-	    outb(bval, ioport+ScsiFifo);
+	    DC390_write8 (ScsiFifo, bval);
 	}
-	bval = INFO_XFER_CMD;
-	outb( bval, ioport+ScsiCmd);
+	DC390_write8 (ScsiCmd, INFO_XFER_CMD);
     }
     else
     {
 mop1:
-	bval = MSG_EXTENDED;
-	outb(bval, ioport+ScsiFifo);
-	bval = 3;	/*    ;length of extended msg */
-	outb(bval, ioport+ScsiFifo);
-	bval = 1;	/*    ; sync nego */
-	outb(bval, ioport+ScsiFifo);
-	bval = pDCB->NegoPeriod;
-	outb(bval, ioport+ScsiFifo);
-	bval = SYNC_NEGO_OFFSET;
-	outb(bval, ioport+ScsiFifo);
+	//printk ("DC390: Send SDTR message to %i %i ... \n", pDCB->UnitSCSIID, pDCB->UnitSCSILUN);
+	DC390_write8 (ScsiFifo, MSG_EXTENDED);
+	DC390_write8 (ScsiFifo, 3);	/*    ;length of extended msg */
+	DC390_write8 (ScsiFifo, EXTENDED_SDTR);	/*    ; sync nego */
+	DC390_write8 (ScsiFifo, pDCB->NegoPeriod);
+	if (pDCB->SyncOffset & 0x0f)
+		    DC390_write8 (ScsiFifo, pDCB->SyncOffset);
+	else
+		    DC390_write8 (ScsiFifo, SYNC_NEGO_OFFSET);		    
 	pSRB->SRBState |= DO_SYNC_NEGO;
-	bval = INFO_XFER_CMD;
-	outb(bval, ioport+ScsiCmd);
+	DC390_write8 (ScsiCmd, INFO_XFER_CMD);
     }
 }
 
 static void
-DC390_MsgInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+dc390_MsgInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
 {
-    UCHAR  bval;
-    USHORT ioport;
-
-    ioport = pACB->IOPortBase;
-    bval = CLEAR_FIFO_CMD;
-    outb(bval, ioport+ScsiCmd);
+    DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
     if( !(pSRB->SRBState & SRB_MSGIN) )
     {
-	pSRB->SRBState &= SRB_DISCONNECT;
+	pSRB->SRBState &= ~SRB_DISCONNECT;
 	pSRB->SRBState |= SRB_MSGIN;
     }
-    bval = INFO_XFER_CMD;
-    outb(bval, ioport+ScsiCmd);
+    DC390_write8 (ScsiCmd, INFO_XFER_CMD);
+    //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
 }
 
 static void
-DC390_Nop_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+dc390_Nop_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
 {
 }
 
 static void
-DC390_Nop_1( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+dc390_Nop_1( PACB pACB, PSRB pSRB, PUCHAR psstatus)
 {
 }
 
 
 static void
-SetXferRate( PACB pACB, PDCB pDCB )
+dc390_SetXferRate( PACB pACB, PDCB pDCB )
 {
-    UCHAR  bval;
-    USHORT cnt, i;
+    UCHAR  bval, i, cnt;
     PDCB   ptr;
 
     if( !(pDCB->IdentifyMsg & 0x07) )
     {
 	if( pACB->scan_devices )
 	{
-	    CurrSyncOffset = pDCB->SyncOffset;
+	    dc390_CurrSyncOffset = pDCB->SyncOffset;
 	}
 	else
 	{
 	    ptr = pACB->pLinkDCB;
-	    cnt = pACB->DeviceCnt;
+	    cnt = pACB->DCBCnt;
 	    bval = pDCB->UnitSCSIID;
 	    for(i=0; i<cnt; i++)
 	    {
@@ -905,39 +1059,34 @@
 }
 
 
-static void
-DC390_Disconnect( PACB pACB )
+void
+dc390_Disconnect( PACB pACB )
 {
     PDCB   pDCB;
     PSRB   pSRB, psrb;
-    ULONG  flags;
-    USHORT ioport, i, cnt;
-    UCHAR  bval;
+    UCHAR  i, cnt;
 
-#ifdef DC390_DEBUG0
-    printk("DISC,");
-#endif
+    DEBUG0(printk(KERN_INFO "DISC,");)
 
-    save_flags(flags);
-    cli();
-    ioport = pACB->IOPortBase;
     pDCB = pACB->pActiveDCB;
     if (!pDCB)
      {
-#ifdef DC390_DEBUG0
-	printk("ACB:%08lx->ActiveDCB:%08lx !,",(ULONG)pACB,(ULONG)pDCB);
-#endif
-	restore_flags(flags); return;
+	int j = 400;
+	DEBUG0(printk(KERN_WARNING "ACB:%08lx->ActiveDCB:%08lx IOPort:%04x IRQ:%02x !\n",\
+	       (ULONG)pACB,(ULONG)pDCB,pACB->IOPortBase,pACB->IRQLevel);)
+	while (--j) udelay (1000);
+	DC390_read8 (INT_Status);	/* Reset Pending INT */
+	DC390_write8 (ScsiCmd, EN_SEL_RESEL);
+	return;
      }
     pSRB = pDCB->pActiveSRB;
     pACB->pActiveDCB = 0;
     pSRB->ScsiPhase = SCSI_NOP0;
-    bval = EN_SEL_RESEL;
-    outb(bval, ioport+ScsiCmd);
+    DC390_write8 (ScsiCmd, EN_SEL_RESEL);
     if( pSRB->SRBState & SRB_UNEXPECT_RESEL )
     {
 	pSRB->SRBState = 0;
-	DoWaitingSRB( pACB );
+	dc390_DoWaitingSRB( pACB );
     }
     else if( pSRB->SRBState & SRB_ABORT_SENT )
     {
@@ -954,7 +1103,7 @@
 	    pSRB = psrb;
 	}
 	pDCB->pGoingSRB = 0;
-	DoWaitingSRB( pACB );
+	dc390_DoWaitingSRB( pACB );
     }
     else
     {
@@ -964,7 +1113,7 @@
 	    if( !(pACB->scan_devices) )
 	    {
 		pSRB->SRBState = SRB_READY;
-		RewaitSRB( pDCB, pSRB);
+		dc390_RewaitSRB( pDCB, pSRB);
 	    }
 	    else
 	    {
@@ -974,76 +1123,66 @@
 	}
 	else if( pSRB->SRBState & SRB_DISCONNECT )
 	{
-	    DoWaitingSRB( pACB );
+	    dc390_DoWaitingSRB( pACB );
 	}
 	else if( pSRB->SRBState & SRB_COMPLETED )
 	{
 disc1:
 	    if(pDCB->MaxCommand > 1)
 	    {
-	       bval = pSRB->TagNumber;
-	       pDCB->TagMask &= (~(1 << bval));   /* free tag mask */
+	       pDCB->TagMask &= (~(1 << pSRB->TagNumber));   /* free tag mask */
 	    }
 	    pDCB->pActiveSRB = 0;
 	    pSRB->SRBState = SRB_FREE;
-	    SRBdone( pACB, pDCB, pSRB);
+	    dc390_SRBdone( pACB, pDCB, pSRB);
 	}
     }
-    restore_flags(flags);
-    return;
+    pACB->MsgLen = 0;
 }
 
 
-static void
-DC390_Reselect( PACB pACB )
+void
+dc390_Reselect( PACB pACB )
 {
-    PDCB   pDCB, pdcb;
+    PDCB   pDCB;
     PSRB   pSRB;
-    USHORT ioport, wval;
-    UCHAR  bval, bval1;
-
+    USHORT wval;
+    UCHAR  bval;
 
-#ifdef DC390_DEBUG0
-    printk("RSEL,");
-#endif
-    ioport = pACB->IOPortBase;
+    DEBUG0(printk(KERN_INFO "RSEL,");)
     pDCB = pACB->pActiveDCB;
     if( pDCB )
-    {	/* Arbitration lost but Reselection win */
+    {	/* Arbitration lost but Reselection won */
+	DEBUG0(printk ("(ActiveDCB != 0)");)
 	pSRB = pDCB->pActiveSRB;
 	if( !( pACB->scan_devices ) )
 	{
 	    pSRB->SRBState = SRB_READY;
-	    RewaitSRB( pDCB, pSRB);
+	    dc390_RewaitSRB( pDCB, pSRB);
 	}
     }
-    bval = inb(ioport+ScsiFifo);	/* get ID */
-    bval = bval ^ pACB->HostID_Bit;
+    bval = DC390_read8 (ScsiFifo);	/* get ID */
+    DEBUG0(printk ("Dev %02x,", bval);)
+    bval ^= 1 << pACB->pScsiHost->this_id; /* Mask AdapterID */
     wval = 0;
-    bval1 = 1;
-    for(;;)
-    {
-	if( !(bval & bval1) )
-	{
-	    bval1 = bval1 << 1;
-	    wval++;
-	}
-	else
-	    break;
-    }
-    wval |=  ( (USHORT) inb(ioport+ScsiFifo) & 7) << 8;  /* get LUN */
+    while (bval >>= 1) wval++;
+    wval |=  ( (USHORT) DC390_read8 (ScsiFifo) & 7) << 8;  /* get LUN */
+    DEBUG0(printk ("(ID %02x, LUN %02x),", wval & 0xff, (wval & 0xff00) >> 8);)
     pDCB = pACB->pLinkDCB;
-    pdcb = pDCB;
     while( wval != *((PUSHORT) &pDCB->UnitSCSIID) )
     {
 	pDCB = pDCB->pNextDCB;
-	if( pDCB == pdcb )
+	if( pDCB == pACB->pLinkDCB )
+	 {
+	    printk (KERN_ERR "DC390: Reselect from non existing device (ID %02x, LUN %02x)\n",
+		    wval & 0xff, (wval & 0xff00) >> 8);
 	    return;
+	 }
     }
     pACB->pActiveDCB = pDCB;
-    if( pDCB->SyncMode & EN_TAG_QUEUING )
+    if( pDCB->SyncMode & EN_TAG_QUEUEING )
     {
-	pSRB = pACB->pTmpSRB;
+	pSRB = pACB->pTmpSRB; /* ?? */
 	pDCB->pActiveSRB = pSRB;
     }
     else
@@ -1053,57 +1192,163 @@
 	{
 	    pSRB= pACB->pTmpSRB;
 	    pSRB->SRBState = SRB_UNEXPECT_RESEL;
+	    printk (KERN_ERR "DC390: Reselect without outstanding cmnd (ID %02x, LUN %02x)\n",
+		    wval & 0xff, (wval & 0xff00) >> 8);
 	    pDCB->pActiveSRB = pSRB;
-	    EnableMsgOut( pACB, pSRB );
+	    dc390_EnableMsgOut_Abort ( pACB, pSRB );
 	}
 	else
 	{
 	    if( pDCB->DCBFlag & ABORT_DEV_ )
 	    {
 		pSRB->SRBState = SRB_ABORT_SENT;
-		EnableMsgOut( pACB, pSRB );
+		printk (KERN_INFO "DC390: Reselect: Abort (ID %02x, LUN %02x)\n",
+			wval & 0xff, (wval & 0xff00) >> 8);
+		dc390_EnableMsgOut_Abort( pACB, pSRB );
 	    }
 	    else
 		pSRB->SRBState = SRB_DATA_XFER;
 	}
     }
+
+    DEBUG1(printk (KERN_DEBUG "Resel SRB(%p): TagNum (%02x)\n", pSRB, pSRB->TagNumber);)
     pSRB->ScsiPhase = SCSI_NOP0;
-    bval = pDCB->UnitSCSIID;
-    outb( bval, ioport+Scsi_Dest_ID);
-    bval = pDCB->SyncPeriod;
-    outb(bval, ioport+Sync_Period);
-    bval = pDCB->SyncOffset;
-    outb( bval, ioport+Sync_Offset);
-    bval = pDCB->CtrlR1;
-    outb(bval, ioport+CtrlReg1);
-    bval = pDCB->CtrlR3;
-    outb(bval, ioport+CtrlReg3);
-    bval = pDCB->CtrlR4;	/* ; Glitch eater */
-    outb(bval, ioport+CtrlReg4);
-    bval = MSG_ACCEPTED_CMD;	/* ;to rls the /ACK signal */
-    outb(bval, ioport+ScsiCmd);
+    DC390_write8 (Scsi_Dest_ID, pDCB->UnitSCSIID);
+    DC390_write8 (Sync_Period, pDCB->SyncPeriod);
+    DC390_write8 (Sync_Offset, pDCB->SyncOffset);
+    DC390_write8 (CtrlReg1, pDCB->CtrlR1);
+    DC390_write8 (CtrlReg3, pDCB->CtrlR3);
+    DC390_write8 (CtrlReg4, pDCB->CtrlR4);	/* ; Glitch eater */
+    DC390_write8 (ScsiCmd, MSG_ACCEPTED_CMD);	/* ;to release the /ACK signal */
 }
 
 
-static void
-SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB )
+static void 
+dc390_remove_dev (PACB pACB, PDCB pDCB)
+{
+   PDCB pPrevDCB = pACB->pLinkDCB;
+
+   pACB->DCBmap[pDCB->UnitSCSIID] &= ~(1 << pDCB->UnitSCSILUN);
+   if (pDCB->GoingSRBCnt > 1)
+     {
+	DCBDEBUG(printk (KERN_INFO "DC390: Driver won't free DCB (ID %i, LUN %i): 0x%08x because of SRBCnt %i\n",\
+		pDCB->UnitSCSIID, pDCB->UnitSCSILUN, (int)pDCB, pDCB->GoingSRBCnt);)
+	return;
+     };
+   
+   if (pDCB == pACB->pLinkDCB) 
+     {
+	if (pDCB->pNextDCB == pDCB) pDCB->pNextDCB = 0;
+	pACB->pLinkDCB = pDCB->pNextDCB;
+	pACB->pLastDCB->pNextDCB = pDCB->pNextDCB;
+     }
+   else
+     {
+	while (pPrevDCB->pNextDCB != pDCB) pPrevDCB = pPrevDCB->pNextDCB;
+	pPrevDCB->pNextDCB = pDCB->pNextDCB;
+	if (pDCB == pACB->pLastDCB) pACB->pLastDCB = pPrevDCB;
+     }
+
+   DCBDEBUG(printk (KERN_INFO "DC390: Driver about to free DCB (ID %i, LUN %i): 0x%08x\n",\
+	   pDCB->UnitSCSIID, pDCB->UnitSCSILUN, (int)pDCB);)
+   kfree (pDCB); if (pDCB == pACB->pActiveDCB) pACB->pActiveDCB = 0;
+   pACB->DCBCnt--;
+   /* pACB->DeviceCnt--; */
+};
+
+
+static UCHAR __inline__
+dc390_tagq_blacklist (char* name)
+{
+   UCHAR i;
+   for(i=0; i<BADDEVCNT; i++)
+     if (memcmp (name, dc390_baddevname1[i], 28) == 0)
+	return 1;
+   return 0;
+};
+   
+
+static void 
+dc390_disc_tagq_set (PDCB pDCB, PSCSI_INQDATA ptr)
+{
+   /* Check for SCSI format (ANSI and Response data format) */
+   if ( (ptr->Vers & 0x07) >= 2 || (ptr->RDF & 0x0F) == 2 )
+   {
+	if ( (ptr->Flags & SCSI_INQ_CMDQUEUE) &&
+	    (pDCB->DevMode & TAG_QUEUEING_) &&
+	    /* ((pDCB->DevType == TYPE_DISK) 
+		|| (pDCB->DevType == TYPE_MOD)) &&*/
+	    !dc390_tagq_blacklist (((char*)ptr)+8) )
+	  {
+	     pDCB->MaxCommand = pDCB->pDCBACB->TagMaxNum;
+	     pDCB->SyncMode |= EN_TAG_QUEUEING /* | EN_ATN_STOP */;
+	     pDCB->TagMask = 0;
+	  }
+	else
+	  {
+	     /* Do we really need to check for DevType here ? */
+	     if ( 0 /*(pDCB->DevMode & EN_DISCONNECT_)*/
+		 /* && ((pDCB->DevType == TYPE_DISK) 
+		     || (pDCB->DevType == TYPE_MOD))*/ )
+		pDCB->SyncMode |= EN_ATN_STOP;
+	     else
+	       //pDCB->SyncMode &= ~EN_ATN_STOP;
+	       pDCB->SyncMode &= ~0;
+	  }
+     }
+};
+
+
+static void 
+dc390_add_dev (PACB pACB, PDCB pDCB, PSCSI_INQDATA ptr)
+{
+   UCHAR bval1 = ptr->DevType & SCSI_DEVTYPE;
+   pDCB->DevType = bval1;
+   /* if (bval1 == TYPE_DISK || bval1 == TYPE_MOD) */
+	dc390_disc_tagq_set (pDCB, ptr);
+};
+
+
+void
+dc390_SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB )
 {
     PSRB   psrb;
-    UCHAR  bval, bval1, i, j, status;
+    UCHAR  bval, status, i;
     PSCSICMD pcmd;
     PSCSI_INQDATA  ptr;
-    USHORT disable_tag;
-    ULONG  flags;
     PSGL   ptr2;
     ULONG  swlval;
 
     pcmd = pSRB->pcmd;
     status = pSRB->TargetStatus;
+    DEBUG0(printk (" SRBdone (%02x,%08x), SRB %p, pid %li\n", status, pcmd->result,\
+		pSRB, pcmd->pid);)
     if(pSRB->SRBFlag & AUTO_REQSENSE)
-    {
+    {	/* Last command was a Request Sense */
 	pSRB->SRBFlag &= ~AUTO_REQSENSE;
 	pSRB->AdaptStatus = 0;
 	pSRB->TargetStatus = SCSI_STAT_CHECKCOND;
+#ifdef DC390_REMOVABLEDEBUG
+	switch (pcmd->sense_buffer[2] & 0x0f)
+	{	    
+	 case NOT_READY: printk (KERN_INFO "DC390: ReqSense: NOT_READY (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n",
+				 pcmd->cmnd[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN,
+				 status, pACB->scan_devices); break;
+	 case UNIT_ATTENTION: printk (KERN_INFO "DC390: ReqSense: UNIT_ATTENTION (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n",
+				      pcmd->cmnd[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN,
+				      status, pACB->scan_devices); break;
+	 case ILLEGAL_REQUEST: printk (KERN_INFO "DC390: ReqSense: ILLEGAL_REQUEST (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n",
+				       pcmd->cmnd[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN,
+				       status, pACB->scan_devices); break;
+	 case MEDIUM_ERROR: printk (KERN_INFO "DC390: ReqSense: MEDIUM_ERROR (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n",
+				    pcmd->cmnd[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN,
+				    status, pACB->scan_devices); break;
+	 case HARDWARE_ERROR: printk (KERN_INFO "DC390: ReqSense: HARDWARE_ERROR (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n",
+				      pcmd->cmnd[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN,
+				      status, pACB->scan_devices); break;
+	}
+#endif
+	//pcmd->result = DRIVER_SENSE << 24 | DID_OK << 16 | status;
 	if(status == SCSI_STAT_CHECKCOND)
 	{
 	    pcmd->result = DID_BAD_TARGET << 16;
@@ -1111,7 +1356,7 @@
 	}
 	if(pSRB->RetryCnt == 0)
 	{
-	    *((PULONG) &(pSRB->CmdBlock[0])) = pSRB->Segment0[0];
+	    (ULONG)(pSRB->CmdBlock[0]) = pSRB->Segment0[0];
 	    pSRB->TotalXferredLen = pSRB->Segment1[1];
 	    if( (pSRB->TotalXferredLen) &&
 		(pSRB->TotalXferredLen >= pcmd->underflow) )
@@ -1119,28 +1364,29 @@
 	    else
 		pcmd->result = (DRIVER_SENSE << 24) | (DRIVER_OK << 16) |
 				SCSI_STAT_CHECKCOND;
-#ifdef DC390_DEBUG0
-	printk("Cmd=%2x,Result=%8x,XferL=%8x,",pSRB->CmdBlock[0],
-		(UINT) pcmd->result, (UINT) pSRB->TotalXferredLen);
-#endif
+	    REMOVABLEDEBUG(printk(KERN_INFO "Cmd=%02x,Result=%08x,XferL=%08x\n",pSRB->CmdBlock[0],\
+		(UINT) pcmd->result, (UINT) pSRB->TotalXferredLen);)
 	    goto ckc_e;
 	}
-	else
+	else /* Retry */
 	{
 	    pSRB->RetryCnt--;
 	    pSRB->AdaptStatus = 0;
 	    pSRB->TargetStatus = 0;
 	    *((PULONG) &(pSRB->CmdBlock[0])) = pSRB->Segment0[0];
 	    *((PULONG) &(pSRB->CmdBlock[4])) = pSRB->Segment0[1];
-	    if( pSRB->CmdBlock[0] == TEST_UNIT_READY )
+	    /* Don't retry on TEST_UNIT_READY */
+	    if( pSRB->CmdBlock[0] == TEST_UNIT_READY /* || pSRB->CmdBlock[0] == START_STOP */)
 	    {
-		pcmd->result = (DRIVER_SENSE << 24) | (DRIVER_OK << 16) |
-				SCSI_STAT_CHECKCOND;
+		pcmd->result = (DRIVER_SENSE << 24) | (DRIVER_OK << 16) 
+			    | SCSI_STAT_CHECKCOND;
+		REMOVABLEDEBUG(printk(KERN_INFO "Cmd=%02x, Result=%08x, XferL=%08x\n",pSRB->CmdBlock[0],\
+		       (UINT) pcmd->result, (UINT) pSRB->TotalXferredLen);)
 		goto ckc_e;
 	    }
 	    pcmd->result |= (DRIVER_SENSE << 24);
-	    pSRB->SGcount		 = (UCHAR) pSRB->Segment1[0];
-	    pSRB->ScsiCmdLen		 = (UCHAR) (pSRB->Segment1[0] >> 8);
+	    pSRB->SGcount	 = (UCHAR) pSRB->Segment1[0];
+	    pSRB->ScsiCmdLen	 = (UCHAR) (pSRB->Segment1[0] >> 8);
 	    pSRB->SGIndex = 0;
 	    pSRB->TotalXferredLen = 0;
 	    pSRB->SGToBeXferLen = 0;
@@ -1152,8 +1398,8 @@
 		pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer;
 		pSRB->Segmentx.length = pcmd->request_bufflen;
 	    }
-	    if( DC390_StartSCSI( pACB, pDCB, pSRB ) )
-		RewaitSRB( pDCB, pSRB );
+	    if( dc390_StartSCSI( pACB, pDCB, pSRB ) )
+		dc390_RewaitSRB( pDCB, pSRB );
 	    return;
 	}
     }
@@ -1161,6 +1407,8 @@
     {
 	if( status == SCSI_STAT_CHECKCOND)
 	{
+	    REMOVABLEDEBUG(printk (KERN_INFO "DC390: Scsi_Stat_CheckCond (Cmd %02x, Id %02x, LUN %02x)\n",\
+		    pcmd->cmnd[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN);)
 	    if( (pSRB->SGIndex < pSRB->SGcount) && (pSRB->SGcount) && (pSRB->SGToBeXferLen) )
 	    {
 		bval = pSRB->SGcount;
@@ -1171,12 +1419,10 @@
 		    swlval += ptr2->length;
 		    ptr2++;
 		}
-#ifdef	DC390_DEBUG0
-		printk("XferredLen=%8x,NotXferLen=%8x,",
-			(UINT) pSRB->TotalXferredLen, (UINT) swlval);
-#endif
+		REMOVABLEDEBUG(printk(KERN_INFO "XferredLen=%08x,NotXferLen=%08x\n",\
+			(UINT) pSRB->TotalXferredLen, (UINT) swlval);)
 	    }
-	    RequestSense( pACB, pDCB, pSRB );
+	    dc390_RequestSense( pACB, pDCB, pSRB );
 	    return;
 	}
 	else if( status == SCSI_STAT_QUEUEFULL )
@@ -1184,7 +1430,7 @@
 	    bval = (UCHAR) pDCB->GoingSRBCnt;
 	    bval--;
 	    pDCB->MaxCommand = bval;
-	    RewaitSRB( pDCB, pSRB );
+	    dc390_RewaitSRB( pDCB, pSRB );
 	    pSRB->AdaptStatus = 0;
 	    pSRB->TargetStatus = 0;
 	    return;
@@ -1194,12 +1440,22 @@
 	    pSRB->AdaptStatus = H_SEL_TIMEOUT;
 	    pSRB->TargetStatus = 0;
 	    pcmd->result = DID_BAD_TARGET << 16;
+	    /* Devices are removed below ... */
 	}
-	else
+	else if (status == SCSI_STAT_BUSY && 
+		 (pSRB->CmdBlock[0] == TEST_UNIT_READY || pSRB->CmdBlock[0] == INQUIRY) &&
+		 pACB->scan_devices)
 	{
 	    pSRB->AdaptStatus = 0;
+	    pSRB->TargetStatus = status;
+	    pcmd->result = (ULONG) (pSRB->EndMessage << 8)
+				/* | (ULONG) status */;
+	}
+	else
+	{   /* Another error */
+	    pSRB->AdaptStatus = 0;
 	    if( pSRB->RetryCnt )
-	    {
+	    {	/* Retry */
 		pSRB->RetryCnt--;
 		pSRB->TargetStatus = 0;
 		pSRB->SGIndex = 0;
@@ -1213,19 +1469,19 @@
 		    pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer;
 		    pSRB->Segmentx.length = pcmd->request_bufflen;
 		}
-		if( DC390_StartSCSI( pACB, pDCB, pSRB ) )
-		    RewaitSRB( pDCB, pSRB );
+		if( dc390_StartSCSI( pACB, pDCB, pSRB ) )
+		    dc390_RewaitSRB( pDCB, pSRB );
 		return;
 	    }
 	    else
-	    {
+	    {	/* Report error */
 		pcmd->result |= (DID_ERROR << 16) | (ULONG) (pSRB->EndMessage << 8) |
 			       (ULONG) status;
 	    }
 	}
     }
     else
-    {
+    {	/*  Target status == 0 */
 	status = pSRB->AdaptStatus;
 	if(status & H_OVER_UNDER_RUN)
 	{
@@ -1249,95 +1505,57 @@
     {
 	if( pSRB->CmdBlock[0] == TEST_UNIT_READY )
 	{
-	    if(pcmd->result != (DID_OK << 16))
-	    {
-		if( pcmd->result & SCSI_STAT_CHECKCOND )
-		{
-		    goto RTN_OK;
-		}
-		else
-		{
-		    pACB->DCBmap[pcmd->target] &= ~(1 << pcmd->lun);
-		    pPrevDCB->pNextDCB = pACB->pLinkDCB;
-		    if( (pcmd->target == pACB->max_id) &&
-		       ((pcmd->lun == 0) || (pcmd->lun == pACB->max_lun)) )
-		    {
-			pACB->scan_devices = 0;
-		    }
-		}
-	    }
-	    else
-	    {
-RTN_OK:
-		pPrevDCB->pNextDCB = pDCB;
-		pDCB->pNextDCB = pACB->pLinkDCB;
-		if( (pcmd->target == pACB->max_id) && (pcmd->lun == pACB->max_lun) )
-		    pACB->scan_devices = END_SCAN;
-	    }
-	}
-	else if( pSRB->CmdBlock[0] == INQUIRY )
-	{
-	    if( (pcmd->target == pACB->max_id) &&
-		(pcmd->lun == pACB->max_lun) )
-	    {
-		pACB->scan_devices = 0;
-	    }
-	    ptr = (PSCSI_INQDATA) (pcmd->request_buffer);
-	    if( pcmd->use_sg )
-		ptr = (PSCSI_INQDATA) (((PSGL) ptr)->address);
-	    bval1 = ptr->DevType & SCSI_DEVTYPE;
-	    if(bval1 == SCSI_NODEV)
-	    {
-		pACB->DCBmap[pcmd->target] &= ~(1 << pcmd->lun);
-		pPrevDCB->pNextDCB = pACB->pLinkDCB;
+#ifdef DC390_DEBUG0
+	    printk (KERN_INFO "DC390: Test_Unit_Ready: result: %08x", pcmd->result);
+	    if (pcmd->result & DRIVER_SENSE << 24) printk (" (sense: %02x %02x %02x %02x)\n",
+				   pcmd->sense_buffer[0], pcmd->sense_buffer[1],
+				   pcmd->sense_buffer[2], pcmd->sense_buffer[3]);
+	    else printk ("\n");
+#endif
+	    if((pcmd->result != (DID_OK << 16) && !(pcmd->result & SCSI_STAT_CHECKCOND) && !(pcmd->result & SCSI_STAT_BUSY)) ||
+	       ((pcmd->result & DRIVER_SENSE << 24) && (pcmd->sense_buffer[0] & 0x70) == 0x70 &&
+		(pcmd->sense_buffer[2] & 0xf) == ILLEGAL_REQUEST) || pcmd->result & DID_ERROR << 16)
+	    {
+	       /* device not present: remove */ 
+	       dc390_remove_dev (pACB, pDCB);
+	       
+	       if( (pcmd->target == pACB->pScsiHost->max_id - 1) &&
+		  ((pcmd->lun == 0) || (pcmd->lun == pACB->pScsiHost->max_lun - 1)) )
+		 pACB->scan_devices = 0;
 	    }
 	    else
 	    {
-		pACB->DeviceCnt++;
-		pPrevDCB = pDCB;
-		pACB->pDCB_free = (PDCB) ((ULONG) (pACB->pDCB_free) + sizeof( DC390_DCB ));
-		pDCB->DevType = bval1;
-		if(bval1 == TYPE_DISK || bval1 == TYPE_MOD)
-		{
-		    if( (((ptr->Vers & 0x07) >= 2) || ((ptr->RDF & 0x0F) == 2)) &&
-			(ptr->Flags & SCSI_INQ_CMDQUEUE) &&
-			(pDCB->DevMode & TAG_QUEUING_) &&
-			(pDCB->DevMode & EN_DISCONNECT_) )
-		    {
-			disable_tag = 0;
-			for(i=0; i<BADDEVCNT; i++)
-			{
-			    for(j=0; j<28; j++)
-			    {
-				if( ((PUCHAR)ptr)[8+j] != baddevname1[i][j])
-				    break;
-			    }
-			    if(j == 28)
-			    {
-				disable_tag = 1;
-				break;
-			    }
-			}
-
-			if( !disable_tag )
-			{
-			    pDCB->MaxCommand = pACB->TagMaxNum;
-			    pDCB->SyncMode |= EN_TAG_QUEUING;
-			    pDCB->TagMask = 0;
-			}
-			else
-			{
-			    pDCB->SyncMode |= EN_ATN_STOP;
-			}
-		    }
-		}
+	        /* device present: add */
+		if( (pcmd->target == pACB->pScsiHost->max_id - 1) && 
+		    (pcmd->lun == pACB->pScsiHost->max_lun - 1) )
+		    pACB->scan_devices = END_SCAN ;
+	        /* pACB->DeviceCnt++; */ /* Dev is added on INQUIRY */
 	    }
 	}
     }
-
-    save_flags( flags );
-    cli();
-/*  ReleaseSRB( pDCB, pSRB ); */
+   
+   if( pSRB->CmdBlock[0] == INQUIRY && 
+      (pcmd->result == DID_OK << 16 || pcmd->result & SCSI_STAT_CHECKCOND) )
+     {
+	ptr = (PSCSI_INQDATA) (pcmd->request_buffer);
+	if( pcmd->use_sg )
+	  ptr = (PSCSI_INQDATA) (((PSGL) ptr)->address);
+	if ((ptr->DevType & SCSI_DEVTYPE) == TYPE_NODEV)
+	  {
+	     /* device not present: remove */
+	     dc390_remove_dev (pACB, pDCB);
+	  }
+	else
+	  {
+	     /* device found: add */ 
+	     dc390_add_dev (pACB, pDCB, ptr);
+	     if (pACB->scan_devices) pACB->DeviceCnt++;
+	  }
+	if( (pcmd->target == pACB->pScsiHost->max_id - 1) &&
+	    (pcmd->lun == pACB->pScsiHost->max_lun - 1) )
+	  pACB->scan_devices = 0;
+     };
+/*  dc390_ReleaseSRB( pDCB, pSRB ); */
 
     if(pSRB == pDCB->pGoingSRB )
     {
@@ -1356,33 +1574,34 @@
     pACB->pFreeSRB = pSRB;
     pDCB->GoingSRBCnt--;
 
-    DoWaitingSRB( pACB );
-    restore_flags(flags);
+    dc390_DoWaitingSRB( pACB );
 
-/*  Notify cmd done */
+    DC390_UNLOCK_ACB_NI;
     pcmd->scsi_done( pcmd );
+    DC390_LOCK_ACB_NI;
 
     if( pDCB->QIORBCnt )
-	DoNextCmd( pACB, pDCB );
+	dc390_DoNextCmd( pACB, pDCB );
     return;
 }
 
 
-static void
-DoingSRB_Done( PACB pACB )
+/* Remove all SRBs and tell midlevel code DID_RESET */
+void
+dc390_DoingSRB_Done( PACB pACB )
 {
-    PDCB  pDCB, pdcb;
-    PSRB  psrb, psrb2;
-    USHORT  cnt, i;
+    PDCB   pDCB, pdcb;
+    PSRB   psrb, psrb2;
+    UCHAR  i;
     PSCSICMD pcmd;
 
     pDCB = pACB->pLinkDCB;
     pdcb = pDCB;
+    if (! pdcb) return;
     do
     {
-	cnt = pdcb->GoingSRBCnt;
 	psrb = pdcb->pGoingSRB;
-	for( i=0; i<cnt; i++)
+	for( i=0; i<pdcb->GoingSRBCnt; i++)
 	{
 	    psrb2 = psrb->pNextSRB;
 	    pcmd = psrb->pcmd;
@@ -1393,62 +1612,46 @@
 	    psrb->pNextSRB = pACB->pFreeSRB;
 	    pACB->pFreeSRB = psrb;
 
+	    DC390_UNLOCK_ACB_NI;
 	    pcmd->scsi_done( pcmd );
+	    DC390_LOCK_ACB_NI;
 	    psrb  = psrb2;
 	}
 	pdcb->GoingSRBCnt = 0;;
 	pdcb->pGoingSRB = NULL;
 	pdcb->TagMask = 0;
 	pdcb = pdcb->pNextDCB;
-    }
-    while( pdcb != pDCB );
+    } while( pdcb != pDCB );
 }
 
 
 static void
-DC390_ResetSCSIBus( PACB pACB )
+dc390_ResetSCSIBus( PACB pACB )
 {
-    USHORT ioport;
-    UCHAR  bval;
-    ULONG  flags;
-
-    save_flags(flags);
-    cli();
     pACB->ACBFlag |= RESET_DEV;
-    ioport = pACB->IOPortBase;
-
-    bval = DMA_IDLE_CMD;
-    outb(bval,ioport+DMA_Cmd);
 
-    bval = RST_SCSI_BUS_CMD;
-    outb(bval,ioport+ScsiCmd);
+    DC390_write8 (ScsiCmd, RST_DEVICE_CMD);
+    udelay (250);
+    DC390_write8 (ScsiCmd, NOP_CMD);
+
+    DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
+    DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
+    DC390_write8 (ScsiCmd, RST_SCSI_BUS_CMD);
 
-    restore_flags(flags);
     return;
 }
 
-
 static void
-DC390_ScsiRstDetect( PACB pACB )
+dc390_ScsiRstDetect( PACB pACB )
 {
-    ULONG wlval, flags;
-    USHORT ioport;
-    UCHAR  bval;
+    printk ("DC390: Rst_Detect: laststat = %08lx\n", dc390_laststatus);
+    //DEBUG0(printk(KERN_INFO "RST_DETECT,");)
 
-#ifdef DC390_DEBUG0
-    printk("RST_DETEC");
-#endif
-    save_flags(flags);
-    sti();
-    wlval = jiffies + HZ;
-    while( jiffies < wlval );	 /* delay 1 sec */
-
-    cli();
-    ioport = pACB->IOPortBase;
-    bval = DMA_IDLE_CMD;
-    outb(bval,ioport+DMA_Cmd);
-    bval = CLEAR_FIFO_CMD;
-    outb(bval,ioport+ScsiCmd);
+    DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
+    /* Unlock before ? */
+    /* delay a second */
+    { unsigned int msec = 1*1000; while (--msec) udelay(1000); }
+    DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
 
     if( pACB->ACBFlag & RESET_DEV )
 	pACB->ACBFlag |= RESET_DONE;
@@ -1456,30 +1659,32 @@
     {
 	pACB->ACBFlag |= RESET_DETECT;
 
-	ResetDevParam( pACB );
-/*	DoingSRB_Done( pACB ); ???? */
-	RecoverSRB( pACB );
+	dc390_ResetDevParam( pACB );
+/*	dc390_DoingSRB_Done( pACB ); ???? */
+	dc390_RecoverSRB( pACB );
 	pACB->pActiveDCB = NULL;
 	pACB->ACBFlag = 0;
-	DoWaitingSRB( pACB );
+	dc390_DoWaitingSRB( pACB );
     }
-    restore_flags(flags);
     return;
 }
 
 
-static void
-RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB )
+static void __inline__
+dc390_RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB )
 {
     PSCSICMD  pcmd;
 
+    REMOVABLEDEBUG(printk (KERN_INFO "DC390: RequestSense (Cmd %02x, Id %02x, LUN %02x)\n",\
+	    pSRB->CmdBlock[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN);)
+
     pSRB->SRBFlag |= AUTO_REQSENSE;
-    pSRB->Segment0[0] = *((PULONG) &(pSRB->CmdBlock[0]));
-    pSRB->Segment0[1] = *((PULONG) &(pSRB->CmdBlock[4]));
+    pSRB->Segment0[0] = (ULONG) pSRB->CmdBlock[0];
+    pSRB->Segment0[1] = (ULONG) pSRB->CmdBlock[4];
     pSRB->Segment1[0] = (ULONG) ((pSRB->ScsiCmdLen << 8) + pSRB->SGcount);
     pSRB->Segment1[1] = pSRB->TotalXferredLen;
     pSRB->AdaptStatus = 0;
-    pSRB->TargetStatus = 0;
+    pSRB->TargetStatus = 0; /* SCSI_STAT_CHECKCOND; */
 
     pcmd = pSRB->pcmd;
 
@@ -1489,52 +1694,24 @@
     pSRB->SGcount = 1;
     pSRB->SGIndex = 0;
 
-    *((PULONG) &(pSRB->CmdBlock[0])) = 0x00000003;
+    pSRB->CmdBlock[0] = REQUEST_SENSE;
     pSRB->CmdBlock[1] = pDCB->IdentifyMsg << 5;
-    *((PUSHORT) &(pSRB->CmdBlock[4])) = sizeof(pcmd->sense_buffer);
+    (USHORT) pSRB->CmdBlock[2] = 0;
+    (USHORT) pSRB->CmdBlock[4] = sizeof(pcmd->sense_buffer);
     pSRB->ScsiCmdLen = 6;
 
     pSRB->TotalXferredLen = 0;
     pSRB->SGToBeXferLen = 0;
-    if( DC390_StartSCSI( pACB, pDCB, pSRB ) )
-	RewaitSRB( pDCB, pSRB );
+    if( dc390_StartSCSI( pACB, pDCB, pSRB ) )
+	dc390_RewaitSRB( pDCB, pSRB );
 }
 
 
-static void
-EnableMsgOut2( PACB pACB, PSRB pSRB )
-{
-    USHORT ioport;
-    UCHAR  bval;
 
-    ioport = pACB->IOPortBase;
-    pSRB->MsgCnt = 1;
-    bval = SET_ATN_CMD;
-    outb(bval, ioport+ScsiCmd);
-}
-
-
-static void
-EnableMsgOut( PACB pACB, PSRB pSRB )
+static void __inline__
+dc390_InvalidCmd( PACB pACB )
 {
-    pSRB->MsgOutBuf[0] = MSG_ABORT;
-    EnableMsgOut2( pACB, pSRB );
-}
-
-
-static void
-DC390_InvalidCmd( PACB pACB )
-{
-   UCHAR bval;
-   USHORT ioport;
-   PSRB   pSRB;
-
-    pSRB = pACB->pActiveDCB->pActiveSRB;
-    if( pSRB->SRBState & (SRB_START_+SRB_MSGOUT) )
-    {
-	ioport = pACB->IOPortBase;
-	bval = CLEAR_FIFO_CMD;
-	outb(bval,(ioport+ScsiCmd));
-    }
+    if( pACB->pActiveDCB->pActiveSRB->SRBState & (SRB_START_+SRB_MSGOUT) )
+	DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
 }
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/seagate.c linux/drivers/scsi/seagate.c
--- linux.vanilla/drivers/scsi/seagate.c	Sun Dec  6 00:14:45 1998
+++ linux/drivers/scsi/seagate.c	Mon Mar 22 00:37:07 1999
@@ -164,7 +164,7 @@
 } Signature;
 	
 static const Signature signatures[] = {
-#ifdef CONFIG_SCSI_SEAGATE
+#if defined(CONFIG_SCSI_SEAGATE) || defined(CONFIG_SCSI_SEAGATE_MODULE)
 {"ST01 v1.7  (C) Copyright 1987 Seagate", 15, 37, SEAGATE},
 {"SCSI BIOS 2.00  (C) Copyright 1987 Seagate", 15, 40, SEAGATE},
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/sym53c416.c linux/drivers/scsi/sym53c416.c
--- linux.vanilla/drivers/scsi/sym53c416.c	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/sym53c416.c	Fri Jan 29 14:09:57 1999
@@ -0,0 +1,805 @@
+/* 
+ *  sym53c416.c
+ *  Low-level SCSI driver for sym53c416 chip.
+ *  Copyright (C) 1998 Lieven Willems (lw_linux@hotmail.com)
+ * 
+ *  LILO command line usage: sym53c416=<PORTBASE>[,<IRQ>]
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License as published by the
+ *  Free Software Foundation; either version 2, or (at your option) any
+ *  later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+#include "sym53c416.h"
+
+#define VERSION_STRING        "Version 1.0.0"
+
+#define TC_LOW       0x00     /* Transfer counter low        */
+#define TC_MID       0x01     /* Transfer counter mid        */
+#define SCSI_FIFO    0x02     /* SCSI FIFO register          */
+#define COMMAND_REG  0x03     /* Command Register            */
+#define STATUS_REG   0x04     /* Status Register (READ)      */
+#define DEST_BUS_ID  0x04     /* Destination Bus ID (WRITE)  */
+#define INT_REG      0x05     /* Interrupt Register (READ)   */
+#define TOM          0x05     /* Time out multiplier (WRITE) */
+#define STP          0x06     /* Synchronous Transfer period */
+#define SYNC_OFFSET  0x07     /* Synchronous Offset          */
+#define CONF_REG_1   0x08     /* Configuration register 1    */
+#define CONF_REG_2   0x0B     /* Configuration register 2    */
+#define CONF_REG_3   0x0C     /* Configuration register 3    */
+#define CONF_REG_4   0x0D     /* Configuration register 4    */
+#define TC_HIGH      0x0E     /* Transfer counter high       */
+#define PIO_FIFO_1   0x10     /* PIO FIFO register 1         */
+#define PIO_FIFO_2   0x11     /* PIO FIFO register 2         */
+#define PIO_FIFO_3   0x12     /* PIO FIFO register 3         */
+#define PIO_FIFO_4   0x13     /* PIO FIFO register 4         */
+#define PIO_FIFO_CNT 0x14     /* PIO FIFO count              */
+#define PIO_INT_REG  0x15     /* PIO interrupt register      */
+#define CONF_REG_5   0x16     /* Configuration register 5    */
+#define FEATURE_EN   0x1D     /* Feature Enable register     */
+
+/* Configuration register 1 entries: */
+/* Bits 2-0: SCSI ID of host adapter */
+#define SCM    0x80                     /* Slow Cable Mode              */
+#define SRID   0x40                     /* SCSI Reset Interrupt Disable */
+#define PTM    0x20                     /* Parity Test Mode             */
+#define EPC    0x10                     /* Enable Parity Checking       */
+#define CTME   0x08                     /* Special Test Mode            */
+
+/* Configuration register 2 entries: */
+#define FE     0x40                     /* Features Enable              */
+#define SCSI2  0x08                     /* SCSI 2 Enable                */
+#define TBPA   0x04                     /* Target Bad Parity Abort      */
+
+/* Configuration register 3 entries: */
+#define IDMRC  0x80                     /* ID Message Reserved Check    */
+#define QTE    0x40                     /* Queue Tag Enable             */
+#define CDB10  0x20                     /* Command Descriptor Block 10  */
+#define FSCSI  0x10                     /* FastSCSI                     */
+#define FCLK   0x08                     /* FastClock                    */
+
+/* Configuration register 4 entries: */
+#define RBS    0x08                     /* Register bank select         */
+#define EAN    0x04                     /* Enable Active Negotiation    */
+
+/* Configuration register 5 entries: */
+#define LPSR   0x80                     /* Lower Power SCSI Reset       */
+#define IE     0x20                     /* Interrupt Enable             */
+#define LPM    0x02                     /* Low Power Mode               */
+#define WSE0   0x01                     /* 0WS Enable                   */
+
+/* Interrupt register entries: */
+#define SRST   0x80                     /* SCSI Reset                   */
+#define ILCMD  0x40                     /* Illegal Command              */
+#define DIS    0x20                     /* Disconnect                   */
+#define BS     0x10                     /* Bus Service                  */
+#define FC     0x08                     /* Function Complete            */
+#define RESEL  0x04                     /* Reselected                   */
+#define SI     0x03                     /* Selection Interrupt          */
+
+/* Status Register Entries: */
+#define SCI    0x80                     /* SCSI Core Int                */
+#define GE     0x40                     /* Gross Error                  */
+#define PE     0x20                     /* Parity Error                 */
+#define TC     0x10                     /* Terminal Count               */
+#define VGC    0x08                     /* Valid Group Code             */
+#define PHBITS 0x07                     /* Phase bits                   */
+
+/* PIO Interrupt Register Entries: */
+#define SCI    0x80                     /* SCSI Core Int                */
+#define PFI    0x40                     /* PIO FIFO Interrupt           */
+#define FULL   0x20                     /* PIO FIFO Full                */
+#define EMPTY  0x10                     /* PIO FIFO Empty               */
+#define CE     0x08                     /* Collision Error              */
+#define OUE    0x04                     /* Overflow / Underflow error   */
+#define FIE    0x02                     /* Full Interrupt Enable        */
+#define EIE    0x01                     /* Empty Interrupt Enable       */
+
+/* SYM53C416 SCSI phases (lower 3 bits of SYM53C416_STATUS_REG) */
+#define PHASE_DATA_OUT    0x00
+#define PHASE_DATA_IN     0x01
+#define PHASE_COMMAND     0x02
+#define PHASE_STATUS      0x03
+#define PHASE_RESERVED_1  0x04
+#define PHASE_RESERVED_2  0x05
+#define PHASE_MESSAGE_OUT 0x06
+#define PHASE_MESSAGE_IN  0x07
+
+/* SYM53C416 core commands */
+#define NOOP                      0x00
+#define FLUSH_FIFO                0x01
+#define RESET_CHIP                0x02
+#define RESET_SCSI_BUS            0x03
+#define DISABLE_SEL_RESEL         0x45
+#define RESEL_SEQ                 0x40
+#define SEL_WITHOUT_ATN_SEQ       0x41
+#define SEL_WITH_ATN_SEQ          0x42
+#define SEL_WITH_ATN_AND_STOP_SEQ 0x43
+#define ENABLE_SEL_RESEL          0x44
+#define SEL_WITH_ATN3_SEQ         0x46
+#define RESEL3_SEQ                0x47
+#define SND_MSG                   0x20
+#define SND_STAT                  0x21
+#define SND_DATA                  0x22
+#define DISCONNECT_SEQ            0x23
+#define TERMINATE_SEQ             0x24
+#define TARGET_COMM_COMPLETE_SEQ  0x25
+#define DISCONN                   0x27
+#define RECV_MSG_SEQ              0x28
+#define RECV_CMD                  0x29
+#define RECV_DATA                 0x2A
+#define RECV_CMD_SEQ              0x2B
+#define TARGET_ABORT_PIO          0x04
+#define TRANSFER_INFORMATION      0x10
+#define INIT_COMM_COMPLETE_SEQ    0x11
+#define MSG_ACCEPTED              0x12
+#define TRANSFER_PAD              0x18
+#define SET_ATN                   0x1A
+#define RESET_ATN                 0x1B
+#define ILLEGAL                   0xFF
+
+#define PIO_MODE                  0x80
+
+#define IO_RANGE 0x20         /* 0x00 - 0x1F                   */
+#define ID       "sym53c416"
+#define PIO_SIZE 128          /* Size of PIO fifo is 128 bytes */
+
+#define READ_TIMEOUT              150
+#define WRITE_TIMEOUT             150
+
+#ifdef MODULE
+
+#define sym53c416_base sym53c416
+#define sym53c416_base_1 sym53c416_1
+#define sym53c416_base_2 sym53c416_2
+#define sym53c416_base_3 sym53c416_3
+
+static unsigned short sym53c416_base = 0;
+static unsigned int sym53c416_irq = 0;
+static unsigned short sym53c416_base_1 = 0;
+static unsigned int sym53c416_irq_1 = 0;
+static unsigned short sym53c416_base_2 = 0;
+static unsigned int sym53c416_irq_2 = 0;
+static unsigned short sym53c416_base_3 = 0;
+static unsigned int sym53c416_irq_3 = 0;
+
+#endif
+
+/* #define DEBUG */
+
+/* Macro for debugging purposes */
+
+#ifdef DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+#define MAXHOSTS 4
+
+enum phases
+  {
+  idle,
+  data_out,
+  data_in,
+  command_ph,
+  status_ph,
+  message_out,
+  message_in
+  };
+
+typedef struct
+  {
+  int base;
+  int irq;
+  int scsi_id;
+  } host;
+
+host hosts[MAXHOSTS] = {
+                       {0, 0, SYM53C416_SCSI_ID},
+                       {0, 0, SYM53C416_SCSI_ID},
+                       {0, 0, SYM53C416_SCSI_ID},
+                       {0, 0, SYM53C416_SCSI_ID}
+                       };
+
+static int host_index = 0;
+
+static char info[120];
+
+static Scsi_Cmnd *current_command = NULL;
+
+struct proc_dir_entry proc_scsi_sym53c416 = {PROC_SCSI_SYM53C416, 7, ID, S_IFDIR | S_IRUGO | S_IXUGO, 2};
+
+int fastpio = 1;
+
+int probeaddrs[] = {0x200, 0x220, 0x240, 0};
+
+static void sym53c416_set_transfer_counter(int base, unsigned int len)
+  {
+  /* Program Transfer Counter */
+  outb(len & 0x0000FF, base + TC_LOW);
+  outb((len & 0x00FF00) >> 8, base + TC_MID);
+  outb((len & 0xFF0000) >> 16, base + TC_HIGH);
+  }
+
+/* Returns the number of bytes read */
+static __inline__ unsigned int sym53c416_read(int base, unsigned char *buffer, unsigned int len)
+  {
+  unsigned int orig_len = len;
+  unsigned long flags = 0;
+  unsigned int bytes_left;
+  int i;
+  int timeout = READ_TIMEOUT;
+
+  /* Do transfer */
+  save_flags(flags);
+  cli();
+  while(len && timeout)
+    {
+    bytes_left = inb(base + PIO_FIFO_CNT); /* Number of bytes in the PIO FIFO */
+    if(fastpio && bytes_left > 3)
+      {
+      insl(base + PIO_FIFO_1, buffer, bytes_left >> 2);
+      buffer += bytes_left & 0xFC;
+      len -= bytes_left & 0xFC;
+      }
+    else if(bytes_left > 0)
+      {
+      len -= bytes_left;
+      for(; bytes_left > 0; bytes_left--)
+        *(buffer++) = inb(base + PIO_FIFO_1);
+      }
+    else
+      {
+      i = jiffies + timeout;
+      restore_flags(flags);
+      while(jiffies < i && (inb(base + PIO_INT_REG) & EMPTY) && timeout)
+        if(inb(base + PIO_INT_REG) & SCI)
+          timeout = 0;
+      save_flags(flags);
+      cli();
+      if(inb(base + PIO_INT_REG) & EMPTY)
+        timeout = 0;
+      }
+    }
+  restore_flags(flags);
+  return orig_len - len;
+  }
+
+/* Returns the number of bytes written */
+static __inline__ unsigned int sym53c416_write(int base, unsigned char *buffer, unsigned int len)
+  {
+  unsigned int orig_len = len;
+  unsigned long flags = 0;
+  unsigned int bufferfree;
+  unsigned int i;
+  unsigned int timeout = WRITE_TIMEOUT;
+
+  /* Do transfer */
+  save_flags(flags);
+  cli();
+  while(len && timeout)
+    {
+    bufferfree = PIO_SIZE - inb(base + PIO_FIFO_CNT);
+    if(bufferfree > len)
+      bufferfree = len;
+    if(fastpio && bufferfree > 3)
+      {
+      outsl(base + PIO_FIFO_1, buffer, bufferfree >> 2);
+      buffer += bufferfree & 0xFC;
+      len -= bufferfree & 0xFC;
+      }
+    else if(bufferfree > 0)
+      {
+      len -= bufferfree;
+      for(; bufferfree > 0; bufferfree--)
+        outb(*(buffer++), base + PIO_FIFO_1);
+      }
+    else
+      {
+      i = jiffies + timeout;
+      restore_flags(flags);
+      while(jiffies < i && (inb(base + PIO_INT_REG) & FULL) && timeout)
+        ;
+      save_flags(flags);
+      cli();
+      if(inb(base + PIO_INT_REG) & FULL)
+        timeout = 0;
+      }
+    }
+  restore_flags(flags);
+  return orig_len - len;
+  }
+
+static void sym53c416_intr_handle(int irq, void *dev_id, struct pt_regs *regs)
+  {
+  int base = 0;
+  int i;
+  unsigned long flags = 0;
+  unsigned char status_reg, pio_int_reg, int_reg;
+  struct scatterlist *sglist;
+  unsigned int sgcount;
+  unsigned int tot_trans = 0;
+
+  /* We search the base address of the host adapter which caused the interrupt */
+  for(i = 0; i < host_index && !base; i++)
+    if(irq == hosts[i].irq)
+      base = hosts[i].base;
+  /* If no adapter found, we cannot handle the interrupt. Leave a message */
+  /* and continue. This should never happen...                            */
+  if(!base)
+    {
+    printk("sym53c416: No host adapter defined for interrupt %d\n", irq);
+    return;
+    }
+  /* Now we have the base address and we can start handling the interrupt */
+  save_flags(flags);
+  cli();
+  status_reg = inb(base + STATUS_REG);
+  pio_int_reg = inb(base + PIO_INT_REG);
+  int_reg = inb(base + INT_REG);
+  restore_flags(flags);
+
+  /* First, we handle error conditions */
+  if(int_reg & SCI)         /* SCSI Reset */
+    {
+    printk("sym53c416: Warning: Reset received\n");
+    current_command->SCp.phase = idle;
+    current_command->result = DID_RESET << 16;
+    current_command->scsi_done(current_command);
+    return;
+    }
+  if(int_reg & ILCMD)       /* Illegal Command */
+    {
+    printk("sym53c416: Warning: Illegal Command: 0x%02x\n", inb(base + COMMAND_REG));
+    current_command->SCp.phase = idle;
+    current_command->result = DID_ERROR << 16;
+    current_command->scsi_done(current_command);
+    return;
+    }
+  if(status_reg & GE)         /* Gross Error */
+    {
+    printk("sym53c416: Warning: Gross Error\n");
+    current_command->SCp.phase = idle;
+    current_command->result = DID_ERROR << 16;
+    current_command->scsi_done(current_command);
+    return;
+    }
+  if(status_reg & PE)         /* Parity Error */
+    {
+    printk("sym53c416: Warning: Parity Error\n");
+    current_command->SCp.phase = idle;
+    current_command->result = DID_PARITY << 16;
+    current_command->scsi_done(current_command);
+    return;
+    }
+  if(pio_int_reg & (CE | OUE))
+    {
+    printk("sym53c416: Warning: PIO Interrupt Error\n");
+    current_command->SCp.phase = idle;
+    current_command->result = DID_ERROR << 16;
+    current_command->scsi_done(current_command);
+    return;
+    }
+  if(int_reg & DIS)           /* Disconnect */
+    {
+    if(current_command->SCp.phase != message_in)
+      current_command->result = DID_NO_CONNECT << 16;
+    else
+      current_command->result = (current_command->SCp.Status & 0xFF) | ((current_command->SCp.Message & 0xFF) << 8) | (DID_OK << 16);
+    current_command->SCp.phase = idle;
+    current_command->scsi_done(current_command);
+    return;
+    }
+  /* Now we handle SCSI phases         */
+  switch(status_reg & PHBITS)       /* Filter SCSI phase out of status reg */
+    {
+    case PHASE_DATA_OUT:
+      {
+      if(int_reg & BS)
+        {
+        current_command->SCp.phase = data_out;
+        outb(FLUSH_FIFO, base + COMMAND_REG);
+        sym53c416_set_transfer_counter(base, current_command->request_bufflen);
+        outb(TRANSFER_INFORMATION | PIO_MODE, base + COMMAND_REG);
+        if(!current_command->use_sg)
+          tot_trans = sym53c416_write(base, current_command->request_buffer, current_command->request_bufflen);
+        else
+          {
+          sgcount = current_command->use_sg;
+          sglist = current_command->request_buffer;
+          while(sgcount--)
+            {
+            tot_trans += sym53c416_write(base, sglist->address, sglist->length);
+            sglist++;
+            }
+          }
+        if(tot_trans < current_command->underflow)
+          printk("sym53c416: Warning: underflow, wrote %d bytes, request for %d bytes\n", tot_trans, current_command->underflow);
+        }
+      break;
+      }
+    case PHASE_DATA_IN:
+      {
+      if(int_reg & BS)
+        {
+        current_command->SCp.phase = data_in;
+        outb(FLUSH_FIFO, base + COMMAND_REG);
+        sym53c416_set_transfer_counter(base, current_command->request_bufflen);
+        outb(TRANSFER_INFORMATION | PIO_MODE, base + COMMAND_REG);
+        if(!current_command->use_sg)
+          tot_trans = sym53c416_read(base, current_command->request_buffer, current_command->request_bufflen);
+        else
+          {
+          sgcount = current_command->use_sg;
+          sglist = current_command->request_buffer;
+          while(sgcount--)
+            {
+            tot_trans += sym53c416_read(base, sglist->address, sglist->length);
+            sglist++;
+            }
+          }
+        if(tot_trans < current_command->underflow)
+          printk("sym53c416: Warning: underflow, read %d bytes, request for %d bytes\n", tot_trans, current_command->underflow);
+        }
+      break;
+      }
+    case PHASE_COMMAND:
+      {
+      current_command->SCp.phase = command_ph;
+      printk("sym53c416: Warning: Unknown interrupt in command phase\n");
+      break;
+      }
+    case PHASE_STATUS:
+      {
+      current_command->SCp.phase = status_ph;
+      outb(FLUSH_FIFO, base + COMMAND_REG);
+      outb(INIT_COMM_COMPLETE_SEQ, base + COMMAND_REG);
+      break;
+      }
+    case PHASE_RESERVED_1:
+    case PHASE_RESERVED_2:
+      {
+      printk("sym53c416: Warning: Reserved phase\n");
+      break;
+      }
+    case PHASE_MESSAGE_OUT:
+      {
+      current_command->SCp.phase = message_out;
+      outb(SET_ATN, base + COMMAND_REG);
+      outb(MSG_ACCEPTED, base + COMMAND_REG);
+      break;
+      }
+    case PHASE_MESSAGE_IN:
+      {
+      current_command->SCp.phase = message_in;
+      current_command->SCp.Status = inb(base + SCSI_FIFO);
+      current_command->SCp.Message = inb(base + SCSI_FIFO);
+      if(current_command->SCp.Message == SAVE_POINTERS || current_command->SCp.Message == DISCONNECT)
+        outb(SET_ATN, base + COMMAND_REG);
+      outb(MSG_ACCEPTED, base + COMMAND_REG);
+      break;
+      }
+    }
+  }
+
+static void sym53c416_init(int base, int scsi_id)
+  {
+  outb(RESET_CHIP, base + COMMAND_REG);
+  outb(NOOP, base + COMMAND_REG);
+  outb(0x99, base + TOM); /* Time out of 250 ms */
+  outb(0x05, base + STP);
+  outb(0x00, base + SYNC_OFFSET);
+  outb(EPC | scsi_id, base + CONF_REG_1);
+  outb(FE | SCSI2 | TBPA, base + CONF_REG_2);
+  outb(IDMRC | QTE | CDB10 | FSCSI | FCLK, base + CONF_REG_3);
+  outb(0x83 | EAN, base + CONF_REG_4);
+  outb(IE | WSE0, base + CONF_REG_5);
+  outb(0, base + FEATURE_EN);
+  }
+
+static int sym53c416_probeirq(int base, int scsi_id)
+  {
+  int irq, irqs, i;
+
+  /* Clear interrupt register */
+  inb(base + INT_REG);
+  /* Start probing for irq's */
+  irqs = probe_irq_on();
+  /* Reinit chip */
+  sym53c416_init(base, scsi_id);
+  /* Cause interrupt */
+  outb(NOOP, base + COMMAND_REG);
+  outb(ILLEGAL, base + COMMAND_REG);
+  outb(0x07, base + DEST_BUS_ID);
+  outb(0x00, base + DEST_BUS_ID);
+  /* Wait for interrupt to occur */
+  i = jiffies + 20;
+  while(i > jiffies && !(inb(base + STATUS_REG) & SCI))
+    barrier();
+  if(i <= jiffies) /* timed out */
+    return 0;
+  /* Get occurred irq */
+  irq = probe_irq_off(irqs);
+  sym53c416_init(base, scsi_id);
+  return irq;
+  }
+
+/* Setup: sym53c416=base,irq */
+void sym53c416_setup(char *str, int *ints)
+  {
+  int i;
+
+  if(host_index >= MAXHOSTS)
+    {
+    printk("sym53c416.c: Too many hosts defined\n");
+    }
+  else
+    {
+    if(ints[0] < 1 || ints[0] > 2)
+      {
+      printk("sym53c416.c: Wrong number of parameters:\n");
+      printk("sym53c416.c: usage: sym53c416=<base>[,<irq>]\n");
+      }
+    else
+      {
+      for(i = 0; i < host_index && i >= 0; i++)
+        if(hosts[i].base == ints[1])
+          i = -2;
+      if(i >= 0)
+        {
+        hosts[host_index].base = ints[1];
+        hosts[host_index].irq = (ints[0] == 2)? ints[2] : 0;
+        host_index++;
+        }
+      }
+    }
+  }
+
+static int sym53c416_test(int base)
+  {
+  outb(RESET_CHIP, base + COMMAND_REG);
+  outb(NOOP, base + COMMAND_REG);
+  if(inb(base + COMMAND_REG) != NOOP)
+    return 0;
+  if(!inb(base + TC_HIGH) || inb(base + TC_HIGH) == 0xFF)
+    return 0;
+  if((inb(base + PIO_INT_REG) & (FULL | EMPTY | CE | OUE | FIE | EIE)) != EMPTY)
+    return 0;
+  return 1;
+  }
+
+void sym53c416_probe(void)
+  {
+  int *base = probeaddrs;
+  int ints[2];
+
+  ints[0] = 1;
+  for(; *base; base++)
+    if(!check_region(*base, IO_RANGE) && sym53c416_test(*base))
+      {
+      ints[1] = *base;
+      sym53c416_setup(NULL, ints);
+      }
+  }
+
+int sym53c416_detect(Scsi_Host_Template *tpnt)
+  {
+  unsigned long flags;
+  struct Scsi_Host * shpnt = NULL;
+  int i;
+  int count;
+
+#ifdef MODULE
+  int ints[3];
+
+  ints[0] = 2;
+  if(sym53c416_base)
+    {
+    ints[1] = sym53c416_base;
+    ints[2] = sym53c416_irq;
+    sym53c416_setup(NULL, ints);
+    }
+  if(sym53c416_base_1)
+    {
+    ints[1] = sym53c416_base_1;
+    ints[2] = sym53c416_irq_1;
+    sym53c416_setup(NULL, ints);
+    }
+  if(sym53c416_base_2)
+    {
+    ints[1] = sym53c416_base_2;
+    ints[2] = sym53c416_irq_2;
+    sym53c416_setup(NULL, ints);
+    }
+  if(sym53c416_base_3)
+    {
+    ints[1] = sym53c416_base_3;
+    ints[2] = sym53c416_irq_3;
+    sym53c416_setup(NULL, ints);
+    }
+#endif
+
+  printk("sym53c416.c: %s\n", VERSION_STRING);
+
+  sym53c416_probe();
+
+  /* Now we register and set up each host adapter found... */
+  for(count = 0, i = 0; i < host_index; i++)
+    if(!sym53c416_test(hosts[i].base))
+      printk("No sym53c416 found at address 0x%03x\n", hosts[i].base);
+    else
+      {
+      if(hosts[i].irq == 0)
+        /* We don't have an irq yet, so we should probe for one */
+        if((hosts[i].irq = sym53c416_probeirq(hosts[i].base, hosts[i].scsi_id)) == 0)
+          printk("irq autoprobing failed for sym53c416 at address 0x%03x\n", hosts[i].base);
+      if(hosts[i].irq && !check_region(hosts[i].base, IO_RANGE))
+        {
+        shpnt = scsi_register(tpnt, 0);
+        save_flags(flags);
+        cli();
+        /* Request for specified IRQ */
+        if(request_irq(hosts[i].irq, sym53c416_intr_handle, 0, ID, NULL))
+          {
+          restore_flags(flags);
+          printk("Unable to assign IRQ %d\n", hosts[i].irq);
+          scsi_unregister(shpnt);
+          }
+        else
+          {
+          /* Inform the kernel of our IO range */
+          request_region(hosts[i].base, IO_RANGE, ID);
+          shpnt->unique_id = hosts[i].base;
+          shpnt->io_port = hosts[i].base;
+          shpnt->n_io_port = IO_RANGE;
+          shpnt->irq = hosts[i].irq;
+          shpnt->this_id = hosts[i].scsi_id;
+          sym53c416_init(hosts[i].base, hosts[i].scsi_id);
+          count++;
+          restore_flags(flags);
+          }
+        }
+      }
+  return count;
+  }
+
+const char *sym53c416_info(struct Scsi_Host *SChost)
+  {
+  int i;
+  int base = SChost->io_port;
+  int irq = SChost->irq;
+  int scsi_id = 0;
+  int rev = inb(base + TC_HIGH);
+
+  for(i = 0; i < host_index; i++)
+    if(hosts[i].base == base)
+      scsi_id = hosts[i].scsi_id;
+  sprintf(info, "Symbios Logic 53c416 (rev. %d) at 0x%03x, irq %d, SCSI-ID %d, %s pio", rev, base, irq, scsi_id, (fastpio)? "fast" : "slow");
+  return info;
+  }
+
+int sym53c416_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+  {
+  int base;
+  unsigned long flags = 0;
+  int i;
+
+  /* Store base register as we can have more than one controller in the system */
+  base = SCpnt->host->io_port;
+  current_command = SCpnt;                  /* set current command                */
+  current_command->scsi_done = done;        /* set ptr to done function           */
+  current_command->SCp.phase = command_ph;  /* currect phase is the command phase */
+  current_command->SCp.Status = 0;
+  current_command->SCp.Message = 0;
+
+  save_flags(flags);
+  cli();
+  outb(SCpnt->target, base + DEST_BUS_ID); /* Set scsi id target        */
+  outb(FLUSH_FIFO, base + COMMAND_REG);    /* Flush SCSI and PIO FIFO's */
+  /* Write SCSI command into the SCSI fifo */
+  for(i = 0; i < SCpnt->cmd_len; i++)
+    outb(SCpnt->cmnd[i], base + SCSI_FIFO);
+  /* Start selection sequence */
+  outb(SEL_WITHOUT_ATN_SEQ, base + COMMAND_REG);
+  /* Now an interrupt will be generated which we will catch in out interrupt routine */
+  restore_flags(flags);
+  return 0;
+  }
+
+static void internal_done(Scsi_Cmnd *SCpnt)
+  {
+  SCpnt->SCp.Status++;
+  }
+
+int sym53c416_command(Scsi_Cmnd *SCpnt)
+  {
+  sym53c416_queuecommand(SCpnt, internal_done);
+  SCpnt->SCp.Status = 0;
+  while(!SCpnt->SCp.Status)
+    barrier();
+  return SCpnt->result;
+  }
+
+int sym53c416_abort(Scsi_Cmnd *SCpnt)
+  {
+  printk("sym53c416_abort\n");
+
+  /* We don't know how to abort for the moment */
+  return SCSI_ABORT_SNOOZE;
+  }
+
+int sym53c416_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+  {
+  int base;
+  int scsi_id = -1;
+  int i;
+
+  printk("sym53c416_reset\n");
+  base = SCpnt->host->io_port;
+  /* search scsi_id */
+  for(i = 0; i < host_index && scsi_id != -1; i++)
+    if(hosts[i].base == base)
+      scsi_id = hosts[i].scsi_id;
+  outb(RESET_CHIP, base + COMMAND_REG);
+  outb(NOOP | PIO_MODE, base + COMMAND_REG);
+  outb(RESET_SCSI_BUS, base + COMMAND_REG);
+  sym53c416_init(base, scsi_id);
+  return SCSI_RESET_PENDING;
+  }
+
+int sym53c416_bios_param(Disk *disk, kdev_t dev, int *ip)
+  {
+  int size;
+
+  size = disk->capacity;
+  ip[0] = 64;                         /* heads                        */
+  ip[1] = 32;                         /* sectors                      */
+  if((ip[2] = size >> 11) > 1024)     /* cylinders, test for big disk */
+    {
+    ip[0] = 255;                      /* heads                        */
+    ip[1] = 63;                       /* sectors                      */
+    ip[2] = size / (255 * 63);        /* cylinders                    */
+    }
+  return 0;
+  }
+
+/* Loadable module support */
+#ifdef MODULE
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,26)
+MODULE_AUTHOR("Lieven Willems");
+MODULE_PARM(sym53c416, "1-2i");
+MODULE_PARM(sym53c416_1, "1-2i");
+MODULE_PARM(sym53c416_2, "1-2i");
+MODULE_PARM(sym53c416_3, "1-2i");
+#endif
+
+Scsi_Host_Template driver_template = SYM53C416;
+
+#include "scsi_module.c"
+#endif
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/sym53c416.h linux/drivers/scsi/sym53c416.h
--- linux.vanilla/drivers/scsi/sym53c416.h	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/sym53c416.h	Mon Mar 22 00:54:37 1999
@@ -0,0 +1,91 @@
+/*
+ *  sym53c416.h
+ * 
+ *  Copyright (C) 1998 Lieven Willems (lw_linux@hotmail.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License as published by the
+ *  Free Software Foundation; either version 2, or (at your option) any
+ *  later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ */
+
+#ifndef _SYM53C416_H
+#define _SYM53C416_H
+
+#if !defined(LINUX_VERSION_CODE)
+#include <linux/version.h>
+#endif
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+#define SYM53C416_SCSI_ID 7
+
+extern struct proc_dir_entry proc_scsi_sym53c416;
+
+extern int sym53c416_detect(Scsi_Host_Template *);
+extern const char *sym53c416_info(struct Scsi_Host *);
+extern int sym53c416_command(Scsi_Cmnd *);
+extern int sym53c416_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+extern int sym53c416_abort(Scsi_Cmnd *);
+extern int sym53c416_reset(Scsi_Cmnd *, unsigned int);
+extern int sym53c416_bios_param(Disk *, kdev_t, int *);
+extern void sym53c416_setup(char *str, int *ints);
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,75)
+
+#define SYM53C416 {                                          \
+                  proc_dir:          &proc_scsi_sym53c416,   \
+                  name:              "Symbios Logic 53c416", \
+                  detect:            sym53c416_detect,       \
+                  info:              sym53c416_info,         \
+                  command:           sym53c416_command,      \
+                  queuecommand:      sym53c416_queuecommand, \
+                  abort:             sym53c416_abort,        \
+                  reset:             sym53c416_reset,        \
+                  bios_param:        sym53c416_bios_param,   \
+                  can_queue:         1,                      \
+                  this_id:           SYM53C416_SCSI_ID,      \
+                  sg_tablesize:      32,                     \
+                  cmd_per_lun:       1,                      \
+                  unchecked_isa_dma: 1,                      \
+                  use_clustering:    ENABLE_CLUSTERING       \
+                  }
+
+#else
+
+#define SYM53C416 {                       \
+                  NULL,                   \
+                  NULL,                   \
+                  &proc_scsi_sym53c416,   \
+                  NULL,                   \
+                  "Symbios Logic 53c416", \
+                  sym53c416_detect,       \
+                  NULL,                   \
+                  sym53c416_info,         \
+                  sym53c416_command,      \
+                  sym53c416_queuecommand, \
+                  sym53c416_abort,        \
+                  sym53c416_reset,        \
+                  NULL,                   \
+                  sym53c416_bios_param,   \
+                  1,                      \
+                  SYM53C416_SCSI_ID,      \
+                  32, /* ???? */          \
+                  1,                      \
+                  0,                      \
+                  1,                      \
+                  ENABLE_CLUSTERING       \
+                  }
+
+#endif
+
+#endif
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/tmscsim.c linux/drivers/scsi/tmscsim.c
--- linux.vanilla/drivers/scsi/tmscsim.c	Sun Jun 21 18:41:18 1998
+++ linux/drivers/scsi/tmscsim.c	Sun Dec 27 20:31:40 1998
@@ -5,36 +5,141 @@
  *		     Bus Master Host Adapter			       *
  * (C)Copyright 1995-1996 Tekram Technology Co., Ltd.		       *
  ***********************************************************************/
-/*	Minor enhancements and bugfixes by				*
- *	Kurt Garloff <K.Garloff@ping.de>				*
+/* (C) Copyright: put under GNU GPL in 10/96				*
+*************************************************************************/
+/* $Id: tmscsim.c,v 2.16 1998/12/25 17:54:44 garloff Exp $		*/
+/*	Enhancements and bugfixes by					*
+ *	Kurt Garloff <kurt@garloff.de>					*
  ***********************************************************************/
 /*	HISTORY:							*
  *									*
  *	REV#	DATE	NAME	DESCRIPTION				*
- *	1.00  04/24/96	CLH	First release				*
- *	1.01  06/12/96	CLH	Fixed bug of Media Change for Removable *
+ *	1.00  96/04/24	CLH	First release				*
+ *	1.01  96/06/12	CLH	Fixed bug of Media Change for Removable *
  *				Device, scan all LUN. Support Pre2.0.10 *
- *	1.02  06/18/96	CLH	Fixed bug of Command timeout ...	*
- *	1.03  09/25/96	KG	Added tmscsim_proc_info()		*
- *	1.04  10/11/96	CLH	Updating for support KV 2.0.x		*
- *	1.05  10/18/96	KG	Fixed bug in DC390_abort(null ptr deref)*
- *	1.06  10/25/96	KG	Fixed module support			*
- *	1.07  11/09/96	KG	Fixed tmscsim_proc_info()		*
- *	1.08  11/18/96	KG	Fixed null ptr in DC390_Disconnect()	*
- *	1.09  11/30/96	KG	Added register the allocated IO space	*
- *	1.10  12/05/96	CLH	Modified tmscsim_proc_info(), and reset *
+ *	1.02  96/06/18	CLH	Fixed bug of Command timeout ...	*
+ *	1.03  96/09/25	KG	Added tmscsim_proc_info()		*
+ *	1.04  96/10/11	CLH	Updating for support KV 2.0.x		*
+ *	1.05  96/10/18	KG	Fixed bug in DC390_abort(null ptr deref)*
+ *	1.06  96/10/25	KG	Fixed module support			*
+ *	1.07  96/11/09	KG	Fixed tmscsim_proc_info()		*
+ *	1.08  96/11/18	KG	Fixed null ptr in DC390_Disconnect()	*
+ *	1.09  96/11/30	KG	Added register the allocated IO space	*
+ *	1.10  96/12/05	CLH	Modified tmscsim_proc_info(), and reset *
  *				pending interrupt in DC390_detect()	*
- * 	1.11  02/05/97	KG/CLH	Fixeds problem with partitions greater	*
- * 				than 1GB				*
+ *	1.11  97/02/05	KG/CLH	Fixeds problem with partitions greater	*
+ *				than 1GB				*
+ *	1.12  98/02/15  MJ      Rewritten PCI probing			*
+ *	1.13  98/04/08	KG	Support for non DC390, __initfunc decls,*
+ *				changed max devs from 10 to 16		*
+ *	1.14a 98/05/05	KG	Dynamic DCB allocation, add-single-dev	*
+ *				for LUNs if LUN_SCAN (BIOS) not set	*
+ *				runtime config using /proc interface	*
+ *	1.14b 98/05/06	KG	eliminated cli (); sti (); spinlocks	*
+ *	1.14c 98/05/07	KG	2.0.x compatibility			*
+ *	1.20a 98/05/07	KG	changed names of funcs to be consistent *
+ *				DC390_ (entry points), dc390_ (internal)*
+ *				reworked locking			*
+ *	1.20b 98/05/12	KG	bugs: version, kfree, _ctmp		*
+ *				debug output				*
+ *	1.20c 98/05/12	KG	bugs: kfree, parsing, EEpromDefaults	*
+ *	1.20d 98/05/14	KG	bugs: list linkage, clear flag after  	*
+ *				reset on startup, code cleanup		*
+ *	1.20e 98/05/15	KG	spinlock comments, name space cleanup	*
+ *				pLastDCB now part of ACB structure	*
+ *				added stats, timeout for 2.1, TagQ bug	*
+ *				RESET and INQUIRY interface commands	*
+ *	1.20f 98/05/18	KG	spinlocks fixes, max_lun fix, free DCBs	*
+ *				for missing LUNs, pending int		*
+ *	1.20g 98/05/19	KG	Clean up: Avoid short			*
+ *	1.20h 98/05/21	KG	Remove AdaptSCSIID, max_lun ...		*
+ *	1.20i 98/05/21	KG	Aiiie: Bug with TagQMask       		*
+ *	1.20j 98/05/24	KG	Handle STAT_BUSY, handle pACB->pLinkDCB	*
+ *				== 0 in remove_dev and DoingSRB_Done	*
+ *	1.20k 98/05/25	KG	DMA_INT	(experimental)	       		*
+ *	1.20l 98/05/27	KG	remove DMA_INT; DMA_IDLE cmds added;	*
+ *	1.20m 98/06/10	KG	glitch configurable; made some global	*
+ *				vars part of ACB; use DC390_readX	*
+ *	1.20n 98/06/11	KG	startup params				*
+ *	1.20o 98/06/15	KG	added TagMaxNum to boot/module params	*
+ *				Device Nr -> Idx, TagMaxNum power of 2  *
+ *	1.20p 98/06/17	KG	Docu updates. Reset depends on settings *
+ *				pci_set_master added; 2.0.xx: pcibios_*	*
+ *				used instead of MechNum things ...	*
+ *	1.20q 98/06/23	KG	Changed defaults. Added debug code for	*
+ *				removable media and fixed it. TagMaxNum	*
+ *				fixed for DC390. Locking: ACB, DRV for	*
+ *				better IRQ sharing. Spelling: Queueing	*
+ *				Parsing and glitch_cfg changes. Display	*
+ *				real SyncSpeed value. Made DisConn	*
+ *				functional (!)				*
+ *	1.20r 98/06/30	KG	Debug macros, allow disabling DsCn, set	*
+ *				BIT4 in CtrlR4, EN_PAGE_INT, 2.0 module	*
+ *				param -1 fixed.				*
+ *	1.20s 98/08/20	KG	Debug info on abort(), try to check PCI,*
+ *				phys_to_bus instead of phys_to_virt,	*
+ *				fixed sel. process, fixed locking,	*
+ *				added MODULE_XXX infos, changed IRQ	*
+ *				request flags, disable DMA_INT		*
+ *	1.20t 98/09/07	KG	TagQ report fixed; Write Erase DMA Stat;*
+ *				initfunc -> __init; better abort;	*
+ *				Timeout for XFER_DONE & BLAST_COMPLETE;	*
+ *				Allow up to 33 commands being processed *
+ *	2.0a  98/10/14	KG	Max Cmnds back to 17. DMA_Stat clearing *
+ *				all flags. Clear within while() loops	*
+ *				in DataIn_0/Out_0. Null ptr in dumpinfo	*
+ *				for pSRB==0. Better locking during init.*
+ *				bios_param() now respects part. table.	*
+ *	2.0b  98/10/24	KG	Docu fixes. Timeout Msg in DMA Blast.	*
+ *				Disallow illegal idx in INQUIRY/REMOVE	*
+ *	2.0c  98/11/19	KG	Cleaned up detect/init for SMP boxes, 	*
+ *				Write Erase DMA (1.20t) caused problems	*
+ *	2.0d  98/12/25	KG	Christmas release ;-) Message handling  *
+ *				competely reworked. Handle target ini-	*
+ *				tiated SDTR correctly.			*
  ***********************************************************************/
 
+/* Uncomment SA_INTERRUPT, if the driver refuses to share its IRQ with other devices */
+#define DC390_IRQ SA_SHIRQ /* | SA_INTERRUPT */
 
-#define DC390_DEBUG
+/* DEBUG options */
+//#define DC390_DEBUG0
+//#define DC390_DEBUG1
+//#define DC390_DCBDEBUG
+//#define DC390_PARSEDEBUG
+//#define DC390_REMOVABLEDEBUG
 
-#define SCSI_MALLOC
+/* Debug definitions */
+#ifdef DC390_DEBUG0
+# define DEBUG0(x) x;
+#else
+# define DEBUG0(x)
+#endif
+#ifdef DC390_DEBUG1
+# define DEBUG1(x) x;
+#else
+# define DEBUG1(x)
+#endif
+#ifdef DC390_DCBDEBUG
+# define DCBDEBUG(x) x;
+#else
+# define DCBDEBUG(x)
+#endif
+#ifdef DC390_PARSEDEBUG
+# define PARSEDEBUG(x) x;
+#else
+# define PARSEDEBUG(x)
+#endif
+#ifdef DC390_REMOVABLEDEBUG
+# define REMOVABLEDEBUG(x) x;
+#else
+# define REMOVABLEDEBUG(x)
+#endif
+#define DCBDEBUG1(x)
 
+/* Includes */
 #ifdef MODULE
-#include <linux/module.h>
+# include <linux/module.h>
 #endif
 
 #include <asm/dma.h>
@@ -46,23 +151,17 @@
 #include <linux/errno.h>
 #include <linux/kernel.h>
 #include <linux/ioport.h>
-#include <linux/bios32.h>
 #include <linux/pci.h>
 #include <linux/proc_fs.h>
 #include <linux/string.h>
+#include <linux/ctype.h>
 #include <linux/mm.h>
 #include <linux/config.h>
-
 #include <linux/version.h>
-#if LINUX_VERSION_CODE < 66354 /* 1.3.50 */
-#include "../block/blk.h"
-#else
 #include <linux/blk.h>
-#endif
 
 #include "scsi.h"
 #include "hosts.h"
-#include "tmscsim.h"
 #include "constants.h"
 #include "sd.h"
 #include <linux/stat.h>
@@ -71,112 +170,530 @@
 
 #define PCI_DEVICE_ID_AMD53C974 	PCI_DEVICE_ID_AMD_SCSI
 
+/* Locking */
 
-#ifndef  VERSION_ELF_1_2_13
-struct proc_dir_entry	proc_scsi_tmscsim ={
-       PROC_SCSI_DC390T, 7 ,"tmscsim",
-       S_IFDIR | S_IRUGO | S_IXUGO, 2
-       };
+/* Note: Starting from 2.1.9x, the mid-level scsi code issues a 
+ * spinlock_irqsave (&io_request_lock) before calling the driver's 
+ * routines, so we don't need to lock.
+ * TODO: Verify, if we are locked in every case!
+ * The policy 3, let the midlevel scsi code do the io_request_locks
+ * and us locking on a driver specific lock, shouldn't hurt anybody; it
+ * just causes a minor performance degradation for setting the locks.
+ */
+
+/* spinlock things
+ * level 3: lock on both adapter specific locks and (global) io_request_lock
+ * level 2: lock on adapter specific locks only
+ * level 1: rely on the locking of the mid level code (io_request_lock)
+ * undef  : traditional save_flags; cli; restore_flags;
+ */
+
+//#define DEBUG_SPINLOCKS 2	/* Set to 0, 1 or 2 in include/asm/spinlock.h */
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,30)
+# include <linux/init.h>
+# include <asm/spinlock.h>
 #endif
 
-static USHORT DC390_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB );
-static void DC390_DataOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
-static void DC390_DataIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
-static void DC390_Command_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
-static void DC390_Status_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
-static void DC390_MsgOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
-static void DC390_MsgIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
-static void DC390_DataOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
-static void DC390_DataInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
-static void DC390_CommandPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
-static void DC390_StatusPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
-static void DC390_MsgOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
-static void DC390_MsgInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
-static void DC390_Nop_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
-static void DC390_Nop_1( PACB pACB, PSRB pSRB, PUCHAR psstatus);
-
-static void SetXferRate( PACB pACB, PDCB pDCB );
-static void DC390_Disconnect( PACB pACB );
-static void DC390_Reselect( PACB pACB );
-static void SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB );
-static void DoingSRB_Done( PACB pACB );
-static void DC390_ScsiRstDetect( PACB pACB );
-static void DC390_ResetSCSIBus( PACB pACB );
-static void RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB );
-static void EnableMsgOut2( PACB pACB, PSRB pSRB );
-static void EnableMsgOut( PACB pACB, PSRB pSRB );
-static void DC390_InvalidCmd( PACB pACB );
 
-int    DC390_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index );
-void   DC390_initDCB( PACB pACB, PDCB pDCB, PSCSICMD cmd );
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93) 
+# define USE_SPINLOCKS 1
+# define NEW_PCI 1
+#else
+# undef NEW_PCI
+# if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,30)
+#  define USE_SPINLOCKS 2
+# endif
+#endif
+
+#ifdef USE_SPINLOCKS
+
+# if USE_SPINLOCKS == 3 /* both */
+
+#  if defined (__SMP__) || DEBUG_SPINLOCKS > 0
+#   define DC390_LOCKA_INIT { spinlock_t __unlocked = SPIN_LOCK_UNLOCKED; pACB->lock = __unlocked; };
+#  else
+#   define DC390_LOCKA_INIT
+#  endif
+   spinlock_t dc390_drvlock = SPIN_LOCK_UNLOCKED;
+
+#  define DC390_AFLAGS unsigned long aflags;
+#  define DC390_IFLAGS unsigned long iflags;
+#  define DC390_DFLAGS unsigned long dflags; 
+
+#  define DC390_LOCK_IO spin_lock_irqsave (&io_request_lock, iflags)
+#  define DC390_UNLOCK_IO spin_unlock_irqrestore (&io_request_lock, iflags)
+
+#  define DC390_LOCK_DRV spin_lock_irqsave (&dc390_drvlock, dflags)
+#  define DC390_UNLOCK_DRV spin_unlock_irqrestore (&dc390_drvlock, dflags)
+#  define DC390_LOCK_DRV_NI spin_lock (&dc390_drvlock)
+#  define DC390_UNLOCK_DRV_NI spin_unlock (&dc390_drvlock)
+
+#  define DC390_LOCK_ACB spin_lock_irqsave (&(pACB->lock), aflags)
+#  define DC390_UNLOCK_ACB spin_unlock_irqrestore (&(pACB->lock), aflags)
+#  define DC390_LOCK_ACB_NI spin_lock (&(pACB->lock))
+#  define DC390_UNLOCK_ACB_NI spin_unlock (&(pACB->lock))
+//#  define DC390_LOCKA_INIT spin_lock_init (&(pACB->lock))
+
+# else
+
+#  if USE_SPINLOCKS == 2 /* adapter specific locks */
+
+#   if defined (__SMP__) || DEBUG_SPINLOCKS > 0
+#    define DC390_LOCKA_INIT { spinlock_t __unlocked = SPIN_LOCK_UNLOCKED; pACB->lock = __unlocked; };
+#   else
+#    define DC390_LOCKA_INIT
+#   endif
+    spinlock_t dc390_drvlock = SPIN_LOCK_UNLOCKED;
+#   define DC390_AFLAGS unsigned long aflags;
+#   define DC390_IFLAGS 
+#  define DC390_DFLAGS unsigned long dflags; 
+#   define DC390_LOCK_IO /* spin_lock_irqsave (&io_request_lock, iflags) */
+#   define DC390_UNLOCK_IO /* spin_unlock_irqrestore (&io_request_lock, iflags) */
+#   define DC390_LOCK_DRV spin_lock_irqsave (&dc390_drvlock, dflags)
+#   define DC390_UNLOCK_DRV spin_unlock_irqrestore (&dc390_drvlock, dflags)
+#   define DC390_LOCK_DRV_NI spin_lock (&dc390_drvlock)
+#   define DC390_UNLOCK_DRV_NI spin_unlock (&dc390_drvlock)
+#   define DC390_LOCK_ACB spin_lock_irqsave (&(pACB->lock), aflags)
+#   define DC390_UNLOCK_ACB spin_unlock_irqrestore (&(pACB->lock), aflags)
+#   define DC390_LOCK_ACB_NI spin_lock (&(pACB->lock))
+#   define DC390_UNLOCK_ACB_NI spin_unlock (&(pACB->lock))
+//#   define DC390_LOCKA_INIT spin_lock_init (&(pACB->lock))
+
+#  else /* USE_SPINLOCKS == 1: global lock io_request_lock */
+
+#   define DC390_AFLAGS 
+#   define DC390_IFLAGS unsigned long iflags;
+#   define DC390_DFLAGS unsigned long dflags; 
+    spinlock_t dc390_drvlock = SPIN_LOCK_UNLOCKED;
+#   define DC390_LOCK_IO spin_lock_irqsave (&io_request_lock, iflags)
+#   define DC390_UNLOCK_IO spin_unlock_irqrestore (&io_request_lock, iflags)
+#   define DC390_LOCK_DRV spin_lock_irqsave (&dc390_drvlock, dflags)
+#   define DC390_UNLOCK_DRV spin_unlock_irqrestore (&dc390_drvlock, dflags)
+#   define DC390_LOCK_DRV_NI spin_lock (&dc390_drvlock)
+#   define DC390_UNLOCK_DRV_NI spin_unlock (&dc390_drvlock)
+#   define DC390_LOCK_ACB /* DC390_LOCK_IO */
+#   define DC390_UNLOCK_ACB /* DC390_UNLOCK_IO */
+#   define DC390_LOCK_ACB_NI /* spin_lock (&(pACB->lock)) */
+#   define DC390_UNLOCK_ACB_NI /* spin_unlock (&(pACB->lock)) */
+#   define DC390_LOCKA_INIT /* DC390_LOCKA_INIT */
+
+#  endif /* 2 */
+# endif /* 3 */
+
+#else /* USE_SPINLOCKS undefined */
+
+# define DC390_AFLAGS unsigned long aflags;
+# define DC390_IFLAGS unsigned long iflags;
+# define DC390_DFLAGS unsigned long dflags; 
+# define DC390_LOCK_IO save_flags (iflags); cli ()
+# define DC390_UNLOCK_IO restore_flags (iflags)
+# define DC390_LOCK_DRV save_flags (dflags); cli ()
+# define DC390_UNLOCK_DRV restore_flags (dflags)
+# define DC390_LOCK_DRV_NI
+# define DC390_UNLOCK_DRV_NI
+# define DC390_LOCK_ACB save_flags (aflags); cli ()
+# define DC390_UNLOCK_ACB restore_flags (aflags)
+# define DC390_LOCK_ACB_NI
+# define DC390_UNLOCK_ACB_NI
+# define DC390_LOCKA_INIT
+#endif /* def */
+
+
+/* These macros are used for uniform access to 2.0.x and 2.1.x PCI config space*/
+
+#ifdef NEW_PCI
+# define PDEV pdev
+# define PDEVDECL struct pci_dev *pdev
+# define PDEVDECL0 struct pci_dev *pdev = NULL
+# define PDEVDECL1 struct pci_dev *pdev
+# define PDEVSET pACB->pdev=pdev
+# define PDEVSET1 pdev=pACB->pdev
+# define PCI_WRITE_CONFIG_BYTE(pd, rv, bv) pci_write_config_byte (pd, rv, bv)
+# define PCI_READ_CONFIG_BYTE(pd, rv, bv) pci_read_config_byte (pd, rv, bv)
+# define PCI_WRITE_CONFIG_WORD(pd, rv, bv) pci_write_config_word (pd, rv, bv)
+# define PCI_READ_CONFIG_WORD(pd, rv, bv) pci_read_config_word (pd, rv, bv)
+# define PCI_BUS_DEV pdev->bus->number, pdev->devfn
+# define PCI_PRESENT pci_present ()
+# define PCI_SET_MASTER pci_set_master (pdev)
+# define PCI_FIND_DEVICE(vend, id) (pdev = pci_find_device (vend, id, pdev))
+# define PCI_GET_IO_AND_IRQ io_port = pdev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; irq = pdev->irq
+#else
+# include <linux/bios32.h>
+# define PDEV pbus, pdevfn
+# define PDEVDECL UCHAR pbus, UCHAR pdevfn
+# define PDEVDECL0 UCHAR pbus = 0; UCHAR pdevfn = 0; USHORT pci_index = 0; int error
+# define PDEVDECL1 UCHAR pbus; UCHAR pdevfn /*; USHORT pci_index */
+# define PDEVSET pACB->pbus=pbus; pACB->pdevfn=pdevfn /*; pACB->pci_index=pci_index */
+# define PDEVSET1 pbus=pACB->pbus; pdevfn=pACB->pdevfn /*; pci_index=pACB->pci_index */
+# define PCI_WRITE_CONFIG_BYTE(pd, rv, bv) pcibios_write_config_byte (pd, rv, bv)
+# define PCI_READ_CONFIG_BYTE(pd, rv, bv) pcibios_read_config_byte (pd, rv, bv)
+# define PCI_WRITE_CONFIG_WORD(pd, rv, bv) pcibios_write_config_word (pd, rv, bv)
+# define PCI_READ_CONFIG_WORD(pd, rv, bv) pcibios_read_config_word (pd, rv, bv)
+# define PCI_BUS_DEV pbus, pdevfn
+# define PCI_PRESENT pcibios_present ()
+# define PCI_SET_MASTER dc390_set_master (pbus, pdevfn)
+# define PCI_FIND_DEVICE(vend, id) (!pcibios_find_device (vend, id, pci_index++, &pbus, &pdevfn))
+# define PCI_GET_IO_AND_IRQ error = pcibios_read_config_dword (pbus, pdevfn, PCI_BASE_ADDRESS_0, &io_port);	\
+ error |= pcibios_read_config_byte (pbus, pdevfn, PCI_INTERRUPT_LINE, &irq);	\
+ io_port &= 0xfffe;	\
+ if (error) { printk (KERN_ERR "DC390_detect: Error reading PCI config registers!\n"); continue; }
+#endif 
+
+#include "tmscsim.h"
+
+#ifndef __init
+# define __init
+#endif
+
+UCHAR dc390_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB );
+void dc390_DataOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+void dc390_DataIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void dc390_Command_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void dc390_Status_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void dc390_MsgOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+void dc390_MsgIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void dc390_DataOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void dc390_DataInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+void dc390_CommandPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void dc390_StatusPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+void dc390_MsgOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void dc390_MsgInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void dc390_Nop_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void dc390_Nop_1( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+
+static void dc390_SetXferRate( PACB pACB, PDCB pDCB );
+void dc390_Disconnect( PACB pACB );
+void dc390_Reselect( PACB pACB );
+void dc390_SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB );
+void dc390_DoingSRB_Done( PACB pACB );
+static void dc390_ScsiRstDetect( PACB pACB );
+static void dc390_ResetSCSIBus( PACB pACB );
+static void __inline__ dc390_RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB );
+static void __inline__ dc390_InvalidCmd( PACB pACB );
+static void __inline__ dc390_EnableMsgOut_Abort (PACB, PSRB);
+static void dc390_remove_dev (PACB pACB, PDCB pDCB);
+void do_DC390_Interrupt( int, void *, struct pt_regs *);
+
+int    dc390_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, UCHAR index );
+void   dc390_initDCB( PACB pACB, PDCB *ppDCB, PSCSICMD cmd );
+void   dc390_updateDCB (PACB pACB, PDCB pDCB);
 
 #ifdef MODULE
-static int DC390_release(struct Scsi_Host *host);
-static int DC390_shutdown (struct Scsi_Host *host);
+ static int DC390_release(struct Scsi_Host *host);
+ static int dc390_shutdown (struct Scsi_Host *host);
 #endif
 
 
-static PSHT	pSHT_start = NULL;
-static PSH	pSH_start = NULL;
-static PSH	pSH_current = NULL;
-static PACB	pACB_start= NULL;
-static PACB	pACB_current = NULL;
-static PDCB	pPrevDCB = NULL;
-static USHORT	adapterCnt = 0;
-static USHORT	InitialTime = 0;
-static USHORT	CurrSyncOffset = 0;
-static ULONG	mech1addr;
-static UCHAR	mech2bus, mech2Agent, mech2CfgSPenR;
-
-static PVOID DC390_phase0[]={
-       DC390_DataOut_0,
-       DC390_DataIn_0,
-       DC390_Command_0,
-       DC390_Status_0,
-       DC390_Nop_0,
-       DC390_Nop_0,
-       DC390_MsgOut_0,
-       DC390_MsgIn_0,
-       DC390_Nop_1
+//static PSHT	dc390_pSHT_start = NULL;
+//static PSH	dc390_pSH_start = NULL;
+//static PSH	dc390_pSH_current = NULL;
+static PACB	dc390_pACB_start= NULL;
+static PACB	dc390_pACB_current = NULL;
+static UCHAR	dc390_adapterCnt = 0;
+static UCHAR	dc390_CurrSyncOffset = 0;
+static ULONG	dc390_lastabortedpid = 0;
+static ULONG	dc390_laststatus = 0;
+
+#ifndef CONFIG_SCSI_DC390T_NOGENSUPP
+/* Startup values, to be overriden on the commandline */
+int tmscsim[] = {7, 1 /* 8MHz */, 
+		PARITY_CHK_ | SEND_START_ | EN_DISCONNECT_
+		| SYNC_NEGO_ | TAG_QUEUEING_,
+		MORE2_DRV | GREATER_1G | RST_SCSI_BUS | ACTIVE_NEGATION
+		/* | NO_SEEK */
+# ifdef CONFIG_SCSI_MULTI_LUN
+		| LUN_CHECK
+# endif
+		, 3 /* 16 Tags per LUN */};
+
+# if defined(MODULE) && LINUX_VERSION_CODE >= LinuxVersionCode(2,1,30)
+MODULE_PARM(tmscsim, "1-5i");
+MODULE_PARM_DESC(tmscsim, "Host SCSI ID, Speed (0=10MHz), Device Flags, Adapter Flags, Max Tags (log2(tags)-1)");
+# endif
+
+#endif /* CONFIG_SCSI_DC390T_NOGENSUPP */
+
+#if defined(MODULE) && LINUX_VERSION_CODE >= LinuxVersionCode(2,1,30)
+MODULE_AUTHOR("C.L. Huang / Kurt Garloff");
+MODULE_DESCRIPTION("SCSI host adapter driver for Tekram DC390 and other AMD53C974A based PCI SCSI adapters");
+MODULE_SUPPORTED_DEVICE("sd,sr,sg,st");
+#endif
+
+static PVOID dc390_phase0[]={
+       dc390_DataOut_0,
+       dc390_DataIn_0,
+       dc390_Command_0,
+       dc390_Status_0,
+       dc390_Nop_0,
+       dc390_Nop_0,
+       dc390_MsgOut_0,
+       dc390_MsgIn_0,
+       dc390_Nop_1
        };
 
-static PVOID DC390_phase1[]={
-       DC390_DataOutPhase,
-       DC390_DataInPhase,
-       DC390_CommandPhase,
-       DC390_StatusPhase,
-       DC390_Nop_0,
-       DC390_Nop_0,
-       DC390_MsgOutPhase,
-       DC390_MsgInPhase,
-       DC390_Nop_1,
+static PVOID dc390_phase1[]={
+       dc390_DataOutPhase,
+       dc390_DataInPhase,
+       dc390_CommandPhase,
+       dc390_StatusPhase,
+       dc390_Nop_0,
+       dc390_Nop_0,
+       dc390_MsgOutPhase,
+       dc390_MsgInPhase,
+       dc390_Nop_1
        };
 
-UCHAR  eepromBuf[MAX_ADAPTER_NUM][128];
-
-
-UCHAR  clock_period1[] = {4, 5, 6, 7, 8, 10, 13, 20};
+#ifdef DC390_DEBUG1
+static char* dc390_p0_str[] = {
+       "dc390_DataOut_0",
+       "dc390_DataIn_0",
+       "dc390_Command_0",
+       "dc390_Status_0",
+       "dc390_Nop_0",
+       "dc390_Nop_0",
+       "dc390_MsgOut_0",
+       "dc390_MsgIn_0",
+       "dc390_Nop_1"
+       };
+     
+static char* dc390_p1_str[] = {
+       "dc390_DataOutPhase",
+       "dc390_DataInPhase",
+       "dc390_CommandPhase",
+       "dc390_StatusPhase",
+       "dc390_Nop_0",
+       "dc390_Nop_0",
+       "dc390_MsgOutPhase",
+       "dc390_MsgInPhase",
+       "dc390_Nop_1"
+       };
+#endif   
 
-UCHAR  baddevname1[2][28] ={
+/* Devices erroneously pretending to be able to do TagQ */
+UCHAR  dc390_baddevname1[2][28] ={
        "SEAGATE ST3390N         9546",
        "HP      C3323-300       4269"};
-
 #define BADDEVCNT	2
 
+static char*  dc390_adapname = "DC390";
+UCHAR  dc390_eepromBuf[MAX_ADAPTER_NUM][EE_LEN];
+UCHAR  dc390_clock_period1[] = {4, 5, 6, 7, 8, 10, 13, 20};
+
+struct proc_dir_entry	DC390_proc_scsi_tmscsim ={
+       PROC_SCSI_DC390T, 7 ,"tmscsim",
+       S_IFDIR | S_IRUGO | S_IXUGO, 2
+       };
+
 
 /***********************************************************************
+ * Functions for access to DC390 EEPROM
+ * and some to emulate it
  *
- *
+ **********************************************************************/
+
+
+static void __init dc390_EnDisableCE( UCHAR mode, PDEVDECL, PUCHAR regval )
+{
+    UCHAR bval;
+
+    bval = 0;
+    if(mode == ENABLE_CE)
+	*regval = 0xc0;
+    else
+	*regval = 0x80;
+    PCI_WRITE_CONFIG_BYTE(PDEV, *regval, bval);
+    if(mode == DISABLE_CE)
+        PCI_WRITE_CONFIG_BYTE(PDEV, *regval, bval);
+    udelay(160);
+}
+
+#ifndef CONFIG_SCSI_DC390T_NOGENSUPP
+static void __init dc390_EEpromDefaults (UCHAR index)
+{
+    PUCHAR ptr;
+    UCHAR  id;
+    ptr = (PUCHAR) dc390_eepromBuf[index];
+    
+    /* Adapter Settings */
+    ptr[EE_ADAPT_SCSI_ID] = (UCHAR)tmscsim[0]; /* Adapter ID */
+    ptr[EE_MODE2] = (UCHAR)tmscsim[3];
+    ptr[EE_DELAY] = 0; /* ?? */
+    ptr[EE_TAG_CMD_NUM] = (UCHAR)tmscsim[4]; /* Tagged Comds */
+    
+    /* Device Settings */
+    for (id = 0; id < MAX_SCSI_ID; id++)
+    {
+      ptr[id<<2] = (UCHAR)tmscsim[2]; /* EE_MODE1 */
+      ptr[(id<<2) + 1] = (UCHAR)tmscsim[1]; /* EE_Speed */
+    };
+    dc390_adapname = "AM53C974";
+}
+
+static void __init dc390_checkparams (void)
+{
+	PARSEDEBUG(printk(KERN_INFO "DC390: setup %08x %08x %08x %08x %08x\n", tmscsim[0],\
+		      tmscsim[1], tmscsim[2], tmscsim[3], tmscsim[4]);)
+	if (tmscsim[0] < 0 || tmscsim[0] > 7) /* modules-2.0.0 passes -1 as string */
+	{
+		tmscsim[0] = 7; tmscsim[1] = 4; 
+		tmscsim[2] = 9; tmscsim[3] = 15;
+		tmscsim[4] = 2;
+		printk (KERN_INFO "DC390: Using safe settings.\n");
+	}
+	else
+	{
+		/* if (tmscsim[0] < 0 || tmscsim[0] > 7) tmscsim[0] = 7; */
+		if (tmscsim[1] < 0 || tmscsim[1] > 7) tmscsim[1] = 4;
+		if (tmscsim[4] < 0 || tmscsim[4] > 5) tmscsim[4] = 4;
+	};
+};
+/* Override defaults on cmdline:
+ * tmscsim: AdaptID, MaxSpeed (Index), DevMode (Bitmapped), AdaptMode (Bitmapped)
+ */
+void __init dc390_setup (char *str, int *ints)
+{
+	int i;
+	for (i = 0; i < ints[0]; i++)
+		tmscsim[i] = ints[i+1];
+	if (ints[0] > 5)
+		printk (KERN_NOTICE "DC390: ignore extra params!\n");
+	/* dc390_checkparams (); */
+};
+#endif /* CONFIG_SCSI_DC390T_NOGENSUPP */
+
+
+static void __init dc390_EEpromOutDI( PDEVDECL, PUCHAR regval, UCHAR Carry )
+{
+    UCHAR bval;
+
+    bval = 0;
+    if(Carry)
+    {
+	bval = 0x40;
+	*regval = 0x80;
+	PCI_WRITE_CONFIG_BYTE(PDEV, *regval, bval);
+    }
+    udelay(160);
+    bval |= 0x80;
+    PCI_WRITE_CONFIG_BYTE(PDEV, *regval, bval);
+    udelay(160);
+    bval = 0;
+    PCI_WRITE_CONFIG_BYTE(PDEV, *regval, bval);
+    udelay(160);
+}
+
+
+static UCHAR __init dc390_EEpromInDO( PDEVDECL )
+{
+    UCHAR bval;
+
+    PCI_WRITE_CONFIG_BYTE(PDEV, 0x80, 0x80);
+    udelay(160);
+    PCI_WRITE_CONFIG_BYTE(PDEV, 0x80, 0x40);
+    udelay(160);
+    PCI_READ_CONFIG_BYTE(PDEV, 0x00, &bval);
+    if(bval == 0x22)
+	return(1);
+    else
+	return(0);
+}
+
+
+static USHORT __init dc390_EEpromGetData1( PDEVDECL )
+{
+    UCHAR i;
+    UCHAR carryFlag;
+    USHORT wval;
+
+    wval = 0;
+    for(i=0; i<16; i++)
+    {
+	wval <<= 1;
+	carryFlag = dc390_EEpromInDO(PDEV);
+	wval |= carryFlag;
+    }
+    return(wval);
+}
+
+
+static void __init dc390_Prepare( PDEVDECL, PUCHAR regval, UCHAR EEpromCmd )
+{
+    UCHAR i,j;
+    UCHAR carryFlag;
+
+    carryFlag = 1;
+    j = 0x80;
+    for(i=0; i<9; i++)
+    {
+	dc390_EEpromOutDI(PDEV,regval,carryFlag);
+	carryFlag = (EEpromCmd & j) ? 1 : 0;
+	j >>= 1;
+    }
+}
+
+
+static void __init dc390_ReadEEprom( PDEVDECL, PUSHORT ptr)
+{
+    UCHAR   regval,cmd;
+    UCHAR   i;
+
+    cmd = EEPROM_READ;
+    for(i=0; i<0x40; i++)
+    {
+	dc390_EnDisableCE(ENABLE_CE, PDEV, &regval);
+	dc390_Prepare(PDEV, &regval, cmd++);
+	*ptr++ = dc390_EEpromGetData1(PDEV);
+	dc390_EnDisableCE(DISABLE_CE, PDEV, &regval);
+    }
+}
+
+
+static UCHAR __init dc390_CheckEEpromCheckSum( PDEVDECL, UCHAR index )
+{
+    UCHAR  i;
+    char  EEbuf[128];
+    USHORT wval, *ptr = (PUSHORT)EEbuf;
+
+    dc390_ReadEEprom( PDEV, ptr );
+    memcpy (dc390_eepromBuf[index], EEbuf, EE_ADAPT_SCSI_ID);
+    memcpy (&dc390_eepromBuf[index][EE_ADAPT_SCSI_ID], 
+	    &EEbuf[REAL_EE_ADAPT_SCSI_ID], EE_LEN - EE_ADAPT_SCSI_ID);
+    wval = 0;
+    for(i=0; i<0x40; i++, ptr++)
+	wval += *ptr;
+    return (wval == 0x1234 ? 0 : 1);
+}
+
+
+/***********************************************************************
+ * Functions for the management of the internal structures 
+ * (DCBs, SRBs, Queueing)
  *
  **********************************************************************/
-static void
-QLinkcmd( PSCSICMD cmd, PDCB pDCB )
+static PDCB __inline__ dc390_findDCB ( PACB pACB, Scsi_Cmnd *cmd)
 {
-    ULONG  flags;
-    PSCSICMD  pcmd;
+   PDCB pDCB = pACB->pLinkDCB; if (!pDCB) return 0;
+   while (pDCB->UnitSCSIID != cmd->target || pDCB->UnitSCSILUN != cmd->lun)
+     {
+	pDCB = pDCB->pNextDCB;
+	if (pDCB == pACB->pLinkDCB)
+	  {
+	     printk (KERN_WARNING "DC390: DCB not found (DCB=%08x, DCBmap[%2x]=%2x)\n",
+		     (int)pDCB, cmd->target, pACB->DCBmap[cmd->target]);
+	     return 0;
+	  }
+     };
+   DCBDEBUG1( printk (KERN_DEBUG "DCB %08x (%02x,%02x) found.\n",	\
+	   (int)pDCB, pDCB->UnitSCSIID, pDCB->UnitSCSILUN);)
+   return pDCB;
+};
 
-    save_flags(flags);
-    cli();
+static void dc390_QLinkcmd( PSCSICMD cmd, PDCB pDCB )
+{
+    PSCSICMD  pcmd;
 
     if( !pDCB->QIORBCnt )
     {
@@ -194,83 +711,61 @@
 	cmd->next = NULL;
     }
 
-    restore_flags(flags);
 }
 
 
-static PSCSICMD
-Getcmd( PDCB pDCB )
+static __inline__ PSCSICMD dc390_Getcmd( PDCB pDCB )
 {
-    ULONG  flags;
     PSCSICMD  pcmd;
 
-    save_flags(flags);
-    cli();
-
     pcmd = pDCB->pQIORBhead;
     pDCB->pQIORBhead = pcmd->next;
     pcmd->next = NULL;
     pDCB->QIORBCnt--;
 
-    restore_flags(flags);
     return( pcmd );
 }
 
 
-static PSRB
-GetSRB( PACB pACB )
+static __inline__ PSRB dc390_GetSRB( PACB pACB )
 {
-    ULONG  flags;
     PSRB   pSRB;
 
-    save_flags(flags);
-    cli();
-
     pSRB = pACB->pFreeSRB;
     if( pSRB )
     {
 	pACB->pFreeSRB = pSRB->pNextSRB;
 	pSRB->pNextSRB = NULL;
     }
-    restore_flags(flags);
+
     return( pSRB );
 }
 
 
-static void
-RewaitSRB0( PDCB pDCB, PSRB pSRB )
+static __inline__ void dc390_RewaitSRB0( PDCB pDCB, PSRB pSRB )
 {
     PSRB   psrb1;
-    ULONG  flags;
-
-    save_flags(flags);
-    cli();
 
     if( (psrb1 = pDCB->pWaitingSRB) )
     {
 	pSRB->pNextSRB = psrb1;
-	pDCB->pWaitingSRB = pSRB;
     }
     else
     {
 	pSRB->pNextSRB = NULL;
-	pDCB->pWaitingSRB = pSRB;
 	pDCB->pWaitLast = pSRB;
     }
-    restore_flags(flags);
+    pDCB->pWaitingSRB = pSRB;
 }
 
 
-static void
-RewaitSRB( PDCB pDCB, PSRB pSRB )
+static void dc390_RewaitSRB( PDCB pDCB, PSRB pSRB )
 {
     PSRB   psrb1;
-    ULONG  flags;
     UCHAR  bval;
 
-    save_flags(flags);
-    cli();
-    pDCB->GoingSRBCnt--;
+    pDCB->GoingSRBCnt--; pDCB->pDCBACB->SelLost++;
+    DEBUG0(printk(KERN_INFO "DC390: RewaitSRB (%p, %p) pid = %li\n", pDCB, pSRB, pSRB->pcmd->pid);)
     psrb1 = pDCB->pGoingSRB;
     if( pSRB == psrb1 )
     {
@@ -298,20 +793,14 @@
 
     bval = pSRB->TagNumber;
     pDCB->TagMask &= (~(1 << bval));	  /* Free TAG number */
-    restore_flags(flags);
 }
 
 
-static void
-DoWaitingSRB( PACB pACB )
+static void dc390_DoWaitingSRB( PACB pACB )
 {
-    ULONG  flags;
     PDCB   ptr, ptr1;
     PSRB   pSRB;
 
-    save_flags(flags);
-    cli();
-
     if( !(pACB->pActiveDCB) && !(pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV) ) )
     {
 	ptr = pACB->pDCBRunRobin;
@@ -333,7 +822,7 @@
 	    }
 	    else
 	    {
-		if( !DC390_StartSCSI(pACB, ptr1, pSRB) )
+		if( !dc390_StartSCSI(pACB, ptr1, pSRB) )
 		{
 		    ptr1->GoingSRBCnt++;
 		    if( ptr1->pWaitLast == pSRB )
@@ -357,55 +846,54 @@
 	    }
 	}
     }
-    restore_flags(flags);
     return;
 }
 
 
-static void
-SRBwaiting( PDCB pDCB, PSRB pSRB)
+static __inline__ void dc390_SRBwaiting( PDCB pDCB, PSRB pSRB)
 {
     if( pDCB->pWaitingSRB )
     {
 	pDCB->pWaitLast->pNextSRB = pSRB;
-	pDCB->pWaitLast = pSRB;
 	pSRB->pNextSRB = NULL;
     }
     else
     {
 	pDCB->pWaitingSRB = pSRB;
-	pDCB->pWaitLast = pSRB;
     }
+    pDCB->pWaitLast = pSRB;
 }
 
 
-static void
-SendSRB( PSCSICMD pcmd, PACB pACB, PSRB pSRB )
+/***********************************************************************
+ * Function: static void dc390_SendSRB (PACB pACB, PSRB pSRB)
+ *
+ * Purpose: Send SCSI Request Block (pSRB) to adapter (pACB)
+ *
+ ***********************************************************************/
+
+static void dc390_SendSRB( PACB pACB, PSRB pSRB )
 {
-    ULONG  flags;
     PDCB   pDCB;
 
-    save_flags(flags);
-    cli();
-
     pDCB = pSRB->pSRBDCB;
     if( !(pDCB->MaxCommand > pDCB->GoingSRBCnt) || (pACB->pActiveDCB) ||
 	(pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV)) )
     {
-	SRBwaiting(pDCB, pSRB);
+	dc390_SRBwaiting(pDCB, pSRB);
 	goto SND_EXIT;
     }
 
     if( pDCB->pWaitingSRB )
     {
-	SRBwaiting(pDCB, pSRB);
-/*	pSRB = GetWaitingSRB(pDCB); */
+	dc390_SRBwaiting(pDCB, pSRB);
+/*	pSRB = GetWaitingSRB(pDCB); */	/* non-existent */
 	pSRB = pDCB->pWaitingSRB;
 	pDCB->pWaitingSRB = pSRB->pNextSRB;
 	pSRB->pNextSRB = NULL;
     }
 
-    if( !DC390_StartSCSI(pACB, pDCB, pSRB) )
+    if( !dc390_StartSCSI(pACB, pDCB, pSRB) )
     {
 	pDCB->GoingSRBCnt++;
 	if( pDCB->pGoingSRB )
@@ -420,13 +908,60 @@
 	}
     }
     else
-	RewaitSRB0( pDCB, pSRB );
+	dc390_RewaitSRB0( pDCB, pSRB );
 
 SND_EXIT:
-    restore_flags(flags);
     return;
 }
 
+/***********************************************************************
+ * Function: static void dc390_BuildSRB (Scsi_Cmd *pcmd, PDCB pDCB, 
+ * 					 PSRB pSRB)
+ *
+ * Purpose: Prepare SRB for being sent to Device DCB w/ command *pcmd
+ *
+ ***********************************************************************/
+
+static void dc390_BuildSRB (Scsi_Cmnd* pcmd, PDCB pDCB, PSRB pSRB)
+{
+    pSRB->pSRBDCB = pDCB;
+    pSRB->pcmd = pcmd;
+    pSRB->ScsiCmdLen = pcmd->cmd_len;
+    memcpy (pSRB->CmdBlock, pcmd->cmnd, pcmd->cmd_len);
+    
+    if( pcmd->use_sg )
+    {
+	pSRB->SGcount = (UCHAR) pcmd->use_sg;
+	pSRB->pSegmentList = (PSGL) pcmd->request_buffer;
+    }
+    else if( pcmd->request_buffer )
+    {
+	pSRB->SGcount = 1;
+	pSRB->pSegmentList = (PSGL) &pSRB->Segmentx;
+	pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer;
+	pSRB->Segmentx.length = pcmd->request_bufflen;
+    }
+    else
+	pSRB->SGcount = 0;
+
+    pSRB->SGIndex = 0;
+    pSRB->AdaptStatus = 0;
+    pSRB->TargetStatus = 0;
+    pSRB->MsgCnt = 0;
+    if( pDCB->DevType != TYPE_TAPE )
+	pSRB->RetryCnt = 1;
+    else
+	pSRB->RetryCnt = 0;
+    pSRB->SRBStatus = 0;
+    pSRB->SRBFlag = 0;
+    pSRB->SRBState = 0;
+    pSRB->TotalXferredLen = 0;
+    pSRB->SGBusAddr = 0;
+    pSRB->SGToBeXferLen = 0;
+    pSRB->ScsiPhase = 0;
+    pSRB->EndMessage = 0;
+};
+
 
 /***********************************************************************
  * Function : static int DC390_queue_command (Scsi_Cmnd *cmd,
@@ -434,276 +969,307 @@
  *
  * Purpose : enqueues a SCSI command
  *
- * Inputs : cmd - SCSI command, done - function called on completion, with
- *	    a pointer to the command descriptor.
+ * Inputs : cmd - SCSI command, done - callback function called on 
+ *	    completion, with a pointer to the command descriptor.
  *
- * Returns : 0
+ * Returns : (depending on kernel version)
+ * 2.0.x: always return 0
+ * 2.1.x: old model: (use_new_eh_code == 0): like 2.0.x
+ *	  TO BE DONE:
+ *	  new model: return 0 if successful
+ *	  	     return 1 if command cannot be queued (queue full)
+ *		     command will be inserted in midlevel queue then ...
  *
  ***********************************************************************/
 
-int
-DC390_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
+int DC390_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
 {
-    USHORT ioport, i;
     Scsi_Cmnd *pcmd;
-    struct Scsi_Host *psh;
-    PACB   pACB;
     PDCB   pDCB;
     PSRB   pSRB;
-    ULONG  flags;
-    PUCHAR ptr,ptr1;
+    DC390_AFLAGS
+    PACB   pACB = (PACB) cmd->host->hostdata;
 
-    psh = cmd->host;
-    pACB = (PACB ) psh->hostdata;
-    ioport = pACB->IOPortBase;
 
-#ifdef DC390_DEBUG0
-/*  if(pACB->scan_devices) */
-	printk("Cmd=%2x,ID=%d,LUN=%d,",cmd->cmnd[0],cmd->target,cmd->lun);
-#endif
+    DEBUG0(/*  if(pACB->scan_devices) */	\
+	printk(KERN_INFO "DC390: Queue Cmd=%02x,ID=%d,LUN=%d (pid=%li)\n",\
+		cmd->cmnd[0],cmd->target,cmd->lun,cmd->pid);)
+
+    DC390_LOCK_ACB;
+    
+    /* Assume BAD_TARGET; will be cleared later */
+    cmd->result = DID_BAD_TARGET << 16;
+   
+    /* TODO: Change the policy: Alway accept TEST_UNIT_READY or INQUIRY 
+     * commands and alloc a DCB for the device if not yet there. DCB will
+     * be removed in dc390_SRBdone if SEL_TIMEOUT */
 
     if( (pACB->scan_devices == END_SCAN) && (cmd->cmnd[0] != INQUIRY) )
-    {
 	pACB->scan_devices = 0;
-	pPrevDCB->pNextDCB = pACB->pLinkDCB;
-    }
-    else if( (pACB->scan_devices) && (cmd->cmnd[0] == 8) )
-    {
+
+    else if( (pACB->scan_devices) && (cmd->cmnd[0] == READ_6) )
 	pACB->scan_devices = 0;
-	pPrevDCB->pNextDCB = pACB->pLinkDCB;
-    }
 
-    if ( ( cmd->target > pACB->max_id ) || (cmd->lun > pACB->max_lun) )
+    if ( ( cmd->target >= pACB->pScsiHost->max_id ) || 
+	 (cmd->lun >= pACB->pScsiHost->max_lun) )
     {
 /*	printk("DC390: Ignore target %d lun %d\n",
 		cmd->target, cmd->lun); */
-	cmd->result = (DID_BAD_TARGET << 16);
+	DC390_UNLOCK_ACB;
 	done(cmd);
 	return( 0 );
     }
 
-    if( (pACB->scan_devices) && !(pACB->DCBmap[cmd->target] & (1 << cmd->lun)) )
+    if( (pACB->scan_devices || cmd->cmnd[0] == TEST_UNIT_READY) && !(pACB->DCBmap[cmd->target] & (1 << cmd->lun)) )
     {
-	if( pACB->DeviceCnt < MAX_DEVICES )
-	{
-	    pACB->DCBmap[cmd->target] |= (1 << cmd->lun);
-	    pDCB = pACB->pDCB_free;
-#ifdef DC390_DEBUG0
-	    printk("pDCB=%8x,ID=%2x,", (UINT) pDCB, cmd->target);
-#endif
-	    DC390_initDCB( pACB, pDCB, cmd );
-	}
-	else	/* ???? */
-	{
-/*	    printk("DC390: Ignore target %d lun %d\n",
-		    cmd->target, cmd->lun); */
-	    cmd->result = (DID_BAD_TARGET << 16);
+	pACB->DCBmap[cmd->target] |= (1 << cmd->lun);
+        pACB->scan_devices = 1;
+
+	dc390_initDCB( pACB, &pDCB, cmd );
+	if (!pDCB)
+	  {
+	    printk (KERN_ERR "DC390: kmalloc for DCB failed, ID=%2x\n", cmd->target);
+	    DC390_UNLOCK_ACB;
 	    done(cmd);
 	    return(0);
-	}
+	  };
+            
     }
     else if( !(pACB->scan_devices) && !(pACB->DCBmap[cmd->target] & (1 << cmd->lun)) )
     {
-/*	printk("DC390: Ignore target %d lun %d\n",
-		cmd->target, cmd->lun); */
-	cmd->result = (DID_BAD_TARGET << 16);
+	printk(KERN_INFO "DC390: Ignore target %02x lun %02x\n",
+		cmd->target, cmd->lun); 
+	DC390_UNLOCK_ACB;
 	done(cmd);
 	return(0);
     }
     else
     {
-	pDCB = pACB->pLinkDCB;
-	while( (pDCB->UnitSCSIID != cmd->target) ||
-	       (pDCB->UnitSCSILUN != cmd->lun) )
-	{
-	    pDCB = pDCB->pNextDCB;
-	}
-#ifdef DC390_DEBUG0
-	    printk("pDCB=%8x,ID=%2x,", (UINT) pDCB, cmd->target);
-#endif
+	pDCB = dc390_findDCB (pACB, cmd);
+	if (!pDCB)
+	 {  /* should never happen */
+	    DC390_UNLOCK_ACB;
+	    done(cmd);
+	    return(0);
+	 };
     }
 
+    pACB->Cmds++;
     cmd->scsi_done = done;
     cmd->result = 0;
 
-    save_flags(flags);
-    cli();
-
-    if( pDCB->QIORBCnt )
+    if( pDCB->QIORBCnt ) /* Unsent commands ? */
     {
-	QLinkcmd( cmd, pDCB );
-	pcmd = Getcmd( pDCB );
+	dc390_QLinkcmd( cmd, pDCB );
+	pcmd = dc390_Getcmd( pDCB ); /* Get first command */
+        pACB->CmdInQ++;
     }
     else
 	pcmd = cmd;
 
-    pSRB = GetSRB( pACB );
+    pSRB = dc390_GetSRB( pACB );
 
     if( !pSRB )
     {
-	QLinkcmd( pcmd, pDCB );
-	restore_flags(flags);
+        dc390_QLinkcmd( pcmd, pDCB ); /* Queue command at the end */
+	pACB->CmdOutOfSRB++;
+	DC390_UNLOCK_ACB;
 	return(0);
     }
 
-/*  BuildSRB(pSRB); */
+    dc390_BuildSRB (pcmd, pDCB, pSRB);
+    dc390_SendSRB( pACB, pSRB );
 
-    pSRB->pSRBDCB = pDCB;
-    pSRB->pcmd = pcmd;
-    ptr = (PUCHAR) pSRB->CmdBlock;
-    ptr1 = (PUCHAR) pcmd->cmnd;
-    pSRB->ScsiCmdLen = pcmd->cmd_len;
-    for(i=0; i< pcmd->cmd_len; i++)
+    DC390_UNLOCK_ACB;
+    DEBUG1(printk (KERN_DEBUG " ... command (%02x) queued successfully.\n", pcmd->cmnd[0]);)
+    return(0);
+}
+
+
+static void dc390_DoNextCmd( PACB pACB, PDCB pDCB )
+{
+    Scsi_Cmnd *pcmd;
+    PSRB   pSRB;
+
+    if( pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV) )
+	return;
+
+    pcmd = dc390_Getcmd( pDCB );
+    pSRB = dc390_GetSRB( pACB );
+    if( !pSRB )
+	dc390_QLinkcmd( pcmd, pDCB );
+    else
+      {
+	dc390_BuildSRB (pcmd, pDCB, pSRB);
+	dc390_SendSRB( pACB, pSRB );
+      };
+}
+
+/* We ignore mapping problems, as we expect everybody to respect 
+ * valid partition tables. Waiting for complaints ;-) */
+
+#ifdef CONFIG_SCSI_DC390T_TRADMAP
+/* 
+ * The next function, partsize(), is copied from scsicam.c.
+ *
+ * This is ugly code duplication, but I didn't find another way to solve it:
+ * We want to respect the partition table and if it fails, we apply the 
+ * DC390 BIOS heuristic. Too bad, just calling scsicam_bios_param() doesn't do
+ * the job, because we don't know, whether the values returned are from
+ * the part. table or determined by setsize(). Unfortunately the setsize() 
+ * values differ from the ones chosen by the DC390 BIOS.
+ *
+ * Looking forward to seeing suggestions for a better solution! KG, 98/10/14
+ */
+#include <asm/unaligned.h>
+
+/*
+ * Function : static int partsize(struct buffer_head *bh, unsigned long 
+ *     capacity,unsigned int *cyls, unsigned int *hds, unsigned int *secs);
+ *
+ * Purpose : to determine the BIOS mapping used to create the partition
+ *	table, storing the results in *cyls, *hds, and *secs 
+ *
+ * Returns : -1 on failure, 0 on success.
+ *
+ */
+
+static int partsize(struct buffer_head *bh, unsigned long capacity,
+    unsigned int  *cyls, unsigned int *hds, unsigned int *secs) {
+    struct partition *p, *largest = NULL;
+    int i, largest_cyl;
+    int cyl, ext_cyl, end_head, end_cyl, end_sector;
+    unsigned int logical_end, physical_end, ext_physical_end;
+    
+
+    if (*(unsigned short *) (bh->b_data+510) == 0xAA55) {
+	for (largest_cyl = -1, p = (struct partition *) 
+    	    (0x1BE + bh->b_data), i = 0; i < 4; ++i, ++p) {
+    	    if (!p->sys_ind)
+    	    	continue;
+    	    cyl = p->cyl + ((p->sector & 0xc0) << 2);
+    	    if (cyl > largest_cyl) {
+    	    	largest_cyl = cyl;
+    	    	largest = p;
+    	    }
+    	}
+    }
+
+    if (largest) {
+    	end_cyl = largest->end_cyl + ((largest->end_sector & 0xc0) << 2);
+    	end_head = largest->end_head;
+    	end_sector = largest->end_sector & 0x3f;
+
+    	physical_end =  end_cyl * (end_head + 1) * end_sector +
+    	    end_head * end_sector + end_sector;
+
+	/* This is the actual _sector_ number at the end */
+	logical_end = get_unaligned(&largest->start_sect)
+			+ get_unaligned(&largest->nr_sects);
+
+	/* This is for >1023 cylinders */
+        ext_cyl= (logical_end-(end_head * end_sector + end_sector))
+                                        /(end_head + 1) / end_sector;
+	ext_physical_end = ext_cyl * (end_head + 1) * end_sector +
+            end_head * end_sector + end_sector;
+
+    	if ((logical_end == physical_end) ||
+	    (end_cyl==1023 && ext_physical_end==logical_end)) {
+    	    *secs = end_sector;
+    	    *hds = end_head + 1;
+    	    *cyls = capacity / ((end_head + 1) * end_sector);
+    	    return 0;
+    	}
+    }
+    return -1;
+}
+
+/***********************************************************************
+ * Function:
+ *   DC390_bios_param
+ *
+ * Description:
+ *   Return the disk geometry for the given SCSI device.
+ *   Respect the partition table, otherwise try own heuristic
+ *
+ * Note:
+ *   In contrary to other externally callable funcs (DC390_), we don't lock
+ ***********************************************************************/
+int DC390_bios_param (Disk *disk, kdev_t devno, int geom[])
+{
+    int heads, sectors, cylinders;
+    PACB pACB = (PACB) disk->device->host->hostdata;
+    struct buffer_head *bh;
+    int ret_code = -1;
+    int size = disk->capacity;
+
+    if ((bh = bread(MKDEV(MAJOR(devno), MINOR(devno)&~0xf), 0, 1024)))
     {
-	*ptr = *ptr1;
-	ptr++;
-	ptr1++;
+	/* try to infer mapping from partition table */
+	ret_code = partsize (bh, (unsigned long) size, (unsigned int *) geom + 2,
+			     (unsigned int *) geom + 0, (unsigned int *) geom + 1);
+	brelse (bh);
     }
-    if( pcmd->use_sg )
+    if (ret_code == -1)
     {
-	pSRB->SGcount = (UCHAR) pcmd->use_sg;
-	pSRB->pSegmentList = (PSGL) pcmd->request_buffer;
-    }
-    else if( pcmd->request_buffer )
-    {
-	pSRB->SGcount = 1;
-	pSRB->pSegmentList = (PSGL) &pSRB->Segmentx;
-	pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer;
-	pSRB->Segmentx.length = pcmd->request_bufflen;
-    }
-    else
-	pSRB->SGcount = 0;
-
-    pSRB->SGIndex = 0;
-    pSRB->AdaptStatus = 0;
-    pSRB->TargetStatus = 0;
-    pSRB->MsgCnt = 0;
-    if( pDCB->DevType != TYPE_TAPE )
-	pSRB->RetryCnt = 1;
-    else
-	pSRB->RetryCnt = 0;
-    pSRB->SRBStatus = 0;
-    pSRB->SRBFlag = 0;
-    pSRB->SRBState = 0;
-    pSRB->TotalXferredLen = 0;
-    pSRB->SGPhysAddr = 0;
-    pSRB->SGToBeXferLen = 0;
-    pSRB->ScsiPhase = 0;
-    pSRB->EndMessage = 0;
-    SendSRB( pcmd, pACB, pSRB );
-
-    restore_flags(flags);
-    return(0);
-}
+	heads = 64;
+	sectors = 32;
+	cylinders = size / (heads * sectors);
 
+	if ( (pACB->Gmode2 & GREATER_1G) && (cylinders > 1024) )
+	{
+		heads = 255;
+		sectors = 63;
+		cylinders = size / (heads * sectors);
+	}
 
-static void
-DoNextCmd( PACB pACB, PDCB pDCB )
-{
-    Scsi_Cmnd *pcmd;
-    PSRB   pSRB;
-    ULONG  flags;
-    PUCHAR ptr,ptr1;
-    USHORT i;
-
-
-    if( pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV) )
-	return;
-    save_flags(flags);
-    cli();
-
-    pcmd = Getcmd( pDCB );
-    pSRB = GetSRB( pACB );
-    if( !pSRB )
-    {
-	QLinkcmd( pcmd, pDCB );
-	restore_flags(flags);
-	return;
-    }
-
-    pSRB->pSRBDCB = pDCB;
-    pSRB->pcmd = pcmd;
-    ptr = (PUCHAR) pSRB->CmdBlock;
-    ptr1 = (PUCHAR) pcmd->cmnd;
-    pSRB->ScsiCmdLen = pcmd->cmd_len;
-    for(i=0; i< pcmd->cmd_len; i++)
-    {
-	*ptr = *ptr1;
-	ptr++;
-	ptr1++;
-    }
-    if( pcmd->use_sg )
-    {
-	pSRB->SGcount = (UCHAR) pcmd->use_sg;
-	pSRB->pSegmentList = (PSGL) pcmd->request_buffer;
-    }
-    else if( pcmd->request_buffer )
-    {
-	pSRB->SGcount = 1;
-	pSRB->pSegmentList = (PSGL) &pSRB->Segmentx;
-	pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer;
-	pSRB->Segmentx.length = pcmd->request_bufflen;
+	geom[0] = heads;
+	geom[1] = sectors;
+	geom[2] = cylinders;
     }
-    else
-	pSRB->SGcount = 0;
 
-    pSRB->SGIndex = 0;
-    pSRB->AdaptStatus = 0;
-    pSRB->TargetStatus = 0;
-    pSRB->MsgCnt = 0;
-    if( pDCB->DevType != TYPE_TAPE )
-	pSRB->RetryCnt = 1;
-    else
-	pSRB->RetryCnt = 0;
-    pSRB->SRBStatus = 0;
-    pSRB->SRBFlag = 0;
-    pSRB->SRBState = 0;
-    pSRB->TotalXferredLen = 0;
-    pSRB->SGPhysAddr = 0;
-    pSRB->SGToBeXferLen = 0;
-    pSRB->ScsiPhase = 0;
-    pSRB->EndMessage = 0;
-    SendSRB( pcmd, pACB, pSRB );
-
-    restore_flags(flags);
-    return;
+    return (0);
 }
-
-
-/***********************************************************************
- * Function:
- *   DC390_bios_param
- *
- * Description:
- *   Return the disk geometry for the given SCSI device.
- ***********************************************************************/
-#ifdef	VERSION_ELF_1_2_13
-int DC390_bios_param(Disk *disk, int devno, int geom[])
 #else
-int DC390_bios_param(Disk *disk, kdev_t devno, int geom[])
-#endif
+int DC390_bios_param (Disk *disk, kdev_t devno, int geom[])
 {
-    int heads, sectors, cylinders;
-    PACB pACB;
-
-    pACB = (PACB) disk->device->host->hostdata;
-    heads = 64;
-    sectors = 32;
-    cylinders = disk->capacity / (heads * sectors);
-
-    if ( (pACB->Gmode2 & GREATER_1G) && (cylinders > 1024) )
-    {
-      heads = 255;
-      sectors = 63;
-      cylinders = disk->capacity / (heads * sectors);
-    }
+    return scsicam_bios_param (disk, devno, geom);
+};
+#endif
 
-    geom[0] = heads;
-    geom[1] = sectors;
-    geom[2] = cylinders;
 
-    return (0);
-}
+void dc390_dumpinfo (PACB pACB, PDCB pDCB, PSRB pSRB)
+{
+    USHORT pstat; PDEVDECL1;
+
+    if (pSRB) 
+    {
+	printk ("DC390: SRB: Xferred %08lx, Remain %08lx, State %08lx, Phase %02x\n",
+		pSRB->TotalXferredLen, pSRB->SGToBeXferLen, pSRB->SRBState,
+		pSRB->ScsiPhase);
+	printk ("DC390: AdpaterStatus: %02x, SRB Status %02x\n", pSRB->AdaptStatus, pSRB->SRBStatus);
+    };
+    printk ("DC390: Status of last IRQ (DMA/SC/Int/IRQ): %08lx\n", dc390_laststatus);
+    printk ("DC390: Register dump: SCSI block:\n");
+    printk ("DC390: XferCnt  Cmd Stat IntS IRQS FFIS Ctl1 Ctl2 Ctl3 Ctl4\n");
+    printk ("DC390:  %06x   %02x   %02x   %02x",
+	    DC390_read8(CtcReg_Low) + (DC390_read8(CtcReg_Mid) << 8) + (DC390_read8(CtcReg_High) << 16),
+	    DC390_read8(ScsiCmd), DC390_read8(Scsi_Status), DC390_read8(Intern_State));
+    printk ("   %02x   %02x   %02x   %02x   %02x   %02x\n",
+	    DC390_read8(INT_Status), DC390_read8(Current_Fifo), DC390_read8(CtrlReg1),
+	    DC390_read8(CtrlReg2), DC390_read8(CtrlReg3), DC390_read8(CtrlReg4));
+    DC390_write32 (DMA_ScsiBusCtrl, WRT_ERASE_DMA_STAT | EN_INT_ON_PCI_ABORT);
+    printk ("DC390: Register dump: DMA engine:\n");
+    printk ("DC390: Cmd   STrCnt    SBusA    WrkBC    WrkAC Stat SBusCtrl\n");
+    printk ("DC390:  %02x %08x %08x %08x %08x   %02x %08x\n",
+	    DC390_read8(DMA_Cmd), DC390_read32(DMA_XferCnt), DC390_read32(DMA_XferAddr),
+	    DC390_read32(DMA_Wk_ByteCntr), DC390_read32(DMA_Wk_AddrCntr),
+	    DC390_read8(DMA_Status), DC390_read32(DMA_ScsiBusCtrl));
+    DC390_write32 (DMA_ScsiBusCtrl, EN_INT_ON_PCI_ABORT);
+    PDEVSET1; PCI_READ_CONFIG_WORD(PDEV, PCI_STATUS, &pstat);
+    printk ("DC390: Register dump: PCI Status: %04x\n", pstat);
+    printk ("DC390: In case of driver trouble read linux/drivers/scsi/README.tmscsim\n");
+};
 
 
 /***********************************************************************
@@ -714,37 +1280,32 @@
  * Inputs : cmd - command to abort
  *
  * Returns : 0 on success, -1 on failure.
+ *
+ * Status: Buggy !
  ***********************************************************************/
 
-int
-DC390_abort (Scsi_Cmnd *cmd)
+int DC390_abort (Scsi_Cmnd *cmd)
 {
-    ULONG flags;
-    PACB  pACB;
-    PDCB  pDCB, pdcb;
+    PDCB  pDCB;
     PSRB  pSRB, psrb;
-    USHORT count, i;
+    ULONG count, i;
     PSCSICMD  pcmd, pcmd1;
     int   status;
+    ULONG sbac;
+    DC390_AFLAGS
+    PACB  pACB = (PACB) cmd->host->hostdata;
+
+    DC390_LOCK_ACB;
+
+    pDCB = dc390_findDCB (pACB, cmd);
+    /* abort() is too buggy at the moment. If it's called we are in trouble anyway.
+     * so let's dump some info into the syslog at least. (KG, 98/08/20) */
+    if (pDCB) pSRB = pDCB->pActiveSRB; else pSRB = 0;
+    printk ("DC390: Abort command (pid %li, DCB %p, SRB %p)\n",
+	    cmd->pid, pDCB, pSRB);
+    dc390_dumpinfo (pACB, pDCB, pSRB);
 
-
-#ifdef DC390_DEBUG0
-    printk("DC390 : Abort Cmd.");
-#endif
-
-    save_flags(flags);
-    cli();
-
-    pACB = (PACB) cmd->host->hostdata;
-    pDCB = pACB->pLinkDCB;
-    pdcb = pDCB;
-    while( (pDCB->UnitSCSIID != cmd->target) ||
-	   (pDCB->UnitSCSILUN != cmd->lun) )
-    {
-	pDCB = pDCB->pNextDCB;
-	if( pDCB == pdcb )
-	    goto  NOT_RUN;
-    }
+    if( !pDCB ) goto  NOT_RUN;
 
     if( pDCB->QIORBCnt )
     {
@@ -774,7 +1335,12 @@
 	    }
 	}
     }
-
+	
+    /* Added 98/07/02 KG */
+    pSRB = pDCB->pActiveSRB;
+    if (pSRB && pSRB->pcmd == cmd )
+	goto ON_GOING;
+	
     pSRB = pDCB->pWaitingSRB;
     if( !pSRB )
 	goto  ON_GOING;
@@ -791,7 +1357,7 @@
 	while( psrb->pNextSRB->pcmd != cmd )
 	{
 	    psrb = psrb->pNextSRB;
-	    if( !(psrb->pNextSRB) )
+	    if( !(psrb->pNextSRB) || psrb == pSRB)
 		goto ON_GOING;
 	}
 	pSRB = psrb->pNextSRB;
@@ -808,6 +1374,8 @@
 
 ON_GOING:
     pSRB = pDCB->pGoingSRB;
+    pDCB->DCBFlag |= ABORT_DEV_;
+    /* Now for the hard part: The command is currently processed */
     for( count = pDCB->GoingSRBCnt, i=0; i<count; i++)
     {
 	if( pSRB->pcmd != cmd )
@@ -817,6 +1385,8 @@
 	    if( (pACB->pActiveDCB == pDCB) && (pDCB->pActiveSRB == pSRB) )
 	    {
 		status = SCSI_ABORT_BUSY;
+		printk ("DC390: Abort current command (pid %li, SRB %p)\n",
+			cmd->pid, pSRB);
 		goto  ABO_X;
 	    }
 	    else
@@ -832,20 +1402,51 @@
 
 ABO_X:
     cmd->result = DID_ABORT << 16;
+    printk(KERN_INFO "DC390: Aborted pid %li with status %i\n", cmd->pid, status);
+    if (cmd->pid == dc390_lastabortedpid) /* repeated failure ? */
+	{
+		/* Let's do something to help the bus getting clean again */
+		DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
+		DC390_write8 (ScsiCmd, DMA_COMMAND);
+			//DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
+		//DC390_write8 (ScsiCmd, RESET_ATN_CMD);
+		DC390_write8 (ScsiCmd, NOP_CMD);
+		//udelay (10000);
+		//DC390_read8 (INT_Status);
+		//DC390_write8 (ScsiCmd, EN_SEL_RESEL);
+	};
+    sbac = DC390_read32 (DMA_ScsiBusCtrl);
+    if (sbac & SCSI_BUSY)
+    {	/* clear BSY, SEL and ATN */
+	printk (KERN_WARNING "DC390: Reset SCSI device: ");
+	//DC390_write32 (DMA_ScsiBusCtrl, (sbac | SCAM) & ~SCSI_LINES);
+	//udelay (250);
+	//sbac = DC390_read32 (DMA_ScsiBusCtrl);
+	//printk ("%08lx ", sbac);
+	//DC390_write32 (DMA_ScsiBusCtrl, sbac & ~(SCSI_LINES | SCAM));
+	//udelay (100);
+	//sbac = DC390_read32 (DMA_ScsiBusCtrl);
+	//printk ("%08lx ", sbac);
+	DC390_write8 (ScsiCmd, RST_DEVICE_CMD);
+	udelay (250);
+	DC390_write8 (ScsiCmd, NOP_CMD);
+	sbac = DC390_read32 (DMA_ScsiBusCtrl);
+	printk ("%08lx\n", sbac);
+    };
+    dc390_lastabortedpid = cmd->pid;
+    DC390_UNLOCK_ACB;
+    //do_DC390_Interrupt (pACB->IRQLevel, 0, 0);
     cmd->scsi_done(cmd);
-    restore_flags(flags);
     return( status );
 }
 
 
-static void
-ResetDevParam( PACB pACB )
+static void dc390_ResetDevParam( PACB pACB )
 {
     PDCB   pDCB, pdcb;
 
     pDCB = pACB->pLinkDCB;
-    if( pDCB == NULL )
-	return;
+    if (! pDCB) return;
     pdcb = pDCB;
     do
     {
@@ -853,24 +1454,22 @@
 	pDCB->SyncPeriod = 0;
 	pDCB->SyncOffset = 0;
 	pDCB->CtrlR3 = FAST_CLK;
-	pDCB->CtrlR4 &= NEGATE_REQACKDATA;
-	pDCB->CtrlR4 |= EATER_25NS;
+	pDCB->CtrlR4 &= NEGATE_REQACKDATA | CTRL4_RESERVED | NEGATE_REQACK;
+	pDCB->CtrlR4 |= pACB->glitch_cfg;
 	pDCB = pDCB->pNextDCB;
     }
     while( pdcb != pDCB );
 }
 
 
-static void
-RecoverSRB( PACB pACB )
+static void dc390_RecoverSRB( PACB pACB )
 {
     PDCB   pDCB, pdcb;
     PSRB   psrb, psrb2;
-    USHORT cnt, i;
+    ULONG  cnt, i;
 
     pDCB = pACB->pLinkDCB;
-    if( pDCB == NULL )
-	return;
+    if( !pDCB ) return;
     pdcb = pDCB;
     do
     {
@@ -880,7 +1479,7 @@
 	{
 	    psrb2 = psrb;
 	    psrb = psrb->pNextSRB;
-/*	    RewaitSRB( pDCB, psrb ); */
+/*	    dc390_RewaitSRB( pDCB, psrb ); */
 	    if( pdcb->pWaitingSRB )
 	    {
 		psrb2->pNextSRB = pdcb->pWaitingSRB;
@@ -897,8 +1496,7 @@
 	pdcb->pGoingSRB = NULL;
 	pdcb->TagMask = 0;
 	pdcb = pdcb->pNextDCB;
-    }
-    while( pdcb != pDCB );
+    } while( pdcb != pDCB );
 }
 
 
@@ -912,83 +1510,82 @@
  * Returns : 0 on success.
  ***********************************************************************/
 
-#ifdef	VERSION_2_0_0
 int DC390_reset(Scsi_Cmnd *cmd, unsigned int resetFlags)
-#else
-int DC390_reset (Scsi_Cmnd *cmd)
-#endif
 {
-    USHORT   ioport;
-    unsigned long flags;
-    PACB  pACB;
-    UCHAR    bval;
-    USHORT  i;
+    UCHAR   bval;
+    ULONG   i;
+    DC390_AFLAGS
+    PACB    pACB = (PACB) cmd->host->hostdata;
 
+    printk(KERN_INFO "DC390: RESET ... ");
 
-#ifdef DC390_DEBUG1
-    printk("DC390: RESET,");
-#endif
-
-    pACB = (PACB ) cmd->host->hostdata;
-    ioport = pACB->IOPortBase;
-    save_flags(flags);
-    cli();
-    bval = inb(ioport+CtrlReg1);
+    DC390_LOCK_ACB;
+    bval = DC390_read8 (CtrlReg1);
     bval |= DIS_INT_ON_SCSI_RST;
-    outb(bval,ioport+CtrlReg1);  /* disable interrupt */
-    DC390_ResetSCSIBus( pACB );
-    for( i=0; i<500; i++ )
+    DC390_write8 (CtrlReg1, bval);	/* disable interrupt */
+
+    dc390_ResetSCSIBus( pACB );
+    /* Unlock ? */
+    for( i=0; i<600; i++ )
 	udelay(1000);
-    bval = inb(ioport+CtrlReg1);
-    bval &= ~DIS_INT_ON_SCSI_RST;
-    outb(bval,ioport+CtrlReg1); /* re-enable interrupt */
 
-    bval = DMA_IDLE_CMD;
-    outb(bval,ioport+DMA_Cmd);
-    bval = CLEAR_FIFO_CMD;
-    outb(bval,ioport+ScsiCmd);
+    DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
+    DC390_read8 (INT_Status);		/* Reset Pending INT */
 
-    ResetDevParam( pACB );
-    DoingSRB_Done( pACB );
+    dc390_ResetDevParam( pACB );
+    dc390_DoingSRB_Done( pACB );
+    /* dc390_RecoverSRB (pACB); */
     pACB->pActiveDCB = NULL;
 
     pACB->ACBFlag = 0;
-    DoWaitingSRB( pACB );
+    bval = DC390_read8 (CtrlReg1);
+    bval &= ~DIS_INT_ON_SCSI_RST;
+    DC390_write8 (CtrlReg1, bval);	/* re-enable interrupt */
 
-    restore_flags(flags);
-#ifdef DC390_DEBUG1
-    printk("DC390: RESET1,");
-#endif
+    dc390_DoWaitingSRB( pACB );
+
+    DC390_UNLOCK_ACB;
+    printk("done\n");
     return( SCSI_RESET_SUCCESS );
 }
 
-
 #include "scsiiom.c"
 
 
 /***********************************************************************
- * Function : static void DC390_initDCB
+ * Function : static void dc390_initDCB()
  *
  * Purpose :  initialize the internal structures for a given DCB
  *
  * Inputs : cmd - pointer to this scsi cmd request block structure
- *
  ***********************************************************************/
-void DC390_initDCB( PACB pACB, PDCB pDCB, PSCSICMD cmd )
+
+void dc390_initDCB( PACB pACB, PDCB *ppDCB, PSCSICMD cmd )
 {
     PEEprom	prom;
-    UCHAR	bval;
-    USHORT	index;
+    UCHAR	index;
+    PDCB pDCB;
 
-    if( pACB->DeviceCnt == 0 )
+    pDCB = kmalloc (sizeof(DC390_DCB), GFP_ATOMIC);
+    DCBDEBUG(printk (KERN_INFO "DC390: alloc mem for DCB (ID %i, LUN %i): 0x%08x\n",	\
+	    cmd->target, cmd->lun, (int)pDCB);)
+ 
+    *ppDCB = pDCB;
+    if (!pDCB) return;
+    if( pACB->DCBCnt == 0 )
     {
 	pACB->pLinkDCB = pDCB;
 	pACB->pDCBRunRobin = pDCB;
-	pDCB->pNextDCB = pDCB;
-	pPrevDCB = pDCB;
     }
     else
-	pPrevDCB->pNextDCB = pDCB;
+    {
+	pACB->pLastDCB->pNextDCB = pDCB;
+    };
+   
+    pACB->DCBCnt++;
+   
+    pACB->pLastDCB = pDCB;
+    pDCB->pNextDCB = pACB->pLinkDCB;
 
     pDCB->pDCBACB = pACB;
     pDCB->QIORBCnt = 0;
@@ -1000,128 +1597,144 @@
     pDCB->pActiveSRB = NULL;
     pDCB->TagMask = 0;
     pDCB->MaxCommand = 1;
-    pDCB->AdaptIndex = pACB->AdapterIndex;
     index = pACB->AdapterIndex;
     pDCB->DCBFlag = 0;
 
-    prom = (PEEprom) &eepromBuf[index][cmd->target << 2];
+    prom = (PEEprom) &dc390_eepromBuf[index][cmd->target << 2];
     pDCB->DevMode = prom->EE_MODE1;
-    pDCB->AdpMode = eepromBuf[index][EE_MODE2];
-
-    if( pDCB->DevMode & EN_DISCONNECT_ )
-	bval = 0xC0;
-    else
-	bval = 0x80;
-    bval |= cmd->lun;
-    pDCB->IdentifyMsg = bval;
 
     pDCB->SyncMode = 0;
-    if( pDCB->DevMode & SYNC_NEGO_ )
-    {
-	if( !(cmd->lun) || CurrSyncOffset )
-	    pDCB->SyncMode = SYNC_ENABLE;
-    }
+    dc390_updateDCB(pACB, pDCB);
 
     pDCB->SyncPeriod = 0;
     pDCB->SyncOffset = 0;
-    pDCB->NegoPeriod = (clock_period1[prom->EE_SPEED] * 25) >> 2;
-
-    pDCB->CtrlR1 = pACB->AdaptSCSIID;
-    if( pDCB->DevMode & PARITY_CHK_ )
-	pDCB->CtrlR1 |= PARITY_ERR_REPO;
+    pDCB->NegoPeriod = (dc390_clock_period1[prom->EE_SPEED] * 25) >> 2;
 
     pDCB->CtrlR3 = FAST_CLK;
 
-    pDCB->CtrlR4 = EATER_25NS;
-    if( pDCB->AdpMode & ACTIVE_NEGATION)
-	pDCB->CtrlR4 |= NEGATE_REQACKDATA;
+    pDCB->CtrlR4 = pACB->glitch_cfg | CTRL4_RESERVED;
+    if( dc390_eepromBuf[index][EE_MODE2] & ACTIVE_NEGATION)
+	pDCB->CtrlR4 |= NEGATE_REQACKDATA | NEGATE_REQACK;
 }
 
+/***********************************************************************
+ * Function : static void dc390_updateDCB()
+ *
+ * Purpose :  Set the configuration dependent DCB parameters
+ ***********************************************************************/
+
+void dc390_updateDCB (PACB pACB, PDCB pDCB)
+{
+  pDCB->IdentifyMsg = IDENTIFY (pDCB->DevMode & EN_DISCONNECT_, pDCB->UnitSCSILUN);
+  
+  if (pDCB->DevMode & TAG_QUEUEING_) pDCB->SyncMode &= EN_TAG_QUEUEING | SYNC_NEGO_DONE | EN_ATN_STOP;
+  else pDCB->SyncMode &= SYNC_NEGO_DONE | EN_ATN_STOP;
+  
+  if( pDCB->DevMode & SYNC_NEGO_ && (!(pDCB->UnitSCSILUN) || dc390_CurrSyncOffset) )
+     pDCB->SyncMode |= SYNC_ENABLE;
+  else
+     {
+	pDCB->SyncMode &= ~(SYNC_NEGO_DONE | SYNC_ENABLE);
+	pDCB->SyncOffset &= ~0x0f;
+     };
+   
+  if (! (pDCB->DevMode & EN_DISCONNECT_)) pDCB->SyncMode &= ~EN_ATN_STOP; 
+  
+  pDCB->CtrlR1 = pACB->pScsiHost->this_id;
+  if( pDCB->DevMode & PARITY_CHK_ )
+    pDCB->CtrlR1 |= PARITY_ERR_REPO;
+};  
+
+
+/***********************************************************************
+ * Function : static void dc390_updateDCBs ()
+ *
+ * Purpose :  Set the configuration dependent DCB params for all DCBs
+ ***********************************************************************/
+
+static void dc390_updateDCBs (PACB pACB)
+{
+  int i;
+  PDCB pDCB = pACB->pLinkDCB;
+  for (i = 0; i < pACB->DeviceCnt; i++)
+    {
+      dc390_updateDCB (pACB, pDCB);
+      pDCB = pDCB->pNextDCB;
+    };
+};
+  
 
 /***********************************************************************
- * Function : static void DC390_initSRB
+ * Function : static void dc390_initSRB()
  *
  * Purpose :  initialize the internal structures for a given SRB
  *
  * Inputs : psrb - pointer to this scsi request block structure
- *
  ***********************************************************************/
-void DC390_initSRB( PSRB psrb )
+
+static void __inline__ dc390_initSRB( PSRB psrb )
 {
-#ifndef VERSION_ELF_1_2_13
-#ifdef DC390_DEBUG0
-   printk("DC390 init: %08lx %08lx,",(ULONG)psrb,(ULONG)virt_to_bus(psrb));
-#endif
-	psrb->PhysSRB = virt_to_bus( psrb );
-#else
-	psrb->PhysSRB = (ULONG) psrb;
-#endif
+  /* psrb->PhysSRB = virt_to_phys( psrb ); */
 }
 
 
-void DC390_linkSRB( PACB pACB )
+void dc390_linkSRB( PACB pACB )
 {
-    USHORT  count, i;
-    PSRB    psrb;
+    ULONG   count, i;
 
     count = pACB->SRBCount;
-
     for( i=0; i< count; i++)
     {
 	if( i != count - 1)
 	    pACB->SRB_array[i].pNextSRB = &pACB->SRB_array[i+1];
 	else
 	    pACB->SRB_array[i].pNextSRB = NULL;
-	psrb = (PSRB) &pACB->SRB_array[i];
-	DC390_initSRB( psrb );
+	dc390_initSRB( &pACB->SRB_array[i] );
     }
 }
 
 
 /***********************************************************************
- * Function : static void DC390_initACB
+ * Function : static void dc390_initACB ()
  *
  * Purpose :  initialize the internal structures for a given SCSI host
  *
  * Inputs : psh - pointer to this host adapter's structure
- *
+ *	    io_port, Irq, index: Resources and adapter index
  ***********************************************************************/
-void DC390_initACB( PSH psh, ULONG io_port, UCHAR Irq, USHORT index )
+
+void __init dc390_initACB (PSH psh, ULONG io_port, UCHAR Irq, UCHAR index)
 {
     PACB    pACB;
-    USHORT  i;
+    UCHAR   i;
+    DC390_AFLAGS
 
     psh->can_queue = MAX_CMD_QUEUE;
     psh->cmd_per_lun = MAX_CMD_PER_LUN;
-    psh->this_id = (int) eepromBuf[index][EE_ADAPT_SCSI_ID];
+    psh->this_id = (int) dc390_eepromBuf[index][EE_ADAPT_SCSI_ID];
     psh->io_port = io_port;
     psh->n_io_port = 0x80;
     psh->irq = Irq;
 
     pACB = (PACB) psh->hostdata;
+    DC390_LOCKA_INIT;
+    DC390_LOCK_ACB;
+
+    pACB->pScsiHost = psh;
+    pACB->IOPortBase = (USHORT) io_port;
+    pACB->IRQLevel = Irq;
 
-#ifndef VERSION_ELF_1_2_13
+    DEBUG0(printk (KERN_INFO "DC390: Adapter index %i, ID %i, IO 0x%08x, IRQ 0x%02x\n",	\
+	    index, psh->this_id, (int)io_port, Irq);)
+   
     psh->max_id = 8;
-#ifdef	CONFIG_SCSI_MULTI_LUN
-    if( eepromBuf[index][EE_MODE2] & LUN_CHECK )
-	psh->max_lun = 8;
-    else
-#endif
-	psh->max_lun = 1;
-#endif
 
-    pACB->max_id = 7;
-    if( pACB->max_id == eepromBuf[index][EE_ADAPT_SCSI_ID] )
-	pACB->max_id--;
-#ifdef	CONFIG_SCSI_MULTI_LUN
-    if( eepromBuf[index][EE_MODE2] & LUN_CHECK )
-	pACB->max_lun = 7;
-    else
-#endif
-	pACB->max_lun = 0;
+    if( psh->max_id - 1 == dc390_eepromBuf[index][EE_ADAPT_SCSI_ID] )
+	psh->max_id--;
+    psh->max_lun = 1;
+    if( dc390_eepromBuf[index][EE_MODE2] & LUN_CHECK )
+	psh->max_lun = 8;
 
-    pACB->pScsiHost = psh;
-    pACB->IOPortBase = (USHORT) io_port;
     pACB->pLinkDCB = NULL;
     pACB->pDCBRunRobin = NULL;
     pACB->pActiveDCB = NULL;
@@ -1129,475 +1742,204 @@
     pACB->SRBCount = MAX_SRB_CNT;
     pACB->AdapterIndex = index;
     pACB->status = 0;
-    pACB->AdaptSCSIID = eepromBuf[index][EE_ADAPT_SCSI_ID];
-    pACB->HostID_Bit = (1 << pACB->AdaptSCSIID);
-    pACB->AdaptSCSILUN = 0;
+    psh->this_id = dc390_eepromBuf[index][EE_ADAPT_SCSI_ID];
     pACB->DeviceCnt = 0;
-    pACB->IRQLevel = Irq;
-    pACB->TagMaxNum = eepromBuf[index][EE_TAG_CMD_NUM] << 2;
+    pACB->DCBCnt = 0;
+    pACB->TagMaxNum = 2 << dc390_eepromBuf[index][EE_TAG_CMD_NUM];
     pACB->ACBFlag = 0;
     pACB->scan_devices = 1;
-    pACB->Gmode2 = eepromBuf[index][EE_MODE2];
-    if( eepromBuf[index][EE_MODE2] & LUN_CHECK )
-	pACB->LUNchk = 1;
-    pACB->pDCB_free = &pACB->DCB_array[0];
-    DC390_linkSRB( pACB );
+    pACB->MsgLen = 0;
+    pACB->Ignore_IRQ = 0;
+    pACB->Gmode2 = dc390_eepromBuf[index][EE_MODE2];
+    dc390_linkSRB( pACB );
     pACB->pTmpSRB = &pACB->TmpSRB;
-    DC390_initSRB( pACB->pTmpSRB );
+    dc390_initSRB( pACB->pTmpSRB );
     for(i=0; i<MAX_SCSI_ID; i++)
 	pACB->DCBmap[i] = 0;
+    pACB->sel_timeout = SEL_TIMEOUT;
+    pACB->glitch_cfg = EATER_25NS;
+    pACB->Cmds = pACB->CmdInQ = pACB->CmdOutOfSRB = pACB->SelLost = 0;
 }
 
 
 /***********************************************************************
- * Function : static int DC390_initAdapter
+ * Function : static int dc390_initAdapter ()
  *
  * Purpose :  initialize the SCSI chip ctrl registers
  *
  * Inputs : psh - pointer to this host adapter's structure
+ *	    io_port, Irq, index: Resources
  *
+ * Outputs: 0 on success, -1 on error
  ***********************************************************************/
-int DC390_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index )
-{
-    USHORT ioport;
-    UCHAR  bval;
-    PACB   pACB, pacb;
-    USHORT used_irq = 0;
 
-    pacb = pACB_start;
-    if( pacb != NULL )
-    {
-	for ( ; (pacb != (PACB) -1) ; )
-	{
-	    if( pacb->IRQLevel == Irq )
-	    {
-		used_irq = 1;
-		break;
-	    }
-	    else
-		pacb = pacb->pNextACB;
-	}
-    }
+int __init dc390_initAdapter (PSH psh, ULONG io_port, UCHAR Irq, UCHAR index)
+{
+    PACB   pACB, pACB2;
+    UCHAR  used_irq = 0, dstate;
+    int    i;
+    
+    pACB = (PACB) psh->hostdata;
+    
+    for ( pACB2 = dc390_pACB_start; pACB2 ; )
+      {
+	if( pACB2->IRQLevel == Irq )
+	  {
+	    used_irq = 1;
+	    break;
+	  }
+	else
+	  pACB2 = pACB2->pNextACB;
+      }
 
-    if( !used_irq )
-    {
-#ifdef	VERSION_ELF_1_2_13
-	if( request_irq(Irq, DC390_Interrupt, SA_INTERRUPT, "tmscsim"))
-#else
-	if( request_irq(Irq, DC390_Interrupt, SA_INTERRUPT | SA_SHIRQ, "tmscsim", NULL))
-#endif
+    if (check_region (io_port, psh->n_io_port))
 	{
-	    printk("DC390: register IRQ error!\n");
+	    printk(KERN_ERR "DC390: register IO ports error!\n");
 	    return( -1 );
 	}
-    }
-
-    request_region(io_port,psh->n_io_port,"tmscsim");
-
-    ioport = (USHORT) io_port;
-
-    pACB = (PACB) psh->hostdata;
-    bval = SEL_TIMEOUT; 		/* 250ms selection timeout */
-    outb(bval,ioport+Scsi_TimeOut);
-
-    bval = CLK_FREQ_40MHZ;		/* Conversion factor = 0 , 40MHz clock */
-    outb(bval,ioport+Clk_Factor);
-
-    bval = NOP_CMD;			/* NOP cmd - clear command register */
-    outb(bval,ioport+ScsiCmd);
-
-    bval = EN_FEATURE+EN_SCSI2_CMD;	/* Enable Feature and SCSI-2 */
-    outb(bval,ioport+CtrlReg2);
-
-    bval = FAST_CLK;			/* fast clock */
-    outb(bval,ioport+CtrlReg3);
-
-    bval = EATER_25NS;
-    if( eepromBuf[index][EE_MODE2] & ACTIVE_NEGATION )
-	 bval |= NEGATE_REQACKDATA;
-    outb(bval,ioport+CtrlReg4);
-
-    bval = DIS_INT_ON_SCSI_RST; 	/* Disable SCSI bus reset interrupt */
-    outb(bval,ioport+CtrlReg1);
-
-    return(0);
-}
-
-
-void
-DC390_EnableCfg( USHORT mechnum, UCHAR regval )
-{
-    ULONG wlval;
-
-    if(mechnum == 2)
-    {
-	outb(mech2bus, PCI_CFG2_FORWARD_REG);
-	outb(mech2CfgSPenR, PCI_CFG2_ENABLE_REG);
-    }
-    else
-    {
-	regval &= 0xFC;
-	wlval = mech1addr;
-	wlval |= (((ULONG)regval) & 0xff);
-	outl(wlval, PCI_CFG1_ADDRESS_REG);
-    }
-}
-
-
-void
-DC390_DisableCfg( USHORT mechnum )
-{
-
-    if(mechnum == 2)
-	outb(0, PCI_CFG2_ENABLE_REG);
-    else
-	outl(0, PCI_CFG1_ADDRESS_REG);
-}
-
-
-UCHAR
-DC390_inByte( USHORT mechnum, UCHAR regval )
-{
-    UCHAR bval;
-    ULONG wval;
-    ULONG flags;
-
-    save_flags(flags);
-    cli();
-    DC390_EnableCfg( mechnum, regval );
-    if(mechnum == 2)
-    {
-	wval = mech2Agent;
-	wval <<= 8;
-	wval |= ((USHORT) regval) & 0xff;
-	bval = inb(wval);
-    }
-    else
-    {
-	regval &= 3;
-	bval = inb(PCI_CFG1_DATA_REG | regval);
-    }
-    DC390_DisableCfg(mechnum);
-    restore_flags(flags);
-    return(bval);
-}
-
-
-USHORT
-DC390_inWord( USHORT mechnum, UCHAR regval )
-{
-    USHORT wval;
-    ULONG flags;
-
-    save_flags(flags);
-    cli();
-    DC390_EnableCfg(mechnum,regval);
-    if(mechnum == 2)
-    {
-	wval = mech2Agent;
-	wval <<= 8;
-	wval |= regval;
-	wval = inw(wval);
-    }
-    else
-    {
-	regval &= 3;
-	wval = inw(PCI_CFG1_DATA_REG | regval);
-    }
-    DC390_DisableCfg(mechnum);
-    restore_flags(flags);
-    return(wval);
-}
-
-
-ULONG
-DC390_inDword(USHORT mechnum, UCHAR regval )
-{
-    ULONG wlval;
-    ULONG flags;
-    USHORT wval;
-
-    save_flags(flags);
-    cli();
-    DC390_EnableCfg(mechnum,regval);
-    if(mechnum == 2)
-    {
-	wval = mech2Agent;
-	wval <<= 8;
-	wval |= regval;
-	wlval = inl(wval);
-    }
-    else
-    {
-	wlval = inl(PCI_CFG1_DATA_REG);
-    }
-    DC390_DisableCfg(mechnum);
-    restore_flags(flags);
-    return(wlval);
-}
-
-
-void
-DC390_OutB(USHORT mechnum, UCHAR regval, UCHAR bval )
-{
-
-    USHORT wval;
-    ULONG  flags;
-
-    save_flags(flags);
-    cli();
-    DC390_EnableCfg(mechnum,regval);
-    if(mechnum == 2)
-    {
-	wval = mech2Agent;
-	wval <<= 8;
-	wval |= regval;
-	outb(bval, wval);
-    }
-    else
-    {
-	regval &= 3;
-	outb(bval, PCI_CFG1_DATA_REG | regval);
-    }
-    DC390_DisableCfg(mechnum);
-    restore_flags(flags);
-}
-
-
-void
-DC390_EnDisableCE( UCHAR mode, USHORT mechnum, PUCHAR regval )
-{
-
-    UCHAR bval;
-
-    bval = 0;
-    if(mode == ENABLE_CE)
-	*regval = 0xc0;
-    else
-	*regval = 0x80;
-    DC390_OutB(mechnum,*regval,bval);
-    if(mode == DISABLE_CE)
-	DC390_OutB(mechnum,*regval,bval);
-    udelay(160);
-}
-
-
-void
-DC390_EEpromOutDI( USHORT mechnum, PUCHAR regval, USHORT Carry )
-{
-    UCHAR bval;
-
-    bval = 0;
-    if(Carry)
-    {
-	bval = 0x40;
-	*regval = 0x80;
-	DC390_OutB(mechnum,*regval,bval);
-    }
-    udelay(160);
-    bval |= 0x80;
-    DC390_OutB(mechnum,*regval,bval);
-    udelay(160);
-    bval = 0;
-    DC390_OutB(mechnum,*regval,bval);
-    udelay(160);
-}
-
-
-UCHAR
-DC390_EEpromInDO( USHORT mechnum )
-{
-    UCHAR bval,regval;
-
-    regval = 0x80;
-    bval = 0x80;
-    DC390_OutB(mechnum,regval,bval);
-    udelay(160);
-    bval = 0x40;
-    DC390_OutB(mechnum,regval,bval);
-    udelay(160);
-    regval = 0x0;
-    bval = DC390_inByte(mechnum,regval);
-    if(bval == 0x22)
-	return(1);
     else
-	return(0);
-}
-
-
-USHORT
-EEpromGetData1( USHORT mechnum )
-{
-    UCHAR i;
-    UCHAR carryFlag;
-    USHORT wval;
-
-    wval = 0;
-    for(i=0; i<16; i++)
-    {
-	wval <<= 1;
-	carryFlag = DC390_EEpromInDO(mechnum);
-	wval |= carryFlag;
-    }
-    return(wval);
-}
-
-
-void
-DC390_Prepare( USHORT mechnum, PUCHAR regval, UCHAR EEpromCmd )
-{
-    UCHAR i,j;
-    USHORT carryFlag;
-
-    carryFlag = 1;
-    j = 0x80;
-    for(i=0; i<9; i++)
-    {
-	DC390_EEpromOutDI(mechnum,regval,carryFlag);
-	carryFlag = (EEpromCmd & j) ? 1 : 0;
-	j >>= 1;
-    }
-}
-
+	request_region (io_port, psh->n_io_port, "tmscsim");
 
-void
-DC390_ReadEEprom( USHORT mechnum, USHORT index )
-{
-    UCHAR   regval,cmd;
-    PUSHORT ptr;
-    USHORT  i;
+    DC390_read8_ (INT_Status, io_port);		/* Reset Pending INT */
 
-    ptr = (PUSHORT) &eepromBuf[index][0];
-    cmd = EEPROM_READ;
-    for(i=0; i<0x40; i++)
+    if( !used_irq )
     {
-	DC390_EnDisableCE(ENABLE_CE, mechnum, &regval);
-	DC390_Prepare(mechnum, &regval, cmd);
-	*ptr = EEpromGetData1(mechnum);
-	ptr++;
-	cmd++;
-	DC390_EnDisableCE(DISABLE_CE,mechnum,&regval);
+	if( (i = request_irq(Irq, do_DC390_Interrupt, DC390_IRQ, "tmscsim", NULL) ))
+	    {
+		    printk(KERN_ERR "DC390: register IRQ error!\n");
+		    return( -1 );
+	    }
     }
-}
 
-
-USHORT
-DC390_CheckEEpromCheckSum( USHORT MechNum, USHORT index )
-{
-    USHORT wval, rc, *ptr;
-    UCHAR  i;
-
-    DC390_ReadEEprom( MechNum, index );
-    wval = 0;
-    ptr = (PUSHORT) &eepromBuf[index][0];
-    for(i=0; i<128 ;i+=2, ptr++)
-	wval += *ptr;
-    if( wval == 0x1234 )
-	rc = 0;
+    if( !dc390_pACB_start )
+      {
+	pACB2 = NULL;
+	dc390_pACB_start = pACB;
+	dc390_pACB_current = pACB;
+	pACB->pNextACB = NULL;
+      }
     else
-	rc = -1;
-    return( rc );
-}
-
-
-USHORT
-DC390_ToMech( USHORT Mechnum, USHORT BusDevFunNum )
-{
-    USHORT devnum;
-
-    devnum = BusDevFunNum;
+      {
+	pACB2 = dc390_pACB_current;
+	dc390_pACB_current->pNextACB = pACB;
+	dc390_pACB_current = pACB;
+	pACB->pNextACB = NULL;
+      };
+
+    DC390_write8 (CtrlReg1, DIS_INT_ON_SCSI_RST | psh->this_id);	/* Disable SCSI bus reset interrupt */
+
+    if (pACB->Gmode2 & RST_SCSI_BUS)
+    {
+	dc390_ResetSCSIBus( pACB );
+	/* Unlock before ? */
+	for( i=0; i<600; i++ )
+		udelay(1000);
+    };
+    pACB->ACBFlag = 0;
+    DC390_read8 (INT_Status);				/* Reset Pending INT */
+    
+    DC390_write8 (Scsi_TimeOut, SEL_TIMEOUT);		/* 250ms selection timeout */
+    DC390_write8 (Clk_Factor, CLK_FREQ_40MHZ);		/* Conversion factor = 0 , 40MHz clock */
+    DC390_write8 (ScsiCmd, NOP_CMD);			/* NOP cmd - clear command register */
+    DC390_write8 (CtrlReg2, EN_FEATURE+EN_SCSI2_CMD);	/* Enable Feature and SCSI-2 */
+    DC390_write8 (CtrlReg3, FAST_CLK);			/* fast clock */
+    DC390_write8 (CtrlReg4, pACB->glitch_cfg |			/* glitch eater */
+		(dc390_eepromBuf[index][EE_MODE2] & ACTIVE_NEGATION) ? NEGATE_REQACKDATA : 0);	/* Negation */
+    DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
+    DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
+    DC390_write32 (DMA_ScsiBusCtrl, EN_INT_ON_PCI_ABORT);
+    dstate = DC390_read8 (DMA_Status);
+    DC390_write8 (DMA_Status, dstate);	/* clear */
 
-    if(Mechnum == 2)
-    {
-	if(devnum & 0x80)
-	     return(-1);
-	mech2bus = (UCHAR)((devnum & 0xff00) >> 8);	  /* Bus num */
-	mech2Agent = ((UCHAR)(devnum & 0xff)) >> 3;	  /* Dev num */
-	mech2Agent |= 0xc0;
-	mech2CfgSPenR = ((UCHAR)(devnum & 0xff)) & 0x07;  /* Fun num */
-	mech2CfgSPenR = (mech2CfgSPenR << 1) | 0x20;
-    }
-    else	/* use mech #1 method */
-    {
-	mech1addr = 0x80000000 | ((ULONG)devnum << 8);
-    }
     return(0);
 }
 
+
 /***********************************************************************
- * Function : static int DC390_init (struct Scsi_Host *host)
+ * Function : static int DC390_init (struct Scsi_Host *host, ...)
  *
  * Purpose :  initialize the internal structures for a given SCSI host
  *
- * Inputs : host - pointer to this host adapter's structure/
+ * Inputs : host - pointer to this host adapter's structure
+ *	    io_port - IO ports mapped to this adapter
+ *	    Irq - IRQ assigned to this adpater
+ *	    PDEVDECL - PCI access handle
+ *	    index - Adapter index
  *
- * Preconditions : when this function is called, the chip_type
- *	field of the pACB structure MUST have been set.
+ * Outputs: 0 on success, -1 on error
+ *
+ * Note: written in capitals, because the locking is only done here,
+ *	not in DC390_detect, called from outside 
  ***********************************************************************/
 
-static int
-DC390_init (PSHT psht, ULONG io_port, UCHAR Irq, USHORT index, USHORT MechNum)
+static int __init DC390_init (PSHT psht, ULONG io_port, UCHAR Irq, PDEVDECL, UCHAR index)
 {
     PSH   psh;
     PACB  pACB;
-
-    if( !DC390_CheckEEpromCheckSum( MechNum, index) )
+    DC390_AFLAGS
+    
+    if (dc390_CheckEEpromCheckSum (PDEV, index))
     {
-	psh = scsi_register( psht, sizeof(DC390_ACB) );
-	if( !psh )
-	    return( -1 );
-	if( !pSH_start )
-	{
-	    pSH_start = psh;
-	    pSH_current = psh;
-	}
-	else
-	{
-	    pSH_current->next = psh;
-	    pSH_current = psh;
-	}
+#ifdef CONFIG_SCSI_DC390T_NOGENSUPP
+	printk (KERN_ERR "DC390_init: No EEPROM found!\n");
+	return( -1 );
+#else
+	int period;
+	printk (KERN_INFO "DC390_init: No EEPROM found!\n");
+	printk (KERN_INFO "DC390_init: Trying default EEPROM settings:\n");
+	dc390_checkparams ();
+	period = dc390_clock_period1[tmscsim[1]];
+	printk (KERN_INFO "DC390: Used defaults: AdaptID=%i, SpeedIdx=%i (%i.%i MHz),"
+		" DevMode=0x%02x, AdaptMode=0x%02x, TaggedCmnds=%i (%i)\n", tmscsim[0], tmscsim[1],
+		40 / period, ((40%period)*10 + period/2) / period,
+		(UCHAR)tmscsim[2], (UCHAR)tmscsim[3], tmscsim[4], 2 << (tmscsim[4]));
+	dc390_EEpromDefaults (index);
+#endif
+    };
+   
+    psh = scsi_register( psht, sizeof(DC390_ACB) );
+    if( !psh ) return( -1 );
+	
+    pACB = (PACB) psh->hostdata;
+    DC390_LOCKA_INIT;
+    DC390_LOCK_ACB;
 
-#ifdef DC390_DEBUG0
-	printk("DC390: pSH = %8x,", (UINT) psh);
-	printk("DC390: Index %02i,", index);
+#if 0
+    if( !dc390_pSH_start )
+    {
+        dc390_pSH_start = psh;
+        dc390_pSH_current = psh;
+    }
+    else
+    {
+        dc390_pSH_current->next = psh;
+        dc390_pSH_current = psh;
+    }
 #endif
 
-	DC390_initACB( psh, io_port, Irq, index );
-	if( !DC390_initAdapter( psh, io_port, Irq, index ) )
-	{
-	    pACB = (PACB) psh->hostdata;
-	    if( !pACB_start )
-	    {
-		pACB_start = pACB;
-		pACB_current = pACB;
-		pACB->pNextACB = (PACB) -1;
-	    }
-	    else
-	    {
-		pACB_current->pNextACB = pACB;
-		pACB_current = pACB;
-		pACB->pNextACB = (PACB)  -1;
-	    }
+    DEBUG0(printk(KERN_INFO "DC390: pSH = %8x,", (UINT) psh);)
+    DEBUG0(printk(" Index %02i,", index);)
 
-#ifdef DC390_DEBUG0
-	printk("DC390: pACB = %8x, pDCB_array = %8x, pSRB_array = %8x\n",
-	      (UINT) pACB, (UINT) pACB->DCB_array, (UINT) pACB->SRB_array);
-	printk("DC390: ACB size= %4x, DCB size= %4x, SRB size= %4x\n",
-	      sizeof(DC390_ACB), sizeof(DC390_DCB), sizeof(DC390_SRB) );
-#endif
+    dc390_initACB( psh, io_port, Irq, index );
+    pACB = (PACB) psh->hostdata;
+        
+    PDEVSET;
 
-	}
-	else
-	{
-	    pSH_start = NULL;
-	    scsi_unregister( psh );
-	    return( -1 );
-	}
-	return( 0 );
+    if( !dc390_initAdapter( psh, io_port, Irq, index ) )
+    {
+	DEBUG0(printk("\nDC390: pACB = %8x, pDCBmap = %8x, pSRB_array = %8x\n",\
+		(UINT) pACB, (UINT) pACB->DCBmap, (UINT) pACB->SRB_array);)
+	DEBUG0(printk("DC390: ACB size= %4x, DCB size= %4x, SRB size= %4x\n",\
+		sizeof(DC390_ACB), sizeof(DC390_DCB), sizeof(DC390_SRB) );)
+
+	DC390_UNLOCK_ACB;
+        return (0);
     }
     else
     {
-	printk("DC390_init: EEPROM reading error!\n");
+	//dc390_pSH_start = NULL;
+	scsi_unregister( psh );
+	DC390_UNLOCK_ACB;
 	return( -1 );
     }
 }
@@ -1616,136 +1958,443 @@
  *
  ***********************************************************************/
 
-int
-DC390_detect(Scsi_Host_Template *psht)
-{
-#ifdef FOR_PCI_OK
-    UCHAR   pci_bus, pci_device_fn;
-    int     error = 0;
-    USHORT  chipType = 0;
-    USHORT  i;
-#endif
+#ifndef NEW_PCI
+/* Acc. to PCI 2.1 spec it's up to the driver to enable Bus mastering:
+ * We use pci_set_master () for 2.1.x and this func for 2.0.x:	*/
+static void __init dc390_set_master (PDEVDECL)
+{
+	USHORT cmd;
+	UCHAR lat;
+	
+	PCI_READ_CONFIG_WORD (PDEV, PCI_COMMAND, &cmd);
+	
+        if (! (cmd & PCI_COMMAND_MASTER)) {	
+		printk("PCI: Enabling bus mastering for device %02x:%02x\n",
+		       PCI_BUS_DEV);
+		cmd |= PCI_COMMAND_MASTER;
+		PCI_WRITE_CONFIG_WORD(PDEV, PCI_COMMAND, cmd);
+	}
+	PCI_READ_CONFIG_BYTE (PDEV, PCI_LATENCY_TIMER, &lat);
+	if (lat < 16 /* || lat == 255 */) {
+		printk("PCI: Setting latency timer of device %02x:%02x from %i to 64\n",
+		       PCI_BUS_DEV, lat);
+		PCI_WRITE_CONFIG_BYTE(PDEV, PCI_LATENCY_TIMER, 64);
+	}
+	
+};
+#endif /* ! NEW_PCI */
+
+static void __init dc390_set_pci_cfg (PDEVDECL)
+{
+	USHORT cmd;
+	PCI_READ_CONFIG_WORD (PDEV, PCI_COMMAND, &cmd);
+	cmd |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY | PCI_COMMAND_IO;
+	PCI_WRITE_CONFIG_WORD (PDEV, PCI_COMMAND, cmd);
+	PCI_WRITE_CONFIG_WORD (PDEV, PCI_STATUS, (PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY));
+};
+	
 
+int __init DC390_detect (Scsi_Host_Template *psht)
+{
+    PDEVDECL0;
     UCHAR   irq;
-    UCHAR   istatus;
-#ifndef VERSION_ELF_1_2_13
     UINT    io_port;
-#else
-    ULONG   io_port;
-#endif
-    USHORT  adaptCnt = 0;	/* Number of boards detected */
-    USHORT  pci_index = 0;	/* Device index to PCI BIOS calls */
-    USHORT  MechNum, BusDevFunNum;
-    ULONG   wlval;
-
-#ifndef VERSION_ELF_1_2_13
-    psht->proc_dir = &proc_scsi_tmscsim;
-#endif
+    DC390_IFLAGS DC390_DFLAGS
 
-    InitialTime = 1;
-    pSHT_start = psht;
-    pACB_start = NULL;
+    DC390_LOCK_DRV;
+    //dc390_pSHT_start = psht;
+    dc390_pACB_start = NULL;
 
-    MechNum = 1;
-    for( ; (MechNum < 3) && (!adaptCnt); MechNum++)
-    {
-	BusDevFunNum = 0;
-	for (; adaptCnt < MAX_ADAPTER_NUM ;)
+    if ( PCI_PRESENT )
+	while (PCI_FIND_DEVICE (PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD53C974))
 	{
-	    if( !DC390_ToMech( MechNum, BusDevFunNum) )
-	    {
-		 wlval = DC390_inDword( MechNum, PCI_VENDOR_ID);
-		 if(wlval == ( (PCI_DEVICE_ID_AMD53C974 << 16)+
-				PCI_VENDOR_ID_AMD) )
-		 {
-		    io_port =DC390_inDword(MechNum,PCI_BASE_ADDRESS_0) & 0xFFFE;
-		    irq = DC390_inByte( MechNum, PCI_INTERRUPT_LINE);
-#ifdef DC390_DEBUG0
-		printk("DC390: IO_PORT=%4x,IRQ=%x,\n",(UINT) io_port, irq);
-#endif
-		    if( !DC390_init(psht, io_port, irq, pci_index, MechNum) )
-		    {
-			adaptCnt++;
-			pci_index++;
-			istatus = inb( (USHORT)io_port+INT_Status );	/* Reset Pending INT */
-#ifdef DC390_DEBUG0
-		printk("DC390: Mech=%2x,\n",(UCHAR) MechNum);
-#endif
-		    }
-		 }
-	    }
-	    if( BusDevFunNum != 0xfff8 )
-		BusDevFunNum += 8;	    /* next device # */
-	    else
-		break;
-	}
-    }
+	    DC390_LOCK_IO;		/* Remove this when going to new eh */
+	    PCI_GET_IO_AND_IRQ;
+	    DEBUG0(printk(KERN_INFO "DC390(%i): IO_PORT=%04x,IRQ=%x\n", dc390_adapterCnt, (UINT) io_port, irq);)
 
-#ifdef FOR_PCI_OK
-    if ( pcibios_present() )
-    {
-	for (i = 0; i < MAX_ADAPTER_NUM; ++i)
-	{
-	    if( !pcibios_find_device( PCI_VENDOR_ID_AMD,
-				PCI_DEVICE_ID_AMD53C974,
-				pci_index, &pci_bus, &pci_device_fn) )
+	    if( !DC390_init(psht, io_port, irq, PDEV, dc390_adapterCnt))
 	    {
-		chipType = PCI_DEVICE_ID_AMD53C974;
-		pci_index++;
-	    }
-
-	    if( chipType )
-	    {
-
-		error = pcibios_read_config_dword(pci_bus, pci_device_fn,
-						  PCI_BASE_ADDRESS_0, &io_port);
-		error |= pcibios_read_config_byte(pci_bus, pci_device_fn,
-						  PCI_INTERRUPT_LINE, &irq);
-		if( error )
-		{
-		    printk("DC390_detect: reading configuration registers error!\n");
-		    InitialTime = 0;
-		    return( 0 );
-		}
-
-		(USHORT) io_port = (USHORT) io_port & 0xFFFE;
-#ifdef DC390_DEBUG0
-		printk("DC390: IO_PORT=%4x,IRQ=%x,\n",(UINT) io_port, irq);
-#endif
-		if( !DC390_init(psht, io_port, irq, i) )
-		    adaptCnt++;
-		chipType = 0;
-	    }
-	    else
-		break;
+		PCI_SET_MASTER;
+		dc390_set_pci_cfg (PDEV);
+		dc390_adapterCnt++;
+	    };
+	    DC390_UNLOCK_IO;		/* Remove when going to new eh */
 	}
-    }
-#endif
-
-    InitialTime = 0;
-    adapterCnt = adaptCnt;
-    return( adaptCnt );
+    else
+	printk (KERN_ERR "DC390: No PCI BIOS found!\n");
+   
+    if (dc390_adapterCnt)
+	psht->proc_dir = &DC390_proc_scsi_tmscsim;
+
+    printk(KERN_INFO "DC390: %i adapters found\n", dc390_adapterCnt);
+    DC390_UNLOCK_DRV;
+    return( dc390_adapterCnt );
 }
 
 
-#ifndef VERSION_ELF_1_2_13
+/***********************************************************************
+ * Functions: dc390_inquiry(), dc390_inquiry_done()
+ *
+ * Purpose: When changing speed etc., we have to issue an INQUIRY
+ *	    command to make sure, we agree upon the nego parameters
+ *	    with the device
+ ***********************************************************************/
+
+static void dc390_inquiry_done (Scsi_Cmnd* cmd)
+{
+   printk (KERN_INFO "DC390: INQUIRY (ID %02x LUN %02x) returned %08x\n",
+	   cmd->target, cmd->lun, cmd->result);
+   if (cmd->result)
+   {
+	PACB pACB = (PACB)cmd->host->hostdata;
+	PDCB pDCB = dc390_findDCB (pACB, cmd);
+	printk ("DC390: Unsetting DsCn, Sync and TagQ!\n");
+	if (pDCB)
+	{
+		pDCB->DevMode &= ~(SYNC_NEGO_ | TAG_QUEUEING_ | EN_DISCONNECT_ );
+		dc390_updateDCB (pACB, pDCB);
+	};
+   };
+   kfree (cmd->buffer);
+   kfree (cmd);
+};
+
+void dc390_inquiry (PACB pACB, PDCB pDCB)
+{
+   char* buffer;
+   Scsi_Cmnd* cmd;
+   buffer = kmalloc (256, GFP_ATOMIC);
+   cmd = kmalloc (sizeof (Scsi_Cmnd), GFP_ATOMIC);
+
+   memset (buffer, 0, 256);
+   memset (cmd, 0, sizeof(Scsi_Cmnd));
+   cmd->cmnd[0] = INQUIRY;
+   cmd->cmnd[1] = (pDCB->UnitSCSILUN << 5) & 0xe0;
+   cmd->cmnd[4] = 0xff;
+   
+   cmd->cmd_len = 6; cmd->old_cmd_len = 6;
+   cmd->host = pACB->pScsiHost;
+   cmd->target = pDCB->UnitSCSIID;
+   cmd->lun = pDCB->UnitSCSILUN; 
+   cmd->serial_number = 1;
+   cmd->bufflen = 128;
+   cmd->buffer = buffer;
+   cmd->request_bufflen = 128;
+   cmd->request_buffer = &buffer[128];
+   cmd->done = dc390_inquiry_done;
+   cmd->scsi_done = dc390_inquiry_done;
+   cmd->timeout_per_command = HZ;
+
+   cmd->request.rq_status = RQ_SCSI_BUSY;
+
+   printk (KERN_INFO "DC390: Queue INQUIRY command to dev ID %02x LUN %02x\n",
+	   pDCB->UnitSCSIID, pDCB->UnitSCSILUN);
+   DC390_queue_command (cmd, dc390_inquiry_done);
+};
 
 /********************************************************************
- * Function: tmscsim_set_info()
+ * Function: dc390_set_info()
  *
- * Purpose: Set adapter info (!)
- *
- * Not yet implemented
+ * Purpose: Change adapter config
  *
+ * Strings are parsed similar to the output of tmscsim_proc_info ()
+ * '-' means no change
  *******************************************************************/
 
-int tmscsim_set_info(char *buffer, int length, struct Scsi_Host *shpnt)
+static int dc390_scanf (char** p1, char** p2, int* var)
 {
-  return(-ENOSYS);  /* Currently this is a no-op */
-}
+   *p2 = *p1;
+   *var = simple_strtoul (*p2, p1, 10);
+   if (*p2 == *p1) return -1;
+   *p1 = strtok (0, " \t\n:=,;.");
+   return 0;
+};
+
+#define SCANF(p1, p2, var, min, max)		\
+if (dc390_scanf (&p1, &p2, &var)) goto einv;	\
+else if (var<min || var>max) goto einv2
+
+static int dc390_yesno (char** p, char* var, char bmask)
+{
+   switch (**p)
+     {
+      case 'Y': *var |= bmask; break;
+      case 'N': *var &= ~bmask; break;
+      case '-': break;
+      default: return -1;
+     }
+   *p = strtok (0, " \t\n:=,;");
+   return 0;
+};
+
+#define YESNO(p, var, bmask)			\
+if (dc390_yesno (&p, &var, bmask)) goto einv;	\
+else dc390_updateDCB (pACB, pDCB);		\
+if (!p) goto ok
+
+static int dc390_search (char **p1, char **p2, char *var, char* txt, int max, int scale, char* ign)
+{
+   int dum;
+   if (! memcmp (*p1, txt, strlen(txt)))
+     {
+	*p2 = strtok (0, " \t\n:=,;");
+	if (!*p2) return -1;
+	dum = simple_strtoul (*p2, p1, 10);
+	if (*p2 == *p1) return -1;
+	if (dum >= 0 && dum <= max) 
+	  { *var = (dum * 100) / scale; }
+	else return -2;
+	*p1 = strtok (0, " \t\n:=,;");
+	if (*ign && *p1 && strlen(*p1) >= strlen(ign) && 
+	    !(memcmp (*p1, ign, strlen(ign)))) 
+		*p1 = strtok (0, " \t\n:=,;");
+
+     }
+   return 0;
+};
+
+#define SEARCH(p1, p2, var, txt, max)						\
+if (dc390_search (&p1, &p2, (PUCHAR)(&var), txt, max, 100, "")) goto einv2;	\
+else if (!p1) goto ok2
+
+#define SEARCH2(p1, p2, var, txt, max, scale)					\
+if (dc390_search (&p1, &p2, &var, txt, max, scale, "")) goto einv2; 		\
+else if (!p1) goto ok2
+
+#define SEARCH3(p1, p2, var, txt, max, scale, ign)				\
+if (dc390_search (&p1, &p2, &var, txt, max, scale, ign)) goto einv2;		\
+else if (!p1) goto ok2
+
+
+#ifdef DC390_PARSEDEBUG
+static char _prstr[256];
+char* prstr (char* p, char* e)
+{
+   char* c = _prstr;
+   while (p < e)
+     if (*p == 0) { *c++ = ':'; p++; }
+     else if (*p == 10) { *c++ = '\\'; *c++ = 'n'; p++; }
+     else *c++ = *p++;
+   *c = 0;
+   return _prstr;
+};
+#endif
+
+int dc390_set_info (char *buffer, int length, PACB pACB)
+{
+  char *pos = buffer, *p0 = buffer;
+  char needs_inquiry = 0; 
+  int dum = 0;
+  char dev;
+  PDCB pDCB = pACB->pLinkDCB;
+  DC390_IFLAGS
+  DC390_AFLAGS 
+  pos[length] = 0;
+
+  DC390_LOCK_IO;
+  DC390_LOCK_ACB;
+  /* UPPERCASE */ 
+  /* Don't use kernel toupper, because of 2.0.x bug: ctmp unexported */
+  while (*pos) 
+    { if (*pos >='a' && *pos <= 'z') *pos = *pos + 'A' - 'a'; pos++; };
+  
+  /* We should protect __strtok ! */
+  /* spin_lock (strtok_lock); */
+
+  /* Remove WS */
+  pos = strtok (buffer, " \t:\n=,;");
+  if (!pos) goto ok;
+   
+ next:
+  if (!memcmp (pos, "RESET", 5)) goto reset;
+  else if (!memcmp (pos, "INQUIRY", 7)) goto inquiry;
+  else if (!memcmp (pos, "REMOVE", 6)) goto remove;
+  
+  if (isdigit (*pos))
+    {
+      /* Device config line */
+      int dev, id, lun; char* pdec;
+      char olddevmode;
+      
+      SCANF (pos, p0, dev, 0, pACB->DCBCnt-1);
+      if (pos) { SCANF (pos, p0, id, 0, 7); } else goto einv;
+      if (pos) { SCANF (pos, p0, lun, 0, 7); } else goto einv;
+      if (!pos) goto einv;
+      
+      PARSEDEBUG(printk (KERN_INFO "DC390: config line %i %i %i:\"%s\"\n", dev, id, lun, prstr (pos, &buffer[length]));)
+      pDCB = pACB->pLinkDCB;
+      for (dum = 0; dum < dev; dum++) pDCB = pDCB->pNextDCB;
+      /* Sanity Check */
+      if (pDCB->UnitSCSIID != id || pDCB->UnitSCSILUN != lun) 
+	 {
+	    printk (KERN_ERR "DC390: no such device: Idx=%02i ID=%02i LUN=%02i\n",
+		    dev, id, lun);
+	    goto einv2;
+	 };
+
+      olddevmode = pDCB->DevMode;
+      YESNO (pos, pDCB->DevMode, PARITY_CHK_);
+      needs_inquiry++;
+      YESNO (pos, pDCB->DevMode, SYNC_NEGO_);
+      if ((olddevmode & SYNC_NEGO_) == (pDCB->DevMode & SYNC_NEGO_)) needs_inquiry--;
+      needs_inquiry++;
+      YESNO (pos, pDCB->DevMode, EN_DISCONNECT_);
+      if ((olddevmode & EN_DISCONNECT_) == (pDCB->DevMode & EN_DISCONNECT_)) needs_inquiry--;
+      YESNO (pos, pDCB->DevMode, SEND_START_);
+      needs_inquiry++;
+      YESNO (pos, pDCB->DevMode, TAG_QUEUEING_);
+      if ((olddevmode & TAG_QUEUEING_) == (pDCB->DevMode & TAG_QUEUEING_)) needs_inquiry--;
+      YESNO (pos, pDCB->SyncMode, EN_ATN_STOP);
+      
+      dc390_updateDCB (pACB, pDCB);
+      if (!pos) goto ok;
+       
+      olddevmode = pDCB->NegoPeriod;
+      /* Look for decimal point (Speed) */
+      pdec = pos; 
+      while (pdec++ < &buffer[length]) if (*pdec == '.') break;
+      /* NegoPeriod */
+      if (*pos != '-')
+	{
+	  SCANF (pos, p0, dum, 72, 800); 
+	  pDCB->NegoPeriod = dum >> 2;
+	  if (pDCB->NegoPeriod != olddevmode) needs_inquiry++;
+	  if (!pos) goto ok;
+	  if (memcmp (pos, "NS", 2) == 0) pos = strtok (0, " \t\n:=,;.");
+	}
+      else pos = strtok (0, " \t\n:=,;.");
+      if (!pos) goto ok;
+      
+      /* Speed: NegoPeriod */
+      if (*pos != '-')
+	{
+	  SCANF (pos, p0, dum, 1, 13); 
+	  pDCB->NegoPeriod = (1000/dum) >> 2;
+	  if (pDCB->NegoPeriod != olddevmode && !pos) needs_inquiry++;
+	  if (!pos) goto ok;
+	  /* decimal */
+	  if (pos-1 == pdec)
+	     {
+		int dumold = dum;
+		dum = simple_strtoul (pos, &p0, 10) * 10;
+		for (; p0-pos > 1; p0--) dum /= 10;
+		pDCB->NegoPeriod = (100000/(100*dumold + dum)) >> 2;
+		if (pDCB->NegoPeriod < 19) pDCB->NegoPeriod = 19;
+		if (pDCB->NegoPeriod != olddevmode) needs_inquiry++;
+		pos = strtok (0, " \t\n:=,;");
+		if (!pos) goto ok;
+	     };
+	  if (*pos == 'M') pos = strtok (0, " \t\n:=,;");
+	}
+      else pos = strtok (0, " \t\n:=,;");
+      /* dc390_updateDCB (pACB, pDCB); */
+      if (!pos) goto ok;
+
+      olddevmode = pDCB->SyncOffset;
+      /* SyncOffs */
+      if (*pos != '-')
+	{
+	  SCANF (pos, p0, dum, 0, 0x0f); 
+	  pDCB->SyncOffset = dum;
+	  if (pDCB->SyncOffset > olddevmode) needs_inquiry++;
+	}
+      else pos = strtok (0, " \t\n:=,;");
+      dc390_updateDCB (pACB, pDCB);
+    }
+  else
+    {
+      char* p1 = pos; UCHAR dum;
+      PARSEDEBUG(printk (KERN_INFO "DC390: chg adapt cfg \"%s\"\n", prstr (pos, &buffer[length]));)
+      dum = GLITCH_TO_NS (pACB->glitch_cfg);
+      /* Adapter setting */
+      SEARCH (pos, p0, pACB->pScsiHost->max_id, "MAXID", 8); 
+      SEARCH (pos, p0, pACB->pScsiHost->max_lun, "MAXLUN", 8); 
+      SEARCH (pos, p0, pACB->pScsiHost->this_id, "ADAPTERID", 7); 
+      SEARCH (pos, p0, pACB->TagMaxNum, "TAGMAXNUM", 32);
+      SEARCH (pos, p0, pACB->ACBFlag, "ACBFLAG", 255);
+      SEARCH3 (pos, p0, dum, "GLITCHEATER", 40, 1000, "NS");
+      SEARCH3 (pos, p0, pACB->sel_timeout, "SELTIMEOUT", 400, 163, "MS");
+    ok2:
+      pACB->glitch_cfg = NS_TO_GLITCH (dum);
+      if (pACB->sel_timeout < 60) pACB->sel_timeout = 60;
+      dum = 0; while (1 << dum <= pACB->TagMaxNum) dum ++;
+      pACB->TagMaxNum &= (1 << --dum);
+      if (pos == p1) goto einv;
+      dc390_updateDCBs (pACB);
+    }
+  if (pos) goto next;
+      
+ ok:
+  /* spin_unlock (strtok_lock); */
+  DC390_UNLOCK_ACB;
+  if (needs_inquiry) 
+     { dc390_updateDCB (pACB, pDCB); dc390_inquiry (pACB, pDCB); };
+  DC390_UNLOCK_IO;
+  return (length);
+
+ einv2:
+  pos = p0;
+ einv:
+  /* spin_unlock (strtok_lock); */
+  DC390_UNLOCK_ACB;
+  DC390_UNLOCK_IO;
+  printk (KERN_WARNING "DC390: parse error near \"%s\"\n", (pos? pos: "NULL"));
+  return (-EINVAL);
+   
+ reset:
+     {
+	Scsi_Cmnd cmd; cmd.host = pACB->pScsiHost;
+	printk (KERN_WARNING "DC390: Driver reset requested!\n");
+	DC390_UNLOCK_ACB;
+	DC390_reset (&cmd, 0);
+	DC390_UNLOCK_IO;
+     };
+  return (length);
+  
+ inquiry:
+     {
+	pos = strtok (0, " \t\n.:;="); if (!pos) goto einv;
+	dev = simple_strtoul (pos, &p0, 10);
+	if (dev >= pACB->DCBCnt) goto einv_dev;
+	for (dum = 0; dum < dev; dum++) pDCB = pDCB->pNextDCB;
+	printk (KERN_NOTICE " DC390: Issue INQUIRY command to Dev(Idx) %i SCSI ID %i LUN %i\n",
+		dev, pDCB->UnitSCSIID, pDCB->UnitSCSILUN);
+	DC390_UNLOCK_ACB;
+	dc390_inquiry (pACB, pDCB);
+	DC390_UNLOCK_IO;
+     };
+   return (length);
+
+ remove:
+     {
+	pos = strtok (0, " \t\n.:;="); if (!pos) goto einv;
+	dev = simple_strtoul (pos, &p0, 10);
+	if (dev >= pACB->DCBCnt) goto einv_dev;
+	for (dum = 0; dum < dev; dum++) pDCB = pDCB->pNextDCB;
+	printk (KERN_NOTICE " DC390: Remove DCB for Dev(Idx) %i SCSI ID %i LUN %i\n",
+		dev, pDCB->UnitSCSIID, pDCB->UnitSCSILUN);
+	dc390_remove_dev (pACB, pDCB);
+	DC390_UNLOCK_ACB;
+	DC390_UNLOCK_IO;
+     };
+   return (length);
+
+ einv_dev:
+   printk (KERN_WARNING "DC390: Ignore cmnd to illegal Dev(Idx) %i. Valid range: 0 - %i.\n", 
+	   dev, pACB->DCBCnt - 1);
+   DC390_UNLOCK_ACB;
+   DC390_UNLOCK_IO;
+   return (-EINVAL);
+	     
+	     
+}
+
+#undef SEARCH
+#undef YESNO
+#undef SCANF
 
 /********************************************************************
- * Function: tmscsim_proc_info(char* buffer, char **start,
+ * Function: DC390_proc_info(char* buffer, char **start,
  *			     off_t offset, int length, int hostno, int inout)
  *
  * Purpose: return SCSI Adapter/Device Info
@@ -1763,84 +2412,89 @@
  *
  ********************************************************************/
 
-/* KG: proc_info taken from driver aha152x.c */
-
 #undef SPRINTF
 #define SPRINTF(args...) pos += sprintf(pos, ## args)
 
-#define YESNO(YN)\
-if (YN) SPRINTF(" Yes ");\
-else SPRINTF(" No  ")
+#define YESNO(YN)		\
+ if (YN) SPRINTF(" Yes ");	\
+ else SPRINTF(" No  ")
+
 
-int tmscsim_proc_info(char *buffer, char **start,
-		      off_t offset, int length, int hostno, int inout)
+int DC390_proc_info (char *buffer, char **start,
+		     off_t offset, int length, int hostno, int inout)
 {
   int dev, spd, spd1;
   char *pos = buffer;
   PSH shpnt;
-  PACB acbpnt;
-  PDCB dcbpnt;
-  unsigned long flags;
-/*  Scsi_Cmnd *ptr; */
+  PACB pACB;
+  PDCB pDCB;
+  DC390_AFLAGS
 
-  acbpnt = pACB_start;
+  pACB = dc390_pACB_start;
 
-  while(acbpnt != (PACB)-1)
+  while(pACB != (PACB)-1)
      {
-	shpnt = acbpnt->pScsiHost;
+	shpnt = pACB->pScsiHost;
 	if (shpnt->host_no == hostno) break;
-	acbpnt = acbpnt->pNextACB;
+	pACB = pACB->pNextACB;
      }
 
-  if (acbpnt == (PACB)-1) return(-ESRCH);
+  if (pACB == (PACB)-1) return(-ESRCH);
   if(!shpnt) return(-ESRCH);
 
   if(inout) /* Has data been written to the file ? */
-    return(tmscsim_set_info(buffer, length, shpnt));
+      return dc390_set_info(buffer, length, pACB);
+   
+  SPRINTF("Tekram DC390/AM53C974 PCI SCSI Host Adapter, ");
+  SPRINTF("Driver Version %s\n", DC390_VERSION);
 
-  SPRINTF("Tekram DC390(T) PCI SCSI Host Adadpter, ");
-  SPRINTF("Driver Version 1.10, 1996/12/05\n");
-
-  save_flags(flags);
-  cli();
+  DC390_LOCK_ACB;
 
   SPRINTF("SCSI Host Nr %i, ", shpnt->host_no);
-  SPRINTF("DC390 Adapter Nr %i\n", acbpnt->AdapterIndex);
-  SPRINTF("IOPortBase 0x%04x, ", acbpnt->IOPortBase);
-  SPRINTF("IRQLevel 0x%02x\n", acbpnt->IRQLevel);
-
-  SPRINTF("MaxID %i, MaxLUN %i, ",acbpnt->max_id, acbpnt->max_lun);
-  SPRINTF("AdapterID %i, AdapterLUN %i\n", acbpnt->AdaptSCSIID, acbpnt->AdaptSCSILUN);
-
-  SPRINTF("TagMaxNum %i, Status %i\n", acbpnt->TagMaxNum, acbpnt->status);
-
-  SPRINTF("Nr of attached devices: %i\n", acbpnt->DeviceCnt);
+  SPRINTF("%s Adapter Nr %i\n", dc390_adapname, pACB->AdapterIndex);
+  SPRINTF("IOPortBase 0x%04x, ", pACB->IOPortBase);
+  SPRINTF("IRQLevel 0x%02x\n", pACB->IRQLevel);
+
+  SPRINTF("MaxID %i, MaxLUN %i, ", shpnt->max_id, shpnt->max_lun);
+  SPRINTF("AdapterID %i, SelTimeout %i ms\n", 
+	  shpnt->this_id, (pACB->sel_timeout*164)/100);
+
+  SPRINTF("TagMaxNum %i, Status %i, ACBFlag %i, GlitchEater %i ns\n",
+	  pACB->TagMaxNum, pACB->status, pACB->ACBFlag, GLITCH_TO_NS(pACB->glitch_cfg)*12);
+
+  SPRINTF("Statistics: Cmnds %li, Cmnds not sent directly %li, Out of SRB conds %li\n",
+	  pACB->Cmds, pACB->CmdInQ, pACB->CmdOutOfSRB);
+  SPRINTF("            Lost arbitrations %li\n", pACB->SelLost);
+   
+  SPRINTF("Nr of attached devices: %i, Nr of DCBs: %i\n", pACB->DeviceCnt, pACB->DCBCnt);
 
-  SPRINTF("Un ID LUN Prty Sync DsCn SndS TagQ NegoPeriod SyncSpeed SyncOffs\n");
+  SPRINTF("Idx ID LUN Prty Sync DsCn SndS TagQ STOP NegoPeriod SyncSpeed SyncOffs\n");
 
-  dcbpnt = acbpnt->pLinkDCB;
-  for (dev = 0; dev < acbpnt->DeviceCnt; dev++)
+  pDCB = pACB->pLinkDCB;
+  for (dev = 0; dev < pACB->DCBCnt; dev++)
      {
-      SPRINTF("%02i %02i  %02i ", dev, dcbpnt->UnitSCSIID, dcbpnt->UnitSCSILUN);
-      YESNO(dcbpnt->DevMode & PARITY_CHK_);
-      YESNO(dcbpnt->SyncMode & SYNC_NEGO_DONE);
-      YESNO(dcbpnt->DevMode & EN_DISCONNECT_);
-      YESNO(dcbpnt->DevMode & SEND_START_);
-      YESNO(dcbpnt->SyncMode & EN_TAG_QUEUING);
-      SPRINTF("  %03i ns ", (dcbpnt->NegoPeriod) << 2);
-      if (dcbpnt->SyncOffset & 0x0f)
+      SPRINTF("%02i  %02i  %02i ", dev, pDCB->UnitSCSIID, pDCB->UnitSCSILUN);
+      YESNO(pDCB->DevMode & PARITY_CHK_);
+      YESNO(pDCB->SyncMode & SYNC_NEGO_DONE);
+      YESNO(pDCB->DevMode & EN_DISCONNECT_);
+      //YESNO(pDCB->SyncMode & EN_ATN_STOP);
+      YESNO(pDCB->DevMode & SEND_START_);
+      YESNO(pDCB->SyncMode & EN_TAG_QUEUEING);
+      YESNO(pDCB->SyncMode & EN_ATN_STOP);
+      if (pDCB->SyncOffset & 0x0f)
       {
-	 spd = 1000/(dcbpnt->NegoPeriod <<2);
-	 spd1 = 1000%(dcbpnt->NegoPeriod <<2);
-	 spd1 = (spd1 * 10)/(dcbpnt->NegoPeriod <<2);
-	 SPRINTF("   %2i.%1i M      %02i\n", spd, spd1, (dcbpnt->SyncOffset & 0x0f));
+	 int sp = pDCB->SyncPeriod; if (! (pDCB->CtrlR3 & FAST_SCSI)) sp++;
+	 SPRINTF("  %03i ns ", (pDCB->NegoPeriod) << 2);
+	 spd = 40/(sp); spd1 = 40%(sp);
+	 spd1 = (spd1 * 10 + sp/2) / (sp);
+	 SPRINTF("   %2i.%1i M      %02i\n", spd, spd1, (pDCB->SyncOffset & 0x0f));
       }
-      else SPRINTF("\n");
+      else SPRINTF(" (%03i ns)\n", (pDCB->NegoPeriod) << 2);
       /* Add more info ...*/
-      dcbpnt = dcbpnt->pNextDCB;
+      pDCB = pDCB->pNextDCB;
      }
 
-  restore_flags(flags);
+  DC390_UNLOCK_ACB;
   *start = buffer + offset;
 
   if (pos - buffer < offset)
@@ -1850,81 +2504,93 @@
   else
     return length;
 }
-#endif /* VERSION_ELF_1_2_13 */
 
+#undef YESNO
+#undef SPRINTF
 
 #ifdef MODULE
 
 /***********************************************************************
- * Function : static int DC390_shutdown (struct Scsi_Host *host)
+ * Function : static int dc390_shutdown (struct Scsi_Host *host)
  *
  * Purpose : does a clean (we hope) shutdown of the SCSI chip.
  *	     Use prior to dumping core, unloading the driver, etc.
  *
  * Returns : 0 on success
  ***********************************************************************/
-static int
-DC390_shutdown (struct Scsi_Host *host)
+static int dc390_shutdown (struct Scsi_Host *host)
 {
     UCHAR    bval;
-    USHORT   ioport;
-    unsigned long flags;
     PACB pACB = (PACB)(host->hostdata);
-
-    ioport = (unsigned int) pACB->IOPortBase;
-
-    save_flags (flags);
-    cli();
-
+   
 /*  pACB->soft_reset(host); */
 
-#ifdef DC390_DEBUG0
-    printk("DC390: shutdown,");
-#endif
+    printk(KERN_INFO "DC390: shutdown\n");
 
-    bval = inb(ioport+CtrlReg1);
+    pACB->ACBFlag = RESET_DONE;
+    bval = DC390_read8 (CtrlReg1);
     bval |= DIS_INT_ON_SCSI_RST;
-    outb(bval,ioport+CtrlReg1);  /* disable interrupt */
-    DC390_ResetSCSIBus( pACB );
+    DC390_write8 (CtrlReg1, bval);	/* disable interrupt */
+    if (pACB->Gmode2 & RST_SCSI_BUS)
+		dc390_ResetSCSIBus (pACB);
 
-    restore_flags (flags);
     return( 0 );
 }
 
+void dc390_freeDCBs (struct Scsi_Host *host)
+{
+    PDCB pDCB, nDCB;
+    PACB pACB = (PACB)(host->hostdata);
+    
+    pDCB = pACB->pLinkDCB;
+    if (!pDCB) return;
+    do
+    {
+	nDCB = pDCB->pNextDCB;
+	DCBDEBUG(printk (KERN_INFO "DC390: Free DCB (ID %i, LUN %i): 0x%08x\n",\
+		pDCB->UnitSCSIID, pDCB->UnitSCSILUN, (int)pDCB);)
+	kfree (pDCB);
+	pDCB = nDCB;
+    } while (pDCB && pDCB != pACB->pLinkDCB);
+
+};
 
 int DC390_release(struct Scsi_Host *host)
 {
-    int irq_count;
-    struct Scsi_Host *tmp;
+    int irq_count; 
+    PACB pACB;
+    DC390_AFLAGS DC390_IFLAGS
+#if USE_SPINLOCKS > 1
+    PACB pACB = (PACB)(host->hostdata);
+#endif
 
-    DC390_shutdown (host);
+    DC390_LOCK_IO;
+    DC390_LOCK_ACB;
+
+    dc390_shutdown (host);
 
     if (host->irq != IRQ_NONE)
     {
-	for (irq_count = 0, tmp = pSH_start; tmp; tmp = tmp->next)
+	for (irq_count = 0, pACB = dc390_pACB_start; 
+	     pACB; pACB = pACB->pNextACB)
 	{
-	    if ( tmp->irq == host->irq )
+	    if ( pACB->IRQLevel == host->irq )
 		++irq_count;
 	}
 	if (irq_count == 1)
 	 {
-#ifdef DC390_DEBUG0
-	    printk("DC390: Free IRQ %i.",host->irq);
-#endif
-#ifndef VERSION_ELF_1_2_13
+	    DEBUG0(printk(KERN_INFO "DC390: Free IRQ %i\n",host->irq);)
 	    free_irq(host->irq,NULL);
-#else
-	    free_irq(host->irq);
-#endif
 	 }
     }
 
     release_region(host->io_port,host->n_io_port);
-
+    dc390_freeDCBs (host);
+    DC390_UNLOCK_ACB;
+    DC390_UNLOCK_IO;
     return( 1 );
 }
 
 Scsi_Host_Template driver_template = DC390_T;
 #include "scsi_module.c"
 #endif /* def MODULE */
-
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/tmscsim.h linux/drivers/scsi/tmscsim.h
--- linux.vanilla/drivers/scsi/tmscsim.h	Sun Jun 21 18:41:18 1998
+++ linux/drivers/scsi/tmscsim.h	Sun Dec 27 20:31:40 1998
@@ -3,12 +3,23 @@
 ;*		    TEKRAM DC-390(T) PCI SCSI Bus Master Host Adapter  *
 ;*		    Device Driver				       *
 ;***********************************************************************/
+/* $Id: tmscsim.h,v 2.4 1998/12/25 17:33:27 garloff Exp $ */
 
-#ifndef TMSCSIM_H
-#define TMSCSIM_H
+#ifndef _TMSCSIM_H
+#define _TMSCSIM_H
 
 #define IRQ_NONE 255
 
+#define MAX_ADAPTER_NUM 	4
+#define MAX_SG_LIST_BUF 	16
+#define MAX_CMD_PER_LUN 	8
+#define MAX_CMD_QUEUE		2*MAX_CMD_PER_LUN+1	
+#define MAX_SCSI_ID		8
+#define MAX_SRB_CNT		MAX_CMD_QUEUE+1	/* Max number of started commands */
+#define END_SCAN		2
+
+#define SEL_TIMEOUT		153	/* 250 ms selection timeout (@ 40 MHz) */
+
 typedef unsigned char	UCHAR;
 typedef unsigned short	USHORT;
 typedef unsigned long	ULONG;
@@ -54,17 +65,6 @@
 } SGentry1, *PSGE;
 
 
-#define MAX_ADAPTER_NUM 	4
-#define MAX_DEVICES		10
-#define MAX_SG_LIST_BUF 	16
-#define MAX_CMD_QUEUE		20
-#define MAX_CMD_PER_LUN 	8
-#define MAX_SCSI_ID		8
-#define MAX_SRB_CNT		MAX_CMD_QUEUE+4
-#define END_SCAN		2
-
-#define SEL_TIMEOUT		153	/* 250 ms selection timeout (@ 40 MHz) */
-
 /*
 ;-----------------------------------------------------------------------
 ; SCSI Request Block
@@ -79,40 +79,44 @@
 PSCSICMD	pcmd;
 PSGL		pSegmentList;
 
-ULONG		PhysSRB;
+ULONG		Segment0[2];
+ULONG		Segment1[2];
+
+/* 0x2c:*/
 ULONG		TotalXferredLen;
-ULONG		SGPhysAddr;	/*;a segment starting address */
+ULONG		SGBusAddr;	/*;a segment starting address as seen by AM53C974A*/
 ULONG		SGToBeXferLen;	/*; to be xfer length */
+ULONG		SRBState;
 
-SGL		Segmentx;	/* make a one entry of S/G list table */
-
-PUCHAR		pMsgPtr;
-USHORT		SRBState;
-USHORT		Revxx2; 	/* ??? */
-
+/* 0x3c: */
 UCHAR		MsgInBuf[6];
 UCHAR		MsgOutBuf[6];
 
+/* 0x48: */
+SGL		Segmentx;	/* make a one entry of S/G list table */
+
+UCHAR		ScsiCmdLen;
+UCHAR		ScsiPhase;
+
 UCHAR		AdaptStatus;
 UCHAR		TargetStatus;
+
+/* 0x58: */
 UCHAR		MsgCnt;
 UCHAR		EndMessage;
+UCHAR		RetryCnt;
+UCHAR		SRBFlag;	/*; b0-AutoReqSense,b6-Read,b7-write */
+				/*; b4-settimeout,b5-Residual valid */
 UCHAR		TagNumber;
 UCHAR		SGcount;
 UCHAR		SGIndex;
-UCHAR		IORBFlag;	/*;81h-Reset, 2-retry */
-
 UCHAR		SRBStatus;
-UCHAR		RetryCnt;
-UCHAR		SRBFlag;	/*; b0-AutoReqSense,b6-Read,b7-write */
-				/*; b4-settimeout,b5-Residual valid */
-UCHAR		ScsiCmdLen;
-UCHAR		ScsiPhase;
-UCHAR		Reserved3[3];	/*;for dword alignment */
-ULONG		Segment0[2];
-ULONG		Segment1[2];
+  //UCHAR		IORBFlag;	/*;81h-Reset, 2-retry */
+
+/* 0x60: */
 };
 
+
 typedef  struct  _SRB	 DC390_SRB, *PSRB;
 
 /*
@@ -129,42 +133,44 @@
 PSCSICMD	pQIORBtail;
 PSCSICMD	AboIORBhead;
 PSCSICMD	AboIORBtail;
-USHORT		QIORBCnt;
-USHORT		AboIORBcnt;
+ULONG		QIORBCnt;
+ULONG		AboIORBcnt;
 
+/* 0x20: */
 PSRB		pWaitingSRB;
 PSRB		pWaitLast;
 PSRB		pGoingSRB;
 PSRB		pGoingLast;
 PSRB		pActiveSRB;
-USHORT		GoingSRBCnt;
-USHORT		WaitSRBCnt;	/* ??? */
+UCHAR		GoingSRBCnt;
+UCHAR		WaitSRBCnt;	/* ??? */
+UCHAR		DevType;
+UCHAR		MaxCommand;
 
+/* 0x38: */
 ULONG		TagMask;
 
-USHORT		MaxCommand;
-USHORT		AdaptIndex;	/*; UnitInfo struc start */
-USHORT		UnitIndex;	/*; nth Unit on this card */
 UCHAR		UnitSCSIID;	/*; SCSI Target ID  (SCSI Only) */
 UCHAR		UnitSCSILUN;	/*; SCSI Log.  Unit (SCSI Only) */
-
+UCHAR		DevMode;
 UCHAR		IdentifyMsg;
+
 UCHAR		CtrlR1;
 UCHAR		CtrlR3;
 UCHAR		CtrlR4;
 
-UCHAR		InqDataBuf[8];
-UCHAR		CapacityBuf[8];
-UCHAR		DevMode;
-UCHAR		AdpMode;
+UCHAR		DCBFlag;
+
+/* 0x44: */
 UCHAR		SyncMode;	/*; 0:async mode */
 UCHAR		NegoPeriod;	/*;for nego. */
 UCHAR		SyncPeriod;	/*;for reg. */
 UCHAR		SyncOffset;	/*;for reg. and nego.(low nibble) */
-UCHAR		UnitCtrlFlag;
-UCHAR		DCBFlag;
-UCHAR		DevType;
-UCHAR		Reserved2[3];	/*;for dword alignment */
+
+/* 0x48:*/
+//UCHAR		InqDataBuf[8];
+//UCHAR		CapacityBuf[8];
+/* 0x58: */
 };
 
 typedef  struct  _DCB	 DC390_DCB, *PDCB;
@@ -175,40 +181,57 @@
 */
 struct	_ACB
 {
-ULONG		PhysACB;
 PSH		pScsiHost;
 struct _ACB	*pNextACB;
 USHORT		IOPortBase;
-USHORT		Revxx1; 	/* ??? */
+UCHAR		IRQLevel;
+UCHAR		status;
+
+UCHAR		SRBCount;
+UCHAR		AdapterIndex;	/*; nth Adapter this driver */
+UCHAR		DeviceCnt;
+UCHAR		DCBCnt;
+
+/* 0x10: */
+UCHAR		TagMaxNum;
+UCHAR		ACBFlag;
+UCHAR		Gmode2;
+UCHAR		scan_devices;
 
 PDCB		pLinkDCB;
+PDCB		pLastDCB;
 PDCB		pDCBRunRobin;
 PDCB		pActiveDCB;
-PDCB		pDCB_free;
 PSRB		pFreeSRB;
 PSRB		pTmpSRB;
-USHORT		SRBCount;
-USHORT		AdapterIndex;	/*; nth Adapter this driver */
-USHORT		max_id;
-USHORT		max_lun;
+
+/* 0x2c: */
 
 UCHAR		msgin123[4];
-UCHAR		status;
-UCHAR		AdaptSCSIID;	/*; Adapter SCSI Target ID */
-UCHAR		AdaptSCSILUN;	/*; Adapter SCSI LUN */
-UCHAR		DeviceCnt;
-UCHAR		IRQLevel;
-UCHAR		TagMaxNum;
-UCHAR		ACBFlag;
-UCHAR		Gmode2;
-UCHAR		LUNchk;
-UCHAR		scan_devices;
-UCHAR		HostID_Bit;
-UCHAR		Reserved1[1];	/*;for dword alignment */
 UCHAR		DCBmap[MAX_SCSI_ID];
-DC390_DCB	DCB_array[MAX_DEVICES]; 	/* +74h,  Len=3E8 */
-DC390_SRB	SRB_array[MAX_SRB_CNT]; 	/* +45Ch, Len=	*/
+
+#if defined(USE_SPINLOCKS) && USE_SPINLOCKS > 1 && (defined(__SMP__) || DEBUG_SPINLOCKS > 0)
+spinlock_t	lock;
+#endif
+UCHAR		sel_timeout;
+UCHAR		glitch_cfg;
+
+UCHAR		MsgLen;
+UCHAR		Ignore_IRQ;	/* Not used */
+
+PDEVDECL1;			/* Pointer to PCI cfg. space */
+/* 0x40/0x3c: */
+ULONG		Cmds;
+ULONG		CmdInQ;
+ULONG		CmdOutOfSRB;
+ULONG		SelLost;
+
+	
+/* 0x50/0x4c: */	
 DC390_SRB	TmpSRB;
+/* 0xb4/0xb0: */
+DC390_SRB	SRB_array[MAX_SRB_CNT]; 	/* 18 SRBs */
+/* 0x7bc/0x7b8: */
 };
 
 typedef  struct  _ACB	 DC390_ACB, *PACB;
@@ -278,14 +301,6 @@
 #define DO_SYNC_NEGO	BIT13
 #define SRB_UNEXPECT_RESEL BIT14
 
-/*;---ACBFlag */
-#define RESET_DEV	BIT0
-#define RESET_DETECT	BIT1
-#define RESET_DONE	BIT2
-
-/*;---DCBFlag */
-#define ABORT_DEV_	BIT0
-
 /*;---SRBstatus */
 #define SRB_OK		BIT0
 #define ABORTION	BIT1
@@ -294,6 +309,14 @@
 #define PARITY_ERROR	BIT4
 #define SRB_ERROR	BIT5
 
+/*;---ACBFlag */
+#define RESET_DEV	BIT0
+#define RESET_DETECT	BIT1
+#define RESET_DONE	BIT2
+
+/*;---DCBFlag */
+#define ABORT_DEV_	BIT0
+
 /*;---SRBFlag */
 #define DATAOUT 	BIT7
 #define DATAIN		BIT6
@@ -316,7 +339,7 @@
 #define H_BAD_CCB_OR_SG  0x1A
 #define H_ABORT 	 0x0FF
 
-/*; SCSI Status byte codes*/
+/*; SCSI Status byte codes*/ /* Twice the values defined in scsi/scsi.h */
 #define SCSI_STAT_GOOD		0x0	/*;  Good status */
 #define SCSI_STAT_CHECKCOND	0x02	/*;  SCSI Check Condition */
 #define SCSI_STAT_CONDMET	0x04	/*;  Condition Met */
@@ -335,9 +358,9 @@
 #define SYNC_DISABLE	0
 #define SYNC_ENABLE	BIT0
 #define SYNC_NEGO_DONE	BIT1
-#define WIDE_ENABLE	BIT2
-#define WIDE_NEGO_DONE	BIT3
-#define EN_TAG_QUEUING	BIT4
+#define WIDE_ENABLE	BIT2	/* Not used ;-) */
+#define WIDE_NEGO_DONE	BIT3	/* Not used ;-) */
+#define EN_TAG_QUEUEING	BIT4
 #define EN_ATN_STOP	BIT5
 
 #define SYNC_NEGO_OFFSET 15
@@ -352,7 +375,7 @@
 #define SCSI_MSG_OUT	6
 #define SCSI_MSG_IN	7
 
-/*;----SCSI MSG BYTE*/
+/*;----SCSI MSG BYTE*/ /* see scsi/scsi.h */
 #define MSG_COMPLETE		0x00
 #define MSG_EXTENDED		0x01
 #define MSG_SAVE_PTR		0x02
@@ -373,13 +396,6 @@
 #define MSG_IDENTIFY		0x80
 #define MSG_HOST_ID		0x0C0
 
-/*;----SCSI STATUS BYTE*/
-#define STATUS_GOOD		0x00
-#define CHECK_CONDITION_	0x02
-#define STATUS_BUSY		0x08
-#define STATUS_INTERMEDIATE	0x10
-#define RESERVE_CONFLICT	0x18
-
 /* cmd->result */
 #define STATUS_MASK_		0xFF
 #define MSG_MASK		0xFF00
@@ -389,7 +405,7 @@
 **  Inquiry Data format
 */
 
-typedef struct	_SCSIInqData { /* INQ */
+typedef struct	_SCSIInqData { /* INQUIRY */
 
 	UCHAR	 DevType;		/* Periph Qualifier & Periph Dev Type*/
 	UCHAR	 RMB_TypeMod;		/* rem media bit & Dev Type Modifier */
@@ -412,6 +428,7 @@
 
 #define SCSI_DEVTYPE	    0x1F      /* Peripheral Device Type 	    */
 #define SCSI_PERIPHQUAL     0xE0      /* Peripheral Qualifier		    */
+#define TYPE_NODEV	    SCSI_DEVTYPE    /* Unknown or no device type    */
 
 
 /*  Inquiry byte 1 mask */
@@ -420,18 +437,10 @@
 
 
 /*  Peripheral Device Type definitions */
+/*  see include/scsi/scsi.h for the rest */
 
-#define SCSI_DASD		 0x00	   /* Direct-access Device	   */
-#define SCSI_SEQACESS		 0x01	   /* Sequential-access device	   */
-#define SCSI_PRINTER		 0x02	   /* Printer device		   */
-#define SCSI_PROCESSOR		 0x03	   /* Processor device		   */
-#define SCSI_WRITEONCE		 0x04	   /* Write-once device 	   */
-#define SCSI_CDROM		 0x05	   /* CD-ROM device		   */
-#define SCSI_SCANNER		 0x06	   /* Scanner device		   */
-#define SCSI_OPTICAL		 0x07	   /* Optical memory device	   */
-#define SCSI_MEDCHGR		 0x08	   /* Medium changer device	   */
-#define SCSI_COMM		 0x09	   /* Communications device	   */
-#define SCSI_NODEV		 0x1F	   /* Unknown or no device type    */
+#define TYPE_PRINTER		 0x02	   /* Printer device		   */
+#define TYPE_COMM		 0x09	   /* Communications device	   */
 
 /*
 ** Inquiry flag definitions (Inq data byte 7)
@@ -459,17 +468,24 @@
 UCHAR	xx2;
 } EEprom, *PEEprom;
 
-#define EE_ADAPT_SCSI_ID 64
-#define EE_MODE2	65
-#define EE_DELAY	66
-#define EE_TAG_CMD_NUM	67
+#define REAL_EE_ADAPT_SCSI_ID 64
+#define REAL_EE_MODE2	65
+#define REAL_EE_DELAY	66
+#define REAL_EE_TAG_CMD_NUM	67
+
+#define EE_ADAPT_SCSI_ID 32
+#define EE_MODE2	33
+#define EE_DELAY	34
+#define EE_TAG_CMD_NUM	35
+
+#define EE_LEN		40
 
 /*; EE_MODE1 bits definition*/
 #define PARITY_CHK_	BIT0
 #define SYNC_NEGO_	BIT1
 #define EN_DISCONNECT_	BIT2
 #define SEND_START_	BIT3
-#define TAG_QUEUING_	BIT4
+#define TAG_QUEUEING_	BIT4
 
 /*; EE_MODE2 bits definition*/
 #define MORE2_DRV	BIT0
@@ -494,34 +510,42 @@
 ;====================
 */
 
-/*; Command Reg.(+0CH) */
+/*; Command Reg.(+0CH) (rw) */
 #define DMA_COMMAND		BIT7
 #define NOP_CMD 		0
 #define CLEAR_FIFO_CMD		1
 #define RST_DEVICE_CMD		2
 #define RST_SCSI_BUS_CMD	3
+
 #define INFO_XFER_CMD		0x10
 #define INITIATOR_CMD_CMPLTE	0x11
 #define MSG_ACCEPTED_CMD	0x12
 #define XFER_PAD_BYTE		0x18
 #define SET_ATN_CMD		0x1A
 #define RESET_ATN_CMD		0x1B
-#define SELECT_W_ATN		0x42
+
+#define SEL_WO_ATN		0x41	/* currently not used */
+#define SEL_W_ATN		0x42
 #define SEL_W_ATN_STOP		0x43
+#define SEL_W_ATN3		0x46
 #define EN_SEL_RESEL		0x44
-#define SEL_W_ATN2		0x46
+#define DIS_SEL_RESEL		0x45	/* currently not used */
+#define RESEL			0x40	/* " */
+#define RESEL_ATN3		0x47	/* " */
+
 #define DATA_XFER_CMD		INFO_XFER_CMD
 
 
-/*; SCSI Status Reg.(+10H) */
+/*; SCSI Status Reg.(+10H) (r) */
 #define INTERRUPT		BIT7
 #define ILLEGAL_OP_ERR		BIT6
 #define PARITY_ERR		BIT5
 #define COUNT_2_ZERO		BIT4
 #define GROUP_CODE_VALID	BIT3
-#define SCSI_PHASE_MASK 	(BIT2+BIT1+BIT0)
+#define SCSI_PHASE_MASK 	(BIT2+BIT1+BIT0) 
+/* BIT2: MSG phase; BIT1: C/D physe; BIT0: I/O phase */
 
-/*; Interrupt Status Reg.(+14H) */
+/*; Interrupt Status Reg.(+14H) (r) */
 #define SCSI_RESET		BIT7
 #define INVALID_CMD		BIT6
 #define DISCONNECTED		BIT5
@@ -531,11 +555,12 @@
 #define SEL_ATTENTION		BIT1
 #define SELECTED		BIT0
 
-/*; Internal State Reg.(+18H) */
+/*; Internal State Reg.(+18H) (r) */
 #define SYNC_OFFSET_FLAG	BIT3
 #define INTRN_STATE_MASK	(BIT2+BIT1+BIT0)
+/* 0x04: Sel. successful (w/o stop), 0x01: Sel. successful (w/ stop) */
 
-/*; Clock Factor Reg.(+24H) */
+/*; Clock Factor Reg.(+24H) (w) */
 #define CLK_FREQ_40MHZ		0
 #define CLK_FREQ_35MHZ		(BIT2+BIT1+BIT0)
 #define CLK_FREQ_30MHZ		(BIT2+BIT1)
@@ -544,47 +569,54 @@
 #define CLK_FREQ_15MHZ		(BIT1+BIT0)
 #define CLK_FREQ_10MHZ		BIT1
 
-/*; Control Reg. 1(+20H) */
+/*; Control Reg. 1(+20H) (rw) */
 #define EXTENDED_TIMING 	BIT7
 #define DIS_INT_ON_SCSI_RST	BIT6
 #define PARITY_ERR_REPO 	BIT4
-#define SCSI_ID_ON_BUS		(BIT2+BIT1+BIT0)
+#define SCSI_ID_ON_BUS		(BIT2+BIT1+BIT0) /* host adapter ID */
 
-/*; Control Reg. 2(+2CH) */
+/*; Control Reg. 2(+2CH) (rw) */
 #define EN_FEATURE		BIT6
 #define EN_SCSI2_CMD		BIT3
 
-/*; Control Reg. 3(+30H) */
+/*; Control Reg. 3(+30H) (rw) */
 #define ID_MSG_CHECK		BIT7
 #define EN_QTAG_MSG		BIT6
 #define EN_GRP2_CMD		BIT5
 #define FAST_SCSI		BIT4	/* ;10MB/SEC */
 #define FAST_CLK		BIT3	/* ;25 - 40 MHZ */
 
-/*; Control Reg. 4(+34H) */
+/*; Control Reg. 4(+34H) (rw) */
 #define EATER_12NS		0
 #define EATER_25NS		BIT7
 #define EATER_35NS		BIT6
 #define EATER_0NS		(BIT7+BIT6)
+#define REDUCED_POWER		BIT5
+#define CTRL4_RESERVED		BIT4	/* must be 1 acc. to AM53C974.c */
 #define NEGATE_REQACKDATA	BIT2
 #define NEGATE_REQACK		BIT3
+
+#define GLITCH_TO_NS(x) (((~x>>6 & 2) >> 1) | ((x>>6 & 1) << 1 ^ (x>>6 & 2)))
+#define NS_TO_GLITCH(y) (((~y<<7) | ~((y<<6) ^ ((y<<5 & 1<<6) | ~0x40))) & 0xc0)
+
 /*
 ;====================
 ; DMA Register
 ;====================
 */
-/*; DMA Command Reg.(+40H) */
+/*; DMA Command Reg.(+40H) (rw) */
 #define READ_DIRECTION		BIT7
 #define WRITE_DIRECTION 	0
 #define EN_DMA_INT		BIT6
-#define MAP_TO_MDL		BIT5
-#define DIAGNOSTIC		BIT4
+#define EN_PAGE_INT		BIT5	/* page transfer interrupt enable */
+#define MAP_TO_MDL		BIT4
+#define DIAGNOSTIC		BIT2
 #define DMA_IDLE_CMD		0
 #define DMA_BLAST_CMD		BIT0
 #define DMA_ABORT_CMD		BIT1
 #define DMA_START_CMD		(BIT1+BIT0)
 
-/*; DMA Status Reg.(+54H) */
+/*; DMA Status Reg.(+54H) (r) */
 #define PCI_MS_ABORT		BIT6
 #define BLAST_COMPLETE		BIT5
 #define SCSI_INTERRUPT		BIT4
@@ -593,88 +625,77 @@
 #define DMA_XFER_ERROR		BIT1
 #define POWER_DOWN		BIT0
 
-/*
-; DMA SCSI Bus and Ctrl.(+70H)
-;EN_INT_ON_PCI_ABORT
-*/
+/*; DMA SCSI Bus and Ctrl.(+70H) */
+#define EN_INT_ON_PCI_ABORT	BIT25
+#define WRT_ERASE_DMA_STAT	BIT24
+#define PW_DOWN_CTRL		BIT21
+#define SCSI_BUSY		BIT20
+#define SCLK			BIT19
+#define SCAM			BIT18
+#define SCSI_LINES		0x0003ffff
 
 /*
 ;==========================================================
 ; SCSI Chip register address offset
 ;==========================================================
+;Registers are rw unless declared otherwise 
 */
-#define CtcReg_Low	0x00
-#define CtcReg_Mid	0x04
+#define CtcReg_Low	0x00	/* r	curr. transfer count */
+#define CtcReg_Mid	0x04	/* r */
+#define CtcReg_High	0x38	/* r */
 #define ScsiFifo	0x08
 #define ScsiCmd 	0x0C
-#define Scsi_Status	0x10
-#define INT_Status	0x14
-#define Sync_Period	0x18
-#define Sync_Offset	0x1C
-#define CtrlReg1	0x20
-#define Clk_Factor	0x24
+#define Scsi_Status	0x10	/* r */
+#define INT_Status	0x14	/* r */
+#define Sync_Period	0x18	/* w */
+#define Sync_Offset	0x1C	/* w */
+#define Clk_Factor	0x24	/* w */
+#define CtrlReg1	0x20	
 #define CtrlReg2	0x2C
 #define CtrlReg3	0x30
 #define CtrlReg4	0x34
-#define CtcReg_High	0x38
 #define DMA_Cmd 	0x40
-#define DMA_XferCnt	0x44
-#define DMA_XferAddr	0x48
-#define DMA_Wk_ByteCntr 0x4C
-#define DMA_Wk_AddrCntr 0x50
-#define DMA_Status	0x54
-#define DMA_MDL_Addr	0x58
-#define DMA_Wk_MDL_Cntr 0x5C
-#define DMA_ScsiBusCtrl 0x70
-
-#define StcReg_Low	CtcReg_Low
-#define StcReg_Mid	CtcReg_Mid
-#define Scsi_Dest_ID	Scsi_Status
-#define Scsi_TimeOut	INT_Status
-#define Intern_State	Sync_Period
-#define Current_Fifo	Sync_Offset
-#define StcReg_High	CtcReg_High
-
-#define am_target	Scsi_Status
-#define am_timeout	INT_Status
-#define am_seq_step	Sync_Period
-#define am_fifo_count	Sync_Offset
-
-
-#define DC390_read8(address)			       \
-	inb(DC390_ioport + (address)))
-
-#define DC390_read16(address)			       \
-	inw(DC390_ioport + (address)))
-
-#define DC390_read32(address)			       \
-	inl(DC390_ioport + (address)))
-
-#define DC390_write8(address,value)		       \
-	outb((value), DC390_ioport + (address)))
-
-#define DC390_write16(address,value)		       \
-	outw((value), DC390_ioport + (address)))
-
-#define DC390_write32(address,value)		       \
-	outl((value), DC390_ioport + (address)))
-
-
-/* Configuration method #1 */
-#define PCI_CFG1_ADDRESS_REG		0xcf8
-#define PCI_CFG1_DATA_REG		0xcfc
-#define PCI_CFG1_ENABLE 		0x80000000
-#define PCI_CFG1_TUPPLE(bus, device, function, register)		\
-	(PCI_CFG1_ENABLE | (((bus) << 16) & 0xff0000) | 		\
-	(((device) << 11) & 0xf800) | (((function) << 8) & 0x700)|	\
-	(((register) << 2) & 0xfc))
-
-/* Configuration method #2 */
-#define PCI_CFG2_ENABLE_REG		0xcf8
-#define PCI_CFG2_FORWARD_REG		0xcfa
-#define PCI_CFG2_ENABLE 		0x0f0
-#define PCI_CFG2_TUPPLE(function)					\
-	(PCI_CFG2_ENABLE | (((function) << 1) & 0xe))
+#define DMA_XferCnt	0x44	/* rw	starting transfer count (32 bit) */
+#define DMA_XferAddr	0x48	/* rw	starting physical address (32 bit) */
+#define DMA_Wk_ByteCntr 0x4C	/* r	working byte counter */
+#define DMA_Wk_AddrCntr 0x50	/* r	working address counter */
+#define DMA_Status	0x54	/* r */
+#define DMA_MDL_Addr	0x58	/* rw	starting MDL address */
+#define DMA_Wk_MDL_Cntr 0x5C	/* r	working MDL counter */
+#define DMA_ScsiBusCtrl 0x70	/* rw	SCSI Bus, PCI/DMA Ctrl */
+
+#define StcReg_Low	CtcReg_Low	/* w	start transfer count */
+#define StcReg_Mid	CtcReg_Mid	/* w */
+#define StcReg_High	CtcReg_High	/* w */
+#define Scsi_Dest_ID	Scsi_Status	/* w */
+#define Scsi_TimeOut	INT_Status	/* w */
+#define Intern_State	Sync_Period	/* r */
+#define Current_Fifo	Sync_Offset	/* r	Curr. FIFO / int. state */
+
+
+#define DC390_read8(address)			\
+	(inb (pACB->IOPortBase + (address)))
+
+#define DC390_read8_(address, base)		\
+	(inb ((USHORT)(base) + (address)))
+
+#define DC390_read16(address)			\
+	(inw (pACB->IOPortBase + (address)))
+
+#define DC390_read32(address)			\
+	(inl (pACB->IOPortBase + (address)))
+
+#define DC390_write8(address,value)		\
+	outb ((value), pACB->IOPortBase + (address))
+
+#define DC390_write8_(address,value,base)	\
+	outb ((value), (USHORT)(base) + (address))
+
+#define DC390_write16(address,value)		\
+	outw ((value), pACB->IOPortBase + (address))
+
+#define DC390_write32(address,value)		\
+	outl ((value), pACB->IOPortBase + (address))
 
 
-#endif /* TMSCSIM_H */
+#endif /* _TMSCSIM_H */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/tripace.c linux/drivers/scsi/tripace.c
--- linux.vanilla/drivers/scsi/tripace.c	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/tripace.c	Mon Mar 22 01:13:02 1999
@@ -0,0 +1,1562 @@
+/* tc2550.c -- Tripace TC-2550x based PCI SCSI Adapter  SCSI driver
+ * Created:D.Ravi jun 8 1998 at chennai lab of Tripace 
+ * Copyright 1998 Tripace Europe BV
+ *
+ * Driver version 1.00.000 (904)
+
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ **************************************************************************
+
+ DESCRIPTION:
+
+
+
+
+ REFERENCES USED:
+
+ 1.0 Design Kit for the Tripace TC-2550x based PCI SCSI  Adapter
+
+ 2.0 Tripace IOLAYER document.
+
+ 3.0 LINUX driver sources in /usr/linux/drivers/scsi directory.
+
+
+ ALPHA TESTERS:
+
+ 1.0 so far no external testers,only developer testing has been done.
+
+ 2.0 Testing with IBMHDD,Quantum HDD,zip drive and sony CDROM has been 
+ done.
+
+
+ NOTES ON USER DEFINABLE OPTIONS:
+
+ 1.0 The following are the command line options that are possible for the
+ TRIPACE TC-2550 PCI SCSI controller without BIOS.
+
+ tripace=fast clock,discon,sync,tag
+
+
+ The values for these four options can be either 0 or 1.
+ If fast clock is set to 1 ,then the chip uses a 60mhz clock.If ultra
+ scsi devices are used this should be set and the controller should have a
+ 60mhz crystal.
+
+ if disconnect is 1 ,then disconnect/reconnect is allowed for all scsi
+ devices connected to the controller.if it is 0 ,it is off.
+
+ sync = 1 means that synchronous negotiation will be done with scsi
+ devices.currently,though the flag is set ,the function is not implemented.
+
+ Tag = 1 means that tagged queue commands can be sent to the scsi devices.
+ This is not implemnted as yet in the driver. 
+
+ The default values are 0,1,1,1
+
+ **************************************************************************/
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/head.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "tripace.h"
+#include <asm/system.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include "tripace_mcode.h"
+
+#define VERSION          "$Revision: 1.00 $"
+
+/* #define DEBUG */
+
+
+#define   MAXTARGET               8
+#define   MAXSRBS		  16
+
+#define   RISC_ENTRY1             0
+
+#define   DEV_PARAM_REG_BASE      CFG_BASE+0x20
+
+#define   SCSI_DMA_POINTER        CFG_BASE+0x40
+#define   SCSI_DMA_COUNTER        CFG_BASE+0x44
+#define   HOST_DMA_POINTER        CFG_BASE+0x48
+#define   HOST_DMA_COUNTER        CFG_BASE+0x4c
+#define   MBX_IN_BASE             CFG_BASE+0x50
+#define   MBX_OUT_BASE            CFG_BASE+0x54
+#define   DEV_HDR_BASE            CFG_BASE+0x58
+#define   TASK_Q_BASE             CFG_BASE+0x5c
+#define   CURRENT_DEV_PTR         CFG_BASE+0x60
+#define   CURRENT_TASK_PTR        CFG_BASE+0x64
+#define   SG_LIST_PTR             CFG_BASE+0x68
+#define   ACC                     CFG_BASE+0x6c
+#define   SCRATCHB                CFG_BASE+0x70
+#define   CURRENT_INST            CFG_BASE+0x74
+#define   FLAG                    CFG_BASE+0x78
+#define   SCRATCHC                CFG_BASE+0x7a
+#define   MBX_IN_INDEX            CFG_BASE+0x7b
+#define   PC                      CFG_BASE+0x7c
+#define   MBX_OUT_INDEX           CFG_BASE+0x7f
+#define   PROGRAM_DATA_REG        CFG_BASE+0x80
+#define   CONTROL_REG             CFG_BASE+0x84
+#define   STATUS_INT_REG          CFG_BASE+0x86
+#define   SCSI_DATA               CFG_BASE+0x88
+#define   SCSI_CONTROL            CFG_BASE+0x8A
+#define   SCSI_ID_REG             CFG_BASE+0x8b
+
+#define   INFO_BASE               0x780
+#define   SIG_BASE                0x7D4
+
+#define   Q_FILE_SIZE             8192
+#define   DEVHDR_SIZE              512
+#define   MBOX_SIZE                 48
+#define   BIOS_SIGNATURE          0x55AAC731
+
+#define   PCI_1                   1
+#define   PCI_2                   2
+#define   MAX                     28
+
+#define   PCI_BUS                 1
+#define   PCI_INDEX_PORT          0xCF8
+#define   PCI_INDEX               0xF0
+#define   PCI_CONFIG              0xC000  /* this needs to include slot number */
+
+#define   FAIL                   -1
+/* #define   GOOD                 0 */
+#define   ERR                     1
+#define   DONE                    2
+#define   INT_HALT             0xfe000000	/* interrupt code */
+
+#define   SRB_DONE                0
+#define   SRB_ACTIVE              2
+#define   SRB_READY               1
+#define   SRB_CLEAN               4
+#define   SRB_ASSIGNED            8
+#define   SRB_STOP                2
+
+#define   STOP_RISC               2
+#define   FIRST_CMD               1
+#define   SPECIAL_CMD             1
+#define   MAX_OFFSET             30
+/* #define   MAX_PERIOD             12 */
+#define   MAX_PERIOD             12
+#define   W_MAX_OFFSET           14
+
+/* msg */
+#define   CMD_ABORT              0x6
+#define   DEVICE_RESET           0xC
+
+
+#define   CHECK_SYNC             0xA
+#define   CHECK_WIDE             0xD
+#define   SYNC_NORESPONSE        0x8
+#define   SYNC_REJECTED          0x9
+#define   SEL_TIME_OUT	         0x11
+#define   ERR_OVERRUN		 0x12
+#define   ERR_BUSFREE		 0x13
+#define   ERR_PHASE		 0x14
+#define   CHECK_COND		 0x04
+#define   ERR_PARITY  		 0x05
+#define   ERR_MESSAGE 		 0x07
+#define   WIDE_NORESPONSE        0x0b
+#define   WIDE_REJECTED          0x0c
+
+
+#define   DO_WIDE_NEGO           0x02
+#define   DO_SYNC_NEGO           0x04
+#define   FIRST_COMMAND          0x01
+
+#define   ENABLE_WIDE_BUS        0x100
+#define   ENABLE_TAG_MSG         0x200
+
+/*---------------------------------------------------------*
+**  parameter in register definitions                      *
+**                                                         *
+**---------------------------------------------------------*/
+/*
+   status register (read only)
+ */
+#define      INTR_PENDING         0x01
+#define      INTR_DIS             0x02
+#define      SCAM_INTR_DIS        0x04
+#define      POWER_SAVE_ON        0x08
+#define      SCSI_RESET_OUT       0x10
+#define      SCAM_INTR            0x20
+#define      RISC_HALT            0x40
+#define      SELECTION_TIMEOUT    0x80
+#define      SCSI_PARITY_ERR      0x100
+#define      SCSI_RESET_LAT       0x200
+#define      STS_SCSI_RESET       0x400
+#define      EEPROM_IN            0x800
+#define      SCAM_ARB_WIN         0x1000
+#define      EN_DMA               0x2000
+#define      EN_ARB_SEL           0x4000
+/*
+   **   interrupt control register (write only)
+ */
+#define      RESET_INTR_LATCH     0x01
+#define      DIS_INTR             0x02
+#define      DIS_SCAM_INTR        0x04
+#define      POWER_SAVE           0x08
+#define      RESET_SCSI_BUS       0x10
+/*ravi 10/3/98 changed for term power disable/enable in command line */
+/*#define      EN_TERM_PWR          0x20 */
+#define      TERM_PWR_EN            0x20
+/*
+   **   control register ( read/write )
+ */
+#define      RISC_CHIP_RESET      0x01
+#define      RISC_SINGLE_SETP     0x02
+#define      HALT_RISC            0x04
+#define      EN_SCSI_SCAM         0x08
+#define      DIS_SCSI_PARITY      0x10
+#define      EN_TARGET_MODE       0x20
+#define      FAST_CLOCK           0x40
+#define      EN_MEMORY_WRITE      0x80
+#define      DIS_PCI_BURST        0x100
+#define      DIS_PCI_CACHE_LINE   0x200
+#define      DIS_MUL_CACHE_LINE   0x400
+#define      EEPROM_CLOCK         0x800
+#define      EEPROM_DATA_OUT      0x1000
+#define      EEPROM_CHIP_SEL      0x2000
+#define      FAST_SELECTION       0x4000
+#define      EN_SCAM_ARB          0x8000
+
+#define      TASK_DONE            0x80
+
+
+/***************************************************************************
+**	 filename: newtypes.h
+**	 usage   : type definition 
+****************************************************************************/
+
+
+/***************************************************************************
+**	 filename: newtypes.h
+**	 usage   : type definition 
+****************************************************************************/
+
+#define   MAX_Adapter   4	/* maximu adapters allow                */
+
+/*
+   **   command related structure (7 bytes)
+ */
+typedef struct _task_cmd
+{
+	u8 CmdInProcess;
+	u8 * REQ_Header;
+	void (*complete) (void);
+}
+TaskCmd, *PTaskCmd;
+
+/*
+   ** total 128 bytes
+ */
+typedef struct _HIM
+{
+	u8 status;		/* device status, 0xFF not installed    */
+	u8 Target_ID;		/* target ID                            */
+	u8 type;		/* target type mapped to device name    */
+	u16 attrib;		/* lo 4bit: 1-Wide, 2-Sync, 4-Tag,      */
+	/*          8-removable,                */
+	/* hi 4bit: 10h do wide, 20h do sync,   */
+	/*          40h do tag, 0x80h - disc    */ 
+	/* bit 15: under BIOS, bit 8: > 1GB     */
+	 u8 op_param;		/* bit 7-5:period, bit4-0:offset        */
+	u8 drv_num;		/* current ID --> 8x for BIOS used only */
+	u8 sect_per_track;	/* sector per track                     */
+	u8 head_per_cyl;	/* sector per track                     */
+	u8 byte_per_sect;	/* byte per sector (BIOS is 512)        */
+	u8 link_count;	/* SRB link count:                      */
+	u8 * ASPICMDLink;	/* SRB link starting pointer            */
+	TaskCmd task[16];	/* array structure for each task        */
+	
+}
+DEVStruct;
+
+/*
+   ** total (4K+ 36) bytes for each adapter 
+ */
+typedef struct _Adapter
+{
+	u16 IoPort;		/* I/O Port address, 0 notsupport       */
+	u8 AdapterID;		/* Adapter scsi ID, default = 7         */
+	u8 first_disk_num;	/* first disk number under BIOS (82->2) */
+	u8 last_disk_num;	/* last disk number under BIOS          */
+	u8 time_factor;	/* used for device scam                 */
+
+	u8 IntrNum;		/* -1 NotSupport, otherwise IRQ         */
+	u16 hw_attr;		/* see eeprom def                       */
+	u16 sw_attr;		/* see eeprom def                       */
+	DEVStruct dev[16];	/* target device structure              */
+	u16 mbx_out_ptr;	/* mail box out pointer                 */
+	u8 HAParam[16];	/* host adapter parameters              */
+	u8 bios_install;	/* adapter has BIOS installed           */
+	u16 scam_type;		/* scam use              */
+	u8 scam_assigned;	/* scam use              */
+	u8 * Signature;	/* BIOS signature & new manager address */
+	u32 mbx_in_base;	/* mbx in base  logical address    */
+	u32 mbx_out_base;	/* mbx out base logical address    */
+	u32 devhdr_base;	/* devhdr base logical address     */
+	u32 taskq_base;	/* taskq base  logical address     */
+	u32 ptaskq_base;	/* taskq base physical address     */
+	u32 pdev_base;		/* taskq base physical address     */
+	u32 pmbi_base;		/* taskq base physical address     */
+	u32 pmbo_base;		/* taskq base physical address     */
+}
+Adapter, *PAdapter;
+
+/*
+   **  adapter information structure reserved for BIOS usage
+ */
+
+typedef struct _dev_parm
+{
+	u8 header;		/* header             */
+	u8 sect_per_track;	/* sector per tarck   */
+}
+target_parm, *ptarget_parm;
+/*
+   **  This structure is for BIOS used and read by driver
+ */
+typedef struct _Ada_data
+{
+	u8 drv_start;		/* first drive           */
+	u8 drv_end;		/* last drive            */
+	u16 timfact;		/* timing factor         */
+	u32 old_int13;		/* old int13 addr        */
+	u8 drive_id[8];	/* index= (8x-drv_start) */
+	u8 drive_num[16];	/* device number         */
+	target_parm dev[16];	/* device parameters     */
+	u8 allow_discon[16];	/* disconnect record     */
+	u16 scam_type;		/* scam use              */
+	u8 scam_assigned;	/* scam use              */
+	u8 adapter_id;	/* scam use              */
+	u32 signature;		/* BIOS signature        */
+}
+HA_data, *PHA_data;
+
+
+typedef struct _ScatGath
+{
+	u32 sg_address;	/* must be physical address */
+	u32 sg_length;		/* sg length     */
+}
+ScatGath, *PScatGath;
+
+/* length must be 32 bytes */
+typedef struct _risc_srb
+{
+	u8 Tag_info;		/* Tag information */
+	u8 SRB_flag;		/* SRB flag */
+	u8 DEV_Status;	/* SRB status */
+	u8 ScsiStatus;	/* scsi command status */
+	u32 CDB;		/* SCSI Command Block */
+	u32 CDBLength;		/* SCSI Command Length */
+	u32 SenseDataPtr;	/* auto sense pointer */
+	u32 Sense_Cmd_Ptr;	/* auto sense pointer */
+	u32 SG_ListPtr;	/* SG list  */
+	u8 SGNum;		/* S/G number */
+	u8 Identify;		/* Identify  message */
+	u8 Sense_LUN;		/* lun               */
+	u8 Sense_len;		/* sense len         */
+	u32 Cmd_sg_addr;	/* cmr sg ptr point to cmd */
+}
+RISC_SRB, *PRISC_SRB;
+
+
+typedef struct _SRB
+{				/* for SCSI */
+	RISC_SRB *risc_srb;	/* structure of SRB in RISC */
+	PAdapter AdapterPtr;	/* a pointer to adapter structure */
+	u8 LUN;		/* lun number */
+	u8 Tag_type;		/* tag type */
+	u8 Request_type;	/* request type */
+}
+SRB, *PSRB;
+
+typedef struct _sync_tbl
+{
+	int period;		/* parameter setting  */
+	int f_factor;		/* fast clock factor  */
+	int s_factor;		/* slow clock factor  */
+}
+sync_tbl;
+
+/*
+   **  total 12 bytes of sg header 
+ */
+typedef struct _dma_hrd
+{
+	u32 size;		/* region size        */
+	u32 offset;		/* offste             */
+	u16 segment;		/* segment            */
+	u16 revsed;		/* reserved           */
+	u16 num_avail;		/* number available   */
+	u16 num_used;		/* number used        */
+}
+DMA_HDR;
+
+/*
+   **  total 60 bytes (for each task in windows)
+ */
+typedef struct _dma_desc
+{
+	DMA_HDR dma_hdr;
+	ScatGath sg_list[6];
+}
+dma_desc, *Pdma_desc;
+
+/* Flags */
+#define SRB_TOHOST    8
+#define SRB_TOTARGET  0x10
+#define SRB_NEEDSDT   0x20
+#define SRB_SENSE     0x40	/* only for OSD      */
+#define SIZE_OF_SG    0x3C	/* size of sg table (60 bytes)  */
+
+/* 2. Target Error == SCSI Status */
+
+/* ============ function definiton ============ 
+   ** 1. INPUT:  PSRB
+   **    Start a scsi command. 
+   * */
+static void StartScsiCmd(PRISC_SRB);
+
+#define  CMD_INPROGRESS      0x01
+#define  CMD_DISCONCTED      0x02
+#define  CMD_DATAXFERED      0x04
+
+#define  TAR_TRUESG          0x01
+#define  NEEDSDTN            0x10
+
+#define  MSG_ABORT           0x06
+#define  MSG_RESET           0x0C
+#define  MSG_ALLOWDISC       0x40
+#define  MSG_IDENTIFY        0x80
+#define  MSG_EXTENDED        0x01
+#define  MSG_NOMSG           0x08
+#define  MSG_CMDCOMP         0x00
+#define  MSG_DISC            0x04
+#define  MSG_IGNWR           0x23
+#define  MSG_RESTPTR         0x03
+#define  MSG_SAVEPTR         0x02
+#define  MSG_REJECTED        0x07
+#define  MSG_INITRECVY       0x0f
+#define  MSG_LNKCMD          0x0a
+#define  MSG_LNKCMDTAG       0x0b
+#define  MSG_SIMPLEQUE       0x20
+#define  MSG_MDFDATPTR       0x00
+#define  MSG_SDTREQ          0x01
+#define  MSG_WDTREQ          0x03
+
+#define  SECOND              18	//ticks per second
+#define  DEV_EMPTY           0xFF
+
+typedef struct _DEVHDR
+{
+	u32 Updatedmap;	/* updated bitmap      */
+	u32 Startedmap;	/* start bitmap        */
+	u32 Currentmap;	/* current bitmap      */
+	u8 Task_Index;	/* current task index  */
+	u8 Request_type;	/* request type        */
+	u8 SpCmddone;		/* special commad done */
+	u8 Srb_loc;		/* location            */
+	u8 TagCmdCnt;		/* Pending task count  */
+	u8 ScsiID;		/* scsi ID             */
+	u8 DeviceNum;		/* device number       */
+	u8 WideMsg;		/* wide bus message    */
+	u8 SyncPeriod;	/* sync period         */
+	u8 SyncOffset;	/* sync offset         */
+	u8 RtnWideMsg;	/* return wide bus msg */
+	u8 RtnSyncPeriod;	/* Rtn sync period     */
+	u8 RtnSyncOffset;	/* rtn sync offset     */
+	u8 ChkSenseTask;	/* check sense task    */
+	u8 SenseCmd[6];	/* sense command       */
+}
+DevHdr, *PDevHdr;
+
+typedef struct _E2Prom
+{
+	u16 hw_parm;
+	u16 sw_parm;
+	u16 dev_parm[16];
+}
+E2prom, *PE2prom;
+
+/* parameter setting for parameter */
+#define		HW_ADAPTER_ID            0x0f
+#define		HW_PARITY_DISABLE        0x10
+#define		HW_TERMPWR_ENABLE        0x20
+#define		HW_FAST_CLOCK            0x40
+#define		HW_DIAG_ENABLE           0x80
+#define		HW_BURST_DISABLE         0x100
+#define		HW_CACHE_LINE_DISABLE    0x200
+#define		HW_MULTI_CACHE_DISABLE   0x400
+#define		HW_SCAM_ENABLE           0x800
+#define		HW_CACHE_LINE_SIZE_4	  0x1000
+#define		HW_CACHE_LINE_SIZE_8	  0x2000
+#define		HW_CACHE_LINE_SIZE_16	  0x3000
+#define		HW_FAST_SELECTION        0x4000
+#define		HW_BIOS_DISABLE          0x8000
+
+
+/* software setting      */
+#define		SW_REMOVABLE_SUPPORT     0x1
+#define		SW_OVER_1GB              0x2
+#define		SW_8_DRIVE_SUPPORT       0x4
+#define		SW_MAX_ID		 0x8
+#define		SW_DEVICE_CHANGED        0x10
+
+/* parameter setting     */
+#define		PM_UNDER_BIOS            0x8000
+#define		PM_BIOSSCAN_DISABLE      0x2000
+#define		PM_NEED_STARTCMD         0x1000
+#define		PM_DISCON_ENABLE         0x800
+#define		PM_SYNC_ENABLE           0x400
+#define		PM_TAG_ENABLE            0x200
+#define		PM_WIDEBUS_ENABLE        0x100
+#define		PM_TRANSFER_RATE         0xE0
+#define		PM_TRANSFER_OFFSET       0x1F
+
+/* ScsiFunc  */
+#define		SUPPORT_MORETHAN16M  0x40
+#define		SUPPORT_RESELECT     0x20
+#define		SUPPORT_SYNC         0x10
+#define		SUPPORT_LINKED       0x08
+#define		SUPPORT_CMDQUEUE     0x02
+#define		SUPPORT_SFTRE        0x01
+#define		SUPPORT_WIDEHOST     0x04
+#define		SUPPORT_PARCHK       0x80
+
+
+static unsigned int port_base = 0;
+static unsigned int CFG_BASE = 0;
+static unsigned int interrupt_level = 0;
+
+/* period table 
+
+   static sync_tbl  period_tbl[8]= {
+   { 0, 12, 12,},
+   { 1, 16, 18,},
+   { 2, 20, 25,},
+   { 3, 25, 31,},
+   { 4, 29, 37,},
+   { 5, 33, 43,},
+   { 6, 50, 50,},
+   { 7, 58, 75,},
+   };
+ */
+
+/* pointer to scsi host struc for each HA */
+/* needs change for multiple HA support */
+
+static struct Scsi_Host *tripace_host;
+
+/* Tc2550 mailbox data structures */
+static unsigned char tcmbdata[Q_FILE_SIZE + DEVHDR_SIZE + MBOX_SIZE + 64];
+/* static alloc of sg table for all tasks */
+/* dynamic memory is giving problems */
+static unsigned char table[8 * MAXSGENT * MAXSRBS * MAXTARGET + 4];
+
+
+/* global variables */
+/* logical addresses of mailbox in/out,dev header base,task que base */
+
+static unsigned char *startsgptr;
+static unsigned long pstartsgptr;
+
+
+static u32 mbx_in_base, mbx_out_base, devhdr_base, taskq_base;
+static u8 TargetID;
+
+
+/* hostadapter structure-as of now we have a single adapter */
+Adapter HostAdapter[1];
+unsigned char ReqType, targetID, HANumber, Index, HostID;
+
+/* variable for term power in case of /T option */
+static unsigned short int EN_TERM_PWR = TERM_PWR_EN;
+static unsigned short fast_clk = 0;
+static unsigned short par_off = 0;
+static unsigned short discon = 1;
+static unsigned short syncflag = 1;
+static unsigned short tagflag = 0;
+
+
+static int makecode(unsigned, unsigned);
+static void Init_struc(int);
+static int download_RISC_code(void);
+static void init_chip_reg(void);
+
+static PRISC_SRB search(PAdapter);
+u32 insert_bit(u32, short int);
+static void Get_Base(Adapter *);
+
+void tc2550_intr(int, void *, struct pt_regs *);
+
+static void internal_done(Scsi_Cmnd *);
+
+int tc2550_command(Scsi_Cmnd *);
+
+static int tc2550_pci_bios_detect(int *irq, int *iobase)
+{
+	int error;
+	unsigned char pci_bus, pci_dev_fn;	/* PCI bus & device function */
+	unsigned char pci_irq;	/* PCI interrupt line */
+	unsigned int pci_base;	/* PCI I/O base address */
+	unsigned short pci_vendor, pci_device;	/* PCI vendor & device IDs */
+
+
+	/* We will have to change this if more than 1 PCI bus is present and the
+	   tripace scsi host is not on the first bus (i.e., a PCI to PCI bridge,
+	   which is not supported by bios32 right now anyway). */
+
+	pci_bus = 0;
+
+	for (pci_dev_fn = 0x0; pci_dev_fn < 0xff; pci_dev_fn++)
+	{
+		pcibios_read_config_word(pci_bus,
+					 pci_dev_fn,
+					 PCI_VENDOR_ID,
+					 &pci_vendor);
+
+		if (pci_vendor == 0x1190)
+		{
+			pcibios_read_config_word(pci_bus,
+						 pci_dev_fn,
+						 PCI_DEVICE_ID,
+						 &pci_device);
+
+			if (pci_device == 0xc731)
+			{
+				/* Break out once we have the correct device.  If othertrip 
+				   PCI devices are added to this driver we will need to add
+				   an or of the other PCI_DEVICE_ID here. */
+				printk(KERN_INFO "Tripace TC-2550x based PCI SCSI Adapter detected\n");
+				break;
+			} else
+			{
+				/* If we can't finl an tripace scsi card we give up. */
+				return 0;
+			}
+		}
+	}
+
+/* vendor id not found */
+
+	if (pci_device != 0xc731)
+	{
+		printk(KERN_INFO "Tripace TC-2550x - No Host Adapter Detected \n");
+		return (0);
+	}
+	/* We now have the appropriate device function for the tripace board so we
+	   just read the PCI config info from the registers.  */
+
+	if ((error = pcibios_read_config_dword(pci_bus,
+					       pci_dev_fn,
+					       PCI_BASE_ADDRESS_0,
+					       &pci_base))
+	    || (error = pcibios_read_config_byte(pci_bus,
+						 pci_dev_fn,
+						 PCI_INTERRUPT_LINE,
+						 &pci_irq)))
+	{
+		printk(KERN_ERR "Tripace TC-2550x not initializing"
+		       " due to error reading configuration space\n");
+		return 0;
+	} else
+	{
+		printk(KERN_INFO "TC-2550x PCI: IRQ = %u, I/O base = %X\n",
+		       pci_irq, pci_base);
+
+		/* Now we have the I/O base address and interrupt from the PCI
+		   configuration registers.  
+		 */
+
+		*irq = pci_irq;
+		*iobase = (pci_base & 0xfff8);
+		CFG_BASE = *iobase;
+
+		printk(KERN_INFO "TC-2550x Driver version 1.00.000 (904)\n");
+		printk(KERN_INFO "TC-2550x: IRQ = %d, I/O base = 0x%X\n", *irq, *iobase);
+		return 1;
+	}
+	return 0;
+}
+
+static void init_chip_reg(void)
+{
+	int i, val, base;
+	unsigned long tick;
+
+	outw(HALT_RISC, CONTROL_REG);
+	base = CFG_BASE + 0x20;
+	for (i = 0; i < 16; i++)
+		outw(0, base + i * 2);
+	outw(EN_TERM_PWR, STATUS_INT_REG);
+	outw(RESET_SCSI_BUS, STATUS_INT_REG);
+
+	udelay(50);		/*wait for 50 micro secs */
+
+	outw(EN_TERM_PWR, STATUS_INT_REG);
+	val = (HALT_RISC | RISC_CHIP_RESET);
+	outw(val, CONTROL_REG);
+	outw(HALT_RISC, CONTROL_REG);
+	val = inw(STATUS_INT_REG);
+	tick = 0;
+	while ((val & 0x40) == 0 && tick < 0x3fff)
+	{
+		val = inw(STATUS_INT_REG);
+		tick += 1;
+	};
+	if (tick == 0x3fff)
+		printk(KERN_DEBUG "val= %x  \n\r", val);
+	outw(EN_TERM_PWR, STATUS_INT_REG);
+	outw(0, SCSI_CONTROL);
+
+/* delay for 50 micro secs */
+	udelay(50);
+}
+
+static int download_RISC_code(void)
+{
+	unsigned short i, j, fast = 0;
+	unsigned short hi, low, base;
+	long tmp;
+	unsigned long start_time;
+
+	i = inw(CONTROL_REG);
+	if (i & 0x40)
+		fast = 1;
+	outw(HALT_RISC, CONTROL_REG);
+
+/* Ravi modified for sanity check dec 17 1998 */
+	start_time = jiffies;
+
+	do
+	{
+		if ((jiffies - start_time) > 5 * HZ)
+		{
+			printk(KERN_ERR "tc2550: Download failure.\n");
+			return 1;
+		}
+		i = inw(STATUS_INT_REG);
+	}
+	while ((i & 0x40) == 0);
+
+	outw(HALT_RISC + EN_MEMORY_WRITE, CONTROL_REG);
+	outw(EN_TERM_PWR + DIS_INTR, STATUS_INT_REG);
+
+	/*  download load RISC code
+	 */
+	outw(0, PC);
+	for (i = 0; i < ucode_size; i++)
+		outl(ucode_instruction[i], CURRENT_INST);
+	/*
+	   //  checksum checking (word)
+	 */
+	base = 0;
+	for (i = 0; i < ucode_size; i++)
+	{
+		outw(i * 4, PC);
+		tmp = inl(PROGRAM_DATA_REG);
+		hi = tmp >> 16;
+		low = (tmp & 0xffff);
+		base = base + (hi + low);
+	}
+	outw(HALT_RISC + RISC_CHIP_RESET, CONTROL_REG);
+	outw(HALT_RISC, CONTROL_REG);
+	if (fast_clk)
+		outw((HALT_RISC | FAST_CLOCK), CONTROL_REG);
+	if (par_off)
+	{
+		i = inw(CONTROL_REG);
+		outw((i | DIS_SCSI_PARITY), CONTROL_REG);
+	}
+	outw(EN_TERM_PWR, STATUS_INT_REG);
+	/*ravi
+	 */
+	if ((unsigned short) (ucode_checksum + base) != 0)
+	{
+		printk(KERN_ERR "tc2550: Checksum Error During Code Download\n");
+		return (1);
+	};
+	/*
+	   load vector table
+	 */
+	j = 0;
+	base = CFG_BASE;
+	for (i = 0, j = 0; i < 15; i++, j = j + 2)
+		outw(ucode_vector[i], (base + j));
+	outw(0, SCSI_CONTROL);
+	return (0);
+}
+
+int tc2550_detect(Scsi_Host_Template * tpnt)
+{
+	int flag = 0;
+	int retcode;
+	struct Scsi_Host *shpnt;
+	unsigned long flags;
+	unsigned int mod4;
+
+
+	flag = tc2550_pci_bios_detect(&interrupt_level, &port_base);
+	if (!flag)
+		return (0);
+
+	init_chip_reg();	/* chip Tc-2550 initialize */
+	flag = download_RISC_code();
+	if (flag == 0)
+	{
+		printk(KERN_INFO "tc2550: Successful F/W download on TC-2550x\n");
+	}
+/* now do a scsi register and get scsi host ptr */
+
+	shpnt = scsi_register(tpnt, 0);
+
+	save_flags(flags);
+	cli();
+	retcode = request_irq(interrupt_level,
+			      tc2550_intr, SA_INTERRUPT, "tripace", NULL);
+	if (retcode)
+	{
+		printk(KERN_ERR "tc2550: Unable to allocate IRQ for Tripace TC-2550x based SCSI Host Adapter.\n");
+		goto unregister;
+	}
+	/* For multiple HA we need to change all this */
+
+
+	tripace_host = shpnt;
+	shpnt->io_port = CFG_BASE;
+	shpnt->n_io_port = 0xfc;	/* Number of bytes of I/O space used */
+	shpnt->dma_channel = 0;
+	shpnt->irq = interrupt_level;
+
+	restore_flags(flags);
+
+
+	/* log i/o ports with the kernel */
+	request_region(port_base, 0xfc, "tripace");
+
+/* when we support multiple HA ,we need to modify */
+	Init_struc(0);		/* init mailboxes for one adapter */
+/* sg table init */
+
+	/*  get physical address */
+	startsgptr = (unsigned char *) table;
+	pstartsgptr = virt_to_phys((unsigned char *) table);
+	mod4 = pstartsgptr % 4;
+	if (mod4)
+	{
+		pstartsgptr += (4 - mod4);
+		startsgptr += (4 - mod4);
+	}
+	return (0);
+
+
+unregister:
+	scsi_unregister(shpnt);
+	return (0);
+}
+
+/****************************************************************************
+**   Init chip registers and allocate required memory space
+****************************************************************************/
+
+static void Init_struc(int id)
+{
+	u32 pmbx_in_base, pmbx_out_base, pdevhdr_base, ptaskq_base;
+	unsigned long paddr;
+	char *laddr;
+	unsigned short modulo;
+
+/* setup ioport address and irq */
+
+	HostAdapter[id].IoPort = (u16) CFG_BASE;
+	HostAdapter[id].IntrNum = (u8) interrupt_level;
+
+	laddr = tcmbdata;
+	paddr = virt_to_phys(tcmbdata);
+/* adjust phys address to 32 byte boundary */
+	modulo = paddr % 32;
+	if (modulo)
+	{
+		paddr = paddr + 32 - modulo;
+		laddr = laddr + 32 - modulo;
+	}
+	/*  logical address  */
+	mbx_in_base = (u32) laddr;
+	pmbx_in_base = paddr;
+	HostAdapter[id].mbx_in_base = mbx_in_base;
+
+	laddr += 32;
+	paddr += 32;
+	mbx_out_base = (u32) laddr;
+	pmbx_out_base = paddr;
+	HostAdapter[id].mbx_out_base = mbx_out_base;
+	memset((char *) mbx_in_base, 0, 48);
+
+	laddr += 32;
+	paddr += 32;
+	devhdr_base = (u32) laddr;
+	pdevhdr_base = paddr;
+	HostAdapter[id].devhdr_base = devhdr_base;
+	memset((char *) devhdr_base, 0, 512);
+
+
+	laddr += 512;
+	paddr += 512;
+
+	taskq_base = (u32) laddr;
+	ptaskq_base = paddr;
+	HostAdapter[id].taskq_base = taskq_base;
+	memset((char *) taskq_base, 0, Q_FILE_SIZE);
+
+	HostAdapter[id].ptaskq_base = (u32) ptaskq_base;
+	HostAdapter[id].pdev_base = (u32) pdevhdr_base;
+	HostAdapter[id].pmbi_base = (u32) pmbx_in_base;
+	HostAdapter[id].pmbo_base = (u32) pmbx_out_base;
+
+	outl(pmbx_in_base, MBX_IN_BASE);
+	outl(pmbx_out_base, MBX_OUT_BASE);
+	outl(pdevhdr_base, DEV_HDR_BASE);
+	outl(ptaskq_base, TASK_Q_BASE);
+	outb(0, MBX_IN_INDEX);
+	outb(0, MBX_OUT_INDEX);
+	/* clear mailbox out pointer */
+	HostAdapter[id].mbx_out_ptr = 0;
+}
+
+
+/***************************************************************************
+**   adjust bitmap position
+**   Input : old bitmap,  new bit location 
+**   Output: return with new  bitmap layout
+****************************************************************************/
+
+extern __inline__ u32 insert_bit(u32 bits, short int loc)
+{
+	u32 lo = 0;
+
+	lo = 1;
+	lo <<= loc;
+
+
+	return ((lo | bits));
+}
+
+
+/***************************************************************************
+**   Get the base address and structures of current host adapter
+**   Input  : adapter structure pointer
+**   Output : none
+****************************************************************************/
+
+static void Get_Base(Adapter * padapter)
+{
+
+	CFG_BASE = padapter->IoPort;
+	interrupt_level = (u16) padapter->IntrNum;
+	mbx_in_base = padapter->mbx_in_base;
+	mbx_out_base = padapter->mbx_out_base;
+	devhdr_base = padapter->devhdr_base;
+	taskq_base = padapter->taskq_base;
+
+}
+
+
+
+/***************************************************************************
+**  name   : search()
+**  Desc   : search an available SRB from taskQ
+**  Input  : adapter structure pointer
+**  Output : 1. a risc structure space pointer or 0 for non available
+**           2. index of task location
+****************************************************************************/
+static PRISC_SRB search(PAdapter pa)
+{
+	PRISC_SRB rsrb;
+	short int i;
+	unsigned long flags;
+
+	Get_Base(pa);
+	rsrb = (PRISC_SRB) (taskq_base + TargetID * 16 * sizeof(RISC_SRB));
+	/* check attrib in case drive doesn't support SYNC xfer */
+
+	if ((pa->dev[TargetID].attrib & DO_SYNC_NEGO) == 0)
+		ReqType = 0;
+	save_flags(flags);
+	cli();
+
+	Index = 0;
+	for (i = 0; i < 16; i++, rsrb++)
+		if (rsrb->SRB_flag == SRB_DONE)
+			break;
+	if (i == 16)
+	{
+		restore_flags(flags);
+		return (0);
+	}
+	Index = i;
+	restore_flags(flags);
+	memset((char *) rsrb, 0, 32);
+	rsrb->SRB_flag = SRB_ASSIGNED;	/* mark for use */
+	if ((pa->dev[TargetID].attrib & 0x44) == 0x44)
+		rsrb->Tag_info = 0x20;
+	return (rsrb);
+}
+
+/***************************************************************************
+**  Name   : StartSCSICmd()
+**  func   : 1. all commands passed through here are regular
+**           2. fill in device structure bitmap && start RISC
+**  Input  : risc srb structure, Index, ReqType
+**  Output : none
+****************************************************************************/
+static void StartScsiCmd(PRISC_SRB rsrb)
+{
+	DevHdr *dev;
+	u16 status;
+	PAdapter pa;
+	unsigned short val;
+	u8 t, find_id, find_last, ch = 0;
+	char *mptr;
+/* Request sense CDB */
+	char RequestSense[6] =
+	{0x03, 0x00, 0x00, 0x00, 0x0e, 0};
+
+	pa = (PAdapter) & HostAdapter[HANumber];
+	pa->dev[TargetID].task[Index].CmdInProcess = 1;		/* command in process */
+	dev = (DevHdr *) (devhdr_base + TargetID * sizeof(DevHdr));
+	dev->Srb_loc = Index;
+	dev->Request_type = ReqType;	/* set request type */
+	dev->Updatedmap = insert_bit(dev->Updatedmap, Index);
+	for (val = 0; val < 6; val++)
+		dev->SenseCmd[val] = RequestSense[val];
+	rsrb->Sense_Cmd_Ptr = virt_to_phys(&dev->SenseCmd[0]);
+
+	if (ReqType >= 2)
+	{
+		if (ReqType & 0x2)
+			dev->WideMsg = 0x1;
+		else
+		{
+/*        printf("firing sync nego");
+   //ravi 10/3/98 -ultra support in parse 
+
+   if(fast_clk)
+   dev->SyncPeriod =   period_tbl[(ultra[TargetID])].f_factor;
+   else
+   dev->SyncPeriod =   period_tbl[(ultra[TargetID])].s_factor;
+ */
+
+			if (pa->dev[TargetID].attrib & 0x1)
+				dev->SyncOffset = W_MAX_OFFSET;
+			else
+				dev->SyncOffset = MAX_OFFSET;
+		}
+	}
+	mptr = (char *) mbx_in_base;
+	find_id = 0xff;
+	find_last = 0xff;
+	for (t = 0; t < 15; t++)
+	{
+		ch = *mptr++;
+		if ((ch & 0xf) == TargetID)
+		{
+			find_id = t;
+			break;
+		}
+	}
+
+	mptr = (char *) mbx_in_base;
+	for (t = 0; t < 15; t++)
+	{
+		ch = *mptr++;
+		if (ch & 0x80)
+		{
+			find_last = t;
+			break;
+		}
+	}
+	val = inw(STATUS_INT_REG);
+	mptr = (char *) mbx_in_base;
+	if (find_id != 0xff && find_last != 0xff)
+	{
+		if (find_id < find_last)
+			*(char *) (mptr + find_id) |= 0x10;
+		else
+		{
+			*(char *) (mptr + find_last) &= 0x7f;
+			*(char *) (mptr + find_id) |= 0x90;
+		}
+	} else if (find_last != 0xff && find_id == 0xff)
+	{
+		*(char *) (mptr + find_last) &= 0x7f;
+		find_last = find_last + 1;
+		*(char *) (mptr + find_last) = (0x90 | TargetID);
+	} else if (find_last == 0xff)
+		*(char *) mptr = (0x90 | TargetID);
+
+
+	ReqType = 0;
+	/*
+	   //  restart the RISC if it is halted before
+	 */
+
+	rsrb->SRB_flag = SRB_READY;
+
+
+	if (val & RISC_HALT)
+	{
+		outw(ucode_start, PC);	/* set pc counter */
+		status = inw(CONTROL_REG);	/* clear halt status */
+		outw((status & ~HALT_RISC), CONTROL_REG);
+	}
+#ifdef DEBUG
+	printk(KERN_DEBUG " start scsi issued \n");
+#endif
+
+}
+
+static void internal_done(Scsi_Cmnd * SCpnt)
+{
+	SCpnt->SCp.Status++;
+}
+
+int tc2550_command(Scsi_Cmnd * SCpnt)
+{
+	tc2550_queue(SCpnt, internal_done);
+
+	SCpnt->SCp.Status = 0;
+	while (!SCpnt->SCp.Status)
+		barrier();
+	return SCpnt->result;
+}
+
+int tc2550_queue(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
+{
+
+	PAdapter pa;
+	PRISC_SRB rsrb;
+	int val;
+	struct scatterlist *sgpnt;
+
+
+	ScatGath *riscsgptr;
+
+
+	int i;
+	unsigned int nentries;
+
+	pa = (PAdapter) & HostAdapter[HANumber];
+	Get_Base(pa);
+	val = 0xaa;
+	TargetID = SCpnt->target;
+	HostID = 7;
+	/* the following code is for corel compatibility */
+	if (SCpnt->lun != 0 ||
+	    (TargetID == HostID))
+	{
+		SCpnt->result = DID_BAD_TARGET << 16;
+		done(SCpnt);
+		return (0);
+	};
+	/* Error if more than 16 tasks /target if no space is available  */
+
+	if ((rsrb = search(pa)) == 0)
+	{
+		SCpnt->result = DID_ERROR << 16;
+		done(SCpnt);
+		return (0);
+	};
+
+/*Ravi -modified for hostid 16/12/98 */
+	outb((u8) ((TargetID << 4) | HostID), SCSI_ID_REG);
+
+
+	rsrb->CDBLength = SCpnt->cmd_len;
+/* get the physical address of CDB */
+	rsrb->CDB = virt_to_phys((unsigned char *) SCpnt->cmnd);
+
+
+	if (discon)
+
+		rsrb->Identify = 0xC0;
+	else
+		rsrb->Identify = 0x80;
+
+/* scatter gather processing */
+
+	nentries = SCpnt->use_sg;
+	if (nentries == 0)
+		nentries = 1;
+	rsrb->SGNum = nentries;
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "sgentries = %d\n", nentries);
+#endif
+
+/*allocate mem for scatter gather table at 32bit boundary */
+	SCpnt->host_scribble = startsgptr + 8 * MAXSGENT * MAXSRBS * TargetID;
+
+	/*(unsigned char *)scsi_malloc(4096); */
+
+	sgpnt = (struct scatterlist *) SCpnt->request_buffer;
+
+	riscsgptr = (PScatGath) (SCpnt->host_scribble);
+	if (riscsgptr == NULL)
+		panic("tripace: unable to allocate DMA memory\n");
+
+
+	/* fill physical address of scatter-gather list */
+	rsrb->SG_ListPtr = virt_to_phys(SCpnt->host_scribble);
+	rsrb->Cmd_sg_addr = virt_to_phys(rsrb + 4);
+
+	if (SCpnt->use_sg)
+	{
+		for (i = 0; i < SCpnt->use_sg; i++)
+		{
+			if (sgpnt[i].length == 0 || SCpnt->use_sg > 255)
+			{
+				unsigned char *ptr;
+				printk(KERN_ERR "tc2550: Bad segment list supplied to Tripace.c (%d, %d)\n", SCpnt->use_sg, i);
+				for (i = 0; i < SCpnt->use_sg; i++)
+				{
+					printk(KERN_ERR "%d: %x %x %d\n", i, (unsigned int) sgpnt[i].address, (unsigned int) sgpnt[i].alt_address,
+					       sgpnt[i].length);
+				};
+				printk(KERN_ERR "RISCGPTR %x: ", (unsigned int) riscsgptr);
+				ptr = (unsigned char *) &riscsgptr[i];
+				for (i = 0; i < 18; i++)
+					printk("%02x ", ptr[i]);
+				panic("Tripace tc-2550x driver!");
+			};
+
+			riscsgptr[i].sg_address = (u32) sgpnt[i].address;
+			riscsgptr[i].sg_length = sgpnt[i].length;
+		};
+	} else
+	{
+		riscsgptr[0].sg_address = (u32) SCpnt->request_buffer;
+		riscsgptr[0].sg_length = SCpnt->request_bufflen;
+	};
+
+
+/* fill sense data pointer and len */
+
+	rsrb->Sense_len = sizeof(SCpnt->sense_buffer);
+	rsrb->SenseDataPtr = virt_to_phys(SCpnt->sense_buffer);
+
+/* store scsi command pointer for use in intr routine */
+	pa->dev[TargetID].task[Index].REQ_Header = (u8 *) SCpnt;
+	SCpnt->scsi_done = done;
+
+	/* pa->dev[TargetID].task[Index].complete = CompleteIORequest; */
+	ReqType = 0;
+	StartScsiCmd(rsrb);
+	return 0;
+}
+
+int tc2550_reset(Scsi_Cmnd * SCpnt)
+{
+	return 0;
+
+}
+
+#include "sd.h"
+
+int tc2550_biosparam(Scsi_Disk * disk, int dev, int *info_array)
+{
+	return 0;
+
+}
+
+int tc2550_abort(Scsi_Cmnd * SCpnt)
+{
+
+	return 0;
+}
+
+
+const char *tc2550_info(struct Scsi_Host *ignore)
+{
+
+	return 0;
+}
+
+void tc2550_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	void (*my_done) (Scsi_Cmnd *) = NULL;
+
+	int val, id, map = 0, tmap, mbx_out_ptr;
+	u8 loc = 0;
+	PRISC_SRB rsrb;
+	DevHdr *dev;
+	char *ptr0;
+	PAdapter padapter;
+/* 
+   int i ;
+   unsigned long flags ;
+ */
+	unsigned int memsize;
+	Scsi_Cmnd *SCtmp;
+	unsigned devstat = 0;
+	unsigned scsistat = 0;
+	long start_time;
+
+#ifdef DEBUG
+	printk("interrupt registered \n");
+#endif
+
+/*
+   save_flags(flags);
+   cli();
+ */
+
+	/* multiple HA? not supported now! */
+	HANumber = 0;
+	padapter = (PAdapter) & HostAdapter[HANumber];
+	Get_Base(padapter);
+
+/* disable interrupts */
+	val = inw(STATUS_INT_REG);
+
+	udelay(10);
+
+	val |= 0x20;		/* clear interrupt pending */
+	outw(val, STATUS_INT_REG);
+
+	udelay(10);
+
+	val |= 0x22;		/* disable interrupt */
+
+	outw(val, STATUS_INT_REG);
+	/*               
+	   // if RISC is in halt state then find out why ?
+	 */
+	tmap = inw(STATUS_INT_REG);
+/* The following code needs to be added when we intro sync /wide nego */
+/*
+   if(tmap & RISC_HALT) {
+   tmap= risc_halt_check();
+   if(tmap) return(0xff);
+   };
+ */
+
+
+	mbx_out_ptr = padapter->mbx_out_ptr;
+
+	val = *(u16 *) (mbx_out_base + mbx_out_ptr);
+
+	while (val & 0x80)
+	{
+		loc = (char) (val & 0x7f);
+		id = val & 0xff00;
+		id >>= 8;
+		TargetID = id;
+		dev = (DevHdr *) (devhdr_base + TargetID * sizeof(DevHdr));
+		/*(u16  *)(mbx_out_base+ mbx_out_ptr)= (val& 0xff7f); */
+		rsrb = (PRISC_SRB) (taskq_base + (id * 16 + loc) * sizeof(RISC_SRB));
+		Index = loc;
+		devstat = rsrb->DEV_Status;
+		scsistat = rsrb->ScsiStatus;
+
+/*      (*padapter->dev[TargetID].task[loc].complete)(rsrb); */
+
+		*(u16 *) (mbx_out_base + mbx_out_ptr) = (val & 0xff7f);
+
+		padapter->dev[TargetID].task[loc].CmdInProcess = 0;
+		mbx_out_ptr += 2;
+		if (mbx_out_ptr == 32)
+			mbx_out_ptr = 0;
+		padapter->mbx_out_ptr = mbx_out_ptr;
+		/*
+		 *	Clear init bimap if no more tasks are waiting
+		 */
+		map = 1;
+		dev->Updatedmap = (dev->Updatedmap ^ (map << loc));
+		if ((dev->Updatedmap & 0x0000ffff) == 0)
+		{
+			ptr0 = (char *) mbx_in_base;
+			for (map = 0; map < 16; map++)
+				if ((*ptr0 & 0xf) == (char) id)
+				{
+					*ptr0 = (*ptr0 & 0xef);
+					break;
+				} else
+					ptr0++;
+		}
+		val = *(u16 *) (mbx_out_base + mbx_out_ptr);
+
+		rsrb->SRB_flag = SRB_DONE;	/* mark done */
+	}
+
+	/* stop RISC if mailbox is empty  */
+
+	ptr0 = (char *) mbx_in_base;
+	for (map = 0; map < 16; map++)
+		if (*ptr0 & 0x10)
+			break;
+	if (map == 16)
+	{
+		tmap = inw(CONTROL_REG);
+		outw((tmap | HALT_RISC), CONTROL_REG);
+
+/*Ravi modified to introduce sanity check&time out dec 16 1998 */
+
+		start_time = jiffies;
+		
+		do
+		{
+			if ((start_time - jiffies) > 5 * HZ)
+			{
+				printk(KERN_ERR "tc2550: TC-2550x Controller Failure\n");
+				return;
+			}
+			tmap = inw(STATUS_INT_REG);
+		}
+		while((tmap & RISC_HALT) == 0);
+	}
+	SCtmp = (Scsi_Cmnd *) padapter->dev[TargetID].task[loc].REQ_Header;
+
+	if (!SCtmp || !SCtmp->scsi_done)
+	{
+		printk(KERN_ERR "tc2550: Tripace_Intr_Handle: Unexpected Interrupt\n");
+		return;
+	}
+	memsize = 255 * sizeof(struct _ScatGath) + 4;
+	my_done = SCtmp->scsi_done;
+	/*if (SCtmp->host_scribble)
+	   scsi_free(SCtmp->host_scribble,4096); */
+
+
+	padapter->dev[TargetID].task[loc].REQ_Header = NULL;
+	SCtmp->result = makecode(devstat, scsistat);
+/*enable chip interrupt signal */
+	val = inw(STATUS_INT_REG);
+	udelay(25);		/* delay for 25 micros */
+
+	val &= 0xfd;		/* enable interrupt-bit1=0 in status-int reg */
+	outw(val, STATUS_INT_REG);
+
+	my_done(SCtmp);		/* inform mid layer that scsi command is over */
+/*
+   restore_flags(flags);
+ */
+
+}
+
+/* called from init/main.c */
+void tripace_setup(char *str, int *ints)
+{
+	switch (ints[0])
+	{
+
+	case 0:
+
+		printk(KERN_INFO "tc2550: No Arguments In Command Line:Assuming Defaults\n");
+		break;
+
+	case 1:
+		fast_clk = ints[1];
+		break;
+
+	case 2:
+		fast_clk = ints[1];
+		discon = ints[2];
+		break;
+
+	case 3:
+		fast_clk = ints[1];
+		discon = ints[2];
+		syncflag = ints[3];
+		break;
+
+	case 4:
+		fast_clk = ints[1];
+		discon = ints[2];
+		syncflag = ints[3];
+		tagflag = ints[4];
+	}
+
+#ifdef DEBUG
+	printk("fast_clk = %d,discon = %d,syncflag =%d,tagflag=%d\n",
+	       fast_clk, discon, syncflag, tagflag);
+	printk("fast_clk = %d,discon = %d,syncflag =%d,tagflag=%d\n",
+	       fast_clk, discon, syncflag, tagflag);
+	printk("fast_clk = %d,discon = %d,syncflag =%d,tagflag=%d\n",
+	       fast_clk, discon, syncflag, tagflag);
+	printk("fast_clk = %d,discon = %d,syncflag =%d,tagflag=%d\n",
+	       fast_clk, discon, syncflag, tagflag);
+#endif
+
+}
+
+static int makecode(unsigned hosterr, unsigned scsierr)
+{
+	switch (hosterr)
+	{
+	case 0x0:
+		hosterr = 0;
+		break;
+
+	case SEL_TIME_OUT:	/* Selection time out-The initiator selection or target
+				   reselection was not complete within the SCSI Time out period */
+		hosterr = DID_TIME_OUT;
+		break;
+
+	case ERR_PARITY:	/* parity error */
+
+		hosterr = DID_PARITY;
+		break;
+
+	case ERR_OVERRUN:	/* Data overrun/underrun-The target attempted to transfer more data
+				   than was allocated by the Data Length field or the sum of the
+				   Scatter / Gather Data Length fields. */
+
+	case ERR_BUSFREE:	/* Unexpected bus free-The target dropped the SCSI BSY at an unexpected time. */
+
+
+	case ERR_PHASE:	/* Target bus phase sequence failure-An invalid bus phase or bus
+				   phase sequence was requested by the target. */
+
+		hosterr = DID_ERROR;	/* Couldn't find any better */
+		break;
+
+	default:
+		hosterr = DID_ERROR;
+		printk(KERN_ERR "tc2550: Makecode: Unknown Hoststatus %x\n", hosterr);
+		break;
+	}
+	return scsierr | (hosterr << 16);
+}
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/tripace.h linux/drivers/scsi/tripace.h
--- linux.vanilla/drivers/scsi/tripace.h	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/tripace.h	Mon Dec 21 08:15:10 1998
@@ -0,0 +1,57 @@
+/* tc2550.h -- Header for tripace TC-2550x PCI-SCSI HA
+ * Created:  Tue June 9 by chennai team of Tripace Ravi 
+ * Author: Ravi ravi01@md2.vsnl.net.in 
+ * Copyright 1998 Tripace B.V 
+ *
+
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+#define MAXSGENT  16
+
+#ifndef _TRIPACE_H
+#define _TRIPACE_H
+
+int        tc2550_detect( Scsi_Host_Template * );
+int        tc2550_command( Scsi_Cmnd * );
+int        tc2550_abort( Scsi_Cmnd * );
+const char *tc2550_info( struct Scsi_Host * );
+int        tc2550_reset( Scsi_Cmnd * ); 
+int        tc2550_queue( Scsi_Cmnd *, void (*done)(Scsi_Cmnd *) );
+int        tc2550_biosparam(Disk *,int,int *) ;
+
+#define TC2550       { NULL,                             \
+		       NULL,                             \
+		       NULL,		                 \
+		       "tripace",                        \
+		       PROC_SCSI_TRIPACE,          \
+		       NULL,				 \
+		       tc2550_detect,              \
+		       NULL,				 \
+		       tc2550_info,                \
+		       tc2550_command,             \
+		       tc2550_queue,               \
+		       tc2550_abort,               \
+		       tc2550_reset,               \
+		       NULL,                             \
+		       tc2550_biosparam,           \
+		       1, 				 \
+		       7, 				 \
+		       MAXSGENT, 				 \
+		       1, 				 \
+		       0, 				 \
+		       0, 				 \
+		       DISABLE_CLUSTERING }
+#endif
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/tripace_mcode.h linux/drivers/scsi/tripace_mcode.h
--- linux.vanilla/drivers/scsi/tripace_mcode.h	Thu Jan  1 01:00:00 1970
+++ linux/drivers/scsi/tripace_mcode.h	Wed Feb 24 21:49:18 1999
@@ -0,0 +1,483 @@
+u16 ucode_vector[]      = { 
+			0x1e0,       /* vector  0 */
+                        0x0,       /* vector  1 */
+                        0x1d8,       /* vector  2 */
+                        0x500,       /* vector  3 */
+                        0x2f0,       /* vector  4 */
+                        0x2e4,       /* vector  5 */
+                        0x604,       /* vector  6 */
+                        0x258,       /* vector  7 */
+                        0x30c,       /* vector  8 */
+                        0x45c,       /* vector  9 */
+                        0x368,       /* vector 10 */
+                        0x4a4,       /* vector 11 */
+                        0x514,       /* vector 12 */
+                        0x520,       /* vector 13 */
+                        0x70c,       /* vector 14 */
+                        0x728        /* vector 15 */
+                      };
+unsigned short ucode_start         =   0x0;         /* starting point (9 bit only) */
+
+unsigned short ucode_size          =   0x1cb;       /* total number of instructions */
+unsigned short ucode_checksum      =   0x87d0;      /* checksum */         
+u32 ucode_instruction[] = { 
+			0x1c0e0078,  /* line   1 --- addr   0 */
+                        0x45800000,  /* line   2 --- addr   4 */
+                        0x08070004,  /* line   3 --- addr   8 */
+                        0x7d040000,  /* line   4 --- addr   c */
+                        0x08040000,  /* line   5 --- addr  10 */
+                        0x9c000f00,  /* line   6 --- addr  14 */
+                        0x59500000,  /* line   7 --- addr  18 */
+                        0x100e0010,  /* line   8 --- addr  1c */
+                        0xcc000000,  /* line   9 --- addr  20 */
+                        0x1c0e0078,  /* line  10 --- addr  24 */
+                        0x6500000d,  /* line  11 --- addr  28 */
+                        0x0c070041,  /* line  12 --- addr  2c */
+                        0x08030016,  /* line  13 --- addr  30 */
+                        0x9c007700,  /* line  14 --- addr  34 */
+                        0x6100000d,  /* line  15 --- addr  38 */
+                        0x78000004,  /* line  16 --- addr  3c */
+                        0x7c000000,  /* line  17 --- addr  40 */
+                        0xdc000100,  /* line  18 --- addr  44 */
+                        0x60000008,  /* line  19 --- addr  48 */
+                        0x6500000f,  /* line  20 --- addr  4c */
+                        0x59700000,  /* line  21 --- addr  50 */
+                        0x3000002a,  /* line  22 --- addr  54 */
+                        0x9c000700,  /* line  23 --- addr  58 */
+                        0x3c00001a,  /* line  24 --- addr  5c */
+                        0xa000000e,  /* line  25 --- addr  60 */
+                        0x38000148,  /* line  26 --- addr  64 */
+                        0x18070021,  /* line  27 --- addr  68 */
+                        0x65000019,  /* line  28 --- addr  6c */
+                        0x9c000f00,  /* line  29 --- addr  70 */
+                        0x59700000,  /* line  30 --- addr  74 */
+                        0x1c0e0078,  /* line  31 --- addr  78 */
+                        0xc8000000,  /* line  32 --- addr  7c */
+                        0x30000145,  /* line  33 --- addr  80 */
+                        0x6500000c,  /* line  34 --- addr  84 */
+                        0x59700000,  /* line  35 --- addr  88 */
+                        0x38000030,  /* line  36 --- addr  8c */
+                        0x64000004,  /* line  37 --- addr  90 */
+                        0x94000000,  /* line  38 --- addr  94 */
+                        0xa0000000,  /* line  39 --- addr  98 */
+                        0x3c008043,  /* line  40 --- addr  9c */
+                        0x60000008,  /* line  41 --- addr  a0 */
+                        0x78000004,  /* line  42 --- addr  a4 */
+                        0x64000008,  /* line  43 --- addr  a8 */
+                        0x3c008041,  /* line  44 --- addr  ac */
+                        0x0800003d,  /* line  45 --- addr  b0 */
+                        0xf8000000,  /* line  46 --- addr  b4 */
+                        0x60000008,  /* line  47 --- addr  b8 */
+                        0x6170000c,  /* line  48 --- addr  bc */
+                        0x6500000d,  /* line  49 --- addr  c0 */
+                        0x0c070041,  /* line  50 --- addr  c4 */
+                        0x9c000700,  /* line  51 --- addr  c8 */
+                        0x38000148,  /* line  52 --- addr  cc */
+                        0x59070000,  /* line  53 --- addr  d0 */
+                        0x1c0e0078,  /* line  54 --- addr  d4 */
+                        0xc8000000,  /* line  55 --- addr  d8 */
+                        0x1c0e0078,  /* line  56 --- addr  dc */
+                        0x6d000001,  /* line  57 --- addr  e0 */
+                        0x0800003f,  /* line  58 --- addr  e4 */
+                        0x6c200004,  /* line  59 --- addr  e8 */
+                        0x6c300008,  /* line  60 --- addr  ec */
+                        0x30000067,  /* line  61 --- addr  f0 */
+                        0xf8000000,  /* line  62 --- addr  f4 */
+                        0x60000008,  /* line  63 --- addr  f8 */
+                        0xdc700100,  /* line  64 --- addr  fc */
+                        0x3000002a,  /* line  65 --- addr 100 */
+                        0x7c000000,  /* line  66 --- addr 104 */
+                        0x6100000c,  /* line  67 --- addr 108 */
+                        0x78000004,  /* line  68 --- addr 10c */
+                        0x30000000,  /* line  69 --- addr 110 */
+                        0xc0000000,  /* line  70 --- addr 114 */
+                        0xc0000000,  /* line  71 --- addr 118 */
+                        0xc0000000,  /* line  72 --- addr 11c */
+                        0xc0000000,  /* line  73 --- addr 120 */
+                        0xc0000000,  /* line  74 --- addr 124 */
+                        0xc0000000,  /* line  75 --- addr 128 */
+                        0xc0000000,  /* line  76 --- addr 12c */
+                        0x1c060183,  /* line  77 --- addr 130 */
+                        0x4d000000,  /* line  78 --- addr 134 */
+                        0x0c07004d,  /* line  79 --- addr 138 */
+                        0x59070000,  /* line  80 --- addr 13c */
+                        0x8c008000,  /* line  81 --- addr 140 */
+                        0x49800000,  /* line  82 --- addr 144 */
+                        0x6d000001,  /* line  83 --- addr 148 */
+                        0x59700000,  /* line  84 --- addr 14c */
+                        0x71000401,  /* line  85 --- addr 150 */
+                        0x59050000,  /* line  86 --- addr 154 */
+                        0x49800000,  /* line  87 --- addr 158 */
+                        0xb0000000,  /* line  88 --- addr 15c */
+                        0xb0050000,  /* line  89 --- addr 160 */
+                        0x1801005d,  /* line  90 --- addr 164 */
+                        0x65000010,  /* line  91 --- addr 168 */
+                        0xc0000000,  /* line  92 --- addr 16c */
+                        0x61000010,  /* line  93 --- addr 170 */
+                        0x59070000,  /* line  94 --- addr 174 */
+                        0x61000012,  /* line  95 --- addr 178 */
+                        0x0c010064,  /* line  96 --- addr 17c */
+                        0xfb000000,  /* line  97 --- addr 180 */
+                        0xfa000000,  /* line  98 --- addr 184 */
+                        0x6570000c,  /* line  99 --- addr 188 */
+                        0x30000030,  /* line 100 --- addr 18c */
+                        0xfb000000,  /* line 101 --- addr 190 */
+                        0xfe000000,  /* line 102 --- addr 194 */
+                        0x30000000,  /* line 103 --- addr 198 */
+                        0xb0000000,  /* line 104 --- addr 19c */
+                        0xfb000000,  /* line 105 --- addr 1a0 */
+                        0xf9000002,  /* line 106 --- addr 1a4 */
+                        0xd1001018,  /* line 107 --- addr 1a8 */
+                        0x65000012,  /* line 108 --- addr 1ac */
+                        0xdc000100,  /* line 109 --- addr 1b0 */
+                        0x9c000f00,  /* line 110 --- addr 1b4 */
+                        0x80000018,  /* line 111 --- addr 1b8 */
+                        0x61000012,  /* line 112 --- addr 1bc */
+                        0xd1001018,  /* line 113 --- addr 1c0 */
+                        0x18010140,  /* line 114 --- addr 1c4 */
+                        0xd1000110,  /* line 115 --- addr 1c8 */
+                        0x380f0140,  /* line 116 --- addr 1cc */
+                        0xb4000000,  /* line 117 --- addr 1d0 */
+                        0x30000140,  /* line 118 --- addr 1d4 */
+                        0x71001102,  /* line 119 --- addr 1d8 */
+                        0x3000004c,  /* line 120 --- addr 1dc */
+                        0xb0020000,  /* line 121 --- addr 1e0 */
+                        0x2c00707a,  /* line 122 --- addr 1e4 */
+                        0x59040080,  /* line 123 --- addr 1e8 */
+                        0xcc000000,  /* line 124 --- addr 1ec */
+                        0x2c00707f,  /* line 125 --- addr 1f0 */
+                        0x6500000c,  /* line 126 --- addr 1f4 */
+                        0x3000008b,  /* line 127 --- addr 1f8 */
+                        0x59040080,  /* line 128 --- addr 1fc */
+                        0x3c200089,  /* line 129 --- addr 200 */
+                        0x3c200089,  /* line 130 --- addr 204 */
+                        0x3c200089,  /* line 131 --- addr 208 */
+                        0x3c200089,  /* line 132 --- addr 20c */
+                        0x3c200089,  /* line 133 --- addr 210 */
+                        0x3c200089,  /* line 134 --- addr 214 */
+                        0x3c210089,  /* line 135 --- addr 218 */
+                        0x3c220089,  /* line 136 --- addr 21c */
+                        0x3000007c,  /* line 137 --- addr 220 */
+                        0x2c00708a,  /* line 138 --- addr 224 */
+                        0x59040080,  /* line 139 --- addr 228 */
+                        0x59700000,  /* line 140 --- addr 22c */
+                        0xc8000000,  /* line 141 --- addr 230 */
+                        0x6c100014,  /* line 142 --- addr 234 */
+                        0x2c002091,  /* line 143 --- addr 238 */
+                        0xb0080000,  /* line 144 --- addr 23c */
+                        0xb0080000,  /* line 145 --- addr 240 */
+                        0x54000000,  /* line 146 --- addr 244 */
+                        0x30000140,  /* line 147 --- addr 248 */
+                        0x54000000,  /* line 148 --- addr 24c */
+                        0x6c100014,  /* line 149 --- addr 250 */
+                        0x30000140,  /* line 150 --- addr 254 */
+                        0x65000015,  /* line 151 --- addr 258 */
+                        0xdc000100,  /* line 152 --- addr 25c */
+                        0x9c000f00,  /* line 153 --- addr 260 */
+                        0x80000018,  /* line 154 --- addr 264 */
+                        0x61000015,  /* line 155 --- addr 268 */
+                        0xd1001018,  /* line 156 --- addr 26c */
+                        0xf0000345,  /* line 157 --- addr 270 */
+                        0x6d000018,  /* line 158 --- addr 274 */
+                        0xc0000000,  /* line 159 --- addr 278 */
+                        0x3c0000a4,  /* line 160 --- addr 27c */
+                        0x69000018,  /* line 161 --- addr 280 */
+                        0x6810001c,  /* line 162 --- addr 284 */
+                        0x54000000,  /* line 163 --- addr 288 */
+                        0x30000096,  /* line 164 --- addr 28c */
+                        0x180500a4,  /* line 165 --- addr 290 */
+                        0x2c006129,  /* line 166 --- addr 294 */
+                        0x2c0070da,  /* line 167 --- addr 298 */
+                        0x180b00ac,  /* line 168 --- addr 29c */
+                        0x280000ac,  /* line 169 --- addr 2a0 */
+                        0x1c0900ac,  /* line 170 --- addr 2a4 */
+                        0xf1000645,  /* line 171 --- addr 2a8 */
+                        0x28000140,  /* line 172 --- addr 2ac */
+                        0x280020b2,  /* line 173 --- addr 2b0 */
+                        0x7c000000,  /* line 174 --- addr 2b4 */
+                        0x59040080,  /* line 175 --- addr 2b8 */
+                        0x2c0020ae,  /* line 176 --- addr 2bc */
+                        0x2c0020ae,  /* line 177 --- addr 2c0 */
+                        0x300000a4,  /* line 178 --- addr 2c4 */
+                        0x280000b8,  /* line 179 --- addr 2c8 */
+                        0x7c000000,  /* line 180 --- addr 2cc */
+                        0x59400080,  /* line 181 --- addr 2d0 */
+                        0x2c0000b4,  /* line 182 --- addr 2d4 */
+                        0x2c0000b4,  /* line 183 --- addr 2d8 */
+                        0x300000a4,  /* line 184 --- addr 2dc */
+                        0x30000140,  /* line 185 --- addr 2e0 */
+                        0x1c0200c1,  /* line 186 --- addr 2e4 */
+                        0x1c0c00bc,  /* line 187 --- addr 2e8 */
+                        0x30000181,  /* line 188 --- addr 2ec */
+                        0xb4030000,  /* line 189 --- addr 2f0 */
+                        0x71000502,  /* line 190 --- addr 2f4 */
+                        0x280050be,  /* line 191 --- addr 2f8 */
+                        0x74400680,  /* line 192 --- addr 2fc */
+                        0x3000004c,  /* line 193 --- addr 300 */
+                        0x71001302,  /* line 194 --- addr 304 */
+                        0x3000004c,  /* line 195 --- addr 308 */
+                        0x65000014,  /* line 196 --- addr 30c */
+                        0xdc000100,  /* line 197 --- addr 310 */
+                        0x9c000f00,  /* line 198 --- addr 314 */
+                        0x80000018,  /* line 199 --- addr 318 */
+                        0x61000014,  /* line 200 --- addr 31c */
+                        0xd1001018,  /* line 201 --- addr 320 */
+                        0xf0000545,  /* line 202 --- addr 324 */
+                        0x2c0040ca,  /* line 203 --- addr 328 */
+                        0x180700cf,  /* line 204 --- addr 32c */
+                        0x6c20000c,  /* line 205 --- addr 330 */
+                        0x6c30001b,  /* line 206 --- addr 334 */
+                        0x30000140,  /* line 207 --- addr 338 */
+                        0x6d000001,  /* line 208 --- addr 33c */
+                        0x9c00fe00,  /* line 209 --- addr 340 */
+                        0x69000001,  /* line 210 --- addr 344 */
+                        0x7c000000,  /* line 211 --- addr 348 */
+                        0xb8000000,  /* line 212 --- addr 34c */
+                        0x80000004,  /* line 213 --- addr 350 */
+                        0x60000004,  /* line 214 --- addr 354 */
+                        0x6c100014,  /* line 215 --- addr 358 */
+                        0x6810001c,  /* line 216 --- addr 35c */
+                        0x54000000,  /* line 217 --- addr 360 */
+                        0x28007140,  /* line 218 --- addr 364 */
+                        0x65000017,  /* line 219 --- addr 368 */
+                        0xdc000100,  /* line 220 --- addr 36c */
+                        0x9c000f00,  /* line 221 --- addr 370 */
+                        0x80000018,  /* line 222 --- addr 374 */
+                        0x61000017,  /* line 223 --- addr 378 */
+                        0xd1001018,  /* line 224 --- addr 37c */
+                        0x59040000,  /* line 225 --- addr 380 */
+                        0x3c000114,  /* line 226 --- addr 384 */
+                        0x3c040104,  /* line 227 --- addr 388 */
+                        0x3c0200ed,  /* line 228 --- addr 38c */
+                        0x3c0700e8,  /* line 229 --- addr 390 */
+                        0x3c0100f6,  /* line 230 --- addr 394 */
+                        0x3c2300ef,  /* line 231 --- addr 398 */
+                        0x3c0800ed,  /* line 232 --- addr 39c */
+                        0xb4030000,  /* line 233 --- addr 3a0 */
+                        0xb0050000,  /* line 234 --- addr 3a4 */
+                        0x2c0070ea,  /* line 235 --- addr 3a8 */
+                        0x28005181,  /* line 236 --- addr 3ac */
+                        0x30000140,  /* line 237 --- addr 3b0 */
+                        0xb0050000,  /* line 238 --- addr 3b4 */
+                        0x30000140,  /* line 239 --- addr 3b8 */
+                        0xb0050000,  /* line 240 --- addr 3bc */
+                        0x28007181,  /* line 241 --- addr 3c0 */
+                        0x59040080,  /* line 242 --- addr 3c4 */
+                        0xb4060000,  /* line 243 --- addr 3c8 */
+                        0x28006140,  /* line 244 --- addr 3cc */
+                        0xb0060000,  /* line 245 --- addr 3d0 */
+                        0x30000140,  /* line 246 --- addr 3d4 */
+                        0xb0050000,  /* line 247 --- addr 3d8 */
+                        0x2c0070f8,  /* line 248 --- addr 3dc */
+                        0x59040080,  /* line 249 --- addr 3e0 */
+                        0x3c0200ff,  /* line 250 --- addr 3e4 */
+                        0x3c0300fd,  /* line 251 --- addr 3e8 */
+                        0x2c0070fc,  /* line 252 --- addr 3ec */
+                        0x59040080,  /* line 253 --- addr 3f0 */
+                        0x2c0070fe,  /* line 254 --- addr 3f4 */
+                        0x59040080,  /* line 255 --- addr 3f8 */
+                        0x2c007100,  /* line 256 --- addr 3fc */
+                        0x59040080,  /* line 257 --- addr 400 */
+                        0x2c007102,  /* line 258 --- addr 404 */
+                        0x59040040,  /* line 259 --- addr 408 */
+                        0x300000e8,  /* line 260 --- addr 40c */
+                        0x180a010a,  /* line 261 --- addr 410 */
+                        0x18060108,  /* line 262 --- addr 414 */
+                        0xb0060000,  /* line 263 --- addr 418 */
+                        0x3000010a,  /* line 264 --- addr 41c */
+                        0x1c0b010a,  /* line 265 --- addr 420 */
+                        0xb0080000,  /* line 266 --- addr 424 */
+                        0x1c01010c,  /* line 267 --- addr 428 */
+                        0xb4000000,  /* line 268 --- addr 42c */
+                        0xb0050000,  /* line 269 --- addr 430 */
+                        0x6c10001c,  /* line 270 --- addr 434 */
+                        0x68100014,  /* line 271 --- addr 438 */
+                        0x50000000,  /* line 272 --- addr 43c */
+                        0x6570000c,  /* line 273 --- addr 440 */
+                        0xfb000000,  /* line 274 --- addr 444 */
+                        0x1c010030,  /* line 275 --- addr 448 */
+                        0x30000000,  /* line 276 --- addr 44c */
+                        0x1807004c,  /* line 277 --- addr 450 */
+                        0xb0050000,  /* line 278 --- addr 454 */
+                        0x30000145,  /* line 279 --- addr 458 */
+                        0x65000013,  /* line 280 --- addr 45c */
+                        0xdc000100,  /* line 281 --- addr 460 */
+                        0x9c000f00,  /* line 282 --- addr 464 */
+                        0x80000018,  /* line 283 --- addr 468 */
+                        0x61000013,  /* line 284 --- addr 46c */
+                        0xd1001018,  /* line 285 --- addr 470 */
+                        0x6d000099,  /* line 286 --- addr 474 */
+                        0x18070120,  /* line 287 --- addr 478 */
+                        0x9c00bf00,  /* line 288 --- addr 47c */
+                        0x1c010123,  /* line 289 --- addr 480 */
+                        0x59400080,  /* line 290 --- addr 484 */
+                        0x30000140,  /* line 291 --- addr 488 */
+                        0x594000c0,  /* line 292 --- addr 48c */
+                        0x2c005125,  /* line 293 --- addr 490 */
+                        0x6d4000c0,  /* line 294 --- addr 494 */
+                        0x2c005127,  /* line 295 --- addr 498 */
+                        0x59470080,  /* line 296 --- addr 49c */
+                        0x30000140,  /* line 297 --- addr 4a0 */
+                        0x65000016,  /* line 298 --- addr 4a4 */
+                        0xdc000100,  /* line 299 --- addr 4a8 */
+                        0x9c000f00,  /* line 300 --- addr 4ac */
+                        0x80000018,  /* line 301 --- addr 4b0 */
+                        0x61000016,  /* line 302 --- addr 4b4 */
+                        0xd1001018,  /* line 303 --- addr 4b8 */
+                        0x59040080,  /* line 304 --- addr 4bc */
+                        0x1c07013e,  /* line 305 --- addr 4c0 */
+                        0x69000003,  /* line 306 --- addr 4c4 */
+                        0x3802013e,  /* line 307 --- addr 4c8 */
+                        0xb4070000,  /* line 308 --- addr 4cc */
+                        0x71000402,  /* line 309 --- addr 4d0 */
+                        0x61700019,  /* line 310 --- addr 4d4 */
+                        0x6d00001a,  /* line 311 --- addr 4d8 */
+                        0x6100001b,  /* line 312 --- addr 4dc */
+                        0x6d00001b,  /* line 313 --- addr 4e0 */
+                        0x6100001e,  /* line 314 --- addr 4e4 */
+                        0x38000140,  /* line 315 --- addr 4e8 */
+                        0x6500000d,  /* line 316 --- addr 4ec */
+                        0x8c008000,  /* line 317 --- addr 4f0 */
+                        0x6100000d,  /* line 318 --- addr 4f4 */
+                        0xb0070000,  /* line 319 --- addr 4f8 */
+                        0x2c00613f,  /* line 320 --- addr 4fc */
+                        0x20204778,  /* line 321 --- addr 500 */
+                        0x18050141,  /* line 322 --- addr 504 */
+                        0x205769ab,  /* line 323 --- addr 508 */
+                        0x107e00c0,  /* line 324 --- addr 50c */
+                        0x30000140,  /* line 325 --- addr 510 */
+                        0x6c200010,  /* line 326 --- addr 514 */
+                        0x74300600,  /* line 327 --- addr 518 */
+                        0x30000067,  /* line 328 --- addr 51c */
+                        0x6500000f,  /* line 329 --- addr 520 */
+                        0x59700000,  /* line 330 --- addr 524 */
+                        0xc8000000,  /* line 331 --- addr 528 */
+                        0x1c0e0078,  /* line 332 --- addr 52c */
+                        0x6500000d,  /* line 333 --- addr 530 */
+                        0x0c0001b6,  /* line 334 --- addr 534 */
+                        0x6c200004,  /* line 335 --- addr 538 */
+                        0x6c300008,  /* line 336 --- addr 53c */
+                        0x0c010153,  /* line 337 --- addr 540 */
+                        0x0c020185,  /* line 338 --- addr 544 */
+                        0x30000000,  /* line 339 --- addr 548 */
+                        0xb4060000,  /* line 340 --- addr 54c */
+                        0xf9000002,  /* line 341 --- addr 550 */
+                        0x28005181,  /* line 342 --- addr 554 */
+                        0x65000013,  /* line 343 --- addr 558 */
+                        0x2c005158,  /* line 344 --- addr 55c */
+                        0x6d4000d9,  /* line 345 --- addr 560 */
+                        0x2c00515a,  /* line 346 --- addr 564 */
+                        0x744001c0,  /* line 347 --- addr 568 */
+                        0x2c00515c,  /* line 348 --- addr 56c */
+                        0x744002c0,  /* line 349 --- addr 570 */
+                        0x2c00515e,  /* line 350 --- addr 574 */
+                        0x744003c0,  /* line 351 --- addr 578 */
+                        0x2c005160,  /* line 352 --- addr 57c */
+                        0x59400080,  /* line 353 --- addr 580 */
+                        0x2800717f,  /* line 354 --- addr 584 */
+                        0x59040080,  /* line 355 --- addr 588 */
+                        0x3c07017d,  /* line 356 --- addr 58c */
+                        0x380100e1,  /* line 357 --- addr 590 */
+                        0x28007181,  /* line 358 --- addr 594 */
+                        0x59040080,  /* line 359 --- addr 598 */
+                        0x38020179,  /* line 360 --- addr 59c */
+                        0x28007181,  /* line 361 --- addr 5a0 */
+                        0x59040080,  /* line 362 --- addr 5a4 */
+                        0x38030179,  /* line 363 --- addr 5a8 */
+                        0x28007181,  /* line 364 --- addr 5ac */
+                        0x59040000,  /* line 365 --- addr 5b0 */
+                        0x61000016,  /* line 366 --- addr 5b4 */
+                        0x71000d02,  /* line 367 --- addr 5b8 */
+                        0x6500000d,  /* line 368 --- addr 5bc */
+                        0x08020175,  /* line 369 --- addr 5c0 */
+                        0xb4030000,  /* line 370 --- addr 5c4 */
+                        0xb0050000,  /* line 371 --- addr 5c8 */
+                        0xfe000000,  /* line 372 --- addr 5cc */
+                        0x30000189,  /* line 373 --- addr 5d0 */
+                        0xb0050000,  /* line 374 --- addr 5d4 */
+                        0xb0060000,  /* line 375 --- addr 5d8 */
+                        0xfe000000,  /* line 376 --- addr 5dc */
+                        0x30000140,  /* line 377 --- addr 5e0 */
+                        0x71000702,  /* line 378 --- addr 5e4 */
+                        0x28007176,  /* line 379 --- addr 5e8 */
+                        0x59040080,  /* line 380 --- addr 5ec */
+                        0x3000017a,  /* line 381 --- addr 5f0 */
+                        0x71000c02,  /* line 382 --- addr 5f4 */
+                        0x30000176,  /* line 383 --- addr 5f8 */
+                        0x71000b02,  /* line 384 --- addr 5fc */
+                        0x30000176,  /* line 385 --- addr 600 */
+                        0x71001402,  /* line 386 --- addr 604 */
+                        0x30000140,  /* line 387 --- addr 608 */
+                        0x6c200004,  /* line 388 --- addr 60c */
+                        0x6c300008,  /* line 389 --- addr 610 */
+                        0x74400000,  /* line 390 --- addr 614 */
+                        0xf9000002,  /* line 391 --- addr 618 */
+                        0x28005181,  /* line 392 --- addr 61c */
+                        0x6d4000d9,  /* line 393 --- addr 620 */
+                        0x2c00518d,  /* line 394 --- addr 624 */
+                        0x2c00518d,  /* line 395 --- addr 628 */
+                        0x300001b2,  /* line 396 --- addr 62c */
+                        0x28005140,  /* line 397 --- addr 630 */
+                        0xb0060000,  /* line 398 --- addr 634 */
+                        0x744001c0,  /* line 399 --- addr 638 */
+                        0x2c005190,  /* line 400 --- addr 63c */
+                        0x744003c0,  /* line 401 --- addr 640 */
+                        0x2c005192,  /* line 402 --- addr 644 */
+                        0x744001c0,  /* line 403 --- addr 648 */
+                        0x65000014,  /* line 404 --- addr 64c */
+                        0x2c005195,  /* line 405 --- addr 650 */
+                        0x594000c0,  /* line 406 --- addr 654 */
+                        0x65000015,  /* line 407 --- addr 658 */
+                        0x2c005198,  /* line 408 --- addr 65c */
+                        0x59400080,  /* line 409 --- addr 660 */
+                        0x280071b4,  /* line 410 --- addr 664 */
+                        0x59040080,  /* line 411 --- addr 668 */
+                        0x3c0701b2,  /* line 412 --- addr 66c */
+                        0x380100e1,  /* line 413 --- addr 670 */
+                        0x28007181,  /* line 414 --- addr 674 */
+                        0x59040080,  /* line 415 --- addr 678 */
+                        0x38030179,  /* line 416 --- addr 67c */
+                        0x28007181,  /* line 417 --- addr 680 */
+                        0x59040080,  /* line 418 --- addr 684 */
+                        0x38010179,  /* line 419 --- addr 688 */
+                        0x28007181,  /* line 420 --- addr 68c */
+                        0x59040080,  /* line 421 --- addr 690 */
+                        0x61000017,  /* line 422 --- addr 694 */
+                        0x28007181,  /* line 423 --- addr 698 */
+                        0x59040080,  /* line 424 --- addr 69c */
+                        0x61000018,  /* line 425 --- addr 6a0 */
+                        0x71000a02,  /* line 426 --- addr 6a4 */
+                        0x30000176,  /* line 427 --- addr 6a8 */
+                        0xc0000000,  /* line 428 --- addr 6ac */
+                        0xc0000000,  /* line 429 --- addr 6b0 */
+                        0xc0000000,  /* line 430 --- addr 6b4 */
+                        0xc0000000,  /* line 431 --- addr 6b8 */
+                        0xc0000000,  /* line 432 --- addr 6bc */
+                        0xc0000000,  /* line 433 --- addr 6c0 */
+                        0xc0000000,  /* line 434 --- addr 6c4 */
+                        0x71000902,  /* line 435 --- addr 6c8 */
+                        0x30000176,  /* line 436 --- addr 6cc */
+                        0x71000802,  /* line 437 --- addr 6d0 */
+                        0x30000176,  /* line 438 --- addr 6d4 */
+                        0xa000000e,  /* line 439 --- addr 6d8 */
+                        0x08000000,  /* line 440 --- addr 6dc */
+                        0x74400000,  /* line 441 --- addr 6e0 */
+                        0xf90000d2,  /* line 442 --- addr 6e4 */
+                        0x28005181,  /* line 443 --- addr 6e8 */
+                        0x6d400084,  /* line 444 --- addr 6ec */
+                        0x7900000c,  /* line 445 --- addr 6f0 */
+                        0x7d000000,  /* line 446 --- addr 6f4 */
+                        0xdc000100,  /* line 447 --- addr 6f8 */
+                        0x6100000e,  /* line 448 --- addr 6fc */
+                        0x1801004c,  /* line 449 --- addr 700 */
+                        0xd1000110,  /* line 450 --- addr 704 */
+                        0x3000004c,  /* line 451 --- addr 708 */
+                        0xf90000ef,  /* line 452 --- addr 70c */
+                        0xfe000000,  /* line 453 --- addr 710 */
+                        0x280051c5,  /* line 454 --- addr 714 */
+                        0x180501c5,  /* line 455 --- addr 718 */
+                        0x74400680,  /* line 456 --- addr 71c */
+                        0xfb000000,  /* line 457 --- addr 720 */
+                        0xfe000000,  /* line 458 --- addr 724 */
+                        0xfe000000   /* line 459 --- addr 728 */
+                      };
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/sound/audio.c linux/drivers/sound/audio.c
--- linux.vanilla/drivers/sound/audio.c	Sun Jun 21 18:41:19 1998
+++ linux/drivers/sound/audio.c	Mon Dec 14 18:09:33 1998
@@ -201,6 +201,9 @@
   p = 0;
   c = count;
 
+  if (count < 0)
+    return -EINVAL;
+
   if ((audio_mode[dev] & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX))
     {				/* Direction change */
     }
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/Config.in linux/fs/Config.in
--- linux.vanilla/fs/Config.in	Fri Jul 24 17:29:04 1998
+++ linux/fs/Config.in	Tue Mar  2 00:40:36 1999
@@ -19,32 +19,36 @@
   dep_tristate 'MSDOS fs support' CONFIG_MSDOS_FS $CONFIG_FAT_FS
   dep_tristate 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS $CONFIG_MSDOS_FS
   dep_tristate 'VFAT (Windows-95) fs support' CONFIG_VFAT_FS $CONFIG_FAT_FS
-  dep_tristate 'Codepage 437'      CONFIG_NLS_CODEPAGE_437 $CONFIG_NLS
-  dep_tristate 'Codepage 737'      CONFIG_NLS_CODEPAGE_737 $CONFIG_NLS
-  dep_tristate 'Codepage 775'      CONFIG_NLS_CODEPAGE_775 $CONFIG_NLS
-  dep_tristate 'Codepage 850'      CONFIG_NLS_CODEPAGE_850 $CONFIG_NLS
-  dep_tristate 'Codepage 852'      CONFIG_NLS_CODEPAGE_852 $CONFIG_NLS
-  dep_tristate 'Codepage 855'      CONFIG_NLS_CODEPAGE_855 $CONFIG_NLS
-  dep_tristate 'Codepage 857'      CONFIG_NLS_CODEPAGE_857 $CONFIG_NLS
-  dep_tristate 'Codepage 860'      CONFIG_NLS_CODEPAGE_860 $CONFIG_NLS
-  dep_tristate 'Codepage 861'      CONFIG_NLS_CODEPAGE_861 $CONFIG_NLS
-  dep_tristate 'Codepage 862'      CONFIG_NLS_CODEPAGE_862 $CONFIG_NLS
-  dep_tristate 'Codepage 863'      CONFIG_NLS_CODEPAGE_863 $CONFIG_NLS
-  dep_tristate 'Codepage 864'      CONFIG_NLS_CODEPAGE_864 $CONFIG_NLS
-  dep_tristate 'Codepage 865'      CONFIG_NLS_CODEPAGE_865 $CONFIG_NLS
-  dep_tristate 'Codepage 866'      CONFIG_NLS_CODEPAGE_866 $CONFIG_NLS
-  dep_tristate 'Codepage 869'      CONFIG_NLS_CODEPAGE_869 $CONFIG_NLS
-  dep_tristate 'Codepage 874'      CONFIG_NLS_CODEPAGE_874 $CONFIG_NLS
-  dep_tristate 'NLS ISO 8859-1'    CONFIG_NLS_ISO8859_1    $CONFIG_NLS 
-  dep_tristate 'NLS ISO 8859-2'    CONFIG_NLS_ISO8859_2    $CONFIG_NLS
-  dep_tristate 'NLS ISO 8859-3'    CONFIG_NLS_ISO8859_3    $CONFIG_NLS
-  dep_tristate 'NLS ISO 8859-4'    CONFIG_NLS_ISO8859_4    $CONFIG_NLS
-  dep_tristate 'NLS ISO 8859-5'    CONFIG_NLS_ISO8859_5    $CONFIG_NLS
-  dep_tristate 'NLS ISO 8859-6'    CONFIG_NLS_ISO8859_6    $CONFIG_NLS
-  dep_tristate 'NLS ISO 8859-7'    CONFIG_NLS_ISO8859_7    $CONFIG_NLS
-  dep_tristate 'NLS ISO 8859-8'    CONFIG_NLS_ISO8859_8    $CONFIG_NLS
-  dep_tristate 'NLS ISO 8859-9'    CONFIG_NLS_ISO8859_9    $CONFIG_NLS
-  dep_tristate 'NLS KOI8-R'        CONFIG_NLS_KOI8_R       $CONFIG_NLS
+  mainmenu_option next_comment
+  comment 'Select available code pages'
+  dep_tristate 'Codepage 437 (United States, Canada)'	CONFIG_NLS_CODEPAGE_437 $CONFIG_NLS
+  dep_tristate 'Codepage 737 (Greek)'			CONFIG_NLS_CODEPAGE_737 $CONFIG_NLS
+  dep_tristate 'Codepage 775 (Baltic Rim)'		CONFIG_NLS_CODEPAGE_775 $CONFIG_NLS
+  dep_tristate 'Codepage 850 (Europe)'			CONFIG_NLS_CODEPAGE_850 $CONFIG_NLS
+  dep_tristate 'Codepage 852 (Central/Eastern Europe)'	CONFIG_NLS_CODEPAGE_852 $CONFIG_NLS
+  dep_tristate 'Codepage 855 (Cyrillic)'		CONFIG_NLS_CODEPAGE_855 $CONFIG_NLS
+  dep_tristate 'Codepage 857 (Turkish)'			CONFIG_NLS_CODEPAGE_857 $CONFIG_NLS
+  dep_tristate 'Codepage 860 (Portugese)'		CONFIG_NLS_CODEPAGE_860 $CONFIG_NLS
+  dep_tristate 'Codepage 861 (Icelandic)'		CONFIG_NLS_CODEPAGE_861 $CONFIG_NLS
+  dep_tristate 'Codepage 862 (Hebrew)'			CONFIG_NLS_CODEPAGE_862 $CONFIG_NLS
+  dep_tristate 'Codepage 863 (Canadian French)'		CONFIG_NLS_CODEPAGE_863 $CONFIG_NLS
+  dep_tristate 'Codepage 864 (Arabic)'			CONFIG_NLS_CODEPAGE_864 $CONFIG_NLS
+  dep_tristate 'Codepage 865 (Nordic European)'		CONFIG_NLS_CODEPAGE_865 $CONFIG_NLS
+  dep_tristate 'Codepage 866 (Cyrillic/Russian)'	CONFIG_NLS_CODEPAGE_866 $CONFIG_NLS
+  dep_tristate 'Codepage 869 (Greek)'			CONFIG_NLS_CODEPAGE_869 $CONFIG_NLS
+  dep_tristate 'Codepage 874 (Thai)'			CONFIG_NLS_CODEPAGE_874 $CONFIG_NLS
+  dep_tristate 'NLS ISO 8859-1 (Latin 1; Western Europe)'    CONFIG_NLS_ISO8859_1    $CONFIG_NLS 
+  dep_tristate 'NLS ISO 8859-2 (Latin 2; Slavic/Central European)'    CONFIG_NLS_ISO8859_2    $CONFIG_NLS
+  dep_tristate 'NLS ISO 8859-3 (Latin 3; Esperanto, Galician, Maltese, Turkish)'    CONFIG_NLS_ISO8859_3    $CONFIG_NLS
+  dep_tristate 'NLS ISO 8859-4 (Latin 4; Estonian, Latvian, Lithuanian)'    CONFIG_NLS_ISO8859_4    $CONFIG_NLS
+  dep_tristate 'NLS ISO 8859-5 (Cyrillic)'    CONFIG_NLS_ISO8859_5    $CONFIG_NLS
+  dep_tristate 'NLS ISO 8859-6 (Arabic)'    CONFIG_NLS_ISO8859_6    $CONFIG_NLS
+  dep_tristate 'NLS ISO 8859-7 (Modern Greek)'    CONFIG_NLS_ISO8859_7    $CONFIG_NLS
+  dep_tristate 'NLS ISO 8859-8 (Hebrew)'    CONFIG_NLS_ISO8859_8    $CONFIG_NLS
+  dep_tristate 'NLS ISO 8859-9 (Latin 5; Turkey)'    CONFIG_NLS_ISO8859_9    $CONFIG_NLS
+  dep_tristate 'NLS ISO 8859-15 (Latin 9; Western European Languages with Euro)' CONFIG_NLS_ISO8859_15 $CONFIG_NLS
+  dep_tristate 'NLS KOI8-R (Russian)'        CONFIG_NLS_KOI8_R       $CONFIG_NLS
+  endmenu
 fi
 
 bool '/proc filesystem support' CONFIG_PROC_FS
@@ -64,6 +68,9 @@
 fi
 if [ "$CONFIG_IPX" != "n" ]; then
   tristate 'NCP filesystem support (to mount NetWare volumes)' CONFIG_NCP_FS
+  if [ "$CONFIG_NCP_FS" != "n" ]; then
+    source fs/ncpfs/Config.in
+  fi
 fi
 tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS
 tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/Makefile linux/fs/Makefile
--- linux.vanilla/fs/Makefile	Sun Jun 21 18:40:54 1998
+++ linux/fs/Makefile	Fri Jan 29 14:32:34 1999
@@ -481,6 +481,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_NLS_ISO8859_15),y)
+NLS += nls_iso8859_15.o
+else
+  ifeq ($(CONFIG_NLS_ISO8859_15),m)
+  M_OBJS += nls_iso8859_15.o
+  endif
+endif
+
 ifeq ($(CONFIG_NLS_KOI8_R),y)
 NLS += nls_koi8_r.o
 else
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/autofs/root.c linux/fs/autofs/root.c
--- linux.vanilla/fs/autofs/root.c	Sun Jun 21 18:40:56 1998
+++ linux/fs/autofs/root.c	Mon Mar  1 21:36:30 1999
@@ -108,6 +108,10 @@
 	*result = NULL;
 	if (!dir)
 		return -ENOENT;
+		
+	if (len > NAME_MAX)
+		return -ENOENT;
+		
 	if (!S_ISDIR(dir->i_mode)) {
 		iput(dir);
 		return -ENOTDIR;
@@ -194,6 +198,9 @@
 		iput(dir);
 		return -EPERM;
 	}
+	if ( len > NAME_MAX)
+		return -ENAMETOOLONG;
+		
 	if ( autofs_hash_lookup(dh,hash,name,len) ) {
 		iput(dir);
 		return -EEXIST;
@@ -252,6 +259,9 @@
 
 	if ( !autofs_oz_mode(sbi) )
 		return -EPERM;
+		
+	if(len > NAME_MAX)
+		return -ENAMETOOLONG;
 
 	ent = autofs_hash_lookup(dh,hash,name,len);
 	if ( !ent )
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/ioctl.c linux/fs/ioctl.c
--- linux.vanilla/fs/ioctl.c	Sun Jun 21 18:40:55 1998
+++ linux/fs/ioctl.c	Fri Jan 29 15:22:08 1999
@@ -8,6 +8,7 @@
 
 #include <linux/sched.h>
 #include <linux/mm.h>
+#include <linux/file.h>
 #include <linux/errno.h>
 #include <linux/string.h>
 #include <linux/stat.h>
@@ -59,50 +60,57 @@
 {	
 	struct file * filp;
 	int on;
+	int retval = 0;
 
-	if (fd >= NR_OPEN || !(filp = current->files->fd[fd]))
+	filp = fget(fd);
+	
+	if(filp==NULL)
 		return -EBADF;
+		
 	switch (cmd) {
 		case FIOCLEX:
 			FD_SET(fd, &current->files->close_on_exec);
-			return 0;
+			break;
 
 		case FIONCLEX:
 			FD_CLR(fd, &current->files->close_on_exec);
-			return 0;
+			break;
 
 		case FIONBIO:
-			on = verify_area(VERIFY_READ, (unsigned int *)arg,
+			retval = verify_area(VERIFY_READ, (unsigned int *)arg,
 				sizeof(unsigned int));
-			if(on)	
-				return on;
-			on = get_user((unsigned int *) arg);
-			if (on)
-				filp->f_flags |= O_NONBLOCK;
-			else
-				filp->f_flags &= ~O_NONBLOCK;
-			return 0;
+			if(!retval)	
+			{
+				on = get_user((unsigned int *) arg);
+				if (on)
+					filp->f_flags |= O_NONBLOCK;
+				else
+					filp->f_flags &= ~O_NONBLOCK;
+			}
+			break;
 
 		case FIOASYNC: /* O_SYNC is not yet implemented,
 				  but it's here for completeness. */
-			on = verify_area(VERIFY_READ, (unsigned int *)arg,
+			retval = verify_area(VERIFY_READ, (unsigned int *)arg,
 				sizeof(unsigned int));
-			if(on)	
-				return on;
-			on = get_user ((unsigned int *) arg);
-			if (on)
-				filp->f_flags |= O_SYNC;
-			else
-				filp->f_flags &= ~O_SYNC;
-			return 0;
+			if(!retval)	
+			{
+				on = get_user ((unsigned int *) arg);
+				if (on)
+					filp->f_flags |= O_SYNC;
+				else
+					filp->f_flags &= ~O_SYNC;
+			}
+			break;
 
 		default:
 			if (filp->f_inode && S_ISREG(filp->f_inode->i_mode))
-				return file_ioctl(filp, cmd, arg);
-
-			if (filp->f_op && filp->f_op->ioctl)
-				return filp->f_op->ioctl(filp->f_inode, filp, cmd, arg);
-
-			return -ENOTTY;
+				retval = file_ioctl(filp, cmd, arg);
+			else if (filp->f_op && filp->f_op->ioctl)
+				retval = filp->f_op->ioctl(filp->f_inode, filp, cmd, arg);
+			else 
+				retval = -ENOTTY;
 	}
+	fput(filp, filp->f_inode);
+	return retval;
 }
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/isofs/inode.c linux/fs/isofs/inode.c
--- linux.vanilla/fs/isofs/inode.c	Sun Dec  6 00:14:45 1998
+++ linux/fs/isofs/inode.c	Sun Dec 27 20:52:57 1998
@@ -32,6 +32,13 @@
  */
 #define IGNORE_WRONG_MULTI_VOLUME_SPECS
 
+/*
+ * A home-burnt Joliet level 3 cd-rom with a 100 MB zip file had more than
+ * 100 file sections, so the limit should be larger than that.  What does the
+ * ISO9660 standard say?  (Ulrik Dickow <ukd@kampsax.dk>)
+ */
+#define MAX_FILE_SECTIONS 1000
+
 #ifdef LEAK_CHECK
 static int check_malloc = 0;
 static int check_bread = 0;
@@ -646,8 +653,9 @@
 			nextino = ino->u.isofs_i.i_next_section_ino;
 			iput(ino);
 		
-			if(++i > 100) {
-				printk("isofs_bmap: More than 100 file sections ?!?, aborting...\n");
+			if(++i > MAX_FILE_SECTIONS) {
+				printk("isofs_bmap: More than %d file sections ?!?, aborting...\n",
+				       MAX_FILE_SECTIONS);
 				printk("isofs_bmap: ino=%lu block=%d firstext=%u size=%u nextino=%lu\n",
 				       inode->i_ino, block, firstext, (unsigned)size, nextino);
 				return 0;
@@ -688,9 +696,10 @@
 	ino = inode->i_ino;
 	i = 0;
 	do {
-		if(i > 100) {
-			printk("isofs_read_level3_size: More than 100 file sections ?!?, aborting...\n"
-			       "isofs_read_level3_size: inode=%lu ino=%lu\n", inode->i_ino, ino);
+		if(i > MAX_FILE_SECTIONS) {
+			printk("isofs_read_level3_size: More than %d file sections ?!?, aborting...\n"
+			       "isofs_read_level3_size: inode=%lu ino=%lu\n", MAX_FILE_SECTIONS,
+			       inode->i_ino, ino);
 			return 0;
 		}
 
@@ -824,8 +833,9 @@
 	}
 
 	/* There are defective discs out there - we do this to protect
-	   ourselves.  A cdrom will never contain more than 800Mb */
-	if((inode->i_size < 0 || inode->i_size > 800000000) &&
+	   ourselves.  A cdrom will never contain more than 800Mb 
+	   Allow 1Gig for DVD however - Ulrich Habel */
+	if((inode->i_size < 0 || inode->i_size > 1073741824) &&
 	    inode->i_sb->u.isofs_sb.s_cruft == 'n') {
 	  printk("Warning: defective cdrom.  Enabling \"cruft\" mount option.\n");
 	  inode->i_sb->u.isofs_sb.s_cruft = 'y';
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/ncpfs/Config.in linux/fs/ncpfs/Config.in
--- linux.vanilla/fs/ncpfs/Config.in	Thu Jan  1 01:00:00 1970
+++ linux/fs/ncpfs/Config.in	Tue Mar  2 00:40:45 1999
@@ -0,0 +1,9 @@
+#
+# NCP Filesystem configuration
+#
+bool     '   Packet singatures' CONFIG_NCPFS_PACKET_SIGNING
+bool     '   Proprietary file locking' CONFIG_NCPFS_IOCTL_LOCKING
+bool     '   Clear remove/delete inhibit when needed' CONFIG_NCPFS_STRONG
+bool     '   Use NFS namespace if available' CONFIG_NCPFS_NFS_NS
+bool     '   Use LONG (OS/2) namespace if available' CONFIG_NCPFS_OS2_NS
+bool     '   Allow mounting of volume subdirectories' CONFIG_NCPFS_MOUNT_SUBDIR
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/ncpfs/Makefile linux/fs/ncpfs/Makefile
--- linux.vanilla/fs/ncpfs/Makefile	Sun Jun 21 18:40:56 1998
+++ linux/fs/ncpfs/Makefile	Tue Mar  2 00:40:45 1999
@@ -8,7 +8,8 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
 O_TARGET := ncpfs.o
-O_OBJS   := dir.o file.o inode.o ioctl.o mmap.o ncplib_kernel.o sock.o
+O_OBJS   := dir.o file.o inode.o ioctl.o mmap.o ncplib_kernel.o sock.o \
+		ncpsign_kernel.o
 M_OBJS   := $(O_TARGET)
 
 # If you want debugging output, please uncomment the following line
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/ncpfs/dir.c linux/fs/ncpfs/dir.c
--- linux.vanilla/fs/ncpfs/dir.c	Sun Jun 21 18:40:56 1998
+++ linux/fs/ncpfs/dir.c	Tue Mar  2 00:40:45 1999
@@ -5,6 +5,8 @@
  *
  */
 
+#include <linux/config.h>
+
 #include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/stat.h>
@@ -103,7 +105,14 @@
 static inline int
 ncp_preserve_case(struct inode *i)
 {
-	return (ncp_namespace(i) == NW_NS_OS2);
+	return
+#ifdef CONFIG_NCPFS_OS2_NS
+	(ncp_namespace(i) == NW_NS_OS2) || 
+#endif	/* CONFIG_NCPFS_OS2_NS */
+#ifdef CONFIG_NCPFS_NFS_NS
+	(ncp_namespace(i) == NW_NS_NFS) ||
+#endif	/* CONFIG_NCPFS_NFS_NS */
+	0;
 }
 
 static struct file_operations ncp_dir_operations = {
@@ -157,7 +166,7 @@
 ncp_info_ino(struct ncp_server *server, struct ncp_inode_info *info)
 {
 	return ncp_single_volume(server)
-		? info->finfo.i.dirEntNum : (ino_t)info;
+		? (info->finfo.i.dirEntNum == server->root.finfo.i.dirEntNum)?0:info->finfo.i.dirEntNum: (ino_t)info;
 }
 
 static inline int
@@ -937,6 +946,7 @@
 {
 	struct nw_file_info finfo;
 	__u8 _name[len+1];
+	int error;
 
 	*result = NULL;
 
@@ -961,15 +971,18 @@
 	}
 
 	lock_super(dir->i_sb);
-	if (ncp_open_create_file_or_subdir(NCP_SERVER(dir),
+	if ((error = ncp_open_create_file_or_subdir(NCP_SERVER(dir),
 					   NCP_ISTRUCT(dir), _name,
 					   OC_MODE_CREATE|OC_MODE_OPEN|
 					   OC_MODE_REPLACE,
 					   0, AR_READ|AR_WRITE,
-					   &finfo) != 0)
+					   &finfo)) != 0)
 	{
 		unlock_super(dir->i_sb);
 		iput(dir);
+		if (error == 0x87) {
+			return -ENAMETOOLONG;
+		} 
 		return -EACCES;
 	}
 
@@ -1095,6 +1108,65 @@
 	return error;
 }
 
+
+#ifdef CONFIG_NCPFS_STRONG
+/* try to delete a readonly file (NW R bit set) */
+
+static int
+ncp_force_unlink(struct inode *dir,char *name,int len)
+{
+        int res=0x9c,res2;
+        struct inode *_inode;
+        struct iattr ia;
+
+        /* remove the Read-Only flag on the NW server */
+
+        dir->i_count++;
+        res2=ncp_lookup(dir,name,len,&_inode);
+        if (res2)
+        {
+                goto leave_me; /* abort operation */
+        }
+        memset(&ia,0,sizeof(struct iattr));
+        ia.ia_mode = _inode->i_mode;
+        ia.ia_mode |= NCP_SERVER(dir)->m.file_mode & 0222;  /* set write bits */
+        ia.ia_valid = ATTR_MODE;
+
+        res2=ncp_notify_change(_inode,&ia);
+        if (res2)
+        {
+                iput(_inode);
+                goto leave_me;
+        }
+
+        /* now try again the delete operation */
+
+        res2 = ncp_del_file_or_subdir(NCP_SERVER(dir),NCP_ISTRUCT(dir),name);
+
+        res=res2; /* save status to use as return value */
+
+        res=res2;
+        if (res2)  /* delete failed, set R bit again */
+        {
+                memset(&ia,0,sizeof(struct iattr));
+                ia.ia_mode = _inode->i_mode;
+                ia.ia_mode &= ~(NCP_SERVER(dir)->m.file_mode & 0222);  /* clear write bits */
+                ia.ia_valid = ATTR_MODE;
+
+                res2=ncp_notify_change(_inode,&ia);
+                if (res2)
+                {
+                        iput(_inode);
+                        goto leave_me;
+                }
+        }
+        iput(_inode);
+
+ leave_me:
+        return(res);
+}
+#endif	/* CONFIG_NCPFS_STRONG */
+
 static int
 ncp_unlink(struct inode *dir, const char *name, int len)
 {
@@ -1126,14 +1198,21 @@
 			str_upper(_name);
 		}
 
-                if ((error = ncp_del_file_or_subdir(NCP_SERVER(dir),
-						    NCP_ISTRUCT(dir),
-						    _name)) == 0)
-		{
-                        ncp_invalid_dir_cache(dir);
-		}
-		else
-		{
+                error = ncp_del_file_or_subdir(NCP_SERVER(dir),
+                                               NCP_ISTRUCT(dir),
+                                               _name);
+#ifdef CONFIG_NCPFS_STRONG
+		if (error == 0x9c && NCP_SERVER(dir)->m.flags & NCP_MOUNT_STRONG)  /* readonly */
+		{
+			error = ncp_force_unlink(dir,_name,len); /* special treatment */
+		}
+#endif	/* CONFIG_NCPFS_STRONG */
+
+		if (error == 0) {
+			ncp_invalid_dir_cache(dir);
+		} else if (error == 0xFF) {
+			error = -ENOENT;
+		} else {
 			error = -EACCES;
 		}
         }
@@ -1141,6 +1220,83 @@
 	return error;
 }
 
+#ifdef CONFIG_NCPFS_STRONG
+static int
+ncp_force_rename(struct inode *old_dir, const char *old_name, char *_old_name, int old_len,
+                 struct inode *new_dir, const char *new_name, char *_new_name, int new_len)
+{
+        int res=0x90,res2;
+        char _rename_old[old_len+1];
+        char _rename_new[new_len+1];
+        struct inode *_inode,*x_dir;
+        struct iattr ia;
+
+        strncpy(_rename_old,old_name,old_len);
+        _rename_old[old_len] = 0;
+        strncpy(_rename_new,new_name,new_len);
+        _rename_new[new_len] = 0;
+
+        /* remove the Read-Only flag on the NW server */
+
+        old_dir->i_count++;
+        res2=ncp_lookup(old_dir,_rename_old,old_len,&_inode);
+        if (res2)
+        {
+                goto leave_me;
+        }
+        memset(&ia,0,sizeof(struct iattr));
+        ia.ia_mode = _inode->i_mode;
+        ia.ia_mode |= NCP_SERVER(old_dir)->m.file_mode & 0222;  /* set write bits */
+        ia.ia_valid = ATTR_MODE;
+
+        res2=ncp_notify_change(_inode,&ia);
+        if (res2)
+        {
+                iput(_inode);
+                goto leave_me;
+        }
+
+        /* now try again the rename operation */
+        res2 = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir),
+                                             NCP_ISTRUCT(old_dir), _old_name,
+                                             NCP_ISTRUCT(new_dir), _new_name);
+
+        res=res2;
+        if (!res2)  /* rename succeeded, get a new inode for the new file */
+        {
+                x_dir=new_dir;
+                new_dir->i_count++;
+                iput(_inode);
+                res2=ncp_lookup(new_dir,_rename_new,new_len,&_inode);
+                if (res2)
+                {
+                        goto leave_me;
+                }
+        }
+        else
+        {
+                x_dir=old_dir;
+        }
+
+        memset(&ia,0,sizeof(struct iattr));
+        ia.ia_mode = _inode->i_mode;
+        ia.ia_mode &= ~(NCP_SERVER(x_dir)->m.file_mode & 0222);  /* clear write bits */
+        ia.ia_valid = ATTR_MODE;
+
+        res2=ncp_notify_change(_inode,&ia);
+        iput(_inode);
+        if (res2)
+        {
+                printk(KERN_INFO "ncpfs: ncp_notify_change (2) failed: %08x\n",res2);
+                goto leave_me;
+        }
+
+ leave_me:
+        return(res);
+}
+#endif	/* CONFIG_NCPFS_STRONG */
+
+
 static int
 ncp_rename(struct inode *old_dir, const char *old_name, int old_len,
            struct inode *new_dir, const char *new_name, int new_len,
@@ -1197,14 +1353,26 @@
 					    NCP_ISTRUCT(old_dir), _old_name,
 					    NCP_ISTRUCT(new_dir), _new_name);
 
-        if (res == 0)
+#ifdef CONFIG_NCPFS_STRONG
+	if (res == 0x90 && NCP_SERVER(old_dir)->m.flags & NCP_MOUNT_STRONG) /* file is readonly */
 	{
-                ncp_invalid_dir_cache(old_dir);
-                ncp_invalid_dir_cache(new_dir);
-        }
+		res=ncp_force_rename(old_dir,old_name,_old_name,old_len,new_dir,new_name,_new_name,new_len);
+	}
+#endif	/* CONFIG_NCPFS_STRONG */
+
+	if (res == 0)
+	{
+		ncp_invalid_dir_cache(old_dir);
+		ncp_invalid_dir_cache(new_dir);
+	}
 	else
 	{
-		res = -EACCES;
+		if (res == 0x9E)
+			res = -ENAMETOOLONG;
+		else if (res == 0xFF)
+			res = -ENOENT;
+		else
+			res = -EACCES;
 	}
 	
  finished:
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/ncpfs/inode.c linux/fs/ncpfs/inode.c
--- linux.vanilla/fs/ncpfs/inode.c	Sun Jun 21 18:40:56 1998
+++ linux/fs/ncpfs/inode.c	Tue Mar  2 00:40:45 1999
@@ -32,7 +32,6 @@
 static void ncp_read_inode(struct inode *);
 static void ncp_put_super(struct super_block *);
 static void ncp_statfs(struct super_block *sb, struct statfs *buf, int bufsiz);
-static int ncp_notify_change(struct inode *inode, struct iattr *attr);
 
 static struct super_operations ncp_sops = {
 	ncp_read_inode,         /* read inode */
@@ -83,6 +82,9 @@
 	else
 	{
                 inode->i_mode = NCP_SERVER(inode)->m.file_mode;
+#if 1
+                if (NCP_ISTRUCT(inode)->attributes & /* 0x60001 incl. DiRi */ 1) inode->i_mode &= ~0222;
+#endif
 		inode->i_size = NCP_ISTRUCT(inode)->dataStreamSize;
 	}
 
@@ -144,10 +146,11 @@
 	 */
 	lock_super(sb);
 
-	if (inode->i_count > 1) {
-printk("ncp_put_inode: inode in use device %s, inode %ld, count=%ld\n", 
-kdevname(inode->i_dev), inode->i_ino, inode->i_count);
-		goto unlock;
+	if (inode->i_count > 1)
+        {
+                printk("ncp_put_inode: inode in use device %s, inode %ld, count=%ld\n", 
+                       kdevname(inode->i_dev), inode->i_ino, inode->i_count);
+                goto unlock;
 	}
 	
 	DDPRINTK("ncp_put_inode: put %s\n",
@@ -198,6 +201,9 @@
 	struct file *msg_filp;
 	kdev_t dev = sb->s_dev;
 	int error;
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+	int options;
+#endif
 
 	if (data == NULL)
 	{
@@ -274,7 +280,10 @@
         server->packet      = NULL;
 	server->buffer_size = 0;
 	server->conn_status = 0;
-
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+	server->sign_wanted = 0;
+	server->sign_active = 0;
+#endif
         server->m = *data;
 	server->m.file_mode = (server->m.file_mode &
 			       (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFREG;
@@ -342,6 +351,29 @@
                 goto disconnect;
 	}
 
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+	if (ncp_negotiate_size_and_options(server, NCP_DEFAULT_BUFSIZE,
+		NCP_DEFAULT_OPTIONS, &(server->buffer_size), &options) == 0)
+	{
+		if (options != NCP_DEFAULT_OPTIONS)
+		{
+			if (ncp_negotiate_size_and_options(server, 
+				NCP_DEFAULT_BUFSIZE,
+				options & 2, 
+				&(server->buffer_size), &options) != 0)
+				
+			{
+				sb->s_dev = 0;
+				printk("ncp_read_super: "
+					"could not set options\n");
+				goto disconnect;
+			}
+		}
+		if (options & 2)
+			server->sign_wanted = 1;
+	}
+	else 
+#endif	/* CONFIG_NCPFS_PACKET_SIGNING */
 	if (ncp_negotiate_buffersize(server, NCP_DEFAULT_BUFSIZE,
 				     &(server->buffer_size)) != 0)
 	{
@@ -449,7 +481,7 @@
 	memcpy_tofs(buf, &tmp, bufsiz);
 }
 
-static int
+int
 ncp_notify_change(struct inode *inode, struct iattr *attr)
 {
 	int result = 0;
@@ -479,6 +511,33 @@
 
 	info_mask = 0;
 	memset(&info, 0, sizeof(info));
+
+#if 1 
+        if ((attr->ia_valid & ATTR_MODE) != 0)
+        {
+                if (NCP_ISTRUCT(inode)->attributes & aDIR)
+                {
+                        return -EPERM;
+                }
+                else
+                {
+			umode_t newmode;
+
+                        info_mask |= DM_ATTRIBUTES;
+                        newmode=attr->ia_mode;
+                        newmode &= NCP_SERVER(inode)->m.file_mode;
+
+                        if (newmode & 0222) /* any write bit set */
+                        {
+                                info.attributes &= ~0x60001;
+                        }
+                        else
+                        {
+                                info.attributes |= 0x60001;
+                        }
+                }
+        }
+#endif
 
 	if ((attr->ia_valid & ATTR_CTIME) != 0)
 	{
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/ncpfs/ioctl.c linux/fs/ncpfs/ioctl.c
--- linux.vanilla/fs/ncpfs/ioctl.c	Sun Jun 21 18:40:56 1998
+++ linux/fs/ncpfs/ioctl.c	Tue Mar  2 00:40:45 1999
@@ -5,6 +5,8 @@
  *
  */
 
+#include <linux/config.h>
+
 #include <asm/segment.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
@@ -13,6 +15,7 @@
 #include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/ncp.h>
+#include "ncplib_kernel.h"
 
 int
 ncp_ioctl (struct inode * inode, struct file * filp,
@@ -151,6 +154,242 @@
                 }
                 put_fs_word(server->m.mounted_uid, (uid_t*) arg);
                 return 0;
+
+#if 0
+	case NCP_IOC_GETMOUNTUID_INT:
+		if (   (permission(inode, MAY_READ) != 0)
+		    && (current->uid != server->m.mounted_uid))
+		{
+			return -EACCES;
+		}
+
+		if ((result = verify_area(VERIFY_WRITE, (unsigned int*)arg,
+					sizeof(unsigned int))) != 0)
+		{
+			return result;
+		}
+		{
+			unsigned int tmp=server->m.mounted_uid;
+			put_fs_long(tmp, (unsigned int*) arg);
+		}
+		return 0;
+#endif
+
+#ifdef CONFIG_NCPFS_MOUNT_SUBDIR
+	case NCP_IOC_GETROOT:
+		{
+			struct ncp_setroot_ioctl sr;
+
+			if (   (permission(inode, MAY_READ) != 0)
+			    && (current->uid != server->m.mounted_uid))
+			{
+				return -EACCES;
+			}
+			if (server->m.mounted_vol[0]) {
+				sr.volNumber = server->root.finfo.i.volNumber;
+				sr.dirEntNum = server->root.finfo.i.dirEntNum;
+				sr.namespace = server->name_space[sr.volNumber];
+			} else {
+				sr.volNumber = -1;
+				sr.namespace = 0;
+				sr.dirEntNum = 0;
+			}
+			if ((result = verify_area(VERIFY_WRITE, 
+						  (struct ncp_setroot_ioctl*)arg,
+						  sizeof(sr))) != 0)
+			{
+				return result;
+			}
+			memcpy_tofs((struct ncp_setroot_ioctl*)arg, 
+				    &sr, sizeof(sr));
+			return 0;
+		}
+	case NCP_IOC_SETROOT:
+		{
+			struct ncp_setroot_ioctl sr;
+
+			if (   (permission(inode, MAY_WRITE) != 0)
+			    && (current->uid != server->m.mounted_uid))
+			{
+				return -EACCES;
+			}
+			if ((result = verify_area(VERIFY_READ, 
+						  (struct ncp_setroot_ioctl*)arg,
+						  sizeof(sr))) != 0)
+			{
+				return result;
+			}
+			memcpy_fromfs(&sr, (struct ncp_setroot_ioctl*)arg, sizeof(sr));
+			if (sr.volNumber < 0) {
+				server->m.mounted_vol[0] = 0;
+				server->root.finfo.i.volNumber = 0;
+				server->root.finfo.i.dirEntNum = 0;
+			} else if (sr.volNumber >= NCP_NUMBER_OF_VOLUMES) {
+				return -EINVAL;
+			} else {
+				if (ncp_mount_subdir(server, sr.volNumber, sr.namespace, sr.dirEntNum)) {
+					return -ENOENT;
+				}
+			}
+			return 0;
+		}
+#endif	/* CONFIG_NCPFS_MOUNT_SUBDIR */
+
+#ifdef CONFIG_NCPFS_PACKET_SIGNING	
+	case NCP_IOC_SIGN_INIT:
+		if ((permission(inode, MAY_WRITE) != 0)
+		    && (current->uid != server->m.mounted_uid))
+		{
+			return -EACCES;
+		}
+		if ((result = verify_area(VERIFY_READ, (struct ncp_sign_init*)arg, 
+					  sizeof(struct ncp_sign_init))) != 0)
+		{
+			return result;
+		}
+		if (server->sign_active)
+		{
+			return -EINVAL;
+		}
+		if (server->sign_wanted)
+		{
+			struct ncp_sign_init sign;
+
+			memcpy_fromfs(&sign, (struct ncp_sign_init *) arg,
+			      sizeof(sign));
+			memcpy(server->sign_root,sign.sign_root,8);
+			memcpy(server->sign_last,sign.sign_last,16);
+			server->sign_active = 1;
+		}
+		/* ignore when signatures not wanted */
+		return 0;		
+	
+        case NCP_IOC_SIGN_WANTED:
+		if (   (permission(inode, MAY_READ) != 0)
+		    && (current->uid != server->m.mounted_uid))
+		{
+			return -EACCES;
+		}
+                if ((result = verify_area(VERIFY_WRITE, (int*) arg,
+                                          sizeof(int))) != 0)
+		{
+                        return result;
+		}
+		/* Should not it be put_fs_long? Vandrove@vc.cvut.cz */
+               	put_fs_word(server->sign_wanted, (int*) arg);
+               	return 0;
+
+	case NCP_IOC_SET_SIGN_WANTED:
+		{
+			int newstate;
+
+			if (   (permission(inode, MAY_WRITE) != 0)
+			    && (current->uid != server->m.mounted_uid))
+			{
+				return -EACCES;
+			}
+			if ((result = verify_area(VERIFY_READ, (int*) arg,
+						  sizeof(int))) != 0)
+			{
+				return result;
+			}
+			/* get only low 8 bits... */
+			newstate = get_fs_byte((unsigned char*)arg);
+			if (server->sign_active) {
+				/* cannot turn signatures OFF when active */
+				if (!newstate) return -EINVAL;
+			} else {
+				server->sign_wanted = newstate != 0;
+			}
+			return 0;
+		}
+
+#endif /* CONFIG_NCPFS_PACKET_SIGNING */
+
+#ifdef CONFIG_NCPFS_IOCTL_LOCKING
+	case NCP_IOC_LOCKUNLOCK:
+		if (   (permission(inode, MAY_WRITE) != 0)
+		    && (current->uid != server->m.mounted_uid))
+		{
+			return -EACCES;
+		}
+		{
+			struct ncp_lock_ioctl	 rqdata;
+			struct nw_file_info	*finfo;
+			int result;
+
+			if ((result = verify_area(VERIFY_READ, 
+					(struct ncp_lock_ioctl*)arg, 
+					sizeof(rqdata))) != 0)
+			{
+				return result;
+			}
+			memcpy_fromfs(&rqdata, (struct ncp_lock_ioctl*)arg,
+				sizeof(rqdata));
+			if (rqdata.origin != 0)
+				return -EINVAL;
+			/* check for cmd */
+			switch (rqdata.cmd) {
+				case NCP_LOCK_EX:
+				case NCP_LOCK_SH:
+						if (rqdata.timeout == 0)
+							rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT;
+						else if (rqdata.timeout > NCP_LOCK_MAX_TIMEOUT)
+							rqdata.timeout = NCP_LOCK_MAX_TIMEOUT;
+						break;
+				case NCP_LOCK_LOG:
+						rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT;	/* has no effect */
+				case NCP_LOCK_CLEAR:
+						break;
+				default:
+						return -EINVAL;
+			}
+			if ((result = ncp_make_open(inode, O_RDWR)) != 0)
+			{
+				return result;
+			}
+			if (!ncp_conn_valid(server))
+			{
+				return -EIO;
+			}
+			if (!S_ISREG(inode->i_mode))
+			{
+				return -EISDIR;
+			}
+			finfo=NCP_FINFO(inode);
+			if (!finfo->opened)
+			{
+				return -EBADFD;
+			}
+			if (rqdata.cmd == NCP_LOCK_CLEAR)
+			{
+				result = ncp_ClearPhysicalRecord(NCP_SERVER(inode),
+							finfo->file_handle, 
+							rqdata.offset,
+							rqdata.length);
+				if (result > 0) result = 0;	/* no such lock */
+			}
+			else
+			{
+				int lockcmd;
+
+				switch (rqdata.cmd)
+				{
+					case NCP_LOCK_EX:  lockcmd=1; break;
+					case NCP_LOCK_SH:  lockcmd=3; break;
+					default:	   lockcmd=0; break;
+				}
+				result = ncp_LogPhysicalRecord(NCP_SERVER(inode),
+							finfo->file_handle,
+							lockcmd,
+							rqdata.offset,
+							rqdata.length,
+							rqdata.timeout);
+				if (result > 0) result = -EAGAIN;
+			}
+			return result;
+		}
+#endif	/* CONFIG_NCPFS_IOCTL_LOCKING */
 
 	default:
 		return -EINVAL;
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/ncpfs/ncplib_kernel.c linux/fs/ncpfs/ncplib_kernel.c
--- linux.vanilla/fs/ncpfs/ncplib_kernel.c	Sun Jun 21 18:40:56 1998
+++ linux/fs/ncpfs/ncplib_kernel.c	Tue Mar  2 00:40:45 1999
@@ -5,6 +5,8 @@
  *
  */
 
+#include <linux/config.h>
+
 #include "ncplib_kernel.h"
 
 typedef __u8  byte;
@@ -129,6 +131,33 @@
 	return *(dword *)(ncp_reply_data(server, offset));
 }
 
+
+/* options: 
+ *	bit 0	ipx checksum
+ *	bit 1	packet signing
+ */
+int
+ncp_negotiate_size_and_options(struct ncp_server *server, 
+	int size, int options, int *ret_size, int *ret_options) {
+	int result;
+
+	ncp_init_request(server);
+	ncp_add_word(server, htons(size));
+	ncp_add_byte(server, options);
+	
+	if ((result = ncp_request(server, 0x61)) != 0)
+	{
+		ncp_unlock_server(server);
+		return result;
+	}
+
+	*ret_size = min(ntohs(ncp_reply_word(server, 0)), size);
+	*ret_options = ncp_reply_byte(server, 4);
+
+	ncp_unlock_server(server);
+	return 0;
+}
+
 int
 ncp_negotiate_buffersize(struct ncp_server *server,
 			 int size, int *target)
@@ -261,7 +290,7 @@
 	ncp_add_byte(server, 6); /* subfunction */
 	ncp_add_byte(server, server->name_space[vol_num]);
 	ncp_add_byte(server, server->name_space[vol_num]);
-	ncp_add_word(server, 0xff); /* get all */
+	ncp_add_word(server, 0x8006); /* get all */
 	ncp_add_dword(server, RIM_ALL);
 	ncp_add_handle_path(server, vol_num, dir_base, 1, path);
 
@@ -277,8 +306,9 @@
 }
 
 static inline int
-ncp_has_os2_namespace(struct ncp_server *server, __u8 volume)
+ncp_get_known_namespace(struct ncp_server *server, __u8 volume)
 {
+#if defined(CONFIG_NCPFS_OS2_NS) || defined(CONFIG_NCPFS_NFS_NS)
 	int result;
 	__u8 *namespace;
 	__u16 no_namespaces;
@@ -291,9 +321,10 @@
 	if ((result = ncp_request(server, 87)) != 0)
 	{
 		ncp_unlock_server(server);
-		return 0;
+		return NW_NS_DOS;
 	}
 
+	result=NW_NS_DOS;
 	no_namespaces = ncp_reply_word(server, 0);
 	namespace = ncp_reply_data(server, 2);
 
@@ -301,16 +332,104 @@
 	{
 		DPRINTK("get_namespaces: found %d on %d\n", *namespace,volume);
 
-		if (*namespace == 4)
+#ifdef CONFIG_NCPFS_NFS_NS
+		if ((*namespace == NW_NS_NFS) && !(server->m.flags&NCP_MOUNT_NO_NFS))
+		{
+			result = NW_NS_NFS;
+			break;
+		}
+#endif	/* CONFIG_NCPFS_NFS_NS */
+#ifdef CONFIG_NCPFS_OS2_NS
+		if ((*namespace == NW_NS_OS2) && !(server->m.flags&NCP_MOUNT_NO_OS2))
 		{
-			DPRINTK("get_namespaces: found OS2\n");
-			ncp_unlock_server(server);
-			return 1;
+			result = NW_NS_OS2;
 		}
+#endif	/* CONFIG_NCPFS_OS2_NS */
 		namespace += 1;
 		no_namespaces -= 1;
 	}
 	ncp_unlock_server(server);
+	return result;
+#else	/* neither OS2 nor NFS - only DOS */
+	return NW_NS_DOS;
+#endif	/* defined(CONFIG_NCPFS_OS2_NS) || defined(CONFIG_NCPFS_NFS_NS) */
+}
+
+static int
+ncp_ObtainSpecificDirBase(struct ncp_server *server,
+		__u8 nsSrc, __u8 nsDst, __u8 vol_num, __u32 dir_base,
+		char *path, /* At most 1 component */
+		__u32 *dirEntNum)
+{
+	int result;
+
+	ncp_init_request(server);
+	ncp_add_byte(server, 6); /* subfunction */
+	ncp_add_byte(server, nsSrc);
+	ncp_add_byte(server, nsDst);
+	ncp_add_word(server, 0x8006); /* get all */
+	ncp_add_dword(server, RIM_ALL);
+	ncp_add_handle_path(server, vol_num, dir_base, 1, path);
+
+	if ((result = ncp_request(server, 87)) != 0)
+	{
+		ncp_unlock_server(server);
+		return result;
+	}
+
+	if (dirEntNum)
+		*dirEntNum = ncp_reply_dword(server, 0x30);
+	ncp_unlock_server(server);
+	return 0;
+}
+
+int
+ncp_mount_subdir(struct ncp_server *server,
+		__u8 volNumber,
+		__u8 srcNS, __u32 dirEntNum)
+{
+	int dstNS;
+	int result;
+	__u32 newDirEnt;
+	
+	dstNS = ncp_get_known_namespace(server, volNumber);
+	if ((result = ncp_ObtainSpecificDirBase(server, srcNS, dstNS, volNumber, 
+				      dirEntNum, NULL, &newDirEnt)) != 0)
+	{
+		return result;
+	}
+	server->name_space[volNumber] = dstNS;
+	server->root.finfo.i.volNumber = volNumber;
+	server->root.finfo.i.dirEntNum = newDirEnt;
+	server->m.mounted_vol[1] = 0;
+	server->m.mounted_vol[0] = 'X';
+	return 0;
+}
+
+static int
+ncp_obtain_DOS_dir_base(struct ncp_server *server,
+		struct nw_info_struct* file,
+		char *path, /* At most 1 component */
+		__u32 *DOS_dir_base)
+{
+	int result;
+
+	ncp_init_request(server);
+	ncp_add_byte(server, 6); /* subfunction */
+	ncp_add_byte(server, server->name_space[file->volNumber]);
+	ncp_add_byte(server, server->name_space[file->volNumber]);
+	ncp_add_word(server, 0x8006); /* get all */
+	ncp_add_dword(server, RIM_DIRECTORY);
+	ncp_add_handle_path(server, file->volNumber, file->dirEntNum, 1, path);
+
+	if ((result = ncp_request(server, 87)) != 0)
+	{
+		ncp_unlock_server(server);
+		return result;
+	}
+
+   	if (DOS_dir_base) *DOS_dir_base=ncp_reply_dword(server, 0x34);
+	ncp_unlock_server(server);
 	return 0;
 }
 
@@ -348,7 +467,8 @@
 	target->volNumber = volnum = ncp_reply_byte(server, 8);
 	ncp_unlock_server(server);
 
-	server->name_space[volnum] = ncp_has_os2_namespace(server,volnum)?4:0;
+	server->name_space[volnum] =
+		ncp_get_known_namespace(server, volnum);
 
 	DPRINTK("lookup_vol: namespace[%d] = %d\n",
 		volnum, server->name_space[volnum]);
@@ -383,25 +503,44 @@
 	return result;
 }
 
-int
-ncp_del_file_or_subdir(struct ncp_server *server,
-		       struct nw_info_struct *dir, char *name)
+static int
+ncp_DeleteNSEntry(struct ncp_server *server,
+		  __u8 have_dir_base, __u8 volume, __u32 dir_base,
+		  char* name, __u8 ns, int attr)
 {
 	int result;
 
 	ncp_init_request(server);
 	ncp_add_byte(server, 8); /* subfunction */
-	ncp_add_byte(server, server->name_space[dir->volNumber]);
+	ncp_add_byte(server, ns); /* namespace */
 	ncp_add_byte(server, 0); /* reserved */
-	ncp_add_word(server, 0x8006); /* search attribs: all */
-	ncp_add_handle_path(server, dir->volNumber,
-			    dir->dirEntNum, 1, name);
+	ncp_add_word(server, attr); /* search attribs */
+	ncp_add_handle_path(server, volume, dir_base, have_dir_base, name);
 	
 	result = ncp_request(server, 87);
 	ncp_unlock_server(server);
 	return result;
 }
 
+int
+ncp_del_file_or_subdir(struct ncp_server *server,
+		       struct nw_info_struct *dir, char *name)
+{
+#ifdef CONFIG_NCPFS_NFS_NS
+	if (server->name_space[dir->volNumber]==NW_NS_NFS)
+	{
+		__u32 DOS_dir_base;
+		int result;
+
+		result=ncp_obtain_DOS_dir_base(server, dir, name, &DOS_dir_base);
+		if (result) return result;
+		return ncp_DeleteNSEntry(server, 1, dir->volNumber, DOS_dir_base, NULL, NW_NS_DOS, 0x8006);
+	}
+	else
+#endif	/* CONFIG_NCPFS_NFS_NS */
+		return ncp_DeleteNSEntry(server, 1, dir->volNumber, dir->dirEntNum, name, server->name_space[dir->volNumber], 0x8006);
+}
+
 static inline void
 ConvertToNWfromDWORD ( __u32 sfd , __u8 ret[6] )
 {
@@ -428,7 +567,7 @@
 	if ((create_attributes & aDIR) != 0)
 	{
 		search_attribs |= 0x8000;	
-}
+        }
 
 	ncp_init_request(server);
 	ncp_add_byte(server, 1); /* subfunction */
@@ -464,7 +603,7 @@
 	if (dir != NULL)
 	{
 		/* in target there's a new finfo to fill */
-		ncp_extract_file_info(ncp_reply_data(server, 5), &(target->i));
+		ncp_extract_file_info(ncp_reply_data(server, 6), &(target->i));
 	}
 
 	ConvertToNWfromDWORD(target->server_file_handle, target->file_handle);
@@ -511,12 +650,21 @@
 	ncp_add_byte(server, 3); /* subfunction */
 	ncp_add_byte(server, server->name_space[seq->volNumber]);
 	ncp_add_byte(server, 0); /* data stream (???) */
-	ncp_add_word(server, 0xffff); /* Search attribs */
+	ncp_add_word(server, 0x8006); /* Search attribs */
 	ncp_add_dword(server, RIM_ALL);	/* return info mask */
 	ncp_add_mem(server, seq, 9);
-	ncp_add_byte(server, 2); /* 2 byte pattern */
-	ncp_add_byte(server, 0xff); /* following is a wildcard */
-	ncp_add_byte(server, '*');
+#ifdef CONFIG_NCPFS_NFS_NS
+	if (server->name_space[seq->volNumber]==NW_NS_NFS)
+	{
+		ncp_add_byte(server, 0);
+	}
+	else
+#endif	/* CONFIG_NCPFS_NFS_NS */
+	{
+		ncp_add_byte(server, 2); /* 2 byte pattern */
+		ncp_add_byte(server, 0xff); /* following is a wildcard */
+		ncp_add_byte(server, '*');
+	}
 	
 	if ((result = ncp_request(server, 87)) != 0)
 	{
@@ -532,9 +680,9 @@
 }
 
 int
-ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
-			      struct nw_info_struct *old_dir, char *old_name,
-			      struct nw_info_struct *new_dir, char *new_name)
+ncp_RenameNSEntry(struct ncp_server *server,
+		  struct nw_info_struct *old_dir, char *old_name, int old_type,
+		  struct nw_info_struct *new_dir, char *new_name)
 {
 	int result;
 
@@ -546,7 +694,7 @@
 	ncp_add_byte(server, 4); /* subfunction */
 	ncp_add_byte(server, server->name_space[old_dir->volNumber]);
 	ncp_add_byte(server, 1); /* rename flag */
-	ncp_add_word(server, 0x8006); /* search attributes */
+	ncp_add_word(server, old_type); /* search attributes */
 
 	/* source Handle Path */
 	ncp_add_byte(server, old_dir->volNumber);
@@ -570,6 +718,31 @@
 	return result;
 }
 	
+int
+ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
+			      struct nw_info_struct *old_dir, char *old_name,
+			      struct nw_info_struct *new_dir, char *new_name)
+{
+        int result;
+        int old_type = 0x0006;
+
+/* If somebody can do it atomic, call me... vandrove@vc.cvut.cz */
+	result = ncp_RenameNSEntry(server, old_dir, old_name, old_type,
+	                                   new_dir, new_name);
+        if (result == 0xFF)	/* File Not Found, try directory */
+	{
+		old_type = 0x0016;
+		result = ncp_RenameNSEntry(server, old_dir, old_name, old_type,
+						   new_dir, new_name);
+	}
+	if (result != 0x92) return result;	/* All except NO_FILES_RENAMED */
+	result = ncp_del_file_or_subdir(server, new_dir, new_name);
+	if (result != 0) return -EACCES;
+	result = ncp_RenameNSEntry(server, old_dir, old_name, old_type,
+					   new_dir, new_name);
+	return result;
+}
+	
 
 /* We have to transfer to/from user space */
 int
@@ -624,4 +797,49 @@
 	ncp_unlock_server(server);
 	return 0;
 }
+
+#ifdef CONFIG_NCPFS_IOCTL_LOCKING
+int
+ncp_LogPhysicalRecord(struct ncp_server *server, const char *file_id,
+	  __u8 locktype, __u32 offset, __u32 length, __u16 timeout)
+{
+	int result;
+
+	ncp_init_request(server);
+	ncp_add_byte(server, locktype);
+	ncp_add_mem(server, file_id, 6);
+	ncp_add_dword(server, htonl(offset));
+	ncp_add_dword(server, htonl(length));
+	ncp_add_word(server, htons(timeout));
+
+	if ((result = ncp_request(server, 0x1A)) != 0)
+	{
+		ncp_unlock_server(server);
+		return result;
+	}
+	ncp_unlock_server(server);
+	return 0;
+}
+
+int
+ncp_ClearPhysicalRecord(struct ncp_server *server, const char *file_id,
+	  __u32 offset, __u32 length)
+{
+	int result;
+
+	ncp_init_request(server);
+	ncp_add_byte(server, 0);	/* who knows... lanalyzer says that */
+	ncp_add_mem(server, file_id, 6);
+	ncp_add_dword(server, htonl(offset));
+	ncp_add_dword(server, htonl(length));
+
+	if ((result = ncp_request(server, 0x1E)) != 0)
+	{
+		ncp_unlock_server(server);
+		return result;
+	}
+	ncp_unlock_server(server);
+	return 0;
+}
+#endif	/* CONFIG_NCPFS_IOCTL_LOCKING */
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/ncpfs/ncplib_kernel.h linux/fs/ncpfs/ncplib_kernel.h
--- linux.vanilla/fs/ncpfs/ncplib_kernel.h	Sun Jun 21 18:40:56 1998
+++ linux/fs/ncpfs/ncplib_kernel.h	Fri Mar 26 17:23:32 1999
@@ -8,6 +8,8 @@
 #ifndef _NCPLIB_H
 #define _NCPLIB_H
 
+#include <linux/config.h>
+
 #include <linux/fs.h>
 #include <linux/ncp.h>
 #include <linux/ncp_fs.h>
@@ -23,6 +25,9 @@
 #include <linux/ncp.h>
 
 int
+ncp_negotiate_size_and_options(struct ncp_server *server, int size,
+ 			  int options, int *ret_size, int *ret_options);
+int
 ncp_negotiate_buffersize(struct ncp_server *server, int size,
 			 int *target);
 int
@@ -165,5 +170,22 @@
 			      struct nw_info_struct *old_dir, char *old_name,
 			      struct nw_info_struct *new_dir, char *new_name);
 
+#ifdef CONFIG_NCPFS_IOCTL_LOCKING
+int
+ncp_LogPhysicalRecord(struct ncp_server *server,
+		      const char *file_id, __u8 locktype,
+		      __u32 offset, __u32 length, __u16 timeout);
+
+int
+ncp_ClearPhysicalRecord(struct ncp_server *server,
+			const char *file_id,
+			__u32 offset, __u32 length);
+#endif	/* CONFIG_NCPFS_IOCTL_LOCKING */
+
+#ifdef CONFIG_NCPFS_MOUNT_SUBDIR
+int
+ncp_mount_subdir(struct ncp_server* server, __u8 volNumber, 
+		 __u8 srcNS, __u32 srcDirEntNum);
+#endif	/* CONFIG_NCPFS_MOUNT_SUBDIR */
 
 #endif /* _NCPLIB_H */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/ncpfs/ncpsign_kernel.c linux/fs/ncpfs/ncpsign_kernel.c
--- linux.vanilla/fs/ncpfs/ncpsign_kernel.c	Thu Jan  1 01:00:00 1970
+++ linux/fs/ncpfs/ncpsign_kernel.c	Tue Mar  2 00:40:45 1999
@@ -0,0 +1,125 @@
+/*
+ *  ncpsign_kernel.c
+ *
+ *  Arne de Bruijn (arne@knoware.nl), 1997
+ *
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+
+#if 0
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+#endif
+
+#include <linux/string.h>
+#include <linux/ncp.h>
+#if 0
+#include <linux/ncp_fs.h>
+#include <linux/ncp_fs_sb.h>
+#endif
+#include "ncpsign_kernel.h"
+
+#define rol32(i,c) (((((i)&0xffffffff)<<c)&0xffffffff)| \
+                    (((i)&0xffffffff)>>(32-c)))
+/* i386: 32-bit, little endian, handles mis-alignment */
+#ifdef __i386__
+#define GET_LE32(p) (*(int *)(p))
+#define PUT_LE32(p,v) { *(int *)(p)=v; }
+#else
+/* from include/ncplib.h */
+#define BVAL(buf,pos) (((__u8 *)(buf))[pos])
+#define PVAL(buf,pos) ((unsigned)BVAL(buf,pos))
+#define BSET(buf,pos,val) (BVAL(buf,pos) = (val))
+
+static inline word
+WVAL_LH(__u8 * buf, int pos)
+{
+	return PVAL(buf, pos) | PVAL(buf, pos + 1) << 8;
+}
+static inline dword
+DVAL_LH(__u8 * buf, int pos)
+{
+	return WVAL_LH(buf, pos) | WVAL_LH(buf, pos + 2) << 16;
+}
+static inline void
+WSET_LH(__u8 * buf, int pos, word val)
+{
+	BSET(buf, pos, val & 0xff);
+	BSET(buf, pos + 1, val >> 8);
+}
+static inline void
+DSET_LH(__u8 * buf, int pos, dword val)
+{
+	WSET_LH(buf, pos, val & 0xffff);
+	WSET_LH(buf, pos + 2, val >> 16);
+}
+
+#define GET_LE32(p) DVAL_LH(p,0)
+#define PUT_LE32(p,v) DSET_LH(p,0,v)
+#endif
+
+#define min(a,b) ((a)<(b)?(a):(b))
+
+static void nwsign(char *r_data1, char *r_data2, char *outdata) {
+ int i;
+ unsigned int w0,w1,w2,w3;
+ static int rbit[4]={0, 2, 1, 3};
+#ifdef __i386__
+ unsigned int *data2=(int *)r_data2;
+#else
+ unsigned int data2[16];
+ for (i=0;i<16;i++)
+  data2[i]=GET_LE32(r_data2+(i<<2));
+#endif 
+ w0=GET_LE32(r_data1);
+ w1=GET_LE32(r_data1+4);
+ w2=GET_LE32(r_data1+8);
+ w3=GET_LE32(r_data1+12);
+ for (i=0;i<16;i+=4) {
+  w0=rol32(w0 + ((w1 & w2) | ((~w1) & w3)) + data2[i+0],3);
+  w3=rol32(w3 + ((w0 & w1) | ((~w0) & w2)) + data2[i+1],7);
+  w2=rol32(w2 + ((w3 & w0) | ((~w3) & w1)) + data2[i+2],11);
+  w1=rol32(w1 + ((w2 & w3) | ((~w2) & w0)) + data2[i+3],19);
+ }
+ for (i=0;i<4;i++) {
+  w0=rol32(w0 + (((w2 | w3) & w1) | (w2 & w3)) + 0x5a827999 + data2[i+0],3);
+  w3=rol32(w3 + (((w1 | w2) & w0) | (w1 & w2)) + 0x5a827999 + data2[i+4],5);
+  w2=rol32(w2 + (((w0 | w1) & w3) | (w0 & w1)) + 0x5a827999 + data2[i+8],9);
+  w1=rol32(w1 + (((w3 | w0) & w2) | (w3 & w0)) + 0x5a827999 + data2[i+12],13);
+ }
+ for (i=0;i<4;i++) {
+  w0=rol32(w0 + ((w1 ^ w2) ^ w3) + 0x6ed9eba1 + data2[rbit[i]+0],3);
+  w3=rol32(w3 + ((w0 ^ w1) ^ w2) + 0x6ed9eba1 + data2[rbit[i]+8],9);
+  w2=rol32(w2 + ((w3 ^ w0) ^ w1) + 0x6ed9eba1 + data2[rbit[i]+4],11);
+  w1=rol32(w1 + ((w2 ^ w3) ^ w0) + 0x6ed9eba1 + data2[rbit[i]+12],15);
+ }
+ PUT_LE32(outdata,(w0+GET_LE32(r_data1)) & 0xffffffff);
+ PUT_LE32(outdata+4,(w1+GET_LE32(r_data1+4)) & 0xffffffff);
+ PUT_LE32(outdata+8,(w2+GET_LE32(r_data1+8)) & 0xffffffff);
+ PUT_LE32(outdata+12,(w3+GET_LE32(r_data1+12)) & 0xffffffff);
+}
+
+/* Make a signature for the current packet and add it at the end of the */
+/* packet. */
+void sign_packet(struct ncp_server *server, int *size) {
+ char data[64];
+
+ memset(data,0,64);
+ memcpy(data,server->sign_root,8);
+ PUT_LE32(data+8,(*size));
+ memcpy(data+12,server->packet+sizeof(struct ncp_request_header)-1,
+  min((*size)-sizeof(struct ncp_request_header)+1,52));
+
+ nwsign(server->sign_last,data,server->sign_last);
+
+ memcpy(server->packet+(*size),server->sign_last,8);
+ (*size)+=8;
+}
+
+#endif	/* CONFIG_NCPFS_PACKET_SIGNING */
+
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/ncpfs/ncpsign_kernel.h linux/fs/ncpfs/ncpsign_kernel.h
--- linux.vanilla/fs/ncpfs/ncpsign_kernel.h	Thu Jan  1 01:00:00 1970
+++ linux/fs/ncpfs/ncpsign_kernel.h	Fri Mar 26 17:23:38 1999
@@ -0,0 +1,16 @@
+/*
+ *  ncpsign_kernel.h
+ *
+ *  Arne de Bruijn (arne@knoware.nl), 1997
+ *
+ */
+ 
+#ifndef _NCPSIGN_KERNEL_H
+#define _NCPSIGN_KERNEL_H
+
+#include <linux/ncp_fs.h>
+#include <linux/ncp_fs_sb.h>
+
+void sign_packet(struct ncp_server *server, int *size);
+
+#endif
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/ncpfs/sock.c linux/fs/ncpfs/sock.c
--- linux.vanilla/fs/ncpfs/sock.c	Sun Jun 21 18:40:56 1998
+++ linux/fs/ncpfs/sock.c	Tue Mar  2 00:40:55 1999
@@ -7,6 +7,8 @@
  *
  */
 
+#include <linux/config.h>
+
 #include <linux/sched.h>
 #include <linux/ncp_fs.h>
 #include <linux/errno.h>
@@ -25,6 +27,7 @@
 #include <linux/ncp_fs_sb.h>
 #include <net/sock.h>
 
+#include "ncpsign_kernel.h"
 
 #define _S(nr) (1<<((nr)-1))
 static int _recvfrom(struct socket *sock, unsigned char *ubuf,
@@ -562,7 +565,12 @@
 		printk("ncpfs: Server not locked!\n");
 		return -EIO;
 	}
-
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+	if (server->sign_active)
+	{
+		sign_packet(server, &size);
+	}
+#endif /* CONFIG_NCPFS_PACKET_SIGNING */
 	if (!ncp_conn_valid(server))
 	{
 		return -EIO;
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/nls.c linux/fs/nls.c
--- linux.vanilla/fs/nls.c	Sun Jun 21 18:40:56 1998
+++ linux/fs/nls.c	Fri Jan 29 14:31:42 1999
@@ -455,6 +455,9 @@
 #ifdef CONFIG_NLS_ISO8859_9
 	init_nls_iso8859_9();
 #endif
+#ifdef CONFIG_NLS_ISO8859_15
+	init_nls_iso8859_15();
+#endif
 #ifdef CONFIG_NLS_CODEPAGE_437
 	init_nls_cp437();
 #endif
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/nls_iso8859_15.c linux/fs/nls_iso8859_15.c
--- linux.vanilla/fs/nls_iso8859_15.c	Thu Jan  1 01:00:00 1970
+++ linux/fs/nls_iso8859_15.c	Tue Mar  2 00:40:55 1999
@@ -0,0 +1,268 @@
+/*
+ * linux/fs/nls_iso8859-15.c
+ *
+ * Charset iso8859-15 translation tables.
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+
+static struct nls_unicode charset2uni[256] = {
+	/* 0x00*/
+	{0x00, 0x00}, {0x01, 0x00}, {0x02, 0x00}, {0x03, 0x00},
+	{0x04, 0x00}, {0x05, 0x00}, {0x06, 0x00}, {0x07, 0x00},
+	{0x08, 0x00}, {0x09, 0x00}, {0x0a, 0x00}, {0x0b, 0x00},
+	{0x0c, 0x00}, {0x0d, 0x00}, {0x0e, 0x00}, {0x0f, 0x00},
+	/* 0x10*/
+	{0x10, 0x00}, {0x11, 0x00}, {0x12, 0x00}, {0x13, 0x00},
+	{0x14, 0x00}, {0x15, 0x00}, {0x16, 0x00}, {0x17, 0x00},
+	{0x18, 0x00}, {0x19, 0x00}, {0x1a, 0x00}, {0x1b, 0x00},
+	{0x1c, 0x00}, {0x1d, 0x00}, {0x1e, 0x00}, {0x1f, 0x00},
+	/* 0x20*/
+	{0x20, 0x00}, {0x21, 0x00}, {0x22, 0x00}, {0x23, 0x00},
+	{0x24, 0x00}, {0x25, 0x00}, {0x26, 0x00}, {0x27, 0x00},
+	{0x28, 0x00}, {0x29, 0x00}, {0x2a, 0x00}, {0x2b, 0x00},
+	{0x2c, 0x00}, {0x2d, 0x00}, {0x2e, 0x00}, {0x2f, 0x00},
+	/* 0x30*/
+	{0x30, 0x00}, {0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00},
+	{0x34, 0x00}, {0x35, 0x00}, {0x36, 0x00}, {0x37, 0x00},
+	{0x38, 0x00}, {0x39, 0x00}, {0x3a, 0x00}, {0x3b, 0x00},
+	{0x3c, 0x00}, {0x3d, 0x00}, {0x3e, 0x00}, {0x3f, 0x00},
+	/* 0x40*/
+	{0x40, 0x00}, {0x41, 0x00}, {0x42, 0x00}, {0x43, 0x00},
+	{0x44, 0x00}, {0x45, 0x00}, {0x46, 0x00}, {0x47, 0x00},
+	{0x48, 0x00}, {0x49, 0x00}, {0x4a, 0x00}, {0x4b, 0x00},
+	{0x4c, 0x00}, {0x4d, 0x00}, {0x4e, 0x00}, {0x4f, 0x00},
+	/* 0x50*/
+	{0x50, 0x00}, {0x51, 0x00}, {0x52, 0x00}, {0x53, 0x00},
+	{0x54, 0x00}, {0x55, 0x00}, {0x56, 0x00}, {0x57, 0x00},
+	{0x58, 0x00}, {0x59, 0x00}, {0x5a, 0x00}, {0x5b, 0x00},
+	{0x5c, 0x00}, {0x5d, 0x00}, {0x5e, 0x00}, {0x5f, 0x00},
+	/* 0x60*/
+	{0x60, 0x00}, {0x61, 0x00}, {0x62, 0x00}, {0x63, 0x00},
+	{0x64, 0x00}, {0x65, 0x00}, {0x66, 0x00}, {0x67, 0x00},
+	{0x68, 0x00}, {0x69, 0x00}, {0x6a, 0x00}, {0x6b, 0x00},
+	{0x6c, 0x00}, {0x6d, 0x00}, {0x6e, 0x00}, {0x6f, 0x00},
+	/* 0x70*/
+	{0x70, 0x00}, {0x71, 0x00}, {0x72, 0x00}, {0x73, 0x00},
+	{0x74, 0x00}, {0x75, 0x00}, {0x76, 0x00}, {0x77, 0x00},
+	{0x78, 0x00}, {0x79, 0x00}, {0x7a, 0x00}, {0x7b, 0x00},
+	{0x7c, 0x00}, {0x7d, 0x00}, {0x7e, 0x00}, {0x7f, 0x00},
+	/* 0x80*/
+	{0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},
+	{0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},
+	{0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},
+	{0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},
+	/* 0x90*/
+	{0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},
+	{0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},
+	{0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},
+	{0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},
+	/* 0xa0*/
+	{0xa0, 0x00}, {0xa1, 0x00}, {0xa2, 0x00}, {0xa3, 0x00},
+	{0xac, 0x20}, {0xa5, 0x00}, {0x60, 0x01}, {0xa7, 0x00},
+	{0x61, 0x01}, {0xa9, 0x00}, {0xaa, 0x00}, {0xab, 0x00},
+	{0xac, 0x00}, {0xad, 0x00}, {0xae, 0x00}, {0xaf, 0x00},
+	/* 0xb0*/
+	{0xb0, 0x00}, {0xb1, 0x00}, {0xb2, 0x00}, {0xb3, 0x00},
+	{0x7d, 0x01}, {0xb5, 0x00}, {0xb6, 0x00}, {0xb7, 0x00},
+	{0x7e, 0x01}, {0xb9, 0x00}, {0xba, 0x00}, {0xbb, 0x00},
+	{0x52, 0x01}, {0x53, 0x01}, {0x78, 0x01}, {0xbf, 0x00},
+	/* 0xc0*/
+	{0xc0, 0x00}, {0xc1, 0x00}, {0xc2, 0x00}, {0xc3, 0x00},
+	{0xc4, 0x00}, {0xc5, 0x00}, {0xc6, 0x00}, {0xc7, 0x00},
+	{0xc8, 0x00}, {0xc9, 0x00}, {0xca, 0x00}, {0xcb, 0x00},
+	{0xcc, 0x00}, {0xcd, 0x00}, {0xce, 0x00}, {0xcf, 0x00},
+	/* 0xd0*/
+	{0xd0, 0x00}, {0xd1, 0x00}, {0xd2, 0x00}, {0xd3, 0x00},
+	{0xd4, 0x00}, {0xd5, 0x00}, {0xd6, 0x00}, {0xd7, 0x00},
+	{0xd8, 0x00}, {0xd9, 0x00}, {0xda, 0x00}, {0xdb, 0x00},
+	{0xdc, 0x00}, {0xdd, 0x00}, {0xde, 0x00}, {0xdf, 0x00},
+	/* 0xe0*/
+	{0xe0, 0x00}, {0xe1, 0x00}, {0xe2, 0x00}, {0xe3, 0x00},
+	{0xe4, 0x00}, {0xe5, 0x00}, {0xe6, 0x00}, {0xe7, 0x00},
+	{0xe8, 0x00}, {0xe9, 0x00}, {0xea, 0x00}, {0xeb, 0x00},
+	{0xec, 0x00}, {0xed, 0x00}, {0xee, 0x00}, {0xef, 0x00},
+	/* 0xf0*/
+	{0xf0, 0x00}, {0xf1, 0x00}, {0xf2, 0x00}, {0xf3, 0x00},
+	{0xf4, 0x00}, {0xf5, 0x00}, {0xf6, 0x00}, {0xf7, 0x00},
+	{0xf8, 0x00}, {0xf9, 0x00}, {0xfa, 0x00}, {0xfb, 0x00},
+	{0xfc, 0x00}, {0xfd, 0x00}, {0xfe, 0x00}, {0xff, 0x00},
+};
+
+static unsigned char page00[256] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+	0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+	0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+	0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+	0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+	0xa0, 0xa1, 0xa2, 0xa3, 0x00, 0xa5, 0x00, 0xa7, /* 0xa0-0xa7 */
+	0x00, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+	0xb0, 0xb1, 0xb2, 0xb3, 0x00, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+	0x00, 0xb9, 0xba, 0xbb, 0x00, 0x00, 0x00, 0xbf, /* 0xb8-0xbf */
+	0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+	0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+	0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+	0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+	0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+	0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char page01[256] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+	0x00, 0x00, 0xbc, 0xbd, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+	0xa6, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+	0xbe, 0x00, 0x00, 0x00, 0x00, 0xb4, 0xb8, 0x00, /* 0x78-0x7f */
+
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char page20[256] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+	0x00, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char *page_uni2charset[256] = {
+	page00, page01,	NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   
+	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,
+
+	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,
+	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,
+
+	page20,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,
+	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,
+};
+
+static void inc_use_count(void)
+{
+	MOD_INC_USE_COUNT;
+}
+
+static void dec_use_count(void)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+static struct nls_table table = {
+	"iso8859-15",
+	page_uni2charset,
+	charset2uni,
+	inc_use_count,
+	dec_use_count,
+	NULL
+};
+
+int init_nls_iso8859_15(void)
+{
+	return register_nls(&table);
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	return init_nls_iso8859_15();
+}
+
+
+void cleanup_module(void)
+{
+	unregister_nls(&table);
+	return;
+}
+#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/proc/array.c linux/fs/proc/array.c
--- linux.vanilla/fs/proc/array.c	Sun Dec  6 00:14:46 1998
+++ linux/fs/proc/array.c	Sun Dec 27 20:49:55 1998
@@ -60,6 +60,7 @@
 int get_malloc(char * buffer);
 #endif
 
+extern unsigned long get_wchan(struct task_struct *);
 
 static int read_core(struct inode * inode, struct file * file,char * buf, int count)
 {
@@ -409,55 +410,6 @@
 	if (!p || !*p || !(*p)->mm)
 		return 0;
 	return get_array(p, (*p)->mm->arg_start, (*p)->mm->arg_end, buffer);
-}
-
-unsigned long get_wchan(struct task_struct *p)
-{
-	if (!p || p == current || p->state == TASK_RUNNING)
-		return 0;
-#if defined(__i386__)
-	{
-		unsigned long ebp, eip;
-		unsigned long stack_page;
-		int count = 0;
-
-		stack_page = p->kernel_stack_page;
-		if (!stack_page)
-			return 0;
-		ebp = p->tss.ebp;
-		do {
-			if (ebp < stack_page || ebp >= 4092+stack_page)
-				return 0;
-			eip = *(unsigned long *) (ebp+4);
-			if (eip < (unsigned long) interruptible_sleep_on
-			    || eip >= (unsigned long) add_timer)
-				return eip;
-			ebp = *(unsigned long *) ebp;
-		} while (count++ < 16);
-	}
-#elif defined(__alpha__)
-	/*
-	 * This one depends on the frame size of schedule().  Do a
-	 * "disass schedule" in gdb to find the frame size.  Also, the
-	 * code assumes that sleep_on() follows immediately after
-	 * interruptible_sleep_on() and that add_timer() follows
-	 * immediately after interruptible_sleep().  Ugly, isn't it?
-	 * Maybe adding a wchan field to task_struct would be better,
-	 * after all...
-	 */
-	{
-	    unsigned long schedule_frame;
-	    unsigned long pc;
-
-	    pc = thread_saved_pc(&p->tss);
-	    if (pc >= (unsigned long) interruptible_sleep_on && pc < (unsigned long) add_timer) {
-		schedule_frame = ((unsigned long *)p->tss.ksp)[6];
-		return ((unsigned long *)schedule_frame)[12];
-	    }
-	    return pc;
-	}
-#endif
-	return 0;
 }
 
 #if defined(__i386__)
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/read_write.c linux/fs/read_write.c
--- linux.vanilla/fs/read_write.c	Sun Dec  6 00:14:46 1998
+++ linux/fs/read_write.c	Fri Dec 11 20:33:58 1998
@@ -254,7 +254,7 @@
 			return retval;
 	}
 
-	retval = locks_verify_area(type == VERIFY_READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE,
+	retval = locks_verify_area(type == VERIFY_WRITE ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE,
 				   inode, file, file->f_pos, tot_len);
 	if (retval)
 		return retval;
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/asm-alpha/ioctls.h linux/include/asm-alpha/ioctls.h
--- linux.vanilla/include/asm-alpha/ioctls.h	Sun Jun 21 18:41:00 1998
+++ linux/include/asm-alpha/ioctls.h	Fri Jan 29 14:27:56 1999
@@ -83,6 +83,8 @@
 #define TIOCGETD	0x5424
 #define TCSBRKP		0x5425	/* Needed for POSIX tcsendbreak() */
 #define TIOCTTYGSTRUCT	0x5426  /* For debugging only */
+#define TIOCSBRK	0x5427  /* BSD compatibility */
+#define TIOCCBRK	0x5428  /* BSD compatibility */
 
 #define TIOCSERCONFIG	0x5453
 #define TIOCSERGWILD	0x5454
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/asm-i386/bugs.h linux/include/asm-i386/bugs.h
--- linux.vanilla/include/asm-i386/bugs.h	Sun Dec  6 00:14:46 1998
+++ linux/include/asm-i386/bugs.h	Fri Mar 26 17:13:59 1999
@@ -132,6 +132,24 @@
 extern int pentium_f00f_bug;
 extern void trap_init_f00f_bug(void);
 
+/*
+ * Access to machine-specific registers (available on 586 and better only)
+ * Note: the rd* operations modify the parameters directly (without using
+ * pointer indirection), this allows gcc to optimize better
+ * Code from Richard Gooch's 2.2 MTRR drivers.
+ */
+ 
+#define rdmsr(msr,val1,val2) \
+       __asm__ __volatile__("rdmsr" \
+			    : "=a" (val1), "=d" (val2) \
+			    : "c" (msr))
+
+#define wrmsr(msr,val1,val2) \
+     __asm__ __volatile__("wrmsr" \
+			  : /* no outputs */ \
+			  : "c" (msr), "a" (val1), "d" (val2))
+
+
 static void check_pentium_f00f(void)
 {
 	/*
@@ -143,6 +161,26 @@
 		pentium_f00f_bug = 1;
 		trap_init_f00f_bug();
 	}
+}
+
+static void check_privacy(void)
+{
+	/*
+	 * Pentium III or higher - processors with mtrrs/cpuid
+	 */
+	if(memcmp(x86_vendor_id, "GenuineIntel", 12))
+		return;
+	if(x86_capability & (1<<18))
+	{
+		/*
+		 *	Thanks to Phil Karn for this bit.
+		 */
+		unsigned long lo,hi;
+		rdmsr(0x119,lo,hi);
+		lo |= 0x200000;
+		wrmsr(0x119,lo,hi);
+		printk(KERN_INFO "Pentium-III serial number disabled.\n");
+	}		
 }
 
 /*
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/asm-i386/ioctls.h linux/include/asm-i386/ioctls.h
--- linux.vanilla/include/asm-i386/ioctls.h	Sun Jun 21 18:40:58 1998
+++ linux/include/asm-i386/ioctls.h	Sun Dec 27 20:46:14 1998
@@ -44,6 +44,8 @@
 #define TIOCGETD	0x5424
 #define TCSBRKP		0x5425	/* Needed for POSIX tcsendbreak() */
 #define TIOCTTYGSTRUCT	0x5426  /* For debugging only */
+#define TIOCSBRK        0x5427  /* BSD compatibility */
+#define TIOCCBRK        0x5428  /* BSD compatibility */
 #define FIONCLEX	0x5450  /* these numbers need to be adjusted. */
 #define FIOCLEX		0x5451
 #define FIOASYNC	0x5452
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/asm-i386/page.h linux/include/asm-i386/page.h
--- linux.vanilla/include/asm-i386/page.h	Sun Jun 21 18:40:58 1998
+++ linux/include/asm-i386/page.h	Fri Dec 11 20:35:53 1998
@@ -7,6 +7,7 @@
 #define PAGE_MASK	(~(PAGE_SIZE-1))
 
 #ifdef __KERNEL__
+#ifndef __ASSEMBLY__
 
 #define STRICT_MM_TYPECHECKS
 
@@ -49,12 +50,14 @@
 #define __pgprot(x)	(x)
 
 #endif
+#endif /* !__ASSEMBLY__ */
 
 /* to align the pointer to the (next) page boundary */
 #define PAGE_ALIGN(addr)	(((addr)+PAGE_SIZE-1)&PAGE_MASK)
 
 /* This handles the memory map.. */
-#define PAGE_OFFSET		0
+#define __PAGE_OFFSET		((0x1000-CONFIG_MAX_MEMSIZE)<<20)
+#define PAGE_OFFSET		(0)
 #define MAP_NR(addr)		(((unsigned long)(addr)) >> PAGE_SHIFT)
 
 #endif /* __KERNEL__ */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/asm-i386/pgtable.h linux/include/asm-i386/pgtable.h
--- linux.vanilla/include/asm-i386/pgtable.h	Sun Jun 21 18:40:58 1998
+++ linux/include/asm-i386/pgtable.h	Fri Mar 26 17:14:01 1999
@@ -19,6 +19,8 @@
  * the i386 page table tree.
  */
 
+#ifndef __ASSEMBLY__
+
 /* Caches aren't brain-dead on the intel. */
 #define flush_cache_all()			do { } while (0)
 #define flush_cache_mm(mm)			do { } while (0)
@@ -46,13 +48,13 @@
  * NOTE! The intel "invlpg" semantics are extremely strange. The
  * chip will add the segment base to the memory address, even though
  * no segment checking is done. We correct for this by using an
- * offset of 0x40000000 that will wrap around the kernel segment base
- * of 0xC0000000 to get the correct address (it will always be outside
+ * offset of -__PAGE_OFFSET that will wrap around the kernel segment base
+ * of __PAGE_OFFSET to get the correct address (it will always be outside
  * the kernel segment, but we're only interested in the final linear
  * address.
  */
 #define __invlpg_mem(addr) \
-	(((char *)(addr))[0x40000000])
+	(*((char *)(addr)-__PAGE_OFFSET))
 #define __invlpg(addr) \
 	__asm__ __volatile__("invlpg %0": :"m" (__invlpg_mem(addr)))
 
@@ -177,6 +179,7 @@
 }
 #endif
 #endif
+#endif /* !__ASSEMBLY__ */
 
 
 /* Certain architectures need to do special things when pte's
@@ -203,6 +206,17 @@
 #define PTRS_PER_PMD	1
 #define PTRS_PER_PGD	1024
 
+/*
+ * pgd entries used up by user/kernel:
+ */
+
+#define USER_PGD_PTRS ((unsigned long)__PAGE_OFFSET >> PGDIR_SHIFT)
+#define KERNEL_PGD_PTRS (PTRS_PER_PGD-USER_PGD_PTRS)
+#define __USER_PGD_PTRS (__PAGE_OFFSET >> PGDIR_SHIFT)
+#define __KERNEL_PGD_PTRS (PTRS_PER_PGD-__USER_PGD_PTRS)
+
+#ifndef __ASSEMBLY__
+
 /* Just any arbitrary offset to the start of the vmalloc VM area: the
  * current 8MB value just means that there will be a 8MB "hole" after the
  * physical memory until the kernel virtual memory starts.  That means that
@@ -507,5 +521,7 @@
 #define SWP_TYPE(entry) (((entry) >> 1) & 0x7f)
 #define SWP_OFFSET(entry) ((entry) >> 8)
 #define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << 8))
+
+#endif /* !__ASSEMBLY__ */
 
 #endif /* _I386_PAGE_H */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/asm-i386/processor.h linux/include/asm-i386/processor.h
--- linux.vanilla/include/asm-i386/processor.h	Sun Dec  6 00:14:46 1998
+++ linux/include/asm-i386/processor.h	Fri Dec 11 20:35:53 1998
@@ -83,10 +83,9 @@
 #define MCA_bus__is_a_macro /* for versions in ksyms.c */
 
 /*
- * User space process size: 3GB. This is hardcoded into a few places,
- * so don't change it unless you know what you are doing.
+ * User space process size: 3GB (default).
  */
-#define TASK_SIZE	(0xC0000000UL)
+#define TASK_SIZE	((unsigned long)__PAGE_OFFSET)
 #define MAX_USER_ADDR	TASK_SIZE
 #define MMAP_SEARCH_START (TASK_SIZE/3)
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/asm-i386/system.h linux/include/asm-i386/system.h
--- linux.vanilla/include/asm-i386/system.h	Sun Dec  6 00:14:46 1998
+++ linux/include/asm-i386/system.h	Fri Dec 11 20:35:53 1998
@@ -275,7 +275,7 @@
 	"movb %%ah,%6\n\t" \
 	"rorl $16,%%eax" \
 	: /* no output */ \
-	:"a" (addr+0xc0000000), "m" (*(n)), "m" (*(n+2)), "m" (*(n+4)), \
+	:"a" (addr+__PAGE_OFFSET), "m" (*(n)), "m" (*(n+2)), "m" (*(n+4)), \
 	 "m" (*(n+5)), "m" (*(n+6)), "m" (*(n+7)) \
 	)
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/linux/blk.h linux/include/linux/blk.h
--- linux.vanilla/include/linux/blk.h	Fri Jul 24 17:29:05 1998
+++ linux/include/linux/blk.h	Fri Mar 26 17:13:59 1999
@@ -310,6 +310,16 @@
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
+#elif (MAJOR_NR == COMPAQ_SMART2_MAJOR)
+
+#define DEVICE_NAME "ida"
+#define DEVICE_INTR do_ida
+#define TIMEOUT_VALUE (25*HZ)
+#define DEVICE_REQUEST do_ida_request0
+#define DEVICE_NR(device) (MINOR(device) >> 4)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
 #endif /* MAJOR_NR == whatever */
 
 #if (MAJOR_NR != SCSI_TAPE_MAJOR)
@@ -370,7 +380,7 @@
 /* end_request() - SCSI devices have their own version */
 /*               - IDE drivers have their own copy too */
 
-#if ! SCSI_BLK_MAJOR(MAJOR_NR)
+#if ! SCSI_BLK_MAJOR(MAJOR_NR) && (MAJOR_NR != COMPAQ_SMART2_MAJOR)
 
 #if defined(IDE_DRIVER) && !defined(_IDE_C) /* shared copy for IDE modules */
 void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup);
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/linux/blkdev.h linux/include/linux/blkdev.h
--- linux.vanilla/include/linux/blkdev.h	Fri Jul 24 17:29:05 1998
+++ linux/include/linux/blkdev.h	Fri Mar 26 17:13:59 1999
@@ -25,6 +25,7 @@
 	int errors;
 	unsigned long sector;
 	unsigned long nr_sectors;
+	unsigned long nr_segments;
 	unsigned long current_nr_sectors;
 	char * buffer;
 	struct semaphore * sem;
@@ -62,5 +63,13 @@
 extern int * blksize_size[MAX_BLKDEV];
 
 extern int * hardsect_size[MAX_BLKDEV];
+
+extern int * max_sectors[MAX_BLKDEV];
+
+extern int * max_segments[MAX_BLKDEV];
+
+#define MAX_SECTORS 254
+
+#define MAX_SEGMENTS MAX_SECTORS
 
 #endif
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/linux/cyclades.h linux/include/linux/cyclades.h
--- linux.vanilla/include/linux/cyclades.h	Sun Dec  6 00:14:46 1998
+++ linux/include/linux/cyclades.h	Fri Jan 29 14:26:15 1999
@@ -581,6 +581,7 @@
 #define	CyISA_Ywin	0x2000
 
 #define CyPCI_Ywin 	0x4000
+#define CyPCI_Yctl	0x80
 #define CyPCI_Zctl 	CTRL_WINDOW_SIZE
 #define CyPCI_Zwin 	0x80000
 #define CyPCI_Ze_win 	(2 * CyPCI_Zwin)
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/linux/fd.h linux/include/linux/fd.h
--- linux.vanilla/include/linux/fd.h	Sun Jun 21 18:40:57 1998
+++ linux/include/linux/fd.h	Mon Mar  1 21:24:56 1999
@@ -215,7 +215,7 @@
 	/* Prevent "aliased" accesses. */
 	int fd_ref;
 	int fd_device;
-	int last_checked; /* when was the drive last checked for a disk 
+	unsigned long last_checked; /* when was the drive last checked for a disk 
 			   * change? */
 	
 	char *dmabuf;
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/linux/hdreg.h linux/include/linux/hdreg.h
--- linux.vanilla/include/linux/hdreg.h	Sun Jun 21 18:40:56 1998
+++ linux/include/linux/hdreg.h	Fri Mar 26 17:13:59 1999
@@ -92,11 +92,12 @@
 #define HDIO_GETGEO		0x0301	/* get device geometry */
 #define HDIO_GET_UNMASKINTR	0x0302	/* get current unmask setting */
 #define HDIO_GET_MULTCOUNT	0x0304	/* get current IDE blockmode setting */
-#define HDIO_GET_IDENTITY 	0x0307	/* get IDE identification info */
+#define HDIO_OBSOLETE_IDENTITY	0x0307	/* OBSOLETE, DO NOT USE: returns 142 bytes */
 #define HDIO_GET_KEEPSETTINGS 	0x0308	/* get keep-settings-on-reset flag */
 #define HDIO_GET_32BIT 		0x0309	/* get current io_32bit setting */
 #define HDIO_GET_NOWERR		0x030a	/* get ignore-write-error flag */
 #define HDIO_GET_DMA		0x030b	/* get use-dma flag */
+#define HDIO_GET_IDENTITY	0x030d	/* get IDE identification info */
 #define HDIO_DRIVE_CMD		0x031f	/* execute a special drive command */
 
 /* hd/ide ctl's that pass (arg) non-ptr values are numbered 0x032n/0x033n */
@@ -166,14 +167,54 @@
 	unsigned short  word79;
 	unsigned short  word80;
 	unsigned short  word81;
-	unsigned short  word82;
-	unsigned short  word83;
+	unsigned short  command_sets;	/* bits 0:Smart 1:Security 2:Removable 3:PM */
+	unsigned short  word83;		/* bits 14:Smart Enabled 13:0 zero */
 	unsigned short  word84;
 	unsigned short  word85;
 	unsigned short  word86;
 	unsigned short  word87;
 	unsigned short  dma_ultra;
-	unsigned short  reserved[167];
+	unsigned short	word89;		/* reserved (word 89) */
+	unsigned short	word90;		/* reserved (word 90) */
+	unsigned short	word91;		/* reserved (word 91) */
+	unsigned short	word92;		/* reserved (word 92) */
+	unsigned short	word93;		/* reserved (word 93) */
+	unsigned short	word94;		/* reserved (word 94) */
+	unsigned short	word95;		/* reserved (word 95) */
+	unsigned short	word96;		/* reserved (word 96) */
+	unsigned short	word97;		/* reserved (word 97) */
+	unsigned short	word98;		/* reserved (word 98) */
+	unsigned short	word99;		/* reserved (word 99) */
+	unsigned short	word100;	/* reserved (word 100) */
+	unsigned short	word101;	/* reserved (word 101) */
+	unsigned short	word102;	/* reserved (word 102) */
+	unsigned short	word103;	/* reserved (word 103) */
+	unsigned short	word104;	/* reserved (word 104) */
+	unsigned short	word105;	/* reserved (word 105) */
+	unsigned short	word106;	/* reserved (word 106) */
+	unsigned short	word107;	/* reserved (word 107) */
+	unsigned short	word108;	/* reserved (word 108) */
+	unsigned short	word109;	/* reserved (word 109) */
+	unsigned short	word110;	/* reserved (word 110) */
+	unsigned short	word111;	/* reserved (word 111) */
+	unsigned short	word112;	/* reserved (word 112) */
+	unsigned short	word113;	/* reserved (word 113) */
+	unsigned short	word114;	/* reserved (word 114) */
+	unsigned short	word115;	/* reserved (word 115) */
+	unsigned short	word116;	/* reserved (word 116) */
+	unsigned short	word117;	/* reserved (word 117) */
+	unsigned short	word118;	/* reserved (word 118) */
+	unsigned short	word119;	/* reserved (word 119) */
+	unsigned short	word120;	/* reserved (word 120) */
+	unsigned short	word121;	/* reserved (word 121) */
+	unsigned short	word122;	/* reserved (word 122) */
+	unsigned short	word123;	/* reserved (word 123) */
+	unsigned short	word124;	/* reserved (word 124) */
+	unsigned short	word125;	/* reserved (word 125) */
+	unsigned short	word126;	/* reserved (word 126) */
+	unsigned short	word127;	/* reserved (word 127) */
+	unsigned short	security;	/* bits 0:suuport 1:enabled 2:locked 3:frozen */
+	unsigned short	reserved[127];
 };
 
 #ifdef __KERNEL__
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/linux/icmp.h linux/include/linux/icmp.h
--- linux.vanilla/include/linux/icmp.h	Sun Jun 21 18:40:57 1998
+++ linux/include/linux/icmp.h	Sun Dec  6 03:18:55 1998
@@ -76,10 +76,13 @@
 };
 
 
+#ifdef __KERNEL__
+
 struct icmp_err {
   int		errno;
   unsigned	fatal:1;
 };
 
+#endif
 
 #endif	/* _LINUX_ICMP_H */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/linux/major.h linux/include/linux/major.h
--- linux.vanilla/include/linux/major.h	Fri Jul 24 17:29:05 1998
+++ linux/include/linux/major.h	Fri Jan 29 14:23:29 1999
@@ -65,10 +65,20 @@
 #define IDETAPE_MAJOR	37
 #define Z2RAM_MAJOR	37
 #define RISCOM8_NORMAL_MAJOR 48
+#define DAC960_MAJOR	48	/* 48..55 */
 #define RISCOM8_CALLOUT_MAJOR 49
 #define MKISS_MAJOR	55
 #define APBLOCK_MAJOR   60   /* AP1000 Block device */
 #define DDV_MAJOR       61   /* AP1000 DDV block device */
+
+#define COMPAQ_SMART2_MAJOR	72
+#define COMPAQ_SMART2_MAJOR1	73
+#define COMPAQ_SMART2_MAJOR2	74
+#define COMPAQ_SMART2_MAJOR3	75
+#define COMPAQ_SMART2_MAJOR4	76
+#define COMPAQ_SMART2_MAJOR5	77
+#define COMPAQ_SMART2_MAJOR6	78
+#define COMPAQ_SMART2_MAJOR7	79
 
 #define SPECIALIX_NORMAL_MAJOR 75
 #define SPECIALIX_CALLOUT_MAJOR 76
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/linux/ncp_fs.h linux/include/linux/ncp_fs.h
--- linux.vanilla/include/linux/ncp_fs.h	Sun Jun 21 18:40:57 1998
+++ linux/include/linux/ncp_fs.h	Fri Mar 26 17:15:35 1999
@@ -29,7 +29,7 @@
 struct ncp_fs_info {
 	int    version;
 	struct sockaddr_ipx addr;
-	uid_t  mounted_uid;
+	__kernel_uid_t  mounted_uid;
 	int    connection;	/* Connection number the server assigned us */
 	int    buffer_size;	/* The negotiated buffer size, to be
 				   used for read/write requests! */
@@ -38,13 +38,50 @@
 	__u32  directory_id;
 };	
 
+struct ncp_sign_init
+{
+	char sign_root[8];
+	char sign_last[16];
+};
+
+struct ncp_lock_ioctl
+{
+#define NCP_LOCK_LOG	0
+#define NCP_LOCK_SH	1
+#define NCP_LOCK_EX	2
+#define NCP_LOCK_CLEAR	256
+	int		cmd;
+	int		origin;
+	unsigned int	offset;
+	unsigned int	length;
+#define NCP_LOCK_DEFAULT_TIMEOUT	18
+#define NCP_LOCK_MAX_TIMEOUT		180
+	int		timeout;
+};
+
+struct ncp_setroot_ioctl
+{
+	int		volNumber;
+	int		namespace;
+	__u32		dirEntNum;
+};
+
 #define	NCP_IOC_NCPREQUEST		_IOR('n', 1, struct ncp_ioctl_request)
-#define	NCP_IOC_GETMOUNTUID		_IOW('n', 2, uid_t)
+#define	NCP_IOC_GETMOUNTUID		_IOW('n', 2, __kernel_uid_t)
+#define NCP_IOC_GETMOUNTUID_INT		_IOW('n', 2, unsigned int)
 #define NCP_IOC_CONN_LOGGED_IN          _IO('n', 3)
 
 #define NCP_GET_FS_INFO_VERSION (1)
 #define NCP_IOC_GET_FS_INFO             _IOWR('n', 4, struct ncp_fs_info)
 
+#define NCP_IOC_SIGN_INIT		_IOR('n', 5, struct ncp_sign_init)
+#define NCP_IOC_SIGN_WANTED		_IOR('n', 6, int)
+#define NCP_IOC_SET_SIGN_WANTED		_IOW('n', 6, int)
+
+#define NCP_IOC_LOCKUNLOCK		_IOR('n', 7, struct ncp_lock_ioctl)
+
+#define NCP_IOC_GETROOT			_IOW('n', 8, struct ncp_setroot_ioctl)
+#define NCP_IOC_SETROOT			_IOR('n', 8, struct ncp_setroot_ioctl)
 /*
  * The packet size to allocate. One page should be enough.
  */
@@ -142,6 +179,7 @@
                unsigned int cmd, unsigned long arg);
 
 /* linux/fs/ncpfs/inode.c */
+int ncp_notify_change(struct inode *inode, struct iattr *attr);
 struct super_block *ncp_read_super(struct super_block *sb,
                                    void *raw_data, int silent);
 extern int init_ncp_fs(void);
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/linux/ncp_fs_sb.h linux/include/linux/ncp_fs_sb.h
--- linux.vanilla/include/linux/ncp_fs_sb.h	Sun Jun 21 18:40:57 1998
+++ linux/include/linux/ncp_fs_sb.h	Tue Mar  2 00:40:55 1999
@@ -14,6 +14,7 @@
 #ifdef __KERNEL__
 
 #define NCP_DEFAULT_BUFSIZE 1024
+#define NCP_DEFAULT_OPTIONS 0		/* 2 for packet signatures */
 
 struct ncp_server {
 
@@ -57,6 +58,12 @@
 
         struct ncp_inode_info root;
 	char       root_path;	/* '\0' */
+
+/* info for packet signing */
+	int sign_wanted;        /* 1=Server needs signed packets */
+	int sign_active;        /* 0=don't do signing, 1=do */
+	char sign_root[8];	/* generated from password and encr. key */
+	char sign_last[16];	
 };
 
 static inline int
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/linux/ncp_mount.h linux/include/linux/ncp_mount.h
--- linux.vanilla/include/linux/ncp_mount.h	Sun Jun 21 18:40:57 1998
+++ linux/include/linux/ncp_mount.h	Tue Mar  2 00:40:55 1999
@@ -21,13 +21,16 @@
 /* Values for flags */
 #define NCP_MOUNT_SOFT 0x0001
 #define NCP_MOUNT_INTR 0x0002
+#define NCP_MOUNT_STRONG 0x0004   /* enable delete/rename of r/o files */
+#define NCP_MOUNT_NO_OS2 0x0008
+#define NCP_MOUNT_NO_NFS 0x0010
 
 struct ncp_mount_data {
 	int version;
 	unsigned int ncp_fd;	/* The socket to the ncp port */
 	unsigned int wdog_fd;	/* Watchdog packets come here */
 	unsigned int message_fd; /* Message notifications come here */
-        uid_t mounted_uid;      /* Who may umount() this filesystem? */
+        __kernel_uid_t mounted_uid;      /* Who may umount() this filesystem? */
 
 	struct sockaddr_ipx serv_addr;
 	unsigned char server_name[NCP_BINDERY_NAME_LEN];
@@ -40,10 +43,10 @@
 	unsigned int retry_count; /* And how often should I retry? */
 	unsigned int flags;
 
-        uid_t uid;
-        gid_t gid;
-        mode_t file_mode;
-        mode_t dir_mode;
+        __kernel_uid_t uid;
+        __kernel_gid_t gid;
+        __kernel_mode_t file_mode;
+        __kernel_mode_t dir_mode;
 };
 
 #endif
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/linux/nls.h linux/include/linux/nls.h
--- linux.vanilla/include/linux/nls.h	Sun Jun 21 18:40:58 1998
+++ linux/include/linux/nls.h	Mon Dec 14 18:36:33 1998
@@ -58,5 +58,6 @@
 extern int init_nls_cp866(void);
 extern int init_nls_cp869(void);
 extern int init_nls_cp874(void);
+extern int init_nls_koi8_r(void);
 
 #endif /* _LINUX_NLS_H */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/linux/pci.h linux/include/linux/pci.h
--- linux.vanilla/include/linux/pci.h	Sun Dec  6 00:14:46 1998
+++ linux/include/linux/pci.h	Fri Mar 26 17:05:23 1999
@@ -119,8 +119,8 @@
 /* bit 1 is reserved if address_space = 1 */
 
 #define PCI_CARDBUS_CIS		0x28
-#define PCI_SUBSYSTEM_ID	0x2c
-#define PCI_SUBSYSTEM_VENDOR_ID	0x2e  
+#define PCI_SUBSYSTEM_VENDOR_ID	0x2c
+#define PCI_SUBSYSTEM_ID	0x2e
 #define PCI_ROM_ADDRESS		0x30	/* 32 bits */
 #define  PCI_ROM_ADDRESS_ENABLE	0x01	/* Write 1 to enable ROM,
 					   bits 31..11 are address,
@@ -304,6 +304,8 @@
 #define PCI_DEVICE_ID_DEC_21052		0x0021
 #define PCI_DEVICE_ID_DEC_21150		0x0022
 #define PCI_DEVICE_ID_DEC_21152		0x0024
+#define PCI_DEVICE_ID_DEC_21154		0x0026
+#define PCI_DEVICE_ID_DEC_21285		0x1065
 
 #define PCI_VENDOR_ID_CIRRUS		0x1013
 #define PCI_DEVICE_ID_CIRRUS_7548	0x0038
@@ -354,7 +356,11 @@
 #define PCI_DEVICE_ID_MATROX_MYS	0x051A
 #define PCI_DEVICE_ID_MATROX_MIL_2	0x051b
 #define PCI_DEVICE_ID_MATROX_MIL_2_AGP	0x051f
+#define PCI_DEVICE_ID_MATROX_G200_PCI   0x0520
+#define PCI_DEVICE_ID_MATROX_G200_AGP   0x0521
 #define PCI_DEVICE_ID_MATROX_MGA_IMP	0x0d10
+#define PCI_DEVICE_ID_MATROX_G100_MM    0x1000
+#define PCI_DEVICE_ID_MATROX_G100_AGP   0x1001
 
 #define PCI_VENDOR_ID_CT		0x102c
 #define PCI_DEVICE_ID_CT_65545		0x00d8
@@ -470,6 +476,12 @@
 #define PCI_DEVICE_ID_PICOP_PT86C52X	0x0001
 #define PCI_DEVICE_ID_PICOP_PT80C524	0x8002
 
+#define PCI_VENDOR_ID_MYLEX		0x1069
+#define PCI_DEVICE_ID_MYLEX_DAC960P_V2	0x0001
+#define PCI_DEVICE_ID_MYLEX_DAC960P_V3	0x0002
+#define PCI_DEVICE_ID_MYLEX_DAC960P_V4	0x0010
+#define PCI_DEVICE_ID_MYLEX_DAC960P_V5	0x0020
+
 #define PCI_VENDOR_ID_APPLE		0x106b
 #define PCI_DEVICE_ID_APPLE_BANDIT	0x0001
 #define PCI_DEVICE_ID_APPLE_GC		0x0002
@@ -646,6 +658,9 @@
 #define PCI_DEVICE_ID_INIT_320P		0x9100
 #define PCI_DEVICE_ID_INIT_360P		0x9500
 
+#define PCI_VENDOR_ID_TTI		0x1103
+#define PCI_DEVICE_ID_TTI_HPT343	0x0003
+
 #define PCI_VENDOR_ID_VIA		0x1106
 #define PCI_DEVICE_ID_VIA_82C505	0x0505
 #define PCI_DEVICE_ID_VIA_82C561	0x0561
@@ -655,6 +670,7 @@
 #define PCI_DEVICE_ID_VIA_82C586_0	0x0586
 #define PCI_DEVICE_ID_VIA_82C595	0x0595
 #define PCI_DEVICE_ID_VIA_82C597_0	0x0597
+#define PCI_DEVICE_ID_VIA_82C598_0      0x0598
 #define PCI_DEVICE_ID_VIA_82C926	0x0926
 #define PCI_DEVICE_ID_VIA_82C416	0x1571
 #define PCI_DEVICE_ID_VIA_82C595_97	0x1595
@@ -662,6 +678,10 @@
 #define PCI_DEVICE_ID_VIA_82C586_3	0x3040
 #define PCI_DEVICE_ID_VIA_86C100A	0x6100
 #define PCI_DEVICE_ID_VIA_82C597_1	0x8597
+#define PCI_DEVICE_ID_VIA_82C598_1      0x8598
+
+#define PCI_VENDOR_ID_SMC2             0x1113
+#define PCI_DEVICE_ID_SMC2_1211TX      0x1211
 
 #define PCI_VENDOR_ID_VORTEX		0x1119
 #define PCI_DEVICE_ID_VORTEX_GDT60x0	0x0000
@@ -893,12 +913,16 @@
 #define PCI_DEVICE_ID_S3_ViRGE_MXPMV	0x8c03
 #define PCI_DEVICE_ID_S3_SONICVIBES	0xca00
 
+#define PCI_VENDOR_ID_DCI       0x6666
+#define PCI_DEVICE_ID_DCI_PCCOM4    0x0001
+
 #define PCI_VENDOR_ID_INTEL		0x8086
 #define PCI_DEVICE_ID_INTEL_82375	0x0482
 #define PCI_DEVICE_ID_INTEL_82424	0x0483
 #define PCI_DEVICE_ID_INTEL_82378	0x0484
 #define PCI_DEVICE_ID_INTEL_82430	0x0486
 #define PCI_DEVICE_ID_INTEL_82434	0x04a3
+#define PCI_DEVICE_ID_INTEL_I960	0x0960
 #define PCI_DEVICE_ID_INTEL_82092AA_0	0x1221
 #define PCI_DEVICE_ID_INTEL_82092AA_1	0x1222
 #define PCI_DEVICE_ID_INTEL_7116	0x1223
@@ -938,9 +962,12 @@
 
 #define PCI_VENDOR_ID_ADAPTEC		0x9004
 #define PCI_DEVICE_ID_ADAPTEC_7810	0x1078
+#define PCI_DEVICE_ID_ADAPTEC_7821	0x2178
 #define PCI_DEVICE_ID_ADAPTEC_7850	0x5078
 #define PCI_DEVICE_ID_ADAPTEC_7855	0x5578
 #define PCI_DEVICE_ID_ADAPTEC_5800	0x5800
+#define PCI_DEVICE_ID_ADAPTEC_3860	0x6038
+#define PCI_DEVICE_ID_ADAPTEC_1480A	0x6075
 #define PCI_DEVICE_ID_ADAPTEC_7860	0x6078
 #define PCI_DEVICE_ID_ADAPTEC_7861	0x6178
 #define PCI_DEVICE_ID_ADAPTEC_7870	0x7078
@@ -954,13 +981,28 @@
 #define PCI_DEVICE_ID_ADAPTEC_7882	0x8278
 #define PCI_DEVICE_ID_ADAPTEC_7883	0x8378
 #define PCI_DEVICE_ID_ADAPTEC_7884	0x8478
+#define PCI_DEVICE_ID_ADAPTEC_7885	0x8578
+#define PCI_DEVICE_ID_ADAPTEC_7886	0x8678
+#define PCI_DEVICE_ID_ADAPTEC_7887	0x8778
+#define PCI_DEVICE_ID_ADAPTEC_7888	0x8878
 #define PCI_DEVICE_ID_ADAPTEC_1030	0x8b78
 
 #define PCI_VENDOR_ID_ADAPTEC2		0x9005
 #define PCI_DEVICE_ID_ADAPTEC2_2940U2	0x0010
+#define PCI_DEVICE_ID_ADAPTEC2_2930U2	0x0011
+#define PCI_DEVICE_ID_ADAPTEC2_7890B	0x0013
 #define PCI_DEVICE_ID_ADAPTEC2_7890	0x001f
 #define PCI_DEVICE_ID_ADAPTEC2_3940U2	0x0050
+#define PCI_DEVICE_ID_ADAPTEC2_3950U2D	0x0051
 #define PCI_DEVICE_ID_ADAPTEC2_7896	0x005f
+#define PCI_DEVICE_ID_ADAPTEC2_7892A	0x0080
+#define PCI_DEVICE_ID_ADAPTEC2_7892B	0x0081
+#define PCI_DEVICE_ID_ADAPTEC2_7892D	0x0083
+#define PCI_DEVICE_ID_ADAPTEC2_7892P	0x008f
+#define PCI_DEVICE_ID_ADAPTEC2_7899A	0x00c0
+#define PCI_DEVICE_ID_ADAPTEC2_7899B	0x00c1
+#define PCI_DEVICE_ID_ADAPTEC2_7899D	0x00c3
+#define PCI_DEVICE_ID_ADAPTEC2_7899P	0x00cf
 
 #define PCI_VENDOR_ID_ATRONICS		0x907f
 #define PCI_DEVICE_ID_ATRONICS_2015	0x2015
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- linux.vanilla/include/linux/proc_fs.h	Sun Dec  6 00:14:46 1998
+++ linux/include/linux/proc_fs.h	Fri Mar 26 17:14:00 1999
@@ -141,14 +141,21 @@
 	PROC_SCSI_AM53C974,
 	PROC_SCSI_SSC,
 	PROC_SCSI_NCR53C406A,
+	PROC_SCSI_SYM53C416,
 	PROC_SCSI_MEGARAID,
+	PROC_SCSI_INIA100,
 	PROC_SCSI_PPA,
+	PROC_SCSI_ATP870U,
 	PROC_SCSI_ESP,
 	PROC_SCSI_A3000,
 	PROC_SCSI_A2091,
 	PROC_SCSI_GVP11,
 	PROC_SCSI_ATARI,
 	PROC_SCSI_GDTH,
+	PROC_SCSI_INI9100U,
+	PROC_SCSI_PCI2000,
+	PROC_SCSI_PCI2220I,
+	PROC_SCSI_PSI240I,
 	PROC_SCSI_IDESCSI,
 	PROC_SCSI_SCSI_DEBUG,	
 	PROC_SCSI_NOT_PRESENT,
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/linux/serial.h linux/include/linux/serial.h
--- linux.vanilla/include/linux/serial.h	Sun Dec  6 00:14:46 1998
+++ linux/include/linux/serial.h	Sun Dec 27 21:36:33 1998
@@ -81,6 +81,7 @@
 #define ASYNC_CTS_FLOW		0x04000000 /* Do CTS flow control */
 #define ASYNC_CHECK_CD		0x02000000 /* i.e., CLOCAL */
 #define ASYNC_SHARE_IRQ		0x01000000 /* for multifunction cards */
+#define ASYNC_PCI           0x00800000 /* this port is on a PCI board */
 
 /*
  * Multiport serial configuration structure --- external structure
@@ -200,6 +201,42 @@
 	unsigned char	mask4, match4;
 	int		port_monitor;
 };
+
+/*
+ * PCI board base -- internal structure 
+ */
+
+/*
+ * There are four config spaces on a PCI board. Which ones house the
+ * serial chips?
+ */
+
+enum pci_spc {
+  pci_space_0 = 1,  
+  pci_space_1 = 2,
+  pci_space_2 = 4,  
+  pci_space_3 = 8
+};
+
+struct pci_struct {
+  int start;                        /* IO Base for this chip */
+  struct pci_serial_boards *type;   /* Pointer to the board type */
+};
+
+struct pci_serial_boards {
+  int vendor_id;          /* Vendor of the Board */
+  int device_id;          /* Device ID */
+  char *board_name;       /* Name of the Board */
+  int board_type;         /* Which chip is on the board */
+  enum pci_spc pci_space; /* How many PCI spaces should be mapped in? */
+  int dev_per_space;      /* How many chips are in every PCI space */
+  int dev_spacing;        /* What spacing are they located? */ 
+  int io_size;            /* Size of the Register IO Space */
+  int baud_base;          /* What crystal is on the board */
+};
+
+
+
 
 /* Export to allow PCMCIA to use this - Dave Hinds */
 extern int register_serial(struct serial_struct *req);
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/linux/tasks.h linux/include/linux/tasks.h
--- linux.vanilla/include/linux/tasks.h	Sun Jun 21 18:40:57 1998
+++ linux/include/linux/tasks.h	Sun Dec 27 20:32:43 1998
@@ -11,7 +11,7 @@
 #define NR_CPUS 1
 #endif
 
-#define NR_TASKS	512
+#define NR_TASKS	512	/* Max 4092, or 4090 w/APM configured on x86 */
 
 #define MAX_TASKS_PER_USER (NR_TASKS/2)
 #define MIN_TASKS_LEFT_FOR_ROOT 4
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/linux/via_ide_dma.h linux/include/linux/via_ide_dma.h
--- linux.vanilla/include/linux/via_ide_dma.h	Thu Jan  1 01:00:00 1970
+++ linux/include/linux/via_ide_dma.h	Sun Dec 27 21:19:48 1998
@@ -0,0 +1,162 @@
+/* Via Apollo timings display header file for triton.c.
+   Copyright (c) 1998 Michel Aubry 
+*/
+
+typedef char *PCHAR;
+
+static int via_get_info(char *, char **, off_t, int, int);
+static PCHAR print_apollo_drive_config(char *buf,byte bus, byte fn);
+static PCHAR print_apollo_ide_config(char *buf, byte bus, byte fn);
+static PCHAR print_apollo_chipset_control1(char *buf,byte bus, byte fn);
+static PCHAR print_apollo_chipset_control2(char *p,byte bus, byte fn);
+static PCHAR print_apollo_chipset_control3(char *p, byte bus, byte fn, unsigned short n);
+
+static struct proc_dir_entry via_proc_entry = {
+  0, 3, "via", S_IFREG | S_IRUGO, 1, 0, 0, 0, 0, via_get_info
+};
+
+/* we save bus, function of chipset here for further debug use */
+static byte bmide_bus, bmide_fn;
+
+static char *FIFO_str[] = {" 1 ", "3/4", "1/2", "1/4"};
+static char *control3_str[] = {"No limitation", "64","128","192"};
+ 
+static int via_get_info(char *buffer, char **addr, off_t offset, int count, int dummy)
+{
+   /* print what /proc/via displays, if required from DISPLAY_APOLLO_TIMINGS */
+     char *p = buffer;
+     /* Parameter of chipset : */
+     /* Miscellaneous control 1 */
+     p = print_apollo_chipset_control1(buffer,bmide_bus, bmide_fn);
+     /* Miscellaneous control 2 */
+     p = print_apollo_chipset_control2(p,bmide_bus, bmide_fn);
+     /* Parameters of drives: */
+     /* Header */
+     p += sprintf(p,"------------------Primary IDE------------Secondary IDE-----\n");
+     p = print_apollo_chipset_control3(p, bmide_bus, bmide_fn, 0);
+     p = print_apollo_ide_config(p,bmide_bus, bmide_fn);
+     p += sprintf(p,"--------------drive0------drive1-------drive0------drive1----\n");
+     p = print_apollo_chipset_control3(p, bmide_bus, bmide_fn, 1);
+     p = print_apollo_drive_config(p,bmide_bus, bmide_fn);
+ 
+     return p-buffer; /* hoping it is less than 4K... */
+}
+ 
+static PCHAR print_apollo_drive_config(char *buf,byte bus, byte fn)
+{
+     int rc;
+     unsigned int time;
+     byte tm;
+     char *p = buf;  
+ 
+     /*  printk("--------------drive0------drive1-------drive0------drive1----");*/
+ 
+     /* Drive Timing Control */
+ 
+     rc = pcibios_read_config_dword(bus, fn, 0x48, &time);
+     p += sprintf(p,"Act Pls Width:  %02d          %02d           %02d          %02d\n",((time & 0xf0000000)>>28)+1,((time & 0xf00000)>>20)+1,((time & 0xf000)>>12)+1, ((time & 0xf0)>>4)+1);
+     p += sprintf(p,"Recovery Time:  %02d          %02d           %02d          %02d\n",((time & 0x0f000000)>>24)+1, ((time & 0x0f0000)>>16)+1, ((time & 0x0f00)>>8)+1, (time & 0x0f)+1);
+ 
+     /* Address Setup Time */
+ 
+     rc = pcibios_read_config_byte(bus, fn, 0x4C, &tm);
+     p += sprintf(p, "Add. Setup T.:  %01dT          %01dT           %01dT          %01dT\n",((tm & 0xc0)>>6) + 1,((tm & 0x30)>>4) + 1,((tm & 0x0c)>>2) + 1,(tm & 0x03) + 1);
+ 
+     /* UltraDMA33 Extended Timing Control */
+ 
+     rc = pcibios_read_config_dword(bus, fn, 0x50, &time);
+     p += sprintf(p, "------------------UDMA-Timing-Control------------------------\n");
+     p += sprintf(p, "Enable Meth.:    %01d           %01d            %01d           %01d\n",(time & 0x80000000)?1:0,(time & 0x800000)?1:0, (time & 0x8000)?1:0, (time & 0x80)?1:0);
+     p += sprintf(p, "Enable:         %s         %s          %s         %s\n",(time & 0x40000000)?"yes":"no ", (time & 0x400000)?"yes":"no ",(time & 0x4000)?"yes":"no ",(time & 0x40)?"yes":"no ");
+     p += sprintf(p, "Transfer Mode: %s         %s          %s         %s\n",(time & 0x20000000)?"PIO":"DMA",(time & 0x200000)?"PIO":"DMA",(time & 0x2000)?"PIO":"DMA",(time & 0x20)?"PIO":"DMA");
+     p += sprintf(p, "Cycle Time:     %01dT          %01dT           %01dT          %01dT\n",((time & 0x03000000)>>24)+2,((time & 0x030000)>>16)+2,((time & 0x0300)>>8)+2,(time & 0x03)+2);
+ 
+     return (PCHAR)p;
+}
+ 
+static PCHAR print_apollo_ide_config(char *buf, byte bus, byte fn)
+{
+     byte time, tmp; 
+     unsigned short size0,size1;
+     int rc;
+     char *p = buf;  
+ 
+     rc = pcibios_read_config_byte(bus, fn, 0x41, &time);
+     p += sprintf(p,"Prefetch Buffer :      %s                     %s\n", (time & 128)?"on ":"off", (time & 32)?"on ":"off");
+     p += sprintf(p,"Post Write Buffer:     %s                     %s\n", (time & 64)? "on ": "off",(time & 16)?"on ":"off");
+ 
+     /* FIFO configuration */
+     rc = pcibios_read_config_byte(bus, fn, 0x43, &time);
+     tmp = ((time & 0x20)>>2) + ((time & 0x40)>>3);
+     p += sprintf(p,"FIFO Conf/Chan. :      %02d                      %02d\n", 16 - tmp, tmp);
+     tmp = (time & 0x0F)>>2;
+     p += sprintf(p,"Threshold Prim. :      %s                     %s\n", FIFO_str[tmp],FIFO_str[time & 0x03]);
+ 
+     /* chipset Control3 */
+     rc = pcibios_read_config_byte(bus, fn, 0x46, &time);
+     p += sprintf(p,"Read DMA FIFO flush:   %s                     %s\n",(time & 0x80)?"on ":"off", (time & 0x40)?"on ":"off");
+     p += sprintf(p,"End Sect. FIFO flush:  %s                     %s\n",(time & 0x20)?"on ":"off", (time & 0x10)?"on ":"off");
+     p += sprintf(p,"Max DRDY Pulse Width:  %s %s\n", control3_str[(time & 0x03)], (time & 0x03)? "PCI clocks":"");
+ 
+     /* Primary and Secondary sector sizes */
+     rc = pcibios_read_config_word(bus, fn, 0x60, &size0);
+     rc = pcibios_read_config_word(bus, fn, 0x68, &size1);
+     p += sprintf(p,"Bytes Per Sector:      %03d                     %03d\n",size0 & 0xfff,size1 & 0xfff);
+     return (PCHAR)p;
+}
+
+static PCHAR print_apollo_chipset_control1(char *buf,byte bus, byte fn)
+{
+     byte t;
+     int rc;
+     char *p = buf;  
+     unsigned short c;
+     byte l,l_max;   
+ 
+     rc = pcibios_read_config_word(bus, fn, 0x04, &c);
+     rc = pcibios_read_config_byte(bus, fn, 0x44, &t);
+     rc = pcibios_read_config_byte(bus, fn, 0x0d, &l);
+     rc = pcibios_read_config_byte(bus, fn, 0x3f, &l_max);
+     p += sprintf(p,"Command register = 0x%x\n",c);
+     p += sprintf(p,"Master Read  Cycle IRDY %d Wait State\n", (t & 64)>>6);
+     p += sprintf(p,"Master Write Cycle IRDY %d Wait State\n", (t & 32)>>5 );
+     p += sprintf(p,"FIFO Output Data 1/2 Clock Advance: %s\n", (t & 16)? "on ":"off");
+     p += sprintf(p,"Bus Master IDE Status Register Read Retry: %s\n", (t & 8)? "on " : "off");
+     p += sprintf(p,"Latency timer = %d (max. = %d)\n",l,l_max);
+     return (PCHAR)p;
+}
+ 
+static PCHAR print_apollo_chipset_control2(char *buf,byte bus, byte fn)
+{
+     byte t;
+     int rc;
+     char *p = buf;  
+     rc = pcibios_read_config_byte(bus, fn, 0x45, &t);
+     p += sprintf(p,"Interrupt Steering Swap: %s\n", (t & 64)? "on ":"off");
+     return (PCHAR)p;
+}
+ 
+static PCHAR print_apollo_chipset_control3(char *buf, byte bus, byte fn, unsigned short n)
+{
+     /* at that point we can be sure that register 0x20 of the chipset contains the right address... */
+     unsigned int bibma;
+     int rc;
+     byte c0,c1;    
+     char *p = buf; 
+ 
+     rc = pcibios_read_config_dword(bus, fn, 0x20, &bibma);
+     bibma = (bibma & 0xfff0) ;
+ 
+     /* at that point bibma+0x2 et bibma+0xa are byte registers to investigate:*/
+     c0 = inb((unsigned short)bibma + 0x02);
+     c1 = inb((unsigned short)bibma + 0x0a);
+ 
+     if(n==0)
+        /*p = sprintf(p,"--------------------Primary IDE------------Secondary IDE-----");*/
+        p += sprintf(p,"both channels togth:   %s                     %s\n",(c0&0x80)?"no":"yes",(c1&0x80)?"no":"yes");
+     else
+        /*p = sprintf(p,"--------------drive0------drive1-------drive0------drive1----");*/
+        p += sprintf(p,"DMA enabled:    %s         %s          %s         %s\n",(c0&0x20)?"yes":"no ",(c0&0x40)?"yes":"no ",(c1&0x20)?"yes":"no ",(c1&0x40)?"yes":"no ");
+ 
+     return (PCHAR)p;
+}
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/linux/wireless.h linux/include/linux/wireless.h
--- linux.vanilla/include/linux/wireless.h	Sun Dec  6 00:14:47 1998
+++ linux/include/linux/wireless.h	Tue Mar  2 00:52:05 1999
@@ -1,7 +1,7 @@
 /*
  * This file define a set of standard wireless extensions
  *
- * Version :	4	12.2.97
+ * Version :	6	18.2.99
  *
  * Authors :	Jean Tourrilhes - HPLB - <jt@hplb.hpl.hp.com>
  */
@@ -63,7 +63,7 @@
  * (there is some stuff that will be added in the future...)
  * I just plan to increment with each new version.
  */
-#define WIRELESS_EXT	5
+#define WIRELESS_EXT	6
 
 /*
  * Changes :
@@ -82,6 +82,10 @@
  * --------
  *	- Missing encoding definitions in range
  *	- Access points stuff
+ *
+ * V5 to V6
+ * --------
+ *	- 802.11 support
  */
 
 /* -------------------------- IOCTL LIST -------------------------- */
@@ -113,11 +117,19 @@
 #define SIOCGIWAP	0x8B15		/* get access point hardware addresses */
 #define SIOCGIWAPLIST	0x8B17		/* get list of access point in range */
 
+/* 802.11 specific support */
+#define SIOCSIWESSID	0x8B1A		/* set ESSID (network name) */
+#define SIOCGIWESSID	0x8B1B		/* get ESSID */
+/* As the ESSID is a string up to 32 bytes long, it doesn't fit within the
+ * 'iwreq' structure, so we need to use the 'data' member to point to a
+ * string in user space, like it is done for RANGE...
+ */
+
 /* ------------------------- IOCTL STUFF ------------------------- */
 
 /* The first and the last (range) */
 #define SIOCIWFIRST	0x8B00
-#define SIOCIWLAST	0x8B17
+#define SIOCIWLAST	0x8B1B
 
 /* Even : get (world access), odd : set (root access) */
 #define IW_IS_SET(cmd)	(!((cmd) & 0x1))
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/net/ip_masq.h linux/include/net/ip_masq.h
--- linux.vanilla/include/net/ip_masq.h	Sun Jun 21 18:41:01 1998
+++ linux/include/net/ip_masq.h	Fri Mar 26 17:15:59 1999
@@ -84,6 +84,14 @@
 	void		*app_data;	/* Application private data */
 	unsigned  flags;        	/* status flags */
 	struct ip_masq	*control;	/* Corresponding control connection */
+#ifdef CONFIG_IP_MASQUERADE_IPSEC
+        struct ip_masq  *d_link;	/* hashed link ptr */
+	__u32		ospi, ispi;	/* outbound and inbound SPI keys for IPSEC */
+                                        /* also the cookie for ISAKMP masq (maybe) */
+	short		ocnt;		/* counter of inits sent - limit blocking  */
+        short           blocking;       /* if we're blocking another host */
+
+#endif /* CONFIG_IP_MASQUERADE_IPSEC */
 };
 
 /*
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/scsi/scsicam.h linux/include/scsi/scsicam.h
--- linux.vanilla/include/scsi/scsicam.h	Sun Jun 21 18:41:01 1998
+++ linux/include/scsi/scsicam.h	Fri Mar 26 17:03:39 1999
@@ -14,4 +14,6 @@
 #define SCSICAM_H
 #include <linux/kdev_t.h>
 extern int scsicam_bios_param (Disk *disk, kdev_t dev, int *ip);
+extern int scsi_partsize(struct buffer_head *bh, unsigned long capacity,
+           unsigned int  *cyls, unsigned int *hds, unsigned int *secs);
 #endif /* def SCSICAM_H */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/init/main.c linux/init/main.c
--- linux.vanilla/init/main.c	Sun Dec  6 00:14:47 1998
+++ linux/init/main.c	Fri Jan 29 14:24:03 1999
@@ -103,7 +103,9 @@
 extern void fdomain_setup(char *str, int *ints);
 extern void in2000_setup(char *str, int *ints);
 extern void NCR53c406a_setup(char *str, int *ints);
+extern void sym53c416_setup(char *str, int *ints);
 extern void wd7000_setup(char *str, int *ints);
+extern void dc390_setup(char* str, int *ints);
 extern void ppa_setup(char *str, int *ints);
 extern void scsi_luns_setup(char *str, int *ints);
 extern void sound_setup(char *str, int *ints);
@@ -204,6 +206,11 @@
 #ifdef CONFIG_PARIDE_PCD
 extern void pcd_setup(char *str, int *ints);
 #endif
+#ifdef CONFIG_BLK_CPQ_DA
+#ifdef CONFIG_BLK_CPQ_DA_EISA
+extern void cpqarray_setup(char *str, int *ints);
+#endif
+#endif
 
 #if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD)
 extern void ipc_init(void);
@@ -372,6 +379,9 @@
 #ifdef CONFIG_SCSI_NCR53C406A
 	{ "ncr53c406a=", NCR53c406a_setup},
 #endif
+#ifdef CONFIG_SCSI_SYM53C416
+	{ "sym53c416=", sym53c416_setup}, 
+#endif
 #ifdef CONFIG_SCSI_FUTURE_DOMAIN
 	{ "fdomain=", fdomain_setup},
 #endif
@@ -384,6 +394,9 @@
 #ifdef CONFIG_SCSI_PPA
         { "ppa=", ppa_setup },
 #endif
+#if defined(CONFIG_SCSI_DC390T) && ! defined(CONFIG_SCSI_DC390T_NOGENSUPP)
+	{ "tmscsim=", dc390_setup },
+#endif
 #ifdef CONFIG_BLK_DEV_XD
 	{ "xd=", xd_setup },
 	{ "xd_geo=", xd_manual_geo_init },
@@ -471,6 +484,11 @@
 #ifdef CONFIG_APM
 	{ "apm=", apm_setup },
 #endif
+#ifdef CONFIG_BLK_CPQ_DA
+#ifdef CONFIG_BLK_CPQ_DA_EISA
+       { "smart2=", cpqarray_setup },
+#endif
+#endif
 	{ 0, 0 }
 };
 
@@ -648,6 +666,24 @@
 		{ "sdn",     0x08d0 },
 		{ "sdo",     0x08e0 },
 		{ "sdp",     0x08f0 },
+#ifdef CONFIG_BLK_DEV_DAC960
+		{ "rd/c0d0p",0x3000 },
+		{ "rd/c0d1p",0x3008 },
+		{ "rd/c0d2p",0x3010 },
+		{ "rd/c0d3p",0x3018 },
+		{ "rd/c0d4p",0x3020 },
+		{ "rd/c0d5p",0x3028 },
+		{ "rd/c0d6p",0x3030 },
+		{ "rd/c0d7p",0x3038 },
+		{ "rd/c0d8p",0x3040 },
+		{ "rd/c0d9p",0x3048 },
+		{ "rd/c0d10p",0x3050 },
+		{ "rd/c0d11p",0x3058 },
+		{ "rd/c0d12p",0x3060 },
+		{ "rd/c0d13p",0x3068 },
+		{ "rd/c0d14p",0x3070 },
+		{ "rd/c0d15p",0x3078 },
+#endif
 		{ "fd",      0x0200 },
 		{ "xda",     0x0d00 },
 		{ "xdb",     0x0d40 },
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/kernel/ksyms.c linux/kernel/ksyms.c
--- linux.vanilla/kernel/ksyms.c	Sun Dec  6 00:14:47 1998
+++ linux/kernel/ksyms.c	Fri Jan 29 14:24:19 1999
@@ -201,6 +201,8 @@
 	X(hardsect_size),
 	X(blk_size),
 	X(blk_dev),
+	X(max_sectors),
+	X(max_segments),
 	X(is_read_only),
 	X(set_device_ro),
 	X(bmap),
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/kernel/panic.c linux/kernel/panic.c
--- linux.vanilla/kernel/panic.c	Sun Jun 21 18:40:56 1998
+++ linux/kernel/panic.c	Sun Dec  6 03:32:15 1998
@@ -18,6 +18,7 @@
 asmlinkage void sys_sync(void);	/* it's really int */
 extern void hard_reset_now(void);
 extern void do_unblank_screen(void);
+extern void DAC960_Finalize(void);
 extern void gdth_halt(void);
 extern int C_A_D;
 
@@ -55,6 +56,9 @@
 		printk(KERN_EMERG "Rebooting in %d seconds..",panic_timeout);
 		for(i = 0; i < (panic_timeout*1000); i++)
 			udelay(1000);
+#ifdef CONFIG_BLK_DEV_DAC960
+		DAC960_Finalize();
+#endif
 #ifdef CONFIG_SCSI_GDTH
 		gdth_halt();
 #endif
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/kernel/sched.c linux/kernel/sched.c
--- linux.vanilla/kernel/sched.c	Sun Dec  6 00:14:47 1998
+++ linux/kernel/sched.c	Sun Dec 27 20:49:55 1998
@@ -87,7 +87,6 @@
 #define _S(nr) (1<<((nr)-1))
 
 extern void mem_use(void);
-extern unsigned long get_wchan(struct task_struct *);
 
 static unsigned long init_kernel_stack[1024] = { STACK_MAGIC, };
 unsigned long init_user_stack[1024] = { STACK_MAGIC, };
@@ -1664,6 +1663,56 @@
 		return -EINTR;
 	}
 
+	return 0;
+}
+
+/* Used in fs/proc/array.c */
+unsigned long get_wchan(struct task_struct *p)
+{
+	if (!p || p == current || p->state == TASK_RUNNING)
+		return 0;
+#if defined(__i386__)
+	{
+		unsigned long ebp, eip;
+		unsigned long stack_page;
+		int count = 0;
+
+		stack_page = p->kernel_stack_page;
+		if (!stack_page)
+			return 0;
+		ebp = p->tss.ebp;
+		do {
+			if (ebp < stack_page || ebp >= 4092+stack_page)
+				return 0;
+			eip = *(unsigned long *) (ebp+4);
+			if (eip < (unsigned long) interruptible_sleep_on
+			    || eip >= (unsigned long) add_timer)
+				return eip;
+			ebp = *(unsigned long *) ebp;
+		} while (count++ < 16);
+	}
+#elif defined(__alpha__)
+	/*
+	 * This one depends on the frame size of schedule().  Do a
+	 * "disass schedule" in gdb to find the frame size.  Also, the
+	 * code assumes that sleep_on() follows immediately after
+	 * interruptible_sleep_on() and that add_timer() follows
+	 * immediately after interruptible_sleep().  Ugly, isn't it?
+	 * Maybe adding a wchan field to task_struct would be better,
+	 * after all...
+	 */
+	{
+	    unsigned long schedule_frame;
+	    unsigned long pc;
+
+	    pc = thread_saved_pc(&p->tss);
+	    if (pc >= (unsigned long) interruptible_sleep_on && pc < (unsigned long) add_timer) {
+		schedule_frame = ((unsigned long *)p->tss.ksp)[6];
+		return ((unsigned long *)schedule_frame)[12];
+	    }
+	    return pc;
+	}
+#endif
 	return 0;
 }
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/kernel/sys.c linux/kernel/sys.c
--- linux.vanilla/kernel/sys.c	Sun Dec  6 00:14:47 1998
+++ linux/kernel/sys.c	Fri Mar 26 16:56:23 1999
@@ -34,6 +34,7 @@
 int C_A_D = 1;
 
 extern void adjust_clock(void);
+extern void DAC960_Finalize(void);
 extern void gdth_halt(void);
 
 asmlinkage int sys_ni_syscall(void)
@@ -188,6 +189,9 @@
 	if (magic != 0xfee1dead || magic_too != 672274793)
 		return -EINVAL;
 	if (flag == 0x01234567) {
+#ifdef CONFIG_BLK_DEV_DAC960
+		DAC960_Finalize();
+#endif
 #ifdef CONFIG_SCSI_GDTH
 		gdth_halt();
 #endif
@@ -197,6 +201,9 @@
 	else if (!flag)
 		C_A_D = 0;
 	else if (flag == 0xCDEF0123) {
+#ifdef CONFIG_BLK_DEV_DAC960
+		DAC960_Finalize();
+#endif
 #ifdef CONFIG_SCSI_GDTH
 		gdth_halt();
 #endif
@@ -219,6 +226,9 @@
 void ctrl_alt_del(void)
 {
 	if (C_A_D) {
+#ifdef CONFIG_BLK_DEV_DAC960
+		DAC960_Finalize();
+#endif
 #ifdef CONFIG_SCSI_GDTH
 		gdth_halt();
 #endif
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/linux/Documentation/README.DAC960 linux/linux/Documentation/README.DAC960
--- linux.vanilla/linux/Documentation/README.DAC960	Thu Jan  1 01:00:00 1970
+++ linux/linux/Documentation/README.DAC960	Thu Mar 25 23:52:55 1999
@@ -0,0 +1,670 @@
+	   Mylex DAC960/DAC1100 PCI RAID Controller Driver for Linux
+
+			Version 2.0.0 for Linux 2.0.36
+			Version 2.2.0 for Linux 2.2.3
+
+			      PRODUCTION RELEASE
+
+				 23 March 1999
+
+			       Leonard N. Zubkoff
+			       Dandelion Digital
+			       lnz@dandelion.com
+
+	 Copyright 1998-1999 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+
+				 INTRODUCTION
+
+Mylex, Inc. designs and manufactures a variety of high performance PCI RAID
+controllers.  Mylex Corporation is located at 34551 Ardenwood Blvd., Fremont,
+California 94555, USA and can be reached at 510/796-6100 or on the World Wide
+Web at http://www.mylex.com.  Mylex RAID Technical Support can be reached by
+electronic mail at support@mylex.com (for eXtremeRAID 1100 and older DAC960
+models) or techsup@mylex.com (for AcceleRAID models), by voice at 510/608-2400,
+or by FAX at 510/745-7715.  Contact information for offices in Europe and Japan
+is available on the Web site.
+
+The latest information on Linux support for DAC960 PCI RAID Controllers, as
+well as the most recent release of this driver, will always be available from
+my Linux Home Page at URL "http://www.dandelion.com/Linux/".  The Linux DAC960
+driver supports all current DAC960 PCI family controllers including the
+AcceleRAID models, as well as the eXtremeRAID 1100; see below for a complete
+list.  For simplicity, in most places this documentation refers to DAC960
+generically rather than explicitly listing all the models.
+
+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
+driver at startup, along with any subsequent system messages relevant to the
+controller's operation, and a detailed description of your system's hardware
+configuration.
+
+Please consult the DAC960 RAID controller documentation for detailed
+information regarding installation and configuration of the controllers.  This
+document primarily provides information specific to the Linux DAC960 support.
+
+
+				DRIVER FEATURES
+
+The DAC960 RAID controllers are supported solely as high performance RAID
+controllers, not as interfaces to arbitrary SCSI devices.  The Linux DAC960
+driver operates at the block device level, the same level as the SCSI and IDE
+drivers.  Unlike other RAID controllers currently supported on Linux, the
+DAC960 driver is not dependent on the SCSI subsystem, and hence avoids all the
+complexity and unnecessary code that would be associated with an implementation
+as a SCSI driver.  The DAC960 driver is designed for as high a performance as
+possible with no compromises or extra code for compatibility with lower
+performance devices.  The DAC960 driver includes extensive error logging and
+online configuration management capabilities.  Except for initial configuration
+of the controller and adding new disk drives, most everything can be handled
+from Linux while the system is operational.
+
+The DAC960 driver is architected to support up to 8 controllers per system.
+Each DAC960 controller can support up to 15 disk drives per channel, for a
+maximum of 45 drives on a three channel controller.  The drives installed on a
+controller are divided into one or more "Drive Groups", and then each Drive
+Group is subdivided further into 1 to 32 "Logical Drives".  Each Logical Drive
+has a specific RAID Level and caching policy associated with it, and it appears
+to Linux as a single block device.  Logical Drives are further subdivided into
+up to 7 partitions through the normal Linux and PC disk partitioning schemes.
+Logical Drives are also known as "System Drives", and Drive Groups are also
+called "Packs".  Both terms are in use in the Mylex documentation; I have
+chosen to standardize on the more generic "Logical Drive" and "Drive Group".
+
+DAC960 RAID disk devices are named in the style of the Device File System
+(DEVFS).  The device corresponding to Logical Drive D on Controller C is
+referred to as /dev/rd/cCdD, and the partitions are called /dev/rd/cCdDp1
+through /dev/rd/cCdDp7.  For example, partition 3 of Logical Drive 5 on
+Controller 2 is referred to as /dev/rd/c2d5p3.  Note that unlike with SCSI
+disks the device names will not change in the event of a disk drive failure.
+The DAC960 driver is assigned major numbers 48 - 55 with one major number per
+controller.  The 8 bits of minor number are divided into 5 bits for the Logical
+Drive and 3 bits for the partition.
+
+
+		 SUPPORTED DAC960/DAC1100 PCI RAID CONTROLLERS
+
+The following list comprises the supported DAC960 and DAC1100 PCI RAID
+Controllers as of the date of this document.  It is recommended that anyone
+purchasing a Mylex PCI RAID Controller not in the following table contact the
+author beforehand to verify that it is or will be supported.
+
+eXtremeRAID 1100 (DAC1164P)
+	    3 Wide Ultra-2/LVD SCSI channels
+	    233MHz StrongARM SA 110 Processor
+	    64 Bit PCI (backward compatible with 32 Bit PCI slots)
+	    16MB/32MB/64MB Parity SDRAM Memory with Battery Backup
+
+AcceleRAID 250 (DAC960PTL1)
+	    Uses onboard Symbios SCSI chips on certain motherboards
+	    Also includes one onboard Wide Ultra-2/LVD SCSI Channel
+	    66MHz Intel i960RD RISC Processor
+	    4MB/8MB/16MB/32MB/64MB/128MB ECC EDO Memory
+
+AcceleRAID 200 (DAC960PTL0)
+	    Uses onboard Symbios SCSI chips on certain motherboards
+	    Includes no onboard SCSI Channels
+	    66MHz Intel i960RD RISC Processor
+	    4MB/8MB/16MB/32MB/64MB/128MB ECC EDO Memory
+
+AcceleRAID 150 (DAC960PRL)
+	    Uses onboard Symbios SCSI chips on certain motherboards
+	    Also includes one onboard Wide Ultra-2/LVD SCSI Channel
+	    33MHz Intel i960RP RISC Processor
+	    4MB Parity EDO Memory
+
+DAC960PJ    1/2/3 Wide Ultra SCSI-3 Channels
+	    66MHz Intel i960RD RISC Processor
+	    4MB/8MB/16MB/32MB/64MB/128MB ECC EDO Memory
+
+DAC960PG    1/2/3 Wide Ultra SCSI-3 Channels
+	    33MHz Intel i960RP RISC Processor
+	    4MB/8MB ECC EDO Memory
+
+DAC960PU    1/2/3 Wide Ultra SCSI-3 Channels
+	    Intel i960CF RISC Processor
+	    4MB/8MB EDRAM or 2MB/4MB/8MB/16MB/32MB DRAM Memory
+
+DAC960PD    1/2/3 Wide Fast SCSI-2 Channels
+	    Intel i960CF RISC Processor
+	    4MB/8MB EDRAM or 2MB/4MB/8MB/16MB/32MB DRAM Memory
+
+DAC960PL    1/2/3 Wide Fast SCSI-2 Channels
+	    Intel i960 RISC Processor
+	    2MB/4MB/8MB/16MB/32MB DRAM Memory
+
+For the eXtremeRAID 1100, firmware version 5.06-0-52 or above is required.
+
+For the AcceleRAID 250, 200, and 150, firmware version 4.06-0-57 or above is
+required.
+
+For the DAC960PJ and DAC960PG, firmware version 4.06-0-00 or above is required.
+
+For the DAC960PU, DAC960PD, and DAC960PL, firmware version 3.51-0-04 or above
+is required.
+
+Note that earlier revisions of the DAC960PU, DAC960PD, and DAC960PL controllers
+were delivered with version 2.xx firmware.  Version 2.xx firmware is not
+supported by this driver and no support is envisioned.  Contact Mylex RAID
+Technical Support to inquire about upgrading controllers with version 2.xx
+firmware to version 3.51-0-04.  Upgrading to version 3.xx firmware requires
+installation of higher capacity Flash ROM chips, and not all DAC960PD and
+DAC960PL controllers can be upgraded.
+
+Please note that not all SCSI disk drives are suitable for use with DAC960
+controllers, and only particular firmware versions of any given model may
+actually function correctly.  Similarly, not all motherboards have a BIOS that
+properly initializes the AcceleRAID 250, AcceleRAID 200, AcceleRAID 150,
+DAC960PJ, and DAC960PG because the Intel i960RD/RP is a multi-function device.
+If in doubt, contact Mylex RAID Technical Support (support@mylex.com) to verify
+compatibility.  Mylex makes available a hard disk compatibility list by FTP at
+ftp://ftp.mylex.com/pub/dac960/diskcomp.html.
+
+
+			      DRIVER INSTALLATION
+
+This distribution was prepared for Linux kernel version 2.0.36 or 2.2.3.
+
+To install the DAC960 RAID driver, you may use the following commands,
+replacing "/usr/src" with wherever you keep your Linux kernel source tree:
+
+  cd /usr/src
+  tar -xvzf DAC960-2.0.0.tar.gz (or DAC960-2.2.0.tar.gz)
+  mv README.DAC960 linux/Documentation
+  mv DAC960.[ch] linux/drivers/block
+  patch -p0 < DAC960.patch
+  cd linux
+  make config
+  make depend
+  make bzImage (or zImage)
+
+Then install "arch/i386/boot/bzImage" or "arch/i386/boot/zImage" as your
+standard kernel, run lilo if appropriate, and reboot.
+
+To create the necessary devices in /dev, the "make_rd" script included in
+"DAC960-Utilities.tar.gz" from http://www.dandelion.com/Linux/ may be used.
+LILO 21 and FDISK v2.9 include DAC960 support; also included in this archive
+are patches to LILO 20 and FDISK v2.8 that add DAC960 support, along with
+statically linked executables of LILO and FDISK.  This modified version of LILO
+will allow booting from a DAC960 controller and/or mounting the root file
+system from a DAC960.  Unfortunately, installing directly onto a DAC960 will be
+problematic until the various Linux distribution vendors update their
+installation utilities.  VA Research Linux Systems (http://www.varesearch.com)
+has created an installation CD capable of installing its modified Red Hat 5.2
+environment directly onto DAC960 controllers, and copies of this CD are being
+made available to owners of supported Mylex RAID products by VA Research in
+cooperation with Mylex.  Please see http://www.mylex.com/support/prsfrm.html to
+request an installation CD.
+
+
+			      INSTALLATION NOTES
+
+Before installing Linux or adding DAC960 logical drives to an existing Linux
+system, the controller must first be configured to provide one or more logical
+drives using the BIOS Configuration Utility or DACCF.  Please note that since
+there are only at most 6 usable partitions on each logical drive, systems
+requiring more partitions should subdivide a drive group into multiple logical
+drives, each of which can have up to 6 partitions.  Also, note that with large
+disk arrays it is advisable to enable the 8GB BIOS Geometry (255/63) rather
+than accepting the default 2GB BIOS Geometry (128/32); failing to so do will
+cause the logical drive geometry to have more than 65535 cylinders which will
+make it impossible for FDISK to be used properly.  The 8GB BIOS Geometry can be
+enabled by configuring the DAC960 BIOS, which is accessible via Alt-M during
+the BIOS initialization sequence.
+
+For maximum performance and the most efficient E2FSCK performance, it is
+recommended that EXT2 file systems be built with a 4KB block size and 16 block
+stride to match the DAC960 controller's 64KB default stripe size.  The command
+"mke2fs -b 4096 -R stride=16 <device>" is appropriate.
+
+
+		      DAC960 ANNOUNCEMENTS MAILING LIST
+
+The DAC960 Announcements Mailing List provides a forum for informing Linux
+users of new driver releases and other announcements regarding Linux support
+for DAC960 PCI RAID Controllers.  To join the mailing list, send a message to
+"dac960-announce-request@dandelion.com" with the line "subscribe" in the
+message body.
+
+
+		CONTROLLER CONFIGURATION AND STATUS MONITORING
+
+The DAC960 RAID controllers running firmware 4.06 or above include a Background
+Initialization facility so that system downtime is minimized both for initial
+installation and subsequent configuration of additional storage.  The BIOS
+Configuration Utility (accessible via Alt-R during the BIOS initialization
+sequence) is used to quickly configure the controller, and then the logical
+drives that have been created are available for immediate use even while they
+are still being initialized by the controller.  The primary need for online
+configuration and status monitoring is then to avoid system downtime when disk
+drives fail and must be replaced.  Note that with a SAF-TE (SCSI Accessed
+Fault-Tolerant Enclosure) enclosure, the controller is able to rebuild failed
+drives automatically as soon as a drive replacement is made available.
+
+The primary interfaces for controller configuration and status monitoring are
+special files created in the /proc/rd/... hierarchy along with the normal
+system console logging mechanism.  Whenever the system is operating, the DAC960
+driver queries each controller for status information every 10 seconds, and
+checks for additional conditions every 60 seconds.  The initial status of each
+controller is always available for controller N in /proc/rd/cN/initial_status,
+and the current status as of the last status monitoring query is available in
+/proc/rd/cN/current_status.  In addition, status changes are also logged by the
+driver to the system console and will appear in the log files maintained by
+syslog.  The progress of asynchronous rebuild or consistency check operations
+is also available in /proc/rd/cN/current_status, and progress messages are
+logged to the system console at most every 60 seconds.
+
+To simplify the monitoring process for custom software, the special file
+/proc/rd/status returns "OK" when all DAC960 controllers in the system are
+operating normally and no failures have occurred, or "ALERT" if any logical
+drives are offline or critical or any non-standby physical drives are dead.
+
+Configuration commands for controller N are available via the special file
+/proc/rd/cN/user_command.  A human readable command can be written to this
+special file to initiate a configuration operation, and the results of the
+operation can then be read back from the special file in addition to being
+logged to the system console.  The shell command sequence
+
+  echo "<configuration-command>" > /proc/rd/c0/user_command
+  cat /proc/rd/c0/user_command
+
+is typically used to execute configuration commands.  The configuration
+commands are:
+
+  flush-cache
+
+    The "flush-cache" command flushes the controller's cache.  The system
+    automatically flushes the cache at shutdown or if the driver module is
+    unloaded, so this command is only needed to be certain a write back cache
+    is flushed to disk before the system is powered off by a command to a UPS.
+    Note that the flush-cache command also stops an asynchronous rebuild or
+    consistency check, so it should not be used except when the system is being
+    halted.
+
+  kill <channel>:<target-id>
+
+    The "kill" command marks the physical drive <channel>:<target-id> as DEAD.
+    This command is provided primarily for testing, and should not be used
+    during normal system operation.
+
+  make-online <channel>:<target-id>
+
+    The "make-online" command changes the physical drive <channel>:<target-id>
+    from status DEAD to status ONLINE.  In cases where multiple physical drives
+    have been killed simultaneously, this command may be used to bring them
+    back online, after which a consistency check is advisable.
+
+    Warning: make-online should only be used on a dead physical drive that is
+    an active part of a drive group, never on a standby drive.
+
+  make-standby <channel>:<target-id>
+
+    The "make-standby" command changes physical drive <channel>:<target-id>
+    from status DEAD to status STANDBY.  It should only be used in cases where
+    a dead drive was replaced after an automatic rebuild was performed onto a
+    standby drive.  It cannot be used to add a standby drive to the controller
+    configuration if one was not created initially; the BIOS Configuration
+    Utility must be used for that currently.
+
+  rebuild <channel>:<target-id>
+
+    The "rebuild" command initiates an asynchronous rebuild onto physical drive
+    <channel>:<target-id>.  It should only be used when a dead drive has been
+    replaced.
+
+  check-consistency <logical-drive-number>
+
+    The "check-consistency" command initiates an asynchronous consistency check
+    of <logical-drive-number> with automatic restoration.  It can be used
+    whenever it is desired to verify the consistency of the redundancy
+    information.
+
+  cancel-rebuild
+  cancel-consistency-check
+
+    The "cancel-rebuild" and "cancel-consistency-check" commands cancel any
+    rebuild or consistency check operations previously initiated.
+
+
+	       EXAMPLE I - DRIVE FAILURE WITHOUT A STANDBY DRIVE
+
+The following annotated logs demonstrate the controller configuration and and
+online status monitoring capabilities of the Linux DAC960 Driver.  The test
+configuration comprises 6 1GB Quantum Atlas I disk drives on two channels of a
+DAC960PJ controller.  The physical drives are configured into a single drive
+group without a standby drive, and the drive group has been configured into two
+logical drives, one RAID-5 and one RAID-6.  First, here is the current status
+of the RAID configuration:
+
+gwynedd:/u/lnz# cat /proc/rd/c0/current_status
+***** DAC960 RAID Driver Version 2.0.0 of 23 March 1999 *****
+Copyright 1998-1999 by Leonard N. Zubkoff <lnz@dandelion.com>
+Configuring Mylex DAC960PJ PCI RAID Controller
+  Firmware Version: 4.06-0-08, Channels: 3, Memory Size: 8MB
+  PCI Bus: 0, Device: 19, Function: 1, I/O Address: Unassigned
+  PCI Address: 0xFD4FC000 mapped at 0x8807000, IRQ Channel: 9
+  Controller Queue Depth: 128, Maximum Blocks per Command: 128
+  Driver Queue Depth: 127, Maximum Scatter/Gather Segments: 33
+  Stripe Size: 64KB, Segment Size: 8KB, BIOS Geometry: 255/63
+  Physical Devices:
+    0:1 - Disk: Online, 2201600 blocks
+    0:2 - Disk: Online, 2201600 blocks
+    0:3 - Disk: Online, 2201600 blocks
+    1:1 - Disk: Online, 2201600 blocks
+    1:2 - Disk: Online, 2201600 blocks
+    1:3 - Disk: Online, 2201600 blocks
+  Logical Drives:
+    /dev/rd/c0d0: RAID-5, Online, 5498880 blocks, Write Thru
+    /dev/rd/c0d1: RAID-6, Online, 3305472 blocks, Write Thru
+  No Rebuild or Consistency Check in Progress
+
+gwynedd:/u/lnz# cat /proc/rd/status
+OK
+
+The above messages indicate that everything is healthy, and /proc/rd/status
+returns "OK" indicating that there are no problems with any DAC960 controller
+in the system.  For demonstration purposes, while I/O is active Physical Drive
+1:1 is now disconnected, simulating a drive failure.  The failure is noted by
+the driver within 10 seconds of the controller's having detected it, and the
+driver logs the following console status messages indicating that Logical
+Drives 0 and 1 are now CRITICAL as a result of Physical Drive 1:1 being DEAD:
+
+DAC960#0: Logical Drive 0 (/dev/rd/c0d0) is now CRITICAL
+DAC960#0: Logical Drive 1 (/dev/rd/c0d1) is now CRITICAL
+DAC960#0: Physical Drive 1:2 Error Log: Sense Key = 6, ASC = 29, ASCQ = 02
+DAC960#0: Physical Drive 1:3 Error Log: Sense Key = 6, ASC = 29, ASCQ = 02
+DAC960#0: Physical Drive 1:1 killed because of timeout on SCSI command
+DAC960#0: Physical Drive 1:1 is now DEAD
+
+The Sense Keys logged here are just Check Condition / Unit Attention conditions
+arising from a SCSI bus reset that is forced by the controller during its error
+recovery procedures.  Concurrently with the above, the driver status available
+from /proc/rd also reflects the drive failure.  The status message in
+/proc/rd/status has changed from "OK" to "ALERT":
+
+gwynedd:/u/lnz# cat /proc/rd/status
+ALERT
+
+and /proc/rd/c0/current_status has been updated:
+
+gwynedd:/u/lnz# cat /proc/rd/c0/current_status
+  ...
+  Physical Devices:
+    0:1 - Disk: Online, 2201600 blocks
+    0:2 - Disk: Online, 2201600 blocks
+    0:3 - Disk: Online, 2201600 blocks
+    1:1 - Disk: Dead, 2201600 blocks
+    1:2 - Disk: Online, 2201600 blocks
+    1:3 - Disk: Online, 2201600 blocks
+  Logical Drives:
+    /dev/rd/c0d0: RAID-5, Critical, 5498880 blocks, Write Thru
+    /dev/rd/c0d1: RAID-6, Critical, 3305472 blocks, Write Thru
+  No Rebuild or Consistency Check in Progress
+
+Since there are no standby drives configured, the system can continue to access
+the logical drives in a performance degraded mode until the failed drive is
+replaced and a rebuild operation completed to restore the redundancy of the
+logical drives.  Once Physical Drive 1:1 is replaced with a properly
+functioning drive, or if the physical drive was killed without having failed
+(e.g., due to electrical problems on the SCSI bus), the user can instruct the
+controller to initiate a rebuild operation onto the newly replaced drive:
+
+gwynedd:/u/lnz# echo "rebuild 1:1" > /proc/rd/c0/user_command
+gwynedd:/u/lnz# cat /proc/rd/c0/user_command
+Rebuild of Physical Drive 1:1 Initiated
+
+The echo command instructs the controller to initiate an asynchronous rebuild
+operation onto Physical Drive 1:1, and the status message that results from the
+operation is then available for reading from /proc/rd/c0/user_command, as well
+as being logged to the console by the driver.
+
+Within 10 seconds of this command the driver logs the initiation of the
+asynchronous rebuild operation:
+
+DAC960#0: Rebuild of Physical Drive 1:1 Initiated
+DAC960#0: Physical Drive 1:1 Error Log: Sense Key = 6, ASC = 29, ASCQ = 01
+DAC960#0: Physical Drive 1:1 is now WRITE-ONLY
+DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 1% completed
+
+and /proc/rd/c0/current_status is updated:
+
+gwynedd:/u/lnz# cat /proc/rd/c0/current_status
+  ...
+  Physical Devices:
+    0:1 - Disk: Online, 2201600 blocks
+    0:2 - Disk: Online, 2201600 blocks
+    0:3 - Disk: Online, 2201600 blocks
+    1:1 - Disk: Write-Only, 2201600 blocks
+    1:2 - Disk: Online, 2201600 blocks
+    1:3 - Disk: Online, 2201600 blocks
+  Logical Drives:
+    /dev/rd/c0d0: RAID-5, Critical, 5498880 blocks, Write Thru
+    /dev/rd/c0d1: RAID-6, Critical, 3305472 blocks, Write Thru
+  Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 6% completed
+
+As the rebuild progresses, the current status in /proc/rd/c0/current_status is
+updated every 10 seconds:
+
+gwynedd:/u/lnz# cat /proc/rd/c0/current_status
+  ...
+  Physical Devices:
+    0:1 - Disk: Online, 2201600 blocks
+    0:2 - Disk: Online, 2201600 blocks
+    0:3 - Disk: Online, 2201600 blocks
+    1:1 - Disk: Write-Only, 2201600 blocks
+    1:2 - Disk: Online, 2201600 blocks
+    1:3 - Disk: Online, 2201600 blocks
+  Logical Drives:
+    /dev/rd/c0d0: RAID-5, Critical, 5498880 blocks, Write Thru
+    /dev/rd/c0d1: RAID-6, Critical, 3305472 blocks, Write Thru
+  Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 15% completed
+
+and every minute a progress message is logged to the console by the driver:
+
+DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 32% completed
+DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 63% completed
+DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 94% completed
+DAC960#0: Rebuild in Progress: Logical Drive 1 (/dev/rd/c0d1) 94% completed
+
+Finally, the rebuild completes successfully.  The driver logs the status of the 
+logical and physical drives and the rebuild completion:
+
+DAC960#0: Logical Drive 0 (/dev/rd/c0d0) is now ONLINE
+DAC960#0: Logical Drive 1 (/dev/rd/c0d1) is now ONLINE
+DAC960#0: Physical Drive 1:1 is now ONLINE
+DAC960#0: Rebuild Completed Successfully
+
+/proc/rd/c0/current_status is updated:
+
+gwynedd:/u/lnz# cat /proc/rd/c0/current_status
+  ...
+  Physical Devices:
+    0:1 - Disk: Online, 2201600 blocks
+    0:2 - Disk: Online, 2201600 blocks
+    0:3 - Disk: Online, 2201600 blocks
+    1:1 - Disk: Online, 2201600 blocks
+    1:2 - Disk: Online, 2201600 blocks
+    1:3 - Disk: Online, 2201600 blocks
+  Logical Drives:
+    /dev/rd/c0d0: RAID-5, Online, 5498880 blocks, Write Thru
+    /dev/rd/c0d1: RAID-6, Online, 3305472 blocks, Write Thru
+  Rebuild Completed Successfully
+
+and /proc/rd/status indicates that everything is healthy once again:
+
+gwynedd:/u/lnz# cat /proc/rd/status
+OK
+
+
+		EXAMPLE II - DRIVE FAILURE WITH A STANDBY DRIVE
+
+The following annotated logs demonstrate the controller configuration and and
+online status monitoring capabilities of the Linux DAC960 Driver.  The test
+configuration comprises 6 1GB Quantum Atlas I disk drives on two channels of a
+DAC960PJ controller.  The physical drives are configured into a single drive
+group with a standby drive, and the drive group has been configured into two
+logical drives, one RAID-5 and one RAID-6.  First, here is the current status
+of the RAID configuration:
+
+gwynedd:/u/lnz# cat /proc/rd/c0/current_status
+***** DAC960 RAID Driver Version 2.0.0 of 23 March 1999 *****
+Copyright 1998-1999 by Leonard N. Zubkoff <lnz@dandelion.com>
+Configuring Mylex DAC960PJ PCI RAID Controller
+  Firmware Version: 4.06-0-08, Channels: 3, Memory Size: 8MB
+  PCI Bus: 0, Device: 19, Function: 1, I/O Address: Unassigned
+  PCI Address: 0xFD4FC000 mapped at 0x8807000, IRQ Channel: 9
+  Controller Queue Depth: 128, Maximum Blocks per Command: 128
+  Driver Queue Depth: 127, Maximum Scatter/Gather Segments: 33
+  Stripe Size: 64KB, Segment Size: 8KB, BIOS Geometry: 255/63
+  Physical Devices:
+    0:1 - Disk: Online, 2201600 blocks
+    0:2 - Disk: Online, 2201600 blocks
+    0:3 - Disk: Online, 2201600 blocks
+    1:1 - Disk: Online, 2201600 blocks
+    1:2 - Disk: Online, 2201600 blocks
+    1:3 - Disk: Standby, 2201600 blocks
+  Logical Drives:
+    /dev/rd/c0d0: RAID-5, Online, 4399104 blocks, Write Thru
+    /dev/rd/c0d1: RAID-6, Online, 2754560 blocks, Write Thru
+  No Rebuild or Consistency Check in Progress
+
+gwynedd:/u/lnz# cat /proc/rd/status
+OK
+
+The above messages indicate that everything is healthy, and /proc/rd/status
+returns "OK" indicating that there are no problems with any DAC960 controller
+in the system.  For demonstration purposes, while I/O is active Physical Drive
+1:2 is now disconnected, simulating a drive failure.  The failure is noted by
+the driver within 10 seconds of the controller's having detected it, and the
+driver logs the following console status messages:
+
+DAC960#0: Logical Drive 0 (/dev/rd/c0d0) is now CRITICAL
+DAC960#0: Logical Drive 1 (/dev/rd/c0d1) is now CRITICAL
+DAC960#0: Physical Drive 1:1 Error Log: Sense Key = 6, ASC = 29, ASCQ = 02
+DAC960#0: Physical Drive 1:3 Error Log: Sense Key = 6, ASC = 29, ASCQ = 02
+DAC960#0: Physical Drive 1:2 killed because of timeout on SCSI command
+DAC960#0: Physical Drive 1:2 is now DEAD
+DAC960#0: Physical Drive 1:2 killed because it was removed
+
+Since a standby drive is configured, the controller automatically begins
+rebuilding onto the standby drive:
+
+DAC960#0: Physical Drive 1:3 is now WRITE-ONLY
+DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 4% completed
+
+Concurrently with the above, the driver status available from /proc/rd also
+reflects the drive failure and automatic rebuild.  The status message in
+/proc/rd/status has changed from "OK" to "ALERT":
+
+gwynedd:/u/lnz# cat /proc/rd/status
+ALERT
+
+and /proc/rd/c0/current_status has been updated:
+
+gwynedd:/u/lnz# cat /proc/rd/c0/current_status
+  ...
+  Physical Devices:
+    0:1 - Disk: Online, 2201600 blocks
+    0:2 - Disk: Online, 2201600 blocks
+    0:3 - Disk: Online, 2201600 blocks
+    1:1 - Disk: Online, 2201600 blocks
+    1:2 - Disk: Dead, 2201600 blocks
+    1:3 - Disk: Write-Only, 2201600 blocks
+  Logical Drives:
+    /dev/rd/c0d0: RAID-5, Critical, 4399104 blocks, Write Thru
+    /dev/rd/c0d1: RAID-6, Critical, 2754560 blocks, Write Thru
+  Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 4% completed
+
+As the rebuild progresses, the current status in /proc/rd/c0/current_status is
+updated every 10 seconds:
+
+gwynedd:/u/lnz# cat /proc/rd/c0/current_status
+  ...
+  Physical Devices:
+    0:1 - Disk: Online, 2201600 blocks
+    0:2 - Disk: Online, 2201600 blocks
+    0:3 - Disk: Online, 2201600 blocks
+    1:1 - Disk: Online, 2201600 blocks
+    1:2 - Disk: Dead, 2201600 blocks
+    1:3 - Disk: Write-Only, 2201600 blocks
+  Logical Drives:
+    /dev/rd/c0d0: RAID-5, Critical, 4399104 blocks, Write Thru
+    /dev/rd/c0d1: RAID-6, Critical, 2754560 blocks, Write Thru
+  Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 40% completed
+
+and every minute a progress message is logged on the console by the driver:
+
+DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 40% completed
+DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 76% completed
+DAC960#0: Rebuild in Progress: Logical Drive 1 (/dev/rd/c0d1) 66% completed
+DAC960#0: Rebuild in Progress: Logical Drive 1 (/dev/rd/c0d1) 84% completed
+
+Finally, the rebuild completes successfully.  The driver logs the status of the 
+logical and physical drives and the rebuild completion:
+
+DAC960#0: Logical Drive 0 (/dev/rd/c0d0) is now ONLINE
+DAC960#0: Logical Drive 1 (/dev/rd/c0d1) is now ONLINE
+DAC960#0: Physical Drive 1:3 is now ONLINE
+DAC960#0: Rebuild Completed Successfully
+
+/proc/rd/c0/current_status is updated:
+
+***** DAC960 RAID Driver Version 2.0.0 of 23 March 1999 *****
+Copyright 1998-1999 by Leonard N. Zubkoff <lnz@dandelion.com>
+Configuring Mylex DAC960PJ PCI RAID Controller
+  Firmware Version: 4.06-0-08, Channels: 3, Memory Size: 8MB
+  PCI Bus: 0, Device: 19, Function: 1, I/O Address: Unassigned
+  PCI Address: 0xFD4FC000 mapped at 0x8807000, IRQ Channel: 9
+  Controller Queue Depth: 128, Maximum Blocks per Command: 128
+  Driver Queue Depth: 127, Maximum Scatter/Gather Segments: 33
+  Stripe Size: 64KB, Segment Size: 8KB, BIOS Geometry: 255/63
+  Physical Devices:
+    0:1 - Disk: Online, 2201600 blocks
+    0:2 - Disk: Online, 2201600 blocks
+    0:3 - Disk: Online, 2201600 blocks
+    1:1 - Disk: Online, 2201600 blocks
+    1:2 - Disk: Dead, 2201600 blocks
+    1:3 - Disk: Online, 2201600 blocks
+  Logical Drives:
+    /dev/rd/c0d0: RAID-5, Online, 4399104 blocks, Write Thru
+    /dev/rd/c0d1: RAID-6, Online, 2754560 blocks, Write Thru
+  Rebuild Completed Successfully
+
+and /proc/rd/status indicates that everything is healthy once again:
+
+gwynedd:/u/lnz# cat /proc/rd/status
+OK
+
+Note that the absence of a viable standby drive does not create an "ALERT"
+status.  Once dead Physical Drive 1:2 has been replaced, the controller must be
+told that this has occurred and that the newly replaced drive should become the
+new standby drive:
+
+gwynedd:/u/lnz# echo "make-standby 1:2" > /proc/rd/c0/user_command
+gwynedd:/u/lnz# cat /proc/rd/c0/user_command
+Make Standby of Physical Drive 1:2 Succeeded
+
+The echo command instructs the controller to make Physical Drive 1:2 into a
+standby drive, and the status message that results from the operation is then
+available for reading from /proc/rd/c0/user_command, as well as being logged to
+the console by the driver.  Within 60 seconds of this command the driver logs:
+
+DAC960#0: Physical Drive 1:2 Error Log: Sense Key = 6, ASC = 29, ASCQ = 01
+DAC960#0: Physical Drive 1:2 is now STANDBY
+DAC960#0: Make Standby of Physical Drive 1:2 Succeeded
+
+and /proc/rd/c0/current_status is updated:
+
+gwynedd:/u/lnz# cat /proc/rd/c0/current_status
+  ...
+  Physical Devices:
+    0:1 - Disk: Online, 2201600 blocks
+    0:2 - Disk: Online, 2201600 blocks
+    0:3 - Disk: Online, 2201600 blocks
+    1:1 - Disk: Online, 2201600 blocks
+    1:2 - Disk: Standby, 2201600 blocks
+    1:3 - Disk: Online, 2201600 blocks
+  Logical Drives:
+    /dev/rd/c0d0: RAID-5, Online, 4399104 blocks, Write Thru
+    /dev/rd/c0d1: RAID-6, Online, 2754560 blocks, Write Thru
+  Rebuild Completed Successfully
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/linux/drivers/block/DAC960.c linux/linux/drivers/block/DAC960.c
--- linux.vanilla/linux/drivers/block/DAC960.c	Thu Jan  1 01:00:00 1970
+++ linux/linux/drivers/block/DAC960.c	Thu Mar 25 23:52:48 1999
@@ -0,0 +1,3013 @@
+/*
+
+  Linux Driver for Mylex DAC960 and DAC1100 PCI RAID Controllers
+
+  Copyright 1998-1999 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+  This program is free software; you may redistribute and/or modify it under
+  the terms of the GNU General Public License Version 2 as published by the
+  Free Software Foundation.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
+  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+  for complete details.
+
+  The author respectfully requests that any modifications to this software be
+  sent directly to him for evaluation and testing.
+
+*/
+
+
+#define DAC960_DriverVersion			"2.0.0"
+#define DAC960_DriverDate			"23 March 1999"
+
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/blk.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/hdreg.h>
+#include <linux/ioport.h>
+#include <linux/locks.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/proc_fs.h>
+#include <linux/timer.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/segment.h>
+#include "DAC960.h"
+
+
+/*
+  DAC960_ControllerCount is the number of DAC960 Controllers detected.
+*/
+
+static int
+  DAC960_ControllerCount =			0;
+
+
+/*
+  DAC960_ActiveControllerCount is the number of Active DAC960 Controllers
+  detected.
+*/
+
+static int
+  DAC960_ActiveControllerCount =		0;
+
+
+/*
+  DAC960_Controllers is an array of pointers to the DAC960 Controller
+  structures.
+*/
+
+static DAC960_Controller_T
+  *DAC960_Controllers[DAC960_MaxControllers] =	{ NULL };
+
+
+/*
+  DAC960_FileOperations is the File Operations structure for DAC960 Logical
+  Disk Devices.
+*/
+
+static FileOperations_T
+  DAC960_FileOperations =
+    { lseek:		    NULL,
+      read:		    block_read,
+      write:		    block_write,
+      readdir:		    NULL,
+      select:		    NULL,
+      ioctl:		    DAC960_Ioctl,
+      mmap:		    NULL,
+      open:		    DAC960_Open,
+      release:		    DAC960_Release,
+      fsync:		    block_fsync,
+      fasync:		    NULL,
+      check_media_change:   NULL,
+      revalidate:	    NULL };
+
+
+/*
+  DAC960_ProcDirectoryEntry is the DAC960 /proc/rd directory entry.
+*/
+
+static PROC_DirectoryEntry_T
+  DAC960_ProcDirectoryEntry;
+
+
+/*
+  DAC960_AnnounceDriver announces the Driver Version and Date, Author's Name,
+  Copyright Notice, and Electronic Mail Address.
+*/
+
+static void DAC960_AnnounceDriver(DAC960_Controller_T *Controller)
+{
+  DAC960_Announce("***** DAC960 RAID Driver Version "
+		  DAC960_DriverVersion " of "
+		  DAC960_DriverDate " *****\n", Controller);
+  DAC960_Announce("Copyright 1998-1999 by Leonard N. Zubkoff "
+		  "<lnz@dandelion.com>\n", Controller);
+}
+
+
+/*
+  DAC960_Failure prints a standardized error message, and then returns false.
+*/
+
+static boolean DAC960_Failure(DAC960_Controller_T *Controller,
+			      char *ErrorMessage)
+{
+  DAC960_Error("While configuring DAC960 PCI RAID Controller at\n",
+	       Controller);
+  if (Controller->IO_Address == 0)
+    DAC960_Error("PCI Bus %d Device %d Function %d I/O Address N/A "
+		 "PCI Address 0x%X\n", Controller,
+		 Controller->Bus, Controller->Device,
+		 Controller->Function, Controller->PCI_Address);
+  else DAC960_Error("PCI Bus %d Device %d Function %d I/O Address "
+		    "0x%X PCI Address 0x%X\n", Controller,
+		    Controller->Bus, Controller->Device,
+		    Controller->Function, Controller->IO_Address,
+		    Controller->PCI_Address);
+  DAC960_Error("%s FAILED - DETACHING\n", Controller, ErrorMessage);
+  return false;
+}
+
+
+/*
+  DAC960_ClearCommand clears critical fields of Command.
+*/
+
+static inline void DAC960_ClearCommand(DAC960_Command_T *Command)
+{
+  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+  CommandMailbox->Words[0] = 0;
+  CommandMailbox->Words[1] = 0;
+  CommandMailbox->Words[2] = 0;
+  CommandMailbox->Words[3] = 0;
+  Command->CommandStatus = 0;
+}
+
+
+/*
+  DAC960_AllocateCommand allocates a Command structure from Controller's
+  free list.
+*/
+
+static inline DAC960_Command_T *DAC960_AllocateCommand(DAC960_Controller_T
+						       *Controller)
+{
+  DAC960_Command_T *Command = Controller->FreeCommands;
+  if (Command == NULL) return NULL;
+  Controller->FreeCommands = Command->Next;
+  Command->Next = NULL;
+  return Command;
+}
+
+
+/*
+  DAC960_DeallocateCommand deallocates Command, returning it to Controller's
+  free list.
+*/
+
+static inline void DAC960_DeallocateCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  Command->Next = Controller->FreeCommands;
+  Controller->FreeCommands = Command;
+}
+
+
+/*
+  DAC960_QueueCommand queues Command.
+*/
+
+static void DAC960_QueueCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  void *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+  DAC960_CommandMailbox_T *NextCommandMailbox;
+  CommandMailbox->Common.CommandIdentifier = Command - Controller->Commands;
+  switch (Controller->ControllerType)
+    {
+    case DAC960_V5_Controller:
+      NextCommandMailbox = Controller->NextCommandMailbox;
+      DAC960_V5_WriteCommandMailbox(NextCommandMailbox, CommandMailbox);
+      if (Controller->PreviousCommandMailbox1->Words[0] == 0 ||
+	  Controller->PreviousCommandMailbox2->Words[0] == 0)
+	if (Controller->DualModeMemoryMailboxInterface)
+	  DAC960_V5_MemoryMailboxNewCommand(ControllerBaseAddress);
+	else DAC960_V5_HardwareMailboxNewCommand(ControllerBaseAddress);
+      Controller->PreviousCommandMailbox2 = Controller->PreviousCommandMailbox1;
+      Controller->PreviousCommandMailbox1 = NextCommandMailbox;
+      if (++NextCommandMailbox > Controller->LastCommandMailbox)
+	NextCommandMailbox = Controller->FirstCommandMailbox;
+      Controller->NextCommandMailbox = NextCommandMailbox;
+      break;
+    case DAC960_V4_Controller:
+      NextCommandMailbox = Controller->NextCommandMailbox;
+      DAC960_V4_WriteCommandMailbox(NextCommandMailbox, CommandMailbox);
+      if (Controller->PreviousCommandMailbox1->Words[0] == 0 ||
+	  Controller->PreviousCommandMailbox2->Words[0] == 0)
+	if (Controller->DualModeMemoryMailboxInterface)
+	  DAC960_V4_MemoryMailboxNewCommand(ControllerBaseAddress);
+	else DAC960_V4_HardwareMailboxNewCommand(ControllerBaseAddress);
+      Controller->PreviousCommandMailbox2 = Controller->PreviousCommandMailbox1;
+      Controller->PreviousCommandMailbox1 = NextCommandMailbox;
+      if (++NextCommandMailbox > Controller->LastCommandMailbox)
+	NextCommandMailbox = Controller->FirstCommandMailbox;
+      Controller->NextCommandMailbox = NextCommandMailbox;
+      break;
+    case DAC960_V3_Controller:
+      while (DAC960_V3_MailboxFullP(ControllerBaseAddress))
+	udelay(1);
+      DAC960_V3_WriteCommandMailbox(ControllerBaseAddress, CommandMailbox);
+      DAC960_V3_NewCommand(ControllerBaseAddress);
+      break;
+    }
+}
+
+
+/*
+  DAC960_ExecuteCommand executes Command and waits for completion.  It
+  returns true on success and false on failure.
+*/
+
+static boolean DAC960_ExecuteCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  Semaphore_T Semaphore = MUTEX_LOCKED;
+  unsigned long ProcessorFlags;
+  Command->Semaphore = &Semaphore;
+  DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
+  DAC960_QueueCommand(Command);
+  DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
+  down(&Semaphore);
+  return Command->CommandStatus == DAC960_NormalCompletion;
+}
+
+
+/*
+  DAC960_ExecuteType3 executes a DAC960 Type 3 Command and waits for
+  completion.  It returns true on success and false on failure.
+*/
+
+static boolean DAC960_ExecuteType3(DAC960_Controller_T *Controller,
+				   DAC960_CommandOpcode_T CommandOpcode,
+				   void *DataPointer)
+{
+  DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
+  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+  boolean Result;
+  DAC960_ClearCommand(Command);
+  Command->CommandType = DAC960_ImmediateCommand;
+  CommandMailbox->Type3.CommandOpcode = CommandOpcode;
+  CommandMailbox->Type3.BusAddress = Virtual_to_Bus(DataPointer);
+  Result = DAC960_ExecuteCommand(Command);
+  DAC960_DeallocateCommand(Command);
+  return Result;
+}
+
+
+/*
+  DAC960_ExecuteType3D executes a DAC960 Type 3D Command and waits for
+  completion.  It returns true on success and false on failure.
+*/
+
+static boolean DAC960_ExecuteType3D(DAC960_Controller_T *Controller,
+				    DAC960_CommandOpcode_T CommandOpcode,
+				    unsigned char Channel,
+				    unsigned char TargetID,
+				    void *DataPointer)
+{
+  DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
+  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+  boolean Result;
+  DAC960_ClearCommand(Command);
+  Command->CommandType = DAC960_ImmediateCommand;
+  CommandMailbox->Type3D.CommandOpcode = CommandOpcode;
+  CommandMailbox->Type3D.Channel = Channel;
+  CommandMailbox->Type3D.TargetID = TargetID;
+  CommandMailbox->Type3D.BusAddress = Virtual_to_Bus(DataPointer);
+  Result = DAC960_ExecuteCommand(Command);
+  DAC960_DeallocateCommand(Command);
+  return Result;
+}
+
+
+/*
+  DAC960_EnableMemoryMailboxInterface enables the Memory Mailbox Interface.
+*/
+
+static boolean DAC960_EnableMemoryMailboxInterface(DAC960_Controller_T
+						   *Controller)
+{
+  void *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_CommandMailbox_T *CommandMailboxesMemory;
+  DAC960_StatusMailbox_T *StatusMailboxesMemory;
+  DAC960_CommandMailbox_T CommandMailbox;
+  DAC960_CommandStatus_T CommandStatus;
+  void *SavedMemoryMailboxesAddress = NULL;
+  short NextCommandMailboxIndex = 0;
+  short NextStatusMailboxIndex = 0;
+  long TimeoutCounter = 1000000;
+  int i;
+  if (Controller->ControllerType == DAC960_V5_Controller)
+    DAC960_V5_RestoreMemoryMailboxInfo(Controller,
+				       &SavedMemoryMailboxesAddress,
+				       &NextCommandMailboxIndex,
+				       &NextStatusMailboxIndex);
+  else DAC960_V4_RestoreMemoryMailboxInfo(Controller,
+					  &SavedMemoryMailboxesAddress,
+					  &NextCommandMailboxIndex,
+					  &NextStatusMailboxIndex);
+  if (SavedMemoryMailboxesAddress == NULL)
+    CommandMailboxesMemory =
+      (DAC960_CommandMailbox_T *) __get_free_pages(GFP_KERNEL, 1, 0);
+  else CommandMailboxesMemory = SavedMemoryMailboxesAddress;
+  memset(CommandMailboxesMemory, 0, PAGE_SIZE << 1);
+  Controller->FirstCommandMailbox = CommandMailboxesMemory;
+  CommandMailboxesMemory += DAC960_CommandMailboxCount - 1;
+  Controller->LastCommandMailbox = CommandMailboxesMemory;
+  Controller->NextCommandMailbox =
+    &Controller->FirstCommandMailbox[NextCommandMailboxIndex];
+  if (--NextCommandMailboxIndex < 0)
+    NextCommandMailboxIndex = DAC960_CommandMailboxCount - 1;
+  Controller->PreviousCommandMailbox1 =
+    &Controller->FirstCommandMailbox[NextCommandMailboxIndex];
+  if (--NextCommandMailboxIndex < 0)
+    NextCommandMailboxIndex = DAC960_CommandMailboxCount - 1;
+  Controller->PreviousCommandMailbox2 =
+    &Controller->FirstCommandMailbox[NextCommandMailboxIndex];
+  StatusMailboxesMemory =
+    (DAC960_StatusMailbox_T *) (CommandMailboxesMemory + 1);
+  Controller->FirstStatusMailbox = StatusMailboxesMemory;
+  StatusMailboxesMemory += DAC960_StatusMailboxCount - 1;
+  Controller->LastStatusMailbox = StatusMailboxesMemory;
+  Controller->NextStatusMailbox =
+    &Controller->FirstStatusMailbox[NextStatusMailboxIndex];
+  if (SavedMemoryMailboxesAddress != NULL) return true;
+  /* Enable the Memory Mailbox Interface. */
+  CommandMailbox.TypeX.CommandOpcode = 0x2B;
+  CommandMailbox.TypeX.CommandIdentifier = 0;
+  CommandMailbox.TypeX.CommandOpcode2 = 0x10;
+  CommandMailbox.TypeX.CommandMailboxesBusAddress =
+    Virtual_to_Bus(Controller->FirstCommandMailbox);
+  CommandMailbox.TypeX.StatusMailboxesBusAddress =
+    Virtual_to_Bus(Controller->FirstStatusMailbox);
+  for (i = 0; i < 2; i++)
+    switch (Controller->ControllerType)
+      {
+      case DAC960_V5_Controller:
+	while (--TimeoutCounter >= 0)
+	  {
+	    if (DAC960_V5_HardwareMailboxEmptyP(ControllerBaseAddress))
+	      break;
+	    udelay(1);
+	  }
+	if (TimeoutCounter < 0) return false;
+	DAC960_V5_WriteHardwareMailbox(ControllerBaseAddress, &CommandMailbox);
+	DAC960_V5_HardwareMailboxNewCommand(ControllerBaseAddress);
+	while (--TimeoutCounter >= 0)
+	  {
+	    if (DAC960_V5_HardwareMailboxStatusAvailableP(
+		  ControllerBaseAddress))
+	      break;
+	    udelay(1);
+	  }
+	if (TimeoutCounter < 0) return false;
+	CommandStatus = DAC960_V5_ReadStatusRegister(ControllerBaseAddress);
+	DAC960_V5_AcknowledgeHardwareMailboxInterrupt(ControllerBaseAddress);
+	DAC960_V5_AcknowledgeHardwareMailboxStatus(ControllerBaseAddress);
+	if (CommandStatus == DAC960_NormalCompletion) return true;
+	Controller->DualModeMemoryMailboxInterface = true;
+	CommandMailbox.TypeX.CommandOpcode2 = 0x14;
+	break;
+      case DAC960_V4_Controller:
+	while (--TimeoutCounter >= 0)
+	  {
+	    if (!DAC960_V4_HardwareMailboxFullP(ControllerBaseAddress))
+	      break;
+	    udelay(1);
+	  }
+	if (TimeoutCounter < 0) return false;
+	DAC960_V4_WriteHardwareMailbox(ControllerBaseAddress, &CommandMailbox);
+	DAC960_V4_HardwareMailboxNewCommand(ControllerBaseAddress);
+	while (--TimeoutCounter >= 0)
+	  {
+	    if (DAC960_V4_HardwareMailboxStatusAvailableP(
+		  ControllerBaseAddress))
+	      break;
+	    udelay(1);
+	  }
+	if (TimeoutCounter < 0) return false;
+	CommandStatus = DAC960_V4_ReadStatusRegister(ControllerBaseAddress);
+	DAC960_V4_AcknowledgeHardwareMailboxInterrupt(ControllerBaseAddress);
+	DAC960_V4_AcknowledgeHardwareMailboxStatus(ControllerBaseAddress);
+	if (CommandStatus == DAC960_NormalCompletion) return true;
+	Controller->DualModeMemoryMailboxInterface = true;
+	CommandMailbox.TypeX.CommandOpcode2 = 0x14;
+	break;
+      default:
+	break;
+      }
+  return false;
+}
+
+
+/*
+  DAC960_DetectControllers detects DAC960 PCI RAID Controllers by interrogating
+  the PCI Configuration Space for Controller Type.
+*/
+
+static void DAC960_DetectControllers(DAC960_ControllerType_T ControllerType)
+{
+  unsigned short VendorID = 0, DeviceID = 0, Index = 0;
+  unsigned char Bus, DeviceFunction, IRQ_Channel;
+  unsigned int BaseAddress0, BaseAddress1;
+  unsigned int MemoryWindowSize = 0;
+  switch (ControllerType)
+    {
+    case DAC960_V5_Controller:
+      VendorID = PCI_VENDOR_ID_DEC;
+      DeviceID = PCI_DEVICE_ID_DEC_21285;
+      MemoryWindowSize = DAC960_V5_RegisterWindowSize;
+      break;
+    case DAC960_V4_Controller:
+      VendorID = PCI_VENDOR_ID_MYLEX;
+      DeviceID = PCI_DEVICE_ID_MYLEX_DAC960P_V4;
+      MemoryWindowSize = DAC960_V4_RegisterWindowSize;
+      break;
+    case DAC960_V3_Controller:
+      VendorID = PCI_VENDOR_ID_MYLEX;
+      DeviceID = PCI_DEVICE_ID_MYLEX_DAC960P_V3;
+      MemoryWindowSize = DAC960_V3_RegisterWindowSize;
+      break;
+    }
+  while (pcibios_find_device(VendorID, DeviceID,
+			     Index++, &Bus, &DeviceFunction) == 0)
+    {
+      DAC960_Controller_T *Controller = (DAC960_Controller_T *)
+	kmalloc(sizeof(DAC960_Controller_T), GFP_ATOMIC);
+      DAC960_IO_Address_T IO_Address = 0;
+      DAC960_PCI_Address_T PCI_Address = 0;
+      unsigned char Device = DeviceFunction >> 3;
+      unsigned char Function = DeviceFunction & 0x7;
+      unsigned short SubsystemVendorID, SubsystemDeviceID;
+      pcibios_read_config_dword(Bus, DeviceFunction,
+				PCI_BASE_ADDRESS_0, &BaseAddress0);
+      pcibios_read_config_dword(Bus, DeviceFunction,
+				PCI_BASE_ADDRESS_1, &BaseAddress1);
+      pcibios_read_config_word(Bus, DeviceFunction,
+			       PCI_SUBSYSTEM_VENDOR_ID, &SubsystemVendorID);
+      pcibios_read_config_word(Bus, DeviceFunction,
+			       PCI_SUBSYSTEM_ID, &SubsystemDeviceID);
+      pcibios_read_config_byte(Bus, DeviceFunction,
+			       PCI_INTERRUPT_LINE, &IRQ_Channel);
+      switch (ControllerType)
+	{
+	case DAC960_V5_Controller:
+	  if (!(SubsystemVendorID == PCI_VENDOR_ID_MYLEX &&
+		SubsystemDeviceID == PCI_DEVICE_ID_MYLEX_DAC960P_V5))
+	    goto Ignore;
+	  PCI_Address = BaseAddress0 & PCI_BASE_ADDRESS_MEM_MASK;
+	  break;
+	case DAC960_V4_Controller:
+	  PCI_Address = BaseAddress0 & PCI_BASE_ADDRESS_MEM_MASK;
+	  break;
+	case DAC960_V3_Controller:
+	  IO_Address = BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK;
+	  PCI_Address = BaseAddress1 & PCI_BASE_ADDRESS_MEM_MASK;
+	  break;
+	}
+      if (DAC960_ControllerCount == DAC960_MaxControllers)
+	{
+	  DAC960_Error("More than %d DAC960 Controllers detected - "
+		       "ignoring from Controller at\n",
+		       NULL, DAC960_MaxControllers);
+	  goto Ignore;
+	}
+      if (Controller == NULL)
+	{
+	  DAC960_Error("Unable to allocate Controller structure for "
+		       "Controller at\n", NULL);
+	  goto Ignore;
+	}
+      memset(Controller, 0, sizeof(DAC960_Controller_T));
+      Controller->ControllerNumber = DAC960_ControllerCount;
+      DAC960_Controllers[DAC960_ControllerCount++] = Controller;
+      DAC960_AnnounceDriver(Controller);
+      Controller->ControllerType = ControllerType;
+      Controller->IO_Address = IO_Address;
+      Controller->PCI_Address = PCI_Address;
+      Controller->Bus = Bus;
+      Controller->Device = Device;
+      Controller->Function = Function;
+      sprintf(Controller->ControllerName, "c%d", Controller->ControllerNumber);
+      /*
+	Acquire shared access to the IRQ Channel.
+      */
+      if (IRQ_Channel == 0 || IRQ_Channel >= NR_IRQS)
+	{
+	  DAC960_Error("IRQ Channel %d illegal for Controller at\n",
+		       Controller, IRQ_Channel);
+	  goto Failure;
+	}
+      strcpy(Controller->FullModelName, "DAC960");
+      if (request_irq(IRQ_Channel, DAC960_InterruptHandler,
+		      SA_INTERRUPT | SA_SHIRQ, Controller->FullModelName,
+		      Controller) < 0)
+	{
+	  DAC960_Error("Unable to acquire IRQ Channel %d for Controller at\n",
+		       Controller, IRQ_Channel);
+	  goto Failure;
+	}
+      Controller->IRQ_Channel = IRQ_Channel;
+      /*
+	Map the Controller Register Window.
+      */
+      if (MemoryWindowSize < PAGE_SIZE)
+	MemoryWindowSize = PAGE_SIZE;
+      Controller->MemoryMappedAddress =
+	ioremap_nocache(PCI_Address & PAGE_MASK, MemoryWindowSize);
+      Controller->BaseAddress =
+	Controller->MemoryMappedAddress + (PCI_Address & ~PAGE_MASK);
+      if (Controller->MemoryMappedAddress == NULL)
+	{
+	  DAC960_Error("Unable to map Controller Register Window for "
+		       "Controller at\n", Controller);
+	  goto Failure;
+	}
+      switch (ControllerType)
+	{
+	case DAC960_V5_Controller:
+	  DAC960_V5_DisableInterrupts(Controller->BaseAddress);
+	  if (!DAC960_EnableMemoryMailboxInterface(Controller))
+	    {
+	      DAC960_Error("Unable to Enable Memory Mailbox Interface "
+			   "for Controller at\n", Controller);
+	      goto Failure;
+	    }
+	  DAC960_V5_EnableInterrupts(Controller->BaseAddress);
+	  break;
+	case DAC960_V4_Controller:
+	  DAC960_V4_DisableInterrupts(Controller->BaseAddress);
+	  if (!DAC960_EnableMemoryMailboxInterface(Controller))
+	    {
+	      DAC960_Error("Unable to Enable Memory Mailbox Interface "
+			   "for Controller at\n", Controller);
+	      goto Failure;
+	    }
+	  DAC960_V4_EnableInterrupts(Controller->BaseAddress);
+	  break;
+	case DAC960_V3_Controller:
+	  request_region(Controller->IO_Address, 0x80,
+			 Controller->FullModelName);
+	  DAC960_V3_EnableInterrupts(Controller->BaseAddress);
+	  break;
+	}
+      DAC960_ActiveControllerCount++;
+      Controller->Commands[0].Controller = Controller;
+      Controller->Commands[0].Next = NULL;
+      Controller->FreeCommands = &Controller->Commands[0];
+      continue;
+    Failure:
+      if (IO_Address == 0)
+	DAC960_Error("PCI Bus %d Device %d Function %d I/O Address N/A "
+		     "PCI Address 0x%X\n", Controller,
+		     Bus, Device, Function, PCI_Address);
+      else DAC960_Error("PCI Bus %d Device %d Function %d I/O Address "
+			"0x%X PCI Address 0x%X\n", Controller,
+			Bus, Device, Function, IO_Address, PCI_Address);
+      if (Controller == NULL) break;
+      if (Controller->IRQ_Channel > 0)
+	free_irq(IRQ_Channel, Controller);
+      if (Controller->MemoryMappedAddress != NULL)
+	iounmap(Controller->MemoryMappedAddress);
+      DAC960_Controllers[Controller->ControllerNumber] = NULL;
+    Ignore:
+      kfree(Controller);
+    }
+}
+
+
+/*
+  DAC960_ReadControllerConfiguration reads the Configuration Information
+  from Controller and initializes the Controller structure.
+*/
+
+static boolean DAC960_ReadControllerConfiguration(DAC960_Controller_T
+						  *Controller)
+{
+  DAC960_Enquiry2_T Enquiry2;
+  DAC960_Config2_T Config2;
+  int LogicalDriveNumber, Channel, TargetID;
+  if (!DAC960_ExecuteType3(Controller, DAC960_Enquiry,
+			   &Controller->Enquiry[0]))
+    return DAC960_Failure(Controller, "ENQUIRY");
+  if (!DAC960_ExecuteType3(Controller, DAC960_Enquiry2, &Enquiry2))
+    return DAC960_Failure(Controller, "ENQUIRY2");
+  if (!DAC960_ExecuteType3(Controller, DAC960_ReadConfig2, &Config2))
+    return DAC960_Failure(Controller, "READ CONFIG2");
+  if (!DAC960_ExecuteType3(Controller, DAC960_GetLogicalDriveInformation,
+			   &Controller->LogicalDriveInformation[0]))
+    return DAC960_Failure(Controller, "GET LOGICAL DRIVE INFORMATION");
+  for (Channel = 0; Channel < Enquiry2.ActualChannels; Channel++)
+    for (TargetID = 0; TargetID < DAC960_MaxTargets; TargetID++)
+      if (!DAC960_ExecuteType3D(Controller, DAC960_GetDeviceState,
+				Channel, TargetID,
+				&Controller->DeviceState[0][Channel][TargetID]))
+	  return DAC960_Failure(Controller, "GET DEVICE STATE");
+  /*
+    Initialize the Controller Model Name and Full Model Name fields.
+  */
+  switch (Enquiry2.HardwareID.SubModel)
+    {
+    case DAC960_P_PD_PU:
+      if (Enquiry2.SCSICapability.BusSpeed == DAC960_Ultra)
+	strcpy(Controller->ModelName, "DAC960PU");
+      else strcpy(Controller->ModelName, "DAC960PD");
+      break;
+    case DAC960_PL:
+      strcpy(Controller->ModelName, "DAC960PL");
+      break;
+    case DAC960_PG:
+      strcpy(Controller->ModelName, "DAC960PG");
+      break;
+    case DAC960_PJ:
+      strcpy(Controller->ModelName, "DAC960PJ");
+      break;
+    case DAC960_PR:
+      strcpy(Controller->ModelName, "DAC960PR");
+      break;
+    case DAC960_PT:
+      strcpy(Controller->ModelName, "DAC960PT");
+      break;
+    case DAC960_PTL0:
+      strcpy(Controller->ModelName, "DAC960PTL0");
+      break;
+    case DAC960_PRL:
+      strcpy(Controller->ModelName, "DAC960PRL");
+      break;
+    case DAC960_PTL1:
+      strcpy(Controller->ModelName, "DAC960PTL1");
+      break;
+    case DAC1164_P:
+      strcpy(Controller->ModelName, "DAC1164P");
+      break;
+    default:
+      return DAC960_Failure(Controller, "MODEL VERIFICATION");
+    }
+  strcpy(Controller->FullModelName, "Mylex ");
+  strcat(Controller->FullModelName, Controller->ModelName);
+  /*
+    Initialize the Controller Firmware Version field and verify that it
+    is a supported firmware version.  The supported firmware versions are:
+
+    DAC1164P		5.06 and above
+    DAC960PTL/PJ/PG	4.06 and above
+    DAC960PU/PD/PL	3.51 and above
+  */
+  sprintf(Controller->FirmwareVersion, "%d.%02d-%c-%02d",
+	  Enquiry2.FirmwareID.MajorVersion, Enquiry2.FirmwareID.MinorVersion,
+	  Enquiry2.FirmwareID.FirmwareType, Enquiry2.FirmwareID.TurnID);
+  if (!((Controller->FirmwareVersion[0] == '5' &&
+	 strcmp(Controller->FirmwareVersion, "5.06") >= 0) ||
+	(Controller->FirmwareVersion[0] == '4' &&
+	 strcmp(Controller->FirmwareVersion, "4.06") >= 0) ||
+	(Controller->FirmwareVersion[0] == '3' &&
+	 strcmp(Controller->FirmwareVersion, "3.51") >= 0)))
+    {
+      DAC960_Failure(Controller, "FIRMWARE VERSION VERIFICATION");
+      DAC960_Error("Firmware Version = '%s'\n", Controller,
+		   Controller->FirmwareVersion);
+      return false;
+    }
+  /*
+    Initialize the Controller Channels, Memory Size, and SAF-TE Fault
+    Management Enabled fields.
+  */
+  Controller->Channels = Enquiry2.ActualChannels;
+  Controller->MemorySize = Enquiry2.MemorySize >> 20;
+  Controller->SAFTE_FaultManagementEnabled =
+    Enquiry2.FaultManagementType == DAC960_SAFTE;
+  /*
+    Initialize the Controller Queue Depth, Driver Queue Depth, Logical Drive
+    Count, Maximum Blocks per Command, and Maximum Scatter/Gather Segments.
+    The Driver Queue Depth must be at most one less than the Controller Queue
+    Depth to allow for an automatic drive rebuild operation.
+  */
+  Controller->ControllerQueueDepth = Controller->Enquiry[0].MaxCommands;
+  Controller->DriverQueueDepth = Controller->ControllerQueueDepth - 1;
+  Controller->LogicalDriveCount = Controller->Enquiry[0].NumberOfLogicalDrives;
+  Controller->MaxBlocksPerCommand = Enquiry2.MaxBlocksPerCommand;
+  Controller->MaxScatterGatherSegments = Enquiry2.MaxScatterGatherEntries;
+  /*
+    Initialize the Stripe Size, Segment Size, and Geometry Translation.
+  */
+  Controller->StripeSize = Config2.BlocksPerStripe * Config2.BlockFactor
+			   >> (10 - DAC960_BlockSizeBits);
+  Controller->SegmentSize = Config2.BlocksPerCacheLine * Config2.BlockFactor
+			    >> (10 - DAC960_BlockSizeBits);
+  switch (Config2.DriveGeometry)
+    {
+    case DAC960_Geometry_128_32:
+      Controller->GeometryTranslationHeads = 128;
+      Controller->GeometryTranslationSectors = 32;
+      break;
+    case DAC960_Geometry_255_63:
+      Controller->GeometryTranslationHeads = 255;
+      Controller->GeometryTranslationSectors = 63;
+      break;
+    default:
+      return DAC960_Failure(Controller, "CONFIG2 DRIVE GEOMETRY");
+    }
+  /*
+    Initialize the Logical Drive Initial State.
+  */
+  for (LogicalDriveNumber = 0;
+       LogicalDriveNumber < Controller->LogicalDriveCount;
+       LogicalDriveNumber++)
+    Controller->LogicalDriveInitialState[LogicalDriveNumber] =
+      Controller->LogicalDriveInformation[0]
+		  [LogicalDriveNumber].LogicalDriveState;
+  return true;
+}
+
+
+/*
+  DAC960_ReportControllerConfiguration reports the configuration of
+  Controller.
+*/
+
+static boolean DAC960_ReportControllerConfiguration(DAC960_Controller_T
+						    *Controller)
+{
+  int LogicalDriveNumber, Channel, TargetID;
+  DAC960_Info("Configuring Mylex %s PCI RAID Controller\n",
+	      Controller, Controller->ModelName);
+  DAC960_Info("  Firmware Version: %s, Channels: %d, Memory Size: %dMB\n",
+	      Controller, Controller->FirmwareVersion,
+	      Controller->Channels, Controller->MemorySize);
+  DAC960_Info("  PCI Bus: %d, Device: %d, Function: %d, I/O Address: ",
+	      Controller, Controller->Bus,
+	      Controller->Device, Controller->Function);
+  if (Controller->IO_Address == 0)
+    DAC960_Info("Unassigned\n", Controller);
+  else DAC960_Info("0x%X\n", Controller, Controller->IO_Address);
+  DAC960_Info("  PCI Address: 0x%X mapped at 0x%lX, IRQ Channel: %d\n",
+	      Controller, Controller->PCI_Address,
+	      (unsigned long) Controller->BaseAddress,
+	      Controller->IRQ_Channel);
+  DAC960_Info("  Controller Queue Depth: %d, "
+	      "Maximum Blocks per Command: %d\n",
+	      Controller, Controller->ControllerQueueDepth,
+	      Controller->MaxBlocksPerCommand);
+  DAC960_Info("  Driver Queue Depth: %d, "
+	      "Maximum Scatter/Gather Segments: %d\n",
+	      Controller, Controller->DriverQueueDepth,
+	      Controller->MaxScatterGatherSegments);
+  DAC960_Info("  Stripe Size: %dKB, Segment Size: %dKB, "
+	      "BIOS Geometry: %d/%d\n", Controller,
+	      Controller->StripeSize,
+	      Controller->SegmentSize,
+	      Controller->GeometryTranslationHeads,
+	      Controller->GeometryTranslationSectors);
+  if (Controller->SAFTE_FaultManagementEnabled)
+    DAC960_Info("  SAF-TE Fault Management Enabled\n", Controller);
+  DAC960_Info("  Physical Devices:\n", Controller);
+  for (Channel = 0; Channel < Controller->Channels; Channel++)
+    for (TargetID = 0; TargetID < DAC960_MaxTargets; TargetID++)
+      {
+	DAC960_DeviceState_T *DeviceState =
+	  &Controller->DeviceState[Controller->DeviceStateIndex]
+				  [Channel][TargetID];
+	if (!DeviceState->Present) continue;
+	switch (DeviceState->DeviceType)
+	  {
+	  case DAC960_OtherType:
+	    DAC960_Info("    %d:%d - Other\n", Controller, Channel, TargetID);
+	    break;
+	  case DAC960_DiskType:
+	    DAC960_Info("    %d:%d - Disk: %s, %d blocks\n", Controller,
+			Channel, TargetID,
+			(DeviceState->DeviceState == DAC960_Device_Dead
+			 ? "Dead"
+			 : DeviceState->DeviceState == DAC960_Device_WriteOnly
+			   ? "Write-Only"
+			   : DeviceState->DeviceState == DAC960_Device_Online
+			     ? "Online" : "Standby"),
+			DeviceState->DiskSize);
+	    break;
+	  case DAC960_SequentialType:
+	    DAC960_Info("    %d:%d - Sequential\n", Controller,
+			Channel, TargetID);
+	    break;
+	  case DAC960_CDROM_or_WORM_Type:
+	    DAC960_Info("    %d:%d - CD-ROM or WORM\n", Controller,
+			Channel, TargetID);
+	    break;
+	  }
+
+      }
+  DAC960_Info("  Logical Drives:\n", Controller);
+  for (LogicalDriveNumber = 0;
+       LogicalDriveNumber < Controller->LogicalDriveCount;
+       LogicalDriveNumber++)
+    {
+      DAC960_LogicalDriveInformation_T *LogicalDriveInformation =
+	&Controller->LogicalDriveInformation
+	   [Controller->LogicalDriveInformationIndex][LogicalDriveNumber];
+      DAC960_Info("    /dev/rd/c%dd%d: RAID-%d, %s, %d blocks, %s\n",
+		  Controller, Controller->ControllerNumber, LogicalDriveNumber,
+		  LogicalDriveInformation->RAIDLevel,
+		  (LogicalDriveInformation->LogicalDriveState ==
+		     DAC960_LogicalDrive_Online
+		   ? "Online"
+		   : LogicalDriveInformation->LogicalDriveState ==
+		     DAC960_LogicalDrive_Critical
+		     ? "Critical" : "Offline"),
+		  LogicalDriveInformation->LogicalDriveSize,
+		  (LogicalDriveInformation->WriteBack
+		   ? "Write Back" : "Write Thru"));
+    }
+  return true;
+}
+
+
+/*
+  DAC960_RegisterBlockDevice registers the Block Device structures
+  associated with Controller.
+*/
+
+static boolean DAC960_RegisterBlockDevice(DAC960_Controller_T *Controller)
+{
+  static void (*RequestFunctions[DAC960_MaxControllers])(void) =
+    { DAC960_RequestFunction0, DAC960_RequestFunction1,
+      DAC960_RequestFunction2, DAC960_RequestFunction3,
+      DAC960_RequestFunction4, DAC960_RequestFunction5,
+      DAC960_RequestFunction6, DAC960_RequestFunction7 };
+  int MajorNumber = DAC960_MAJOR + Controller->ControllerNumber;
+  GenericDiskInfo_T *GenericDiskInfo;
+  int MinorNumber;
+  /*
+    Register the Block Device Major Number for this DAC960 Controller.
+  */
+  if (register_blkdev(MajorNumber, "rd", &DAC960_FileOperations) < 0)
+    {
+      DAC960_Error("UNABLE TO ACQUIRE MAJOR NUMBER %d - DETACHING\n",
+		   Controller, MajorNumber);
+      return false;
+    }
+  /*
+    Initialize the I/O Request Function.
+  */
+  blk_dev[MajorNumber].request_fn =
+    RequestFunctions[Controller->ControllerNumber];
+  /*
+    Initialize the Disk Partitions array, Partition Sizes array, Block Sizes
+    array, Max Sectors per Request array, and Max Segments per Request array.
+  */
+  for (MinorNumber = 0; MinorNumber < DAC960_MinorCount; MinorNumber++)
+    {
+      Controller->BlockSizes[MinorNumber] = BLOCK_SIZE;
+      Controller->MaxSectorsPerRequest[MinorNumber] =
+	Controller->MaxBlocksPerCommand;
+      Controller->MaxSegmentsPerRequest[MinorNumber] =
+	Controller->MaxScatterGatherSegments;
+    }
+  Controller->GenericDiskInfo.part = Controller->DiskPartitions;
+  Controller->GenericDiskInfo.sizes = Controller->PartitionSizes;
+  blksize_size[MajorNumber] = Controller->BlockSizes;
+  max_sectors[MajorNumber] = Controller->MaxSectorsPerRequest;
+  max_segments[MajorNumber] = Controller->MaxSegmentsPerRequest;
+  /*
+    Initialize Read Ahead to 128 sectors.
+  */
+  read_ahead[MajorNumber] = 128;
+  /*
+    Complete initialization of the Generic Disk Information structure.
+  */
+  Controller->GenericDiskInfo.major = MajorNumber;
+  Controller->GenericDiskInfo.major_name = "rd";
+  Controller->GenericDiskInfo.minor_shift = DAC960_MaxPartitionsBits;
+  Controller->GenericDiskInfo.max_p = DAC960_MaxPartitions;
+  Controller->GenericDiskInfo.max_nr = DAC960_MaxLogicalDrives;
+  Controller->GenericDiskInfo.init = DAC960_InitializeGenericDiskInfo;
+  Controller->GenericDiskInfo.nr_real = Controller->LogicalDriveCount;
+  Controller->GenericDiskInfo.real_devices = Controller;
+  Controller->GenericDiskInfo.next = NULL;
+  /*
+    Install the Generic Disk Information structure at the end of the list.
+  */
+  if ((GenericDiskInfo = gendisk_head) != NULL)
+    {
+      while (GenericDiskInfo->next != NULL)
+	GenericDiskInfo = GenericDiskInfo->next;
+      GenericDiskInfo->next = &Controller->GenericDiskInfo;
+    }
+  else gendisk_head = &Controller->GenericDiskInfo;
+  /*
+    Indicate the Block Device Registration completed successfully,
+  */
+  return true;
+}
+
+
+/*
+  DAC960_UnregisterBlockDevice unregisters the Block Device structures
+  associated with Controller.
+*/
+
+static void DAC960_UnregisterBlockDevice(DAC960_Controller_T *Controller)
+{
+  int MajorNumber = DAC960_MAJOR + Controller->ControllerNumber;
+  /*
+    Unregister the Block Device Major Number for this DAC960 Controller.
+  */
+  unregister_blkdev(MajorNumber, "rd");
+  /*
+    Remove the I/O Request Function.
+  */
+  blk_dev[MajorNumber].request_fn = NULL;
+  /*
+    Remove the Disk Partitions array, Partition Sizes array, Block Sizes
+    array, Max Sectors per Request array, and Max Segments per Request array.
+  */
+  Controller->GenericDiskInfo.part = NULL;
+  Controller->GenericDiskInfo.sizes = NULL;
+  blk_size[MajorNumber] = NULL;
+  blksize_size[MajorNumber] = NULL;
+  max_sectors[MajorNumber] = NULL;
+  max_segments[MajorNumber] = NULL;
+  /*
+    Remove the Generic Disk Information structure from the list.
+  */
+  if (gendisk_head != &Controller->GenericDiskInfo)
+    {
+      GenericDiskInfo_T *GenericDiskInfo = gendisk_head;
+      while (GenericDiskInfo != NULL &&
+	     GenericDiskInfo->next != &Controller->GenericDiskInfo)
+	GenericDiskInfo = GenericDiskInfo->next;
+      if (GenericDiskInfo != NULL)
+	GenericDiskInfo->next = GenericDiskInfo->next->next;
+    }
+  else gendisk_head = Controller->GenericDiskInfo.next;
+}
+
+
+/*
+  DAC960_InitializeController initializes Controller.
+*/
+
+static void DAC960_InitializeController(DAC960_Controller_T *Controller)
+{
+  if (DAC960_ReadControllerConfiguration(Controller) &&
+      DAC960_ReportControllerConfiguration(Controller) &&
+      DAC960_RegisterBlockDevice(Controller))
+    {
+      /*
+	Initialize the Command structures.
+      */
+      DAC960_Command_T *Commands = Controller->Commands;
+      int CommandIdentifier;
+      Controller->FreeCommands = NULL;
+      for (CommandIdentifier = 0;
+	   CommandIdentifier < Controller->DriverQueueDepth;
+	   CommandIdentifier++)
+	{
+	  Commands[CommandIdentifier].Controller = Controller;
+	  Commands[CommandIdentifier].Next = Controller->FreeCommands;
+	  Controller->FreeCommands = &Commands[CommandIdentifier];
+	}
+      /*
+	Initialize the Monitoring Timer.
+      */
+      init_timer(&Controller->MonitoringTimer);
+      Controller->MonitoringTimer.expires =
+	jiffies + DAC960_MonitoringTimerInterval;
+      Controller->MonitoringTimer.data = (unsigned long) Controller;
+      Controller->MonitoringTimer.function = DAC960_MonitoringTimerFunction;
+      add_timer(&Controller->MonitoringTimer);
+      Controller->ControllerInitialized = true;
+    }
+  else DAC960_FinalizeController(Controller);
+}
+
+
+/*
+  DAC960_FinalizeController finalizes Controller.
+*/
+
+static void DAC960_FinalizeController(DAC960_Controller_T *Controller)
+{
+  if (Controller->ControllerInitialized)
+    {
+      del_timer(&Controller->MonitoringTimer);
+      DAC960_Notice("Flushing Cache...", Controller);
+      DAC960_ExecuteType3(Controller, DAC960_Flush, NULL);
+      DAC960_Notice("done\n", Controller);
+      switch (Controller->ControllerType)
+	{
+	case DAC960_V5_Controller:
+	  if (!Controller->DualModeMemoryMailboxInterface)
+	    DAC960_V5_SaveMemoryMailboxInfo(Controller);
+	  break;
+	case DAC960_V4_Controller:
+	  if (!Controller->DualModeMemoryMailboxInterface)
+	    DAC960_V4_SaveMemoryMailboxInfo(Controller);
+	  break;
+	case DAC960_V3_Controller:
+	  break;
+	}
+    }
+  free_irq(Controller->IRQ_Channel, Controller);
+  iounmap(Controller->MemoryMappedAddress);
+  if (Controller->IO_Address > 0)
+    release_region(Controller->IO_Address, 0x80);
+  DAC960_UnregisterBlockDevice(Controller);
+  DAC960_Controllers[Controller->ControllerNumber] = NULL;
+  kfree(Controller);
+}
+
+
+/*
+  DAC960_Initialize initializes the DAC960 Driver.
+*/
+
+void DAC960_Initialize(void)
+{
+  int ControllerNumber;
+  DAC960_DetectControllers(DAC960_V5_Controller);
+  DAC960_DetectControllers(DAC960_V4_Controller);
+  DAC960_DetectControllers(DAC960_V3_Controller);
+  if (DAC960_ActiveControllerCount == 0) return;
+  for (ControllerNumber = 0;
+       ControllerNumber < DAC960_ControllerCount;
+       ControllerNumber++)
+    if (DAC960_Controllers[ControllerNumber] != NULL)
+      DAC960_InitializeController(DAC960_Controllers[ControllerNumber]);
+  DAC960_CreateProcEntries();
+}
+
+
+/*
+  DAC960_Finalize finalizes the DAC960 Driver.
+*/
+
+void DAC960_Finalize(void)
+{
+  int ControllerNumber;
+  if (DAC960_ActiveControllerCount == 0) return;
+  for (ControllerNumber = 0;
+       ControllerNumber < DAC960_ControllerCount;
+       ControllerNumber++)
+    if (DAC960_Controllers[ControllerNumber] != NULL)
+      DAC960_FinalizeController(DAC960_Controllers[ControllerNumber]);
+  DAC960_DestroyProcEntries();
+}
+
+
+/*
+  DAC960_ProcessRequest attempts to remove one I/O Request from Controller's
+  I/O Request Queue and queues it to the Controller.  WaitForCommand is true if
+  this function should wait for a Command to become available if necessary.
+  This function returns true if an I/O Request was queued and false otherwise.
+*/
+
+static boolean DAC960_ProcessRequest(DAC960_Controller_T *Controller,
+				     boolean WaitForCommand)
+{
+  IO_Request_T **RequestQueuePointer =
+    &blk_dev[DAC960_MAJOR + Controller->ControllerNumber].current_request;
+  IO_Request_T *Request;
+  DAC960_Command_T *Command;
+  char *RequestBuffer;
+  while (true)
+    {
+      Request = *RequestQueuePointer;
+      if (Request == NULL || Request->rq_status == RQ_INACTIVE) return false;
+      Command = DAC960_AllocateCommand(Controller);
+      if (Command != NULL) break;
+      if (!WaitForCommand) return false;
+      sleep_on(&Controller->CommandWaitQueue);
+    }
+  DAC960_ClearCommand(Command);
+  if (Request->cmd == READ)
+    Command->CommandType = DAC960_ReadCommand;
+  else Command->CommandType = DAC960_WriteCommand;
+  Command->Semaphore = Request->sem;
+  Command->LogicalDriveNumber = DAC960_LogicalDriveNumber(Request->rq_dev);
+  Command->BlockNumber =
+    Request->sector
+    + Controller->GenericDiskInfo.part[MINOR(Request->rq_dev)].start_sect;
+  Command->BlockCount = Request->nr_sectors;
+  Command->SegmentCount = Request->nr_segments;
+  Command->BufferHeader = Request->bh;
+  RequestBuffer = Request->buffer;
+  Request->rq_status = RQ_INACTIVE;
+  *RequestQueuePointer = Request->next;
+  wake_up(&wait_for_request);
+  if (Command->SegmentCount == 1)
+    {
+      DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+      if (Command->CommandType == DAC960_ReadCommand)
+	CommandMailbox->Type5.CommandOpcode = DAC960_Read;
+      else CommandMailbox->Type5.CommandOpcode = DAC960_Write;
+      CommandMailbox->Type5.LD.TransferLength = Command->BlockCount;
+      CommandMailbox->Type5.LD.LogicalDriveNumber = Command->LogicalDriveNumber;
+      CommandMailbox->Type5.LogicalBlockAddress = Command->BlockNumber;
+      CommandMailbox->Type5.BusAddress = Virtual_to_Bus(RequestBuffer);
+    }
+  else
+    {
+      DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+      DAC960_ScatterGatherSegment_T
+	*ScatterGatherList = Command->ScatterGatherList;
+      BufferHeader_T *BufferHeader = Command->BufferHeader;
+      char *LastDataEndPointer = NULL;
+      int SegmentNumber = 0;
+      if (Command->CommandType == DAC960_ReadCommand)
+	CommandMailbox->Type5.CommandOpcode = DAC960_ReadWithOldScatterGather;
+      else
+	CommandMailbox->Type5.CommandOpcode = DAC960_WriteWithOldScatterGather;
+      CommandMailbox->Type5.LD.TransferLength = Command->BlockCount;
+      CommandMailbox->Type5.LD.LogicalDriveNumber = Command->LogicalDriveNumber;
+      CommandMailbox->Type5.LogicalBlockAddress = Command->BlockNumber;
+      CommandMailbox->Type5.BusAddress = Virtual_to_Bus(ScatterGatherList);
+      CommandMailbox->Type5.ScatterGatherCount = Command->SegmentCount;
+      while (BufferHeader != NULL)
+	{
+	  if (BufferHeader->b_data == LastDataEndPointer)
+	    {
+	      ScatterGatherList[SegmentNumber-1].SegmentByteCount +=
+		BufferHeader->b_size;
+	      LastDataEndPointer += BufferHeader->b_size;
+	    }
+	  else
+	    {
+	      ScatterGatherList[SegmentNumber].SegmentDataPointer =
+		Virtual_to_Bus(BufferHeader->b_data);
+	      ScatterGatherList[SegmentNumber].SegmentByteCount =
+		BufferHeader->b_size;
+	      LastDataEndPointer = BufferHeader->b_data + BufferHeader->b_size;
+	      if (SegmentNumber++ > Controller->MaxScatterGatherSegments)
+		panic("DAC960: Scatter/Gather Segment Overflow\n");
+	    }
+	  BufferHeader = BufferHeader->b_reqnext;
+	}
+      if (SegmentNumber != Command->SegmentCount)
+	panic("DAC960: SegmentNumber != SegmentCount\n");
+    }
+  DAC960_QueueCommand(Command);
+  return true;
+}
+
+
+/*
+  DAC960_ProcessRequests attempts to remove as many I/O Requests as possible
+  from Controller's I/O Request Queue and queue them to the Controller.
+*/
+
+static inline void DAC960_ProcessRequests(DAC960_Controller_T *Controller)
+{
+  int Counter = 0;
+  while (DAC960_ProcessRequest(Controller, Counter++ == 0)) ;
+}
+
+
+/*
+  DAC960_RequestFunction0 is the I/O Request Function for DAC960 Controller 0.
+*/
+
+static void DAC960_RequestFunction0(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[0];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction1 is the I/O Request Function for DAC960 Controller 1.
+*/
+
+static void DAC960_RequestFunction1(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[1];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction2 is the I/O Request Function for DAC960 Controller 2.
+*/
+
+static void DAC960_RequestFunction2(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[2];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction3 is the I/O Request Function for DAC960 Controller 3.
+*/
+
+static void DAC960_RequestFunction3(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[3];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction4 is the I/O Request Function for DAC960 Controller 4.
+*/
+
+static void DAC960_RequestFunction4(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[4];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction5 is the I/O Request Function for DAC960 Controller 5.
+*/
+
+static void DAC960_RequestFunction5(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[5];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction6 is the I/O Request Function for DAC960 Controller 6.
+*/
+
+static void DAC960_RequestFunction6(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[6];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction7 is the I/O Request Function for DAC960 Controller 7.
+*/
+
+static void DAC960_RequestFunction7(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[7];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_ReadWriteError prints an appropriate error message for Command when
+  an error occurs on a Read or Write operation.
+*/
+
+static void DAC960_ReadWriteError(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  char *CommandName = "UNKNOWN";
+  switch (Command->CommandType)
+    {
+    case DAC960_ReadCommand:
+    case DAC960_ReadRetryCommand:
+      CommandName = "READ";
+      break;
+    case DAC960_WriteCommand:
+    case DAC960_WriteRetryCommand:
+      CommandName = "WRITE";
+      break;
+    case DAC960_MonitoringCommand:
+    case DAC960_ImmediateCommand:
+      break;
+    }
+  switch (Command->CommandStatus)
+    {
+    case DAC960_IrrecoverableDataError:
+      DAC960_Error("Irrecoverable Data Error on %s:\n",
+		   Controller, CommandName);
+      break;
+    case DAC960_LogicalDriveNonexistentOrOffline:
+      DAC960_Error("Logical Drive Nonexistent or Offline on %s:\n",
+		   Controller, CommandName);
+      break;
+    case DAC960_AccessBeyondEndOfLogicalDrive:
+      DAC960_Error("Attempt to Access Beyond End of Logical Drive "
+		   "on %s:\n", Controller, CommandName);
+      break;
+    case DAC960_BadDataEncountered:
+      DAC960_Error("Bad Data Encountered on %s:\n", Controller, CommandName);
+      break;
+    default:
+      DAC960_Error("Unexpected Error Status %04X on %s:\n",
+		   Controller, Command->CommandStatus, CommandName);
+      break;
+    }
+  DAC960_Error("  /dev/rd/c%dd%d:   absolute blocks %d..%d\n",
+	       Controller, Controller->ControllerNumber,
+	       Command->LogicalDriveNumber, Command->BlockNumber,
+	       Command->BlockNumber + Command->BlockCount - 1);
+  if (DAC960_PartitionNumber(Command->BufferHeader->b_rdev) > 0)
+    DAC960_Error("  /dev/rd/c%dd%dp%d: relative blocks %d..%d\n",
+		 Controller, Controller->ControllerNumber,
+		 Command->LogicalDriveNumber,
+		 DAC960_PartitionNumber(Command->BufferHeader->b_rdev),
+		 Command->BufferHeader->b_rsector,
+		 Command->BufferHeader->b_rsector + Command->BlockCount - 1);
+}
+
+
+/*
+  DAC960_ProcessCompletedBuffer performs completion processing for an
+  individual Buffer.
+*/
+
+static inline void DAC960_ProcessCompletedBuffer(BufferHeader_T *BufferHeader,
+						 boolean SuccessfulIO)
+{
+  mark_buffer_uptodate(BufferHeader, SuccessfulIO);
+  unlock_buffer(BufferHeader);
+}
+
+
+/*
+  DAC960_ProcessCompletedCommand performs completion processing for Command.
+*/
+
+static void DAC960_ProcessCompletedCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  DAC960_CommandType_T CommandType = Command->CommandType;
+  DAC960_CommandStatus_T CommandStatus = Command->CommandStatus;
+  BufferHeader_T *BufferHeader = Command->BufferHeader;
+  if (CommandType == DAC960_ReadCommand ||
+      CommandType == DAC960_WriteCommand)
+    {
+      if (CommandStatus == DAC960_NormalCompletion)
+	{
+	  /*
+	    Perform completion processing for all buffers in this I/O Request.
+	  */
+	  while (BufferHeader != NULL)
+	    {
+	      BufferHeader_T *NextBufferHeader = BufferHeader->b_reqnext;
+	      BufferHeader->b_reqnext = NULL;
+	      DAC960_ProcessCompletedBuffer(BufferHeader, true);
+	      BufferHeader = NextBufferHeader;
+	    }
+	  /*
+	    Wake up requestor for swap file paging requests.
+	  */
+	  if (Command->Semaphore != NULL)
+	    {
+	      up(Command->Semaphore);
+	      Command->Semaphore = NULL;
+	    }
+	  add_blkdev_randomness(DAC960_MAJOR + Controller->ControllerNumber);
+	}
+      else if ((CommandStatus == DAC960_IrrecoverableDataError ||
+		CommandStatus == DAC960_BadDataEncountered) &&
+	       BufferHeader != NULL &&
+	       BufferHeader->b_reqnext != NULL)
+	{
+	  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+	  if (CommandType == DAC960_ReadCommand)
+	    {
+	      Command->CommandType = DAC960_ReadRetryCommand;
+	      CommandMailbox->Type5.CommandOpcode = DAC960_Read;
+	    }
+	  else
+	    {
+	      Command->CommandType = DAC960_WriteRetryCommand;
+	      CommandMailbox->Type5.CommandOpcode = DAC960_Write;
+	    }
+	  Command->BlockCount = BufferHeader->b_size >> DAC960_BlockSizeBits;
+	  CommandMailbox->Type5.LD.TransferLength = Command->BlockCount;
+	  CommandMailbox->Type5.BusAddress =
+	    Virtual_to_Bus(BufferHeader->b_data);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      else
+	{
+	  if (CommandStatus != DAC960_LogicalDriveNonexistentOrOffline)
+	    DAC960_ReadWriteError(Command);
+	  /*
+	    Perform completion processing for all buffers in this I/O Request.
+	  */
+	  while (BufferHeader != NULL)
+	    {
+	      BufferHeader_T *NextBufferHeader = BufferHeader->b_reqnext;
+	      BufferHeader->b_reqnext = NULL;
+	      DAC960_ProcessCompletedBuffer(BufferHeader, false);
+	      BufferHeader = NextBufferHeader;
+	    }
+	  /*
+	    Wake up requestor for swap file paging requests.
+	  */
+	  if (Command->Semaphore != NULL)
+	    {
+	      up(Command->Semaphore);
+	      Command->Semaphore = NULL;
+	    }
+	}
+    }
+  else if (CommandType == DAC960_ReadRetryCommand ||
+	   CommandType == DAC960_WriteRetryCommand)
+    {
+      BufferHeader_T *NextBufferHeader = BufferHeader->b_reqnext;
+      BufferHeader->b_reqnext = NULL;
+      /*
+	Perform completion processing for this single buffer.
+      */
+      if (CommandStatus == DAC960_NormalCompletion)
+	DAC960_ProcessCompletedBuffer(BufferHeader, true);
+      else
+	{
+	  if (CommandStatus != DAC960_LogicalDriveNonexistentOrOffline)
+	    DAC960_ReadWriteError(Command);
+	  DAC960_ProcessCompletedBuffer(BufferHeader, false);
+	}
+      if (NextBufferHeader != NULL)
+	{
+	  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+	  Command->BlockNumber +=
+	    BufferHeader->b_size >> DAC960_BlockSizeBits;
+	  Command->BlockCount =
+	    NextBufferHeader->b_size >> DAC960_BlockSizeBits;
+	  Command->BufferHeader = NextBufferHeader;
+	  CommandMailbox->Type5.LD.TransferLength = Command->BlockCount;
+	  CommandMailbox->Type5.LogicalBlockAddress = Command->BlockNumber;
+	  CommandMailbox->Type5.BusAddress =
+	    Virtual_to_Bus(NextBufferHeader->b_data);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+    }
+  else if (CommandType == DAC960_MonitoringCommand)
+    {
+      DAC960_CommandOpcode_T CommandOpcode =
+	Command->CommandMailbox.Common.CommandOpcode;
+      unsigned int OldCriticalLogicalDriveCount = 0;
+      unsigned int NewCriticalLogicalDriveCount = 0;
+      if (CommandOpcode == DAC960_Enquiry)
+	{
+	  DAC960_Enquiry_T *OldEnquiry =
+	    &Controller->Enquiry[Controller->EnquiryIndex];
+	  DAC960_Enquiry_T *NewEnquiry =
+	    &Controller->Enquiry[Controller->EnquiryIndex ^= 1];
+	  OldCriticalLogicalDriveCount = OldEnquiry->CriticalLogicalDriveCount;
+	  NewCriticalLogicalDriveCount = NewEnquiry->CriticalLogicalDriveCount;
+	  if (NewEnquiry->StatusFlags.DeferredWriteError !=
+	      OldEnquiry->StatusFlags.DeferredWriteError)
+	    DAC960_Critical("Deferred Write Error Flag is now %s\n", Controller,
+			    (NewEnquiry->StatusFlags.DeferredWriteError
+			     ? "TRUE" : "FALSE"));
+	  if ((NewCriticalLogicalDriveCount > 0 ||
+	       NewCriticalLogicalDriveCount != OldCriticalLogicalDriveCount) ||
+	      (NewEnquiry->OfflineLogicalDriveCount > 0 ||
+	       NewEnquiry->OfflineLogicalDriveCount !=
+	       OldEnquiry->OfflineLogicalDriveCount) ||
+	      (NewEnquiry->DeadDriveCount > 0 ||
+	       NewEnquiry->DeadDriveCount !=
+	       OldEnquiry->DeadDriveCount) ||
+	      (NewEnquiry->EventLogSequenceNumber !=
+	       OldEnquiry->EventLogSequenceNumber) ||
+	      Controller->MonitoringTimerCount == 0 ||
+	      (jiffies - Controller->SecondaryMonitoringTime
+	       >= DAC960_SecondaryMonitoringInterval))
+	    {
+	      Controller->NeedLogicalDriveInformation = true;
+	      Controller->NewEventLogSequenceNumber =
+		NewEnquiry->EventLogSequenceNumber;
+	      Controller->NeedErrorTableInformation = true;
+	      Controller->NeedDeviceStateInformation = true;
+	      Controller->DeviceStateChannel = 0;
+	      Controller->DeviceStateTargetID = 0;
+	      Controller->SecondaryMonitoringTime = jiffies;
+	    }
+	  if (NewEnquiry->RebuildFlag == DAC960_StandbyRebuildInProgress ||
+	      NewEnquiry->RebuildFlag == DAC960_BackgroundRebuildInProgress ||
+	      OldEnquiry->RebuildFlag == DAC960_StandbyRebuildInProgress ||
+	      OldEnquiry->RebuildFlag == DAC960_BackgroundRebuildInProgress)
+	    Controller->NeedRebuildProgress = true;
+	  if (OldEnquiry->RebuildFlag == DAC960_BackgroundCheckInProgress)
+	    switch (NewEnquiry->RebuildFlag)
+	      {
+	      case DAC960_NoStandbyRebuildOrCheckInProgress:
+		DAC960_Progress("Consistency Check Completed Successfully\n",
+				Controller);
+		break;
+	      case DAC960_StandbyRebuildInProgress:
+	      case DAC960_BackgroundRebuildInProgress:
+		break;
+	      case DAC960_BackgroundCheckInProgress:
+		Controller->NeedConsistencyCheckProgress = true;
+		break;
+	      case DAC960_StandbyRebuildCompletedWithError:
+		DAC960_Progress("Consistency Check Completed with Error\n",
+				Controller);
+		break;
+	      case DAC960_BackgroundRebuildOrCheckFailed_DriveFailed:
+		DAC960_Progress("Consistency Check Failed - "
+				"Physical Drive Failed\n", Controller);
+		break;
+	      case DAC960_BackgroundRebuildOrCheckFailed_LogicalDriveFailed:
+		DAC960_Progress("Consistency Check Failed - "
+				"Logical Drive Failed\n", Controller);
+		break;
+	      case DAC960_BackgroundRebuildOrCheckFailed_OtherCauses:
+		DAC960_Progress("Consistency Check Failed - Other Causes\n",
+				Controller);
+		break;
+	      case DAC960_BackgroundRebuildOrCheckSuccessfullyTerminated:
+		DAC960_Progress("Consistency Check Successfully Terminated\n",
+				Controller);
+		break;
+	      }
+	  else if (NewEnquiry->RebuildFlag == DAC960_BackgroundCheckInProgress)
+	    Controller->NeedConsistencyCheckProgress = true;
+	}
+      else if (CommandOpcode == DAC960_GetLogicalDriveInformation)
+	{
+	  int LogicalDriveNumber;
+	  for (LogicalDriveNumber = 0;
+	       LogicalDriveNumber < Controller->LogicalDriveCount;
+	       LogicalDriveNumber++)
+	    {
+	      DAC960_LogicalDriveInformation_T *OldLogicalDriveInformation =
+		&Controller->LogicalDriveInformation
+			     [Controller->LogicalDriveInformationIndex]
+			     [LogicalDriveNumber];
+	      DAC960_LogicalDriveInformation_T *NewLogicalDriveInformation =
+		&Controller->LogicalDriveInformation
+			     [Controller->LogicalDriveInformationIndex ^ 1]
+			     [LogicalDriveNumber];
+	      if (NewLogicalDriveInformation->LogicalDriveState !=
+		  OldLogicalDriveInformation->LogicalDriveState)
+		DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) "
+				"is now %s\n", Controller,
+				LogicalDriveNumber,
+				Controller->ControllerNumber,
+				LogicalDriveNumber,
+				(NewLogicalDriveInformation->LogicalDriveState
+				 == DAC960_LogicalDrive_Online
+				 ? "ONLINE"
+				 : NewLogicalDriveInformation->LogicalDriveState
+				 == DAC960_LogicalDrive_Critical
+				 ? "CRITICAL" : "OFFLINE"));
+	      if (NewLogicalDriveInformation->WriteBack !=
+		  OldLogicalDriveInformation->WriteBack)
+		DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) "
+				"is now %s\n", Controller,
+				LogicalDriveNumber,
+				Controller->ControllerNumber,
+				LogicalDriveNumber,
+				(NewLogicalDriveInformation->WriteBack
+				 ? "WRITE BACK" : "WRITE THRU"));
+	    }
+	  Controller->LogicalDriveInformationIndex ^= 1;
+	}
+      else if (CommandOpcode == DAC960_PerformEventLogOperation)
+	{
+	  DAC960_EventLogEntry_T *EventLogEntry = &Controller->EventLogEntry;
+	  if (EventLogEntry->SequenceNumber ==
+	      Controller->OldEventLogSequenceNumber)
+	    {
+	      unsigned char SenseKey = EventLogEntry->SenseKey;
+	      unsigned char AdditionalSenseCode =
+		EventLogEntry->AdditionalSenseCode;
+	      unsigned char AdditionalSenseCodeQualifier =
+		EventLogEntry->AdditionalSenseCodeQualifier;
+	      if (SenseKey == 9 &&
+		  AdditionalSenseCode == 0x80 &&
+		  AdditionalSenseCodeQualifier < DAC960_EventMessagesCount)
+		DAC960_Critical("Physical Drive %d:%d %s\n", Controller,
+				EventLogEntry->Channel,
+				EventLogEntry->TargetID,
+				DAC960_EventMessages[
+				  AdditionalSenseCodeQualifier]);
+	      else if (!((SenseKey == 2 &&
+			  AdditionalSenseCode == 0x04 &&
+			  (AdditionalSenseCodeQualifier == 0x01 ||
+			   AdditionalSenseCodeQualifier == 0x02)) ||
+			 (SenseKey == 6 && AdditionalSenseCode == 0x29 &&
+			  Controller->MonitoringTimerCount == 0)))
+		DAC960_Critical("Physical Drive %d:%d Error Log: "
+				"Sense Key = %d, ASC = %02X, ASCQ = %02X\n",
+				Controller,
+				EventLogEntry->Channel,
+				EventLogEntry->TargetID,
+				SenseKey,
+				AdditionalSenseCode,
+				AdditionalSenseCodeQualifier);
+	    }
+	  Controller->OldEventLogSequenceNumber++;
+	}
+      else if (CommandOpcode == DAC960_GetErrorTable)
+	{
+	  DAC960_ErrorTable_T *OldErrorTable =
+	    &Controller->ErrorTable[Controller->ErrorTableIndex];
+	  DAC960_ErrorTable_T *NewErrorTable =
+	    &Controller->ErrorTable[Controller->ErrorTableIndex ^= 1];
+	  int Channel, TargetID;
+	  for (Channel = 0; Channel < Controller->Channels; Channel++)
+	    for (TargetID = 0; TargetID < DAC960_MaxTargets; TargetID++)
+	      {
+		DAC960_ErrorTableEntry_T *NewErrorEntry =
+		  &NewErrorTable->ErrorTableEntries[Channel][TargetID];
+		DAC960_ErrorTableEntry_T *OldErrorEntry =
+		  &OldErrorTable->ErrorTableEntries[Channel][TargetID];
+		if ((NewErrorEntry->ParityErrorCount !=
+		     OldErrorEntry->ParityErrorCount) ||
+		    (NewErrorEntry->SoftErrorCount !=
+		     OldErrorEntry->SoftErrorCount) ||
+		    (NewErrorEntry->HardErrorCount !=
+		     OldErrorEntry->HardErrorCount) ||
+		    (NewErrorEntry->MiscErrorCount !=
+		     OldErrorEntry->MiscErrorCount))
+		  DAC960_Critical("Physical Drive %d:%d Errors: "
+				  "Parity = %d, Soft = %d, "
+				  "Hard = %d, Misc = %d\n",
+				  Controller, Channel, TargetID,
+				  NewErrorEntry->ParityErrorCount,
+				  NewErrorEntry->SoftErrorCount,
+				  NewErrorEntry->HardErrorCount,
+				  NewErrorEntry->MiscErrorCount);
+	      }
+	}
+      else if (CommandOpcode == DAC960_GetDeviceState)
+	{
+	  DAC960_DeviceState_T *OldDeviceState =
+	    &Controller->DeviceState[Controller->DeviceStateIndex]
+				    [Controller->DeviceStateChannel]
+				    [Controller->DeviceStateTargetID];
+	  DAC960_DeviceState_T *NewDeviceState =
+	    &Controller->DeviceState[Controller->DeviceStateIndex ^ 1]
+				    [Controller->DeviceStateChannel]
+				    [Controller->DeviceStateTargetID];
+	  if (NewDeviceState->DeviceState != OldDeviceState->DeviceState)
+	    DAC960_Critical("Physical Drive %d:%d is now %s\n", Controller,
+			    Controller->DeviceStateChannel,
+			    Controller->DeviceStateTargetID,
+			    (NewDeviceState->DeviceState == DAC960_Device_Dead
+			     ? "DEAD"
+			     : NewDeviceState->DeviceState
+			       == DAC960_Device_WriteOnly
+			       ? "WRITE-ONLY"
+			       : NewDeviceState->DeviceState
+				 == DAC960_Device_Online
+				 ? "ONLINE" : "STANDBY"));
+	  if (++Controller->DeviceStateTargetID == DAC960_MaxTargets)
+	    {
+	      Controller->DeviceStateChannel++;
+	      Controller->DeviceStateTargetID = 0;
+	    }
+	}
+      else if (CommandOpcode == DAC960_GetRebuildProgress)
+	{
+	  unsigned int LogicalDriveNumber =
+	    Controller->RebuildProgress.LogicalDriveNumber;
+	  unsigned int LogicalDriveSize =
+	    Controller->RebuildProgress.LogicalDriveSize;
+	  unsigned int BlocksCompleted =
+	    LogicalDriveSize - Controller->RebuildProgress.RemainingBlocks;
+	  switch (CommandStatus)
+	    {
+	    case DAC960_NormalCompletion:
+	      Controller->EphemeralProgressMessage = true;
+	      DAC960_Progress("Rebuild in Progress: "
+			      "Logical Drive %d (/dev/rd/c%dd%d) "
+			      "%d%% completed\n",
+			      Controller, LogicalDriveNumber,
+			      Controller->ControllerNumber,
+			      LogicalDriveNumber,
+			      (100 * (BlocksCompleted >> 7))
+			      / (LogicalDriveSize >> 7));
+	      Controller->EphemeralProgressMessage = false;
+	      break;
+	    case DAC960_RebuildFailed_LogicalDriveFailure:
+	      DAC960_Progress("Rebuild Failed due to "
+			      "Logical Drive Failure\n", Controller);
+	      break;
+	    case DAC960_RebuildFailed_BadBlocksOnOther:
+	      DAC960_Progress("Rebuild Failed due to "
+			      "Bad Blocks on Other Drives\n", Controller);
+	      break;
+	    case DAC960_RebuildFailed_NewDriveFailed:
+	      DAC960_Progress("Rebuild Failed due to "
+			      "Failure of Drive Being Rebuilt\n", Controller);
+	      break;
+	    case DAC960_RebuildSuccessful:
+	    case DAC960_NoRebuildOrCheckInProgress:
+	      DAC960_Progress("Rebuild Completed Successfully\n", Controller);
+	      break;
+	    }
+	}
+      else if (CommandOpcode == DAC960_RebuildStat)
+	{
+	  unsigned int LogicalDriveNumber =
+	    Controller->RebuildProgress.LogicalDriveNumber;
+	  unsigned int LogicalDriveSize =
+	    Controller->RebuildProgress.LogicalDriveSize;
+	  unsigned int BlocksCompleted =
+	    LogicalDriveSize - Controller->RebuildProgress.RemainingBlocks;
+	  if (CommandStatus == DAC960_NormalCompletion)
+	    {
+	      Controller->EphemeralProgressMessage = true;
+	      DAC960_Progress("Consistency Check in Progress: "
+			      "Logical Drive %d (/dev/rd/c%dd%d) "
+			      "%d%% completed\n",
+			      Controller, LogicalDriveNumber,
+			      Controller->ControllerNumber,
+			      LogicalDriveNumber,
+			      (100 * (BlocksCompleted >> 7))
+			      / (LogicalDriveSize >> 7));
+	      Controller->EphemeralProgressMessage = false;
+	    }
+	}
+      if (Controller->NeedLogicalDriveInformation &&
+	  NewCriticalLogicalDriveCount >= OldCriticalLogicalDriveCount)
+	{
+	  Controller->NeedLogicalDriveInformation = false;
+	  Command->CommandMailbox.Type3.CommandOpcode =
+	    DAC960_GetLogicalDriveInformation;
+	  Command->CommandMailbox.Type3.BusAddress =
+	    Virtual_to_Bus(
+	      &Controller->LogicalDriveInformation
+			   [Controller->LogicalDriveInformationIndex ^ 1]);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      if (Controller->NewEventLogSequenceNumber
+	  - Controller->OldEventLogSequenceNumber > 0)
+	{
+	  Command->CommandMailbox.Type3E.CommandOpcode =
+	    DAC960_PerformEventLogOperation;
+	  Command->CommandMailbox.Type3E.OperationType =
+	    DAC960_GetEventLogEntry;
+	  Command->CommandMailbox.Type3E.OperationQualifier = 1;
+	  Command->CommandMailbox.Type3E.SequenceNumber =
+	    Controller->OldEventLogSequenceNumber;
+	  Command->CommandMailbox.Type3E.BusAddress =
+	    Virtual_to_Bus(&Controller->EventLogEntry);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      if (Controller->NeedErrorTableInformation)
+	{
+	  Controller->NeedErrorTableInformation = false;
+	  Command->CommandMailbox.Type3.CommandOpcode = DAC960_GetErrorTable;
+	  Command->CommandMailbox.Type3.BusAddress =
+	    Virtual_to_Bus(
+	      &Controller->ErrorTable[Controller->ErrorTableIndex ^ 1]);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      if (Controller->NeedDeviceStateInformation)
+	{
+	  while (Controller->DeviceStateChannel < Controller->Channels)
+	    {
+	      DAC960_DeviceState_T *OldDeviceState =
+		&Controller->DeviceState[Controller->DeviceStateIndex]
+					[Controller->DeviceStateChannel]
+					[Controller->DeviceStateTargetID];
+	      if (OldDeviceState->Present &&
+		  OldDeviceState->DeviceType == DAC960_DiskType)
+		{
+		  Command->CommandMailbox.Type3D.CommandOpcode =
+		    DAC960_GetDeviceState;
+		  Command->CommandMailbox.Type3D.Channel =
+		    Controller->DeviceStateChannel;
+		  Command->CommandMailbox.Type3D.TargetID =
+		    Controller->DeviceStateTargetID;
+		  Command->CommandMailbox.Type3D.BusAddress =
+		    Virtual_to_Bus(&Controller->DeviceState
+				      [Controller->DeviceStateIndex ^ 1]
+				      [Controller->DeviceStateChannel]
+				      [Controller->DeviceStateTargetID]);
+		  DAC960_QueueCommand(Command);
+		  return;
+		}
+	      if (++Controller->DeviceStateTargetID == DAC960_MaxTargets)
+		{
+		  Controller->DeviceStateChannel++;
+		  Controller->DeviceStateTargetID = 0;
+		}
+	    }
+	  Controller->NeedDeviceStateInformation = false;
+	  Controller->DeviceStateIndex ^= 1;
+	}
+      if (Controller->NeedRebuildProgress)
+	{
+	  Controller->NeedRebuildProgress = false;
+	  Command->CommandMailbox.Type3.CommandOpcode =
+	    DAC960_GetRebuildProgress;
+	  Command->CommandMailbox.Type3.BusAddress =
+	    Virtual_to_Bus(&Controller->RebuildProgress);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      if (Controller->NeedConsistencyCheckProgress)
+	{
+	  Controller->NeedConsistencyCheckProgress = false;
+	  Command->CommandMailbox.Type3.CommandOpcode = DAC960_RebuildStat;
+	  Command->CommandMailbox.Type3.BusAddress =
+	    Virtual_to_Bus(&Controller->RebuildProgress);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      if (Controller->NeedLogicalDriveInformation &&
+	  NewCriticalLogicalDriveCount < OldCriticalLogicalDriveCount)
+	{
+	  Controller->NeedLogicalDriveInformation = false;
+	  Command->CommandMailbox.Type3.CommandOpcode =
+	    DAC960_GetLogicalDriveInformation;
+	  Command->CommandMailbox.Type3.BusAddress =
+	    Virtual_to_Bus(
+	      &Controller->LogicalDriveInformation
+			   [Controller->LogicalDriveInformationIndex ^ 1]);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      Controller->MonitoringTimerCount++;
+      Controller->MonitoringTimer.expires =
+	jiffies + DAC960_MonitoringTimerInterval;
+      add_timer(&Controller->MonitoringTimer);
+    }
+  else if (CommandType == DAC960_ImmediateCommand)
+    {
+      up(Command->Semaphore);
+      Command->Semaphore = NULL;
+      return;
+    }
+  else panic("DAC960: Unknown Command Type %d\n", CommandType);
+  /*
+    Queue a Status Monitoring Command to the Controller using the just
+    completed Command if one was deferred previously due to lack of a
+    free Command when the Monitoring Timer Function was called.
+  */
+  if (Controller->MonitoringCommandDeferred)
+    {
+      Controller->MonitoringCommandDeferred = false;
+      DAC960_QueueMonitoringCommand(Command);
+      return;
+    }
+  /*
+    Execute a User Command using the just completed Command if one was
+    deferred previously due to lack of a free Command.
+  */
+  if (Controller->UserCommandDeferred)
+    {
+      Controller->UserCommandDeferred = false;
+      Controller->UserCommand = Command;
+      up(Controller->UserCommandSemaphore);
+      return;
+    }
+  /*
+    Deallocate the Command, and wake up any processes waiting on a free Command.
+  */
+  DAC960_DeallocateCommand(Command);
+  wake_up(&Controller->CommandWaitQueue);
+}
+
+
+/*
+  DAC960_InterruptHandler handles hardware interrupts from DAC960 Controllers.
+*/
+
+static void DAC960_InterruptHandler(int IRQ_Channel,
+				    void *DeviceIdentifier,
+				    Registers_T *InterruptRegisters)
+{
+  DAC960_Controller_T *Controller = (DAC960_Controller_T *) DeviceIdentifier;
+  void *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_StatusMailbox_T *NextStatusMailbox;
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockIH(Controller, &ProcessorFlags);
+  /*
+    Process Hardware Interrupts for Controller.
+  */
+  switch (Controller->ControllerType)
+    {
+    case DAC960_V5_Controller:
+      DAC960_V5_AcknowledgeInterrupt(ControllerBaseAddress);
+      NextStatusMailbox = Controller->NextStatusMailbox;
+      while (NextStatusMailbox->Fields.Valid)
+	{
+	  DAC960_CommandIdentifier_T CommandIdentifier =
+	    NextStatusMailbox->Fields.CommandIdentifier;
+	  DAC960_Command_T *Command = &Controller->Commands[CommandIdentifier];
+	  Command->CommandStatus = NextStatusMailbox->Fields.CommandStatus;
+	  NextStatusMailbox->Word = 0;
+	  if (++NextStatusMailbox > Controller->LastStatusMailbox)
+	    NextStatusMailbox = Controller->FirstStatusMailbox;
+	  DAC960_ProcessCompletedCommand(Command);
+	}
+      Controller->NextStatusMailbox = NextStatusMailbox;
+      break;
+    case DAC960_V4_Controller:
+      DAC960_V4_AcknowledgeInterrupt(ControllerBaseAddress);
+      NextStatusMailbox = Controller->NextStatusMailbox;
+      while (NextStatusMailbox->Fields.Valid)
+	{
+	  DAC960_CommandIdentifier_T CommandIdentifier =
+	    NextStatusMailbox->Fields.CommandIdentifier;
+	  DAC960_Command_T *Command = &Controller->Commands[CommandIdentifier];
+	  Command->CommandStatus = NextStatusMailbox->Fields.CommandStatus;
+	  NextStatusMailbox->Word = 0;
+	  if (++NextStatusMailbox > Controller->LastStatusMailbox)
+	    NextStatusMailbox = Controller->FirstStatusMailbox;
+	  DAC960_ProcessCompletedCommand(Command);
+	}
+      Controller->NextStatusMailbox = NextStatusMailbox;
+      break;
+    case DAC960_V3_Controller:
+      while (DAC960_V3_StatusAvailableP(ControllerBaseAddress))
+	{
+	  DAC960_CommandIdentifier_T CommandIdentifier =
+	    DAC960_V3_ReadStatusCommandIdentifier(ControllerBaseAddress);
+	  DAC960_Command_T *Command = &Controller->Commands[CommandIdentifier];
+	  Command->CommandStatus =
+	    DAC960_V3_ReadStatusRegister(ControllerBaseAddress);
+	  DAC960_V3_AcknowledgeInterrupt(ControllerBaseAddress);
+	  DAC960_V3_AcknowledgeStatus(ControllerBaseAddress);
+	  DAC960_ProcessCompletedCommand(Command);
+	}
+      break;
+    }
+  /*
+    Attempt to remove additional I/O Requests from the Controller's
+    I/O Request Queue and queue them to the Controller.
+  */
+  while (DAC960_ProcessRequest(Controller, false)) ;
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockIH(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_QueueMonitoringCommand queues a Monitoring Command to Controller.
+*/
+
+static void DAC960_QueueMonitoringCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+  DAC960_ClearCommand(Command);
+  Command->CommandType = DAC960_MonitoringCommand;
+  CommandMailbox->Type3.CommandOpcode = DAC960_Enquiry;
+  CommandMailbox->Type3.BusAddress =
+    Virtual_to_Bus(&Controller->Enquiry[Controller->EnquiryIndex ^ 1]);
+  DAC960_QueueCommand(Command);
+}
+
+
+/*
+  DAC960_MonitoringTimerFunction is the timer function for monitoring
+  the status of DAC960 Controllers.
+*/
+
+static void DAC960_MonitoringTimerFunction(unsigned long TimerData)
+{
+  DAC960_Controller_T *Controller = (DAC960_Controller_T *) TimerData;
+  DAC960_Command_T *Command;
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
+  /*
+    Queue a Status Monitoring Command to Controller.
+  */
+  Command = DAC960_AllocateCommand(Controller);
+  if (Command != NULL)
+    DAC960_QueueMonitoringCommand(Command);
+  else Controller->MonitoringCommandDeferred = true;
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_Open is the Device Open Function for the DAC960 Driver.
+*/
+
+static int DAC960_Open(Inode_T *Inode, File_T *File)
+{
+  int ControllerNumber = DAC960_ControllerNumber(Inode->i_rdev);
+  int LogicalDriveNumber = DAC960_LogicalDriveNumber(Inode->i_rdev);
+  DAC960_Controller_T *Controller;
+  if (ControllerNumber < 0 || ControllerNumber > DAC960_ControllerCount - 1)
+    return -ENXIO;
+  Controller = DAC960_Controllers[ControllerNumber];
+  if (Controller == NULL ||
+      LogicalDriveNumber > Controller->LogicalDriveCount - 1)
+    return -ENXIO;
+  if (Controller->LogicalDriveInformation
+		  [Controller->LogicalDriveInformationIndex]
+		  [LogicalDriveNumber].LogicalDriveState
+      == DAC960_LogicalDrive_Offline)
+    return -ENXIO;
+  if (Controller->LogicalDriveInitialState[LogicalDriveNumber]
+      == DAC960_LogicalDrive_Offline)
+    {
+      Controller->LogicalDriveInitialState[LogicalDriveNumber] =
+	DAC960_LogicalDrive_Online;
+      DAC960_InitializeGenericDiskInfo(&Controller->GenericDiskInfo);
+      resetup_one_dev(&Controller->GenericDiskInfo, LogicalDriveNumber);
+    }
+  if (Controller->GenericDiskInfo.sizes[MINOR(Inode->i_rdev)] == 0)
+    return -ENXIO;
+  /*
+    Increment Controller and Logical Drive Usage Counts.
+  */
+  Controller->ControllerUsageCount++;
+  Controller->LogicalDriveUsageCount[LogicalDriveNumber]++;
+  return 0;
+}
+
+
+/*
+  DAC960_Release is the Device Release Function for the DAC960 Driver.
+*/
+
+static void DAC960_Release(Inode_T *Inode, File_T *File)
+{
+  int ControllerNumber = DAC960_ControllerNumber(Inode->i_rdev);
+  int LogicalDriveNumber = DAC960_LogicalDriveNumber(Inode->i_rdev);
+  DAC960_Controller_T *Controller = DAC960_Controllers[ControllerNumber];
+  /*
+    Force any buffered data to be written.
+  */
+  fsync_dev(Inode->i_rdev);
+  /*
+    Decrement the Logical Drive and Controller Usage Counts.
+  */
+  Controller->LogicalDriveUsageCount[LogicalDriveNumber]--;
+  Controller->ControllerUsageCount--;
+}
+
+
+/*
+  DAC960_Ioctl is the Device Ioctl Function for the DAC960 Driver.
+*/
+
+static int DAC960_Ioctl(Inode_T *Inode, File_T *File,
+			unsigned int Request, unsigned long Argument)
+{
+  int ControllerNumber = DAC960_ControllerNumber(Inode->i_rdev);
+  int LogicalDriveNumber = DAC960_LogicalDriveNumber(Inode->i_rdev);
+  int PartitionNumber, ErrorCode;
+  unsigned short Cylinders;
+  DiskGeometry_T *Geometry;
+  DAC960_Controller_T *Controller;
+  if (ControllerNumber < 0 || ControllerNumber > DAC960_ControllerCount - 1)
+    return -ENXIO;
+  Controller = DAC960_Controllers[ControllerNumber];
+  if (Controller == NULL ||
+      LogicalDriveNumber > Controller->LogicalDriveCount - 1)
+    return -ENXIO;
+  switch (Request)
+    {
+    case HDIO_GETGEO:
+      /* Get BIOS Disk Geometry. */
+      Geometry = (DiskGeometry_T *) Argument;
+      if (Geometry == NULL) return -EINVAL;
+      ErrorCode = verify_area(VERIFY_WRITE, Geometry, sizeof(DiskGeometry_T));
+      if (ErrorCode != 0) return ErrorCode;
+      Cylinders =
+	Controller->LogicalDriveInformation
+		    [Controller->LogicalDriveInformationIndex]
+		    [LogicalDriveNumber].LogicalDriveSize
+	/ (Controller->GeometryTranslationHeads *
+	   Controller->GeometryTranslationSectors);
+      put_user(Controller->GeometryTranslationHeads, &Geometry->heads);
+      put_user(Controller->GeometryTranslationSectors, &Geometry->sectors);
+      put_user(Cylinders, &Geometry->cylinders);
+      put_user(Controller->GenericDiskInfo.part[MINOR(Inode->i_rdev)]
+					  .start_sect, &Geometry->start);
+      return 0;
+    case BLKGETSIZE:
+      /* Get Device Size. */
+      if ((long *) Argument == NULL) return -EINVAL;
+      ErrorCode = verify_area(VERIFY_WRITE, (long *) Argument, sizeof(long));
+      if (ErrorCode != 0) return ErrorCode;
+      put_user(Controller->GenericDiskInfo.part[MINOR(Inode->i_rdev)].nr_sects,
+	       (long *) Argument);
+      return 0;
+    case BLKRAGET:
+      /* Get Read-Ahead. */
+      if ((int *) Argument == NULL) return -EINVAL;
+      ErrorCode = verify_area(VERIFY_WRITE, (int *) Argument, sizeof(int));
+      if (ErrorCode != 0) return ErrorCode;
+      put_user(read_ahead[MAJOR(Inode->i_rdev)], (int *) Argument);
+      return 0;
+    case BLKRASET:
+      /* Set Read-Ahead. */
+      if (!suser()) return -EACCES;
+      if (Argument > 256) return -EINVAL;
+      read_ahead[MAJOR(Inode->i_rdev)] = Argument;
+      return 0;
+    case BLKFLSBUF:
+      /* Flush Buffers. */
+      if (!suser()) return -EACCES;
+      fsync_dev(Inode->i_rdev);
+      invalidate_buffers(Inode->i_rdev);
+      return 0;
+    case BLKRRPART:
+      /* Re-Read Partition Table. */
+      if (!suser()) return -EACCES;
+      if (Controller->LogicalDriveUsageCount[LogicalDriveNumber] > 1)
+	return -EBUSY;
+      for (PartitionNumber = 0;
+	   PartitionNumber < DAC960_MaxPartitions;
+	   PartitionNumber++)
+	{
+	  KernelDevice_T Device = DAC960_KernelDevice(ControllerNumber,
+						      LogicalDriveNumber,
+						      PartitionNumber);
+	  int MinorNumber = DAC960_MinorNumber(LogicalDriveNumber,
+					       PartitionNumber);
+	  if (Controller->GenericDiskInfo.part[MinorNumber].nr_sects == 0)
+	    continue;
+	  /*
+	    Flush all changes and invalidate buffered state.
+	  */
+	  sync_dev(Device);
+	  invalidate_inodes(Device);
+	  invalidate_buffers(Device);
+	  /*
+	    Clear existing partition sizes.
+	  */
+	  if (PartitionNumber > 0)
+	    {
+	      Controller->GenericDiskInfo.part[MinorNumber].start_sect = 0;
+	      Controller->GenericDiskInfo.part[MinorNumber].nr_sects = 0;
+	    }
+	  /*
+	    Reset the Block Size so that the partition table can be read.
+	  */
+	  set_blocksize(Device, BLOCK_SIZE);
+	}
+      resetup_one_dev(&Controller->GenericDiskInfo, LogicalDriveNumber);
+      return 0;
+    }
+  return -EINVAL;
+}
+
+
+/*
+  DAC960_GenericDiskInit is the Generic Disk Information Initialization
+  Function for the DAC960 Driver.
+*/
+
+static void DAC960_InitializeGenericDiskInfo(GenericDiskInfo_T *GenericDiskInfo)
+{
+  DAC960_Controller_T *Controller =
+    (DAC960_Controller_T *) GenericDiskInfo->real_devices;
+  DAC960_LogicalDriveInformation_T *LogicalDriveInformation =
+    Controller->LogicalDriveInformation
+		[Controller->LogicalDriveInformationIndex];
+  int LogicalDriveNumber;
+  for (LogicalDriveNumber = 0;
+       LogicalDriveNumber < Controller->LogicalDriveCount;
+       LogicalDriveNumber++)
+    GenericDiskInfo->part[DAC960_MinorNumber(LogicalDriveNumber, 0)].nr_sects =
+      LogicalDriveInformation[LogicalDriveNumber].LogicalDriveSize;
+}
+
+
+/*
+  DAC960_Message prints Driver Messages.
+*/
+
+static void DAC960_Message(DAC960_MessageLevel_T MessageLevel,
+			   char *Format,
+			   DAC960_Controller_T *Controller,
+			   ...)
+{
+  static char Buffer[DAC960_LineBufferSize];
+  static boolean BeginningOfLine = true;
+  va_list Arguments;
+  int Length = 0;
+  va_start(Arguments, Controller);
+  Length = vsprintf(Buffer, Format, Arguments);
+  va_end(Arguments);
+  if (Controller == NULL)
+    printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel],
+	   DAC960_ControllerCount, Buffer);
+  else if (MessageLevel == DAC960_AnnounceLevel ||
+	   MessageLevel == DAC960_InfoLevel)
+    {
+      if (!Controller->ControllerInitialized)
+	{
+	  strcpy(&Controller->InitialStatusBuffer[
+		    Controller->InitialStatusLength], Buffer);
+	  Controller->InitialStatusLength += Length;
+	  if (MessageLevel == DAC960_AnnounceLevel)
+	    {
+	      static int AnnouncementLines = 0;
+	      if (++AnnouncementLines <= 2)
+		printk("%sDAC960: %s", DAC960_MessageLevelMap[MessageLevel],
+		       Buffer);
+	    }
+	  else
+	    {
+	      if (BeginningOfLine)
+		{
+		  if (Buffer[0] != '\n' || Length > 1)
+		    printk("%sDAC960#%d: %s",
+			   DAC960_MessageLevelMap[MessageLevel],
+			   Controller->ControllerNumber, Buffer);
+		}
+	      else printk("%s", Buffer);
+	    }
+	}
+      else
+	{
+	  strcpy(&Controller->CurrentStatusBuffer[
+		    Controller->CurrentStatusLength], Buffer);
+	  Controller->CurrentStatusLength += Length;
+	}
+    }
+  else if (MessageLevel == DAC960_ProgressLevel)
+    {
+      strcpy(Controller->RebuildProgressBuffer, Buffer);
+      Controller->RebuildProgressLength = Length;
+      if (Controller->EphemeralProgressMessage)
+	{
+	  if (jiffies - Controller->LastProgressReportTime
+	      >= DAC960_ProgressReportingInterval)
+	    {
+	      printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel],
+		     Controller->ControllerNumber, Buffer);
+	      Controller->LastProgressReportTime = jiffies;
+	    }
+	}
+      else printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel],
+		  Controller->ControllerNumber, Buffer);
+    }
+  else if (MessageLevel == DAC960_UserCriticalLevel)
+    {
+      strcpy(&Controller->UserStatusBuffer[Controller->UserStatusLength],
+	     Buffer);
+      Controller->UserStatusLength += Length;
+      if (Buffer[0] != '\n' || Length > 1)
+	printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel],
+	       Controller->ControllerNumber, Buffer);
+    }
+  else
+    {
+      if (BeginningOfLine)
+	printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel],
+	       Controller->ControllerNumber, Buffer);
+      else printk("%s", Buffer);
+    }
+  BeginningOfLine = (Buffer[Length-1] == '\n');
+}
+
+
+/*
+  DAC960_AllocateUserCommand allocates a Command structure for a User Command.
+  If a User Command is already active, NULL is returned.
+*/
+
+static DAC960_Command_T *DAC960_AllocateUserCommand(DAC960_Controller_T
+						    *Controller)
+{
+  Semaphore_T Semaphore = MUTEX_LOCKED;
+  DAC960_Command_T *Command = NULL;
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
+  /*
+    Acquire exclusive access to the User Command facility.
+  */
+  if (Controller->UserCommandActive) goto Done;
+  Controller->UserCommandActive = true;
+  /*
+    Allocate a Command.  If none is available, set the User Command Deferred
+    flag to tell the Interrupt Handler to save the next Command completed.
+  */
+  Command = DAC960_AllocateCommand(Controller);
+  if (Command == NULL)
+    {
+      Controller->UserCommandDeferred = true;
+      Controller->UserCommandSemaphore = &Semaphore;
+      DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
+      down(&Semaphore);
+      DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
+      Command = Controller->UserCommand;
+      Controller->UserCommand = NULL;
+    }
+  /*
+    Initialize the Command.
+  */
+  DAC960_ClearCommand(Command);
+  Command->CommandType = DAC960_ImmediateCommand;
+  /*
+    Release exclusive access to Controller.
+  */
+ Done:
+  DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
+  return Command;
+}
+
+
+/*
+  DAC960_DeallocateUserCommand deallocates a User Command.
+*/
+
+static void DAC960_DeallocateUserCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
+  /*
+    Deallocate the Command.
+  */
+  DAC960_DeallocateCommand(Command);
+  /*
+    Release exclusive access to the User Command facility.
+  */
+  Controller->UserCommandActive = false;
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_ParsePhysicalDrive parses spaces followed by a Physical Drive
+  Channel:TargetID specification from a User Command string.  It updates
+  Channel and TargetID and returns true on success and returns false otherwise.
+*/
+
+static boolean DAC960_ParsePhysicalDrive(DAC960_Controller_T *Controller,
+					 char *UserCommandString,
+					 unsigned char *Channel,
+					 unsigned char *TargetID)
+{
+  char *NewUserCommandString = UserCommandString;
+  unsigned long XChannel, XTargetID;
+  while (*UserCommandString == ' ') UserCommandString++;
+  if (UserCommandString == NewUserCommandString)
+    return false;
+  XChannel = simple_strtoul(UserCommandString, &NewUserCommandString, 10);
+  if (NewUserCommandString == UserCommandString ||
+      *NewUserCommandString != ':' ||
+      XChannel >= Controller->Channels)
+    return false;
+  UserCommandString = ++NewUserCommandString;
+  XTargetID = simple_strtoul(UserCommandString, &NewUserCommandString, 10);
+  if (NewUserCommandString == UserCommandString ||
+      *NewUserCommandString != '\0' ||
+      XTargetID >= DAC960_MaxTargets)
+    return false;
+  *Channel = XChannel;
+  *TargetID = XTargetID;
+  return true;
+}
+
+
+/*
+  DAC960_ParseLogicalDrive parses spaces followed by a Logical Drive Number
+  specification from a User Command string.  It updates LogicalDriveNumber and
+  returns true on success and returns false otherwise.
+*/
+
+static boolean DAC960_ParseLogicalDrive(DAC960_Controller_T *Controller,
+					char *UserCommandString,
+					unsigned char *LogicalDriveNumber)
+{
+  char *NewUserCommandString = UserCommandString;
+  unsigned long XLogicalDriveNumber;
+  while (*UserCommandString == ' ') UserCommandString++;
+  if (UserCommandString == NewUserCommandString)
+    return false;
+  XLogicalDriveNumber =
+    simple_strtoul(UserCommandString, &NewUserCommandString, 10);
+  if (NewUserCommandString == UserCommandString ||
+      *NewUserCommandString != '\0' ||
+      XLogicalDriveNumber >= Controller->LogicalDriveCount)
+    return false;
+  *LogicalDriveNumber = XLogicalDriveNumber;
+  return true;
+}
+
+
+/*
+  DAC960_SetDeviceState sets the Device State for a Physical Drive.
+*/
+
+static void DAC960_SetDeviceState(DAC960_Controller_T *Controller,
+				  DAC960_Command_T *Command,
+				  unsigned char Channel,
+				  unsigned char TargetID,
+				  DAC960_PhysicalDeviceState_T DeviceState,
+				  const char *DeviceStateString)
+{
+  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+  CommandMailbox->Type3D.CommandOpcode = DAC960_StartDevice;
+  CommandMailbox->Type3D.Channel = Channel;
+  CommandMailbox->Type3D.TargetID = TargetID;
+  CommandMailbox->Type3D.DeviceState = DeviceState;
+  CommandMailbox->Type3D.Modifier = 0;
+  DAC960_ExecuteCommand(Command);
+  switch (Command->CommandStatus)
+    {
+    case DAC960_NormalCompletion:
+      DAC960_UserCritical("%s of Physical Drive %d:%d Succeeded\n", Controller,
+			  DeviceStateString, Channel, TargetID);
+      break;
+    case DAC960_UnableToStartDevice:
+      DAC960_UserCritical("%s of Physical Drive %d:%d Failed - "
+			  "Unable to Start Device\n", Controller,
+			  DeviceStateString, Channel, TargetID);
+      break;
+    case DAC960_NoDeviceAtAddress:
+      DAC960_UserCritical("%s of Physical Drive %d:%d Failed - "
+			  "No Device at Address\n", Controller,
+			  DeviceStateString, Channel, TargetID);
+      break;
+    case DAC960_InvalidChannelOrTargetOrModifier:
+      DAC960_UserCritical("%s of Physical Drive %d:%d Failed - "
+			  "Invalid Channel or Target or Modifier\n",
+			  Controller, DeviceStateString, Channel, TargetID);
+      break;
+    case DAC960_ChannelBusy:
+      DAC960_UserCritical("%s of Physical Drive %d:%d Failed - "
+			  "Channel Busy\n", Controller,
+			  DeviceStateString, Channel, TargetID);
+      break;
+    default:
+      DAC960_UserCritical("%s of Physical Drive %d:%d Failed - "
+			  "Unexpected Status %04X\n", Controller,
+			  DeviceStateString, Channel, TargetID,
+			  Command->CommandStatus);
+      break;
+    }
+}
+
+
+/*
+  DAC960_ExecuteUserCommand executes a User Command.
+*/
+
+static boolean DAC960_ExecuteUserCommand(DAC960_Controller_T *Controller,
+					 char *UserCommand)
+{
+  DAC960_Command_T *Command;
+  DAC960_CommandMailbox_T *CommandMailbox;
+  unsigned char Channel, TargetID, LogicalDriveNumber;
+  Command = DAC960_AllocateUserCommand(Controller);
+  if (Command == NULL) return false;
+  CommandMailbox = &Command->CommandMailbox;
+  Controller->UserStatusLength = 0;
+  if (strcmp(UserCommand, "flush-cache") == 0)
+    {
+      CommandMailbox->Type3.CommandOpcode = DAC960_Flush;
+      DAC960_ExecuteCommand(Command);
+      DAC960_UserCritical("Cache Flush Completed\n", Controller);
+    }
+  else if (strncmp(UserCommand, "kill", 4) == 0 &&
+	   DAC960_ParsePhysicalDrive(Controller, &UserCommand[4],
+				     &Channel, &TargetID))
+    {
+      DAC960_DeviceState_T *DeviceState =
+	&Controller->DeviceState[Controller->DeviceStateIndex]
+				[Channel][TargetID];
+      if (DeviceState->Present &&
+	  DeviceState->DeviceType == DAC960_DiskType &&
+	  DeviceState->DeviceState != DAC960_Device_Dead)
+	DAC960_SetDeviceState(Controller, Command, Channel, TargetID,
+			      DAC960_Device_Dead, "Kill");
+      else DAC960_UserCritical("Kill of Physical Drive %d:%d Illegal\n",
+			       Controller, Channel, TargetID);
+    }
+  else if (strncmp(UserCommand, "make-online", 11) == 0 &&
+	   DAC960_ParsePhysicalDrive(Controller, &UserCommand[11],
+				     &Channel, &TargetID))
+    {
+      DAC960_DeviceState_T *DeviceState =
+	&Controller->DeviceState[Controller->DeviceStateIndex]
+				[Channel][TargetID];
+      if (DeviceState->Present &&
+	  DeviceState->DeviceType == DAC960_DiskType &&
+	  DeviceState->DeviceState == DAC960_Device_Dead)
+	DAC960_SetDeviceState(Controller, Command, Channel, TargetID,
+			      DAC960_Device_Online, "Make Online");
+      else DAC960_UserCritical("Make Online of Physical Drive %d:%d Illegal\n",
+			       Controller, Channel, TargetID);
+
+    }
+  else if (strncmp(UserCommand, "make-standby", 12) == 0 &&
+	   DAC960_ParsePhysicalDrive(Controller, &UserCommand[12],
+				     &Channel, &TargetID))
+    {
+      DAC960_DeviceState_T *DeviceState =
+	&Controller->DeviceState[Controller->DeviceStateIndex]
+				[Channel][TargetID];
+      if (DeviceState->Present &&
+	  DeviceState->DeviceType == DAC960_DiskType &&
+	  DeviceState->DeviceState == DAC960_Device_Dead)
+	DAC960_SetDeviceState(Controller, Command, Channel, TargetID,
+			      DAC960_Device_Standby, "Make Standby");
+      else DAC960_UserCritical("Make Standby of Physical Drive %d:%d Illegal\n",
+			       Controller, Channel, TargetID);
+    }
+  else if (strncmp(UserCommand, "rebuild", 7) == 0 &&
+	   DAC960_ParsePhysicalDrive(Controller, &UserCommand[7],
+				     &Channel, &TargetID))
+    {
+      CommandMailbox->Type3D.CommandOpcode = DAC960_RebuildAsync;
+      CommandMailbox->Type3D.Channel = Channel;
+      CommandMailbox->Type3D.TargetID = TargetID;
+      DAC960_ExecuteCommand(Command);
+      switch (Command->CommandStatus)
+	{
+	case DAC960_NormalCompletion:
+	  DAC960_UserCritical("Rebuild of Physical Drive %d:%d Initiated\n",
+			      Controller, Channel, TargetID);
+	  break;
+	case DAC960_AttemptToRebuildOnlineDrive:
+	  DAC960_UserCritical("Rebuild of Physical Drive %d:%d Failed - "
+			      "Attempt to Rebuild Online or "
+			      "Unresponsive Drive\n",
+			      Controller, Channel, TargetID);
+	  break;
+	case DAC960_NewDiskFailedDuringRebuild:
+	  DAC960_UserCritical("Rebuild of Physical Drive %d:%d Failed - "
+			      "New Disk Failed During Rebuild\n",
+			      Controller, Channel, TargetID);
+	  break;
+	case DAC960_InvalidDeviceAddress:
+	  DAC960_UserCritical("Rebuild of Physical Drive %d:%d Failed - "
+			      "Invalid Device Address\n",
+			      Controller, Channel, TargetID);
+	  break;
+	case DAC960_RebuildOrCheckAlreadyInProgress:
+	  DAC960_UserCritical("Rebuild of Physical Drive %d:%d Failed - "
+			      "Rebuild or Consistency Check Already "
+			      "in Progress\n", Controller, Channel, TargetID);
+	  break;
+	default:
+	  DAC960_UserCritical("Rebuild of Physical Drive %d:%d Failed - "
+			      "Unexpected Status %04X\n", Controller,
+			      Channel, TargetID, Command->CommandStatus);
+	  break;
+	}
+    }
+  else if (strncmp(UserCommand, "check-consistency", 17) == 0 &&
+	   DAC960_ParseLogicalDrive(Controller, &UserCommand[17],
+				    &LogicalDriveNumber))
+    {
+      CommandMailbox->Type3C.CommandOpcode = DAC960_CheckConsistencyAsync;
+      CommandMailbox->Type3C.LogicalDriveNumber = LogicalDriveNumber;
+      CommandMailbox->Type3C.AutoRestore = true;
+      DAC960_ExecuteCommand(Command);
+      switch (Command->CommandStatus)
+	{
+	case DAC960_NormalCompletion:
+	  DAC960_UserCritical("Consistency Check of Logical Drive %d "
+			      "(/dev/rd/c%dd%d) Initiated\n",
+			      Controller, LogicalDriveNumber,
+			      Controller->ControllerNumber,
+			      LogicalDriveNumber);
+	  break;
+	case DAC960_DependentDiskIsDead:
+	  DAC960_UserCritical("Consistency Check of Logical Drive %d "
+			      "(/dev/rd/c%dd%d) Failed - "
+			      "Dependent Physical Drive is DEAD\n",
+			      Controller, LogicalDriveNumber,
+			      Controller->ControllerNumber,
+			      LogicalDriveNumber);
+	  break;
+	case DAC960_InvalidOrNonredundantLogicalDrive:
+	  DAC960_UserCritical("Consistency Check of Logical Drive %d "
+			      "(/dev/rd/c%dd%d) Failed - "
+			      "Invalid or Nonredundant Logical Drive\n",
+			      Controller, LogicalDriveNumber,
+			      Controller->ControllerNumber,
+			      LogicalDriveNumber);
+	  break;
+	case DAC960_RebuildOrCheckAlreadyInProgress:
+	  DAC960_UserCritical("Consistency Check of Logical Drive %d "
+			      "(/dev/rd/c%dd%d) Failed - Rebuild or "
+			      "Consistency Check Already in Progress\n",
+			      Controller, LogicalDriveNumber,
+			      Controller->ControllerNumber,
+			      LogicalDriveNumber);
+	  break;
+	default:
+	  DAC960_UserCritical("Consistency Check of Logical Drive %d "
+			      "(/dev/rd/c%dd%d) Failed - "
+			      "Unexpected Status %04X\n",
+			      Controller, LogicalDriveNumber,
+			      Controller->ControllerNumber,
+			      LogicalDriveNumber, Command->CommandStatus);
+	  break;
+	}
+    }
+  else if (strcmp(UserCommand, "cancel-rebuild") == 0 ||
+	   strcmp(UserCommand, "cancel-consistency-check") == 0)
+    {
+      unsigned char OldRebuildRateConstant;
+      CommandMailbox->Type3R.CommandOpcode = DAC960_RebuildControl;
+      CommandMailbox->Type3R.RebuildRateConstant = 0xFF;
+      CommandMailbox->Type3R.BusAddress =
+	Virtual_to_Bus(&OldRebuildRateConstant);
+      DAC960_ExecuteCommand(Command);
+      switch (Command->CommandStatus)
+	{
+	case DAC960_NormalCompletion:
+	  DAC960_UserCritical("Rebuild or Consistency Check Cancelled\n",
+			      Controller);
+	  break;
+	default:
+	  DAC960_UserCritical("Cancellation of Rebuild or "
+			      "Consistency Check Failed - "
+			      "Unexpected Status %04X\n",
+			      Controller, Command->CommandStatus);
+	  break;
+	}
+    }
+  else DAC960_UserCritical("Illegal User Command: '%s'\n",
+			   Controller, UserCommand);
+  DAC960_DeallocateUserCommand(Command);
+  return true;
+}
+
+
+/*
+  DAC960_ProcReadStatus implements reading /proc/rd/status.
+*/
+
+static int DAC960_ProcReadStatus(Inode_T *Inode, File_T *File,
+				 char *Buffer, int Count)
+{
+  char *StatusMessage = "OK\n";
+  int StatusMessageOffset, BytesAvailable;
+  int ControllerNumber;
+  for (ControllerNumber = 0;
+       ControllerNumber < DAC960_ControllerCount;
+       ControllerNumber++)
+    {
+      DAC960_Controller_T *Controller = DAC960_Controllers[ControllerNumber];
+      DAC960_Enquiry_T *Enquiry;
+      if (Controller == NULL) continue;
+      Enquiry = &Controller->Enquiry[Controller->EnquiryIndex];
+      if (Enquiry->CriticalLogicalDriveCount > 0 ||
+	  Enquiry->OfflineLogicalDriveCount > 0 ||
+	  Enquiry->DeadDriveCount > 0)
+	{
+	  StatusMessage = "ALERT\n";
+	  break;
+	}
+    }
+  StatusMessageOffset = File->f_pos;
+  BytesAvailable = strlen(StatusMessage) - StatusMessageOffset;
+  if (Count > BytesAvailable) Count = BytesAvailable;
+  if (Count <= 0) return 0;
+  memcpy_tofs(Buffer, &StatusMessage[StatusMessageOffset], Count);
+  File->f_pos += Count;
+  return Count;
+}
+
+
+/*
+  DAC960_ProcReadInitialStatus implements reading /proc/rd/cN/initial_status.
+*/
+
+static int DAC960_ProcReadInitialStatus(Inode_T *Inode, File_T *File,
+					char *Buffer, int Count)
+{
+  DAC960_Controller_T *Controller = NULL;
+  int InitialStatusOffset, BytesAvailable;
+  int ControllerNumber;
+  for (ControllerNumber = 0;
+       ControllerNumber < DAC960_ControllerCount;
+       ControllerNumber++)
+    {
+      Controller = DAC960_Controllers[ControllerNumber];
+      if (Controller == NULL) continue;
+      if (Controller->InitialStatusProcEntry.low_ino == Inode->i_ino) break;
+    }
+  InitialStatusOffset = File->f_pos;
+  BytesAvailable = Controller->InitialStatusLength - InitialStatusOffset;
+  if (Count > BytesAvailable) Count = BytesAvailable;
+  if (Count <= 0) return 0;
+  memcpy_tofs(Buffer,
+	      &Controller->InitialStatusBuffer[InitialStatusOffset], Count);
+  File->f_pos += Count;
+  return Count;
+}
+
+
+/*
+  DAC960_ProcReadCurrentStatus implements reading /proc/rd/cN/current_status.
+*/
+
+static int DAC960_ProcReadCurrentStatus(Inode_T *Inode, File_T *File,
+					char *Buffer, int Count)
+{
+  DAC960_Controller_T *Controller = NULL;
+  int CurrentStatusOffset, BytesAvailable;
+  int ControllerNumber;
+  for (ControllerNumber = 0;
+       ControllerNumber < DAC960_ControllerCount;
+       ControllerNumber++)
+    {
+      Controller = DAC960_Controllers[ControllerNumber];
+      if (Controller == NULL) continue;
+      if (Controller->CurrentStatusProcEntry.low_ino == Inode->i_ino) break;
+    }
+  Controller->CurrentStatusLength = 0;
+  DAC960_AnnounceDriver(Controller);
+  DAC960_ReportControllerConfiguration(Controller);
+  Controller->CurrentStatusBuffer[Controller->CurrentStatusLength++] = ' ';
+  Controller->CurrentStatusBuffer[Controller->CurrentStatusLength++] = ' ';
+  if (Controller->RebuildProgressLength > 0)
+    {
+      strcpy(&Controller->CurrentStatusBuffer[Controller->CurrentStatusLength],
+	     Controller->RebuildProgressBuffer);
+      Controller->CurrentStatusLength += Controller->RebuildProgressLength;
+    }
+  else
+    {
+      char *StatusMessage = "No Rebuild or Consistency Check in Progress\n";
+      strcpy(&Controller->CurrentStatusBuffer[Controller->CurrentStatusLength],
+	     StatusMessage);
+      Controller->CurrentStatusLength += strlen(StatusMessage);
+    }
+  CurrentStatusOffset = File->f_pos;
+  BytesAvailable = Controller->CurrentStatusLength - CurrentStatusOffset;
+  if (Count > BytesAvailable) Count = BytesAvailable;
+  if (Count <= 0) return 0;
+  memcpy_tofs(Buffer,
+	      &Controller->CurrentStatusBuffer[CurrentStatusOffset], Count);
+  File->f_pos += Count;
+  return Count;
+}
+
+
+/*
+  DAC960_ProcReadUserCommand implements reading /proc/rd/cN/user_command.
+*/
+
+static int DAC960_ProcReadUserCommand(Inode_T *Inode, File_T *File,
+				      char *Buffer, int Count)
+{
+  DAC960_Controller_T *Controller = NULL;
+  int UserStatusOffset, BytesAvailable;
+  int ControllerNumber;
+  for (ControllerNumber = 0;
+       ControllerNumber < DAC960_ControllerCount;
+       ControllerNumber++)
+    {
+      Controller = DAC960_Controllers[ControllerNumber];
+      if (Controller == NULL) continue;
+      if (Controller->UserCommandProcEntry.low_ino == Inode->i_ino) break;
+    }
+  UserStatusOffset = File->f_pos;
+  BytesAvailable = Controller->UserStatusLength - UserStatusOffset;
+  if (Count > BytesAvailable) Count = BytesAvailable;
+  if (Count <= 0) return 0;
+  memcpy_tofs(Buffer, &Controller->UserStatusBuffer[UserStatusOffset], Count);
+  File->f_pos += Count;
+  return Count;
+}
+
+
+/*
+  DAC960_ProcWriteUserCommand implements writing /proc/rd/cN/user_command.
+*/
+
+static int DAC960_ProcWriteUserCommand(Inode_T *Inode, File_T *File,
+				       const char *Buffer, int Count)
+{
+  DAC960_Controller_T *Controller = NULL;
+  char CommandBuffer[80];
+  int ControllerNumber, Length;
+  if (Count > sizeof(CommandBuffer)-1) return -EINVAL;
+  memcpy_fromfs(CommandBuffer, Buffer, Count);
+  CommandBuffer[Count] = '\0';
+  Length = strlen(CommandBuffer);
+  if (CommandBuffer[Length-1] == '\n')
+    CommandBuffer[--Length] = '\0';
+  for (ControllerNumber = 0;
+       ControllerNumber < DAC960_ControllerCount;
+       ControllerNumber++)
+    {
+      Controller = DAC960_Controllers[ControllerNumber];
+      if (Controller == NULL) continue;
+      if (Controller->UserCommandProcEntry.low_ino == Inode->i_ino) break;
+    }
+  return (DAC960_ExecuteUserCommand(Controller, CommandBuffer)
+	  ? Count : -EBUSY);
+}
+
+
+/*
+  DAC960_CreateProcEntries creates the /proc/rd/... entries for the DAC960
+  Driver.
+*/
+
+static void DAC960_CreateProcEntries(void)
+{
+  static PROC_DirectoryEntry_T StatusProcEntry;
+  static FileOperations_T
+    StatusFileOperations = { read: DAC960_ProcReadStatus };
+  static InodeOperations_T
+    StatusInodeOperations = { &StatusFileOperations };
+  static FileOperations_T
+    InitialStatusFileOperations = { read: DAC960_ProcReadInitialStatus };
+  static InodeOperations_T
+    InitialStatusInodeOperations = { &InitialStatusFileOperations };
+  static FileOperations_T
+    CurrentStatusFileOperations = { read: DAC960_ProcReadCurrentStatus };
+  static InodeOperations_T
+    CurrentStatusInodeOperations = { &CurrentStatusFileOperations };
+  static FileOperations_T
+    UserCommandFileOperations = { read: DAC960_ProcReadUserCommand,
+				  write: DAC960_ProcWriteUserCommand };
+  static InodeOperations_T
+    UserCommandInodeOperations = { &UserCommandFileOperations };
+  int ControllerNumber;
+  DAC960_ProcDirectoryEntry.name = "rd";
+  DAC960_ProcDirectoryEntry.namelen = strlen(DAC960_ProcDirectoryEntry.name);
+  DAC960_ProcDirectoryEntry.mode = S_IFDIR | S_IRUGO | S_IXUGO;
+  DAC960_ProcDirectoryEntry.ops = &proc_dir_inode_operations;
+  proc_register_dynamic(&proc_root, &DAC960_ProcDirectoryEntry);
+  StatusProcEntry.name = "status";
+  StatusProcEntry.namelen = strlen(StatusProcEntry.name);
+  StatusProcEntry.mode = S_IFREG | S_IRUGO;
+  StatusProcEntry.ops = &StatusInodeOperations;
+  proc_register_dynamic(&DAC960_ProcDirectoryEntry, &StatusProcEntry);
+  for (ControllerNumber = 0;
+       ControllerNumber < DAC960_ControllerCount;
+       ControllerNumber++)
+    {
+      DAC960_Controller_T *Controller = DAC960_Controllers[ControllerNumber];
+      PROC_DirectoryEntry_T *ControllerProcEntry, *InitialStatusProcEntry;
+      PROC_DirectoryEntry_T *CurrentStatusProcEntry, *UserCommandProcEntry;
+      if (Controller == NULL) continue;
+      ControllerProcEntry = &Controller->ControllerProcEntry;
+      ControllerProcEntry->name = Controller->ControllerName;
+      ControllerProcEntry->namelen = strlen(ControllerProcEntry->name);
+      ControllerProcEntry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+      ControllerProcEntry->ops = &proc_dir_inode_operations;
+      proc_register_dynamic(&DAC960_ProcDirectoryEntry, ControllerProcEntry);
+      InitialStatusProcEntry = &Controller->InitialStatusProcEntry;
+      InitialStatusProcEntry->name = "initial_status";
+      InitialStatusProcEntry->namelen = strlen(InitialStatusProcEntry->name);
+      InitialStatusProcEntry->mode = S_IFREG | S_IRUGO;
+      InitialStatusProcEntry->ops = &InitialStatusInodeOperations;
+      proc_register_dynamic(ControllerProcEntry, InitialStatusProcEntry);
+      CurrentStatusProcEntry = &Controller->CurrentStatusProcEntry;
+      CurrentStatusProcEntry->name = "current_status";
+      CurrentStatusProcEntry->namelen = strlen(CurrentStatusProcEntry->name);
+      CurrentStatusProcEntry->mode = S_IFREG | S_IRUGO;
+      CurrentStatusProcEntry->ops = &CurrentStatusInodeOperations;
+      proc_register_dynamic(ControllerProcEntry, CurrentStatusProcEntry);
+      UserCommandProcEntry = &Controller->UserCommandProcEntry;
+      UserCommandProcEntry->name = "user_command";
+      UserCommandProcEntry->namelen = strlen(UserCommandProcEntry->name);
+      UserCommandProcEntry->mode = S_IFREG | S_IWUSR | S_IRUSR;
+      UserCommandProcEntry->ops = &UserCommandInodeOperations;
+      proc_register_dynamic(ControllerProcEntry, UserCommandProcEntry);
+    }
+}
+
+
+/*
+  DAC960_DestroyProcEntries destroys the /proc/rd/... entries for the DAC960
+  Driver.
+*/
+
+static void DAC960_DestroyProcEntries(void)
+{
+  proc_unregister(&proc_root, DAC960_ProcDirectoryEntry.low_ino);
+}
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/linux/drivers/block/DAC960.h linux/linux/drivers/block/DAC960.h
--- linux.vanilla/linux/drivers/block/DAC960.h	Thu Jan  1 01:00:00 1970
+++ linux/linux/drivers/block/DAC960.h	Thu Mar 25 23:52:48 1999
@@ -0,0 +1,2071 @@
+/*
+
+  Linux Driver for Mylex DAC960 and DAC1100 PCI RAID Controllers
+
+  Copyright 1998-1999 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+  This program is free software; you may redistribute and/or modify it under
+  the terms of the GNU General Public License Version 2 as published by the
+  Free Software Foundation.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
+  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+  for complete details.
+
+  The author respectfully requests that any modifications to this software be
+  sent directly to him for evaluation and testing.
+
+*/
+
+
+/*
+  DAC960_DriverVersion protects the private portion of this file.
+*/
+
+#ifdef DAC960_DriverVersion
+
+
+/*
+  Define the maximum number of DAC960 Controllers supported by this driver.
+*/
+
+#define DAC960_MaxControllers			8
+
+
+/*
+  Define the maximum number of Controller Channels supported by this driver.
+*/
+
+#define DAC960_MaxChannels			3
+
+
+/*
+  Define the maximum number of Targets per Channel supported by this driver.
+*/
+
+#define DAC960_MaxTargets			16
+
+
+/*
+  Define the maximum number of Logical Drives supported by any DAC960 model.
+*/
+
+#define DAC960_MaxLogicalDrives			32
+
+
+/*
+  Define the maximum number of Partitions allowed for each Logical Drive.
+*/
+
+#define DAC960_MaxPartitions			8
+#define DAC960_MaxPartitionsBits		3
+
+
+/*
+  Define the maximum Driver Queue Depth and Controller Queue Depth supported
+  by any DAC960 model.
+*/
+
+#define DAC960_MaxDriverQueueDepth		127
+#define DAC960_MaxControllerQueueDepth		128
+
+
+/*
+  Define the maximum number of Scatter/Gather Segments supported by any
+  DAC960 model.
+*/
+
+#define DAC960_MaxScatterGatherSegments		33
+
+
+/*
+  Define the DAC960 Controller Monitoring Timer Interval.
+*/
+
+#define DAC960_MonitoringTimerInterval		(10 * HZ)
+
+
+/*
+  Define the DAC960 Controller Secondary Monitoring Interval.
+*/
+
+#define DAC960_SecondaryMonitoringInterval	(60 * HZ)
+
+
+/*
+  Define the DAC960 Controller Progress Reporting Interval.
+*/
+
+#define DAC960_ProgressReportingInterval	(60 * HZ)
+
+
+/*
+  Define the number of Command Mailboxes and Status Mailboxes used by the
+  Memory Mailbox Interface.
+*/
+
+#define DAC960_CommandMailboxCount		256
+#define DAC960_StatusMailboxCount		1024
+
+
+/*
+  Define macros to extract the Controller Number, Logical Drive Number, and
+  Partition Number from a Kernel Device, and to construct a Major Number, Minor
+  Number, and Kernel Device from the Controller Number, Logical Drive Number,
+  and Partition Number.  There is one Major Number assigned to each Controller.
+  The associated Minor Number is divided into the Logical Drive Number and
+  Partition Number.
+*/
+
+#define DAC960_ControllerNumber(Device) \
+  (MAJOR(Device) - DAC960_MAJOR)
+
+#define DAC960_LogicalDriveNumber(Device) \
+  (MINOR(Device) >> DAC960_MaxPartitionsBits)
+
+#define DAC960_PartitionNumber(Device) \
+  (MINOR(Device) & (DAC960_MaxPartitions - 1))
+
+#define DAC960_MajorNumber(ControllerNumber) \
+  (DAC960_MAJOR + (ControllerNumber))
+
+#define DAC960_MinorNumber(LogicalDriveNumber, PartitionNumber) \
+   (((LogicalDriveNumber) << DAC960_MaxPartitionsBits) | (PartitionNumber))
+
+#define DAC960_MinorCount			(DAC960_MaxLogicalDrives \
+						 * DAC960_MaxPartitions)
+
+#define DAC960_KernelDevice(ControllerNumber,				       \
+			    LogicalDriveNumber,				       \
+			    PartitionNumber)				       \
+   MKDEV(DAC960_MajorNumber(ControllerNumber),				       \
+	 DAC960_MinorNumber(LogicalDriveNumber, PartitionNumber))
+
+
+/*
+  Define the DAC960 Controller fixed Block Size and Block Size Bits.
+*/
+
+#define DAC960_BlockSize			512
+#define DAC960_BlockSizeBits			9
+
+
+/*
+  Define the Controller Line, Status Buffer, Rebuild Progress, and
+  User Message Sizes.
+*/
+
+#define DAC960_LineBufferSize			100
+#define DAC960_StatusBufferSize			5000
+#define DAC960_RebuildProgressSize		200
+#define DAC960_UserMessageSize			200
+
+
+/*
+  Define the Driver Message Levels.
+*/
+
+typedef enum DAC960_MessageLevel
+{
+  DAC960_AnnounceLevel =			0,
+  DAC960_InfoLevel =				1,
+  DAC960_NoticeLevel =				2,
+  DAC960_WarningLevel =				3,
+  DAC960_ErrorLevel =				4,
+  DAC960_ProgressLevel =			5,
+  DAC960_CriticalLevel =			6,
+  DAC960_UserCriticalLevel =			7
+}
+DAC960_MessageLevel_T;
+
+static char
+  *DAC960_MessageLevelMap[] =
+    { KERN_NOTICE, KERN_NOTICE, KERN_NOTICE, KERN_WARNING,
+      KERN_ERR, KERN_CRIT, KERN_CRIT, KERN_CRIT };
+
+
+/*
+  Define Driver Message macros.
+*/
+
+#define DAC960_Announce(Format, Arguments...) \
+  DAC960_Message(DAC960_AnnounceLevel, Format, ##Arguments)
+
+#define DAC960_Info(Format, Arguments...) \
+  DAC960_Message(DAC960_InfoLevel, Format, ##Arguments)
+
+#define DAC960_Notice(Format, Arguments...) \
+  DAC960_Message(DAC960_NoticeLevel, Format, ##Arguments)
+
+#define DAC960_Warning(Format, Arguments...) \
+  DAC960_Message(DAC960_WarningLevel, Format, ##Arguments)
+
+#define DAC960_Error(Format, Arguments...) \
+  DAC960_Message(DAC960_ErrorLevel, Format, ##Arguments)
+
+#define DAC960_Progress(Format, Arguments...) \
+  DAC960_Message(DAC960_ProgressLevel, Format, ##Arguments)
+
+#define DAC960_Critical(Format, Arguments...) \
+  DAC960_Message(DAC960_CriticalLevel, Format, ##Arguments)
+
+#define DAC960_UserCritical(Format, Arguments...) \
+  DAC960_Message(DAC960_UserCriticalLevel, Format, ##Arguments)
+
+
+/*
+  Define the types of DAC960 Controllers that are supported.
+*/
+
+typedef enum
+{
+  DAC960_V5_Controller =			1,	/* DAC1164P */
+  DAC960_V4_Controller =			2,	/* DAC960PTL/PJ/PG */
+  DAC960_V3_Controller =			3	/* DAC960PU/PD/PL */
+}
+DAC960_ControllerType_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 DAC960_IO_Address_T;
+
+
+/*
+  Define a 32 bit PCI Bus Address data type.
+*/
+
+typedef unsigned int DAC960_PCI_Address_T;
+
+
+/*
+  Define a 32 bit Bus Address data type.
+*/
+
+typedef unsigned int DAC960_BusAddress_T;
+
+
+/*
+  Define a 32 bit Byte Count data type.
+*/
+
+typedef unsigned int DAC960_ByteCount_T;
+
+
+/*
+  Define types for some of the structures that interface with the rest
+  of the Linux Kernel and I/O Subsystem.
+*/
+
+typedef struct buffer_head BufferHeader_T;
+typedef struct file File_T;
+typedef struct file_operations FileOperations_T;
+typedef struct gendisk GenericDiskInfo_T;
+typedef struct hd_geometry DiskGeometry_T;
+typedef struct hd_struct DiskPartition_T;
+typedef struct inode Inode_T;
+typedef struct inode_operations InodeOperations_T;
+typedef kdev_t KernelDevice_T;
+typedef struct proc_dir_entry PROC_DirectoryEntry_T;
+typedef unsigned long ProcessorFlags_T;
+typedef struct pt_regs Registers_T;
+typedef struct request IO_Request_T;
+typedef struct semaphore Semaphore_T;
+typedef struct timer_list Timer_T;
+typedef struct wait_queue WaitQueue_T;
+
+
+/*
+  Define the DAC960 V5 Controller Interface Register Offsets.
+*/
+
+#define DAC960_V5_RegisterWindowSize		0x80
+
+typedef enum
+{
+  DAC960_V5_InboundDoorBellRegisterOffset =	0x60,
+  DAC960_V5_OutboundDoorBellRegisterOffset =	0x61,
+  DAC960_V5_InterruptMaskRegisterOffset =	0x34,
+  DAC960_V5_CommandOpcodeRegisterOffset =	0x50,
+  DAC960_V5_CommandIdentifierRegisterOffset =	0x51,
+  DAC960_V5_MailboxRegister2Offset =		0x52,
+  DAC960_V5_MailboxRegister3Offset =		0x53,
+  DAC960_V5_MailboxRegister4Offset =		0x54,
+  DAC960_V5_MailboxRegister5Offset =		0x55,
+  DAC960_V5_MailboxRegister6Offset =		0x56,
+  DAC960_V5_MailboxRegister7Offset =		0x57,
+  DAC960_V5_MailboxRegister8Offset =		0x58,
+  DAC960_V5_MailboxRegister9Offset =		0x59,
+  DAC960_V5_MailboxRegister10Offset =		0x5A,
+  DAC960_V5_MailboxRegister11Offset =		0x5B,
+  DAC960_V5_MailboxRegister12Offset =		0x5C,
+  DAC960_V5_StatusCommandIdentifierRegOffset =	0x5D,
+  DAC960_V5_StatusRegisterOffset =		0x5E
+}
+DAC960_V5_RegisterOffsets_T;
+
+
+/*
+  Define the structure of the DAC960 V5 Inbound Door Bell Register.
+*/
+
+typedef union DAC960_V5_InboundDoorBellRegister
+{
+  unsigned char All;
+  struct {
+    boolean HardwareMailboxNewCommand:1;		/* Bit 0 */
+    boolean AcknowledgeHardwareMailboxStatus:1;		/* Bit 1 */
+    boolean GenerateInterrupt:1;			/* Bit 2 */
+    boolean ControllerReset:1;				/* Bit 3 */
+    boolean MemoryMailboxNewCommand:1;			/* Bit 4 */
+    unsigned char :3;					/* Bits 5-7 */
+  } Write;
+  struct {
+    boolean HardwareMailboxEmpty:1;			/* Bit 0 */
+    unsigned char :7;					/* Bits 1-7 */
+  } Read;
+}
+DAC960_V5_InboundDoorBellRegister_T;
+
+
+/*
+  Define the structure of the DAC960 V5 Outbound Door Bell Register.
+*/
+
+typedef union DAC960_V5_OutboundDoorBellRegister
+{
+  unsigned char All;
+  struct {
+    boolean AcknowledgeHardwareMailboxInterrupt:1;	/* Bit 0 */
+    boolean AcknowledgeMemoryMailboxInterrupt:1;	/* Bit 1 */
+    unsigned char :6;					/* Bits 2-7 */
+  } Write;
+  struct {
+    boolean HardwareMailboxStatusAvailable:1;		/* Bit 0 */
+    boolean MemoryMailboxStatusAvailable:1;		/* Bit 1 */
+    unsigned char :6;					/* Bits 2-7 */
+  } Read;
+}
+DAC960_V5_OutboundDoorBellRegister_T;
+
+
+/*
+  Define the structure of the DAC960 V5 Interrupt Mask Register.
+*/
+
+typedef union DAC960_V5_InterruptMaskRegister
+{
+  unsigned char All;
+  struct {
+    unsigned char :2;					/* Bits 0-1 */
+    boolean DisableInterrupts:1;			/* Bit 2 */
+    unsigned char :5;					/* Bits 3-7 */
+  } Bits;
+}
+DAC960_V5_InterruptMaskRegister_T;
+
+
+/*
+  Define the DAC960 V4 Controller Interface Register Offsets.
+*/
+
+#define DAC960_V4_RegisterWindowSize		0x2000
+
+typedef enum
+{
+  DAC960_V4_InboundDoorBellRegisterOffset =	0x0020,
+  DAC960_V4_OutboundDoorBellRegisterOffset =	0x002C,
+  DAC960_V4_InterruptMaskRegisterOffset =	0x0034,
+  DAC960_V4_CommandOpcodeRegisterOffset =	0x1000,
+  DAC960_V4_CommandIdentifierRegisterOffset =	0x1001,
+  DAC960_V4_MailboxRegister2Offset =		0x1002,
+  DAC960_V4_MailboxRegister3Offset =		0x1003,
+  DAC960_V4_MailboxRegister4Offset =		0x1004,
+  DAC960_V4_MailboxRegister5Offset =		0x1005,
+  DAC960_V4_MailboxRegister6Offset =		0x1006,
+  DAC960_V4_MailboxRegister7Offset =		0x1007,
+  DAC960_V4_MailboxRegister8Offset =		0x1008,
+  DAC960_V4_MailboxRegister9Offset =		0x1009,
+  DAC960_V4_MailboxRegister10Offset =		0x100A,
+  DAC960_V4_MailboxRegister11Offset =		0x100B,
+  DAC960_V4_MailboxRegister12Offset =		0x100C,
+  DAC960_V4_StatusCommandIdentifierRegOffset =	0x1018,
+  DAC960_V4_StatusRegisterOffset =		0x101A
+}
+DAC960_V4_RegisterOffsets_T;
+
+
+/*
+  Define the structure of the DAC960 V4 Inbound Door Bell Register.
+*/
+
+typedef union DAC960_V4_InboundDoorBellRegister
+{
+  unsigned int All;
+  struct {
+    boolean HardwareMailboxNewCommand:1;		/* Bit 0 */
+    boolean AcknowledgeHardwareMailboxStatus:1;		/* Bit 1 */
+    boolean GenerateInterrupt:1;			/* Bit 2 */
+    boolean ControllerReset:1;				/* Bit 3 */
+    boolean MemoryMailboxNewCommand:1;			/* Bit 4 */
+    unsigned int :27;					/* Bits 5-31 */
+  } Write;
+  struct {
+    boolean HardwareMailboxFull:1;			/* Bit 0 */
+    unsigned int :31;					/* Bits 1-31 */
+  } Read;
+}
+DAC960_V4_InboundDoorBellRegister_T;
+
+
+/*
+  Define the structure of the DAC960 V4 Outbound Door Bell Register.
+*/
+
+typedef union DAC960_V4_OutboundDoorBellRegister
+{
+  unsigned int All;
+  struct {
+    boolean AcknowledgeHardwareMailboxInterrupt:1;	/* Bit 0 */
+    boolean AcknowledgeMemoryMailboxInterrupt:1;	/* Bit 1 */
+    unsigned int :30;					/* Bits 2-31 */
+  } Write;
+  struct {
+    boolean HardwareMailboxStatusAvailable:1;		/* Bit 0 */
+    boolean MemoryMailboxStatusAvailable:1;		/* Bit 1 */
+    unsigned int :30;					/* Bits 2-31 */
+  } Read;
+}
+DAC960_V4_OutboundDoorBellRegister_T;
+
+
+/*
+  Define the structure of the DAC960 V4 Interrupt Mask Register.
+*/
+
+typedef union DAC960_V4_InterruptMaskRegister
+{
+  unsigned int All;
+  struct {
+    unsigned int MessageUnitInterruptMask1:2;		/* Bits 0-1 */
+    boolean DisableInterrupts:1;			/* Bit 2 */
+    unsigned int MessageUnitInterruptMask2:5;		/* Bits 3-7 */
+    unsigned int Reserved0:24;				/* Bits 8-31 */
+  } Bits;
+}
+DAC960_V4_InterruptMaskRegister_T;
+
+
+/*
+  Define the DAC960 V3 Controller Interface Register Offsets.
+*/
+
+#define DAC960_V3_RegisterWindowSize		0x80
+
+typedef enum
+{
+  DAC960_V3_CommandOpcodeRegisterOffset =	0x00,
+  DAC960_V3_CommandIdentifierRegisterOffset =	0x01,
+  DAC960_V3_MailboxRegister2Offset =		0x02,
+  DAC960_V3_MailboxRegister3Offset =		0x03,
+  DAC960_V3_MailboxRegister4Offset =		0x04,
+  DAC960_V3_MailboxRegister5Offset =		0x05,
+  DAC960_V3_MailboxRegister6Offset =		0x06,
+  DAC960_V3_MailboxRegister7Offset =		0x07,
+  DAC960_V3_MailboxRegister8Offset =		0x08,
+  DAC960_V3_MailboxRegister9Offset =		0x09,
+  DAC960_V3_MailboxRegister10Offset =		0x0A,
+  DAC960_V3_MailboxRegister11Offset =		0x0B,
+  DAC960_V3_MailboxRegister12Offset =		0x0C,
+  DAC960_V3_StatusCommandIdentifierRegOffset =	0x0D,
+  DAC960_V3_StatusRegisterOffset =		0x0E,
+  DAC960_V3_InboundDoorBellRegisterOffset =	0x40,
+  DAC960_V3_OutboundDoorBellRegisterOffset =	0x41,
+  DAC960_V3_InterruptEnableRegisterOffset =	0x43
+}
+DAC960_V3_RegisterOffsets_T;
+
+
+/*
+  Define the structure of the DAC960 V3 Inbound Door Bell Register.
+*/
+
+typedef union DAC960_V3_InboundDoorBellRegister
+{
+  unsigned char All;
+  struct {
+    boolean NewCommand:1;				/* Bit 0 */
+    boolean AcknowledgeStatus:1;			/* Bit 1 */
+    boolean GenerateInterrupt:1;			/* Bit 2 */
+    boolean ControllerReset:1;				/* Bit 3 */
+    unsigned char :4;					/* Bits 4-7 */
+  } Write;
+  struct {
+    boolean MailboxFull:1;				/* Bit 0 */
+    unsigned char :7;					/* Bits 1-7 */
+  } Read;
+}
+DAC960_V3_InboundDoorBellRegister_T;
+
+
+/*
+  Define the structure of the DAC960 V3 Outbound Door Bell Register.
+*/
+
+typedef union DAC960_V3_OutboundDoorBellRegister
+{
+  unsigned char All;
+  struct {
+    boolean AcknowledgeInterrupt:1;			/* Bit 0 */
+    unsigned char :7;					/* Bits 1-7 */
+  } Write;
+  struct {
+    boolean StatusAvailable:1;				/* Bit 0 */
+    unsigned char :7;					/* Bits 1-7 */
+  } Read;
+}
+DAC960_V3_OutboundDoorBellRegister_T;
+
+
+/*
+  Define the structure of the DAC960 V3 Interrupt Enable Register.
+*/
+
+typedef union DAC960_V3_InterruptEnableRegister
+{
+  unsigned char All;
+  struct {
+    boolean EnableInterrupts:1;				/* Bit 0 */
+    unsigned char :7;					/* Bits 1-7 */
+  } Bits;
+}
+DAC960_V3_InterruptEnableRegister_T;
+
+
+/*
+  Define the DAC960 Command Identifier type.
+*/
+
+typedef unsigned char DAC960_CommandIdentifier_T;
+
+
+/*
+  Define the DAC960 Command Opcodes.
+*/
+
+typedef enum
+{
+  /* I/O Commands */
+  DAC960_ReadExtended =				0x33,
+  DAC960_WriteExtended =			0x34,
+  DAC960_ReadAheadExtended =			0x35,
+  DAC960_ReadExtendedWithScatterGather =	0xB3,
+  DAC960_WriteExtendedWithScatterGather =	0xB4,
+  DAC960_Read =					0x36,
+  DAC960_ReadWithOldScatterGather =		0xB6,
+  DAC960_Write =				0x37,
+  DAC960_WriteWithOldScatterGather =		0xB7,
+  DAC960_DCDB =					0x04,
+  DAC960_DCDBWithScatterGather =		0x84,
+  DAC960_Flush =				0x0A,
+  /* Controller Status Related Commands */
+  DAC960_Enquiry =				0x53,
+  DAC960_Enquiry2 =				0x1C,
+  DAC960_GetLogicalDriveElement =		0x55,
+  DAC960_GetLogicalDriveInformation =		0x19,
+  DAC960_IOPortRead =				0x39,
+  DAC960_IOPortWrite =				0x3A,
+  DAC960_GetSDStats =				0x3E,
+  DAC960_GetPDStats =				0x3F,
+  DAC960_PerformEventLogOperation =		0x72,
+  /* Device Related Commands */
+  DAC960_StartDevice =				0x10,
+  DAC960_GetDeviceState =			0x50,
+  DAC960_StopChannel =				0x13,
+  DAC960_StartChannel =				0x12,
+  DAC960_ResetChannel =				0x1A,
+  /* Commands Associated with Data Consistency and Errors */
+  DAC960_Rebuild =				0x09,
+  DAC960_RebuildAsync =				0x16,
+  DAC960_CheckConsistency =			0x0F,
+  DAC960_CheckConsistencyAsync =		0x1E,
+  DAC960_RebuildStat =				0x0C,
+  DAC960_GetRebuildProgress =			0x27,
+  DAC960_RebuildControl =			0x1F,
+  DAC960_ReadBadBlockTable =			0x0B,
+  DAC960_ReadBadDataTable =			0x25,
+  DAC960_ClearBadDataTable =			0x26,
+  DAC960_GetErrorTable =			0x17,
+  DAC960_AddCapacityAsync =			0x2A,
+  /* Configuration Related Commands */
+  DAC960_ReadConfig2 =				0x3D,
+  DAC960_WriteConfig2 =				0x3C,
+  DAC960_ReadConfigurationOnDisk =		0x4A,
+  DAC960_WriteConfigurationOnDisk =		0x4B,
+  DAC960_ReadConfiguration =			0x4E,
+  DAC960_ReadBackupConfiguration =		0x4D,
+  DAC960_WriteConfiguration =			0x4F,
+  DAC960_AddConfiguration =			0x4C,
+  DAC960_ReadConfigurationLabel =		0x48,
+  DAC960_WriteConfigurationLabel =		0x49,
+  /* Firmware Upgrade Related Commands */
+  DAC960_LoadImage =				0x20,
+  DAC960_StoreImage =				0x21,
+  DAC960_ProgramImage =				0x22,
+  /* Diagnostic Commands */
+  DAC960_SetDiagnosticMode =			0x31,
+  DAC960_RunDiagnostic =			0x32,
+  /* Subsystem Service Commands */
+  DAC960_GetSubsystemData =			0x70,
+  DAC960_SetSubsystemParameters =		0x71
+}
+__attribute__ ((packed))
+DAC960_CommandOpcode_T;
+
+
+/*
+  Define the DAC960 Command Status Codes.
+*/
+
+#define DAC960_NormalCompletion			0x0000	/* Common */
+#define DAC960_CheckConditionReceived		0x0002	/* Common */
+#define DAC960_NoDeviceAtAddress		0x0102	/* Common */
+#define DAC960_InvalidDeviceAddress		0x0105	/* Common */
+#define DAC960_InvalidParameter			0x0105	/* Common */
+#define DAC960_IrrecoverableDataError		0x0001	/* I/O */
+#define DAC960_LogicalDriveNonexistentOrOffline	0x0002	/* I/O */
+#define DAC960_AccessBeyondEndOfLogicalDrive	0x0105	/* I/O */
+#define DAC960_BadDataEncountered		0x010C	/* I/O */
+#define DAC960_DeviceBusy			0x0008	/* DCDB */
+#define DAC960_DeviceNonresponsive		0x000E	/* DCDB */
+#define DAC960_CommandTerminatedAbnormally	0x000F	/* DCDB */
+#define DAC960_UnableToStartDevice		0x0002	/* Device */
+#define DAC960_InvalidChannelOrTargetOrModifier	0x0105	/* Device */
+#define DAC960_ChannelBusy			0x0106	/* Device */
+#define DAC960_ChannelNotStopped		0x0002	/* Device */
+#define DAC960_AttemptToRebuildOnlineDrive	0x0002	/* Consistency */
+#define DAC960_RebuildBadBlocksEncountered	0x0003	/* Consistency */
+#define DAC960_NewDiskFailedDuringRebuild	0x0004	/* Consistency */
+#define DAC960_RebuildOrCheckAlreadyInProgress	0x0106	/* Consistency */
+#define DAC960_DependentDiskIsDead		0x0002	/* Consistency */
+#define DAC960_InconsistentBlocksFound		0x0003	/* Consistency */
+#define DAC960_InvalidOrNonredundantLogicalDrive 0x0105	/* Consistency */
+#define DAC960_NoRebuildOrCheckInProgress	0x0105	/* Consistency */
+#define DAC960_RebuildInProgress_DataValid	0x0000	/* Consistency */
+#define DAC960_RebuildFailed_LogicalDriveFailure 0x0002	/* Consistency */
+#define DAC960_RebuildFailed_BadBlocksOnOther	0x0003	/* Consistency */
+#define DAC960_RebuildFailed_NewDriveFailed	0x0004	/* Consistency */
+#define DAC960_RebuildSuccessful		0x0100	/* Consistency */
+#define DAC960_AddCapacityInProgress		0x0004	/* Consistency */
+#define DAC960_AddCapacityFailedOrSuspended	0x00F4	/* Consistency */
+#define DAC960_Config2ChecksumError		0x0002	/* Configuration */
+#define DAC960_ConfigurationSuspended		0x0106	/* Configuration */
+#define DAC960_FailedToConfigureNVRAM		0x0105	/* Configuration */
+#define DAC960_ConfigurationNotSavedStateChange	0x0106	/* Configuration */
+#define DAC960_SubsystemNotInstalled		0x0001	/* Subsystem */
+#define DAC960_SubsystemFailed			0x0002	/* Subsystem */
+#define DAC960_SubsystemBusy			0x0106	/* Subsystem */
+
+typedef unsigned short DAC960_CommandStatus_T;
+
+
+/*
+  Define the Enquiry reply structure.
+*/
+
+typedef struct DAC960_Enquiry
+{
+  unsigned char NumberOfLogicalDrives;			/* Byte 0 */
+  unsigned int :24;					/* Bytes 1-3 */
+  unsigned int LogicalDriveSizes[32];			/* Bytes 4-131 */
+  unsigned short FlashAge;				/* Bytes 132-133 */
+  struct {
+    boolean DeferredWriteError:1;			/* Byte 134 Bit 0 */
+    boolean BatteryLow:1;				/* Byte 134 Bit 1 */
+    unsigned char :6;					/* Byte 134 Bits 2-7 */
+  } StatusFlags;
+  unsigned char :8;					/* Byte 135 */
+  unsigned char MinorFirmwareVersion;			/* Byte 136 */
+  unsigned char MajorFirmwareVersion;			/* Byte 137 */
+  enum {
+    DAC960_NoStandbyRebuildOrCheckInProgress =			0x00,
+    DAC960_StandbyRebuildInProgress =				0x01,
+    DAC960_BackgroundRebuildInProgress =			0x02,
+    DAC960_BackgroundCheckInProgress =				0x03,
+    DAC960_StandbyRebuildCompletedWithError =			0xFF,
+    DAC960_BackgroundRebuildOrCheckFailed_DriveFailed =		0xF0,
+    DAC960_BackgroundRebuildOrCheckFailed_LogicalDriveFailed =	0xF1,
+    DAC960_BackgroundRebuildOrCheckFailed_OtherCauses =		0xF2,
+    DAC960_BackgroundRebuildOrCheckSuccessfullyTerminated =	0xF3
+  } __attribute__ ((packed)) RebuildFlag;		/* Byte 138 */
+  unsigned char MaxCommands;				/* Byte 139 */
+  unsigned char OfflineLogicalDriveCount;		/* Byte 140 */
+  unsigned char :8;					/* Byte 141 */
+  unsigned short EventLogSequenceNumber;		/* Bytes 142-143 */
+  unsigned char CriticalLogicalDriveCount;		/* Byte 144 */
+  unsigned int :24;					/* Bytes 145-147 */
+  unsigned char DeadDriveCount;				/* Byte 148 */
+  unsigned char :8;					/* Byte 149 */
+  unsigned char RebuildCount;				/* Byte 150 */
+  struct {
+    unsigned char :3;					/* Byte 151 Bits 0-2 */
+    boolean BatteryBackupUnitPresent:1;			/* Byte 151 Bit 3 */
+    unsigned char :3;					/* Byte 151 Bits 4-6 */
+    unsigned char :1;					/* Byte 151 Bit 7 */
+  } MiscFlags;
+  struct {
+    unsigned char TargetID;
+    unsigned char Channel;
+  } DeadDrives[21];					/* Bytes 152-194 */
+  unsigned char Reserved[62];				/* Bytes 195-255 */
+}
+__attribute__ ((packed))
+DAC960_Enquiry_T;
+
+
+/*
+  Define the Enquiry2 reply structure.
+*/
+
+typedef struct DAC960_Enquiry2
+{
+  struct {
+    enum {
+      DAC960_P_PD_PU =				0x01,
+      DAC960_PL =				0x02,
+      DAC960_PG =				0x10,
+      DAC960_PJ =				0x11,
+      DAC960_PR =				0x12,
+      DAC960_PT =				0x13,
+      DAC960_PTL0 =				0x14,
+      DAC960_PRL =				0x15,
+      DAC960_PTL1 =				0x16,
+      DAC1164_P =				0x20
+    } __attribute__ ((packed)) SubModel;		/* Byte 0 */
+    unsigned char ActualChannels;			/* Byte 1 */
+    enum {
+      DAC960_FiveChannelBoard =			0x01,
+      DAC960_ThreeChannelBoard =		0x02,
+      DAC960_TwoChannelBoard =			0x03,
+      DAC960_ThreeChannelASIC_DAC =		0x04
+    } __attribute__ ((packed)) Model;			/* Byte 2 */
+    enum {
+      DAC960_EISA_Controller =			0x01,
+      DAC960_MicroChannel_Controller =		0x02,
+      DAC960_PCI_Controller =			0x03,
+      DAC960_SCSItoSCSI_Controller =		0x08
+    } __attribute__ ((packed)) ProductFamily;		/* Byte 3 */
+  } HardwareID;						/* Bytes 0-3 */
+  /* MajorVersion.MinorVersion-FirmwareType-TurnID */
+  struct {
+    unsigned char MajorVersion;				/* Byte 4 */
+    unsigned char MinorVersion;				/* Byte 5 */
+    unsigned char TurnID;				/* Byte 6 */
+    char FirmwareType;					/* Byte 7 */
+  } FirmwareID;						/* Bytes 4-7 */
+  unsigned char :8;					/* Byte 8 */
+  unsigned int :24;					/* Bytes 9-11 */
+  unsigned char ConfiguredChannels;			/* Byte 12 */
+  unsigned char ActualChannels;				/* Byte 13 */
+  unsigned char MaxTargets;				/* Byte 14 */
+  unsigned char MaxTags;				/* Byte 15 */
+  unsigned char MaxLogicalDrives;			/* Byte 16 */
+  unsigned char MaxArms;				/* Byte 17 */
+  unsigned char MaxSpans;				/* Byte 18 */
+  unsigned char :8;					/* Byte 19 */
+  unsigned int :32;					/* Bytes 20-23 */
+  unsigned int MemorySize;				/* Bytes 24-27 */
+  unsigned int CacheSize;				/* Bytes 28-31 */
+  unsigned int FlashMemorySize;				/* Bytes 32-35 */
+  unsigned int NonVolatileMemorySize;			/* Bytes 36-39 */
+  struct {
+    enum {
+      DAC960_DRAM =				0x00,
+      DAC960_EDO =				0x01,
+      DAC960_SDRAM =				0x02
+    } __attribute__ ((packed)) RamType:3;		/* Byte 40 Bits 0-2 */
+    enum {
+      DAC960_None =				0x00,
+      DAC960_Parity =				0x01,
+      DAC960_ECC =				0x02
+    } __attribute__ ((packed)) ErrorCorrection:3;	/* Byte 40 Bits 3-5 */
+    boolean FastPageMode:1;				/* Byte 40 Bit 6 */
+    boolean LowPowerMemory:1;				/* Byte 40 Bit 7 */
+    unsigned char :8;					/* Bytes 41 */
+  } MemoryType;
+  unsigned short ClockSpeed;				/* Bytes 42-43 */
+  unsigned short MemorySpeed;				/* Bytes 44-45 */
+  unsigned short HardwareSpeed;				/* Bytes 46-47 */
+  unsigned int :32;					/* Bytes 48-51 */
+  unsigned int :32;					/* Bytes 52-55 */
+  unsigned char :8;					/* Byte 56 */
+  unsigned char :8;					/* Byte 57 */
+  unsigned short :16;					/* Bytes 58-59 */
+  unsigned short MaxCommands;				/* Bytes 60-61 */
+  unsigned short MaxScatterGatherEntries;		/* Bytes 62-63 */
+  unsigned short MaxDriveCommands;			/* Bytes 64-65 */
+  unsigned short MaxIODescriptors;			/* Bytes 66-67 */
+  unsigned short MaxCombinedSectors;			/* Bytes 68-69 */
+  unsigned char Latency;				/* Byte 70 */
+  unsigned char :8;					/* Byte 71 */
+  unsigned char SCSITimeout;				/* Byte 72 */
+  unsigned char :8;					/* Byte 73 */
+  unsigned short MinFreeLines;				/* Bytes 74-75 */
+  unsigned int :32;					/* Bytes 76-79 */
+  unsigned int :32;					/* Bytes 80-83 */
+  unsigned char RebuildRateConstant;			/* Byte 84 */
+  unsigned char :8;					/* Byte 85 */
+  unsigned char :8;					/* Byte 86 */
+  unsigned char :8;					/* Byte 87 */
+  unsigned int :32;					/* Bytes 88-91 */
+  unsigned int :32;					/* Bytes 92-95 */
+  unsigned short PhysicalDriveBlockSize;		/* Bytes 96-97 */
+  unsigned short LogicalDriveBlockSize;			/* Bytes 98-99 */
+  unsigned short MaxBlocksPerCommand;			/* Bytes 100-101 */
+  unsigned short BlockFactor;				/* Bytes 102-103 */
+  unsigned short CacheLineSize;				/* Bytes 104-105 */
+  struct {
+    enum {
+      DAC960_Narrow_8bit =			0x00,
+      DAC960_Wide_16bit =			0x01,
+      DAC960_Wide_32bit =			0x02
+    } __attribute__ ((packed)) BusWidth:2;		/* Byte 106 Bits 0-1 */
+    enum {
+      DAC960_Fast =				0x00,
+      DAC960_Ultra =				0x01,
+      DAC960_Ultra2 =				0x02
+    } __attribute__ ((packed)) BusSpeed:2;		/* Byte 106 Bits 2-3 */
+    boolean Differential:1;				/* Byte 106 Bit 4 */
+    unsigned char :3;					/* Byte 106 Bits 5-7 */
+  } SCSICapability;
+  unsigned char :8;					/* Byte 107 */
+  unsigned int :32;					/* Bytes 108-111 */
+  unsigned short FirmwareBuildNumber;			/* Bytes 112-113 */
+  enum {
+    DAC960_AEMI =				0x01,
+    DAC960_OEM1 =				0x02,
+    DAC960_OEM2 =				0x04,
+    DAC960_OEM3 =				0x08,
+    DAC960_Conner =				0x10,
+    DAC960_SAFTE =				0x20
+  } __attribute__ ((packed)) FaultManagementType;	/* Byte 114 */
+  unsigned char :8;					/* Byte 115 */
+  struct {
+    boolean Clustering:1;				/* Byte 116 Bit 0 */
+    boolean MylexOnlineRAIDExpansion:1;			/* Byte 116 Bit 1 */
+    unsigned int :30;					/* Bytes 116-119 */
+  } FirmwareFeatures;
+  unsigned int :32;					/* Bytes 120-123 */
+  unsigned int :32;					/* Bytes 124-127 */
+}
+DAC960_Enquiry2_T;
+
+
+/*
+  Define the Logical Drive State type.
+*/
+
+typedef enum
+{
+    DAC960_LogicalDrive_Online =		0x03,
+    DAC960_LogicalDrive_Critical =		0x04,
+    DAC960_LogicalDrive_Offline =		0xFF
+}
+__attribute__ ((packed))
+DAC960_LogicalDriveState_T;
+
+
+/*
+  Define the Get Logical Drive Information reply structure.
+*/
+
+typedef struct DAC960_LogicalDriveInformation
+{
+  unsigned int LogicalDriveSize;			/* Bytes 0-3 */
+  DAC960_LogicalDriveState_T LogicalDriveState;		/* Byte 4 */
+  unsigned char RAIDLevel:7;				/* Byte 5 Bits 0-6 */
+  boolean WriteBack:1;					/* Byte 5 Bit 7 */
+  unsigned int :16;					/* Bytes 6-7 */
+}
+DAC960_LogicalDriveInformation_T;
+
+
+/*
+  Define the Perform Event Log Operation Types.
+*/
+
+typedef enum
+{
+  DAC960_GetEventLogEntry =			0x00
+}
+__attribute__ ((packed))
+DAC960_PerformEventLogOpType_T;
+
+
+/*
+  Define the Get Event Log Entry reply structure.
+*/
+
+typedef struct DAC960_EventLogEntry
+{
+  unsigned char MessageType;				/* Byte 0 */
+  unsigned char MessageLength;				/* Byte 1 */
+  unsigned char TargetID:5;				/* Byte 2 Bits 0-4 */
+  unsigned char Channel:3;				/* Byte 2 Bits 5-7 */
+  unsigned char LogicalUnit:6;				/* Byte 3 Bits 0-5 */
+  unsigned char :2;					/* Byte 3 Bits 6-7 */
+  unsigned short SequenceNumber;			/* Bytes 4-5 */
+  unsigned char ErrorCode:7;				/* Byte 6 Bits 0-6 */
+  boolean Valid:1;					/* Byte 6 Bit 7 */
+  unsigned char SegmentNumber;				/* Byte 7 */
+  unsigned char SenseKey:4;				/* Byte 8 Bits 0-3 */
+  unsigned char :1;					/* Byte 8 Bit 4 */
+  boolean ILI:1;					/* Byte 8 Bit 5 */
+  boolean EOM:1;					/* Byte 8 Bit 6 */
+  boolean Filemark:1;					/* Byte 8 Bit 7 */
+  unsigned char Information[4];				/* Bytes 9-12 */
+  unsigned char AdditionalSenseLength;			/* Byte 13 */
+  unsigned char CommandSpecificInformation[4];		/* Bytes 14-17 */
+  unsigned char AdditionalSenseCode;			/* Byte 18 */
+  unsigned char AdditionalSenseCodeQualifier;		/* Byte 19 */
+  unsigned char Dummy[12];				/* Bytes 20-31 */
+}
+DAC960_EventLogEntry_T;
+
+#define DAC960_EventMessagesCount		13
+
+static char
+  *DAC960_EventMessages[DAC960_EventMessagesCount] =
+     { "killed because write recovery failed",
+       "killed because of SCSI bus reset failure",
+       "killed because of double check condition",
+       "killed because it was removed",
+       "killed because of gross error on SCSI chip",
+       "killed because of bad tag returned from drive",
+       "killed because of timeout on SCSI command",
+       "killed because of reset SCSI command issued from system",
+       "killed because busy or parity error count exceeded limit",
+       "killed because of 'kill drive' command from system",
+       "killed because of selection timeout",
+       "killed due to SCSI phase sequence error",
+       "killed due to unknown status" };
+
+
+/*
+  Define the Physical Device State type.
+*/
+
+typedef enum
+{
+    DAC960_Device_Dead =			0x00,
+    DAC960_Device_WriteOnly =			0x02,
+    DAC960_Device_Online =			0x03,
+    DAC960_Device_Standby =			0x10
+}
+__attribute__ ((packed))
+DAC960_PhysicalDeviceState_T;
+
+
+/*
+  Define the Get Device State reply structure.
+*/
+
+typedef struct DAC960_DeviceState
+{
+  boolean Present:1;					/* Byte 0 Bit 0 */
+  unsigned char :7;					/* Byte 0 Bits 1-7 */
+  enum {
+    DAC960_OtherType =				0x00,
+    DAC960_DiskType =				0x01,
+    DAC960_SequentialType =			0x02,
+    DAC960_CDROM_or_WORM_Type =			0x03
+    } __attribute__ ((packed)) DeviceType:2;		/* Byte 1 Bits 0-1 */
+  boolean :1;						/* Byte 1 Bit 2 */
+  boolean Fast20:1;					/* Byte 1 Bit 3 */
+  boolean Sync:1;					/* Byte 1 Bit 4 */
+  boolean Fast:1;					/* Byte 1 Bit 5 */
+  boolean Wide:1;					/* Byte 1 Bit 6 */
+  boolean TaggedQueuingSupported:1;			/* Byte 1 Bit 7 */
+  DAC960_PhysicalDeviceState_T DeviceState;		/* Byte 2 */
+  unsigned char :8;					/* Byte 3 */
+  unsigned char SynchronousMultiplier;			/* Byte 4 */
+  unsigned char SynchronousOffset:5;			/* Byte 5 Bits 0-4 */
+  unsigned char :3;					/* Byte 5 Bits 5-7 */
+  unsigned long DiskSize __attribute__ ((packed));	/* Bytes 6-9 */
+}
+DAC960_DeviceState_T;
+
+
+/*
+  Define the Get Rebuild Progress reply structure.
+*/
+
+typedef struct DAC960_RebuildProgress
+{
+  unsigned int LogicalDriveNumber;			/* Bytes 0-3 */
+  unsigned int LogicalDriveSize;			/* Bytes 4-7 */
+  unsigned int RemainingBlocks;				/* Bytes 8-11 */
+}
+DAC960_RebuildProgress_T;
+
+
+/*
+  Define the Error Table Entry and Get Error Table reply structure.
+*/
+
+typedef struct DAC960_ErrorTableEntry
+{
+  unsigned char ParityErrorCount;			/* Byte 0 */
+  unsigned char SoftErrorCount;				/* Byte 1 */
+  unsigned char HardErrorCount;				/* Byte 2 */
+  unsigned char MiscErrorCount;				/* Byte 3 */
+}
+DAC960_ErrorTableEntry_T;
+
+
+/*
+  Define the Get Error Table reply structure.
+*/
+
+typedef struct DAC960_ErrorTable
+{
+  DAC960_ErrorTableEntry_T
+    ErrorTableEntries[DAC960_MaxChannels][DAC960_MaxTargets];
+}
+DAC960_ErrorTable_T;
+
+
+/*
+  Define the Config2 reply structure.
+*/
+
+typedef struct DAC960_Config2
+{
+  unsigned char :1;					/* Byte 0 Bit 0 */
+  boolean ActiveNegationEnabled:1;			/* Byte 0 Bit 1 */
+  unsigned char :5;					/* Byte 0 Bits 2-6 */
+  boolean NoRescanIfResetReceivedDuringScan:1;		/* Byte 0 Bit 7 */
+  boolean StorageWorksSupportEnabled:1;			/* Byte 1 Bit 0 */
+  boolean HewlettPackardSupportEnabled:1;		/* Byte 1 Bit 1 */
+  boolean NoDisconnectOnFirstCommand:1;			/* Byte 1 Bit 2 */
+  unsigned char :2;					/* Byte 1 Bits 3-4 */
+  boolean AEMI_ARM:1;					/* Byte 1 Bit 5 */
+  boolean AEMI_OFM:1;					/* Byte 1 Bit 6 */
+  unsigned char :1;					/* Byte 1 Bit 7 */
+  enum {
+    DAC960_OEMID_Mylex =			0x00,
+    DAC960_OEMID_IBM =				0x08,
+    DAC960_OEMID_HP =				0x0A,
+    DAC960_OEMID_DEC =				0x0C,
+    DAC960_OEMID_Siemens =			0x10,
+    DAC960_OEMID_Intel =			0x12
+  } __attribute__ ((packed)) OEMID;			/* Byte 2 */
+  unsigned char OEMModelNumber;				/* Byte 3 */
+  unsigned char PhysicalSector;				/* Byte 4 */
+  unsigned char LogicalSector;				/* Byte 5 */
+  unsigned char BlockFactor;				/* Byte 6 */
+  boolean ReadAheadEnabled:1;				/* Byte 7 Bit 0 */
+  boolean LowBIOSDelay:1;				/* Byte 7 Bit 1 */
+  unsigned char :2;					/* Byte 7 Bits 2-3 */
+  boolean ReassignRestrictedToOneSector:1;		/* Byte 7 Bit 4 */
+  unsigned char :1;					/* Byte 7 Bit 5 */
+  boolean ForceUnitAccessDuringWriteRecovery:1;		/* Byte 7 Bit 6 */
+  boolean EnableLeftSymmetricRAID5Algorithm:1;		/* Byte 7 Bit 7 */
+  unsigned char DefaultRebuildRate;			/* Byte 8 */
+  unsigned char :8;					/* Byte 9 */
+  unsigned char BlocksPerCacheLine;			/* Byte 10 */
+  unsigned char BlocksPerStripe;			/* Byte 11 */
+  struct {
+    enum {
+      DAC960_Async =				0x00,
+      DAC960_Sync_8MHz =			0x01,
+      DAC960_Sync_5MHz =			0x02,
+      DAC960_Sync_10or20MHz =			0x03	/* Bits 0-1 */
+    } __attribute__ ((packed)) Speed:2;
+    boolean Force8Bit:1;				/* Bit 2 */
+    boolean DisableFast20:1;				/* Bit 3 */
+    unsigned char :3;					/* Bits 4-6 */
+    boolean EnableTaggedQueuing:1;			/* Bit 7 */
+  } __attribute__ ((packed)) ChannelParameters[6];	/* Bytes 12-17 */
+  unsigned char SCSIInitiatorID;			/* Byte 18 */
+  unsigned char :8;					/* Byte 19 */
+  enum {
+    DAC960_StartupMode_ControllerSpinUp =	0x00,
+    DAC960_StartupMode_PowerOnSpinUp =		0x01
+  } __attribute__ ((packed)) StartupMode;		/* Byte 20 */
+  unsigned char SimultaneousDeviceSpinUpCount;		/* Byte 21 */
+  unsigned char SecondsDelayBetweenSpinUps;		/* Byte 22 */
+  unsigned char Reserved1[29];				/* Bytes 23-51 */
+  boolean BIOSDisabled:1;				/* Byte 52 Bit 0 */
+  boolean CDROMBootEnabled:1;				/* Byte 52 Bit 1 */
+  unsigned char :3;					/* Byte 52 Bits 2-4 */
+  enum {
+    DAC960_Geometry_128_32 =			0x00,
+    DAC960_Geometry_255_63 =			0x01,
+    DAC960_Geometry_Reserved1 =			0x02,
+    DAC960_Geometry_Reserved2 =			0x03
+  } __attribute__ ((packed)) DriveGeometry:2;		/* Byte 52 Bits 5-6 */
+  unsigned char :1;					/* Byte 52 Bit 7 */
+  unsigned char Reserved2[9];				/* Bytes 53-61 */
+  unsigned short Checksum;				/* Bytes 62-63 */
+}
+DAC960_Config2_T;
+
+
+/*
+  Define the Scatter/Gather List Type 1 32 Bit Address 32 Bit Byte Count
+  structure.
+*/
+
+typedef struct DAC960_ScatterGatherSegment
+{
+  DAC960_BusAddress_T SegmentDataPointer;		/* Bytes 0-3 */
+  DAC960_ByteCount_T SegmentByteCount;			/* Bytes 4-7 */
+}
+DAC960_ScatterGatherSegment_T;
+
+
+/*
+  Define the 13 Byte DAC960 Command Mailbox structure.  Bytes 13-15 are
+  not used.  The Command Mailbox structure is padded to 16 bytes for
+  efficient access.
+*/
+
+typedef union DAC960_CommandMailbox
+{
+  unsigned int Words[4];				/* Words 0-3 */
+  unsigned char Bytes[16];				/* Bytes 0-15 */
+  struct {
+    DAC960_CommandOpcode_T CommandOpcode;		/* Byte 0 */
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 1 */
+    unsigned char Dummy[14];				/* Bytes 2-15 */
+  } __attribute__ ((packed)) Common;
+  struct {
+    DAC960_CommandOpcode_T CommandOpcode;		/* Byte 0 */
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 1 */
+    unsigned char Dummy1[6];				/* Bytes 2-7 */
+    DAC960_BusAddress_T BusAddress;			/* Bytes 8-11 */
+    unsigned char Dummy2[4];				/* Bytes 12-15 */
+  } __attribute__ ((packed)) Type3;
+  struct {
+    DAC960_CommandOpcode_T CommandOpcode;		/* Byte 0 */
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 1 */
+    unsigned char Dummy1[5];				/* Bytes 2-6 */
+    unsigned char LogicalDriveNumber:6;			/* Byte 7 Bits 0-6 */
+    boolean AutoRestore:1;				/* Byte 7 Bit 7 */
+    unsigned char Dummy2[8];				/* Bytes 8-15 */
+  } __attribute__ ((packed)) Type3C;
+  struct {
+    DAC960_CommandOpcode_T CommandOpcode;		/* Byte 0 */
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 1 */
+    unsigned char Channel;				/* Byte 2 */
+    unsigned char TargetID;				/* Byte 3 */
+    DAC960_PhysicalDeviceState_T DeviceState:5;		/* Byte 4 Bits 0-4 */
+    unsigned char Modifier:3;				/* Byte 4 Bits 5-7 */
+    unsigned char Dummy1[3];				/* Bytes 5-7 */
+    DAC960_BusAddress_T BusAddress;			/* Bytes 8-11 */
+    unsigned char Dummy2[4];				/* Bytes 12-15 */
+  } __attribute__ ((packed)) Type3D;
+  struct {
+    DAC960_CommandOpcode_T CommandOpcode;		/* Byte 0 */
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 1 */
+    DAC960_PerformEventLogOpType_T OperationType;	/* Byte 2 */
+    unsigned char OperationQualifier;			/* Byte 3 */
+    unsigned short SequenceNumber;			/* Bytes 4-5 */
+    unsigned char Dummy1[2];				/* Bytes 6-7 */
+    DAC960_BusAddress_T BusAddress;			/* Bytes 8-11 */
+    unsigned char Dummy2[4];				/* Bytes 12-15 */
+  } __attribute__ ((packed)) Type3E;
+  struct {
+    DAC960_CommandOpcode_T CommandOpcode;		/* Byte 0 */
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 1 */
+    unsigned char Dummy1[2];				/* Bytes 2-3 */
+    unsigned char RebuildRateConstant;			/* Byte 4 */
+    unsigned char Dummy2[3];				/* Bytes 5-7 */
+    DAC960_BusAddress_T BusAddress;			/* Bytes 8-11 */
+    unsigned char Dummy3[4];				/* Bytes 12-15 */
+  } __attribute__ ((packed)) Type3R;
+  struct {
+    DAC960_CommandOpcode_T CommandOpcode;		/* Byte 0 */
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 1 */
+    struct {
+      unsigned short TransferLength:11;			/* Bytes 2-3 */
+      unsigned char LogicalDriveNumber:5;		/* Byte 3 Bits 3-7 */
+    } __attribute__ ((packed)) LD;
+    unsigned int LogicalBlockAddress;			/* Bytes 4-7 */
+    DAC960_BusAddress_T BusAddress;			/* Bytes 8-11 */
+    unsigned char ScatterGatherCount:6;			/* Byte 12 Bits 0-5 */
+    enum {
+      DAC960_ScatterGather_32BitAddress_32BitByteCount =	0x0,
+      DAC960_ScatterGather_32BitAddress_16BitByteCount =	0x1,
+      DAC960_ScatterGather_32BitByteCount_32BitAddress =	0x2,
+      DAC960_ScatterGather_16BitByteCount_32BitAddress =	0x3
+    } __attribute__ ((packed)) ScatterGatherType:2;	/* Byte 12 Bits 6-7 */
+    unsigned char Dummy[3];				/* Bytes 13-15 */
+  } __attribute__ ((packed)) Type5;
+  struct {
+    DAC960_CommandOpcode_T CommandOpcode;		/* Byte 0 */
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 1 */
+    unsigned char CommandOpcode2;			/* Byte 2 */
+    unsigned char :8;					/* Byte 3 */
+    DAC960_BusAddress_T CommandMailboxesBusAddress;	/* Bytes 4-7 */
+    DAC960_BusAddress_T StatusMailboxesBusAddress;	/* Bytes 8-11 */
+    unsigned char Dummy[4];				/* Bytes 12-15 */
+  } __attribute__ ((packed)) TypeX;
+}
+DAC960_CommandMailbox_T;
+
+
+/*
+  Define the DAC960 Controller Status Mailbox structure.
+*/
+
+typedef union DAC960_StatusMailbox
+{
+  unsigned int Word;					/* Bytes 0-3 */
+  struct {
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 0 */
+    unsigned char :7;					/* Byte 1 Bits 0-6 */
+    boolean Valid:1;					/* Byte 1 Bit 7 */
+    DAC960_CommandStatus_T CommandStatus;		/* Bytes 2-3 */
+  } Fields;
+}
+DAC960_StatusMailbox_T;
+
+
+/*
+  Define the DAC960 Driver Command Types.
+*/
+
+typedef enum
+{
+  DAC960_ReadCommand =				1,
+  DAC960_WriteCommand =				2,
+  DAC960_ReadRetryCommand =			3,
+  DAC960_WriteRetryCommand =			4,
+  DAC960_MonitoringCommand =			5,
+  DAC960_ImmediateCommand =			6
+}
+DAC960_CommandType_T;
+
+
+/*
+  Define the DAC960 Driver Command structure.
+*/
+
+typedef struct DAC960_Command
+{
+  DAC960_CommandType_T CommandType;
+  DAC960_CommandMailbox_T CommandMailbox;
+  DAC960_CommandStatus_T CommandStatus;
+  struct DAC960_Controller *Controller;
+  struct DAC960_Command *Next;
+  Semaphore_T *Semaphore;
+  unsigned int LogicalDriveNumber;
+  unsigned int BlockNumber;
+  unsigned int BlockCount;
+  unsigned int SegmentCount;
+  BufferHeader_T *BufferHeader;
+  DAC960_ScatterGatherSegment_T
+    ScatterGatherList[DAC960_MaxScatterGatherSegments];
+}
+DAC960_Command_T;
+
+
+/*
+  Define the DAC960 Driver Controller structure.
+*/
+
+typedef struct DAC960_Controller
+{
+  void *BaseAddress;
+  void *MemoryMappedAddress;
+  DAC960_ControllerType_T ControllerType;
+  DAC960_IO_Address_T IO_Address;
+  DAC960_PCI_Address_T PCI_Address;
+  unsigned char ControllerNumber;
+  unsigned char ControllerName[4];
+  unsigned char ModelName[12];
+  unsigned char FullModelName[18];
+  unsigned char FirmwareVersion[14];
+  unsigned char Bus;
+  unsigned char Device;
+  unsigned char Function;
+  unsigned char IRQ_Channel;
+  unsigned char Channels;
+  unsigned char MemorySize;
+  unsigned char LogicalDriveCount;
+  unsigned char GeometryTranslationHeads;
+  unsigned char GeometryTranslationSectors;
+  unsigned short ControllerQueueDepth;
+  unsigned short DriverQueueDepth;
+  unsigned short MaxBlocksPerCommand;
+  unsigned short MaxScatterGatherSegments;
+  unsigned short StripeSize;
+  unsigned short SegmentSize;
+  unsigned short NewEventLogSequenceNumber;
+  unsigned short OldEventLogSequenceNumber;
+  unsigned short InitialStatusLength;
+  unsigned short CurrentStatusLength;
+  unsigned short UserStatusLength;
+  unsigned short RebuildProgressLength;
+  unsigned int ControllerUsageCount;
+  unsigned int EnquiryIndex;
+  unsigned int LogicalDriveInformationIndex;
+  unsigned int ErrorTableIndex;
+  unsigned int DeviceStateIndex;
+  unsigned int DeviceStateChannel;
+  unsigned int DeviceStateTargetID;
+  unsigned long MonitoringTimerCount;
+  unsigned long SecondaryMonitoringTime;
+  unsigned long LastProgressReportTime;
+  boolean DualModeMemoryMailboxInterface;
+  boolean SAFTE_FaultManagementEnabled;
+  boolean ControllerInitialized;
+  boolean UserCommandActive;
+  boolean UserCommandDeferred;
+  boolean MonitoringCommandDeferred;
+  boolean NeedLogicalDriveInformation;
+  boolean NeedErrorTableInformation;
+  boolean NeedDeviceStateInformation;
+  boolean NeedRebuildProgress;
+  boolean NeedConsistencyCheckProgress;
+  boolean EphemeralProgressMessage;
+  Timer_T MonitoringTimer;
+  Semaphore_T *UserCommandSemaphore;
+  GenericDiskInfo_T GenericDiskInfo;
+  DAC960_Command_T *FreeCommands;
+  DAC960_Command_T *UserCommand;
+  DAC960_CommandMailbox_T *FirstCommandMailbox;
+  DAC960_CommandMailbox_T *LastCommandMailbox;
+  DAC960_CommandMailbox_T *NextCommandMailbox;
+  DAC960_CommandMailbox_T *PreviousCommandMailbox1;
+  DAC960_CommandMailbox_T *PreviousCommandMailbox2;
+  DAC960_StatusMailbox_T *FirstStatusMailbox;
+  DAC960_StatusMailbox_T *LastStatusMailbox;
+  DAC960_StatusMailbox_T *NextStatusMailbox;
+  PROC_DirectoryEntry_T ControllerProcEntry;
+  PROC_DirectoryEntry_T InitialStatusProcEntry;
+  PROC_DirectoryEntry_T CurrentStatusProcEntry;
+  PROC_DirectoryEntry_T UserCommandProcEntry;
+  WaitQueue_T *CommandWaitQueue;
+  DAC960_Enquiry_T Enquiry[2];
+  DAC960_ErrorTable_T ErrorTable[2];
+  DAC960_EventLogEntry_T EventLogEntry;
+  DAC960_RebuildProgress_T RebuildProgress;
+  DAC960_LogicalDriveInformation_T
+    LogicalDriveInformation[2][DAC960_MaxLogicalDrives];
+  DAC960_LogicalDriveState_T LogicalDriveInitialState[DAC960_MaxLogicalDrives];
+  DAC960_DeviceState_T DeviceState[2][DAC960_MaxChannels][DAC960_MaxTargets];
+  DAC960_Command_T Commands[DAC960_MaxDriverQueueDepth];
+  DiskPartition_T DiskPartitions[DAC960_MinorCount];
+  int LogicalDriveUsageCount[DAC960_MaxLogicalDrives];
+  int PartitionSizes[DAC960_MinorCount];
+  int BlockSizes[DAC960_MinorCount];
+  int MaxSectorsPerRequest[DAC960_MinorCount];
+  int MaxSegmentsPerRequest[DAC960_MinorCount];
+  char InitialStatusBuffer[DAC960_StatusBufferSize];
+  char CurrentStatusBuffer[DAC960_StatusBufferSize];
+  char UserStatusBuffer[DAC960_UserMessageSize];
+  char RebuildProgressBuffer[DAC960_RebuildProgressSize];
+}
+DAC960_Controller_T;
+
+
+/*
+  DAC960_AcquireControllerLock acquires exclusive access to Controller.
+*/
+
+static inline
+void DAC960_AcquireControllerLock(DAC960_Controller_T *Controller,
+				  ProcessorFlags_T *ProcessorFlags)
+{
+  save_flags(*ProcessorFlags);
+  cli();
+}
+
+
+/*
+  DAC960_ReleaseControllerLock releases exclusive access to Controller.
+*/
+
+static inline
+void DAC960_ReleaseControllerLock(DAC960_Controller_T *Controller,
+				  ProcessorFlags_T *ProcessorFlags)
+{
+  restore_flags(*ProcessorFlags);
+}
+
+
+/*
+  DAC960_AcquireControllerLockRF acquires exclusive access to Controller,
+  but is only called from the request function when interrupts are disabled.
+*/
+
+static inline
+void DAC960_AcquireControllerLockRF(DAC960_Controller_T *Controller,
+				    ProcessorFlags_T *ProcessorFlags)
+{
+}
+
+
+/*
+  DAC960_ReleaseControllerLockRF releases exclusive access to Controller,
+  but is only called from the request function when interrupts are disabled.
+*/
+
+static inline
+void DAC960_ReleaseControllerLockRF(DAC960_Controller_T *Controller,
+				    ProcessorFlags_T *ProcessorFlags)
+{
+}
+
+
+/*
+  DAC960_AcquireControllerLockIH acquires exclusive access to Controller,
+  but is only called from the interrupt handler when interrupts are disabled.
+*/
+
+static inline
+void DAC960_AcquireControllerLockIH(DAC960_Controller_T *Controller,
+				    ProcessorFlags_T *ProcessorFlags)
+{
+}
+
+
+/*
+  DAC960_ReleaseControllerLockIH releases exclusive access to Controller,
+  but is only called from the interrupt handler when interrupts are disabled.
+*/
+
+static inline
+void DAC960_ReleaseControllerLockIH(DAC960_Controller_T *Controller,
+				    ProcessorFlags_T *ProcessorFlags)
+{
+}
+
+
+/*
+  Define inline functions to provide an abstraction for reading and writing the
+  DAC960 V5 Controller Interface Registers.
+*/
+
+static inline
+void DAC960_V5_HardwareMailboxNewCommand(void *ControllerBaseAddress)
+{
+  DAC960_V5_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.HardwareMailboxNewCommand = true;
+  writeb(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V5_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V5_AcknowledgeHardwareMailboxStatus(void *ControllerBaseAddress)
+{
+  DAC960_V5_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.AcknowledgeHardwareMailboxStatus = true;
+  writeb(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V5_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V5_GenerateInterrupt(void *ControllerBaseAddress)
+{
+  DAC960_V5_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.GenerateInterrupt = true;
+  writeb(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V5_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V5_ControllerReset(void *ControllerBaseAddress)
+{
+  DAC960_V5_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.ControllerReset = true;
+  writeb(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V5_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V5_MemoryMailboxNewCommand(void *ControllerBaseAddress)
+{
+  DAC960_V5_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.MemoryMailboxNewCommand = true;
+  writeb(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V5_InboundDoorBellRegisterOffset);
+}
+
+static inline
+boolean DAC960_V5_HardwareMailboxEmptyP(void *ControllerBaseAddress)
+{
+  DAC960_V5_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All =
+    readb(ControllerBaseAddress + DAC960_V5_InboundDoorBellRegisterOffset);
+  return InboundDoorBellRegister.Read.HardwareMailboxEmpty;
+}
+
+static inline
+void DAC960_V5_AcknowledgeHardwareMailboxInterrupt(void *ControllerBaseAddress)
+{
+  DAC960_V5_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All = 0;
+  OutboundDoorBellRegister.Write.AcknowledgeHardwareMailboxInterrupt = true;
+  writeb(OutboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V5_OutboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V5_AcknowledgeMemoryMailboxInterrupt(void *ControllerBaseAddress)
+{
+  DAC960_V5_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All = 0;
+  OutboundDoorBellRegister.Write.AcknowledgeMemoryMailboxInterrupt = true;
+  writeb(OutboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V5_OutboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V5_AcknowledgeInterrupt(void *ControllerBaseAddress)
+{
+  DAC960_V5_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All = 0;
+  OutboundDoorBellRegister.Write.AcknowledgeHardwareMailboxInterrupt = true;
+  OutboundDoorBellRegister.Write.AcknowledgeMemoryMailboxInterrupt = true;
+  writeb(OutboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V5_OutboundDoorBellRegisterOffset);
+}
+
+static inline
+boolean DAC960_V5_HardwareMailboxStatusAvailableP(void *ControllerBaseAddress)
+{
+  DAC960_V5_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All =
+    readb(ControllerBaseAddress + DAC960_V5_OutboundDoorBellRegisterOffset);
+  return OutboundDoorBellRegister.Read.HardwareMailboxStatusAvailable;
+}
+
+static inline
+boolean DAC960_V5_MemoryMailboxStatusAvailableP(void *ControllerBaseAddress)
+{
+  DAC960_V5_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All =
+    readb(ControllerBaseAddress + DAC960_V5_OutboundDoorBellRegisterOffset);
+  return OutboundDoorBellRegister.Read.MemoryMailboxStatusAvailable;
+}
+
+static inline
+void DAC960_V5_EnableInterrupts(void *ControllerBaseAddress)
+{
+  DAC960_V5_InterruptMaskRegister_T InterruptMaskRegister;
+  InterruptMaskRegister.All = 0;
+  InterruptMaskRegister.Bits.DisableInterrupts = false;
+  writeb(InterruptMaskRegister.All,
+	 ControllerBaseAddress + DAC960_V5_InterruptMaskRegisterOffset);
+}
+
+static inline
+void DAC960_V5_DisableInterrupts(void *ControllerBaseAddress)
+{
+  DAC960_V5_InterruptMaskRegister_T InterruptMaskRegister;
+  InterruptMaskRegister.All = 0;
+  InterruptMaskRegister.Bits.DisableInterrupts = true;
+  writeb(InterruptMaskRegister.All,
+	 ControllerBaseAddress + DAC960_V5_InterruptMaskRegisterOffset);
+}
+
+static inline
+boolean DAC960_V5_InterruptsEnabledP(void *ControllerBaseAddress)
+{
+  DAC960_V5_InterruptMaskRegister_T InterruptMaskRegister;
+  InterruptMaskRegister.All =
+    readb(ControllerBaseAddress + DAC960_V5_InterruptMaskRegisterOffset);
+  return !InterruptMaskRegister.Bits.DisableInterrupts;
+}
+
+static inline
+void DAC960_V5_WriteCommandMailbox(DAC960_CommandMailbox_T *NextCommandMailbox,
+				   DAC960_CommandMailbox_T *CommandMailbox)
+{
+  NextCommandMailbox->Words[1] = CommandMailbox->Words[1];
+  NextCommandMailbox->Words[2] = CommandMailbox->Words[2];
+  NextCommandMailbox->Words[3] = CommandMailbox->Words[3];
+  NextCommandMailbox->Words[0] = CommandMailbox->Words[0];
+}
+
+static inline
+void DAC960_V5_WriteHardwareMailbox(void *ControllerBaseAddress,
+				    DAC960_CommandMailbox_T *CommandMailbox)
+{
+  writel(CommandMailbox->Words[0],
+	 ControllerBaseAddress + DAC960_V5_CommandOpcodeRegisterOffset);
+  writel(CommandMailbox->Words[1],
+	 ControllerBaseAddress + DAC960_V5_MailboxRegister4Offset);
+  writel(CommandMailbox->Words[2],
+	 ControllerBaseAddress + DAC960_V5_MailboxRegister8Offset);
+  writeb(CommandMailbox->Bytes[12],
+	 ControllerBaseAddress + DAC960_V5_MailboxRegister12Offset);
+}
+
+static inline DAC960_CommandIdentifier_T
+DAC960_V5_ReadStatusCommandIdentifier(void *ControllerBaseAddress)
+{
+  return readb(ControllerBaseAddress
+	       + DAC960_V5_StatusCommandIdentifierRegOffset);
+}
+
+static inline DAC960_CommandStatus_T
+DAC960_V5_ReadStatusRegister(void *ControllerBaseAddress)
+{
+  return readw(ControllerBaseAddress + DAC960_V5_StatusRegisterOffset);
+}
+
+static inline
+void DAC960_V5_SaveMemoryMailboxInfo(DAC960_Controller_T *Controller)
+{
+  void *ControllerBaseAddress = Controller->BaseAddress;
+  writel(0x743C485E,
+	 ControllerBaseAddress + DAC960_V5_CommandOpcodeRegisterOffset);
+  writel((unsigned long) Controller->FirstCommandMailbox,
+	 ControllerBaseAddress + DAC960_V5_MailboxRegister4Offset);
+  writew(Controller->NextCommandMailbox - Controller->FirstCommandMailbox,
+	 ControllerBaseAddress + DAC960_V5_MailboxRegister8Offset);
+  writew(Controller->NextStatusMailbox - Controller->FirstStatusMailbox,
+	 ControllerBaseAddress + DAC960_V5_MailboxRegister10Offset);
+}
+
+static inline
+void DAC960_V5_RestoreMemoryMailboxInfo(DAC960_Controller_T *Controller,
+					void **MemoryMailboxAddress,
+					short *NextCommandMailboxIndex,
+					short *NextStatusMailboxIndex)
+{
+  void *ControllerBaseAddress = Controller->BaseAddress;
+  if (readl(ControllerBaseAddress
+	    + DAC960_V5_CommandOpcodeRegisterOffset) != 0x743C485E)
+    return;
+  *MemoryMailboxAddress =
+    (void *) readl(ControllerBaseAddress + DAC960_V5_MailboxRegister4Offset);
+  *NextCommandMailboxIndex =
+    readw(ControllerBaseAddress + DAC960_V5_MailboxRegister8Offset);
+  *NextStatusMailboxIndex =
+    readw(ControllerBaseAddress + DAC960_V5_MailboxRegister10Offset);
+}
+
+
+/*
+  Define inline functions to provide an abstraction for reading and writing the
+  DAC960 V4 Controller Interface Registers.
+*/
+
+static inline
+void DAC960_V4_HardwareMailboxNewCommand(void *ControllerBaseAddress)
+{
+  DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.HardwareMailboxNewCommand = true;
+  writel(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V4_AcknowledgeHardwareMailboxStatus(void *ControllerBaseAddress)
+{
+  DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.AcknowledgeHardwareMailboxStatus = true;
+  writel(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V4_GenerateInterrupt(void *ControllerBaseAddress)
+{
+  DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.GenerateInterrupt = true;
+  writel(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V4_ControllerReset(void *ControllerBaseAddress)
+{
+  DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.ControllerReset = true;
+  writel(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V4_MemoryMailboxNewCommand(void *ControllerBaseAddress)
+{
+  DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.MemoryMailboxNewCommand = true;
+  writel(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset);
+}
+
+static inline
+boolean DAC960_V4_HardwareMailboxFullP(void *ControllerBaseAddress)
+{
+  DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All =
+    readl(ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset);
+  return InboundDoorBellRegister.Read.HardwareMailboxFull;
+}
+
+static inline
+void DAC960_V4_AcknowledgeHardwareMailboxInterrupt(void *ControllerBaseAddress)
+{
+  DAC960_V4_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All = 0;
+  OutboundDoorBellRegister.Write.AcknowledgeHardwareMailboxInterrupt = true;
+  writel(OutboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V4_OutboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V4_AcknowledgeMemoryMailboxInterrupt(void *ControllerBaseAddress)
+{
+  DAC960_V4_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All = 0;
+  OutboundDoorBellRegister.Write.AcknowledgeMemoryMailboxInterrupt = true;
+  writel(OutboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V4_OutboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V4_AcknowledgeInterrupt(void *ControllerBaseAddress)
+{
+  DAC960_V4_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All = 0;
+  OutboundDoorBellRegister.Write.AcknowledgeHardwareMailboxInterrupt = true;
+  OutboundDoorBellRegister.Write.AcknowledgeMemoryMailboxInterrupt = true;
+  writel(OutboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V4_OutboundDoorBellRegisterOffset);
+}
+
+static inline
+boolean DAC960_V4_HardwareMailboxStatusAvailableP(void *ControllerBaseAddress)
+{
+  DAC960_V4_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All =
+    readl(ControllerBaseAddress + DAC960_V4_OutboundDoorBellRegisterOffset);
+  return OutboundDoorBellRegister.Read.HardwareMailboxStatusAvailable;
+}
+
+static inline
+boolean DAC960_V4_MemoryMailboxStatusAvailableP(void *ControllerBaseAddress)
+{
+  DAC960_V4_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All =
+    readl(ControllerBaseAddress + DAC960_V4_OutboundDoorBellRegisterOffset);
+  return OutboundDoorBellRegister.Read.MemoryMailboxStatusAvailable;
+}
+
+static inline
+void DAC960_V4_EnableInterrupts(void *ControllerBaseAddress)
+{
+  DAC960_V4_InterruptMaskRegister_T InterruptMaskRegister;
+  InterruptMaskRegister.All = 0;
+  InterruptMaskRegister.Bits.MessageUnitInterruptMask1 = 0x3;
+  InterruptMaskRegister.Bits.DisableInterrupts = false;
+  InterruptMaskRegister.Bits.MessageUnitInterruptMask2 = 0x1F;
+  writel(InterruptMaskRegister.All,
+	 ControllerBaseAddress + DAC960_V4_InterruptMaskRegisterOffset);
+}
+
+static inline
+void DAC960_V4_DisableInterrupts(void *ControllerBaseAddress)
+{
+  DAC960_V4_InterruptMaskRegister_T InterruptMaskRegister;
+  InterruptMaskRegister.All = 0;
+  InterruptMaskRegister.Bits.MessageUnitInterruptMask1 = 0x3;
+  InterruptMaskRegister.Bits.DisableInterrupts = true;
+  InterruptMaskRegister.Bits.MessageUnitInterruptMask2 = 0x1F;
+  writel(InterruptMaskRegister.All,
+	 ControllerBaseAddress + DAC960_V4_InterruptMaskRegisterOffset);
+}
+
+static inline
+boolean DAC960_V4_InterruptsEnabledP(void *ControllerBaseAddress)
+{
+  DAC960_V4_InterruptMaskRegister_T InterruptMaskRegister;
+  InterruptMaskRegister.All =
+    readl(ControllerBaseAddress + DAC960_V4_InterruptMaskRegisterOffset);
+  return !InterruptMaskRegister.Bits.DisableInterrupts;
+}
+
+static inline
+void DAC960_V4_WriteCommandMailbox(DAC960_CommandMailbox_T *NextCommandMailbox,
+				   DAC960_CommandMailbox_T *CommandMailbox)
+{
+  NextCommandMailbox->Words[1] = CommandMailbox->Words[1];
+  NextCommandMailbox->Words[2] = CommandMailbox->Words[2];
+  NextCommandMailbox->Words[3] = CommandMailbox->Words[3];
+  NextCommandMailbox->Words[0] = CommandMailbox->Words[0];
+}
+
+static inline
+void DAC960_V4_WriteHardwareMailbox(void *ControllerBaseAddress,
+				    DAC960_CommandMailbox_T *CommandMailbox)
+{
+  writel(CommandMailbox->Words[0],
+	 ControllerBaseAddress + DAC960_V4_CommandOpcodeRegisterOffset);
+  writel(CommandMailbox->Words[1],
+	 ControllerBaseAddress + DAC960_V4_MailboxRegister4Offset);
+  writel(CommandMailbox->Words[2],
+	 ControllerBaseAddress + DAC960_V4_MailboxRegister8Offset);
+  writeb(CommandMailbox->Bytes[12],
+	 ControllerBaseAddress + DAC960_V4_MailboxRegister12Offset);
+}
+
+static inline DAC960_CommandIdentifier_T
+DAC960_V4_ReadStatusCommandIdentifier(void *ControllerBaseAddress)
+{
+  return readb(ControllerBaseAddress
+	       + DAC960_V4_StatusCommandIdentifierRegOffset);
+}
+
+static inline DAC960_CommandStatus_T
+DAC960_V4_ReadStatusRegister(void *ControllerBaseAddress)
+{
+  return readw(ControllerBaseAddress + DAC960_V4_StatusRegisterOffset);
+}
+
+static inline
+void DAC960_V4_SaveMemoryMailboxInfo(DAC960_Controller_T *Controller)
+{
+  void *ControllerBaseAddress = Controller->BaseAddress;
+  writel(0xAABBFFFF,
+	 ControllerBaseAddress + DAC960_V4_CommandOpcodeRegisterOffset);
+  writel((unsigned long) Controller->FirstCommandMailbox,
+	 ControllerBaseAddress + DAC960_V4_MailboxRegister4Offset);
+  writew(Controller->NextCommandMailbox - Controller->FirstCommandMailbox,
+	 ControllerBaseAddress + DAC960_V4_MailboxRegister8Offset);
+  writew(Controller->NextStatusMailbox - Controller->FirstStatusMailbox,
+	 ControllerBaseAddress + DAC960_V4_MailboxRegister10Offset);
+}
+
+static inline
+void DAC960_V4_RestoreMemoryMailboxInfo(DAC960_Controller_T *Controller,
+					void **MemoryMailboxAddress,
+					short *NextCommandMailboxIndex,
+					short *NextStatusMailboxIndex)
+{
+  void *ControllerBaseAddress = Controller->BaseAddress;
+  if (readl(ControllerBaseAddress
+	    + DAC960_V4_CommandOpcodeRegisterOffset) != 0xAABBFFFF)
+    return;
+  *MemoryMailboxAddress =
+    (void *) readl(ControllerBaseAddress + DAC960_V4_MailboxRegister4Offset);
+  *NextCommandMailboxIndex =
+    readw(ControllerBaseAddress + DAC960_V4_MailboxRegister8Offset);
+  *NextStatusMailboxIndex =
+    readw(ControllerBaseAddress + DAC960_V4_MailboxRegister10Offset);
+}
+
+
+/*
+  Define inline functions to provide an abstraction for reading and writing the
+  DAC960 V3 Controller Interface Registers.
+*/
+
+static inline
+void DAC960_V3_NewCommand(void *ControllerBaseAddress)
+{
+  DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.NewCommand = true;
+  writeb(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V3_AcknowledgeStatus(void *ControllerBaseAddress)
+{
+  DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.AcknowledgeStatus = true;
+  writeb(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V3_GenerateInterrupt(void *ControllerBaseAddress)
+{
+  DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.GenerateInterrupt = true;
+  writeb(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V3_ControllerReset(void *ControllerBaseAddress)
+{
+  DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.ControllerReset = true;
+  writeb(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset);
+}
+
+static inline
+boolean DAC960_V3_MailboxFullP(void *ControllerBaseAddress)
+{
+  DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All =
+    readb(ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset);
+  return InboundDoorBellRegister.Read.MailboxFull;
+}
+
+static inline
+void DAC960_V3_AcknowledgeInterrupt(void *ControllerBaseAddress)
+{
+  DAC960_V3_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All = 0;
+  OutboundDoorBellRegister.Write.AcknowledgeInterrupt = true;
+  writeb(OutboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V3_OutboundDoorBellRegisterOffset);
+}
+
+static inline
+boolean DAC960_V3_StatusAvailableP(void *ControllerBaseAddress)
+{
+  DAC960_V3_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All =
+    readb(ControllerBaseAddress + DAC960_V3_OutboundDoorBellRegisterOffset);
+  return OutboundDoorBellRegister.Read.StatusAvailable;
+}
+
+static inline
+void DAC960_V3_EnableInterrupts(void *ControllerBaseAddress)
+{
+  DAC960_V3_InterruptEnableRegister_T InterruptEnableRegister;
+  InterruptEnableRegister.All = 0;
+  InterruptEnableRegister.Bits.EnableInterrupts = true;
+  writeb(InterruptEnableRegister.All,
+	 ControllerBaseAddress + DAC960_V3_InterruptEnableRegisterOffset);
+}
+
+static inline
+void DAC960_V3_DisableInterrupts(void *ControllerBaseAddress)
+{
+  DAC960_V3_InterruptEnableRegister_T InterruptEnableRegister;
+  InterruptEnableRegister.All = 0;
+  InterruptEnableRegister.Bits.EnableInterrupts = false;
+  writeb(InterruptEnableRegister.All,
+	 ControllerBaseAddress + DAC960_V3_InterruptEnableRegisterOffset);
+}
+
+static inline
+boolean DAC960_V3_InterruptsEnabledP(void *ControllerBaseAddress)
+{
+  DAC960_V3_InterruptEnableRegister_T InterruptEnableRegister;
+  InterruptEnableRegister.All =
+    readb(ControllerBaseAddress + DAC960_V3_InterruptEnableRegisterOffset);
+  return InterruptEnableRegister.Bits.EnableInterrupts;
+}
+
+static inline
+void DAC960_V3_WriteCommandMailbox(void *ControllerBaseAddress,
+				   DAC960_CommandMailbox_T *CommandMailbox)
+{
+  writel(CommandMailbox->Words[0],
+	 ControllerBaseAddress + DAC960_V3_CommandOpcodeRegisterOffset);
+  writel(CommandMailbox->Words[1],
+	 ControllerBaseAddress + DAC960_V3_MailboxRegister4Offset);
+  writel(CommandMailbox->Words[2],
+	 ControllerBaseAddress + DAC960_V3_MailboxRegister8Offset);
+  writeb(CommandMailbox->Bytes[12],
+	 ControllerBaseAddress + DAC960_V3_MailboxRegister12Offset);
+}
+
+static inline DAC960_CommandIdentifier_T
+DAC960_V3_ReadStatusCommandIdentifier(void *ControllerBaseAddress)
+{
+  return readb(ControllerBaseAddress
+	       + DAC960_V3_StatusCommandIdentifierRegOffset);
+}
+
+static inline DAC960_CommandStatus_T
+DAC960_V3_ReadStatusRegister(void *ControllerBaseAddress)
+{
+  return readw(ControllerBaseAddress + DAC960_V3_StatusRegisterOffset);
+}
+
+
+/*
+  Virtual_to_Bus and Bus_to_Virtual map between Kernel Virtual Addresses
+  and PCI Bus Addresses.
+*/
+
+static inline DAC960_BusAddress_T Virtual_to_Bus(void *VirtualAddress)
+{
+  return (DAC960_BusAddress_T) virt_to_bus(VirtualAddress);
+}
+
+static inline void *Bus_to_Virtual(DAC960_BusAddress_T BusAddress)
+{
+  return (void *) bus_to_virt(BusAddress);
+}
+
+
+/*
+  Define compatibility macros between Linux 2.0 and Linux 2.1.
+*/
+
+#if LINUX_VERSION_CODE < 0x20100
+
+#define MODULE_PARM(Variable, Type)
+#define ioremap_nocache(Offset, Size)	vremap(Offset, Size)
+#define iounmap(Address)		vfree(Address)
+
+#endif
+
+
+/*
+  Define prototypes for the forward referenced DAC960 Driver Internal Functions.
+*/
+
+static void DAC960_FinalizeController(DAC960_Controller_T *);
+static void DAC960_RequestFunction0(void);
+static void DAC960_RequestFunction1(void);
+static void DAC960_RequestFunction2(void);
+static void DAC960_RequestFunction3(void);
+static void DAC960_RequestFunction4(void);
+static void DAC960_RequestFunction5(void);
+static void DAC960_RequestFunction6(void);
+static void DAC960_RequestFunction7(void);
+static void DAC960_InterruptHandler(int, void *, Registers_T *);
+static void DAC960_QueueMonitoringCommand(DAC960_Command_T *);
+static void DAC960_MonitoringTimerFunction(unsigned long);
+static int DAC960_Open(Inode_T *, File_T *);
+static void DAC960_Release(Inode_T *, File_T *);
+static int DAC960_Ioctl(Inode_T *, File_T *, unsigned int, unsigned long);
+static void DAC960_InitializeGenericDiskInfo(GenericDiskInfo_T *);
+static void DAC960_Message(DAC960_MessageLevel_T, char *,
+			   DAC960_Controller_T *, ...);
+static void DAC960_CreateProcEntries(void);
+static void DAC960_DestroyProcEntries(void);
+
+
+#endif /* DAC960_DriverVersion */
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/net/appletalk/ddp.c linux/net/appletalk/ddp.c
--- linux.vanilla/net/appletalk/ddp.c	Sun Jun 21 18:41:01 1998
+++ linux/net/appletalk/ddp.c	Sun Mar 21 23:57:10 1999
@@ -408,6 +408,7 @@
 	 *	Scan the networks.
 	 */
 	 
+	atif->status |= ATIF_PROBE;
 	for(netct=0;netct<=netrange;netct++)
 	{
 		/*
@@ -435,8 +436,10 @@
 					if(atif->status&ATIF_PROBE_FAIL)
 						break;
 				}
-				if(!(atif->status&ATIF_PROBE_FAIL))
+				if(!(atif->status&ATIF_PROBE_FAIL)) {
+					atif->status &= ~ATIF_PROBE;
 					return 0;
+				}
 			}
 			atif->status&=~ATIF_PROBE_FAIL;
 		}
@@ -444,6 +447,7 @@
 		if(probe_net>ntohs(atif->nets.nr_lastnet))
 			probe_net=ntohs(atif->nets.nr_firstnet);
 	}
+	atif->status &= ~ATIF_PROBE;
 	return -EADDRINUSE;	/* Network is full... */
 }
 
@@ -509,7 +513,7 @@
 	struct atalk_iface *iface;
 	for(iface=atalk_iface_list;iface!=NULL;iface=iface->next)
 	{
-		if((node==ATADDR_BCAST || iface->address.s_node==node) 
+		if((node==ATADDR_BCAST || node==ATADDR_ANYNODE || iface->address.s_node==node) 
 			&& iface->address.s_net==net && !(iface->status&ATIF_PROBE))
 			return iface;
 	}
@@ -1544,7 +1548,10 @@
 	/* Which socket - atalk_search_socket() looks for a *full match*
 	   of the <net,node,port> tuple */
 	tosat.sat_addr.s_net = ddp->deh_dnet;
-	tosat.sat_addr.s_node = ddp->deh_dnode;
+	if (ddp->deh_dnode == ATADDR_ANYNODE)
+		tosat.sat_addr.s_node = atif->address.s_node;
+	else
+		tosat.sat_addr.s_node = ddp->deh_dnode;
 	tosat.sat_port = ddp->deh_dport;
 
 	sock=atalk_search_socket( &tosat, atif );
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/net/bridge/br.c linux/net/bridge/br.c
--- linux.vanilla/net/bridge/br.c	Sun Dec  6 00:14:47 1998
+++ linux/net/bridge/br.c	Mon Mar  1 21:50:24 1999
@@ -91,6 +91,8 @@
 
 void transmit_config(int port_no)			  /* (4.6.1)	 */
 {
+	if(!(br_stats.flags & BR_UP))
+		return; /* this should not happen but happens */
 	if (hold_timer[port_no].active) {	  /* (4.6.1.3.1)	 */
 		port_info[port_no].config_pending = TRUE;	/* (4.6.1.3.1)	 */
 	} else {				  /* (4.6.1.3.2)	 */
@@ -871,11 +873,12 @@
 
 int send_config_bpdu(int port_no, Config_bpdu *config_bpdu)
 {
-struct sk_buff *skb;
-struct device *dev = port_info[port_no].dev;
-int size;
-unsigned long flags;
+	struct sk_buff *skb;
+	struct device *dev = port_info[port_no].dev;
+	int size;
 	
+	if(!(br_stats.flags & BR_UP))
+		return(-1); /* this should not happen but happens */
 	if (port_info[port_no].state == Disabled) {
 		printk(KERN_DEBUG "send_config_bpdu: port %i not valid\n",port_no);
 		return(-1);
@@ -927,11 +930,12 @@
 
 int send_tcn_bpdu(int port_no, Tcn_bpdu *bpdu)
 {
-struct sk_buff *skb;
-struct device *dev = port_info[port_no].dev;
-int size;
-unsigned long flags;
+	struct sk_buff *skb;
+	struct device *dev = port_info[port_no].dev;
+	int size;
 	
+	if(!(br_stats.flags & BR_UP))
+		return(-1); /* this should not happen but happens */
 	if (port_info[port_no].state == Disabled) {
 		printk(KERN_DEBUG "send_tcn_bpdu: port %i not valid\n",port_no);
 		return(-1);
@@ -1047,7 +1051,6 @@
 int br_receive_frame(struct sk_buff *skb)	/* 3.5 */
 {
 	int port;
-	int i;
 	
 	if (br_stats.flags & BR_DEBUG)
 		printk("br_receive_frame: ");
@@ -1301,9 +1304,16 @@
 		/*
 		 *	Sending
 		 */
-		if (f->port!=port && port_info[f->port].state == Forwarding) {
+
+		/*
+		 * Vova Oksman: There was the BUG, we must to check timer 
+		 * before comparing source and destination ports, becouse
+		 * case that destination was switched from same port with
+		 * source to other port. 
+		 */
 			/* has entry expired? */
-			if (f->timer + fdb_aging_time < CURRENT_TIME) {
+		if (port_info[f->port].state == Forwarding &&
+		    f->timer + fdb_aging_time < CURRENT_TIME) {
 				/* timer expired, invalidate entry */
 				f->flags &= ~FDB_ENT_VALID;
 				if (br_stats.flags & BR_DEBUG)
@@ -1314,6 +1324,7 @@
 				br_flood(skb, port);
 				return(br_dev_drop(skb));
 			}
+		if (f->port!=port && port_info[f->port].state == Forwarding) {
 			/* mark that's we've been here... */
 			skb->pkt_bridged = IS_BRIDGED;
 			
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/net/bridge/br_tree.c linux/net/bridge/br_tree.c
--- linux.vanilla/net/bridge/br_tree.c	Sun Jun 21 18:41:03 1998
+++ linux/net/bridge/br_tree.c	Sun Dec  6 03:24:25 1998
@@ -215,6 +215,11 @@
 			break;
 		*stack_ptr++ = nodeplace; stack_count++;
 		if (addr_cmp(new_node->ula, node->ula) == 0) { /* update */
+		/*
+		 * Vova Oksman: There was the BUG, in case that port 
+		 * was changed we must to update it.
+		 */
+			node->port = new_node->port;
 			node->flags = new_node->flags;
 			node->timer = new_node->timer;	
 			return(0);
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/net/ipv4/Config.in linux/net/ipv4/Config.in
--- linux.vanilla/net/ipv4/Config.in	Sun Jun 21 18:41:02 1998
+++ linux/net/ipv4/Config.in	Tue Mar  2 01:03:21 1999
@@ -16,6 +16,21 @@
         comment 'Protocol-specific masquerading support will be built as modules.'
         if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then	
           bool 'IP: ipautofw masquerading (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_IPAUTOFW
+          bool 'IP: MS PPTP client masq support (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_PPTP
+          if [ "$CONFIG_IP_MASQUERADE_PPTP" = "y" ]; then	
+            bool 'IP: MS PPTP masq debugging' DEBUG_IP_MASQUERADE_PPTP
+            if [ "$DEBUG_IP_MASQUERADE_PPTP" = "y" ]; then	
+              bool 'IP: MS PPTP masq verbose debugging' DEBUG_IP_MASQUERADE_PPTP_VERBOSE
+            fi
+         fi
+         bool 'IP: IPSEC ESP & ISAKMP masq support (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_IPSEC
+         if [ "$CONFIG_IP_MASQUERADE_IPSEC" = "y" ]; then	
+           int 'IP: IPSEC masq table lifetime (minutes)' CONFIG_IP_MASQUERADE_IPSEC_EXPIRE 30
+           bool 'IP: IPSEC masq debugging' DEBUG_IP_MASQUERADE_IPSEC
+           if [ "$DEBUG_IP_MASQUERADE_IPSEC" = "y" ]; then	
+             bool 'IP: IPSEC masq verbose debugging' DEBUG_IP_MASQUERADE_IPSEC_VERBOSE
+           fi
+         fi
         fi
         bool 'IP: ICMP masquerading' CONFIG_IP_MASQUERADE_ICMP
       fi
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/net/ipv4/arp.c linux/net/ipv4/arp.c
--- linux.vanilla/net/ipv4/arp.c	Sun Dec  6 00:14:47 1998
+++ linux/net/ipv4/arp.c	Mon Mar  1 21:31:52 1999
@@ -1784,13 +1784,15 @@
  *	is not from an IP number.  We can't currently handle this, so toss
  *	it. 
  */  
-#ifdef CONFIG_FDDI
-	if (dev->type == ARPHRD_FDDI)
+#if defined(CONFIG_NET_ETHERNET) || defined(CONFIG_FDDI)
+	if (dev->type == ARPHRD_ETHER || dev->type == ARPHRD_FDDI)
 	{
 		/*
 		 * According to RFC 1390, FDDI devices should accept ARP hardware types
 		 * of 1 (Ethernet).  However, to be more robust, we'll accept hardware
 		 * types of either 1 (Ethernet) or 6 (IEEE 802.2).
+		 *
+		 * ETHERNET devices will accept both hardware types, too. (RFC 1042)
 		 */
 		if (arp->ar_hln != dev->addr_len    || 
 			((ntohs(arp->ar_hrd) != ARPHRD_ETHER) && (ntohs(arp->ar_hrd) != ARPHRD_IEEE802)) ||
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/net/ipv4/igmp.c linux/net/ipv4/igmp.c
--- linux.vanilla/net/ipv4/igmp.c	Sun Jun 21 18:41:02 1998
+++ linux/net/ipv4/igmp.c	Fri Dec 11 20:42:33 1998
@@ -254,8 +254,12 @@
 
 	if(skb==NULL)
 		return;
-	tmp=ip_build_header(skb, dev->pa_addr, address, &dev, IPPROTO_IGMP, NULL,
-				28 , 0, 1, NULL);
+	if (type != IGMP_HOST_LEAVE_MESSAGE)
+		tmp=ip_build_header(skb, dev->pa_addr, address, &dev, IPPROTO_IGMP, NULL,
+			28 , 0, 1, NULL);
+	else
+		tmp=ip_build_header(skb, dev->pa_addr, IGMP_ALL_ROUTER, &dev, IPPROTO_IGMP, NULL,
+			28, 0, 1, NULL);
 	if(tmp<0)
 	{
 		kfree_skb(skb, FREE_WRITE);
@@ -429,7 +433,8 @@
 	del_timer(&im->timer);
         /* It seems we have to send Leave Messages to 224.0.0.2 and not to
            the group itself, to remain RFC 2236 compliant... (jmel) */
-        igmp_send_report(im->interface, IGMP_ALL_ROUTER, IGMP_HOST_LEAVE_MESSAGE);
+        /*igmp_send_report(im->interface, IGMP_ALL_ROUTER, IGMP_HOST_LEAVE_MESSAGE);*/
+        igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_LEAVE_MESSAGE);
 	ip_mc_filter_del(im->interface, im->multiaddr);
 }
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/net/ipv4/ip_masq.c linux/net/ipv4/ip_masq.c
--- linux.vanilla/net/ipv4/ip_masq.c	Sun Dec  6 00:14:48 1998
+++ linux/net/ipv4/ip_masq.c	Tue Mar  2 01:00:05 1999
@@ -20,6 +20,7 @@
  *	Delian Delchev		:	Added support for ICMP requests and replys
  *	Nigel Metheringham	:	ICMP in ICMP handling, tidy ups, bug fixes, made ICMP optional
  *	Juan Jose Ciarlante	:	re-assign maddr if no packet received from outside
+ * 	John D. Hardin		:	Added PPTP and IPSEC protocols
  *	
  */
 
@@ -45,6 +46,152 @@
 
 #define IP_MASQ_TAB_SIZE 256    /* must be power of 2 */
 
+#define IPPROTO_GRE	47	/* GRE Encapsulation used by PPTP */
+
+#ifdef CONFIG_IP_MASQUERADE_PPTP
+/*
+ * This is clumsier than it otherwise might be (i.e. the
+ * PPTP control channel sniffer should be a module, and there
+ * should be a separate table for GRE masq entries so that
+ * we're not making all of the hacks to the TCP table code)
+ # but I wanted to keep the code changes localized to one file
+ # if possible.
+ * This should all be modular, and the table routines need to
+ * be somewhat more generic.
+ *
+ * Maybe for 2.0.38 - we'll see.
+ */
+
+static const char *strGREProt = "GRE";
+
+#ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT
+/*
+ * MULTICLIENT watches the control channel and preloads the
+ * call ID into the masq table entry, so we want the
+ * masq table entry to persist until a Call Disconnect
+ * occurs, otherwise the call IDs will be lost and the link broken.
+ */
+#define MASQUERADE_EXPIRE_PPTP 15*60*HZ
+
+/*
+ * To support multiple clients communicating with the same server,
+ * we have to sniff the control channel and trap the client's
+ * call ID, then substitute a unique-to-the-firewall call ID.
+ * Then on inbound GRE packets we use the bogus call ID to figure
+ * out which client to route the traffic to, then replace the
+ * bogus call ID with the client's real call ID, which we've saved.
+ * For simplicity we'll use masq port as the bogus call ID.
+ * The actual call ID will be stored in the masq table as
+ * the source port, and the destination port will always be zero.
+ *
+ * NB: PPTP servers can tell whether the client is masqueraded by
+ * looking for call IDs above 61000.
+ */
+#define PPTP_CONTROL_PORT 1723
+
+#else /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */
+
+/* non-MULTICLIENT ignores call IDs, so masq table
+ * entries may expire quickly without causing problems.
+ */
+#define MASQUERADE_EXPIRE_PPTP 5*60*HZ
+
+#endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */
+
+/*
+ * Define this here rather than in /usr/src/linux/include/wherever/whatever.h
+ * in order to localize my mistakes to one file...
+ *
+ * This struct may be architecture-specific because of the bitmaps.
+ */
+struct pptp_gre_header {
+        __u8
+                recur:3,
+                is_strict:1,
+                has_seq:1,
+                has_key:1,
+                has_routing:1,
+                has_cksum:1;
+        __u8
+                version:3,
+                flags:5;
+        __u16   
+                protocol,
+                payload_len,
+                call_id;        /* peer's call_id for this session */
+
+};
+
+#endif /* CONFIG_IP_MASQUERADE_PPTP */
+
+
+#define IPPROTO_ESP	50	/* ESP protocol for IPSEC */
+#define IPPROTO_AH 	51	/* AH protocol for IPSEC */
+
+#ifdef CONFIG_IP_MASQUERADE_IPSEC
+/*
+ * The above comments about PPTP apply here, too. This should all be a module.
+ *
+ * The "port numbers" for masq table purposes will be part of the
+ * SPI, just to gain a little benefit from the hashing.
+ */
+
+static const char *strESPProt = "ESP";
+static const char *strAHProt = "AH";
+
+/*
+ * ISAKMP uses 500/udp, and the traffic must come from
+ * 500/udp (i.e. 500/udp <-> 500/udp), so we need to
+ * check for ISAKMP UDP traffic and avoid changing the
+ * source port number. In order to associate the data streams
+ * we may need to sniff the ISAKMP cookies as well.
+ */
+#define UDP_PORT_ISAKMP	500	/* ISAKMP default UDP port */
+
+#if CONFIG_IP_MASQUERADE_IPSEC_EXPIRE > 15
+#define MASQUERADE_EXPIRE_IPSEC CONFIG_IP_MASQUERADE_IPSEC_EXPIRE*60*HZ
+#else
+#define MASQUERADE_EXPIRE_IPSEC 15*60*HZ
+#endif
+
+/*
+ * We can't know the inbound SPI until it comes in (the ISAKMP exchange
+ * is encryptd so we can't sniff it out of that), so we associate inbound
+ * and outbound traffic by inspection. If somebody sends a new packet to a
+ * remote server, then block all other new traffic to that server until we
+ * get a response from that server with a SPI we haven't seen yet. It is
+ * assumed that this is the correct response - we have no way to verify it,
+ * as everything else is encrypted.
+ *
+ * If there is a collision, the block will last for up to two minutes (or
+ * whatever MASQUERADE_EXPIRE_IPSEC_INIT is set to), and if the client
+ * retries during that time the timer will be reset. This could easily lead
+ * to a Denial of Service, so we limit the number of retries that will
+ * reset the timer. This means the maximum time the server could be blocked
+ * is ((IPSEC_INIT_RETRIES + 1) * MASQUERADE_EXPIRE_IPSEC_INIT).
+ *
+ * Note: blocking will not affect already-established traffic (i.e. where
+ * the inbound SPI has been associated with an outbound SPI).
+ */
+#define MASQUERADE_EXPIRE_IPSEC_INIT 2*60*HZ
+#define IPSEC_INIT_RETRIES 5
+
+/*
+ * ...connections that don't get an answer are squelched
+ * (recognized but ignored) for a short time to prevent DoS.
+ * SPI values 1-255 are reserved by the IANA and are currently (2/99)
+ * not assigned. If that should change, this number must also be changed
+ * to an unused NONZERO value:
+ */
+#define IPSEC_INIT_SQUELCHED 1
+
+struct ip_masq * ip_masq_out_get_ipsec(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port, __u32 o_spi);
+struct ip_masq * ip_masq_in_get_ipsec(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port, __u32 i_spi);
+struct ip_masq * ip_masq_out_get_isakmp(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port, __u32 cookie);
+struct ip_masq * ip_masq_in_get_isakmp(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port, __u32 cookie);
+
+#endif /* CONFIG_IP_MASQUERADE_IPSEC */
+
 /*
  *	Implement IP packet masquerading
  */
@@ -53,6 +200,9 @@
 
 /*
  * masq_proto_num returns 0 for UDP, 1 for TCP, 2 for ICMP
+ *
+ * No, I am NOT going to add GRE/ESP/AH support to everything that relies on this...
+ *
  */
 
 static int masq_proto_num(unsigned proto)
@@ -60,6 +210,12 @@
    switch (proto)
    {
       case IPPROTO_UDP:  return (0); break;
+#ifdef CONFIG_IP_MASQUERADE_PPTP
+      case IPPROTO_GRE:
+#endif /* CONFIG_IP_MASQUERADE_PPTP */
+#ifdef CONFIG_IP_MASQUERADE_IPSEC
+      case IPPROTO_ESP:
+#endif /* CONFIG_IP_MASQUERADE_IPSEC */
       case IPPROTO_TCP:  return (1); break;
       case IPPROTO_ICMP: return (2); break;
       default:           return (-1); break;
@@ -96,6 +252,24 @@
 
 static __inline__ const char *masq_proto_name(unsigned proto)
 {
+
+	/*
+	 * I don't want to track down everything that
+	 * relies on masq_proto_num() and make it GRE/ESP/AH-tolerant.
+	 */
+#ifdef CONFIG_IP_MASQUERADE_PPTP
+	if (proto == IPPROTO_GRE) {
+          return strGREProt;
+	}
+#endif /* CONFIG_IP_MASQUERADE_PPTP */
+#ifdef CONFIG_IP_MASQUERADE_IPSEC
+	if (proto == IPPROTO_ESP) {
+          return strESPProt;
+	} else if (proto == IPPROTO_AH) {
+          return strAHProt;
+	}
+#endif /* CONFIG_IP_MASQUERADE_IPSEC */
+
         return strProt[masq_proto_num(proto)];
 }
 
@@ -140,6 +314,14 @@
 struct ip_masq *ip_masq_m_tab[IP_MASQ_TAB_SIZE];
 struct ip_masq *ip_masq_s_tab[IP_MASQ_TAB_SIZE];
 
+#ifdef CONFIG_IP_MASQUERADE_IPSEC
+	/*
+	 * Add a third hash table for input lookup by remote side
+	 */
+struct ip_masq *ip_masq_d_tab[IP_MASQ_TAB_SIZE];
+
+#endif /* CONFIG_IP_MASQUERADE_IPSEC */
+
 /*
  * timeouts
  */
@@ -284,10 +466,26 @@
         /*
          *	Hash by proto,s{addr,port}
          */
+#ifdef CONFIG_IP_MASQUERADE_PPTP
+	if (ms->protocol == IPPROTO_GRE) {
+                /* Ignore the source port (Call ID) when hashing, as
+                 * outbound packets will not be able to supply it...
+                 */
+                hash = ip_masq_hash_key(ms->protocol, ms->saddr, 0);
+        } else
+#endif /* CONFIG_IP_MASQUERADE_PPTP */
         hash = ip_masq_hash_key(ms->protocol, ms->saddr, ms->sport);
         ms->s_link = ip_masq_s_tab[hash];
         ip_masq_s_tab[hash] = ms;
 
+#ifdef CONFIG_IP_MASQUERADE_IPSEC
+        /* 
+          * Hash by proto,d{addr,port}
+          */
+        hash = ip_masq_hash_key(ms->protocol, ms->daddr, ms->dport);
+	ms->d_link = ip_masq_d_tab[hash];
+	ip_masq_d_tab[hash] = ms;
+#endif /* CONFIG_IP_MASQUERADE_IPSEC */
 
         ms->flags |= IP_MASQ_F_HASHED;
         return 1;
@@ -319,6 +517,11 @@
         /*
          *	UNhash by s{addr,port}
          */
+#ifdef CONFIG_IP_MASQUERADE_PPTP
+	if (ms->protocol == IPPROTO_GRE) {
+                hash = ip_masq_hash_key(ms->protocol, ms->saddr, 0);
+        } else
+#endif /* CONFIG_IP_MASQUERADE_PPTP */
         hash = ip_masq_hash_key(ms->protocol, ms->saddr, ms->sport);
         for (ms_p = &ip_masq_s_tab[hash]; *ms_p ; ms_p = &(*ms_p)->s_link)
                 if (ms == (*ms_p))  {
@@ -326,6 +529,18 @@
                         break;
                 }
 
+#ifdef CONFIG_IP_MASQUERADE_IPSEC
+	/* 
+	 * UNhash by d{addr,port}
+	 */
+	hash = ip_masq_hash_key(ms->protocol, ms->daddr, ms->dport);
+	for (ms_p = &ip_masq_d_tab[hash]; *ms_p ; ms_p = &(*ms_p)->d_link)
+		if (ms == (*ms_p))  {
+			*ms_p = ms->d_link;
+			break;
+		}
+#endif /* CONFIG_IP_MASQUERADE_IPSEC */
+
         ms->flags &= ~IP_MASQ_F_HASHED;
         return 1;
 }
@@ -348,6 +563,9 @@
         int protocol;
         __u32 s_addr, d_addr;
         __u16 s_port, d_port;
+#ifdef CONFIG_IP_MASQUERADE_IPSEC
+        __u32 cookie;
+#endif /* CONFIG_IP_MASQUERADE_IPSEC */
 
  	portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]);
         protocol = iph->protocol;
@@ -356,6 +574,13 @@
         d_addr = iph->daddr;
         d_port = portptr[1];
 
+#ifdef CONFIG_IP_MASQUERADE_IPSEC
+        if (protocol == IPPROTO_UDP && ntohs(s_port) == UDP_PORT_ISAKMP && ntohs(d_port) == UDP_PORT_ISAKMP) {
+                cookie = *((__u32 *)&portptr[4]);
+                return ip_masq_in_get_isakmp(protocol, s_addr, s_port, d_addr, d_port, cookie);
+        } else
+#endif /* CONFIG_IP_MASQUERADE_IPSEC */
+
         return ip_masq_in_get_2(protocol, s_addr, s_port, d_addr, d_port);
 }
 
@@ -400,6 +625,32 @@
                         return ms;
 		}
         }
+
+#ifdef CONFIG_IP_MASQUERADE_PPTP
+	if (protocol == IPPROTO_GRE) {
+                for(ms = ip_masq_m_tab[hash]; ms ; ms = ms->m_link) {
+                        if (protocol==ms->protocol &&
+#ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT
+                            ms->mport == d_port && /* ignore source port */
+#else /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */
+                            ms->mport == 0 && ms->sport == 0 &&
+#endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */
+                            s_addr==ms->daddr && d_addr==ms->maddr) {
+#ifdef DEBUG_IP_MASQUERADE_PPTP_VERBOSE
+                                printk(KERN_DEBUG "MASQ: look/in %d %08X:%04hX->%08X:%04hX OK\n",
+                                       protocol,
+                                       s_addr,
+                                       s_port,
+                                       d_addr,
+                                       d_port);
+#endif /* DEBUG_IP_MASQUERADE_PPTP_VERBOSE */
+                                return ms;
+                        }
+		}
+        }
+#endif /* CONFIG_IP_MASQUERADE_PPTP */
+
+
 #ifdef DEBUG_IP_MASQUERADE_VERBOSE
 	printk("MASQ: look/in %d %08X:%04hX->%08X:%04hX fail\n",
 	       protocol,
@@ -411,6 +662,121 @@
         return NULL;
 }
 
+#ifdef CONFIG_IP_MASQUERADE_IPSEC
+struct ip_masq *
+ip_masq_in_get_ipsec(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port, __u32 i_spi)
+{
+        unsigned hash;
+        struct ip_masq *ms;
+
+        if (protocol != IPPROTO_ESP) {
+                return ip_masq_in_get_2(protocol,s_addr,s_port,d_addr,d_port);
+        }
+
+        /* find an entry for a packet coming in from outside,
+         * or find whether there's a setup pending
+         */
+
+        if (i_spi != 0) {
+                /* there's a SPI - look for a completed entry */
+                hash = ip_masq_hash_key(protocol, s_addr, s_port);
+                for(ms = ip_masq_d_tab[hash]; ms ; ms = ms->d_link) {
+                        if (protocol==ms->protocol &&
+                            s_addr==ms->daddr &&
+                            d_addr==ms->maddr &&
+                            ms->ispi != 0 && i_spi==ms->ispi) {
+#ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE
+                                printk(KERN_DEBUG "MASQ: IPSEC look/in %08X->%08X:%08X OK\n",
+                                       s_addr,
+                                       d_addr,
+                                       i_spi);
+#endif
+                                return ms;
+                        }
+                }
+        }
+        
+        /* no joy. look for a pending connection - maybe somebody else's
+         * if we're checking for a pending setup, the d_addr will be zero
+         * to avoid having to know the masq IP.
+         */
+        hash = ip_masq_hash_key(protocol, s_addr, 0);
+        for(ms = ip_masq_d_tab[hash]; ms ; ms = ms->d_link) {
+                if (protocol==ms->protocol &&
+                    s_addr==ms->daddr &&
+                    (d_addr==0 || d_addr==ms->maddr) &&
+                    ms->ispi==0) {
+#ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE
+                        printk(KERN_DEBUG "MASQ: IPSEC look/in %08X->%08X:0 OK\n",
+                               s_addr,
+                               d_addr
+                               );
+#endif
+                        return ms;
+                }
+        }
+
+#ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE
+	printk(KERN_DEBUG "MASQ: IPSEC look/in %08X->%08X:%08X fail\n",
+	       s_addr,
+	       d_addr,
+	       i_spi);
+#endif
+        return NULL;
+}
+
+struct ip_masq *
+ip_masq_in_get_isakmp(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port, __u32 cookie)
+{
+        unsigned hash;
+        struct ip_masq *ms;
+
+#ifdef DEBUG_IP_MASQUERADE_IPSEC
+	printk(KERN_DEBUG "ip_masq_in_get_isakmp(): ");
+	printk("%s -> ", in_ntoa(s_addr));
+	printk("%s cookie %lX\n", in_ntoa(d_addr), ntohl(cookie));
+#endif /* DEBUG_IP_MASQUERADE_IPSEC */
+
+        if (cookie == 0) {
+                printk(KERN_INFO "ip_masq_in_get_isakmp(): ");
+                printk("zero cookie from %s\n", in_ntoa(s_addr));
+        }
+
+        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 &&
+		    cookie==ms->ospi &&
+		    ((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)) {
+#ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE
+			printk(KERN_DEBUG "MASQ: look/in %d %08X:%04hX->%08X:%04hX %08X OK\n",
+			       protocol,
+			       s_addr,
+			       s_port,
+			       d_addr,
+			       d_port,
+                               cookie);
+#endif
+                        return ms;
+		}
+        }
+
+#ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE
+	printk(KERN_DEBUG "MASQ: look/in %d %08X:%04hX->%08X:%04hX %08X fail\n",
+	       protocol,
+	       s_addr,
+	       s_port,
+	       d_addr,
+	       d_port,
+               cookie);
+#endif
+        return NULL;
+}
+
+#endif /* CONFIG_IP_MASQUERADE_IPSEC */
+
 /*
  *	Returns ip_masq associated with addresses found in iph.
  *	called for pkts coming from inside-to-OUTside the firewall.
@@ -423,6 +789,10 @@
         int protocol;
         __u32 s_addr, d_addr;
         __u16 s_port, d_port;
+#ifdef CONFIG_IP_MASQUERADE_IPSEC
+        __u32 cookie;
+#endif /* CONFIG_IP_MASQUERADE_IPSEC */
+
 
  	portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]);
         protocol = iph->protocol;
@@ -431,6 +801,13 @@
         d_addr = iph->daddr;
         d_port = portptr[1];
 
+#ifdef CONFIG_IP_MASQUERADE_IPSEC
+        if (protocol == IPPROTO_UDP && ntohs(s_port) == UDP_PORT_ISAKMP && ntohs(d_port) == UDP_PORT_ISAKMP) {
+                cookie = *((__u32 *)&portptr[4]);
+                return ip_masq_out_get_isakmp(protocol, s_addr, s_port, d_addr, d_port, cookie);
+        } else
+#endif /* CONFIG_IP_MASQUERADE_IPSEC */
+
         return ip_masq_out_get_2(protocol, s_addr, s_port, d_addr, d_port);
 }
 
@@ -455,6 +832,35 @@
         struct ip_masq *ms;
 
 
+#ifdef CONFIG_IP_MASQUERADE_PPTP
+#ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT
+	if (protocol == IPPROTO_GRE) {
+                /*
+                 * Call ID is saved in source port number,
+                 * but we have no way of knowing it on the outbound packet...
+                 * we only know the *other side's* Call ID
+                 */
+
+                hash = ip_masq_hash_key(protocol, s_addr, 0);
+                for(ms = ip_masq_s_tab[hash]; ms ; ms = ms->s_link) {
+                        if (protocol == ms->protocol &&
+                            s_addr == ms->saddr && (s_port == 0 || s_port == ms->sport) &&
+                            d_addr == ms->daddr && d_port == ms->dport ) {
+#ifdef DEBUG_IP_MASQUERADE_VERBOSE
+                                printk(KERN_DEBUG "MASQ: lk/out2 %d %08X:%04hX->%08X:%04hX OK\n",
+                                       protocol,
+                                       s_addr,
+                                       s_port,
+                                       d_addr,
+                                       d_port);
+#endif /* DEBUG_IP_MASQUERADE_VERBOSE */
+                                return ms;
+                        }
+                }
+        }
+#endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */
+#endif /* CONFIG_IP_MASQUERADE_PPTP */
+
         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 &&
@@ -499,6 +905,92 @@
         return NULL;
 }
 
+#ifdef CONFIG_IP_MASQUERADE_IPSEC
+struct ip_masq *
+ip_masq_out_get_ipsec(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port, __u32 o_spi)
+{
+        unsigned hash;
+        struct ip_masq *ms;
+
+	if (protocol != IPPROTO_ESP) {
+                return ip_masq_out_get_2(protocol,s_addr,s_port,d_addr,d_port);
+        }
+
+        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 &&
+		    d_addr==ms->daddr &&
+                    o_spi==ms->ospi) {
+#ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE
+			printk(KERN_DEBUG "MASQ: IPSEC look/out %08X:%08X->%08X OK\n",
+			       s_addr,
+			       o_spi,
+			       d_addr);
+#endif
+                        return ms;
+		}
+        }
+        
+#ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE
+	printk(KERN_DEBUG "MASQ: IPSEC look/out %08X:%08X->%08X fail\n",
+	       s_addr,
+	       o_spi,
+	       d_addr);
+#endif
+        return NULL;
+}
+
+struct ip_masq *
+ip_masq_out_get_isakmp(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port, __u32 cookie)
+{
+        unsigned hash;
+        struct ip_masq *ms;
+
+#ifdef DEBUG_IP_MASQUERADE_IPSEC
+	printk(KERN_DEBUG "ip_masq_out_get_isakmp(): ");
+	printk("%s -> ", in_ntoa(s_addr));
+	printk("%s cookie %lX\n", in_ntoa(d_addr), ntohl(cookie));
+#endif /* DEBUG_IP_MASQUERADE_IPSEC */
+
+        if (cookie == 0) {
+                printk(KERN_INFO "ip_masq_out_get_isakmp(): ");
+                printk("zero cookie from %s\n", in_ntoa(s_addr));
+        }
+
+        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 &&
+		    cookie == ms->ospi &&
+		    s_addr == ms->saddr && s_port == ms->sport &&
+                    d_addr == ms->daddr && d_port == ms->dport ) {
+#ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE
+			printk(KERN_DEBUG "MASQ: lk/out1 %d %08X:%04hX->%08X:%04hX %08X OK\n",
+			       protocol,
+			       s_addr,
+			       s_port,
+			       d_addr,
+			       d_port,
+                               cookie);
+#endif
+                        return ms;
+		}
+        }
+
+#ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE
+	printk(KERN_DEBUG "MASQ: lk/out1 %d %08X:%04hX->%08X:%04hX %08X fail\n",
+	       protocol,
+	       s_addr,
+	       s_port,
+	       d_addr,
+	       d_port,
+               cookie);
+#endif
+        return NULL;
+}
+
+#endif /* CONFIG_IP_MASQUERADE_IPSEC */
+
 /*
  *	Returns ip_masq for given proto,m_addr,m_port.
  *      called by allocation routine to find an unused m_port.
@@ -647,6 +1139,34 @@
         for (ports_tried = 0; 
 	     (*free_ports_p && (ports_tried <= (PORT_MASQ_END - PORT_MASQ_BEGIN)));
 	     ports_tried++){
+
+#ifdef CONFIG_IP_MASQUERADE_PPTP
+#ifndef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT
+                /* Ignoring PPTP call IDs.
+                 * Don't needlessly increase the TCP port pointer.
+                 */
+                if (proto == IPPROTO_GRE) {
+                        ms->mport = 0;
+                        mst = NULL;
+                } else {
+#endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */
+#endif /* CONFIG_IP_MASQUERADE_PPTP */
+
+#ifdef CONFIG_IP_MASQUERADE_IPSEC
+                /* ESP masq keys off the SPI, not the port number.
+                 * Don't needlessly increase the TCP port pointer.
+                 */
+                if (proto == IPPROTO_ESP) {
+                        ms->mport = 0;
+                        mst = NULL;
+                } else {
+                if (proto == IPPROTO_UDP && ntohs(sport) == UDP_PORT_ISAKMP && ntohs(dport) == UDP_PORT_ISAKMP) {
+                        /* the port number cannot be changed */
+                        ms->mport = htons(UDP_PORT_ISAKMP);
+                        mst = NULL;
+                } else {
+#endif /* CONFIG_IP_MASQUERADE_IPSEC */
+
                 save_flags(flags);
                 cli();
                 
@@ -667,6 +1187,18 @@
                  */
                 
                 mst = ip_masq_getbym(proto, ms->maddr, ms->mport);
+
+#ifdef CONFIG_IP_MASQUERADE_PPTP
+#ifndef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT
+                }
+#endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */
+#endif /* CONFIG_IP_MASQUERADE_PPTP */
+
+#ifdef CONFIG_IP_MASQUERADE_IPSEC
+                }
+                }
+#endif /* CONFIG_IP_MASQUERADE_IPSEC */
+
                 if (mst == NULL || matchport) {
                         save_flags(flags);
                         cli();
@@ -707,11 +1239,11 @@
 
 void ip_masq_set_expire(struct ip_masq *ms, unsigned long tout)
 {
+	/* There Can Be Only One (timer on a masq table entry, that is) */
+        del_timer(&ms->timer);
         if (tout) {
                 ms->timer.expires = jiffies+tout;
                 add_timer(&ms->timer);
-        } else {
-                del_timer(&ms->timer);
         }
 }
 
@@ -725,6 +1257,972 @@
 		uh->check=0xFFFF;
 }
 	
+
+#ifdef CONFIG_IP_MASQUERADE_PPTP
+/*
+ *      Masquerade of GRE connections
+ *      to support a PPTP VPN client or server.
+ *      John Hardin <jhardin@wolfenet.com> gets all blame...
+ *      See also http://www.wolfenet.com/~jhardin/ip_masq_pptp.html
+ */
+
+/*
+ *	Handle outbound GRE packets.
+ *
+ *	This is largely a copy of ip_fw_masquerade()
+ */
+
+int ip_fw_masq_gre(struct sk_buff **skb_p, struct device *dev)
+{
+        struct sk_buff 	*skb   = *skb_p;
+        struct iphdr	*iph   = skb->h.iph;
+        struct pptp_gre_header	*greh;
+#ifdef DEBUG_IP_MASQUERADE_PPTP
+#ifdef DEBUG_IP_MASQUERADE_PPTP_VERBOSE
+        __u8		*greraw;
+#endif /* DEBUG_IP_MASQUERADE_PPTP_VERBOSE */
+#endif /* DEBUG_IP_MASQUERADE_PPTP */
+        struct ip_masq	*ms;
+        unsigned long    flags;
+
+
+        greh = (struct pptp_gre_header *)&(((char *)iph)[iph->ihl*4]);
+
+#ifdef DEBUG_IP_MASQUERADE_PPTP
+
+        printk(KERN_DEBUG "ip_fw_masq_gre(): ");
+        printk("Outbound GRE packet from %s", in_ntoa(iph->saddr));
+        printk(" to %s\n", in_ntoa(iph->daddr));
+
+#ifdef DEBUG_IP_MASQUERADE_PPTP_VERBOSE
+	greraw = (__u8 *) greh;
+	printk(KERN_DEBUG "ip_fw_masq_gre(): ");
+	printk("GRE raw: %X %X %X %X %X %X %X %X %X %X %X %X.\n",
+		greraw[0],
+		greraw[1],
+		greraw[2],
+		greraw[3],
+		greraw[4],
+		greraw[5],
+		greraw[6],
+		greraw[7],
+		greraw[8],
+		greraw[9],
+		greraw[10],
+		greraw[11]);
+	printk(KERN_DEBUG "ip_fw_masq_gre(): ");
+	printk("GRE C: %d R: %d K: %d S: %d s: %d recur: %X.\n",
+                greh->has_cksum,
+                greh->has_routing,
+                greh->has_key,
+                greh->has_seq,
+                greh->is_strict,
+                greh->recur);
+	printk(KERN_DEBUG "ip_fw_masq_gre(): ");
+	printk("GRE flags: %X ver: %X.\n", greh->flags, greh->version);
+	printk(KERN_DEBUG "ip_fw_masq_gre(): ");
+	printk("GRE proto: %X.\n", ntohs(greh->protocol));
+#endif /* DEBUG_IP_MASQUERADE_PPTP_VERBOSE */
+#endif /* DEBUG_IP_MASQUERADE_PPTP */
+
+	if (ntohs(greh->protocol) != 0x880B) {
+#ifdef DEBUG_IP_MASQUERADE_PPTP
+	  printk(KERN_INFO "ip_fw_masq_gre(): ");
+	  printk("GRE protocol %X not 0x880B (non-PPTP encap?) - discarding.\n", ntohs(greh->protocol));
+#endif /* DEBUG_IP_MASQUERADE_PPTP */
+	  return -1;
+	}
+
+	/*
+	 *	Look for masq table entry
+	 */
+
+        ms = ip_masq_out_get_2(IPPROTO_GRE,
+                iph->saddr, 0,
+                iph->daddr, 0);
+
+	if (ms!=NULL) {
+                /* delete the expiration timer */
+        	ip_masq_set_expire(ms,0);
+
+		/*
+                 *      Make sure that the masq IP address is correct
+                 *      for dynamic IP...
+		 */
+		if ( (ms->maddr != dev->pa_addr) && (sysctl_ip_dynaddr & 3) ) {
+                        printk(KERN_INFO "ip_fw_masq_gre(): ");
+                        printk("change maddr from %s", in_ntoa(ms->maddr));
+                        printk(" to %s\n", in_ntoa(dev->pa_addr));
+		        save_flags(flags);
+		        cli();
+		        ip_masq_unhash(ms);
+		        ms->maddr = dev->pa_addr;
+		        ip_masq_hash(ms);
+		        restore_flags(flags);
+                }
+	} else {
+                /*
+                 *	Nope, not found, create a new entry for it, maybe
+                 */
+	
+#ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT
+                /* masq table entry has to come from control channel sniffing.
+                 * If we can't find one, it may have expired.
+                 * How can this happen with the control channel active?
+                 */
+	        printk(KERN_INFO "ip_fw_masq_gre(): ");
+                printk("Outbound GRE to %s has no masq table entry.\n",
+                        in_ntoa(iph->daddr));
+                return -1;
+#else /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */
+                /* call IDs ignored, can create masq table entries on the fly. */
+		ms = ip_masq_new(dev, iph->protocol,
+				 iph->saddr, 0,
+				 iph->daddr, 0,
+				 0);
+
+                if (ms == NULL) {
+                        printk(KERN_NOTICE "ip_fw_masq_gre(): Couldn't create masq table entry.\n");
+			return -1;
+                }
+#endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */
+ 	}
+
+        /*
+         *	Set iph source addr from ip_masq obj.
+         */
+ 	iph->saddr = ms->maddr;
+
+ 	/*
+ 	 *	set timeout and check IP header
+ 	 */
+ 	
+        ip_masq_set_expire(ms, MASQUERADE_EXPIRE_PPTP);
+ 	ip_send_check(iph);
+
+#ifdef DEBUG_IP_MASQUERADE_PPTP
+ 	printk(KERN_DEBUG "MASQ: GRE O-routed from %s over %s\n",
+                in_ntoa(ms->maddr), dev->name);
+#endif /* DEBUG_IP_MASQUERADE_PPTP */
+
+	return 0;
+}
+
+/*
+ *	Handle inbound GRE packets.
+ *
+ */
+
+int ip_fw_demasq_gre(struct sk_buff **skb_p, struct device *dev)
+{
+        struct sk_buff 	*skb   = *skb_p;
+ 	struct iphdr	*iph   = skb->h.iph;
+ 	struct pptp_gre_header	*greh;
+#ifdef DEBUG_IP_MASQUERADE_PPTP
+#ifdef DEBUG_IP_MASQUERADE_PPTP_VERBOSE
+	__u8		*greraw;
+#endif /* DEBUG_IP_MASQUERADE_PPTP_VERBOSE */
+#endif /* DEBUG_IP_MASQUERADE_PPTP */
+        struct ip_masq	*ms;
+
+
+	greh = (struct pptp_gre_header *)&(((char *)iph)[iph->ihl*4]);
+
+#ifdef DEBUG_IP_MASQUERADE_PPTP
+
+	printk(KERN_DEBUG "ip_fw_demasq_gre(): ");
+	printk("Inbound GRE packet from %s", in_ntoa(iph->saddr));
+	printk(" to %s\n", in_ntoa(iph->daddr));
+
+#ifdef DEBUG_IP_MASQUERADE_PPTP_VERBOSE
+	greraw = (__u8 *) greh;
+	printk(KERN_DEBUG "ip_fw_demasq_gre(): ");
+	printk("GRE raw: %X %X %X %X %X %X %X %X %X %X %X %X.\n",
+		greraw[0],
+		greraw[1],
+		greraw[2],
+		greraw[3],
+		greraw[4],
+		greraw[5],
+		greraw[6],
+		greraw[7],
+		greraw[8],
+		greraw[9],
+		greraw[10],
+		greraw[11]);
+	printk(KERN_DEBUG "ip_fw_demasq_gre(): ");
+	printk("GRE C: %d R: %d K: %d S: %d s: %d recur: %X.\n",
+                greh->has_cksum,
+                greh->has_routing,
+                greh->has_key,
+                greh->has_seq,
+                greh->is_strict,
+                greh->recur);
+	printk(KERN_DEBUG "ip_fw_demasq_gre(): ");
+	printk("GRE flags: %X ver: %X.\n", greh->flags, greh->version);
+	printk(KERN_DEBUG "ip_fw_demasq_gre(): ");
+	printk("GRE proto: %X.\n", ntohs(greh->protocol));
+#endif /* DEBUG_IP_MASQUERADE_PPTP_VERBOSE */
+
+#ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT
+	printk(KERN_DEBUG "ip_fw_demasq_gre(): ");
+	printk("PPTP call ID: %X.\n", ntohs(greh->call_id));
+#endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */
+
+#endif /* DEBUG_IP_MASQUERADE_PPTP */
+
+	if (ntohs(greh->protocol) != 0x880B) {
+#ifdef DEBUG_IP_MASQUERADE_PPTP
+	  printk(KERN_INFO "ip_fw_demasq_gre(): ");
+	  printk("GRE protocol %X not 0x880B (non-PPTP encap?) - discarding.\n", ntohs(greh->protocol));
+#endif /* DEBUG_IP_MASQUERADE_PPTP */
+	  return -1;
+	}
+
+ 	/*
+ 	 *      Look for a masq table entry and reroute if found
+         */
+
+#ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT
+        ms = ip_masq_getbym(IPPROTO_GRE,
+                iph->daddr, greh->call_id);
+#else /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */
+        ms = ip_masq_in_get_2(IPPROTO_GRE,
+                iph->saddr, 0,
+                iph->daddr, 0);
+#endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */
+
+        if (ms != NULL)
+        {
+                /* delete the expiration timer */
+		ip_masq_set_expire(ms,0);
+
+                iph->daddr = ms->saddr;
+
+#ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT
+                /*
+                 * change peer call ID to original value
+                 * (saved in masq table source port)
+                 */
+
+                greh->call_id = ms->sport;
+
+#ifdef DEBUG_IP_MASQUERADE_PPTP
+	        printk(KERN_DEBUG "ip_fw_demasq_gre(): ");
+                printk("inbound PPTP from %s call ID now %X\n",
+                       in_ntoa(iph->saddr), ntohs(greh->call_id));
+#endif /* DEBUG_IP_MASQUERADE_PPTP */
+#endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */
+
+                /*
+                 * resum checksums and set timeout
+                 */
+		ip_masq_set_expire(ms, MASQUERADE_EXPIRE_PPTP);
+                ip_send_check(iph);
+
+#ifdef DEBUG_IP_MASQUERADE_PPTP
+                printk(KERN_DEBUG "MASQ: GRE I-routed to %s\n", in_ntoa(iph->daddr));
+#endif /* DEBUG_IP_MASQUERADE_PPTP */
+                return 1;
+ 	}
+
+ 	/* sorry, all this trouble for a no-hit :) */
+	printk(KERN_INFO "ip_fw_demasq_gre(): ");
+	printk("Inbound from %s has no masq table entry.\n", in_ntoa(iph->saddr));
+ 	return 0;
+}
+
+#ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT
+/*
+ *      Define all of the PPTP control channel message structures.
+ *      Sniff the control channel looking for start- and end-call
+ *      messages, and masquerade the Call ID as if it was a TCP
+ *      port.
+ */
+
+#define PPTP_CONTROL_PACKET            1
+#define PPTP_MGMT_PACKET               2
+#define PPTP_MAGIC_COOKIE              0x1A2B3C4D
+
+struct PptpPacketHeader {
+       __u16 packetLength;
+       __u16 packetType;
+       __u32 magicCookie;
+};
+
+/* PptpControlMessageType values */
+#define PPTP_START_SESSION_REQUEST     1
+#define PPTP_START_SESSION_REPLY       2
+#define PPTP_STOP_SESSION_REQUEST      3
+#define PPTP_STOP_SESSION_REPLY        4
+#define PPTP_ECHO_REQUEST              5
+#define PPTP_ECHO_REPLY                6
+#define PPTP_OUT_CALL_REQUEST          7
+#define PPTP_OUT_CALL_REPLY            8
+#define PPTP_IN_CALL_REQUEST           9
+#define PPTP_IN_CALL_REPLY             10
+#define PPTP_CALL_CLEAR_REQUEST        11
+#define PPTP_CALL_DISCONNECT_NOTIFY    12
+#define PPTP_CALL_ERROR_NOTIFY         13
+#define PPTP_WAN_ERROR_NOTIFY          14
+#define PPTP_SET_LINK_INFO             15
+
+struct PptpControlHeader {
+    __u16 messageType;
+    __u16 reserved;
+};
+
+struct PptpOutCallRequest {
+    __u16 callID;
+    __u16 callSerialNumber;
+    __u32 minBPS;
+    __u32 maxBPS;
+    __u32 bearerType;
+    __u32 framingType;
+    __u16 packetWindow;
+    __u16 packetProcDelay;
+    __u16 reserved1;
+    __u16 phoneNumberLength;
+    __u16 reserved2;
+    __u8  phoneNumber[64];
+    __u8  subAddress[64];
+};
+
+struct PptpOutCallReply {
+    __u16 callID;
+    __u16 peersCallID;
+    __u8  resultCode;
+    __u8  generalErrorCode;
+    __u16 causeCode;
+    __u32 connectSpeed;
+    __u16 packetWindow;
+    __u16 packetProcDelay;
+    __u32 physChannelID;
+};
+
+struct PptpInCallRequest {
+    __u16 callID;
+    __u16 callSerialNumber;
+    __u32 callBearerType;
+    __u32 physChannelID;
+    __u16 dialedNumberLength;
+    __u16 dialingNumberLength;
+    __u8  dialedNumber[64];
+    __u8  dialingNumber[64];
+    __u8  subAddress[64];
+};
+
+struct PptpInCallReply {
+    __u16 callID;
+    __u16 peersCallID;
+    __u8  resultCode;
+    __u8  generalErrorCode;
+    __u16 packetWindow;
+    __u16 packetProcDelay;
+    __u16 reserved;
+};
+
+struct PptpCallDisconnectNotify {
+    __u16 callID;
+    __u8  resultCode;
+    __u8  generalErrorCode;
+    __u16 causeCode;
+    __u16 reserved;
+    __u8  callStatistics[128];
+};
+
+struct PptpWanErrorNotify {
+    __u16 peersCallID;
+    __u16 reserved;
+    __u32 crcErrors;
+    __u32 framingErrors;
+    __u32 hardwareOverRuns;
+    __u32 bufferOverRuns;
+    __u32 timeoutErrors;
+    __u32 alignmentErrors;
+};
+
+struct PptpSetLinkInfo {
+    __u16 peersCallID;
+    __u16 reserved;
+    __u32 sendAccm;
+    __u32 recvAccm;
+};
+
+
+/* Packet sent to or from PPTP control port. Process it. */
+/* Yes, all of this should be in a kernel module. Real Soon Now... */
+void ip_masq_pptp(struct sk_buff *skb, struct ip_masq *ms, struct device *dev)
+{
+        struct iphdr    *iph   = skb->h.iph;
+        struct PptpPacketHeader  *pptph = NULL;
+        struct PptpControlHeader *ctlh = NULL;
+        union {
+                char *req;
+                struct PptpOutCallRequest       *ocreq;
+                struct PptpOutCallReply         *ocack;
+                struct PptpInCallRequest        *icreq;
+                struct PptpInCallReply          *icack;
+                struct PptpCallDisconnectNotify *disc;
+                struct PptpWanErrorNotify       *wanerr;
+                struct PptpSetLinkInfo          *setlink;
+        } pptpReq;
+        struct ip_masq  *ms_gre = NULL;
+
+        /*
+         * The GRE data channel will be treated as the "control channel"
+         * for the purposes of masq because there are keepalives happening
+         * on the control channel, whereas the data channel may be subject
+         * to relatively long periods of inactivity.
+         */
+         
+        pptph = (struct PptpPacketHeader *)&(((char *)iph)[sizeof(struct iphdr) + sizeof(struct tcphdr)]);
+#ifdef DEBUG_IP_MASQUERADE_PPTP_VERBOSE
+        printk(KERN_DEBUG "ip_masq_pptp(): ");
+        printk("LEN=%d TY=%d MC=%lX\n", ntohs(pptph->packetLength),
+                ntohs(pptph->packetType), ntohl(pptph->magicCookie));
+#endif /* DEBUG_IP_MASQUERADE_PPTP_VERBOSE */
+
+        if (ntohs(pptph->packetType) == PPTP_CONTROL_PACKET &&
+            ntohl(pptph->magicCookie) == PPTP_MAGIC_COOKIE) {
+#ifdef DEBUG_IP_MASQUERADE_PPTP
+                printk(KERN_DEBUG "ip_masq_pptp(): ");
+                printk("PPTP control packet from %s", in_ntoa(iph->saddr));
+                printk(" to %s\n", in_ntoa(iph->daddr));
+#endif /* DEBUG_IP_MASQUERADE_PPTP */
+                ctlh = (struct PptpControlHeader *)&(((char*)pptph)[sizeof(struct PptpPacketHeader)]);
+                pptpReq.req = &(((char*)ctlh)[sizeof(struct PptpControlHeader)]);
+#ifdef DEBUG_IP_MASQUERADE_PPTP_VERBOSE
+                printk(KERN_DEBUG "ip_masq_pptp(): ");
+                printk("MTY=%X R0=%X\n",
+                        ntohs(ctlh->messageType), ctlh->reserved);
+#endif /* DEBUG_IP_MASQUERADE_PPTP_VERBOSE */
+
+                switch (ntohs(ctlh->messageType))
+                {
+                        case PPTP_OUT_CALL_REQUEST:
+                                if (iph->daddr == ms->daddr)    /* outbound only */
+                                {
+#ifdef DEBUG_IP_MASQUERADE_PPTP
+                                        printk(KERN_DEBUG "ip_masq_pptp(): ");
+                                        printk("Call request, call ID %X\n",
+                                                ntohs(pptpReq.ocreq->callID));
+#endif /* DEBUG_IP_MASQUERADE_PPTP */
+                                        ms_gre = ip_masq_new(dev, IPPROTO_GRE,
+                                                ms->saddr, pptpReq.ocreq->callID,
+                                                ms->daddr, 0,
+                                                0);
+                                        if (ms_gre != NULL)
+                                        {
+                                                ms->control = ms_gre;
+                                                ms_gre->flags |= IP_MASQ_F_CONTROL;
+                                                ip_masq_set_expire(ms_gre, 0);
+                                                ip_masq_set_expire(ms_gre, 2*60*HZ);
+                                                pptpReq.ocreq->callID = ms_gre->mport;
+                                                printk(KERN_INFO "ip_masq_pptp(): ");
+                                                printk("Req outcall PPTP sess %s", in_ntoa(ms->saddr));
+                                                printk(" -> %s", in_ntoa(ms->daddr));
+                                                printk(" Call ID %X -> %X.\n", ntohs(ms_gre->sport), ntohs(ms_gre->mport));
+#ifdef DEBUG_IP_MASQUERADE_PPTP
+                                                printk(KERN_DEBUG "ip_masq_pptp(): ");
+                                                printk("masqed call ID %X\n",
+                                                        ntohs(pptpReq.ocreq->callID));
+#endif /* DEBUG_IP_MASQUERADE_PPTP */
+                                        } else {
+                                                printk(KERN_NOTICE "ip_masq_pptp(): ");
+                                                printk("Couldn't create GRE masq table entry (%s)\n", "OUT_CALL_REQ");
+                                        }
+                                }
+                        break;
+                        case PPTP_OUT_CALL_REPLY:
+                                if (iph->saddr == ms->daddr)    /* inbound only */
+                                {
+#ifdef DEBUG_IP_MASQUERADE_PPTP
+                                        printk(KERN_DEBUG "ip_masq_pptp(): ");
+                                        printk("Call reply, peer call ID %X\n",
+                                                ntohs(pptpReq.ocack->peersCallID));
+#endif /* DEBUG_IP_MASQUERADE_PPTP */
+                                        ms_gre = ip_masq_getbym(IPPROTO_GRE,
+                                                ms->maddr, pptpReq.ocack->peersCallID);
+                                        if (ms_gre != NULL)
+                                        {
+                                                ip_masq_set_expire(ms_gre, 0);
+                                                ip_masq_set_expire(ms_gre, 2*60*HZ);
+                                                pptpReq.ocack->peersCallID = ms_gre->sport;
+                                                printk(KERN_INFO "ip_masq_pptp(): ");
+                                                printk("Estab outcall PPTP sess %s", in_ntoa(ms->saddr));
+                                                printk(" -> %s", in_ntoa(ms->daddr));
+                                                printk(" Call ID %X -> %X.\n", ntohs(ms_gre->sport), ntohs(ms_gre->mport));
+#ifdef DEBUG_IP_MASQUERADE_PPTP
+                                                printk(KERN_DEBUG "ip_masq_pptp(): ");
+                                                printk("unmasqed call ID %X\n",
+                                                        ntohs(pptpReq.ocack->callID));
+#endif /* DEBUG_IP_MASQUERADE_PPTP */
+                                        } else {
+                                                printk(KERN_INFO "ip_masq_pptp(): ");
+                                                printk("Lost GRE masq table entry (%s)\n", "OUT_CALL_REPLY");
+                                        }
+                                }
+                        break;
+                        case PPTP_IN_CALL_REQUEST:
+                                if (iph->daddr == ms->daddr)    /* outbound only */
+                                {
+#ifdef DEBUG_IP_MASQUERADE_PPTP
+                                        printk(KERN_DEBUG "ip_masq_pptp(): ");
+                                        printk("Call request, call ID %X\n",
+                                                ntohs(pptpReq.icreq->callID));
+#endif /* DEBUG_IP_MASQUERADE_PPTP */
+                                        ms_gre = ip_masq_new(dev, IPPROTO_GRE,
+                                                 ms->saddr, pptpReq.icreq->callID,
+                                                 ms->daddr, 0,
+                                                 0);
+                                        if (ms_gre != NULL)
+                                        {
+                                                ms->control = ms_gre;
+                                                ms_gre->flags |= IP_MASQ_F_CONTROL;
+                                                ip_masq_set_expire(ms_gre, 0);
+                                                ip_masq_set_expire(ms_gre, 2*60*HZ);
+                                                pptpReq.icreq->callID = ms_gre->mport;
+                                                printk(KERN_INFO "ip_masq_pptp(): ");
+                                                printk("Req incall PPTP sess %s", in_ntoa(ms->saddr));
+                                                printk(" -> %s", in_ntoa(ms->daddr));
+                                                printk(" Call ID %X -> %X.\n", ntohs(ms_gre->sport), ntohs(ms_gre->mport));
+#ifdef DEBUG_IP_MASQUERADE_PPTP
+                                                printk(KERN_DEBUG "ip_masq_pptp(): ");
+                                                printk("masqed call ID %X\n",
+                                                        ntohs(pptpReq.icreq->callID));
+#endif /* DEBUG_IP_MASQUERADE_PPTP */
+                                        } else {
+                                                printk(KERN_NOTICE "ip_masq_pptp(): ");
+                                                printk("Couldn't create GRE masq table entry (%s)\n", "IN_CALL_REQ");
+                                        }
+                                }
+                        break;
+                        case PPTP_IN_CALL_REPLY:
+                                if (iph->saddr == ms->daddr)    /* inbound only */
+                                {
+#ifdef DEBUG_IP_MASQUERADE_PPTP
+                                        printk(KERN_DEBUG "ip_masq_pptp(): ");
+                                        printk("Call reply, peer call ID %X\n",
+                                                ntohs(pptpReq.icack->peersCallID));
+#endif /* DEBUG_IP_MASQUERADE_PPTP */
+                                        ms_gre = ip_masq_getbym(IPPROTO_GRE,
+                                                ms->maddr, pptpReq.icack->peersCallID);
+                                        if (ms_gre != NULL)
+                                        {
+                                                ip_masq_set_expire(ms_gre, 0);
+                                                ip_masq_set_expire(ms_gre, 2*60*HZ);
+                                                pptpReq.icack->peersCallID = ms_gre->sport;
+                                                printk(KERN_INFO "ip_masq_pptp(): ");
+                                                printk("Estab incall PPTP sess %s", in_ntoa(ms->saddr));
+                                                printk(" -> %s", in_ntoa(ms->daddr));
+                                                printk(" Call ID %X -> %X.\n", ntohs(ms_gre->sport), ntohs(ms_gre->mport));
+#ifdef DEBUG_IP_MASQUERADE_PPTP
+                                                printk(KERN_DEBUG "ip_masq_pptp(): ");
+                                                printk("unmasqed call ID %X\n",
+                                                        ntohs(pptpReq.icack->callID));
+#endif /* DEBUG_IP_MASQUERADE_PPTP */
+                                        } else {
+                                                printk(KERN_INFO "ip_masq_pptp(): ");
+                                                printk("Lost GRE masq table entry (%s)\n", "IN_CALL_REPLY");
+                                        }
+                                }
+                        break;
+                        case PPTP_CALL_DISCONNECT_NOTIFY:
+                                if (iph->daddr == ms->daddr)    /* outbound only */
+                                {
+#ifdef DEBUG_IP_MASQUERADE_PPTP
+                                        printk(KERN_DEBUG "ip_masq_pptp(): ");
+                                        printk("Disconnect notify, call ID %X\n",
+                                                ntohs(pptpReq.disc->callID));
+#endif /* DEBUG_IP_MASQUERADE_PPTP */
+                                        ms_gre = ip_masq_out_get_2(IPPROTO_GRE,
+                                                iph->saddr, pptpReq.disc->callID,
+                                                iph->daddr, 0);
+                                        if (ms_gre != NULL)
+                                        {
+                                                /*
+                                                 * expire the data channel
+                                                 * table entry quickly now.
+                                                 */
+                                                ip_masq_set_expire(ms_gre, 0);
+                                                ip_masq_set_expire(ms_gre, 30*HZ);
+                                                ms->control = NULL;
+                                                ms_gre->flags &= ~IP_MASQ_F_CONTROL;
+                                                pptpReq.disc->callID = ms_gre->mport;
+                                                printk(KERN_INFO "ip_masq_pptp(): ");
+                                                printk("Disconnect PPTP sess %s", in_ntoa(ms->saddr));
+                                                printk(" -> %s", in_ntoa(ms->daddr));
+                                                printk(" Call ID %X -> %X.\n", ntohs(ms_gre->sport), ntohs(ms_gre->mport));
+#ifdef DEBUG_IP_MASQUERADE_PPTP
+                                                printk(KERN_DEBUG "ip_masq_pptp(): ");
+                                                printk("masqed call ID %X\n",
+                                                        ntohs(pptpReq.disc->callID));
+#endif /* DEBUG_IP_MASQUERADE_PPTP */
+                                        }
+                                }
+                        break;
+                        case PPTP_WAN_ERROR_NOTIFY:
+                                if (iph->saddr == ms->daddr)    /* inbound only */
+                                {
+#ifdef DEBUG_IP_MASQUERADE_PPTP
+                                        printk(KERN_DEBUG "ip_masq_pptp(): ");
+                                        printk("Error notify, peer call ID %X\n",
+                                                ntohs(pptpReq.wanerr->peersCallID));
+#endif /* DEBUG_IP_MASQUERADE_PPTP */
+                                        ms_gre = ip_masq_getbym(IPPROTO_GRE,
+                                                ms->maddr, pptpReq.wanerr->peersCallID);
+                                        if (ms_gre != NULL)
+                                        {
+                                                pptpReq.wanerr->peersCallID = ms_gre->sport;
+#ifdef DEBUG_IP_MASQUERADE_PPTP
+                                                printk(KERN_DEBUG "ip_masq_pptp(): ");
+                                                printk("unmasqed call ID %X\n",
+                                                        ntohs(pptpReq.wanerr->peersCallID));
+#endif /* DEBUG_IP_MASQUERADE_PPTP */
+                                        } else {
+                                                printk(KERN_INFO "ip_masq_pptp(): ");
+                                                printk("Lost GRE masq table entry (%s)\n", "WAN_ERROR_NOTIFY");
+                                        }
+                                }
+                        break;
+                        case PPTP_SET_LINK_INFO:
+                                if (iph->saddr == ms->daddr)    /* inbound only */
+                                {
+#ifdef DEBUG_IP_MASQUERADE_PPTP
+                                        printk(KERN_DEBUG "ip_masq_pptp(): ");
+                                        printk("Set link info, peer call ID %X\n",
+                                                ntohs(pptpReq.setlink->peersCallID));
+#endif /* DEBUG_IP_MASQUERADE_PPTP */
+                                        ms_gre = ip_masq_getbym(IPPROTO_GRE,
+                                                ms->maddr, pptpReq.setlink->peersCallID);
+                                        if (ms_gre != NULL)
+                                        {
+                                                pptpReq.setlink->peersCallID = ms_gre->sport;
+#ifdef DEBUG_IP_MASQUERADE_PPTP
+                                                printk(KERN_DEBUG "ip_masq_pptp(): ");
+                                                printk("unmasqed call ID %X\n",
+                                                        ntohs(pptpReq.setlink->peersCallID));
+#endif /* DEBUG_IP_MASQUERADE_PPTP */
+                                        } else {
+                                                printk(KERN_INFO "ip_masq_pptp(): ");
+                                                printk("Lost GRE masq table entry (%s)\n", "SET_LINK_INFO");
+                                        }
+                                }
+                        break;
+                }
+        }
+}
+#endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */
+
+static struct symbol_table pptp_masq_syms = {
+#include <linux/symtab_begin.h>
+	X(ip_fw_masq_gre),
+	X(ip_fw_demasq_gre),
+#ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT
+	X(ip_masq_pptp),
+#endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */
+#include <linux/symtab_end.h>
+};
+
+#endif /* CONFIG_IP_MASQUERADE_PPTP */
+
+
+#ifdef CONFIG_IP_MASQUERADE_IPSEC
+/*
+ *      Quick-and-dirty handling of ESP connections
+ *      John Hardin <jhardin@wolfenet.com> gets all blame...
+ */
+
+/*
+ *	Handle outbound ESP packets.
+ *
+ *	This is largely a copy of ip_fw_masquerade()
+ *
+ * To associate inbound traffic with outbound traffic, we only
+ * allow one session per remote host to be negotiated at a time.
+ * If a packet comes in and there's no masq table entry for it,
+ * then check for other masq table entries for the same server
+ * with the inbound SPI set to zero (i.e. no response yet). If
+ * found, discard the packet.
+ * This will DoS the server for the duration of the connection
+ * attempt, so keep the masq entry's lifetime short until a
+ * response comes in.
+ */
+
+int ip_fw_masq_esp(struct sk_buff **skb_p, struct device *dev)
+{
+        struct sk_buff 	*skb   = *skb_p;
+        struct iphdr	*iph   = skb->h.iph;
+        struct ip_masq	*ms;
+        unsigned long    flags;
+        __u32 o_spi;
+        __u16 fake_sport;
+        unsigned long    timeout = MASQUERADE_EXPIRE_IPSEC;
+
+        o_spi = *((__u32 *)&(((char *)iph)[iph->ihl*4]));
+        fake_sport = (__u16) ntohl(o_spi) & 0xffff;
+
+#ifdef DEBUG_IP_MASQUERADE_IPSEC
+        printk(KERN_DEBUG "ip_fw_masq_esp(): ");
+        printk("pkt %s", in_ntoa(iph->saddr));
+        printk(" -> %s SPI %lX (fakeport %X)\n", in_ntoa(iph->daddr), ntohl(o_spi), fake_sport);
+#endif /* DEBUG_IP_MASQUERADE_IPSEC */
+
+        if (o_spi == 0) {
+                /* illegal SPI - discard */
+                printk(KERN_INFO "ip_fw_masq_esp(): ");
+                printk("zero SPI from %s discarded\n", in_ntoa(iph->saddr));
+                return -1;
+        }
+
+	/*
+	 *	Look for masq table entry
+	 */
+
+        ms = ip_masq_out_get_ipsec(IPPROTO_ESP,
+                iph->saddr, fake_sport,
+                iph->daddr, 0,
+                o_spi);
+
+	if (ms!=NULL) {
+                if (ms->ispi == IPSEC_INIT_SQUELCHED) {
+                        /* squelched: toss the packet without changing the timer */
+#ifdef DEBUG_IP_MASQUERADE_IPSEC
+                        printk(KERN_INFO "ip_fw_masq_esp(): ");
+			printk("init %s ", in_ntoa(iph->saddr));
+			printk("-> %s SPI %lX ", in_ntoa(iph->daddr), ntohl(o_spi));
+			printk("squelched\n");
+#endif /* DEBUG_IP_MASQUERADE_IPSEC */
+                        return -1;
+                }
+
+                /* delete the expiration timer */
+        	ip_masq_set_expire(ms,0);
+
+		/*
+                 *      Make sure that the masq IP address is correct
+                 *      for dynamic IP...
+		 */
+		if ( (ms->maddr != dev->pa_addr) && (sysctl_ip_dynaddr & 3) ) {
+                        printk(KERN_INFO "ip_fw_masq_esp(): ");
+                        printk("change maddr from %s", in_ntoa(ms->maddr));
+                        printk(" to %s\n", in_ntoa(dev->pa_addr));
+		        save_flags(flags);
+		        cli();
+		        ip_masq_unhash(ms);
+		        ms->maddr = dev->pa_addr;
+		        ip_masq_hash(ms);
+		        restore_flags(flags);
+                }
+
+                if (ms->ispi == 0) {
+                        /* no response yet, keep timeout short */
+			timeout = MASQUERADE_EXPIRE_IPSEC_INIT;
+                        if (ms->blocking) {
+                                /* prevent DoS: limit init packet timer resets */
+                                ms->ocnt++;
+        #ifdef DEBUG_IP_MASQUERADE_IPSEC
+                                printk(KERN_INFO "ip_fw_masq_esp(): ");
+                                printk("init %s ", in_ntoa(iph->saddr));
+                                printk("-> %s SPI %lX ", in_ntoa(iph->daddr), ntohl(o_spi));
+                                printk("retry %d\n", ms->ocnt);
+        #endif /* DEBUG_IP_MASQUERADE_IPSEC */
+                                if (ms->ocnt > IPSEC_INIT_RETRIES) {
+                                        /* more than IPSEC_INIT_RETRIES tries, give up */
+                                        printk(KERN_INFO "ip_fw_masq_esp(): ");
+                                        printk("init %s ", in_ntoa(iph->saddr));
+                                        printk("-> %s SPI %lX ", in_ntoa(iph->daddr), ntohl(o_spi));
+                                        printk("no response after %d tries, unblocking & squelching\n", ms->ocnt);
+                                        /* squelch that source+SPI for a bit */
+                                        timeout = 30*HZ;
+                                        save_flags(flags);
+                                        cli();
+                                        ip_masq_unhash(ms);
+                                        ms->ispi = IPSEC_INIT_SQUELCHED;
+                                        ms->dport = IPSEC_INIT_SQUELCHED;
+                                        ip_masq_hash(ms);
+                                        restore_flags(flags);
+                                        ip_masq_set_expire(ms, timeout);
+                                        /* toss the packet */
+                                        return -1;
+                                }
+                        }
+                }
+	} else {
+                /*
+                 *	Nope, not found, create a new entry for it, maybe
+                 */
+	
+                /* see if there are any pending inits with the same destination... */
+                ms = ip_masq_in_get_ipsec(IPPROTO_ESP,
+                        iph->daddr, 0,
+                        0, 0,
+                        0);
+                
+                if (ms != NULL) {
+                        /* found one with ispi == 0 */
+                        if (ms->saddr != iph->saddr) {
+                                /* it's not ours, don't step on their toes */
+                                printk(KERN_INFO "ip_fw_masq_esp(): ");
+                                printk("init %s ", in_ntoa(iph->saddr));
+                                printk("-> %s ", in_ntoa(iph->daddr));
+                                printk("temporarily blocked by pending ");
+                                printk("%s init\n", in_ntoa(ms->saddr));
+                                /* let it know it has competition */
+                                ms->blocking = 1;
+                                /* toss the packet */
+                                return -1;
+                        }
+                        if (ms->ospi != o_spi) {
+                                /* SPIs differ, still waiting for a previous attempt to expire */
+                                printk(KERN_INFO "ip_fw_masq_esp(): ");
+                                printk("init %s ", in_ntoa(iph->saddr));
+                                printk("-> %s SPI %lX ", in_ntoa(iph->daddr), ntohl(o_spi));
+                                printk("temporarily blocked by pending ");
+                                printk("init w/ SPI %lX\n", ntohl(ms->ospi));
+                                /* let it know it has competition */
+                                ms->blocking = 1;
+                                /* toss the packet */
+                                return -1;
+                        }
+                } else  /* nothing pending, make new entry, pending response */
+                        ms = ip_masq_new(dev, iph->protocol,
+				 iph->saddr, fake_sport,
+				 iph->daddr, 0,
+				 0);
+
+                if (ms == NULL) {
+                        printk(KERN_NOTICE "ip_fw_masq_esp(): Couldn't create masq table entry.\n");
+			return -1;
+                }
+
+                ms->blocking = ms->ocnt = 0;
+                ms->ospi = o_spi;
+                timeout = MASQUERADE_EXPIRE_IPSEC_INIT;      /* fairly brief timeout while waiting for a response */
+ 	}
+
+        /*
+         *	Set iph source addr from ip_masq obj.
+         */
+ 	iph->saddr = ms->maddr;
+
+ 	/*
+ 	 *	set timeout and check IP header
+ 	 */
+ 	
+        ip_masq_set_expire(ms, timeout);
+ 	ip_send_check(iph);
+
+#ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE
+ 	printk(KERN_DEBUG "MASQ: ESP O-routed from %s over %s\n",
+                in_ntoa(ms->maddr), dev->name);
+#endif /* DEBUG_IP_MASQUERADE_IPSEC_VERBOSE */
+
+	return 0;
+}
+
+/*
+ *	Handle inbound ESP packets.
+ *
+ */
+
+int ip_fw_demasq_esp(struct sk_buff **skb_p, struct device *dev)
+{
+        struct sk_buff 	*skb   = *skb_p;
+ 	struct iphdr	*iph   = skb->h.iph;
+        struct ip_masq	*ms;
+        unsigned long    flags;
+        __u32 i_spi;
+        __u16 fake_sport;
+
+
+        i_spi = *((__u32 *)&(((char *)iph)[iph->ihl*4]));
+        fake_sport = (__u16) ntohl(i_spi) & 0xffff;
+
+#ifdef DEBUG_IP_MASQUERADE_IPSEC
+        printk(KERN_DEBUG "ip_fw_demasq_esp(): ");
+	printk("pkt %s", in_ntoa(iph->saddr));
+	printk(" -> %s SPI %lX (fakeport %X)\n", in_ntoa(iph->daddr), ntohl(i_spi), fake_sport);
+#endif /* DEBUG_IP_MASQUERADE_IPSEC */
+
+        if (i_spi == 0) {
+                /* illegal SPI - discard */
+                printk(KERN_INFO "ip_fw_demasq_esp(): ");
+                printk("zero SPI from %s discarded\n", in_ntoa(iph->saddr));
+                return 0;
+        }
+
+        if (i_spi == IPSEC_INIT_SQUELCHED) {
+                /* Ack! This shouldn't happen! */
+                printk(KERN_NOTICE "ip_fw_demasq_esp(): ");
+                printk("SPI from %s is IPSEC_INIT_SQUELCHED - modify ip_masq.c!\n", in_ntoa(iph->saddr));
+                return 0;
+        }
+
+ 	/*
+ 	 *      Look for a masq table entry and reroute if found
+         */
+
+        ms = ip_masq_in_get_ipsec(IPPROTO_ESP,
+                iph->saddr, fake_sport,
+                iph->daddr, 0,
+                i_spi);
+
+        if (ms != NULL)
+        {
+                /* delete the expiration timer */
+		ip_masq_set_expire(ms,0);
+
+                iph->daddr = ms->saddr;
+
+                if (ms->ispi == 0) {
+#ifdef DEBUG_IP_MASQUERADE_IPSEC
+                        printk(KERN_INFO "ip_fw_demasq_esp(): ");
+                        printk("resp from %s SPI %lX", in_ntoa(iph->saddr), ntohl(i_spi));
+                        printk(" routed to %s (SPI %lX)\n", in_ntoa(ms->saddr), ntohl(ms->ospi));
+#endif /* DEBUG_IP_MASQUERADE_IPSEC */
+		        save_flags(flags);
+		        cli();
+		        ip_masq_unhash(ms);
+		        ms->ispi = i_spi;
+		        ms->dport = fake_sport;
+		        ip_masq_hash(ms);
+		        restore_flags(flags);
+                }
+                
+                /*
+                 * resum checksums and set timeout
+                 */
+		ip_masq_set_expire(ms, MASQUERADE_EXPIRE_IPSEC);
+                ip_send_check(iph);
+
+#ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE
+                printk(KERN_DEBUG "MASQ: ESP I-routed to %s\n", in_ntoa(iph->daddr));
+#endif /* DEBUG_IP_MASQUERADE_IPSEC_VERBOSE */
+                return 1;
+ 	}
+
+ 	/* sorry, all this trouble for a no-hit :) */
+        printk(KERN_INFO "ip_fw_demasq_esp(): ");
+	printk("Inbound from %s SPI %lX has no masq table entry.\n", in_ntoa(iph->saddr), ntohl(i_spi));
+ 	return 0;
+}
+
+static struct symbol_table ipsec_masq_syms = {
+#include <linux/symtab_begin.h>
+	X(ip_masq_out_get_ipsec),
+	X(ip_masq_in_get_ipsec),
+	X(ip_masq_out_get_isakmp),
+	X(ip_masq_in_get_isakmp),
+	X(ip_fw_masq_esp),
+	X(ip_fw_demasq_esp),
+#include <linux/symtab_end.h>
+};
+
+#endif /* CONFIG_IP_MASQUERADE_IPSEC */
+
+
 int ip_fw_masquerade(struct sk_buff **skb_ptr, struct device *dev)
 {
 	struct sk_buff  *skb=*skb_ptr;
@@ -742,6 +2240,38 @@
 
         if (iph->protocol==IPPROTO_ICMP) 
             return (ip_fw_masq_icmp(skb_ptr,dev));
+        if (iph->protocol==IPPROTO_GRE) 
+#ifdef CONFIG_IP_MASQUERADE_PPTP
+            return (ip_fw_masq_gre(skb_ptr,dev));
+#else
+        {
+                /* received the traffic but can't handle it - let user know. */
+                printk(KERN_INFO "MASQ: %s %s ", "GRE", in_ntoa(iph->saddr));
+                printk("-> %s: ", in_ntoa(iph->daddr));
+                printk("%s Masq not enabled - reconfigure kernel\n", "PPTP");
+                return -1;
+        }
+#endif /* CONFIG_IP_MASQUERADE_PPTP */
+        if (iph->protocol==IPPROTO_ESP) 
+#ifdef CONFIG_IP_MASQUERADE_IPSEC
+            return (ip_fw_masq_esp(skb_ptr,dev));
+#else
+        {
+                /* received the traffic but can't handle it - let user know. */
+                printk(KERN_INFO "MASQ: %s %s ", "ESP", in_ntoa(iph->saddr));
+                printk("-> %s: ", in_ntoa(iph->daddr));
+                printk("%s Masq not enabled - reconfigure kernel\n", "IPSEC/ISAKMP");
+                return -1;
+        }
+#endif /* CONFIG_IP_MASQUERADE_IPSEC */
+        if (iph->protocol==IPPROTO_AH) 
+        {
+                /* received the traffic but can't handle it - let user know. */
+                printk(KERN_INFO "MASQ: %s %s ", "AH", in_ntoa(iph->saddr));
+                printk("-> %s: ", in_ntoa(iph->daddr));
+                printk("protocol cannot be masqueraded.\n");
+                return -1;
+        }
 	if (iph->protocol!=IPPROTO_UDP && iph->protocol!=IPPROTO_TCP)
 		return -1;
 
@@ -758,6 +2288,7 @@
 #endif
 
         ms = ip_masq_out_get(iph);
+
 	if (ms!=NULL) {
                 ip_masq_set_expire(ms,0);
                 
@@ -835,8 +2366,29 @@
                         	         0);
                 if (ms == NULL)
 			return -1;
+
+#ifdef CONFIG_IP_MASQUERADE_IPSEC
+                if (iph->protocol == IPPROTO_UDP && ntohs(portptr[0]) == UDP_PORT_ISAKMP && ntohs(portptr[1]) == UDP_PORT_ISAKMP) {
+                        /* save the initiator cookie */
+                        ms->ospi = *((__u32 *)&portptr[4]);
+                }
+#endif /* CONFIG_IP_MASQUERADE_IPSEC */
  	}
 
+#ifdef CONFIG_IP_MASQUERADE_PPTP
+#ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT
+	if (iph->protocol == IPPROTO_TCP && ntohs(portptr[1]) == PPTP_CONTROL_PORT)
+	{
+                /*
+                 * Packet sent to PPTP control port. Process it.
+                 * May change call ID word in request, but
+                 * packet length will not change.
+                 */
+		ip_masq_pptp(skb, ms, dev);
+	}
+#endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */
+#endif /* CONFIG_IP_MASQUERADE_PPTP */
+
  	/*
  	 *	Change the fragments origin
  	 */
@@ -1360,14 +2912,23 @@
  	__u16	*portptr;
  	struct ip_masq	*ms;
 	unsigned short len;
-	unsigned long 	timeout;
+	unsigned long 	timeout = MASQUERADE_EXPIRE_TCP;
 #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW 
  	struct ip_autofw *af;
 #endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
 
+
 	switch (iph->protocol) {
 	case IPPROTO_ICMP:
 		return(ip_fw_demasq_icmp(skb_p, dev));
+#ifdef CONFIG_IP_MASQUERADE_PPTP
+	case IPPROTO_GRE:
+		return(ip_fw_demasq_gre(skb_p, dev));
+#endif /* CONFIG_IP_MASQUERADE_PPTP */
+#ifdef CONFIG_IP_MASQUERADE_IPSEC
+	case IPPROTO_ESP:
+		return(ip_fw_demasq_esp(skb_p, dev));
+#endif /* CONFIG_IP_MASQUERADE_IPSEC */
 	case IPPROTO_TCP:
 	case IPPROTO_UDP:
 		/* Make sure packet is in the masq range */
@@ -1380,6 +2941,9 @@
 		    && !ip_autofw_check_direct(portptr[1], iph->protocol)
 		    && !ip_autofw_check_port(portptr[1], iph->protocol)
 #endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
+#ifdef CONFIG_IP_MASQUERADE_IPSEC
+                    && ((iph->protocol != IPPROTO_UDP) || (ntohs(portptr[0]) != UDP_PORT_ISAKMP) || (ntohs(portptr[1]) != UDP_PORT_ISAKMP))
+#endif /* CONFIG_IP_MASQUERADE_IPSEC */
 			)
 			return 0;
 
@@ -1502,6 +3066,20 @@
                         len = ntohs(iph->tot_len) - (iph->ihl * 4);
                 }
 
+#ifdef CONFIG_IP_MASQUERADE_PPTP
+#ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT
+                if (iph->protocol == IPPROTO_TCP && ntohs(portptr[0]) == PPTP_CONTROL_PORT)
+                {
+                        /*
+                         * Packet received from PPTP control port. Process it.
+                         * May change call ID word in request, but
+                         * packet length will not change.
+                         */
+                        ip_masq_pptp(skb, ms, dev);
+                }
+#endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */
+#endif /* CONFIG_IP_MASQUERADE_PPTP */
+
                 /*
                  * Yug! adjust UDP/TCP and IP checksums, also update
 		 * timeouts.
@@ -1675,6 +3253,12 @@
 int ip_masq_init(void)
 {
         register_symtab (&ip_masq_syms);
+#ifdef CONFIG_IP_MASQUERADE_PPTP
+        register_symtab (&pptp_masq_syms);
+#endif /* CONFIG_IP_MASQUERADE_PPTP */
+#ifdef CONFIG_IP_MASQUERADE_IPSEC
+        register_symtab (&ipsec_masq_syms);
+#endif /* CONFIG_IP_MASQUERADE_IPSEC */
 #ifdef CONFIG_PROC_FS        
 	proc_net_register(&(struct proc_dir_entry) {
 		PROC_NET_IPMSQHST, 13, "ip_masquerade",
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/net/ipv4/ip_sockglue.c linux/net/ipv4/ip_sockglue.c
--- linux.vanilla/net/ipv4/ip_sockglue.c	Sun Jun 21 18:41:02 1998
+++ linux/net/ipv4/ip_sockglue.c	Wed Mar 17 23:27:14 1999
@@ -455,9 +455,6 @@
 int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen)
 {
 	int val,err;
-#ifdef CONFIG_IP_MULTICAST
-	int len;
-#endif
 	
 	if(level!=SOL_IP)
 		return -EOPNOTSUPP;
@@ -541,16 +538,33 @@
 			val=sk->ip_mc_loop;
 			break;
 		case IP_MULTICAST_IF:
+		{
+			struct device *dev;
+			struct in_addr ia;
+			
 			err=verify_area(VERIFY_WRITE, optlen, sizeof(int));
 			if(err)
   				return err;
-  			len=strlen(sk->ip_mc_name);
-  			err=verify_area(VERIFY_WRITE, optval, len);
+  			err=verify_area(VERIFY_WRITE, optval, sizeof(ia));
 		  	if(err)
   				return err;
-  			put_user(len,(int *) optlen);
-			memcpy_tofs((void *)optval,sk->ip_mc_name, len);
+  			
+  			if(*sk->ip_mc_name)
+  			{
+	  			dev=dev_get(sk->ip_mc_name);
+  				/* Someone ran off with the interface, its probably
+  				   been downed. */
+  				if(dev==NULL)
+  					return -ENODEV;
+	  			ia.s_addr = dev->pa_addr;
+	  		}
+	  		else
+	  			ia.s_addr = 0L;
+	 
+  			put_user(sizeof(ia),(int *) optlen);
+			memcpy_tofs((void *)optval, &ia, sizeof(ia));
 			return 0;
+		}
 #endif
 		default:
 			return(-ENOPROTOOPT);
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/net/ipv4/rarp.c linux/net/ipv4/rarp.c
--- linux.vanilla/net/ipv4/rarp.c	Fri Jul 24 17:29:06 1998
+++ linux/net/ipv4/rarp.c	Mon Dec 14 18:37:02 1998
@@ -189,6 +189,8 @@
         rarp_pkt_inited=1;
 }
 
+#ifdef MODULE
+
 static void rarp_end_pkt(void)
 {
         if(!rarp_pkt_inited)
@@ -197,6 +199,8 @@
         unregister_netdevice_notifier(&rarp_dev_notifier);
         rarp_pkt_inited=0;
 }
+
+#endif
 
 
 /*
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/net/netrom/af_netrom.c linux/net/netrom/af_netrom.c
--- linux.vanilla/net/netrom/af_netrom.c	Sun Dec  6 00:14:48 1998
+++ linux/net/netrom/af_netrom.c	Mon Mar 22 00:42:33 1999
@@ -960,10 +960,18 @@
 	 */
 	if (frametype != NR_CONNREQ) {
 		/*
+		 * Here it would be nice to be able to send a reset but
+		 * NET/ROM doesn't have one. The following hack would
+		 * have been a way to extend the protocol but apparently
+		 * it kills BPQ boxes... :-(
+		 */
+#if 0
+		/*
 		 * Never reply to a CONNACK/CHOKE.
 		 */
 		if (frametype != NR_CONNACK || flags != NR_CHOKE_FLAG)
 			nr_transmit_dm(skb, 1);
+#endif
 		return 0;
 	}
 
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/net/netsyms.c linux/net/netsyms.c
--- linux.vanilla/net/netsyms.c	Fri Jul 24 17:29:07 1998
+++ linux/net/netsyms.c	Mon Mar 22 00:01:21 1999
@@ -12,6 +12,7 @@
 #include <linux/net.h>
 #include <linux/netdevice.h>
 #include <linux/trdevice.h>
+#include <linux/fddidevice.h>
 #include <linux/ioport.h>
 #include <net/sock.h>
 
@@ -129,6 +130,11 @@
 #ifdef CONFIG_TR
 	X(tr_setup),
 	X(tr_type_trans),
+#endif
+                          
+#ifdef CONFIG_FDDI
+	X(fddi_setup),
+	X(fddi_type_trans),
 #endif
                           
 #ifdef CONFIG_NET_ALIAS
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/scripts/Menuconfig linux/scripts/Menuconfig
--- linux.vanilla/scripts/Menuconfig	Sun Jun 21 18:41:30 1998
+++ linux/scripts/Menuconfig	Mon Dec 28 14:27:18 1998
@@ -77,6 +77,14 @@
 }
 
 #
+# Define an int to a specific value.
+#
+
+function define_int () {
+	eval $1=$2
+}
+
+#
 # Create a boolean (Yes/No) function for our current menu
 # which calls our local bool function.
 #
@@ -954,6 +962,13 @@
                 	;;
         	esac
 	}
+
+	function define_int () {
+		eval $1="$2"
+		echo "$1=$2" >>$CONFIG
+		echo "#define $1 $2" >>$CONFIG_H
+	}
+
 
 	function choice () {
 		#
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/scripts/tkgen.c linux/scripts/tkgen.c
--- linux.vanilla/scripts/tkgen.c	Sun Jun 21 18:41:30 1998
+++ linux/scripts/tkgen.c	Mon Dec 28 15:09:53 1998
@@ -280,6 +280,7 @@
    */
   switch(item->tok)
     {
+    case tok_define_int:
     case tok_define:
       printf("} then { set %s %s } \n",  item->optionname, item->value);
       break;
@@ -464,6 +465,9 @@
    */
   switch(item->tok)
     {
+    case tok_define_int:
+      printf("} then {write_int $cfg $autocfg %s %s $notmod }\n", item->optionname, item->value);
+      break;
     case tok_define:
       printf("} then {write_tristate $cfg $autocfg %s %s $notmod }\n", item->optionname, item->value);
       break;
@@ -572,7 +576,8 @@
        * Skip items not for this menu, or ones having no conditions.
        */
       if (cfg->menu_number != menu_num ) continue;
-      if (cfg->tok != tok_define) continue;
+      if (cfg->tok != tok_define && cfg->tok != tok_define_int)
+      	continue;
       /*
        * Clear all of the booleans that are defined in this menu.
        */
@@ -717,6 +722,7 @@
 	  cfg->menu_line = menu_line++;
 	  break;
 	case tok_define:
+	case tok_define_int:
 	  cfg->menu_number = -1;
 	case tok_choice:
 	default:
@@ -923,7 +929,7 @@
       /*
        * Skip items not for this menu, or ones having no conditions.
        */
-      if( cfg->tok != tok_define) continue;
+      if( cfg->tok != tok_define && cfg->tok != tok_define_int) continue;
       if (cfg->cond != NULL ) 
 	generate_if(cfg, cfg->cond, menu_num, cfg->menu_line);
       else
@@ -988,6 +994,7 @@
 	case tok_tristate:
 	case tok_dep_tristate:
 	case tok_define:
+	case tok_define_int:
 	case tok_choose:
 	  if(!(cfg->flags & GLOBAL_WRITTEN))
 	    {
@@ -1019,7 +1026,7 @@
 		  printf("\twrite_comment $cfg $autocfg \"%s\"\n", cfg->label);
 		}
 #if 0
-	      else if(cfg->tok == tok_define)
+	      else if(cfg->tok == tok_define || cfg->tok == tok_define_int)
 		{
 		  printf("\twrite_define %s %s\n", cfg->optionname,
 			 cfg->value);
@@ -1043,6 +1050,12 @@
 			 cfg->optionname,
 			 cfg->optionname);
 	        }
+	      else if (cfg->tok == tok_define_int )
+	        {
+		  printf("\twrite_int $cfg $autocfg %s $%s $notmod\n",
+			 cfg->optionname,
+			 cfg->optionname);
+	        }
 	      else if (cfg->tok == tok_hex )
 	        {
 		  printf("\twrite_hex $cfg $autocfg %s $%s $notmod\n",
@@ -1109,13 +1122,14 @@
   clear_globalflags(config);
   for(cfg = scfg; cfg != NULL; cfg = cfg->next)
     {
-      if( cfg->tok != tok_define ) continue;
+      if( cfg->tok != tok_define && cfg->tok != tok_define_int ) 
+      	continue;
       printf("\tglobal %s; set %s 0\n",  cfg->optionname,  cfg->optionname);
       cfg->flags |= GLOBAL_WRITTEN;
     }
   for(cfg = scfg; cfg != NULL; cfg = cfg->next)
     {
-      if( cfg->tok != tok_define ) continue;
+      if( cfg->tok != tok_define && cfg->tok != tok_define_int ) continue;
       if (cfg->cond != NULL ) 
 	generate_if(cfg, cfg->cond, -1, 0);
       else
Binary files linux.vanilla/scripts/tkparse and linux/scripts/tkparse differ
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/scripts/tkparse.c linux/scripts/tkparse.c
--- linux.vanilla/scripts/tkparse.c	Sun Jun 21 18:41:30 1998
+++ linux/scripts/tkparse.c	Mon Dec 28 15:02:46 1998
@@ -345,6 +345,11 @@
       tok = tok_define;
       pnt += 11;
     }
+  else if (strncmp(pnt, "define_int", 10) == 0) 
+    {
+      tok = tok_define_int;
+      pnt += 10;
+    }
   else if (strncmp(pnt, "bool", 4) == 0) 
     {
       tok = tok_bool;
@@ -446,6 +451,10 @@
       if(*pnt == 'y' || *pnt == 'Y' ) kcfg->value = "1";
       if(*pnt == 'n' || *pnt == 'N' ) kcfg->value = "0";
       if(*pnt == 'm' || *pnt == 'M' ) kcfg->value = "2";
+      break;
+    case tok_define_int:
+      pnt = get_string(pnt, &kcfg->optionname);
+      pnt = get_string(pnt, &kcfg->value);
       break;
     case tok_menuname:
       pnt = get_qstring(pnt, &kcfg->label);
diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/scripts/tkparse.h linux/scripts/tkparse.h
--- linux.vanilla/scripts/tkparse.h	Sun Jun 21 18:41:30 1998
+++ linux/scripts/tkparse.h	Mon Dec 28 14:30:22 1998
@@ -14,6 +14,7 @@
   tok_hex,
   tok_make,
   tok_define,
+  tok_define_int,
   tok_choose,
   tok_choice,
   tok_endmenu,
