diff -u --recursive --new-file v2.0.30/linux/CREDITS linux/CREDITS
--- v2.0.30/linux/CREDITS	Tue Apr  8 09:04:28 1997
+++ linux/CREDITS	Sun Aug 17 12:48:45 1997
@@ -240,7 +240,7 @@
 
 N: Juan Jose Ciarlante
 E: jjciarla@raiz.uncu.edu.ar
-E: juanjo@irriga.uncu.edu.ar
+E: irriga@impsat1.com.ar
 D: Network driver alias support
 D: IP masq hashing and app modules
 S: Las Cuevas 2385 - Bo Guemes
@@ -413,7 +413,7 @@
 S: Australia
 
 N: Ralf Flaxa
-E: rfflaxa@immd4.informatik.uni-erlangen.de
+E: rf@lst.de
 D: The Linux Support Team Erlangen
 D: Creator of LST distribution
 D: Author of installation tool LISA
@@ -466,7 +466,7 @@
 S: USA
 
 N: Philip Gladstone
-E: philipg@onsett.com
+E: philip@raptor.com
 D: Kernel / timekeeping stuff
 
 N: Michael A. Griffith
@@ -715,10 +715,9 @@
 N: Alain L. Knaff
 E: Alain.Knaff@imag.fr
 D: floppy driver
-S: Appartement 310B
-S: 11, rue General Mangin
-S: 38100 Grenoble
-S: France
+S: 19, rue Jean l'Aveugle
+S: L-1148 Luxembourg-City
+S: Luxembourg
 
 N: Harald Koenig
 E: koenig@tat.physik.uni-tuebingen.de
@@ -862,8 +861,9 @@
 
 N: Martin Mares
 E: mj@k332.feld.cvut.cz
+W: http://atrey.karlin.mff.cuni.cz/~mj/
 D: BIOS video mode handling code
-D: Miscellaneous kernel fixes
+D: Miscellaneous kernel fixes and hacks
 D: MOXA C-218 serial board driver
 D: BOOTP support
 S: Kankovskeho 1241
@@ -1124,10 +1124,16 @@
 S: 79539 Loerrach
 S: Germany
 
+N: Joerg Reuter
+E: jreuter@poboxes.com
+E: dl1bke@db0pra.ampr.org (amateur radio)
+W: http://www.rat.de/jr
+D: Z8530 SCC driver and DAMA Slave for AX.25
+
 N: William E. Roadcap
-E: roadcapw@cfw.com
+E: roadcapw@titus.org
 W: http://www.cfw.com/~roadcapw
-D: Author of menu based configuration tool, Menuconfig.
+D: Author of ncurses based configuration tool, Menuconfig.
 S: 1407 Broad Street
 S: Waynesboro, Virginia  22980
 S: USA
@@ -1178,14 +1184,14 @@
 S: Finland
 
 N: Eric Schenk
-E: schenk@cs.toronto.edu
+E: Eric.Schenk@dna.lth.se
 D: Random kernel debugging.
 D: SYSV Semaphore code rewrite.
 D: Network layer debugging.
 D: Dial on demand facility (diald).
-S: 7 Borden Street
-S: Toronto, Ontario
-S: Canada M5S 2M8
+S: Dag Hammerskjolds v. 3E
+S: S-226 64 LUND
+S: Sweden
 
 N: Peter De Schrijver
 E: stud11@cc4.kuleuven.ac.be
@@ -1524,8 +1530,9 @@
 S: Finland
 
 N: Roger E. Wolff
-E: wolff@dutecai.et.tudelft.nl
+E: R.E.Wolff@BitWizard.nl
 D: Written kmalloc/kfree
+D: Written Specialix IO8+ driver
 S: Oosterstraat 23
 S: 2611 TT Delft
 S: The Netherlands
diff -u --recursive --new-file v2.0.30/linux/Documentation/Changes linux/Documentation/Changes
--- v2.0.30/linux/Documentation/Changes	Tue Nov 12 22:23:13 1996
+++ linux/Documentation/Changes	Mon Aug 18 20:16:26 1997
@@ -5,7 +5,10 @@
 important packages for Linux as well as instructions for newcomers to
 the 2.0.x series of kernels.  By glancing through it, you should be
 able to find out what you need to upgrade in order to successfully run
-the latest kernels.
+the latest kernels.  Note that I tend to mention the earliest releases
+of software known to work, not necessarily the latest and greatest
+versions, as the goal is to provide you with a list of sure-to-work
+software.
 
    It was originally based on material from the linux-kernel mailing
 list, Jared Mauch's web page "Software Victims of the 1.3 Kernel
@@ -35,9 +38,9 @@
 http://jf.gee.kyoto-u.ac.jp/JF/v2.0/Changes-2.0.html ni arimasu.
 
    Voyez le site http://www.linux-kheops.com/traduc/kernels/ pour la
-traduction francais (merci, David Bourgin).  (French translation)
+traduction francaise (merci, David Bourgin).  (French translation)
 
-Last updated: November 5, 1996.
+Last updated: August 14, 1997.
 Current Author: Chris Ricker (gt1355b@prism.gatech.edu).
 
 Current Releases
@@ -48,7 +51,7 @@
 - Dynamic linker (ld.so) 1.7.14
 - GNU CC		 2.7.2.1
 - Binutils		 2.7.0.3
-- Linux C Library	 Stable:  5.2.18, Beta:  5.4.10
+- Linux C Library	 Stable:  5.2.18, Beta:  5.4.33
 - Linux C++ Library	 2.7.2.1
 - Termcap		 2.0.8
 - Procps		 1.01
@@ -123,22 +126,24 @@
 `release.libc-5.2.18' file, since GNU make and a few other fairly
 important utils can be broken by the upgrade.
 
-   The current (beta) Linux C Library release is 5.3.12. In this
-release there are some important changes that may cause troubles to
-buggy programs (programs that call free() on a pointer not returned by
-malloc() work with previous libc, but not with this release) so read the
-`release.libc-5.3.12' file carefully!  In the latest libc releases a
-dirent bug, which erroneously defined d->reclen to d->namlen if USE_GNU
-was defined, has been fixed.  Unfortunately, some GNU packages depend
-on this bug.  GNU make 3.xx is one of them.  To fix that you need to
-patch and recompile those programs (a patch for make is included in the
-file `release.libc-.5.3.9', and the address to obtain a precompiled
-binary is at the end of this file).
+   A current common Linux C Library release is 5.3.12. In this release
+there are some important changes that may cause troubles to buggy
+programs (programs that call free() on a pointer not returned by
+malloc(), such as Netscape, work with previous libc, but not with this
+release) so read the `release.libc-5.3.12' file carefully!  In the
+latest libc releases a dirent bug, which erroneously defined d->reclen
+to d->namlen if USE_GNU was defined, has been fixed.  Unfortunately,
+some GNU packages depend on this bug.  GNU make 3.xx is one of them.
+To fix that you need to patch and recompile those programs (a patch for
+make is included in the file `release.libc-.5.3.9', and the address to
+obtain a precompiled binary is at the end of this file).
 
    Also, the libc-5.3.x line has a known security hole relating to
 rlogin.  Libc-5.3.12 fixes this, so if you're going to run an
-experimental libc, be sure to upgrade to 5.3.12.  Libc-5.4.10 is
-currently available as well, but it may have problems, so caveat emptor.
+experimental libc, be sure to upgrade to 5.3.12.  Libc-5.4.33 is
+currently available as well, but it may have problems, so caveat
+emptor.  It fixes lots of problems, but may break even more programs
+than 5.3.12.
 
    If you're getting an error message that is something to the effect of
 
@@ -273,7 +278,7 @@
 details.  Among the programs this has impacted are older sendmails.  If
 you get a message that sendmail cannot lock aliases.dir (or other
 files), you'll need to upgrade to at least 8.7.x. The latest sendmail
-is at ftp://ftp.cs.berkeley.edu/ucb/src/sendmail/sendmail.8.8.2.tar.gz.
+is at ftp://ftp.cs.berkeley.edu/ucb/src/sendmail/sendmail.8.8.7.tar.gz.
 
 Uugetty
 =======
@@ -626,10 +631,10 @@
 Installation notes for 5.2.18:
 ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.2.18
 
-The latest 5.4.10 release:
-ftp://sunsite.unc.edu/pub/Linux/GCC/libc-5.4.10.bin.tar.gz
-Installation notes for 5.4.10:
-ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.4.10
+The latest 5.4.33 release:
+ftp://sunsite.unc.edu/pub/Linux/GCC/libc-5.4.33.bin.tar.gz
+Installation notes for 5.4.33:
+ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.4.33
 
 Patched make sources:
 ftp://sunsite.unc.edu/pub/Linux/devel/make/make-3.74.patched.tar.gz
diff -u --recursive --new-file v2.0.30/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.0.30/linux/Documentation/Configure.help	Tue Apr  8 08:47:45 1997
+++ linux/Documentation/Configure.help	Fri Sep  5 20:43:58 1997
@@ -124,18 +124,18 @@
   nothing to do with the loopback device used for network connections
   from the machine to itself.  Most users will answer N here.
 
-Enhanced IDE/MFM/RLL disk/cdrom/tape support
+Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support
 CONFIG_BLK_DEV_IDE 
-  This will use the full-featured IDE driver to control up to four IDE
-  interfaces, for a combination of up to eight IDE disk/cdrom/tape
-  drives.  Useful information about large (>540MB) IDE disks,
-  soundcard IDE ports, and other topics, is all contained in
-  Documentation/ide.txt.  If you have one or more IDE drives, say Y
-  here.  If your system has no IDE drives, or if memory requirements
-  are really tight, you could say N here, and select the Old harddisk
-  driver instead to save about 13kB of memory in the kernel.  To
-  fine-tune IDE drive/interface parameters for improved performance,
-  look for the hdparm package at
+  This will use the full-featured IDE driver to control up to four
+  IDE interfaces, for a combination of up to eight IDE
+  disk/cdrom/tape/floppy drives.  Useful information about large
+  (>540MB) IDE disks, soundcard IDE ports, and other topics, is all
+  contained in Documentation/ide.txt.  If you have one or more IDE
+  drives, say Y here.  If your system has no IDE drives, or if memory
+  requirements are really tight, you could say N here, and select
+  the Old harddisk driver instead to save about 13kB of memory in
+  the kernel.  To fine-tune IDE drive/interface parameters for
+  improved performance, look for the hdparm package at
   sunsite.unc.edu:/pub/Linux/kernel/patches/diskdrives/
 
 Old harddisk (MFM/RLL/IDE) driver
@@ -190,9 +190,32 @@
   ATAPI is a new protocol used by IDE TAPE and ATAPI drives,
   similar to the SCSI protocol.  At boot time, the TAPE drive will
   be identified along with other IDE devices, as "hdb" or "hdc",
-  or something similar.  Be sure to consult the drivers/block/ide-tape.c
+  or something similar, and will be mapped to a character device
+  such as "ht0".  Be sure to consult the drivers/block/ide-tape.c
   and Documentation/ide.txt files for usage information.
 
+Include IDE/ATAPI FLOPPY support (new)
+CONFIG_BLK_DEV_IDEFLOPPY
+  If you have an IDE floppy which uses the ATAPI protocol, say Y.
+  ATAPI is a new protocol used by IDE cdrom/tape/floppy drives,
+  similar to the SCSI protocol.  IDE floppy drives include the
+  LS-120 and the ATAPI ZIP (ATAPI PD-CD drives are not supported
+  by this driver; support for PD-CD drives is available through
+  the SCSI emulation).  At boot time, the FLOPPY drive will be
+  identified along with other IDE devices, as "hdb" or "hdc", or
+  something similar.
+
+SCSI emulation support
+CONFIG_BLK_DEV_IDESCSI
+  This will provide SCSI host adapter emulation for IDE ATAPI devices,
+  and will allow you to use a SCSI device driver instead of a native
+  ATAPI driver. This is useful if you have an ATAPI device for which
+  no native driver has been written (for example, an ATAPI PD-CD
+  drive); you can then use this emulation together with an appropriate
+  SCSI device driver. If both this SCSI emulation and native ATAPI
+  support are compiled into the kernel, the native support will be
+  used.  Normally, say N.
+
 Support removable IDE interfaces (PCMCIA)
 CONFIG_BLK_DEV_IDE_PCMCIA
   This option adds code to the IDE driver to handle hot insertion
@@ -449,7 +472,10 @@
   the same end. SYN cookies use less space than RST cookies,
   but have a small probability of introducing an non timed-out
   failure to connect in the remote TCP. You can use both options
-  simultatenously.
+  simultatenously.  If you are SYN flooded, the source address
+  reported by the kernel is likely to have been forged by the attacker.
+  The source address is reported as an aid in tracing the packets to 
+  their actual source.  
 
 SYN flood protection
 CONFIG_RST_COOKIES
@@ -463,7 +489,10 @@
   The SYN_COOKIES option provides an alternative method to accomplish
   the same end. RST cookies use more space than SYN cookies on your
   machine, but never increase the probability of a frozen connection
-  in a remote TCP. You can use both options simultatenously.
+  in a remote TCP. You can use both options simultatenously.  If you 
+  are SYN flooded, the source address reported by the kernel is likely 
+  to have been forged by the attacker.  The source address is reported 
+  as an aid in tracing the packets to their actual source.  
 
 Sun floppy controller support
 CONFIG_BLK_DEV_SUNFD
@@ -589,10 +618,11 @@
 Intel 82371 PIIX (Triton I/II) DMA support
 CONFIG_BLK_DEV_TRITON
   If your PCI system uses an IDE harddrive (as opposed to SCSI, say)
-  and includes the Intel 430FX PCI Triton chipset, you will want to
-  enable this option to allow use of bus-mastering DMA data transfers.
-  Read the comments at the beginning of drivers/block/triton.c.  Check
-  the file Documentation/Changes for location and latest version of
+  and includes the Intel Triton I/II IDE interface chipset (i82371FB,
+  i82371SB or i82371AB), you will want to enable this option to allow
+  use of bus-mastering DMA data transfers. Read the comments at the
+  beginning of drivers/block/triton.c and Documentation/ide.txt.
+  Check the file Documentation/Changes for location and latest version of
   the hdparm utility. It is safe to say Y to this question.
 
 System V IPC
@@ -947,12 +977,14 @@
   If you want this, say Y.
 
 IP: ipautofw masquerade support
-CONFIG_IP_MASQUERADE_IPAUTOFW
+CONFIG_IP_MASQUERADE_IPAUTOFW (Experimental)
   ipautofw is a program by Richard Lynch allowing additional
   support for masquerading protocols which do not (as yet)
   have additional protocol helpers.  
   Information and source for ipautofw is available from
   ftp://ftp.netis.com/pub/members/rlynch/
+  The ipautofw code is still under development and so is currently
+  marked EXPERIMENTAL.
   If you want this, say Y.
 
 IP: ICMP masquerading
@@ -1528,24 +1560,41 @@
   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.
-  This driver has been tested OK with linux/i386 and is currently
-  untested under linux/Alpha. If you intend to use this driver under
-  linux/Alpha, just try it first with read-only or mounted read-only
-  devices.  Memory mapped io is currently not supported under
-  linux/Alpha. Please read drivers/scsi/README.ncr53c8xx for more
-  information. 
+  Support of Ultra SCSI data transfers with NCR53C860 and NCR53C875 
+  controllers has been recently added to the driver.
+  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.
 
-force normal IO
+use normal IO
 CONFIG_SCSI_NCR53C8XX_IOMAPPED
-  Under linux/Alpha only normal io is currently supported.
-  Under linux/i386, this option allows you to force the driver to use
-  normal IO.  Memory mapped IO has less latency than normal IO.
-  During the initialization phase, the driver first tries to use
-  memory mapped io.  If nothing seems wrong, it will use memory mapped
-  io.  If a flaw is detected, it will use normal io.  However, it's
-  possible that memory mapped does not work properly for you and the
-  driver has not detected the problem; then you would want to say Y
-  here.  The normal answer therefore is N.
+  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.
 
 not allow targets to disconnect
 CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT
@@ -1570,36 +1619,51 @@
   The safe answer therefore is N.
   The normal answer therefore is Y.
 
-force asynchronous transfer mode
-CONFIG_SCSI_NCR53C8XX_FORCE_ASYNCHRONOUS
-  This option allows you to force asynchronous transfer mode for all
-  devices at linux startup. You can enable synchronous negotiation
-  with the "setsync" control command after boot-up, for example:
-     echo "setsync 2 25" >/proc/scsi/ncr53c8xx/0
-  asks the driver to set the period to 25 ns (10MB/sec) for target 2
-  of controller 0 (please read drivers/scsi/README.ncr53c8xx for more
-  information). The safe answer therefore is Y.  The normal answer
-  therefore is N.
- 
-force synchronous negotiation
-CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO
-  Some scsi-2 devices support synchronous negotiations but do not
-  report this feature in byte 7 of inquiry data.
-  Answer Y only if you suspect some device to be so humble.
-  The normal answer therefore is N.
- 
-disable master parity checking
-CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK
-  Some hardware may have problems with parity during master cycles on
-  PCI bus.  Only seen once. Answer Y if you suspect such problem.  The
-  normal answer therefore is N.
-
-disable scsi parity checking
-CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK
-  Parity on scsi bus is a system option. If one device checks parity,
-  then all devices on the scsi bus must generate parity.  However, the
-  parity can be ignored by the scsi devices.  Answer Y only if you
-  know what you are doing.  The normal answer therefore is N.
+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.
+
+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
+  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.
 
 Always IN2000 SCSI support
 CONFIG_SCSI_IN2000
@@ -1716,6 +1780,13 @@
   and removed from the running kernel whenever you want), say M here
   and read Documentation/modules.txt.
 
+Tekram DC390(T) (AMD 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.
+
 AM53/79C974 PCI SCSI support
 CONFIG_SCSI_AM53C974
   This is support for the AM53/79C974 SCSI host adapters. Please read
@@ -1723,6 +1794,15 @@
   available via ftp (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO, is for you.
 
+GDT SCSI Disk Array Controller support
+CONFIG_SCSI_GDTH
+  This is a driver for all SCSI Disk Array Controllers (EISA/ISA/PCI) 
+  manufactured by ICP vortex. It is documented in the kernel source in
+  drivers/scsi/gdth.c and drivers/scsi/gdth.h. This driver is also
+  available as a module ( = code which can be inserted in and removed
+  from the running kernel whenever you want). If you want to compile 
+  it as a module, say M here and read Documentation/modules.txt. 	
+
 IOMEGA Parallel Port ZIP drive SCSI support
 CONFIG_SCSI_PPA
   This driver supports the parallel port version of IOMEGA's ZIP drive
@@ -2096,6 +2176,20 @@
   mixing an SMC Ultra and an Adaptec AHA1542 SCSI card causes corruption
   problems with many operating systems.
 
+SMC Ultra32 support
+CONFIG_ULTRA32
+  This is support for the SMC Ultra32 EISA card in shared memory mode.
+  If you have a network (ethernet) card of this type, say Y and read
+  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO.  This driver is also
+  available as a module ( = code which can be inserted in and removed
+  from the running kernel whenever you want). If you want to compile
+  it as a module, say M here and read Documentation/modules.txt as
+  well as Documentation/networking/net-modules.txt. If you plan to use
+  more than one network card under linux, read the
+  Multiple-Ethernet-mini-HOWTO, available from
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+
 SMC 9194 Support
 CONFIG_SMC9194
   This is support for the SMC9xxx based Ethernet cards.  Choose this
@@ -2348,6 +2442,12 @@
   FMV-184 and it is not working, you may need to disable Plug & Play
   mode of the card.
 
+Intel EtherExpress/Pro 100B support'
+CONFIG_EEXPRESS_PRO100B
+  If you have an Intel EtherExpress Pro 100 10/100Mbps PCI Ethernet
+  card, answer yes.  As of kernel release 2.0.31 this driver was
+  still experimental.
+
 EtherExpressPro support
 CONFIG_EEXPRESS_PRO
   If you have a network (ethernet) card of this type, say Y and read
@@ -2417,19 +2517,18 @@
 
 AT&T WaveLAN & DEC RoamAbout DS support
 CONFIG_WAVELAN
-  These are cards for wireless ethernet-like networking. Supported are
-  AT&T GIS and NCR WaveLAN cards. If you want to use a card of this
-  type under Linux, say Y and read the Ethernet-HOWTO, available via
-  ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Some
-  more specific information is contained in
-  drivers/net/README.wavelan. This driver is also available as a
-  module ( = code which can be inserted in and removed from the
-  running kernel whenever you want). If you want to compile it as a
-  module, say M here and read Documentation/modules.txt as well as
-  Documentation/networking/net-modules.txt. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+    The Lucent Wavelan (formerly NCR and AT&T ; or DEC RoamAbout DS)
+  is a Radio LAN (wireless ethernet-like) at 900 MHz and 2.4 GHz.
+    This driver support the ISA version of the Wavelan. A driver for
+  the pcmcia hardware is available in David Hinds's pcmcia package.
+    This driver is fairly stable and may be compiled as a module
+  (wavelan.o). It implements many nice feature and the Wireless
+  Extensions (you must get the Wireless tools from the net).
+    For documentation, refer to :
+      o the wavelan man page, wireless tools man pages
+      o wavelan.p.h and the source code
+      o Ethernet-HOWTO, Multiple-Ethernet-mini-HOWTO, Module-HOWTO
+      o More documentation to come when I will have the time :-)
 
 HP PCLAN+ (27247B and 27252A) support
 CONFIG_HPLAN_PLUS
@@ -3112,6 +3211,17 @@
   Documentation/modules.txt. If you haven't heard about all of this
   before, it's safe to say N.
 
+Kernel automounter support (experimental)
+CONFIG_AUTOFS_FS
+  The automounter is a tool to automatically mount remote filesystems
+  on demand.  This implementation is partially kernel-based to reduce
+  overhead in the already-mounted case; this is unlike the BSD
+  automounter (amd), which is only in user space.  To use the
+  automounter you also need the user-space tools from
+  ftp.kernel.org:/pub/linux/daemons/autofs.  If you are not a part of
+  a fairly large, distributed network, you probably do not need an
+  automounter, and can say N here.
+
 BSD UFS filesystem support (read only)
 CONFIG_UFS_FS
   BSD and derivate versions of Unix (such as SunOS, FreeBSD, NetBSD
@@ -3805,8 +3915,8 @@
   (mgetty+sendfax by gert@greenie.muc.de with an extension, available
   with the ISDN utility package for example), you will be able
   to use your Linux box as an ISDN-answering machine. Of course, this
-  must be supported by the lowlevel driver also. Currently, the Teles
-  driver is the only voice-supporting one. See
+  must be supported by the lowlevel driver also. Currently HiSax 
+  driver is the only voice-supporting drivers. See
   Documentation/isdn/README.audio for more information.
 
 ICN 2B and 4B support
@@ -3819,14 +3929,89 @@
   separately.  See Documentation/isdn/README and README.icn for more
   information.
 
-Teles, NICCY1016PC, Creatix support
-CONFIG_ISDN_DRV_TELES
-  This enables support for the Teles ISDN-cards S0-16.0, S0-16.3, S0-8
-  and many compatibles. By default, the driver is configured to
-  support a 16.0-type using EDSS1-protocol. See
-  Documentation/isdn/README on how to configure it using 16.3, a
-  different D-channel protocol, or non-standard irq/port/shmem
-  settings.
+HiSax SiemensChipSet driver support
+CONFIG_ISDN_DRV_HISAX
+  This driver replaces the old Teles driver. It supports the Siemens
+  chipset in a more general way. This chipset is used on various
+  ISDN-cards (like AVM A1, Elsa ISDN cards, Teles S0-16.0,
+  Teles S0-16.3, Teles S0-8, Teles/Creatix PnP, ITK micro ix1 and 
+  many compatibles). It's a complete rewrite of the original Teles 
+  driver.
+  See Documentation/isdn/README.HiSax for further informations on
+  using this driver.
+
+HiSax Support for Teles 16.0/8.0
+CONFIG_HISAX_16_0
+  This enables HiSax support for the Teles ISDN-cards S0-16.0,
+  S0-8 and many compatibles.
+  See Documentation/isdn/README.HiSax on how to configure it 
+  using the different cards, a different D-channel protocol, or
+  non-standard irq/port/shmem settings.
+
+HiSax Support for Teles 16.3 or PNP or PCMCIA
+CONFIG_HISAX_16_3
+  This enables HiSax support for the Teles ISDN-cards S0-16.3
+  the Teles/Creatix PnP and the Teles PCMCIA.
+  See Documentation/isdn/README.HiSax on how to configure it 
+  using the different cards, a different D-channel protocol, or
+  non-standard irq/port/shmem settings.
+
+HiSax Support for AVM A1 (Fritz)
+CONFIG_HISAX_AVM_A1
+  This enables HiSax support for the AVM A1 (aka "Fritz").
+  See Documentation/isdn/README.HiSax on how to configure it
+  using the different cards, a different D-channel protocol, or
+  non-standard irq/port/shmem settings.
+
+HiSax Support for Elsa ISA cards
+CONFIG_HISAX_ELSA_PCC
+  This enables HiSax support for the Elsa Mircolink cards and
+  for the Elsa Quickstep series cards for the ISA bus.
+  You don't have to select "HiSax Support for Elsa PCMCIA card"
+  at the same time.
+  See Documentation/isdn/README.HiSax on how to configure it 
+  using the different cards, a different D-channel protocol, or
+  non-standard irq/port/shmem settings.
+
+HiSax Support for Elsa PCMCIA card
+CONFIG_HISAX_ELSA_PCMCIA
+  This enables HiSax support for the Elsa PCMCIA card.
+  You don't have to select "HiSax Support for Elsa ISA cards" at
+  the same time.
+  See Documentation/isdn/README.HiSax on how to configure it 
+  using the different cards, a different D-channel protocol, or
+  non-standard irq/port/shmem settings.
+
+HiSax Support for ITK ix1-micro Revision 2
+CONFIG_HISAX_IX1MICROR2
+  This enables HiSax support for the ITK ix1-micro Revision 2 card.
+  See Documentation/isdn/README.HiSax on how to configure it 
+  using the different cards, a different D-channel protocol, or
+  non-standard irq/port/shmem settings.
+
+HiSax Support for EURO/DSS1
+CONFIG_HISAX_EURO
+  You should choose your D-channel protocol your local
+  telephone service provider uses here by saying Y or N.
+  NOTE: This is mutually exclusive with HiSax Support for
+  german 1TR6 and US/NI-1 if you have only one ISDN card
+  installed.
+
+HiSax Support for US/NI-1
+CONFIG_HISAX_NI1
+  You should choose your D-channel protocol your local
+  telephone service provider uses here by saying Y or N.
+  NOTE: This is mutually exclusive with HiSax Support for
+  german 1TR6 and EURO/DSS1 if you have only one ISDN card
+  installed. (not working yet, under developement)
+
+HiSax Support for german 1TR6
+CONFIG_HISAX_1TR6
+  You should choose your D-channel protocol your local
+  telephone service provider uses here by saying Y or N.
+  NOTE: This is mutually exclusive with HiSax Support for
+  EURO/DSS1 and US/NI-1 if you have only one ISDN card
+  installed.
 
 PCBIT-D support
 CONFIG_ISDN_DRV_PCBIT
@@ -3836,6 +4021,21 @@
   using a utility which is distributed separately.  See
   Documentation/isdn/README and Documentation/isdn/README.pcbit for
   more information.
+
+Spellcaster support (EXPERIMENTAL)
+CONFIG_ISDN_DRV_SC
+  This enables support for the Spellcaster BRI boards. This driver
+  currently builds in a modularized version only.
+  See Documentation/isdn/README.sc and http://www.spellcast.com
+  for more information.
+
+AVM-B1 with CAPI2.0 support
+CONFIG_ISDN_DRV_AVMB1
+  This enables support for the AVM B1 card and also adds a CAPI2.0
+  interface for this card. For running this card, additional firmware
+  is necessary, which has to be downloaded into the card using a
+  utility which is distributed separately.
+  See Documentation/isdn/README.avmb1 for more information.
 
 Support for AP1000 multicomputer
 CONFIG_AP1000
diff -u --recursive --new-file v2.0.30/linux/Documentation/ide.txt linux/Documentation/ide.txt
--- v2.0.30/linux/Documentation/ide.txt	Wed Sep 25 01:12:11 1996
+++ linux/Documentation/ide.txt	Mon Aug  4 11:45:55 1997
@@ -14,6 +14,7 @@
 
 Major features of ide.c & ide-cd.c ("NEW!" marks changes since 1.2.13):
 
+NEW!	- support for IDE ATAPI *floppy* drives
 NEW!	- support for IDE ATAPI *tape* drives, courtesy of Gadi Oxman
 		(re-run MAKEDEV.ide to create the tape device entries in /dev/)
 NEW!	- support for up to *four* IDE interfaces on one or more IRQs
@@ -111,12 +112,12 @@
 Apparently many releases of Slackware 2.2/2.3 have incorrect entries
 in /dev for hdc* and hdd* -- this can also be corrected by running MAKEDEV.ide
 
-ide.c automatically probes for the primary and secondary interfaces,
+ide.c automatically probes for the standard four IDE interfaces,
 for the drives/geometries attached to those interfaces, and for the
-IRQ numbers being used by the interfaces (normally IRQ14 & IRQ15).
+IRQ numbers being used by the interfaces (normally 14, 15, 11 and 10).
 
-Interfaces beyond the first two are not normally probed for, but may be
-specified using kernel "command line" options.  For example,
+For special cases, interfaces may be specified using kernel "command line"
+options.  For example,
 
 	ide3=0x168,0x36e,10	/* ioports 0x168-0x16f,0x36e, irq 10 */
 
diff -u --recursive --new-file v2.0.30/linux/Documentation/isdn/00-INDEX linux/Documentation/isdn/00-INDEX
--- v2.0.30/linux/Documentation/isdn/00-INDEX	Thu Jun  6 04:57:43 1996
+++ linux/Documentation/isdn/00-INDEX	Mon Aug  4 17:33:58 1997
@@ -10,12 +10,13 @@
 	- info for running audio over ISDN.
 README.icn
 	- info on the ICN-ISDN-card and its driver.
+README.HiSax
+	- info on the HiSax driver which replaces the old teles.
 README.pcbit
 	- info on the PCBIT-D ISDN adapter and driver.
 README.syncppp
 	- info on running Sync PPP over ISDN.
-README.teles
-	- info on driver for Teles compatible ISDN cards.
 syncPPP.FAQ
 	- frequently asked questions about running PPP over ISDN.
-
+README.avmb1
+	- info on driver for AVM-B1 ISDN card
diff -u --recursive --new-file v2.0.30/linux/Documentation/isdn/CREDITS linux/Documentation/isdn/CREDITS
--- v2.0.30/linux/Documentation/isdn/CREDITS	Sun Apr 21 01:56:13 1996
+++ linux/Documentation/isdn/CREDITS	Mon Aug  4 17:33:58 1997
@@ -12,11 +12,18 @@
   For contribution of man-pages, the imontty-tool and a perfect
   maintaining of the mailing-list at hub-wue.
 
+Bernhard Hailer (Bernhard.Hailer@lrz.uni-muenchen.de)
+  For maintaining the FAQ.
+
+Michael 'Ghandi' Herold (michael@abadonna.franken.de)
+  For contribution of the vbox answering machine.
+
 Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
   For his Sync-PPP-code.
 
 Karsten Keil (isdn4@temic-ech.spacenet.de)
   For adding 1TR6-support to the Teles-driver.
+  For the HiSax-driver.
 
 Michael Knigge (knick@cove.han.de)
   For contributing the imon-tool
@@ -39,7 +46,9 @@
 Gerhard 'Fido' Schneider (fido@wuff.franken.de)
   For heavy-duty-beta-testing with his BBS ;)
 
-Thomas Uhl (uhl@hn-net.de)
+Thomas Uhl (uhl@think.de)
   For distributing the cards.
   For pushing me to work ;-)
 
+Carsten Paeth (calle@calle.in-berlin.de)
+  For the AVM-B1-CAPI2.0 driver
diff -u --recursive --new-file v2.0.30/linux/Documentation/isdn/INTERFACE linux/Documentation/isdn/INTERFACE
--- v2.0.30/linux/Documentation/isdn/INTERFACE	Tue Nov 12 22:36:19 1996
+++ linux/Documentation/isdn/INTERFACE	Mon Aug  4 17:33:58 1997
@@ -1,4 +1,4 @@
-$Id: INTERFACE,v 1.5 1996/11/06 17:40:47 keil Exp $
+$Id: INTERFACE,v 1.6 1997/02/10 22:40:57 fritz Exp $
 
 Description of the Interface between Linklevel and Hardwarelevel
   of isdn4linux:
@@ -140,7 +140,7 @@
 
   int (*writebuf)(int, int, u_char*, int, int);
 
-    ***CHANGEc1.14: Declared obsolete. Do NOT use this field/function
+    ***CHANGED1.14: Declared obsolete. Do NOT use this field/function
                     anymore, since it will be removed when all current
                     LL drivers have been changed accordingly. Set this
                     field to NULL and use writebuf_skb instead.
@@ -199,7 +199,7 @@
       int     driver-Id.
       int     channel-number locally to the HL-driver. (starts with 0)
 
-***CHANGEc1.14: The driver-Id and channel-number are new since this revision.
+***CHANGED1.14: The driver-Id and channel-number are new since this revision.
 
     Returnvalue:
       Length of data accepted on success, else error-code (-EINVAL etc.)
@@ -223,7 +223,7 @@
       int     driver-Id.
       int     channel-number locally to the HL-driver. (starts with 0)
 
-***CHANGEc1.14: The driver-Id and channel-number are new since this revision.
+***CHANGED1.14: The driver-Id and channel-number are new since this revision.
 
     Returnvalue:
       Length of data on success, else error-code (-EINVAL etc.)
@@ -249,6 +249,9 @@
 
    Until now, the following commands are defined:
 
+***CHANGED1.34: The parameter "num" has been replaced by a union "para" containing
+                the old "num" and a new setup_type struct used for ISDN_CMD_DIAL
+                and ISDN_STAT_ICALL callback.
 
    ISDN_CMD_IOCTL:
 
@@ -262,10 +265,10 @@
        called with the field command set to 1.
 
      Parameter:
-       driver  = driver-Id.
-       command = ISDN_CMD_IOCTL
-       arg     = Original ioctl-cmd - IIOCDRVCTL
-       num     = first bytes filled with (unsigned long)arg
+       driver   = driver-Id.
+       command  = ISDN_CMD_IOCTL
+       arg      = Original ioctl-cmd - IIOCDRVCTL
+       para.num = first bytes filled with (unsigned long)arg
    
      Returnvalue:
        Depending on driver.
@@ -280,12 +283,14 @@
       driver      = driver-Id.
       command     = ISDN_CMD_DIAL
       arg         = channel-number locally to the driver. (starting with 0)
-      num         = An ASCII-String containing the number to dial, the own
-                    EAZ or MSN, the Service-Indicator and the Additional
-                    Info. Format:
-                    "%s,%s,%d,%d" RemotePhoneNumber,EazOrMsn,SI,AI
+      
+      para.setup.phone  = An ASCII-String containing the number to dial.
+      para.setup.eazmsn = An ASCII-Sting containing the own EAZ or MSN.
+      para.setup.si1    = The Service-Indicator.
+      para.setup.si2    = Additional Service-Indicator.
+
                     If the Line has been designed as SPV (a special german
-                    feature, meaning semi-leased-line) the number has to
+                    feature, meaning semi-leased-line) the phone has to
                     start with an "S".
       ***CHANGE0.6: In previous versions the EAZ has been given in the
                     highbyte of arg.
@@ -300,7 +305,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_ACCEPTD
       arg         = channel-number locally to the driver. (starting with 0)
-      num         = unused.
+      para        = unused.
 
   ISDN_CMD_ACCEPTB:
 
@@ -311,7 +316,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_ACCEPTB
       arg         = channel-number locally to the driver. (starting with 0)
-      num         = unused.
+      para        = unused.
 
   ISDN_CMD_HANGUP:
 
@@ -323,7 +328,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_HANGUP
       arg         = channel-number locally to the driver. (starting with 0)
-      num         = unused.
+      para        = unused.
 
   ISDN_CMD_CLREAZ:
 
@@ -334,7 +339,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_CLREAZ
       arg         = channel-number locally to the driver. (starting with 0)
-      num         = unused.
+      para        = unused.
 
   ISDN_CMD_SETEAZ:
 
@@ -345,7 +350,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_SETEAZ
       arg         = channel-number locally to the driver. (starting with 0)
-      num         = ASCII-String, containing the desired EAZ's/MSN's
+      para.num    = ASCII-String, containing the desired EAZ's/MSN's
                     (comma-separated). If an empty String is given, the
                     HL-driver should respond to ALL incoming calls,
                     regardless of the destination-address.
@@ -360,7 +365,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_GETEAZ
       arg         = channel-number locally to the driver. (starting with 0)
-      num         = ASCII-String, containing the current EAZ's/MSN's
+      para.num    = ASCII-String, containing the current EAZ's/MSN's
 
   ISDN_CMD_SETSIL: (currently unused)
 
@@ -371,7 +376,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_SETSIL
       arg         = channel-number locally to the driver. (starting with 0)
-      num         = ASCII-String, containing the desired Service-Indicators.
+      para.num    = ASCII-String, containing the desired Service-Indicators.
 
   ISDN_CMD_GETSIL: (currently unused)
 
@@ -382,7 +387,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_SETSIL
       arg         = channel-number locally to the driver. (starting with 0)
-      num         = ASCII-String, containing the current Service-Indicators.
+      para.num    = ASCII-String, containing the current Service-Indicators.
 
   ISDN_CMD_SETL2:
 
@@ -397,7 +402,7 @@
       arg         = channel-number locally to the driver. (starting with 0)
                     logical or'ed with (protocol-Id << 8)
                     protocol-Id is one of the constants ISDN_PROTO_L2...
-      num         = unused.
+      para        = unused.
 
   ISDN_CMD_GETL2: (currently unused)
 
@@ -408,7 +413,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_GETL2
       arg         = channel-number locally to the driver. (starting with 0)
-      num         = unused.
+      para        = unused.
     Returnvalue:
       current protocol-Id (one of the constants ISDN_L2_PROTO)
 
@@ -425,7 +430,7 @@
       arg         = channel-number locally to the driver. (starting with 0)
                     logical or'ed with (protocol-Id << 8)
                     protocol-Id is one of the constants ISDN_PROTO_L3...
-      num         = unused.
+      para        = unused.
 
   ISDN_CMD_GETL2: (currently unused)
 
@@ -436,7 +441,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_GETL3
       arg         = channel-number locally to the driver. (starting with 0)
-      num         = unused.
+      para        = unused.
     Returnvalue:
       current protocol-Id (one of the constants ISDN_L3_PROTO)
 
@@ -450,7 +455,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_LOCK
       arg         = unused.
-      num         = unused.
+      para        = unused.
 
   ISDN_CMD_UNLOCK:
 
@@ -462,7 +467,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_UNLOCK
       arg         = unused.
-      num         = unused.
+      para        = unused.
 
 3. Description of the events to be signaled by the HL-driver to th LL.
 
@@ -484,19 +489,23 @@
       driver      = driver-Id
       command     = ISDN_STAT_STAVAIL
       arg         = length of available data.
-      num         = unused.
+      para        = unused.
 
   ISDN_STAT_ICALL:
 
     With this call, the HL-driver signals an incoming call to the LL.
 
     Parameter:
-      driver      = driver-Id
-      command     = ISDN_STAT_ICALL
-      arg         = channel-number, locally to the driver. (starting with 0)
-      num         = ASCII-String in the following format:
-                    "%s,%d,%d,%s",CallerNumber,ServiceIndicator,AddInfo,
-                    CalledNumber.
+      driver            = driver-Id
+      command           = ISDN_STAT_ICALL
+      arg               = channel-number, locally to the driver. (starting with 0)
+      para.setup.phone  = Callernumber.
+      para.setup.eazmsn = CalledNumber.
+      para.setup.si1    = Service Indicator.
+      para.setup.si2    = Additional Service Indicator.
+      para.setup.plan   = octet 3 from Calling party number Information Element.
+      para.setup.screen = octet 3a from Calling party number Information Element.
+
     Return:
       0           = No device matching this call.
       1           = At least one device matching this call (RING on ttyI).
@@ -513,7 +522,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_RUN
       arg         = unused.
-      num         = unused.
+      para        = unused.
 
   ISDN_STAT_STOP:
 
@@ -524,7 +533,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_STOP
       arg         = unused.
-      num         = unused.
+      para        = unused.
 
   ISDN_STAT_DCONN:
 
@@ -535,7 +544,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_DCONN
       arg         = channel-number, locally to the driver. (starting with 0)
-      num         = unused.
+      para        = unused.
 
   ISDN_STAT_BCONN:
 
@@ -547,7 +556,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_BCONN
       arg         = channel-number, locally to the driver. (starting with 0)
-      num         = unused.
+      para        = unused.
 
   ISDN_STAT_DHUP:
 
@@ -560,7 +569,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_DHUP
       arg         = channel-number, locally to the driver. (starting with 0)
-      num         = unused.
+      para        = unused.
 
   ISDN_STAT_BHUP:
 
@@ -572,7 +581,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_BHUP
       arg         = channel-number, locally to the driver. (starting with 0)
-      num         = unused.
+      para        = unused.
 
   ISDN_STAT_CINF:
 
@@ -583,7 +592,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_CINF
       arg         = channel-number, locally to the driver. (starting with 0)
-      num         = ASCII string containing charge-units (digits only).
+      para.num    = ASCII string containing charge-units (digits only).
 
   ISDN_STAT_LOAD: (currently unused)
 
@@ -596,7 +605,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_UNLOAD
       arg         = unused.
-      num         = unused.
+      para        = unused.
 
   ISDN_STAT_BSENT:
 
@@ -608,7 +617,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_BSENT
       arg         = channel-number, locally to the driver. (starting with 0)
-      num         = unused.
+      para        = unused.
 
   ISDN_STAT_NODCH:
 
@@ -619,7 +628,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_NODCH
       arg         = channel-number, locally to the driver. (starting with 0)
-      num         = unused.
+      para        = unused.
 
   ISDN_STAT_ADDCH: (currently unused)
 
@@ -633,7 +642,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_ADDCH
       arg         = to be defined.
-      num         = to be defined.
+      para        = to be defined.
 
   ISDN_STAT_CAUSE:
 
@@ -646,5 +655,5 @@
       driver      = driver-Id
       command     = ISDN_STAT_NODCH
       arg         = channel-number, locally to the driver. (starting with 0)
-      num         = ASCII string containing CAUSE-message.
+      para.num    = ASCII string containing CAUSE-message.
 
diff -u --recursive --new-file v2.0.30/linux/Documentation/isdn/README linux/Documentation/isdn/README
--- v2.0.30/linux/Documentation/isdn/README	Fri Jun  7 06:02:39 1996
+++ linux/Documentation/isdn/README	Mon Aug  4 17:33:59 1997
@@ -27,7 +27,14 @@
    subscribe isdn4linux <your_email_address>
 
    To write to the mailing-list, write to isdn4linux@hub-wue.franken.de
+
+   This mailinglist is bidirectionally gated to the newsgroup
+
+     de.alt.comm.isdn4linux
    
+  There is also a well maintained FAQ (both english and german) available
+  at ftp.franken.de in /pub/isdn4linux/FAQ/
+  This FAQ is also available at http://www.lrz-muenchen.de/~ui161ab/www/isdn/
 
   1.1 Technical details
 
@@ -53,7 +60,7 @@
   A raw-control-device with the following functions:
      write: raw D-channel-messages (format: depends on driver).
      read:  raw D-channel-messages (format: depends on driver).
-     ioctl: depends on driver, for the ICN-driver, the base-address of
+     ioctl: depends on driver, i.e. for the ICN-driver, the base-address of
             the ports and the shared memory on the card can be set and read
             also the boot-code an the protocol software can be loaded into 
             the card.
@@ -89,6 +96,7 @@
 	       ATI      Return "ISDN for Linux...".
                ATI0        "
                ATI1        "
+			   ATI2 Report of last connection.
                ATO      On line (data mode).
                ATQ0     Enable result codes (default).
                ATQ1     Disable result codes (default).
@@ -99,7 +107,7 @@
 	       ATZ      Load registers and EAZ/MSN from Profile.
 	       AT&Bx    Set Send-Packet-size to x (max. 4000)
                         The real packet-size may be limited by the
-                        low-level-driver used. i.e.: the Teles-Module-
+                        low-level-driver used. i.e.: the HiSax-Module-
                         limit is 2000. You will get NO Error-Message,
                         if you set it to higher Values, because at the
                         time of giving this command the corresponding
@@ -112,8 +120,7 @@
                AT&D3    Same as AT&D2 but also resets all registers.
 	       AT&Ex    Set the EAZ/MSN for this channel to x.
 	       AT&F     Reset all registers and profile to "factory-defaults"
-	       AT&Sx    Set window-size for Teles-driver (x = 1..8) (not yet
-                        implemented)
+	       AT&Sx    Set window-size (x = 1..8) (not yet implemented)
 	       AT&V     Show all settings.
                AT&W0    Write registers and EAZ/MSN to profile. See also
                         iprofd (5.c in this README).
@@ -173,6 +180,10 @@
                                       1 = T.70 protocol (Only for BTX!) on
                             Bit 2:    0 = Don't hangup on DTR low.
                                       1 = Hangup on DTR low.
+			    Bit 3:    0 = Standard response messages
+				      1 = Extended response messages
+                            Bit 4:    0 = CALLER NUMBER before every RING.
+                                      1 = CALLER NUMBER after first RING.
 	     14   0         Layer-2 protocol:
 				      0 = X75/LAPB with I-frames
 				      1 = X75/LAPB with UI-frames
@@ -182,7 +193,7 @@
              15   0         Layer-3 protocol: (at the moment always 0)
                                       0 = transparent
 	     16   250       Send-Packet-size/16
-             17   8         Window-size for Teles-driver (not yet implemented)
+             17   8         Window-size (not yet implemented)
              18   4         Bit coded register, Service-Octet-1 to accept,
                             or to be used on dialout:
                             Bit 0:    Service 1 (audio) when set.
@@ -201,6 +212,14 @@
              20   0         Bit coded register (readonly)
                             Service-Octet-1 of last call.
                             Bit mapping is the same like register 18
+             21   0         Bit coded register (readonly)
+                            Set on incoming call (during RING) to
+                            octet 3 of calling party number IE (Numbering plan)
+                            See section 4.5.10 of ITU Q.931
+             22   0         Bit coded register (readonly)
+                            Set on incoming call (during RING) to
+                            octet 3a of calling party number IE (Screening info)
+                            See section 4.5.10 of ITU Q.931
 
   Last but not least a (at the moment fairly primitive) device to request
   the line-status (/dev/isdninfo) is made available.
@@ -243,124 +262,16 @@
 
 2 System prerequisites:
 
-  ATTENTION! The program "insmod" from the Package "modules-1.2.8" (It's
-             on nearly all newer distributions) has a bug, which makes
-             it impossible to set both driver-Id's when loading the
-             icn-module for the Double-ICN-Card. A patch is supplied
-             in the utility-package called "insmod-1.2.8.patch". Change into
-             the source-directory of insmod, and type
-             "patch < insmod-1.2.8.patch". Then recompile it. This will fix
-             the bug.
-             This bug does NOT occur when using insmod with the Teles-driver
-             or a single ICN-card.
+  ATTENTION!
+
+  Always use the latest module utilities. The current version is
+  named in Documentation/Changes. Some old versions of insmod
+  are not capable of setting the driver-Ids correctly.
  
 3. Lowlevel-driver configuration.
 
-   Configuration depends on how the drivers are built.
-
-   3.1 Drivers built into the kernel.
-
-     3.1.1 Teles driver.
-
-       The Teles driver can be configured using the commandline-feature
-       while loading the kernel with LILO or LOADLIN. It accepts the
-       following syntax:
-
-       teles=p0,i0,m0,d0[,p1,i1,m1,d1 ... ,pn,in,mn,dn][,idstring]
-
-       where
-
-         p0 = portbase of 1st card.                           (default: 0xd80)
-         i0 = irq of 1st card.                                (default: 15)
-         m0 = shared memory of 1st card.                      (default: 0xd0000)
-         d0 = D-channel protocol of 1st card. 1=1TR6, 2=EDSS1 (default: 2)
-
-         p1,i1,m1,d1 = Parameters of second card (defaults: none)
-         pn,in,mn,d1 = Parameters of n'th card (up to 16 cards are supported)
-
-         idstring = Driver-Id for accessing with utilities and identification
-                    when using a Line-monitor. (default: none)
-                    idstring must start with a character!
-
-         The type of the card is determined by the port, irq and shared memory:
-
-           port == 0, shared memory != 0          -> Teles S0-8
-           port != 0, shared memory != 0          -> Teles S0-16.0
-           port != 0, shared memory == 0          -> Teles S0-16.3
-
-       ATTENTION:
-
-       Due to limited hardware-capabilities, there is no way to check the
-       existence of a card. Therefore you need to be sure your card's setup
-       is correct. Also there are bugs in the printed manual of some newer
-       16.3 cards. Have a look to the kernel-syslog. With most of the cards,
-       you should see a line "HSCX version A:5 B:5" there.
-       
-     3.1.2 ICN driver.
-
-       The ICN driver can be configured using the commandline-feature while
-       loading the kernel with LILO or LOADLIN. It accepts the following
-       syntax
-
-       icn=p,m[,idstring1[,idstring2]]
-
-       where
-
-         p = portbase      (default: 0x320)
-         m = shared memory (default: 0xd0000)
-
-       When using the ICN double card, you MUST define TWO idstrings.
-       idstring must start with a character!
-
-       If you like to use more than one card, you can use the program
-       "icnctrl" from the utility-package to configure additional cards.
-       You need to configure shared memory only once, since the icn-driver
-       maps all cards into the same address-space.
-
-       Using the "icnctrl"-utility, portbase and shared memory can also be
-       changed during runtime.
-
-       The D-channel protocol is configured by loading different firmware
-       into the card's memory using the "icnctrl"-utility.
-
-
-   3.2 Drivers built as modules.
-
-     3.2.1 Teles driver.
-
-       The module teles.o can be configured during "insmod'ing" it by
-       appending its parameters to the insmod-commandline. The following
-       syntax is accepted:
-
-       io=m0,i0,p0,d0[,m1,i1,p1,d1 ... ,mn,in,pn,dn] teles_id=idstring
-
-       where
-
-         m0,i0,p0,d0 ... mn,in,pn,dn have the same meanings like the
-                                     parameters described for the kernel-
-                                     version above. Watch out: different
-                                     sequence!
-
-     3.2.2 ICN driver.
-
-       The module icn.o can be configured during "insmod'ing" it by
-       appending its parameters to the insmod-commandline. The following
-       syntax is accepted:
-
-       portbase=p membase=m icn_id=idstring icn_id2=idstring2
-
-       where p, m, idstring1 and idstring2 have the same meanings like
-                                           parameters described for the kernel-
-                                           version above.
-      
-       When using the ICN double card, you MUST define TWO idstrings.
-       idstring must start with a character!
-
-       Using the "icnctrl"-utility, the same features apply to the modularized
-       version like to the kernel-builtin one.
-
-       The D-channel protocol is configured by loading different firmware
-       into the card's memory using the "icnctrl"-utility.
+   Configuration depends on how the drivers are built. See the
+   README.<yourDriver> for information on driver-specific setup.
 
 4. Device-inodes
 
@@ -371,26 +282,11 @@
      44 for the ISDN-callout-tty's.
      45 for control/info/debug devices.
 
-
 5. Application
 
-   a) (Only for ICN-cards) Load the firmware into the card:
-
-       cd icn
-     For 1TR6:
-       icnctrl [-d IDstring] load download/loadpg.bin download/pc_1t_ca.bin
-     For Euro-ISDN:
-       icnctrl [-d IDstring] load download/loadpg.bin download/pc_eu_ca.bin
-
-     When using the ICN-4B, the protocol-software for the second half of
-     the card must be appended to the command line.
-
-     -> The two LEDs at the back cover of the card (ICN-4B: 4 LEDs) must be
-        blinking intermittently now. If a connection is up, the corresponding
-        led is lit continuously.
-
-     For loading pcbit-firmware, refer to Documentation/isdn/README.pcbit
-     and the pcbit manpage, included in the utility-package.
+   a) For some card-types, firmware has to be loaded into the cards, before
+      proceeding with device-independant setup. See README.<yourDriver>
+      for how to do that.
 
    b) If you only intend to use ttys, you are nearly ready now.
 
@@ -422,8 +318,7 @@
 
    h) additionally you may activate charge-hang-up (= Hang up before 
       next charge-info, this only works, if your isdn-provider transmits
-      the charge-info during and after the connection, it does NOT work
-      with the Teles on an EDSS1-Line.):
+      the charge-info during and after the connection):
        isdnctrl chargehup isdn0 on
 
    i) Setup the interface with ifconfig as usual, and set a route to it.
@@ -527,7 +422,7 @@
 
    "isdnctrl l2_prot <InterfaceName> <L2-ProtocolName>" 
    Selects a layer-2-protocol. 
-   (With the ICN-driver and the Teles-driver, "x75i" and "hdlc" is available.
+   (With the ICN-driver and the HiSax-driver, "x75i" and "hdlc" is available.
    With other drivers, "x75ui", "x75bui" may be possible too.)
 
    isdnctrl l3_prot <InterfaceName> <L3-ProtocolName> 
diff -u --recursive --new-file v2.0.30/linux/Documentation/isdn/README.HiSax linux/Documentation/isdn/README.HiSax
--- v2.0.30/linux/Documentation/isdn/README.HiSax	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/isdn/README.HiSax	Mon Aug  4 17:33:59 1997
@@ -0,0 +1,323 @@
+HiSax is a Linux hardware-level driver for passive ISDN cards with Siemens
+chipset (ISAC_S 2085/2086/2186, HSCX SAB 82525). It is based on the Teles
+driver from Jan den Ouden.
+It is meant to be used with isdn4linux, an ISDN link-level module for Linux 
+written by Fritz Elfert.
+
+    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.
+
+
+Supported cards
+---------------
+
+Teles 8.0/16.0/16.3 and compatible ones
+Teles S0/PCMCIA
+Creatix PnP S0 
+AVM A1 (Fritz)
+ELSA Microlink PCC-16, PCF, PCF-Pro, PCC-8
+ELSA Quickstep 1000
+ELSA PCMCIA
+ITK ix1-micro Rev.2
+
+Note: PCF, PCF-Pro: up to now, only the ISDN part is supported
+      PCC-8: not tested yet
+      Teles PCMCIA is EXPERIMENTAL
+
+If you know other passive cards with the Siemens chipset, please let me know.
+To use the PNP cards you need the isapnptools.
+You can combine any card, if there is no conflict between the ressources
+(io, mem, irq), with one exception: The ELSA PCMCIA cannot work with an other
+non PCMCIA ELSA card at the same time. You cannot select ELSA ISA and ELSA
+PCMCIA support at the same time during kernel config.
+
+
+Configuring the driver
+----------------------
+
+The HiSax driver can either be built directly into the kernel or as a module.
+It can be configured using the command line feature while loading the kernel
+with LILO or LOADLIN or, if built as a module, using insmod/modprobe with
+parameters.
+There is also some config needed before you compile the kernel and/or
+modules. It is enclose in the normal "make [menu]config" target at the
+kernel. Don't forget it, especially to select the right D-channel protocol.
+
+Please note: All PnP cards need to be configured with isapnp and will work
+only with the HiSax driver used as a module.
+
+a) when built as a module
+-------------------------
+
+insmod/modprobe  hisax.o \
+  io=iobase irq=IRQ mem=membase type=card_type \
+  protocol=D_channel_protocol id=idstring
+
+or, if several cards are installed:
+
+insmod/modprobe hisax.o \
+  io=iobase1,iobase2,... irq=IRQ1,IRQ2,... mem=membase1,membase2,... \
+  type=card_type1,card_type2,... \
+  protocol=D_channel_protocol1,D_channel_protocol2,... \
+  id=idstring1%idstring2 ...
+
+where "iobaseN" represents the I/O base address of the Nth card, "membaseN"
+the memory base address of the Nth card, etc.
+
+The reason for the delimiter "%" being used in the idstrings is that ","
+won't work with the current modules package.
+
+The parameters may be specified in any order. For example, the "io"
+parameter may precede the "irq" parameter, or vice versa. If several
+cards are installed, the ordering within the comma separated parameter
+lists must of course be consistent.
+
+Only parameters applicable to the card type need to be specified. For
+example, the Teles 16.3 card is not memory-mapped, so the "mem"
+parameter may be omitted for this card. Sometimes it may be necessary
+to specify a dummy parameter, however. This is the case when there is
+a card of a different type later in the list that needs a parameter
+which the preceding card does not. For instance, if a Teles 16.0 card
+is listed after a Teles 16.3 card, a dummy memory base parameter of 0
+must be specified for the 16.3. Instead of a dummy value, the parameter
+can also be skipped by simply omitting the value. For example:
+mem=,0xd0000. See example 6 below.
+
+The parameter for the D-Channel protocol may be omitted if you selected the
+correct one during kernel config. Valid values are "1" for German 1TR6,
+"2" for EDSS1 (Euro ISDN) and "3" for leased lines (no D-Channel).
+
+The Creatix/Teles PnP cards use io1= and io2= instead of io= for specifying 
+the I/O addresses of the ISAC and HSCX chips, respectively.
+
+Card types:
+
+    Type                Required parameters (in addition to type and protocol)
+
+    1   Teles 16.0               irq, mem, io
+    2   Teles  8.0               irq, mem
+    3   Teles 16.3 (non PnP)     irq, io
+    4   Creatix/Teles PnP        irq, io0 (ISAC), io1 (HSCX)
+    5   AVM A1 (Fritz)           irq, io
+    6   ELSA PCC/PCF cards       io or nothing for autodetect (the iobase is
+                                 required only if you have more than one ELSA
+                                 card in your PC)
+    7   ELSA Quickstep 1000	 irq, io  (from isapnp setup)
+    7   ELSA PCMCIA		 irq, io  (set with card manager)
+    8   Teles 16.3 PCMCIA     	 irq, io
+    9   ITK ix1-micro Rev.2      irq, io
+        
+At the moment IRQ sharing is not possible. Please make sure that your IRQ
+is free and enabled for ISA use.
+Note: For using the ELSA PCMCIA you need the cardmanager under MSDOS for
+enabling in the moment, then boot linux with loadlin.
+
+
+Examples for module loading
+
+1. Teles 16.3, Euro ISDN, I/O base 280 hex, IRQ 10
+   modprobe hisax type=3 protocol=2 io=0x280 irq=10
+
+2. Teles 16.0, 1TR6 ISDN, I/O base d80 hex, IRQ 5, Memory d0000 hex
+   modprobe hisax protocol=1 type=1 io=0xd80 mem=0xd0000 irq=5
+
+3. Fritzcard, Euro ISDN, I/O base 340 hex, IRQ 10 and ELSA PCF, Euro ISDN
+   modprobe hisax type=5,6 protocol=2,2 io=0x340 irq=10 id=Fritz%Elsa
+
+4. Any ELSA PCC/PCF card, Euro ISDN
+   modprobe hisax type=6 protocol=2
+
+5. Teles 16.3 PnP, Euro ISDN, with isapnp configured 
+   isapnp config:  (INT 0 (IRQ 10 (MODE +E)))
+ 		   (IO 0 (BASE 0x0580))
+                   (IO 1 (BASE 0x0180))
+   modprobe hisax type=4 protocol=2 irq=10 io0=0x580 io1=0x180
+
+6. Teles 16.3, Euro ISDN, I/O base 280 hex, IRQ 12 and
+   Teles 16.0, 1TR6, IRQ 5, Memory d0000 hex
+   modprobe hisax type=3,1 protocol=2,1 io=0x280 mem=0,0xd0000
+
+   Please note the dummy 0 memory address for the Teles 16.3, used as a
+   placeholder as described above, in the last example.
+
+7. Teles PCMCIA, Euro ISDN, I/O base 180 hex, IRQ 15 (default values)
+   modprobe hisax type=8 protocol=2 io=0x180 irq=15
+
+
+b) using LILO/LOADLIN, with the driver compiled directly into the kernel
+------------------------------------------------------------------------
+
+hisax=typ1,dp1,pa_1,pb_1,pc_1[,typ2,dp2,pa_2 ... \
+      typn,dpn,pa_n,pb_n,pc_n][,idstring1[,idstring2,...,idstringn]]
+
+where
+     typ1 = type of 1st card (default depends on kernel settings)
+     dp1  = D-Channel protocol of 1st card. 1=1TR6, 2=EDSS1, 3=leased
+     pa_1 = 1st parameter (depending on the type of the card)
+     pb_1 = 2nd parameter (    "     "   "   "   "   "   "  )
+     pc_1 = 3rd parameter (    "     "   "   "   "   "   "  )
+
+     typ2,dp2,pa_2,pb_2,pc_2 = Parameters of the second card (defaults: none)
+     typn,dpn,pa_n,pb_n,pc_n = Parameters of the n'th card (up to 16 cards are
+                                                                     supported)
+
+     idstring = Driver ID for accessing the particular card with utility
+                programs and for identification when using a line monitor
+                (default: "HiSax")
+
+                Note: the ID string must start with an alphabetical character!
+
+Card types:
+       
+  type  
+    1 	Teles 16.0     		pa=irq	pb=membase	pc=iobase
+    2 	Teles  8.0      	pa=irq	pb=membase
+    3 	Teles 16.3      	pa=irq	pb=iobase
+    4 	Creatix/Teles PNP     	ONLY WORKS AS A MODULE !
+    5 	AVM A1 (Fritz)  	pa=irq	pb=iobase
+    6 	ELSA PCC/PCF cards      pa=iobase or nothing for autodetect
+    7   ELSA Quickstep 1000	ONLY WORKS AS A MODULE !
+    7   ELSA PCMCIA		irq, io  (set with card manager)
+    8   Teles S0 PCMCIA    	pa=irq  pb=iobase
+    9   ITK ix1-micro Rev.2     pa=irq  pb=iobase
+
+
+Running the driver
+------------------
+
+When you insmod isdn.o and hisax.o (or with the in-kernel version, during 
+boot time), a few lines should appear in your syslog. Look for something like:
+
+Apr 13 21:01:59 kke01 kernel: HiSax: Driver for Siemens chip set ISDN cards
+Apr 13 21:01:59 kke01 kernel: HiSax: Version 2.1
+Apr 13 21:01:59 kke01 kernel: HiSax: Revisions 1.14/1.9/1.10/1.25/1.8
+Apr 13 21:01:59 kke01 kernel: HiSax: Total 1 card defined
+Apr 13 21:01:59 kke01 kernel: HiSax: Card 1 Protocol EDSS1 Id=HiSax1 (0)
+Apr 13 21:01:59 kke01 kernel: HiSax: Elsa driver Rev. 1.13
+...
+Apr 13 21:01:59 kke01 kernel: Elsa: PCF-Pro found at 0x360 Rev.:C IRQ 10
+Apr 13 21:01:59 kke01 kernel: Elsa: timer OK; resetting card
+Apr 13 21:01:59 kke01 kernel: Elsa: HSCX version A: V2.1  B: V2.1
+Apr 13 21:01:59 kke01 kernel: Elsa: ISAC 2086/2186 V1.1
+...
+Apr 13 21:01:59 kke01 kernel: HiSax: DSS1 Rev. 1.14
+Apr 13 21:01:59 kke01 kernel: HiSax: 2 channels added
+
+This means that the card is ready for use.
+Cabling problems or line-downs are not detected, and only ELSA cards can detect
+the S0 power.
+
+Remember that, according to the new strategy for accessing low-level drivers
+from within isdn4linux, you should also define a driver ID while doing
+insmod: Simply append hisax_id=<SomeString> to the insmod command line. This
+string MUST NOT start with a digit or a small 'x'!
+
+At this point you can run a 'cat /dev/isdnctrl0' and view debugging 
+messages. 
+
+At the moment, debugging messages are enabled with the telesctrl tool:
+
+    telesctrl <DriverId> DebugCmd <debugging_flags>
+
+<DriverId> default is HiSax, if you didn't specified one.
+
+DebugCmd is  1  for generic debugging
+            11  for layer 1 development debugging
+            13  for layer 3 development debugging
+
+where <debugging_flags> is the integer sum of the following debugging
+options you wish enabled:
+
+With DebugCmd set to 1:
+
+         1  Link-level <--> hardware-level communication
+         2  Top state machine
+         4  D-Channel Q.931 (call control messages)
+         8  D-Channel Q.921
+        16  B-Channel X.75
+        32  D-Channel l2
+        64  B-Channel l2
+       128  D-Channel link state debugging
+       256  B-Channel link state debugging
+       512  TEI debug
+      1024  LOCK debug in callc.c
+      2048  More paranoid debug in callc.c (not for normal use)
+
+With DebugCmd set to 11:
+
+         1  Warnings (default: on)
+         2  IRQ status
+         4  ISAC
+         8  ISAC FIFO
+        16  HSCX
+        32  HSCX FIFO (attention: full B-Channel output!)
+        64  D-Channel LAPD frame types
+
+With DebugCmd set to 13:
+
+         1  Warnings (default: on)
+         2  l3 protocol discriptor errors
+         4  l3 state machine
+         8  charge info debugging (1TR6)
+
+For example, 'telesctrl HiSax 1 0x3ff' enables full generic debugging.
+
+
+Warning
+-------
+HiSax is a work in progress and may crash your machine. It has not been
+certified and therefore operation on your PTT's ISDN network is probably
+illegal.
+
+
+Limitations
+-----------
+At this time, HiSax only works on Euro ISDN lines and German 1TR6 lines.
+
+
+Bugs 
+----
+If you find any, please let me know. 
+
+
+Thanks
+------
+Special thanks to:
+
+        Emil Stephan for the name HiSax which is a mix of HSCX and ISAC.
+
+        Fritz Elfert, Jan den Ouden, Michael Hipp, Michael Wein,
+        Andreas Kool, Pekka Sarnila, Sim Yskes, Johan Myrre'en,
+	Klaus-Peter Nischke (ITK AG), Christof Petig, Werner Fehn (ELSA GmbH),
+	Volker Schmidt
+        and more people who are hunting bugs. (If I forgot somebody, please
+	send me a mail). 
+
+        Firma ELSA GmbH
+        
+        My girl friend and partner in life Ute for her patience with me.
+
+
+Enjoy,
+
+Karsten Keil   
+keil@temic-ech.spacenet.de
+
+
+Appendix: Teles PCMCIA driver
+-----------------------------
+
+See 
+   http://www.stud.uni-wuppertal.de/~ea0141/pcmcia.html
+for instructions.
diff -u --recursive --new-file v2.0.30/linux/Documentation/isdn/README.audio linux/Documentation/isdn/README.audio
--- v2.0.30/linux/Documentation/isdn/README.audio	Fri Jun  7 06:02:39 1996
+++ linux/Documentation/isdn/README.audio	Mon Aug  4 17:33:59 1997
@@ -1,4 +1,4 @@
-$Id: README.audio,v 1.3 1996/06/05 02:19:36 fritz Exp $
+$Id: README.audio,v 1.5 1997/02/23 23:53:46 fritz Exp $
 
 ISDN subsystem for Linux.
   Description of audio mode.
@@ -12,7 +12,7 @@
 
         AT+FCLASS=8      Enable audio mode.
                          This affects the following registers:
-                           S18: Bits 0 and 3 are set.
+                           S18: Bits 0 and 2 are set.
                            S16: Set to 48 and any further change to
                                 larger values is blocked.
         AT+FCLASS=0      Disable audio mode.
@@ -86,8 +86,11 @@
     starts sending audio data to the application. There are several
     escape sequences defined, all using DLE (0x10) as Escape char:
 
-    <DLE><ETX>              End of audio data. Emulator stops
+    <DLE><ETX>              End of audio data. (i.e. caused by a
+                            hangup of the remote side) Emulator stops
                             recording, responding with VCON.
+	<DLE><DC4>				Abort recording, (send by appl.) Emulator
+							stops recording, sends DLE,ETX.
     <DLE><DLE>              Escape sequence for DLE in data stream.
     <DLE>0                  Touchtone "0" received.
          ...
@@ -107,13 +110,16 @@
     <DLE>s                  silence. Silence detected from the
                             start of recording.
 
-    Any character sent by the application, except XON (0x11) or XOFF (0x13)
-    immediately stops recording.
-
   Audio playback.
 
     When sending audio data, upon AT+VTX command, emulator responds with
     CONNECT, and starts transferring data from application to the phone line.
     The same DLE sequences apply to this mode.
 
+  Full-Duplex-Audio:
+
+    When _both_ commands for recording and playback are given in _one_
+    AT-command-line (i.e.: "AT+VTX+VRX"), full-duplex-mode is selected.
+	In this mode, the only way to stop recording is sending <DLE><DC4>
+    and the only way to stop playback is to send <DLE><ETX>.
 
diff -u --recursive --new-file v2.0.30/linux/Documentation/isdn/README.avmb1 linux/Documentation/isdn/README.avmb1
--- v2.0.30/linux/Documentation/isdn/README.avmb1	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/isdn/README.avmb1	Mon Aug  4 17:33:59 1997
@@ -0,0 +1,62 @@
+The driver provides a kernel capi2.0 Interface (kernelcapi) and
+on top of this a User-Level-CAPI2.0-interface (capi)
+and a driver to connect isdn4linux with CAPI2.0 (capidrv).
+
+The Author can be reached at calle@calle.in-berlin.de
+The command avmcapictrl is part of the isdn4linux-utils.
+t4-files can be found at ftp.avm.de.
+
+Installing
+----------
+
+You need at least /dev/capi20 to load the firmware.
+
+mknod /dev/capi20 c 68 0
+mknod /dev/capi20.00 c 68 1
+mknod /dev/capi20.01 c 68 2
+.
+.
+.
+mknod /dev/capi20.19 c 68 20
+
+Running
+-------
+
+To use the card you need the t4-files to download the firmware.
+AVM GmbH provides several t4-files for the different D-channel
+protocols (b1.t4 for Euro-ISDN). Install these file in /lib/isdn.
+
+If you not compile the driver as modules, you have to add the
+card(s) and load them after booting:
+
+avmcapictrl add 0x150 15
+avmcapictrl load /lib/isdn/b1.t4 1
+
+if you configure as modules you have two possibilities:
+
+insmod /lib/modules/current/misc/capiutil.o
+insmod /lib/modules/current/misc/kernelcapi.o portbase=0x150 irq=15
+insmod /lib/modules/current/misc/capidrv.o
+insmod /lib/modules/current/misc/capi.o
+avmcapictrl load /lib/isdn/b1.t4
+
+or
+
+insmod /lib/modules/current/misc/capiutil.o
+insmod /lib/modules/current/misc/kernelcapi.o
+insmod /lib/modules/current/misc/capidrv.o
+insmod /lib/modules/current/misc/capi.o
+avmcapictrl add 0x150 15
+avmcapictrl load /lib/isdn/b1.t4
+
+Questions
+---------
+Check out the FAQ (ftp.franken.de).
+
+Bugs 
+----
+If you find any please let me know. 
+
+Enjoy,
+
+Carsten Paeth (calle@calle.in-berlin.de)
diff -u --recursive --new-file v2.0.30/linux/Documentation/isdn/README.icn linux/Documentation/isdn/README.icn
--- v2.0.30/linux/Documentation/isdn/README.icn	Fri Jun  7 06:02:39 1996
+++ linux/Documentation/isdn/README.icn	Mon Aug  4 17:33:59 1997
@@ -1,9 +1,9 @@
-$Id: README.icn,v 1.4 1996/06/03 19:57:07 fritz Exp $
+$Id: README.icn,v 1.5 1997/04/23 18:55:55 fritz Exp $
 
 You can get the ICN-ISDN-card from:
 
 Thinking Objects Software GmbH
-Obere Heerbergstr. 17
+Versbacher Röthe 159
 97078 Würzburg
 Tel: +49 931 2877950
 Fax: +49 931 2877951
@@ -61,4 +61,88 @@
       1  1  0  1  0x358
       1  1  1  0  0x368
       1  1  1  1  NOT ALLOWED!
+
+The ICN driver either may be build into kernel or as a module. Initialization
+depends on how the drive is built:
+
+Driver built into the kernel:
+
+  The ICN driver can be configured using the commandline-feature while
+  loading the kernel with LILO or LOADLIN. It accepts the following syntax:
+
+  icn=p,m[,idstring1[,idstring2]]
+
+  where
+
+    p = portbase      (default: 0x320)
+    m = shared memory (default: 0xd0000)
+
+  When using the ICN double card (4B), you MUST define TWO idstrings.
+  idstring must start with a character! There is no way for the driver
+  to distinguish between a 2B and 4B type card. Therefore, by supplying
+  TWO idstrings, you tell the driver that you have a 4B installed.
+  
+  If you like to use more than one card, you can use the program
+  "icnctrl" from the utility-package to configure additional cards.
+  You need to configure shared memory only once, since the icn-driver
+  maps all cards into the same address-space.
+
+  Using the "icnctrl"-utility, portbase and shared memory can also be
+  changed during runtime.
+
+  The D-channel protocol is configured by loading different firmware
+  into the card's memory using the "icnctrl"-utility.
+
+
+Driver built as module:
+
+  The module icn.o can be configured during "insmod'ing" it by
+  appending its parameters to the insmod-commandline. The following
+  syntax is accepted:
+
+    portbase=p membase=m icn_id=idstring [icn_id2=idstring2]
+
+  where p, m, idstring1 and idstring2 have the same meanings like
+  parameters described for the kernel-version above.
+      
+  When using the ICN double card (4B), you MUST define TWO idstrings.
+  idstring must start with a character! There is no way for the driver
+  to distinguish between a 2B and 4B type card. Therefore, by supplying
+  TWO idstrings, you tell the driver that you have a 4B installed.
+  
+  Using the "icnctrl"-utility, the same features apply to the modularized
+  version like to the kernel-builtin one.
+
+  The D-channel protocol is configured by loading different firmware
+  into the card's memory using the "icnctrl"-utility.
+
+Loading the firmware into the card:
+
+  The firmware is supplied together with the isdn4k-utils package. It
+  can be found in the subdirectory icnctrl/firmware/
+
+  There are 3 files:
+
+    loadpg.bin   - Image of the bootstrap loader.
+    pc_1t_ca.bin - Image of firmware for german 1TR6 protocol.
+    pc_eu_ca.bin - Image if firmware for EDSS1 (Euro-ISDN) protocol.
+
+  Assumed you have installed the utility-package correctly, the firmware
+  will be downloaded into the 2B-card using the following command:
+
+    icnctrl -d Idstring load /etc/isdn/loadpg.bin /etc/isdn/pc_XX_ca.bin
+
+  where XX is either "1t" or "eu", depending of the D-Channel protocol
+  used on your S0-bus and Idstring is the Name of the card, given during
+  insmod-time or (for kernel-builtin driver) on the kernel commandline.
+
+  To load a 4B-card, the same command is used, except a second firmware
+  file is appended to the commandline of icnctrl.
+
+  -> After dowloading firmware, the two LEDs at the back cover of the card
+     (ICN-4B: 4 LEDs) must be blinking intermittently now. If a connection
+     is up, the corresponding led is lit continuously.
+
+  For further documentation (adding more ICN-cards), refer to the manpage
+  icnctrl.8 which is included in the isdn4k-utils package.
 
diff -u --recursive --new-file v2.0.30/linux/Documentation/isdn/README.sc linux/Documentation/isdn/README.sc
--- v2.0.30/linux/Documentation/isdn/README.sc	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/isdn/README.sc	Mon Aug  4 17:33:59 1997
@@ -0,0 +1,274 @@
+Welcome to Beta Release 2 of the combination ISDN driver for SpellCaster's
+ISA ISDN adapters. Please note this release 2 includes support for the
+DataCommute/BRI and TeleCommute/BRI adapters only and any other use is 
+guaranteed to fail. If you have a DataCommute/PRI installed in the test
+computer, we recommend removing it as it will be detected but will not
+be usable.  To see what we have done to Beta Release 2, see section 3.
+
+Speaking of guarantees, THIS IS BETA SOFTWARE and as such contains
+bugs and defects either known or unknown. Use this software at your own
+risk. There is NO SUPPORT for this software. Some help may be available
+through the web site or the mailing list but such support is totally at
+our own option and without warrantee. If you choose to assume all and
+total risk by using this driver, we encourage you to join the beta
+mailing list.
+
+To join the Linux beta mailing list, send a message to:
+majordomo@spellcast.com with the words "subscribe linux-beta" as the only
+contents of the message. Do not include a signature. If you choose to
+remove yourself from this list at a later date, send another message to
+the same address with the words "unsubscribe linux-beta" as it's only
+contents.
+
+TABLE OF CONTENTS
+-----------------
+	1. Introduction
+	 1.1 What is ISDN4Linux?
+	 1.2 What is different between this driver and previous drivers?
+	 1.3 How do I setup my system with the correct software to use
+	     this driver release?
+	
+	2. Basic Operations
+	 2.1 Unpacking and installing the driver
+	 2.2 Read the man pages!!!
+	 2.3 Installing the driver
+	 2.4 Removing the driver
+	 2.5 What to do if it doesn't load
+	 2.6 How to setup ISDN4Linux with the driver
+
+	3. Beta Change Summaries and Miscellaneous Notes
+
+1. Introduction
+---------------
+
+The revision 2 Linux driver for SpellCaster ISA ISDN adapters is built
+upon ISDN4Linux available seperately or as included in Linux 2.0 and later.
+The driver will support a maximum of 4 adapters in any one system of any
+type including DataCommute/BRI, DataCommute/PRI and TeleCommute/BRI for a
+maximum of 92 channels for host. The driver is supplied as a module in
+source form and needs to be complied before it can be used. It has been
+tested on Linux 2.0.20.
+
+1.1 What Is ISDN4Linux
+
+ISDN4Linux is a driver and set of tools used to access and use ISDN devices
+on a Linux platform in a common and standard way. It supports HDLC and PPP
+protocols and offers channel bundling and MLPPP support. To use ISDN4Linux
+you need to configure your kernel for ISDN support and get the ISDN4Linux
+tool kit from our web site.
+
+ISDN4Linux creates a channel pool from all of the available ISDN channels
+and therefore can function across adapters. When an ISDN4Linux compliant
+driver (such as ours) is loaded, all of the channels go into a pool and
+are used on a first-come first-served basis. In addition, individual
+channels can be specifically bound to particular interfaces.
+
+1.2 What is different between this driver and previous drivers?
+
+The revision 2 driver besides adopting the ISDN4Linux architecture has many
+subtle and not so subtle functional differences from previous releases. These
+include:
+	- More efficient shared memory management combined with a simpler
+	  configuration. All adapters now use only 16Kbytes of shared RAM
+	  versus between 16K and 64K. New methods for using the shared RAM
+	  allow us to utilize all of the available RAM on the adapter through
+	  only one 16K page.
+	- Better detection of available upper memory. The probing routines
+	  have been improved to better detect avaialble shared RAM pages and
+	  used pages are now locked.
+	- Decreased loading time and a wider range of I/O ports probed.
+	  We have significantly reduced the amount of time it takes to load
+	  the driver and at the same time doubled the number of I/O ports
+	  probed increasing the likelyhood of finding an adapter.
+	- We now support all ISA adapter models with a single driver instead
+	  of seperate drivers for each model. The revision 2 driver supports
+	  the DataCommute/BRI, DataCommute/PRI and TeleCommute/BRI in any
+	  combination up to a maximum of four adapters per system.
+	- On board PPP protocol support has been removed in favour of the
+	  sync-PPP support used in ISDN4Linux. This means more control of
+	  the protocol parameters, faster negotiation time and a more
+	  familiar interface.
+
+1.3 How do I setup my system with the correct software to use
+    this driver release?
+
+Before you can compile, install and use the SpellCaster ISA ISDN driver, you
+must ensure that the following software is installed, configuraed and running:
+
+	- Linux kernel 2.0.20 or later with the required init and ps
+	  versions. Please see your distribution vendor for the correct
+	  utility packages. The latest kernel is available from
+	  ftp://sunsite.unc.edu/pub/Linux/kernel/v2.0/
+
+	- The latest modules package (modules-2.0.0.tar.gz) from
+	  ftp://sunsite.unc.edu/pub/Linux/kernel/modules-2.0.0.tar.gz
+
+	- The ISDN4Linux tools available from 
+	  ftp://ftp.franken.de/pub/isdn4linux/v2.0/isdn4k-utils-2.0.tar.gz
+	  This package may fail to compile for you so you can alternatively
+	  get a pre-compiled version from
+	  ftp://ftp.spellcast.com/pub/drivers/isdn4linux/isdn4k-bin-2.0.tar.gz
+
+
+2. Basic Operations
+-------------------
+
+2.1 Unpacking and installing the driver
+
+	1. As root, create a directory in a convienient place. We suggest
+	   /usr/src/spellcaster.
+	
+	2. Unpack the archive with :
+		tar xzf sc-n.nn.tar.gz -C /usr/src/spellcaster
+	
+	3. Change directory to /usr/src/spellcaster
+
+	4. Read the README and RELNOTES files.
+
+	5. Run 'make' and if all goes well, run 'make install'.
+
+2.2 Read the man pages!!!
+
+Make sure you read the scctrl(8) and sc(4) manual pages before continuing
+any further. Type 'man 8 scctrl' and 'man 4 sc'.
+
+2.3 Installing the driver
+
+To install the driver, type '/sbin/insmod sc' as root. sc(4) details options
+you can specify but you shouldn't need to use any unless this doesn't work.
+
+Make sure the driver loaded and detected all of the adapters by typing
+'dmesg'.
+
+The driver can be configured so that it is loaded upon startup.  To do this, 
+edit the file "/etc/modules/'uname -f'/'uname -v'" and insert the driver name
+"sc" into this file.
+
+2.4 Removing the driver
+
+To remove the driver, delete any interfaces that may exist (see isdnctrl(8)
+for more on this) and then type '/sbin/rmmod sc'.
+
+2.5 What to do if it doesn't load
+
+If, when you try to install the driver, you get a message mentioning
+'register_isdn' then you do not have the ISDN4Linux system installed. Please
+make sure that ISDN support is configured in the kernel.
+
+If you get a message that says 'initialization of sc failed', then the
+driver failed to detect an adapter or failed to find resources needed such
+as a free IRQ line or shared memory segment. If you are sure there are free
+resources available, use the insmod options detailed in sc(4) to override
+the probing function.  
+
+Upon testing, the following problem was noted, the driver would load without
+problems, but the board would not respond beyond that point.  When a check was 
+done with 'cat /proc/interrupts' the interrupt count for sc was 0.  In the event 
+of this problem, change the BIOS settings so that the interrupts in question are
+reserved for ISA use only.   
+
+
+2.6 How to setup ISDN4Linux with the driver
+
+There are two main configurations which you can use with the driver:
+
+A)	Basic HDLC connection
+B)	PPP connection
+C)	MLPPP connection
+
+It should be mentioned here that you may also use a tty connection if you desire. 
+The Documentation directory of the isdn4linux subsystem offers a good documentation
+on this feature.
+
+A) 10 steps to the establishment of a basic HDLC connection
+-----------------------------------------------------------
+
+- please open the isdn-hdlc file in the examples directory and follow along...
+	
+	This file is a script used to configure a BRI ISDN TA to establish a basic HDLC
+	connection between its two channels.  There two network interfaces which are 
+	created and two routes added between the channels.
+
+	i)   using the isdnctrl utitity, add an interface with "addif" and name it "isdn0"
+	ii)  add the outgoing and inbound telephone numbers
+	iii) set the Layer 2 protocol to hdlc
+	iv)  set the eaz of the interface to be the phone number of that specific channel
+	v)   to turn the callback features off, set the callback to "off" and
+	     the callback delay (cbdelay) to 0.
+	vi)  the hangup timeout can be set to a specified number of seconds
+	vii) the hangup upon incomming call can be set on or off 
+	viii) use the ifconfig command to bring-up the network interface with a specific
+	     IP address and point to point address
+	viv) add a route to the IP address through the isdn0 interface
+	x)   a ping should result in the establishment of the connection
+
+	
+B) Establishment of a PPP connection
+------------------------------------
+
+- please open the isdn-ppp file in the examples directory and follow along...
+	
+	This file is a script used to configure a BRI ISDN TA to establish a PPP connection 
+	between the two channels.  The file is almost identical to the HDLC connection
+	example except that the packet ecapsulation type has to be set.
+	
+	use the same procedure as in the HDLC connection from steps i) to iii) then, 
+	after the Layer 2 protocol is set, set the encapsulation  "encap" to syncppp.
+	With this done, the rest of the steps, iv) to x) can be followed from above.
+
+	Then, the ipppd (ippp daemon) must be setup:
+	
+	xi)   use the ipppd function found in /sbin/ipppd to set the following:
+	xii)  take out (minus) VJ compression and bsd compression
+	xiii) set the mru size to 2000
+	xiv)  link the two /dev interfaces to the daemon
+
+NOTE:  A "*" in the inbound telephone number specifies that a call can be accepted
+       on any number.
+
+C) Establishment of a MLPPP connection
+--------------------------------------
+
+- please open the isdn-mppp file in the examples directory and follow along...
+	
+	This file is a script used to configure a BRI ISDN TA to accept a Multi Link PPP
+	connection. 
+	
+	i)   using the isdnctrl utitity, add an interface with "addif" and name it "ippp0"
+	ii)  add the inbound telephone number
+	iii) set the Layer 2 protocol to hdlc and the Layer 3 protocol to trans (transparent)
+	iv)  set the packet encapsulation to syncppp
+	v)   set the eaz of the interface to be the phone number of that specific channel
+	vi)   to turn the callback features off, set the callback to "off" and
+	     the callback delay (cbdelay) to 0.
+	vi)  the hangup timeout can be set to a specified number of seconds
+	vii) the hangup upon incomming call can be set on or off 
+	viii) add a slave interface and name it "ippp32" for example
+	viv)  set the similar parameters for the ippp32 interface
+	x)    use the ifconfig command to bring-up the ippp0 interface with a specific
+	     IP address and point to point address
+	xi)  add a route to the IP address through the ippp0 interface
+	xii) use the ipppd function found in /sbin/ipppd to set the following:
+	xiii) take out (minus) bsd compression
+	xiv) set the mru size to 2000
+	xv)  add (+) the multi-link function "+mp"
+	xv)  link the two /dev interfaces to the daemon
+
+NOTE:  To use the MLPPP connection to dial OUT to a MLPPP connection, change the 
+       inbound telephone numbers to the outgoing telephone numbers of the MLPPP 
+       host.
+
+	
+3. Beta Change Summaries and Miscellaneous Notes
+------------------------------------------------
+When using the "scctrl" utility to upload firmware revisions on the board, please
+note that the byte count displayed at the end of the operation may be different 
+than the total number of bytes in the "dcbfwn.nn.sr" file.  Please disregard the 
+displayed byte count.
+
+It was noted that in Beta Release 1, the module would fail to load and result in a
+segmentation fault when insmod"ed".  This problem was created when one of the 
+isdn4linux parameters, (isdn_ctrl, data field) was filled in.  In some cases, this
+data field was NULL, and was left unchecked, so when it was referenced.. segv.  
+The bug has been fixed around line 63-68 of event.c.
+
diff -u --recursive --new-file v2.0.30/linux/Documentation/isdn/README.teles linux/Documentation/isdn/README.teles
--- v2.0.30/linux/Documentation/isdn/README.teles	Sat Jun 29 10:36:22 1996
+++ linux/Documentation/isdn/README.teles	Wed Dec 31 16:00:00 1969
@@ -1,73 +0,0 @@
-This is my Linux hardware level driver for Teles compatible ISDN cards. It is 
-meant to be used with isdn4isdn4linux, an ISDN Link-level module for Linux written
-by Fritz Elfert.
-
-Isdn4linux can be obtained from ftp.franken.de:/pub/isdn4linux. The most recent 
-Teles driver can be found on my homepage, http://www.xs4all.nl:/~jdo.
-
-Warning
--------
-Teles4isdn4linux is a work in progress and may crash your machine. It has not 
-been certified and therefore operation on your PTT's ISDN network is probably 
-illegal.
-
-Limitations
------------
-Teles4isdn4linux only works on Euro ISDN lines and german 1TR6-lines.
-
-For the B channel transparent (HDLC) protocol and X.75 have been implemented. 
-
-Running
--------
-When you insmod isdn.o and teles.o (or with the kernel-version, during boottime)
-a few lines should appear in your syslog. Look for something like:
-
-Oct 11 16:53:30 jedi kernel: channels 2
-Oct 11 16:53:31 jedi kernel: Teles module installed
-
-Remember, that according to the new strategy for accessing Low-level-drivers
-from within isdn4linux you should also define a driver-id while doing
-insmod: Simply append teles_id=<SomeString> to the insmod-commandline. This
-string MUST NOT start with a digit or a small 'x'!
-
-At this point you can run a 'cat /dev/isdnctrl0' and view debugging 
-messages. Debugging messages are enabled with the telesctrl tool: 
-
-	teles/telesctrl <DriverId> 1 <debugging_flags>
-
-where <debugging_flags> is the integer sum of the following debugging
-options you wish enabled:
-
-	1   Link-level <--> Hardware-level communication
-	2   Top state machine 
-	4   D channel Q.931 (call control messages)
-	8   D channel Q.921 
-	16  B channel X.75
-	32  Lowlevel (irq and Layer1 stuff)
-
-For example 'teles/telesctrl MyTeles 1 63' enables full 
-debugging.
-
-Questions
----------
-Check out the FAQ (ftp.franken.de).
-
-Bugs 
-----
-If you find any please let me know. 
-
-Thanks
-------
-Special thanks to:
-
-	Erik Bos,Beat Doebeli,Fritz Elfert,	
-	Pauline Middelink,Paula de Nie, 	
-	Bernd Oerding,Stephan Seidl,Matthias Urlichs, 
-	Rogier Wolff
-
-
-
-Enjoy,
-
-Jan den Ouden   denouden@groovin.xs4all.nl
-
diff -u --recursive --new-file v2.0.30/linux/Documentation/locks.txt linux/Documentation/locks.txt
--- v2.0.30/linux/Documentation/locks.txt	Wed May 15 01:22:04 1996
+++ linux/Documentation/locks.txt	Sun Aug  3 13:59:07 1997
@@ -2,37 +2,30 @@
 
 		Andy Walker <andy@lysaker.kvaerner.no>
 
-			    15 May 1996
+			    12 May 1997
 
 
-What's New?
------------
+1. What's New?
+--------------
 
-Flock Emulation Warnings
-------------------------
-Many people will have noticed the ugly messages that the file locking
-code started generating with the release of kernel version 1.3.95. The
-messages look something like this:
+1.1 Broken Flock Emulation
+--------------------------
 
-    fcntl_setlk() called by process XX with broken flock() emulation
+The old flock(2) emulation in the kernel was swapped for proper BSD
+compatible flock(2) support in the 1.3.x series of kernels. With the
+release of the 2.1.x kernel series, support for the old emulation has
+been totally removed, so that we don't need to carry this baggage
+forever.
 
-This is a warning for people using older C libraries that those libraries
-are still calling the pre 1.3.x flock() emulation routines, instead of
-the real flock() system call. The old routines are quite badly broken,
-especially with respect to parent-child lock sharing, and can give bad
-results if, for example, sendmail attempts to use them.
+This should not cause problems for anybody, since everybody using a
+2.1.x kernel should have updated their C library to a suitable version
+anyway (see the file "linux/Documentation/Changes".)
 
-Fixed versions of the C libraries have been on public release for many
-months. The latest versions are 5.2.18 or 5.3.12 for ELF, and I believe
-somebody made a 4.7.6 release for people using a.out systems.
+1.2 Allow Mixed Locks Again
+---------------------------
 
-In 1.3.96 Linus decided to be lenient on the stragglers and changed the
-warning message so that the kernel will only complain five times and
-then shut up. That should make life more bearable even for people who,
-for some reason, don't want to upgrade.
-
-Sendmail Problems
------------------
+1.2.1 Typical Problems - Sendmail
+---------------------------------
 Because sendmail was unable to use the old flock() emulation, many sendmail
 installations use fcntl() instead of flock(). This is true of Slackware 3.0
 for example. This gave rise to some other subtle problems if sendmail was
@@ -42,23 +35,50 @@
 over time, or under a very heavy mail load, would eventually cause the kernel
 to lock solid with deadlocked processes.
 
-Disallow Mixed Locks
---------------------
-I have chosen the rather cruel solution of disallowing mixed locking styles
-on a given file at a given time. Attempts to lock a file with flock() when
-fcntl() locks exist, or vice versa, return with an error status of EBUSY.
-This seemed to be the only way to avoid all possible deadlock conditions,
-as flock() locks do not strictly have one owner process and so can't be
-checked for deadlocking in the usual manner.
-
-The process that created a lock with flock() might have forked multiple
-children and exited. Previously the parent process would have been marked
-as the owner of the lock, but deadlocks could just have easily occurred in
-one or more of the children, which we would not have been able to identify
-and avoid.
-
-Some programs may break (again, groan). In particular the aforementioned
-sendmail may have problems running in 'newaliases' mode. It will no longer
-deadlock though. Recompile sendmail to use flock() and your troubles will
-be over.
 
+1.2.2 The Solution
+------------------
+The solution I have chosen, after much experimentation and discussion,
+is to make flock() and fcntl() locks oblivious to each other. Both can
+exists, and neither will have any effect on the other.
+
+I wanted the two lock styles to be cooperative, but there were so many
+race and deadlock conditions that the current solution was the only
+practical one. It puts us in the same position as, for example, SunOS
+4.1.x and serveral other commercial Unices. The only OS's that support
+cooperative flock()/fcntl() are those that emulate flock() using
+fcntl(), with all the problems that implies.
+
+
+1.3 Mandatory Locking As A Mount Option
+---------------------------------------
+
+Mandatory locking, as described in 'Documentation/mandatory.txt' was prior
+to this release a general configuration option that was valid for all
+mounted filesystems. This had a number of inherent dangers, not the least
+of which was the ability to freeze an NFS server by asking it to read a
+file for which a mandatory lock existed.
+
+From this release of the kernel, mandatory locking can be turned on and off
+on a per-filesystem basis, using the mount options 'mand' and 'nomand'.
+The default is to disallow mandatory locking. The intention is that
+mandatory locking only be enabled on a local filesystem as the specific need
+arises.
+
+Until an updated version of mount(8) becomes available you may have to apply
+this patch to the mount sources (based on the version distributed with Rick
+Faiths util-linux-2.5 package):
+
+*** mount.c.orig	Sat Jun  8 09:14:31 1996
+--- mount.c	Sat Jun  8 09:13:02 1996
+***************
+*** 100,105 ****
+--- 100,107 ----
+    { "noauto",	0, MS_NOAUTO	},	/* Can  only be mounted explicitly */
+    { "user",	0, MS_USER	},	/* Allow ordinary user to mount */
+    { "nouser",	1, MS_USER	},	/* Forbid ordinary user to mount */
++   { "mand",	0, MS_MANDLOCK	},	/* Allow mandatory locks on this FS */
++   { "nomand",	1, MS_MANDLOCK	},	/* Forbid mandatory locks on this FS */
+    /* add new options here */
+  #ifdef MS_NOSUB
+    { "sub",	1, MS_NOSUB	},	/* allow submounts */
diff -u --recursive --new-file v2.0.30/linux/Documentation/mandatory.txt linux/Documentation/mandatory.txt
--- v2.0.30/linux/Documentation/mandatory.txt	Tue Apr 16 00:27:10 1996
+++ linux/Documentation/mandatory.txt	Sun Aug  3 13:59:07 1997
@@ -5,8 +5,8 @@
 			   15 April 1996
 
 
-What is  mandatory locking?
----------------------------
+1. What is  mandatory locking?
+------------------------------
 
 Mandatory locking is kernel enforced file locking, as opposed to the more usual
 cooperative file locking used to guarantee sequential access to files among
@@ -44,8 +44,8 @@
 borrowing the fcntl() locking scheme from System V. The mandatory locking
 scheme is defined by the System V Interface Definition (SVID) Version 3.
 
-Marking a file for mandatory locking
-------------------------------------
+2. Marking a file for mandatory locking
+---------------------------------------
 
 A file is marked as a candidate for mandatory by setting the group-id bit in
 its file mode but removing the group-execute bit. This is an otherwise
@@ -58,8 +58,8 @@
 refrain from clearing this bit. Similarly the kernel has been modified not
 to run mandatory lock candidates with setgid privileges.
 
-Available implementations
--------------------------
+3. Available implementations
+----------------------------
 
 I have considered the implementations of mandatory locking available with
 SunOS 4.1.x, Solaris 2.x and HP-UX 9.x.
@@ -93,8 +93,8 @@
 below are just as valid as any others, so long as the main points seem to
 agree. 
 
-Semantics
----------
+4. Semantics
+------------
 
 1. Mandatory locks can only be applied via the fcntl()/lockf() locking
    interface - in other words the System V/POSIX interface. BSD style
@@ -124,8 +124,8 @@
    that has any mandatory locks in effect will be rejected with the error status
    EAGAIN.
 
-Which system calls are affected?
---------------------------------
+5. Which system calls are affected?
+-----------------------------------
 
 Those which modify a file's contents, not just the inode. That gives read(),
 write(), readv(), writev(), open(), creat(), mmap(), truncate() and
@@ -142,8 +142,8 @@
 checking in my eagerness to get this code out the door. Please let me know, or
 better still fix the system calls yourself and submit a patch to me or Linus.
 
-Warning!
---------
+6. Warning!
+-----------
 
 Not even root can override a mandatory lock, so runaway process can wreak
 havoc if they lock crucial files. The way around it is to change the file
diff -u --recursive --new-file v2.0.30/linux/Documentation/memory-tuning.txt linux/Documentation/memory-tuning.txt
--- v2.0.30/linux/Documentation/memory-tuning.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/memory-tuning.txt	Mon Aug 18 20:16:47 1997
@@ -0,0 +1,48 @@
+There are several files in /proc/sys/vm you can use to tune the
+memory system with.
+
+You inspect them with 'cat', and set them with 'echo'. For example,
+/proc/sys/vm/freepages:
+
+'# cat /proc/sys/vm/freepages' may yield:
+64      96      128
+
+These three numbers are: min_free_pages, free_pages_low and 
+free_pages_high.
+
+You can adjust these with a command such as:
+
+# echo "128 256 512" > /proc/sys/vm/freepages
+
+Free memory never goes down below min_free_pages except for atomic
+allocation.  Background swapping is started if the number of free
+pages falls below free_pages_high, and intensive swapping is started
+below free_pages_low.  A "page" is 4 kB.
+
+The values selected as boot defaults are the following:  For a
+machine with n>=8 Megabytes of memory, set min_free_pages = n*2,
+free_pages_low = n*3 and free_pages_high = n*4.  Machines with less
+than 8 Megabytes or less as if they had 8 Megabytes.
+
+If "out of memory" errors sometimes occur, or if your machine does lots
+of networking, increasing min_free_pages to 64 or more may be a good
+idea.
+
+free_pages_low should probably be about double of min_free_pages.
+
+After a period of inactivity, the difference between free_pages_high and
+free_pages low is immediately available for any program you want to
+start up, without any need to swap out anything else.  If your memory
+is large enough (e.g. > 16 Meg), keeping 2 or 3 megabytes of memory
+ready for this purpose is probably a good idea.
+
+I've found that
+
+# echo "128 256 1024" > /proc/sys/vm/freepages
+
+gives good performance for a 32 Meg system used as a small server and
+personal workstation.
+
+The other three files in /proc/sys/vm are undocumented, as yet.
+
+Thomas Koenig, ig25@rz.uni-karlsruhe.de
diff -u --recursive --new-file v2.0.30/linux/Documentation/networking/alias.txt linux/Documentation/networking/alias.txt
--- v2.0.30/linux/Documentation/networking/alias.txt	Wed Jan  3 10:36:23 1996
+++ linux/Documentation/networking/alias.txt	Tue Aug 12 11:17:41 1997
@@ -1,9 +1,11 @@
-NET_ALIAS device aliasing v0.4x
+NET_ALIAS device aliasing v0.5x
 ===============================
   The main step taken in versions 0.40+ is the implementation of a
   device aliasing mechanism that creates *actual* devices.
   This development includes NET_ALIAS (generic aliasing) plus IP_ALIAS
   (specific IP) support.
+  From version 0.50, dynamic configuration of max alias per device and
+  tx/rx stats for aliases added.
 
 Features
 --------
@@ -13,6 +15,10 @@
 o  hashed alias address lookup
 o  net_alias_type objs registration/unreg., module-ables.
 o  /proc/net/aliases & /proc/net/alias_types entries
+o  /proc/sys/net/core/net_alias_max entry (affects hash table size
+   also)
+o  tx/rx stats   
+
 
 o IP alias implementation: static or runtime module.
 
@@ -23,10 +29,14 @@
      # cat /proc/net/alias*
 
   For IP aliasing you must have IP_ALIAS support included by
-  static linking ('y' to 2nd question above), or runtime module
-  insertion ('m' to 2nd q. above):
-     # insmod /usr/src/linux/modules/ip_alias.o (1.3.xx)
-     # insmod /usr/src/ip_alias/ip_alias.o      (1.2.xx) see above.
+  static linking ('y' to CONFIG_IP_ALIAS? question), or runtime module
+  insertion ('m'):
+     # insmod /usr/src/linux/modules/ip_alias.o (2.0.xx) or
+     # modprobe ip_alias.o
+     
+     Also, dynamic loading is supported (kerneld).
+       You should have the following line in /etc/conf.modules:
+       alias net_alias-2 ip_alias
 
 o Alias creation.
   Alias creation is done by 'magic' iface naming: eg. to create a
@@ -49,11 +59,11 @@
   alias device is closed before deletion, so all network stuff that
   points to it (routes, arp entries, ...) will be released.
   		   		   
-Alias (re-)configuring
+o Alias (re-)configuring
   Aliases *are* devices, so you configure and refer to them as usual (ifconfig,
   route, etc).
   
-o Procfs entries
+o PROCfs entries
   2 entries are added to help fetching alias runtime configuration:
   a) /proc/net/alias_types
      Will show you alias_types registered (ie. address families that
@@ -70,7 +80,50 @@
      # cat /proc/net/aliases
      device           family address                                
      eth0:0           2      200.1.1.1
-
+     
+o PROCfs dynamic configuration
+  You can now change the max aliases per device limit via
+  /proc/sys/net/core/net_alias_max entry
+     # cat /proc/sys/net/core/net_alias_max
+     256
+     # echo 1000 > /proc/sys/net/core/net_alias_max
+     # cat /proc/sys/net/core/net_alias_max
+     1000
+     # _
+          
+  With this funcionality you can disable net_alias creation from now on
+     # echo 0 > /proc/sys/net/core/net_alias_max
+
+  The new aliasing limit is considered (grabbed) when creating the
+  FIRST alias for the main device.
+  Eg:
+     # echo 10 > /proc/sys/net/core/net_alias_max
+     # ifconfig eth0:0 xx.xx.xx.xx  (first alias creation for eth0,
+                                     eth0 will 'remember' max==10)
+     # echo 1000 > /proc/sys/net/core/net_alias_max
+     # ifconfig eth0:999 xx.xx.xx.xx
+     SIOCIFSADDR: No such device
+  Of course these semantics can be changed, please let me know.
+  
+  Configuration changes get logged as usual (klogd -> /var/log/messages)
+  
+o Alias devices rx/tx stats
+  Fake rx/tx stats are accounted:
+  - TX
+    When the packet is ``switched'' from logical alias device to
+    physical device, tx counter gets incr.
+  - RX
+    When an incoming packet's address equals alias device's addr it
+    gets ``switched'' from physical to logical device, rx counter gets
+    incr.
+    
+  Please NOTE that for ``same'' network alias devices you usually have
+  one net-route through physical device (eg. eth0), so output pkts
+  will NOT pass down via alias device (so, no tx++ will occur).
+  
+  Also NOTE that currently ifconfig does not handle the ``:'' of alias devices
+  names, a little patch (attached) solves the problem.
+  
 Relationship with main device
 -----------------------------
   - On main device closing, all aliases will be closed and freed.
@@ -78,12 +131,12 @@
     main device (aliases get 'stacked' after main_dev), eg:
       lo->eth0->eth0:0->eth0:2->eth1->0
     If eth0 is unregistered, all it aliases will also be:
-      lo->eth1->0     
+      lo->eth1->0
 
 Contact
 -------
-Please finger or e-mail me:
-   Juan Jose Ciarlante <jjciarla@raiz.uncu.edu.ar>
+Please e-mail me:
+   Juan Jose Ciarlante <irriga@impsat1.com.ar> or <jjciarla@raiz.uncu.edu.ar>
    
 		   		
 ; local variables:
diff -u --recursive --new-file v2.0.30/linux/Documentation/networking/ipx.txt linux/Documentation/networking/ipx.txt
--- v2.0.30/linux/Documentation/networking/ipx.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/networking/ipx.txt	Tue Aug 12 11:17:41 1997
@@ -0,0 +1,19 @@
+The IPX support in the Linux kernel has two modes of operation:
+With and without the full internal IPX network. For all normal
+operations, you do not need the full internal IPX network.
+
+The full internal IPX network enables you to allocate sockets on
+different virtual nodes of the internal network. This is done by
+evaluating the field sipx_node of the socket address given to the bind
+call. So applications should always initialize the node field to 0
+when binding a socket on the primary network. In this case the socket
+is assigned the default node that has been given to the kernel when
+the internal network was created.
+By enabling the full internal IPX network the cross-forwarding of
+packets targeted at 'special' sockets to sockets listening on the
+primary network is disabled. This might break existing applications,
+especially RIP/SAP daemons. A RIP/SAP daemon that works well with
+the full internal net can be found on ftp.gwdg.de:/pub/linux/misc/ncpfs.
+
+If you want the full internal network, please uncomment the correspondig
+#define in line 19 of include/net/ipx.h
diff -u --recursive --new-file v2.0.30/linux/Documentation/networking/so_bindtodevice.txt linux/Documentation/networking/so_bindtodevice.txt
--- v2.0.30/linux/Documentation/networking/so_bindtodevice.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/networking/so_bindtodevice.txt	Tue Aug 12 11:17:41 1997
@@ -0,0 +1,69 @@
+SO_BINDTODEVICE socket option for Linux 2.0.30+
+by Elliot Poger (elliot@poger.com)
+of Stanford's MosquitoNet project (http://mosquitonet.stanford.edu)
+
+Using the SO_BINDTODEVICE socket option allows your user-level Berkeley
+sockets code to explicitly select which network interface is used for
+both input and output on a per-socket basis.  I originally wrote it to 
+allow the Internet Software Consortium DHCP server 
+(http://www.fugue.com/dhcp/) to run on Linux machines with multiple 
+interfaces.  It has been tested with UDP and TCP sockets.
+
+Usage is as follows:
+
+
+        int skfd;
+        struct ifreq interface;
+
+        skfd = socket(AF_INET, SOCK_DGRAM, 0);
+        strncpy(interface.ifr_ifrn.ifrn_name, "eth1", IFNAMSIZ);
+        if (setsockopt(skfd, SOL_SOCKET, SO_BINDTODEVICE,
+                       (char *)&interface, sizeof(interface)) < 0) {
+                perror("sendpacket: setting SO_BINDTODEVICE");
+                exit(1);
+        }
+
+
+Once the BINDTODEVICE socket option has been set for a socket, as above,
+any data sent over this socket is guaranteed to go out of the "eth1" 
+interface, and any data received through the socket is guaranteed to 
+have arrived on eth1.  If you want to send and receive over multiple
+interfaces, keeping them separate, you can open several sockets and bind
+each one to a different interface with SO_BINDTODEVICE.  (You _can_ call
+BINDTODEVICE more than once for a socket to change the interface it's 
+bound to, but results may be unpredictable because of caching effects
+in the kernel...)
+
+Note that the routing table is still consulted when packets are transmitted.
+Basically, routing proceeds as usual, except that any routes which go
+through a network interface other than the one specified in the BINDTODEVICE
+call are ignored.  If you attempt to send a packet to a certain IP address 
+through an interface which provides no route to that IP address, you'll get
+a "network unreachable" error.  Here is an example of a routing table which
+will allow you to send packets to any IP address through either eth0 or
+eth1:
+
+Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
+171.64.69.0     0.0.0.0         255.255.255.192 U     0      0       37 eth0
+171.64.69.192   0.0.0.0         255.255.255.192 U     0      0      677 eth1
+127.0.0.0       0.0.0.0         255.0.0.0       U     0      0        4 lo
+0.0.0.0         171.64.69.1     0.0.0.0         UG    0      0       45 eth0
+0.0.0.0         171.64.69.193   0.0.0.0         UG    1      0        5 eth1
+
+Note that there are actually TWO default routes.  The routing table is
+searched from top to bottom, so every time you send out a packet, the first
+(uppermost) matching route which the kernel routing function finds which
+matches the destination IP address is used.  In this case, packets sent to
+the IP address 152.2.128.159 will normally be sent through eth0 and gateway
+171.64.69.1; if the socket is bound to the eth1 device, the packets will be
+sent through eth1 and gateway 171.64.69.193; if the socket is bound to some
+other device, you will get a "network unreachable" error. 
+
+By the way, you can add multiple default routes and set the order of
+preference as follows:
+
+route add default gateway 171.64.69.1
+route add default gateway 171.64.69.193 metric 1
+
+Routes with a higher "metric" are put lower in the table and thus have a 
+lower preference.
diff -u --recursive --new-file v2.0.30/linux/Documentation/networking/z8530drv.txt linux/Documentation/networking/z8530drv.txt
--- v2.0.30/linux/Documentation/networking/z8530drv.txt	Thu Apr 11 23:49:29 1996
+++ linux/Documentation/networking/z8530drv.txt	Tue Aug 12 14:47:03 1997
@@ -1,18 +1,8 @@
 This is a subset of the documentation. To use this driver you MUST have the
-full package from:
-
-Internet:
-=========
-
-ftp.ucsd.edu:/hamradio/packet/tcpip/incoming/z8530drv-2.0.dl1bke.real.tar.gz
-
-[
-  if you can't find it there, try:
-  .../tcpip/linux/z8530drv-2.0.dl1bke.tar.gz
-
-]
-
-and various mirrors (i.e. nic.switch.ch)
+full package "z8530drv-2.4c.dl1bke.tar.gz" from either ftp.pspt.fi, 
+sunsite.unc.edu or db0bm.automation.fh-aachen.de. Do not try to use the
+utilities from z8530drv-utils-3.0 as they will not work with the 2.4 series
+of the driver!
 
 ---------------------------------------------------------------------------
 
@@ -21,7 +11,7 @@
 
    ********************************************************************
 
-        (c) 1993,1995 by Joerg Reuter DL1BKE
+        (c) 1993,1997 by Joerg Reuter DL1BKE
 
         portions (c) 1993 Guido ten Dolle PE1NNZ
 
@@ -787,5 +777,5 @@
 in the Linux standard distribution and their support.
 
 Joerg Reuter	ampr-net: dl1bke@db0pra.ampr.org
-		AX-25   : DL1BKE @ DB0ACH.#NRW.DEU.EU
-		Internet: jreuter@lykos.tng.oche.de
+		WWW     : http://www.rat.de/jr
+		Internet: jreuter@poboxes.com
diff -u --recursive --new-file v2.0.30/linux/Documentation/specialix.txt linux/Documentation/specialix.txt
--- v2.0.30/linux/Documentation/specialix.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/specialix.txt	Tue Aug 12 11:17:41 1997
@@ -0,0 +1,334 @@
+
+      specialix.txt  -- specialix IO8+ multiport serial driver readme.
+
+
+
+      Copyright (C) 1997  Roger Wolff (R.E.Wolff@BitWizard.nl)
+
+      Specialix pays for the development and support of this driver.
+      Please DO contact io8-linux@specialix.co.uk if you require
+      support.
+
+      This driver was developed in the BitWizard linux device
+      driver service. If you require a linux device driver for your
+      product, please contact devices@BitWizard.nl for a quote.
+
+      This code is firmly based on the riscom/8 serial driver,
+      written by Dmitry Gorodchanin. The specialix IO8+ card
+      programming information was obtained from the CL-CD1865 Data
+      Book, and Specialix document number 6200059: IO8+ Hardware
+      Functional Specification.
+
+      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.
+
+
+Intro
+=====
+
+ 
+This file contains some random information, that I like to have online
+instead of in a manual that can get lost. Ever misplace your Linux
+kernel sources?  And the manual of one of the boards in your computer?
+
+
+Adresses and interrupts
+=======================
+
+Addres dip switch settings:
+The dip switch sets bits 2-9 of the IO address. 
+
+       9 8 7 6 5 4 3 2 
+     +-----------------+
+   0 | X   X X X X X X |
+     |                 |    =   IoBase = 0x100 
+   1 |   X             |
+     +-----------------+          ------ RS232 connectors ---->
+         
+         |    |    |
+       edge connector
+         |    |    |
+         V    V    V
+
+Base address 0x100 caused a conflict in one of my computers once.  I
+haven't the foggiest why. My Specialix card is now at 0x180.  My
+other computer runs just fine with the Specialix card at 0x100....
+The card occupies 4 addresses, but actually only two are really used.
+
+The driver now still autoprobes at 0x100, 0x180, 0x250 and 0x260.  If
+that causes trouble for you, please report that. I'll remove
+autoprobing then.
+
+The driver will tell the card what IRQ to use, so you don't have to
+change any jumpers to change the IRQ. Just use a command line
+argument (irq=xx) to the insmod program to set the interrupt.
+
+If your specialix cards are not at the default locations, you can use
+the kernel command line argument "specialix=io0,irq0,io1,irq1...".
+Here "io0" is the io address for the first card, and "irq0" is the
+irq line that the first card should use. And so on. 
+
+Examples. 
+
+You use the driver as a module and have three cards at 0x100, 0x250
+and 0x180. And some way or another you want them detected in that
+order. Moreover irq 12 is taken (e.g. by your PS/2 mouse).
+
+  insmod specialix.o iobase=0x100,0x250,0x180 irq=9,11,15
+
+The same three cards, but now in the kernel would require you to
+add 
+
+   specialix=0x100,9,0x250,11,0x180,15
+
+to the command line. This would become 
+
+   append="specialix=0x100,9,0x250,11,0x180,15" 
+
+in your /etc/lilo.conf file if you use lilo. 
+
+
+Baud rates
+==========
+
+The rev 1.2 and below boards use a CL-CD1864. These chips can only 
+do 64kbit. The rev 1.3 and newer boards use a CL-CD1865. These chips
+are officially capable of 115k2.
+
+The Specialix card uses a 25MHz crystal (in times two mode, which in
+fact is a divided by two mode). This is not enough to reach the rated
+115k2 on all ports at the same time. With this clock rate you can only
+do 37% of this rate. This means that at 115k2 on all ports you are
+going to loose characters (The chip cannot handle that many incoming
+bits at this clock rate.) (Yes, you read that correctly: there is a
+limit to the number of -=bits=- per second that the chip can handle.)
+
+If you near the "limit" you will first start to see a graceful
+degradation in that the chip cannot keep the transmitter busy at all
+times. However with a central clock this slow, you can also get it to
+miss incoming characters.
+
+The specialix card cannot reliably do 115k2. If you use it, you have
+to do "extensive testing" (*) to verify if it actually works.
+
+When "mgetty" communicates with my modem at 115k2 it reports:
+got: +++[0d]ATQ0V1H0[0d][0d][8a]O[cb][0d][8a]
+                            ^^^^ ^^^^ ^^^^ 
+
+The three characters that have the "^^^" under them have suffered a
+bit error in the highest bit. In conclusion: I've tested it, and found
+that it simply DOESN"T work for me. I also suspect that this is also
+caused by the baud rate being just a little bit out of tune. 
+
+
+(*) Cirrus logic CD1864 databook, page 40.
+
+
+Cables for the Specialix IO8+
+=============================
+
+The pinout of the connectors on the IO8+ is:
+
+     pin    short    direction    long name
+            name
+    Pin 1   DCD      input        Data Carrier Detect
+    Pin 2   RXD      input        Receive
+    Pin 3   DTR/RTS  output       Data Terminal Ready/Ready To Send
+    Pin 4   GND      -            Ground
+    Pin 5   TXD      output       Transmit
+    Pin 6   CTS      input        Clear To Send
+        
+    
+             -- 6  5  4  3  2  1 --
+             |                    |
+             |                    |
+             |                    |
+             |                    |
+             +-----          -----+
+                  |__________|
+                      clip
+    
+    Front view of an RJ12 connector. Cable moves "into" the paper.
+    (the plug is ready to plug into your mouth this way...)
+
+    
+    NULL cable. I don't know who is going to use these except for
+    testing purposes, but I tested the cards with this cable. (It 
+    took quite a while to figure out, so I'm not going to delete
+    it. So there! :-)
+    
+    
+    This end goes               This end needs
+    straight into the           some twists in
+    RJ12 plug.                  the wiring.
+       IO8+ RJ12                   IO8+ RJ12
+        1  DCD       white          -
+        -             -             1 DCD
+        2  RXD       black          5 TXD
+        3  DTR/RTS   red            6 CTS
+        4  GND       green          4 GND
+        5  TXD       yellow         2 RXD
+        6  CTS       blue           3 DTR/RTS
+    
+
+    Same NULL cable, but now sorted on the second column.
+ 
+        1  DCD       white          -
+        -             -             1 DCD
+        5  TXD       yellow         2 RXD
+        6  CTS       blue           3 DTR/RTS
+        4  GND       green          4 GND
+        2  RXD       black          5 TXD
+        3  DTR/RTS   red            6 CTS
+    
+    
+    
+    This is a modem cable usable for hardware handshaking:
+        RJ12                        DB25           DB9
+        1   DCD      white          8 DCD          1 DCD
+        2   RXD      black          3 RXD          2 RXD
+        3   DTR/RTS  red            4 RTS          7 RTS
+        4   GND      green          7 GND          5 GND
+        5   TXD      yellow         2 TXD          3 TXD
+        6   CTS      blue           5 CTS          8 CTS
+                            +----   6 DSR          6 DSR
+                            +----  20 DTR          4 DTR
+
+    This is a modem cable usable for software handshaking:
+    It allows you to reset the modem using the DTR ioctls.
+    I (REW) have never tested this, "but xxxxxxxxxxxxx
+    says that it works." If you test this, please
+    tell me and I'll fill in your name on the xxx's.
+
+        RJ12                        DB25           DB9
+        1   DCD      white          8 DCD          1 DCD
+        2   RXD      black          3 RXD          2 RXD
+        3   DTR/RTS  red           20 DTR          4 DTR
+        4   GND      green          7 GND          5 GND
+        5   TXD      yellow         2 TXD          3 TXD
+        6   CTS      blue           5 CTS          8 CTS
+                            +----   6 DSR          6 DSR
+                            +----   4 RTS          7 RTS
+
+   I bought a 6 wire flat cable. It was colored as indicated.
+   Check that yours is the same before you trust me on this.
+   
+ 
+Hardware handshaking issues.
+============================
+
+The driver can be compiled in two different ways. The default
+("Specialix DTR/RTS pin is RTS" is off) the pin behaves as DTR when
+hardware handshaking is off. It behaves as the RTS hardware
+handshaking signal when hardware handshaking is selected.
+
+When you use this, you have to use the appropriate cable. The
+cable will either be compatible with hardware handshaking or with
+software handshaking. So switching on the fly is not really an
+option.
+
+I actually prefer to use the "Specialix DTR/RTS pin is RTS" option.
+This makes the DTR/RTS pin always an RTS pin, and ioctls to
+change DTR are always ignored. I have a cable that is configured
+for this. 
+
+
+Ports and devices
+=================
+
+Port 0 is the one furthest from the ISA connector.
+
+Devices:
+
+You should make the devices as follows:
+
+bash
+cd /dev
+for i in  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 \
+         16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
+do
+  echo -n "$i "
+  mknod /dev/ttyW$i c 75 $i
+  mknod /dev/cuw$i c 76 $i
+done
+echo ""
+
+
+You cannot have more than 4 boards in one computer. The card only
+supports 4 different interrupts. If you really want this, contact me
+about this and I'll give you a few tips (requires soldering iron)....
+
+
+------------------------------------------------------------------------
+
+
+  Fixed bugs and restrictions:
+       - During intialization, interrupts are blindly turned on.
+            Having a shadow variable would cause an extra memory
+            access on every IO instruction. 
+       - The interrupt (on the card) should be disabled when we
+         don't allocate the Linux end of the interrupt. This allows 
+         a different driver/card to use it while all ports are not in
+         use..... (a la standard serial port)
+       == An extra _off variant of the sx_in and sx_out macros are
+          now available. They don't set the interrupt enable bit.
+          These are used during initialization. Normal operation uses
+          the old variant which enables the interrupt line.
+       - RTS/DTR issue needs to be implemented according to 
+         specialix' spec.
+            I kind of like the "determinism" of the current 
+            implementation. Compile time flag?
+       == Ok. Compile time flag! Default is how Specialix likes it.
+       == Now a config time flag! Gets saved in your config file. Neat!
+       - Can you set the IO address from the lilo command line?
+            If you need this, bug me about it, I'll make it. 
+       == Hah! No bugging needed. Fixed! :-)
+       - Cirrus logic hasn't gotten back to me yet why the CD1865 can
+            and the CD1864 can't do 115k2. I suspect that this is
+            because the CD1864 is not rated for 33MHz operation.
+            Therefore the CD1864 versions of the card can't do 115k2 on 
+            all ports just like the CD1865 versions. The driver does
+            not block 115k2 on CD1864 cards. 
+        == I called the Cirrus Logic representative here in Holland.
+           The CD1864 databook is identical to the CD1865 databook, 
+           except for an extra warning at the end. Similar Bit errors
+           have been observed in testing at 115k2 on both an 1865 and
+           a 1864 chip. I see no reason why I would prohibit 115k2 on
+           1864 chips and not do it on 1865 chips. Actually there is
+           reason to prohibit it on BOTH chips. I print a warning.
+           If you use 115k2, you're on your own. 
+       - A spiky CD may send spurious HUPs. Also in CLOCAL???
+         -- A fix for this turned out to be counter productive. 
+            Different fix? Current behaviour is acceptable?
+         -- Maybe the current implementation is correct. If anybody
+            gets bitten by this, please report, and it will get fixed.
+
+         -- Testing revealed that when in CLOCAL, the problem doesn't
+            occur. As warned for in the CD1865 manual, the chip may
+            send modem intr's on a spike. We could filter those out,
+            but that would be a cludge anyway (You'd still risk getting
+            a spurious HUP when two spikes occur.).....
+ 
+
+
+  Bugs & restrictions:
+       - This is a difficult card to autoprobe.
+            You have to WRITE to the address register to even 
+            read-probe a CD186x register. Disable autodetection?
+         -- Specialix: any suggestions?
+       - Arbitrary baud rates are not implemented yet. 
+            If you need this, bug me about it. 
+
+
diff -u --recursive --new-file v2.0.30/linux/MAINTAINERS linux/MAINTAINERS
--- v2.0.30/linux/MAINTAINERS	Tue Apr  8 08:47:45 1997
+++ linux/MAINTAINERS	Fri Sep  5 20:43:58 1997
@@ -110,6 +110,12 @@
 M:	jt@hplb.hpl.hp.com
 S:	Maintained
 
+TOKEN-RING NETWORK DRIVER
+P:      Paul Norton
+M:      pnorton@cts.com
+L:      linux-net@vger.rutgers.edu
+S:      Maintained
+
 APM DRIVER
 P:	Rik Faith & Stephen Rothwell
 M:	faith@cs.unc.edu, Stephen.Rothwell@canb.auug.org.au
@@ -128,6 +134,24 @@
 L:	linux-hams@vger.rutgers.edu
 S:	Maintained
 
+IP MASQUERADING (IPV4)
+P:	Nigel Metheringham
+M:	Nigel.Metheringham@ThePLAnet.net
+L:	masq@indyramp.com
+S:	Maintained
+
+AX.25 DAMA SLAVE
+P:	Joerg Reuter
+M:	jreuter@poboxes.com
+L:	linux-hams@vger.rutgers.edu
+S:	Maintained
+
+Z8530 SCC DRIVER FOR AX.25
+P:	Joerg Reuter
+M:	jreuter@poboxes.com
+L:	linux-hams@vger.rutgers.edu
+S:	Maintained
+
 BUSLOGIC SCSI DRIVER
 P:	Leonard N. Zubkoff
 M:	Leonard N. Zubkoff <lnz@dandelion.com>
@@ -164,6 +188,12 @@
 L:	linux-eata@i-connect.net, linux-scsi@vger.rutgers.edu
 S:	Maintained
 
+FILE LOCKING (flock() and fcntl()/lockf())
+P:	Andy Walker
+M:	andy@lysaker.kvaerner.no
+L:	linux-kernel@vger.rutgers.edu
+S:	Maintained
+
 FRAME RELAY DLCI/FRAD (Sangoma drivers too)
 P:	Mike McLagan
 M:	mike.mclagan@linux.org
@@ -206,6 +236,12 @@
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
+IDE/ATAPI TAPE/FLOPPY DRIVERS
+P:	Gadi Oxman
+M:	Gadi Oxman <gadio@netvision.net.il>
+L:	linux-kernel@vger.rutgers.edu
+S:	Maintained
+
 ISDN SUBSYSTEM
 P:	Fritz Elfert
 M:	fritz@wuemaus.franken.de
@@ -288,7 +324,7 @@
 SVGA HANDLING:
 P:	Martin Mares
 M:	mj@k332.feld.cvut.cz
-L:	linux-kernel@vger.rutgers.edu
+L:	linux-video@atrey.karlin.mff.cuni.cz
 S:	Maintained
 
 VFAT FILESYSTEM:
@@ -310,6 +346,13 @@
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
+SPECIALIX IO8+ MULTIPORT SERIAL CARD DRIVER
+P:	Roger Wolff
+M:	R.E.Wolff@BitWizard.nl
+M:	io8-linux@specialix.co.uk
+L:	linux-kernel@vger.rutgers.edu ?
+S:	Supported
+
 MOUSE AND MISC DEVICES [GENERAL]
 P:	Alessandro Rubini
 M:	rubini@ipvvis.unipv.it
@@ -318,7 +361,7 @@
 
 MENUCONFIG:
 P:	William Roadcap
-M:	roadcapw@cfw.com
+M:	roadcapw@titus.org
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
@@ -361,6 +404,18 @@
 CREDITS FILE
 P:	John A. Martin
 M:	jam@acm.org
+S:	Maintained
+
+KERNEL AUTOMOUNTER (AUTOFS)
+P:	H. Peter Anvin
+M:	hpa@zytor.com
+L:	autofs@linux.kernel.org
+S:	Maintained
+
+DEVICE NUMBER REGISTRY
+P:	H. Peter Anvin
+M:	hpa@zytor.com
+L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
 REST:
diff -u --recursive --new-file v2.0.30/linux/Makefile linux/Makefile
--- v2.0.30/linux/Makefile	Mon Mar 17 14:58:22 1997
+++ linux/Makefile	Mon Aug  4 15:23:02 1997
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 0
-SUBLEVEL = 30
+SUBLEVEL = 31
 
 ARCH = i386
 
@@ -319,7 +319,6 @@
 mrproper: clean
 	rm -f include/linux/autoconf.h include/linux/version.h
 	rm -f drivers/sound/local.h drivers/sound/.defines
-	rm -f drivers/scsi/aic7xxx_asm drivers/scsi/aic7xxx_seq.h
 	rm -f drivers/char/uni_hash.tbl drivers/char/conmakehash
 	rm -f .version .config* config.in config.old
 	rm -f scripts/tkparse scripts/kconfig.tk scripts/kconfig.tmp
diff -u --recursive --new-file v2.0.30/linux/arch/alpha/defconfig linux/arch/alpha/defconfig
--- v2.0.30/linux/arch/alpha/defconfig	Tue Oct 29 17:42:40 1996
+++ linux/arch/alpha/defconfig	Fri Sep  5 20:43:58 1997
@@ -169,6 +169,7 @@
 # ISDN subsystem
 #
 # CONFIG_ISDN is not set
+CONFIG_HISAX_EURO=y
 
 #
 # CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
@@ -179,7 +180,6 @@
 # Filesystems
 #
 # CONFIG_QUOTA is not set
-# CONFIG_LOCK_MANDATORY is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_EXT_FS is not set
 CONFIG_EXT2_FS=y
@@ -196,6 +196,7 @@
 # CONFIG_HPFS_FS is not set
 # CONFIG_SYSV_FS is not set
 # CONFIG_UFS_FS is not set
+# CONFIG_AUTOFS_FS is not set
 
 #
 # Character devices
diff -u --recursive --new-file v2.0.30/linux/arch/alpha/kernel/osf_sys.c linux/arch/alpha/kernel/osf_sys.c
--- v2.0.30/linux/arch/alpha/kernel/osf_sys.c	Tue Aug 20 23:18:07 1996
+++ linux/arch/alpha/kernel/osf_sys.c	Sun Aug  3 10:58:38 1997
@@ -714,29 +714,25 @@
 osf_getsysinfo (unsigned long op, void * buffer, unsigned long nbytes,
 		int * start, void *arg)
 {
-    extern unsigned long rdfpcr (void);
-    unsigned long fpcw;
-
-    switch (op) {
-      case 45:	/* GSI_IEEE_FP_CONTROL */
-	  /* build and return current fp control word: */
-	  fpcw = current->tss.flags & IEEE_TRAP_ENABLE_MASK;
-	  fpcw |= ((rdfpcr() >> 52) << 17) & IEEE_STATUS_MASK;
-	  put_user(fpcw, (unsigned long *) buffer);
-	  return 0;
-
-      case 46:	/* GSI_IEEE_STATE_AT_SIGNAL */
-	  /*
-	   * Not sure anybody will ever use this weird stuff.  These
-	   * ops can be used (under OSF/1) to set the fpcr that should
-	   * be used when a signal handler starts executing.
-	   */
-	  break;
-
-      default:
-	  break;
-    }
-    return -EOPNOTSUPP;
+	switch (op) {
+	case 45:	/* GSI_IEEE_FP_CONTROL */
+		/* Return current sw control & status bits.  */
+		put_user(current->tss.flags & IEEE_SW_MASK,
+			 (unsigned long *)buffer);
+		return 0;
+
+	case 46:	/* GSI_IEEE_STATE_AT_SIGNAL */
+		/*
+		 * Not sure anybody will ever use this weird stuff.  These
+		 * ops can be used (under OSF/1) to set the fpcr that should
+		 * be used when a signal handler starts executing.
+		 */
+		break;
+
+	default:
+		break;
+	}
+	return -EOPNOTSUPP;
 }
 
 
@@ -744,25 +740,43 @@
 osf_setsysinfo (unsigned long op, void * buffer, unsigned long nbytes,
 		int * start, void *arg)
 {
-    unsigned long fpcw;
-
-    switch (op) {
-      case 14:	/* SSI_IEEE_FP_CONTROL */
-	  /* update trap enable bits: */
-	  fpcw = get_user((unsigned long *) buffer);
-	  current->tss.flags &= ~IEEE_TRAP_ENABLE_MASK;
-	  current->tss.flags |= (fpcw & IEEE_TRAP_ENABLE_MASK);
-	  return 0;
-
-      case 15:	/* SSI_IEEE_STATE_AT_SIGNAL */
-      case 16:	/* SSI_IEEE_IGNORE_STATE_AT_SIGNAL */
-	  /*
-	   * Not sure anybody will ever use this weird stuff.  These
-	   * ops can be used (under OSF/1) to set the fpcr that should
-	   * be used when a signal handler starts executing.
-	   */
-      default:
-	  break;
-    }
-    return -EOPNOTSUPP;
+	switch (op) {
+	case 14: {	/* SSI_IEEE_FP_CONTROL */
+		unsigned long sw, fpcw;
+
+		/*
+		 * Alpha Architecture Handbook 4.7.7.3:
+		 * To be fully IEEE compiant, we must track the current IEEE
+		 * exception state in software, because spurrious bits can be
+		 * set in the trap shadow of a software-complete insn.
+		 */
+
+		/* Update software trap enable bits.  */
+		sw = get_user((unsigned long *) buffer) & IEEE_SW_MASK;
+		current->tss.flags &= ~IEEE_SW_MASK;
+		current->tss.flags |= sw & IEEE_SW_MASK;
+
+		/* Update the real fpcr.  For exceptions that are disabled,
+		   but that we have seen, turn off exceptions in h/w.
+		   Otherwise leave them enabled so that we can update our
+		   software status mask.  */
+		fpcw = rdfpcr() & (~FPCR_MASK | FPCR_DYN_MASK);
+		fpcw |= ieee_sw_to_fpcr(sw | ((~sw & IEEE_STATUS_MASK) >> 16));
+		wrfpcr(fpcw);
+		return 0;
+	}
+
+	case 15:	/* SSI_IEEE_STATE_AT_SIGNAL */
+	case 16:	/* SSI_IEEE_IGNORE_STATE_AT_SIGNAL */
+		/*
+		 * Not sure anybody will ever use this weird stuff.  These
+		 * ops can be used (under OSF/1) to set the fpcr that should
+		 * be used when a signal handler starts executing.
+		 */
+		break;
+
+	default:
+		break;
+	}
+	return -EOPNOTSUPP;
 }
diff -u --recursive --new-file v2.0.30/linux/arch/alpha/kernel/ptrace.c linux/arch/alpha/kernel/ptrace.c
--- v2.0.30/linux/arch/alpha/kernel/ptrace.c	Wed Sep 11 07:57:13 1996
+++ linux/arch/alpha/kernel/ptrace.c	Mon Aug  4 15:41:59 1997
@@ -641,7 +641,7 @@
 		return;
 	current->exit_code = SIGTRAP;
 	current->state = TASK_STOPPED;
-	notify_parent(current);
+	notify_parent(current, SIGCHLD);
 	schedule();
 	/*
 	 * this isn't the same as continuing with a signal, but it will do
diff -u --recursive --new-file v2.0.30/linux/arch/alpha/kernel/signal.c linux/arch/alpha/kernel/signal.c
--- v2.0.30/linux/arch/alpha/kernel/signal.c	Tue Aug 20 23:18:07 1996
+++ linux/arch/alpha/kernel/signal.c	Mon Aug  4 15:41:48 1997
@@ -291,7 +291,7 @@
 		if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
 			current->exit_code = signr;
 			current->state = TASK_STOPPED;
-			notify_parent(current);
+			notify_parent(current, SIGCHLD);
 			schedule();
 			single_stepping |= ptrace_cancel_bpt(current);
 			if (!(signr = current->exit_code))
@@ -330,7 +330,7 @@
 				current->exit_code = signr;
 				if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & 
 						SA_NOCLDSTOP))
-					notify_parent(current);
+					notify_parent(current, SIGCHLD);
 				schedule();
 				single_stepping |= ptrace_cancel_bpt(current);
 				continue;
diff -u --recursive --new-file v2.0.30/linux/arch/alpha/kernel/traps.c linux/arch/alpha/kernel/traps.c
--- v2.0.30/linux/arch/alpha/kernel/traps.c	Sun Feb  2 05:00:20 1997
+++ linux/arch/alpha/kernel/traps.c	Wed Aug 13 12:54:02 1997
@@ -420,7 +420,7 @@
 			  unsigned long a3, unsigned long a4, unsigned long a5,
 			  struct pt_regs regs)
 {
-	if (regs.r0 != 112)
+	if (regs.r0 != 112 && regs.r0 < 300)
 		printk("<sc %ld(%lx,%lx,%lx)>", regs.r0, a0, a1, a2);
 	return -1;
 }
diff -u --recursive --new-file v2.0.30/linux/arch/alpha/math-emu/fp-emul.c linux/arch/alpha/math-emu/fp-emul.c
--- v2.0.30/linux/arch/alpha/math-emu/fp-emul.c	Thu Apr 11 23:49:30 1996
+++ linux/arch/alpha/math-emu/fp-emul.c	Sun Aug  3 10:58:38 1997
@@ -298,19 +298,29 @@
 	 *
 	 *	- Set the appropriate bits in the FPCR
 	 *	- If the specified exception is enabled in the FPCR,
-	 *	  return.  The caller (mxr_signal_handler) will dispatch
+	 *	  return.  The caller (entArith) will dispatch
 	 *	  the appropriate signal to the translated program.
+	 *
+	 * In addition, properly track the exception state in software
+	 * as described in Alpha Architecture Handbook 4.7.7.3.
 	 */
 	if (res) {
-		fpcr |= FPCR_SUM | res;
-		wrfpcr(fpcr);
-		if (((res & FPCR_INV) && (fpcw & IEEE_TRAP_ENABLE_INV)) ||
-		    ((res & FPCR_DZE) && (fpcw & IEEE_TRAP_ENABLE_DZE)) ||
-		    ((res & FPCR_OVF) && (fpcw & IEEE_TRAP_ENABLE_OVF)) ||
-		    ((res & FPCR_UNF) && (fpcw & IEEE_TRAP_ENABLE_UNF)) ||
-		    ((res & FPCR_INE) && (fpcw & IEEE_TRAP_ENABLE_INE)))
+		/* record exceptions in software control word.  */
+		fpcw |= res >> 35;
+		current->tss.flags = fpcw;
+
+		/* update hardware control register, disabling hardware
+		   exceptions for disabled software exceptions for which
+		   we have a status.  (no, really.)  */
+		fpcr &= (~FPCR_MASK | FPCR_DYN_MASK);
+		fpcr |= ieee_sw_to_fpcr(fpcw | (~fpcw & IEEE_STATUS_MASK)>>16);
+                wrfpcr(fpcr);
+
+		/* Do we generate a signal?  */
+		if (res >> 51 & fpcw & IEEE_TRAP_ENABLE_MASK)
 			return 0;
 	}
+
 	/*
 	 * Whoo-kay... we got this far, and we're not generating a signal
 	 * to the translated program.  All that remains is to write the
diff -u --recursive --new-file v2.0.30/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.0.30/linux/arch/i386/defconfig	Tue Oct 29 17:42:40 1996
+++ linux/arch/i386/defconfig	Fri Sep  5 20:43:58 1997
@@ -42,11 +42,13 @@
 # CONFIG_BLK_DEV_HD_IDE is not set
 CONFIG_BLK_DEV_IDECD=y
 # CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
 # CONFIG_BLK_DEV_IDE_PCMCIA is not set
 CONFIG_BLK_DEV_CMD640=y
 # CONFIG_BLK_DEV_CMD640_ENHANCED is not set
 CONFIG_BLK_DEV_RZ1000=y
-# CONFIG_BLK_DEV_TRITON is not set
+CONFIG_BLK_DEV_TRITON=y
 # CONFIG_IDE_CHIPSETS is not set
 
 #
@@ -66,7 +68,10 @@
 CONFIG_INET=y
 # CONFIG_IP_FORWARD is not set
 # CONFIG_IP_MULTICAST is not set
+# CONFIG_SYN_COOKIES is not set
 # CONFIG_IP_ACCT is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
 
 #
 # (it is safe to leave these untouched)
@@ -129,7 +134,6 @@
 # Filesystems
 #
 # CONFIG_QUOTA is not set
-# CONFIG_LOCK_MANDATORY is not set
 CONFIG_MINIX_FS=y
 # CONFIG_EXT_FS is not set
 CONFIG_EXT2_FS=y
@@ -146,6 +150,7 @@
 # CONFIG_HPFS_FS is not set
 # CONFIG_SYSV_FS is not set
 # CONFIG_UFS_FS is not set
+# CONFIG_AUTOFS_FS is not set
 
 #
 # Character devices
@@ -156,6 +161,7 @@
 # CONFIG_STALDRV is not set
 # CONFIG_RISCOM8 is not set
 # CONFIG_PRINTER is not set
+# CONFIG_SPECIALIX is not set
 # CONFIG_MOUSE is not set
 # CONFIG_UMISC is not set
 # CONFIG_QIC02_TAPE is not set
diff -u --recursive --new-file v2.0.30/linux/arch/i386/kernel/bios32.c linux/arch/i386/kernel/bios32.c
--- v2.0.30/linux/arch/i386/kernel/bios32.c	Tue Apr  8 08:47:45 1997
+++ linux/arch/i386/kernel/bios32.c	Mon Aug  4 08:38:56 1997
@@ -1,6 +1,8 @@
 /*
  * bios32.c - BIOS32, PCI BIOS functions.
  *
+ * $Id: bios32.c,v 1.3.2.4 1997/08/02 22:24:23 mj Exp $
+ *
  * Sponsored by
  *	iX Multiuser Multitasking Magazine
  *	Hannover, Germany
@@ -52,6 +54,13 @@
  * Feb 3, 1997  : Set internal functions to static, save/restore flags
  *	avoid dead locks reading broken PCI BIOS, werner@suse.de 
  *
+ * Apr 26, 1997 : Fixed case when there is BIOS32, but not PCI BIOS
+ *	(mj@atrey.karlin.mff.cuni.cz)
+ *
+ * May 7,  1997 : Added some missing cli()'s. [mj]
+ * 
+ * Jun 20, 1997 : Corrected problems in "conf1" type accesses.
+ *      (paubert@iram.es)
  */
 
 #include <linux/config.h>
@@ -156,7 +165,7 @@
 	unsigned long entry;		/* %edx */
 	unsigned long flags;
 
-	save_flags(flags);
+	save_flags(flags); cli();
 	__asm__("lcall (%%edi)"
 		: "=a" (return_code),
 		  "=b" (address),
@@ -171,10 +180,10 @@
 		case 0:
 			return address + entry;
 		case 0x80:	/* Not present */
-			printk("bios32_service(%ld) : not present\n", service);
+			printk("bios32_service(0x%lx) : not present\n", service);
 			return 0;
 		default: /* Shouldn't happen */
-			printk("bios32_service(%ld) : returned 0x%x, mail drew@colorado.edu\n",
+			printk("bios32_service(0x%lx) : returned 0x%x, mail drew@colorado.edu\n",
 				service, return_code);
 			return 0;
 	}
@@ -187,7 +196,7 @@
 } pci_indirect = { 0, KERNEL_CS };
 
 
-extern unsigned long check_pcibios(unsigned long memory_start, unsigned long memory_end)
+static int check_pcibios(void)
 {
 	unsigned long signature;
 	unsigned char present_status;
@@ -199,7 +208,7 @@
 	if ((pcibios_entry = bios32_service(PCI_SERVICE))) {
 		pci_indirect.address = pcibios_entry;
 
-		save_flags(flags);
+		save_flags(flags); cli();
 		__asm__("lcall (%%edi)\n\t"
 			"jc 1f\n\t"
 			"xor %%ah, %%ah\n"
@@ -230,9 +239,10 @@
 		if (pcibios_entry) {
 			printk ("pcibios_init : PCI BIOS revision %x.%02x entry at 0x%lx\n",
 				major_revision, minor_revision, pcibios_entry);
+			return 1;
 		}
 	}
-	return memory_start;
+	return 0;
 }
 
 
@@ -243,7 +253,7 @@
 	unsigned long ret;
 	unsigned long flags;
 
-	save_flags(flags);
+	save_flags(flags); cli();
 	__asm__ ("lcall (%%edi)\n\t"
 		"jc 1f\n\t"
 		"xor %%ah, %%ah\n"
@@ -268,7 +278,7 @@
 	unsigned short ret;
 	unsigned long flags;
 
-	save_flags(flags);
+	save_flags(flags); cli();
 	__asm__("lcall (%%edi)\n\t"
 		"jc 1f\n\t"
 		"xor %%ah, %%ah\n"
@@ -293,7 +303,7 @@
 	unsigned long bx = (bus << 8) | device_fn;
 	unsigned long flags;
 
-	save_flags(flags);
+	save_flags(flags); cli();
 	__asm__("lcall (%%esi)\n\t"
 		"jc 1f\n\t"
 		"xor %%ah, %%ah\n"
@@ -315,7 +325,7 @@
 	unsigned long bx = (bus << 8) | device_fn;
 	unsigned long flags;
 
-	save_flags(flags);
+	save_flags(flags); cli();
 	__asm__("lcall (%%esi)\n\t"
 		"jc 1f\n\t"
 		"xor %%ah, %%ah\n"
@@ -337,7 +347,7 @@
 	unsigned long bx = (bus << 8) | device_fn;
 	unsigned long flags;
 
-	save_flags(flags);
+	save_flags(flags); cli();
 	__asm__("lcall (%%esi)\n\t"
 		"jc 1f\n\t"
 		"xor %%ah, %%ah\n"
@@ -359,7 +369,7 @@
 	unsigned long bx = (bus << 8) | device_fn;
 	unsigned long flags;
 
-	save_flags(flags);
+	save_flags(flags); cli();
 	__asm__("lcall (%%esi)\n\t"
 		"jc 1f\n\t"
 		"xor %%ah, %%ah\n"
@@ -381,7 +391,7 @@
 	unsigned long bx = (bus << 8) | device_fn;
 	unsigned long flags;
 
-	save_flags(flags);
+	save_flags(flags); cli();
 	__asm__("lcall (%%esi)\n\t"
 		"jc 1f\n\t"
 		"xor %%ah, %%ah\n"
@@ -403,7 +413,7 @@
 	unsigned long bx = (bus << 8) | device_fn;
 	unsigned long flags;
 
-	save_flags(flags);
+	save_flags(flags); cli();
 	__asm__("lcall (%%esi)\n\t"
 		"jc 1f\n\t"
 		"xor %%ah, %%ah\n"
@@ -444,21 +454,17 @@
 {
     unsigned int curr = 0;
     struct pci_dev *dev;
-    unsigned long flags;
 
-    save_flags(flags);
     for (dev = pci_devices; dev; dev = dev->next) {
 	if (dev->vendor == vendor && dev->device == device_id) {
 	    if (curr == index) {
 		*devfn = dev->devfn;
 		*bus = dev->bus->number;
-		restore_flags(flags);
 		return PCIBIOS_SUCCESSFUL;
 	    }
 	    ++curr;
 	}
     }
-    restore_flags(flags);
     return PCIBIOS_DEVICE_NOT_FOUND;
 }
 
@@ -472,21 +478,17 @@
 {
     unsigned int curr = 0;
     struct pci_dev *dev;
-    unsigned long flags;
 
-    save_flags(flags);
     for (dev = pci_devices; dev; dev = dev->next) {
 	if (dev->class == class_code) {
 	    if (curr == index) {
 		*devfn = dev->devfn;
 		*bus = dev->bus->number;
-		restore_flags(flags);
 		return PCIBIOS_SUCCESSFUL;
 	    }
 	    ++curr;
 	}
     }
-    restore_flags(flags);
     return PCIBIOS_DEVICE_NOT_FOUND;
 }
 
@@ -500,18 +502,9 @@
 {
     unsigned long flags;
 
-    save_flags(flags);
+    save_flags(flags); cli();
     outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
-    switch (where & 3) {
-	case 0: *value = inb(0xCFC);
-		break;
-	case 1: *value = inb(0xCFD);
-		break;
-	case 2: *value = inb(0xCFE);
-		break;
-	case 3: *value = inb(0xCFF);
-		break;
-    }
+    *value = inb(0xCFC + (where&3));
     restore_flags(flags);
     return PCIBIOS_SUCCESSFUL;
 }
@@ -521,12 +514,10 @@
 {
     unsigned long flags;
 
-    save_flags(flags);
+    if (where&1) return PCIBIOS_BAD_REGISTER_NUMBER;
+    save_flags(flags); cli();
     outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);    
-    if (where & 2)
-    	*value = inw(0xCFE);
-    else
-    	*value = inw(0xCFC);
+    *value = inw(0xCFC + (where&2));
     restore_flags(flags);
     return PCIBIOS_SUCCESSFUL;    
 }
@@ -536,7 +527,8 @@
 {
     unsigned long flags;
 
-    save_flags(flags);
+    if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER;
+    save_flags(flags); cli();
     outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
     *value = inl(0xCFC);
     restore_flags(flags);
@@ -548,9 +540,9 @@
 {
     unsigned long flags;
 
-    save_flags(flags);
+    save_flags(flags); cli();
     outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);    
-    outb(value, 0xCFC);
+    outb(value, 0xCFC + (where&3));
     restore_flags(flags);
     return PCIBIOS_SUCCESSFUL;
 }
@@ -560,9 +552,10 @@
 {
     unsigned long flags;
 
-    save_flags(flags);
+    if (where&1) return PCIBIOS_BAD_REGISTER_NUMBER;
+    save_flags(flags); cli();
     outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
-    outw(value, 0xCFC);
+    outw(value, 0xCFC + (where&2));
     restore_flags(flags);
     return PCIBIOS_SUCCESSFUL;
 }
@@ -572,7 +565,8 @@
 {
     unsigned long flags;
 
-    save_flags(flags);
+    if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER;
+    save_flags(flags); cli();
     outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
     outl(value, 0xCFC);
     restore_flags(flags);
@@ -608,7 +602,7 @@
 
     if (device_fn & 0x80)
 	return PCIBIOS_DEVICE_NOT_FOUND;
-    save_flags(flags);
+    save_flags(flags); cli();
     outb (FUNC(device_fn), 0xCF8);
     outb (bus, 0xCFA);
     *value = inb(IOADDR(device_fn,where));
@@ -624,7 +618,7 @@
 
     if (device_fn & 0x80)
 	return PCIBIOS_DEVICE_NOT_FOUND;
-    save_flags(flags);
+    save_flags(flags); cli();
     outb (FUNC(device_fn), 0xCF8);
     outb (bus, 0xCFA);
     *value = inw(IOADDR(device_fn,where));
@@ -640,7 +634,7 @@
 
     if (device_fn & 0x80)
 	return PCIBIOS_DEVICE_NOT_FOUND;
-    save_flags(flags);
+    save_flags(flags); cli();
     outb (FUNC(device_fn), 0xCF8);
     outb (bus, 0xCFA);
     *value = inl (IOADDR(device_fn,where));    
@@ -654,7 +648,7 @@
 {
     unsigned long flags;
 
-    save_flags(flags);
+    save_flags(flags); cli();
     outb (FUNC(device_fn), 0xCF8);
     outb (bus, 0xCFA);
     outb (value, IOADDR(device_fn,where));
@@ -668,7 +662,7 @@
 {
     unsigned long flags;
 
-    save_flags(flags);
+    save_flags(flags); cli();
     outb (FUNC(device_fn), 0xCF8);
     outb (bus, 0xCFA);
     outw (value, IOADDR(device_fn,where));
@@ -682,7 +676,7 @@
 {
     unsigned long flags;
 
-    save_flags(flags);
+    save_flags(flags); cli();
     outb (FUNC(device_fn), 0xCF8);
     outb (bus, 0xCFA);
     outl (value, IOADDR(device_fn,where));    
@@ -714,7 +708,7 @@
     unsigned int tmp;
     unsigned long flags;
 
-    save_flags(flags);
+    save_flags(flags); cli();
 
     /*
      * check if configuration type 1 works
@@ -736,7 +730,7 @@
     outb (0x00, 0xCFB);
     outb (0x00, 0xCF8);
     outb (0x00, 0xCFA);
-    if (inb (0xCF8) == 0x00 && inb (0xCFC) == 0x00) {
+    if (inb (0xCF8) == 0x00 && inb (0xCFB) == 0x00) {
 	restore_flags(flags);
 	printk("pcibios_init: Using configuration type 2\n");
 	return &pci_direct_conf2;
@@ -883,7 +877,9 @@
 	 *
 	 */
 
-	for (check = (union bios32 *) 0xe0000; check <= (union bios32 *) 0xffff0; ++check) {
+	for (check = (union bios32 *) 0xe0000;
+	     check <= (union bios32 *) 0xffff0;
+	     ++check) {
 		if (check->fields.signature != BIOS32_SIGNATURE)
 			continue;
 		length = check->fields.length * 16;
@@ -908,13 +904,11 @@
 				bios32_entry = check->fields.entry;
 				printk ("pcibios_init : BIOS32 Service Directory entry at 0x%lx\n", bios32_entry);
 				bios32_indirect.address = bios32_entry;
-				access_pci = &pci_bios_access;
 			}
 		}
 	}
-	if (bios32_entry) {
-		memory_start = check_pcibios (memory_start, memory_end);
-	}
+	if (bios32_entry && check_pcibios())
+ 		access_pci = &pci_bios_access;
 #endif
 	return memory_start;
 }
diff -u --recursive --new-file v2.0.30/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S
--- v2.0.30/linux/arch/i386/kernel/entry.S	Wed Dec 11 06:41:01 1996
+++ linux/arch/i386/kernel/entry.S	Mon Aug 11 13:42:11 1997
@@ -132,7 +132,10 @@
 	decl SYMBOL_NAME(syscall_count); \
 	decl SYMBOL_NAME(kernel_counter); \
 	jnz 1f;	\
-	movb $(NO_PROC_ID), SYMBOL_NAME(active_kernel_processor); \
+	movb SYMBOL_NAME(saved_active_kernel_processor), %al; \
+	movb %al, SYMBOL_NAME(active_kernel_processor); \
+	cmpb $(NO_PROC_ID), %al; \
+	jnz 1f; \
 	lock; \
 	btrl $0, SYMBOL_NAME(kernel_flag); \
 1:	popfl;
@@ -163,9 +166,13 @@
 
 #define	ENTER_KERNEL \
 	pushl %eax; \
+	pushl %ebx; \
+	pushl %ecx; \
 	pushl %edx; \
 	pushfl; \
 	cli; \
+	movl $6000, %ebx; \
+	movl SYMBOL_NAME(smp_loops_per_tick), %ecx; \
 	GET_PROCESSOR_ID \
 	btsl $ SMP_FROM_SYSCALL,SYMBOL_NAME(smp_proc_in_lock)(,%eax,4); \
 	SMP_PROF_A \
@@ -182,7 +189,18 @@
 	jnc 5f; \
 	movl %cr3,%edx; \
 	movl %edx,%cr3; \
-5:	btl $0, SYMBOL_NAME(kernel_flag); \
+5:	sti; \
+	decl %ecx; \
+	cli; \
+	jne 7f; \
+	decl %ebx; \
+	jne 6f; \
+	call SYMBOL_NAME(non_irq_deadlock_detected); \
+6:	movl SYMBOL_NAME(smp_loops_per_tick), %ecx; \
+	cmpb SYMBOL_NAME(boot_cpu_id), %al; \
+	jne 7f; \
+	incl SYMBOL_NAME(jiffies);  \
+7:	btl $0, SYMBOL_NAME(kernel_flag); \
 	jc 2b; \
 	jmp 1b; \
 3:	movb %al, SYMBOL_NAME(active_kernel_processor); \
@@ -190,6 +208,8 @@
 	incl SYMBOL_NAME(syscall_count); \
 	popfl; \
 	popl %edx; \
+	popl %ecx; \
+	popl %ebx; \
 	popl %eax;
 
 
@@ -325,6 +345,10 @@
 9:	movl SYMBOL_NAME(bh_mask),%eax
 	andl SYMBOL_NAME(bh_active),%eax
 	jne handle_bottom_half
+#ifdef __SMP__
+	cmpb $(NO_PROC_ID), SYMBOL_NAME(saved_active_kernel_processor)
+	jne 2f
+#endif
 	movl EFLAGS(%esp),%eax		# check VM86 flag: CS/SS are
 	testl $(VM_MASK),%eax		# different then
 	jne 1f
diff -u --recursive --new-file v2.0.30/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c
--- v2.0.30/linux/arch/i386/kernel/ptrace.c	Wed Sep 11 07:57:13 1996
+++ linux/arch/i386/kernel/ptrace.c	Mon Aug  4 12:12:22 1997
@@ -535,7 +535,7 @@
 		return;
 	current->exit_code = SIGTRAP;
 	current->state = TASK_STOPPED;
-	notify_parent(current);
+	notify_parent(current, SIGCHLD);
 	schedule();
 	/*
 	 * this isn't the same as continuing with a signal, but it will do
diff -u --recursive --new-file v2.0.30/linux/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c
--- v2.0.30/linux/arch/i386/kernel/signal.c	Wed Dec 11 06:41:01 1996
+++ linux/arch/i386/kernel/signal.c	Mon Aug  4 12:12:51 1997
@@ -286,7 +286,7 @@
 		if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
 			current->exit_code = signr;
 			current->state = TASK_STOPPED;
-			notify_parent(current);
+			notify_parent(current, SIGCHLD);
 			schedule();
 			if (!(signr = current->exit_code))
 				continue;
@@ -324,7 +324,7 @@
 				current->exit_code = signr;
 				if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & 
 						SA_NOCLDSTOP))
-					notify_parent(current);
+					notify_parent(current, SIGCHLD);
 				schedule();
 				continue;
 
diff -u --recursive --new-file v2.0.30/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- v2.0.30/linux/arch/i386/kernel/smp.c	Sun May 12 21:17:23 1996
+++ linux/arch/i386/kernel/smp.c	Mon Aug 11 13:42:11 1997
@@ -1202,3 +1202,15 @@
 	apic_read(APIC_SPIV);		/* Dummy read */
 	apic_write(APIC_EOI, 0);	/* Docs say use 0 for future compatibility */
 }
+
+void irq_deadlock_detected(void)
+{
+  printk("IRQ DEADLOCK DETECTED BY CPU %d\n", smp_processor_id());
+  __asm__("hlt");
+}
+
+void non_irq_deadlock_detected(void)
+{
+  printk("NON-IRQ DEADLOCK DETECTED BY CPU %d\n", smp_processor_id());
+  __asm__("hlt");
+}
diff -u --recursive --new-file v2.0.30/linux/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c
--- v2.0.30/linux/arch/i386/kernel/traps.c	Wed Dec 11 06:41:01 1996
+++ linux/arch/i386/kernel/traps.c	Mon Aug 11 13:37:24 1997
@@ -276,7 +276,10 @@
 asmlinkage void do_spurious_interrupt_bug(struct pt_regs * regs,
 					  long error_code)
 {
+#if 0
+	/* No need to warn about this any longer. */
 	printk("Ignoring P6 Local APIC Spurious Interrupt Bug...\n");
+#endif
 }
 
 /*
diff -u --recursive --new-file v2.0.30/linux/arch/i386/mm/fault.c linux/arch/i386/mm/fault.c
--- v2.0.30/linux/arch/i386/mm/fault.c	Wed Sep 11 07:57:13 1996
+++ linux/arch/i386/mm/fault.c	Sat Aug 16 22:21:20 1997
@@ -130,10 +130,9 @@
 		printk("This processor honours the WP bit even when in supervisor mode. Good.\n");
 		return;
 	}
-	if ((unsigned long) (address-TASK_SIZE) < PAGE_SIZE) {
+	if ((unsigned long) (address-TASK_SIZE) < PAGE_SIZE)
 		printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
-		pg0[0] = pte_val(mk_pte(0, PAGE_SHARED));
-	} else
+	else
 		printk(KERN_ALERT "Unable to handle kernel paging request");
 	printk(" at virtual address %08lx\n",address);
 	__asm__("movl %%cr3,%0" : "=r" (page));
diff -u --recursive --new-file v2.0.30/linux/arch/i386/mm/init.c linux/arch/i386/mm/init.c
--- v2.0.30/linux/arch/i386/mm/init.c	Thu Nov 14 05:20:09 1996
+++ linux/arch/i386/mm/init.c	Tue Aug 12 13:06:54 1997
@@ -35,6 +35,8 @@
 #endif
 #endif
 
+const char bad_pmd_string[] = "Bad pmd in pte_alloc: %08lx\n";
+
 extern void die_if_kernel(char *,struct pt_regs *,long);
 extern void show_net_buffers(void);
 
diff -u --recursive --new-file v2.0.30/linux/arch/m68k/defconfig linux/arch/m68k/defconfig
--- v2.0.30/linux/arch/m68k/defconfig	Mon May  6 02:44:30 1996
+++ linux/arch/m68k/defconfig	Fri Sep  5 20:43:58 1997
@@ -122,6 +122,7 @@
 # CONFIG_ISO9660_FS is not set
 # CONFIG_HPFS_FS is not set
 # CONFIG_SYSV_FS is not set
+# CONFIG_AUTOFS_FS is not set
 # CONFIG_AFFS_FS is not set
 # CONFIG_UFS_FS is not set
 
diff -u --recursive --new-file v2.0.30/linux/arch/mips/defconfig linux/arch/mips/defconfig
--- v2.0.30/linux/arch/mips/defconfig	Wed Feb  7 19:41:50 1996
+++ linux/arch/mips/defconfig	Fri Sep  5 20:43:58 1997
@@ -61,6 +61,7 @@
 # CONFIG_ISO9660_FS is not set
 # CONFIG_HPFS_FS is not set
 # CONFIG_SYSV_FS is not set
+# CONFIG_AUTOFS_FS is not set
 # CONFIG_SMB_FS is not set
 
 #
diff -u --recursive --new-file v2.0.30/linux/arch/sparc/defconfig linux/arch/sparc/defconfig
--- v2.0.30/linux/arch/sparc/defconfig	Thu Apr 25 03:22:05 1996
+++ linux/arch/sparc/defconfig	Fri Sep  5 20:43:58 1997
@@ -114,6 +114,7 @@
 # CONFIG_HPFS_FS is not set
 # CONFIG_SYSV_FS is not set
 # CONFIG_AFFS_FS is not set
+# CONFIG_AUTOFS_FS is not set
 CONFIG_UFS_FS=y
 
 #
diff -u --recursive --new-file v2.0.30/linux/drivers/block/Config.in linux/drivers/block/Config.in
--- v2.0.30/linux/drivers/block/Config.in	Mon Aug  5 00:13:50 1996
+++ linux/drivers/block/Config.in	Mon Aug  4 11:45:55 1997
@@ -5,7 +5,7 @@
 comment 'Floppy, IDE, and other block devices'
 
 tristate 'Normal floppy disk support' CONFIG_BLK_DEV_FD
-bool 'Enhanced IDE/MFM/RLL disk/cdrom/tape support' CONFIG_BLK_DEV_IDE
+bool 'Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support' CONFIG_BLK_DEV_IDE
 comment 'Please see Documentation/ide.txt for help/info on IDE drives'
 if [ "$CONFIG_BLK_DEV_IDE" = "n" ]; then
   bool 'Old harddisk (MFM/RLL/IDE) driver' CONFIG_BLK_DEV_HD_ONLY
@@ -13,6 +13,8 @@
   bool '   Use old disk-only driver on primary interface' CONFIG_BLK_DEV_HD_IDE
   bool '   Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD
   bool '   Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE
+  bool '   Include IDE/ATAPI FLOPPY support (new)' CONFIG_BLK_DEV_IDEFLOPPY
+  bool '   SCSI emulation support' CONFIG_BLK_DEV_IDESCSI
   bool '   Support removable IDE interfaces (PCMCIA)' CONFIG_BLK_DEV_IDE_PCMCIA
   bool '   CMD640 chipset bugfix/support' CONFIG_BLK_DEV_CMD640
   if [ "$CONFIG_BLK_DEV_CMD640" = "y" ]; then
diff -u --recursive --new-file v2.0.30/linux/drivers/block/Makefile linux/drivers/block/Makefile
--- v2.0.30/linux/drivers/block/Makefile	Sat Aug 10 00:03:14 1996
+++ linux/drivers/block/Makefile	Mon Aug  4 11:45:55 1997
@@ -97,6 +97,10 @@
 L_OBJS += ide-tape.o
 endif
 
+ifeq ($(CONFIG_BLK_DEV_IDEFLOPPY),y)
+L_OBJS += ide-floppy.o
+endif
+
 ifeq ($(CONFIG_BLK_DEV_XD),y)
 L_OBJS += xd.o
 else
diff -u --recursive --new-file v2.0.30/linux/drivers/block/genhd.c linux/drivers/block/genhd.c
--- v2.0.30/linux/drivers/block/genhd.c	Tue Aug 20 23:18:07 1996
+++ linux/drivers/block/genhd.c	Mon Aug  4 11:45:55 1997
@@ -331,7 +331,7 @@
 				   && (q->sector & 63) == 1
 				   && (q->end_sector & 63) == 63) {
 					unsigned int heads = q->end_head + 1;
-					if (heads == 32 || heads == 64 || heads == 128) {
+					if (heads == 32 || heads == 64 || heads == 128 || heads == 255) {
 
 						(void) ide_xlate_1024(dev, heads, " [PTBL]");
 						break;
diff -u --recursive --new-file v2.0.30/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v2.0.30/linux/drivers/block/ide-cd.c	Tue Mar 11 13:28:36 1997
+++ linux/drivers/block/ide-cd.c	Mon Aug  4 11:45:55 1997
@@ -125,7 +125,8 @@
  *                          you must define IHAVEADOLPHIN)
  *                        Added identifier so new Sanyo CD-changer works
  *                        Better detection if door locking isn't supported 
- *                         
+ * 3.21  Jun 16,1997  -- Add work-around for GCD-R580B
+ *
  * NOTE: Direct audio reads will only work on some types of drive.
  * So far, i've received reports of success for Sony and Toshiba drives.
  *
@@ -1141,6 +1142,11 @@
 	/* Number of sectors to transfer. */
 	nsect = rq->nr_sectors;
 
+#if !STANDARD_ATAPI
+	if (nsect > drive->cdrom_info.max_sectors)
+		nsect = drive->cdrom_info.max_sectors;
+#endif /* not STANDARD_ATAPI */
+
 	/* Starting sector. */
 	sector = rq->sector;
 
@@ -2674,6 +2680,8 @@
 		CDROM_CONFIG_FLAGS (drive)->drq_interrupt = 0;
 
 #if ! STANDARD_ATAPI
+	drive->cdrom_info.max_sectors = 252;
+
 	CDROM_CONFIG_FLAGS (drive)->old_readcd = 0;
 	CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 0;
 	CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 0;
@@ -2698,6 +2706,9 @@
 			/* Vertos 600 ESD. */
 			CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 1;
 		}
+
+		else if (strcmp (drive->id->model, "GCD-R580B") == 0)
+			drive->cdrom_info.max_sectors = 124;
 
 		else if (strcmp (drive->id->model,
 				 "NEC CD-ROM DRIVE:260") == 0 &&
diff -u --recursive --new-file v2.0.30/linux/drivers/block/ide-floppy.c linux/drivers/block/ide-floppy.c
--- v2.0.30/linux/drivers/block/ide-floppy.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/ide-floppy.c	Mon Aug  4 11:45:55 1997
@@ -0,0 +1,1388 @@
+/*
+ * linux/drivers/block/ide-floppy.c	Version 0.7 - ALPHA	Aug   4, 1997
+ *
+ * Copyright (C) 1996, 1997 Gadi Oxman <gadio@netvision.net.il>
+ */
+
+/*
+ * IDE ATAPI floppy driver.
+ *
+ * The driver currently doesn't have any fancy features, just the bare
+ * minimum read/write support.
+ *
+ * Many thanks to Lode Leroy <Lode.Leroy@www.ibase.be>, who tested so many
+ * ALPHA patches to this driver on an EASYSTOR LS-120 ATAPI floppy drive.
+ *
+ * Ver 0.1   Oct 17 96   Initial test version, mostly based on ide-tape.c.
+ * Ver 0.2   Oct 31 96   Minor changes.
+ * Ver 0.3   Dec  2 96   Fixed error recovery bug.
+ * Ver 0.4   Jan 26 97   Add support for the HDIO_GETGEO ioctl.
+ * Ver 0.5   Feb 21 97   Add partitions support.
+ *                       Use the minimum of the LBA and CHS capacities.
+ *                       Avoid hwgroup->rq == NULL on the last irq.
+ *                       Fix potential null dereferencing with DEBUG_LOG.
+ * Ver 0.6   Jul  3 97   Limit max sectors per read/write command to 64.
+ * Ver 0.7   Aug  4 97   Increase irq timeout from 10 to 100 seconds.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/genhd.h>
+#include <linux/malloc.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
+#include <asm/bitops.h>
+
+/*
+ *	Main Linux ide driver include file
+ */
+#include "ide.h"
+
+/*
+ *	The following are used to debug the driver.
+ */
+#define IDEFLOPPY_DEBUG_LOG		0
+#define IDEFLOPPY_DEBUG_INFO		0
+#define IDEFLOPPY_DEBUG_BUGS		1
+
+/*
+ *	After each failed packet command we issue a request sense command
+ *	and retry the packet command IDEFLOPPY_MAX_PC_RETRIES times.
+ */
+#define IDEFLOPPY_MAX_PC_RETRIES	3
+
+/*
+ *	With each packet command, we allocate a buffer of
+ *	IDEFLOPPY_PC_BUFFER_SIZE bytes.
+ */
+#define IDEFLOPPY_PC_BUFFER_SIZE	256
+
+/*
+ *	In various places in the driver, we need to allocate storage
+ *	for packet commands and requests, which will remain valid while
+ *	we leave the driver to wait for an interrupt or a timeout event.
+ */
+#define IDEFLOPPY_PC_STACK		(10 + IDEFLOPPY_MAX_PC_RETRIES)
+
+/*
+ *	Some drives fail read/write requests with 64 or more sectors.
+ */
+#define IDEFLOPPY_MAX_SECTORS		64
+
+/*
+ *	Some drives require a longer irq timeout.
+ */
+#define IDEFLOPPY_WAIT_CMD		(10 * WAIT_CMD)
+
+/*
+ *	Our view of a packet command.
+ */
+typedef struct idefloppy_packet_command_s {
+	u8 c[12];				/* Actual packet bytes */
+	int retries;				/* On each retry, we increment retries */
+	int error;				/* Error code */
+	int request_transfer;			/* Bytes to transfer */
+	int actually_transferred;		/* Bytes actually transferred */
+	int buffer_size;			/* Size of our data buffer */
+	char *b_data;				/* Pointer which runs on the buffers */
+	int b_count;				/* Missing/Available data on the current buffer */
+	struct request *rq;			/* The corresponding request */
+	byte *buffer;				/* Data buffer */
+	byte *current_position;			/* Pointer into the above buffer */
+	void (*callback) (ide_drive_t *);	/* Called when this packet command is completed */
+	byte pc_buffer[IDEFLOPPY_PC_BUFFER_SIZE];	/* Temporary buffer */
+	unsigned int flags;			/* Status/Action bit flags */
+} idefloppy_pc_t;
+
+/*
+ *	Packet command flag bits.
+ */
+#define	PC_ABORT			0	/* Set when an error is considered normal - We won't retry */
+#define PC_DMA_RECOMMENDED		2	/* 1 when we prefer to use DMA if possible */
+#define	PC_DMA_IN_PROGRESS		3	/* 1 while DMA in progress */
+#define	PC_DMA_ERROR			4	/* 1 when encountered problem during DMA */
+#define	PC_WRITING			5	/* Data direction */
+
+/*
+ *	Removable Block Access Capabilities Page
+ */
+typedef struct {
+	unsigned	page_code	:6;	/* Page code - Should be 0x1b */
+	unsigned	reserved1_6	:1;	/* Reserved */
+	unsigned	ps		:1;	/* Should be 0 */
+	u8		page_length;		/* Page Length - Should be 0xa */
+	unsigned	reserved2	:6;
+	unsigned	srfp		:1;	/* Supports reporting progress of format */
+	unsigned	sflp		:1;	/* System floppy type device */
+	unsigned	tlun		:3;	/* Total logical units supported by the device */
+	unsigned	reserved3	:3;
+	unsigned	sml		:1;	/* Single / Multiple lun supported */
+	unsigned	ncd		:1;	/* Non cd optical device */
+	u8		reserved[8];
+} idefloppy_capabilities_page_t;
+
+/*
+ *	Flexible disk page.
+ */
+typedef struct {
+	unsigned	page_code	:6;	/* Page code - Should be 0x5 */
+	unsigned	reserved1_6	:1;	/* Reserved */
+	unsigned	ps		:1;	/* The device is capable of saving the page */
+	u8		page_length;		/* Page Length - Should be 0x1e */
+	u16		transfer_rate;		/* In kilobits per second */
+	u8		heads, sectors;		/* Number of heads, Number of sectors per track */
+	u16		sector_size;		/* Byes per sector */
+	u16		cyls;			/* Number of cylinders */
+	u8		reserved10[10];
+	u8		motor_delay;		/* Motor off delay */
+	u8		reserved21[7];
+	u16		rpm;			/* Rotations per minute */
+	u8		reserved30[2];
+} idefloppy_flexible_disk_page_t;
+ 
+/*
+ *	Format capacity
+ */
+typedef struct {
+	u8		reserved[3];
+	u8		length;			/* Length of the following descriptors in bytes */
+} idefloppy_capacity_header_t;
+
+typedef struct {
+	u32		blocks;			/* Number of blocks */
+	unsigned	dc		:2;	/* Descriptor Code */
+	unsigned	reserved	:6;
+	u8		length_msb;		/* Block Length (MSB)*/
+	u16		length;			/* Block Length */
+} idefloppy_capacity_descriptor_t;
+
+#define CAPACITY_INVALID	0x00
+#define CAPACITY_UNFORMATTED	0x01
+#define CAPACITY_CURRENT	0x02
+#define CAPACITY_NO_CARTRIDGE	0x03
+
+/*
+ *	Most of our global data which we need to save even as we leave the
+ *	driver due to an interrupt or a timer event is stored in a variable
+ *	of type idefloppy_floppy_t, defined below.
+ */
+typedef struct {
+	ide_drive_t *drive;
+
+	idefloppy_pc_t *pc;			/* Current packet command */
+	idefloppy_pc_t *failed_pc; 		/* Last failed packet command */
+	idefloppy_pc_t pc_stack[IDEFLOPPY_PC_STACK];/* Packet command stack */
+	int pc_stack_index;			/* Next free packet command storage space */
+	struct request rq_stack[IDEFLOPPY_PC_STACK];
+	int rq_stack_index;			/* We implement a circular array */
+
+	/*
+	 *	Last error information
+	 */
+	byte sense_key, asc, ascq;
+
+	/*
+	 *	Device information
+	 */
+	int blocks, block_size, bs_factor;			/* Current format */
+	idefloppy_capacity_descriptor_t capacity;		/* Last format capacity */
+	idefloppy_flexible_disk_page_t flexible_disk_page;	/* Copy of the flexible disk page */
+
+	unsigned int flags;			/* Status/Action flags */
+} idefloppy_floppy_t;
+
+/*
+ *	Floppy flag bits values.
+ */
+#define IDEFLOPPY_DRQ_INTERRUPT		0	/* DRQ interrupt device */
+#define IDEFLOPPY_MEDIA_CHANGED		1	/* Media may have changed */
+#define IDEFLOPPY_USE_READ12		2	/* Use READ12/WRITE12 or READ10/WRITE10 */
+
+/*
+ *	ATAPI floppy drive packet commands
+ */
+#define IDEFLOPPY_FORMAT_UNIT_CMD	0x04
+#define IDEFLOPPY_INQUIRY_CMD		0x12
+#define IDEFLOPPY_MODE_SELECT_CMD	0x55
+#define IDEFLOPPY_MODE_SENSE_CMD	0x5a
+#define IDEFLOPPY_READ10_CMD		0x28
+#define IDEFLOPPY_READ12_CMD		0xa8
+#define IDEFLOPPY_READ_CAPACITY_CMD	0x23
+#define IDEFLOPPY_REQUEST_SENSE_CMD	0x03
+#define IDEFLOPPY_PREVENT_REMOVAL_CMD	0x1e
+#define IDEFLOPPY_SEEK_CMD		0x2b
+#define IDEFLOPPY_START_STOP_CMD	0x1b
+#define IDEFLOPPY_TEST_UNIT_READY_CMD	0x00
+#define IDEFLOPPY_VERIFY_CMD		0x2f
+#define IDEFLOPPY_WRITE10_CMD		0x2a
+#define IDEFLOPPY_WRITE12_CMD		0xaa
+#define IDEFLOPPY_WRITE_VERIFY_CMD	0x2e
+
+/*
+ *	Defines for the mode sense command
+ */
+#define MODE_SENSE_CURRENT		0x00
+#define MODE_SENSE_CHANGEABLE		0x01
+#define MODE_SENSE_DEFAULT		0x02 
+#define MODE_SENSE_SAVED		0x03
+
+/*
+ *	Special requests for our block device strategy routine.
+ */
+#define	IDEFLOPPY_FIRST_RQ		90
+
+/*
+ * 	IDEFLOPPY_PC_RQ is used to queue a packet command in the request queue.
+ */
+#define	IDEFLOPPY_PC_RQ			90
+
+#define IDEFLOPPY_LAST_RQ		90
+
+/*
+ *	A macro which can be used to check if a given request command
+ *	originated in the driver or in the buffer cache layer.
+ */
+#define IDEFLOPPY_RQ_CMD(cmd) 		((cmd >= IDEFLOPPY_FIRST_RQ) && (cmd <= IDEFLOPPY_LAST_RQ))
+
+/*
+ *	Error codes which are returned in rq->errors to the higher part
+ *	of the driver.
+ */
+#define	IDEFLOPPY_ERROR_GENERAL		101
+
+/*
+ *	The ATAPI Status Register.
+ */
+typedef union {
+	unsigned all			:8;
+	struct {
+		unsigned check		:1;	/* Error occurred */
+		unsigned idx		:1;	/* Reserved */
+		unsigned corr		:1;	/* Correctable error occurred */
+		unsigned drq		:1;	/* Data is request by the device */
+		unsigned dsc		:1;	/* Media access command finished */
+		unsigned reserved5	:1;	/* Reserved */
+		unsigned drdy		:1;	/* Ignored for ATAPI commands (ready to accept ATA command) */
+		unsigned bsy		:1;	/* The device has access to the command block */
+	} b;
+} idefloppy_status_reg_t;
+
+/*
+ *	The ATAPI error register.
+ */
+typedef union {
+	unsigned all			:8;
+	struct {
+		unsigned ili		:1;	/* Illegal Length Indication */
+		unsigned eom		:1;	/* End Of Media Detected */
+		unsigned abrt		:1;	/* Aborted command - As defined by ATA */
+		unsigned mcr		:1;	/* Media Change Requested - As defined by ATA */
+		unsigned sense_key	:4;	/* Sense key of the last failed packet command */
+	} b;
+} idefloppy_error_reg_t;
+
+/*
+ *	ATAPI Feature Register
+ */
+typedef union {
+	unsigned all			:8;
+	struct {
+		unsigned dma		:1;	/* Using DMA or PIO */
+		unsigned reserved321	:3;	/* Reserved */
+		unsigned reserved654	:3;	/* Reserved (Tag Type) */
+		unsigned reserved7	:1;	/* Reserved */
+	} b;
+} idefloppy_feature_reg_t;
+
+/*
+ *	ATAPI Byte Count Register.
+ */
+typedef union {
+	unsigned all			:16;
+	struct {
+		unsigned low		:8;	/* LSB */
+		unsigned high		:8;	/* MSB */
+	} b;
+} idefloppy_bcount_reg_t;
+
+/*
+ *	ATAPI Interrupt Reason Register.
+ */
+typedef union {
+	unsigned all			:8;
+	struct {
+		unsigned cod		:1;	/* Information transferred is command (1) or data (0) */
+		unsigned io		:1;	/* The device requests us to read (1) or write (0) */
+		unsigned reserved	:6;	/* Reserved */
+	} b;
+} idefloppy_ireason_reg_t;
+
+/*
+ *	ATAPI floppy Drive Select Register
+ */
+typedef union {	
+	unsigned all			:8;
+	struct {
+		unsigned sam_lun	:3;	/* Logical unit number */
+		unsigned reserved3	:1;	/* Reserved */
+		unsigned drv		:1;	/* The responding drive will be drive 0 (0) or drive 1 (1) */
+		unsigned one5		:1;	/* Should be set to 1 */
+		unsigned reserved6	:1;	/* Reserved */
+		unsigned one7		:1;	/* Should be set to 1 */
+	} b;
+} idefloppy_drivesel_reg_t;
+
+/*
+ *	ATAPI Device Control Register
+ */
+typedef union {			
+	unsigned all			:8;
+	struct {
+		unsigned zero0		:1;	/* Should be set to zero */
+		unsigned nien		:1;	/* Device interrupt is disabled (1) or enabled (0) */
+		unsigned srst		:1;	/* ATA software reset. ATAPI devices should use the new ATAPI srst. */
+		unsigned one3		:1;	/* Should be set to 1 */
+		unsigned reserved4567	:4;	/* Reserved */
+	} b;
+} idefloppy_control_reg_t;
+
+/*
+ *	The following is used to format the general configuration word of
+ *	the ATAPI IDENTIFY DEVICE command.
+ */
+struct idefloppy_id_gcw {	
+	unsigned packet_size		:2;	/* Packet Size */
+	unsigned reserved234		:3;	/* Reserved */
+	unsigned drq_type		:2;	/* Command packet DRQ type */
+	unsigned removable		:1;	/* Removable media */
+	unsigned device_type		:5;	/* Device type */
+	unsigned reserved13		:1;	/* Reserved */
+	unsigned protocol		:2;	/* Protocol type */
+};
+
+/*
+ *	INQUIRY packet command - Data Format
+ */
+typedef struct {
+	unsigned	device_type	:5;	/* Peripheral Device Type */
+	unsigned	reserved0_765	:3;	/* Peripheral Qualifier - Reserved */
+	unsigned	reserved1_6t0	:7;	/* Reserved */
+	unsigned	rmb		:1;	/* Removable Medium Bit */
+	unsigned	ansi_version	:3;	/* ANSI Version */
+	unsigned	ecma_version	:3;	/* ECMA Version */
+	unsigned	iso_version	:2;	/* ISO Version */
+	unsigned	response_format :4;	/* Response Data Format */
+	unsigned	reserved3_45	:2;	/* Reserved */
+	unsigned	reserved3_6	:1;	/* TrmIOP - Reserved */
+	unsigned	reserved3_7	:1;	/* AENC - Reserved */
+	u8		additional_length;	/* Additional Length (total_length-4) */
+	u8		rsv5, rsv6, rsv7;	/* Reserved */
+	u8		vendor_id[8];		/* Vendor Identification */
+	u8		product_id[16];		/* Product Identification */
+	u8		revision_level[4];	/* Revision Level */
+	u8		vendor_specific[20];	/* Vendor Specific - Optional */
+	u8		reserved56t95[40];	/* Reserved - Optional */
+						/* Additional information may be returned */
+} idefloppy_inquiry_result_t;
+
+/*
+ *	REQUEST SENSE packet command result - Data Format.
+ */
+typedef struct {
+	unsigned	error_code	:7;	/* Current error (0x70) */
+	unsigned	valid		:1;	/* The information field conforms to SFF-8070i */
+	u8		reserved1	:8;	/* Reserved */
+	unsigned	sense_key	:4;	/* Sense Key */
+	unsigned	reserved2_4	:1;	/* Reserved */
+	unsigned	ili		:1;	/* Incorrect Length Indicator */
+	unsigned	reserved2_67	:2;
+	u32		information __attribute__ ((packed));
+	u8		asl;			/* Additional sense length (n-7) */
+	u32		command_specific;	/* Additional command specific information */
+	u8		asc;			/* Additional Sense Code */
+	u8		ascq;			/* Additional Sense Code Qualifier */
+	u8		replaceable_unit_code;	/* Field Replaceable Unit Code */
+	u8		reserved[3];
+	u8		pad[2];			/* Padding to 20 bytes */
+} idefloppy_request_sense_result_t;
+
+/*
+ *	Pages of the SELECT SENSE / MODE SENSE packet commands.
+ */
+#define	IDEFLOPPY_CAPABILITIES_PAGE	0x1b
+#define IDEFLOPPY_FLEXIBLE_DISK_PAGE	0x05
+
+/*
+ *	Mode Parameter Header for the MODE SENSE packet command
+ */
+typedef struct {
+	u16		mode_data_length;	/* Length of the following data transfer */
+	u8		medium_type;		/* Medium Type */
+	unsigned	reserved3	:7;
+	unsigned	wp		:1;	/* Write protect */
+	u8		reserved[4];
+} idefloppy_mode_parameter_header_t;
+
+#define IDEFLOPPY_MIN(a,b)	((a)<(b) ? (a):(b))
+#define	IDEFLOPPY_MAX(a,b)	((a)>(b) ? (a):(b))
+
+/*
+ *	Too bad. The drive wants to send us data which we are not ready to accept.
+ *	Just throw it away.
+ */
+static void idefloppy_discard_data (ide_drive_t *drive, unsigned int bcount)
+{
+	while (bcount--)
+		IN_BYTE (IDE_DATA_REG);
+}
+
+#if IDEFLOPPY_DEBUG_BUGS
+static void idefloppy_write_zeros (ide_drive_t *drive, unsigned int bcount)
+{
+	while (bcount--)
+		OUT_BYTE (0, IDE_DATA_REG);
+}
+#endif /* IDEFLOPPY_DEBUG_BUGS */
+
+/*
+ *	idefloppy_end_request is used to finish servicing a request.
+ *
+ *	For read/write requests, we will call ide_end_request to pass to the
+ *	next buffer.
+ */
+void idefloppy_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
+{
+	ide_drive_t *drive = hwgroup->drive;
+	idefloppy_floppy_t *floppy = drive->floppy;
+	struct request *rq = hwgroup->rq;
+	int error;
+
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "Reached idefloppy_end_request\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	switch (uptodate) {
+		case 0: error = IDEFLOPPY_ERROR_GENERAL; break;
+		case 1: error = 0; break;
+		default: error = uptodate;
+	}
+	if (error)
+		floppy->failed_pc = NULL;
+	if (!IDEFLOPPY_RQ_CMD (rq->cmd)) {
+		ide_end_request (uptodate, hwgroup);
+		return;
+	}
+	rq->errors = error;
+	ide_end_drive_cmd (drive, 0, 0);
+}
+
+static void idefloppy_input_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, unsigned int bcount)
+{
+	struct request *rq = pc->rq;
+	struct buffer_head *bh = rq->bh;
+	int count;
+
+	while (bcount) {
+		if (pc->b_count == bh->b_size) {
+			idefloppy_end_request (1, HWGROUP(drive));
+			if ((bh = rq->bh) != NULL)
+				pc->b_count = 0;
+		}
+		if (bh == NULL) {
+			printk (KERN_ERR "%s: bh == NULL in idefloppy_input_buffers, bcount == %d\n", drive->name, bcount);
+			idefloppy_discard_data (drive, bcount);
+			return;
+		}
+		count = IDEFLOPPY_MIN (bh->b_size - pc->b_count, bcount);
+		atapi_input_bytes (drive, bh->b_data + pc->b_count, count);
+		bcount -= count; pc->b_count += count;
+		if (pc->b_count == bh->b_size) {
+			rq->sector += rq->current_nr_sectors;
+			rq->nr_sectors -= rq->current_nr_sectors;
+		}
+	}
+}
+
+static void idefloppy_output_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, unsigned int bcount)
+{
+	struct request *rq = pc->rq;
+	struct buffer_head *bh = rq->bh;
+	int count;
+	
+	while (bcount) {
+		if (!pc->b_count) {
+			idefloppy_end_request (1, HWGROUP(drive));
+			if ((bh = rq->bh) != NULL) {
+				pc->b_data = bh->b_data;
+				pc->b_count = bh->b_size;
+			}
+		}
+		if (bh == NULL) {
+			printk (KERN_ERR "%s: bh == NULL in idefloppy_output_buffers, bcount == %d\n", drive->name, bcount);
+			idefloppy_write_zeros (drive, bcount);
+			return;
+		}
+		count = IDEFLOPPY_MIN (pc->b_count, bcount);
+		atapi_output_bytes (drive, pc->b_data, count);
+		bcount -= count; pc->b_data += count; pc->b_count -= count;
+		if (!pc->b_count) {
+			rq->sector += rq->current_nr_sectors;
+			rq->nr_sectors -= rq->current_nr_sectors;
+		}
+	}
+}
+
+#ifdef CONFIG_BLK_DEV_TRITON
+static void idefloppy_update_buffers (ide_drive_t *drive, idefloppy_pc_t *pc)
+{
+	struct request *rq = pc->rq;
+	struct buffer_head *bh = rq->bh;
+
+	while ((bh = rq->bh) != NULL)
+		idefloppy_end_request (1, HWGROUP(drive));
+}
+#endif /* CONFIG_BLK_DEV_TRITON */
+
+/*
+ *	idefloppy_queue_pc_head generates a new packet command request in front
+ *	of the request queue, before the current request, so that it will be
+ *	processed immediately, on the next pass through the driver.
+ */
+static void idefloppy_queue_pc_head (ide_drive_t *drive,idefloppy_pc_t *pc,struct request *rq)
+{
+	ide_init_drive_cmd (rq);
+	rq->buffer = (char *) pc;
+	rq->cmd = IDEFLOPPY_PC_RQ;
+	(void) ide_do_drive_cmd (drive, rq, ide_preempt);
+}
+
+static idefloppy_pc_t *idefloppy_next_pc_storage (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+
+	if (floppy->pc_stack_index==IDEFLOPPY_PC_STACK)
+		floppy->pc_stack_index=0;
+	return (&floppy->pc_stack[floppy->pc_stack_index++]);
+}
+
+static struct request *idefloppy_next_rq_storage (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+
+	if (floppy->rq_stack_index==IDEFLOPPY_PC_STACK)
+		floppy->rq_stack_index=0;
+	return (&floppy->rq_stack[floppy->rq_stack_index++]);
+}
+
+/*
+ *	idefloppy_analyze_error is called on each failed packet command retry
+ *	to analyze the request sense.
+ */
+static void idefloppy_analyze_error (ide_drive_t *drive,idefloppy_request_sense_result_t *result)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+
+	floppy->sense_key = result->sense_key; floppy->asc = result->asc; floppy->ascq = result->ascq;
+#if IDEFLOPPY_DEBUG_LOG
+	if (floppy->failed_pc)
+		printk (KERN_INFO "ide-floppy: pc = %x, sense key = %x, asc = %x, ascq = %x\n",floppy->failed_pc->c[0],result->sense_key,result->asc,result->ascq);
+	else
+		printk (KERN_INFO "ide-floppy: sense key = %x, asc = %x, ascq = %x\n",result->sense_key,result->asc,result->ascq);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+}
+
+static void idefloppy_request_sense_callback (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "ide-floppy: Reached idefloppy_request_sense_callback\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+	if (!floppy->pc->error) {
+		idefloppy_analyze_error (drive,(idefloppy_request_sense_result_t *) floppy->pc->buffer);
+		idefloppy_end_request (1,HWGROUP (drive));
+	} else {
+		printk (KERN_ERR "Error in REQUEST SENSE itself - Aborting request!\n");
+		idefloppy_end_request (0,HWGROUP (drive));
+	}
+}
+
+/*
+ *	General packet command callback function.
+ */
+static void idefloppy_pc_callback (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+	
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "ide-floppy: Reached idefloppy_pc_callback\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	idefloppy_end_request (floppy->pc->error ? 0:1, HWGROUP(drive));
+}
+
+/*
+ *	idefloppy_init_pc initializes a packet command.
+ */
+static void idefloppy_init_pc (idefloppy_pc_t *pc)
+{
+	memset (pc->c, 0, 12);
+	pc->retries = 0;
+	pc->flags = 0;
+	pc->request_transfer = 0;
+	pc->buffer = pc->pc_buffer;
+	pc->buffer_size = IDEFLOPPY_PC_BUFFER_SIZE;
+	pc->b_data = NULL;
+	pc->callback = &idefloppy_pc_callback;
+}
+
+static void idefloppy_create_request_sense_cmd (idefloppy_pc_t *pc)
+{
+	idefloppy_init_pc (pc);	
+	pc->c[0] = IDEFLOPPY_REQUEST_SENSE_CMD;
+	pc->c[4] = 255;
+	pc->request_transfer = 18;
+	pc->callback = &idefloppy_request_sense_callback;
+}
+
+/*
+ *	idefloppy_retry_pc is called when an error was detected during the
+ *	last packet command. We queue a request sense packet command in
+ *	the head of the request list.
+ */
+static void idefloppy_retry_pc (ide_drive_t *drive)
+{
+	idefloppy_pc_t *pc;
+	struct request *rq;
+	idefloppy_error_reg_t error;
+
+	error.all = IN_BYTE (IDE_ERROR_REG);
+	pc = idefloppy_next_pc_storage (drive);
+	rq = idefloppy_next_rq_storage (drive);
+	idefloppy_create_request_sense_cmd (pc);
+	idefloppy_queue_pc_head (drive, pc, rq);
+}
+
+/*
+ *	idefloppy_pc_intr is the usual interrupt handler which will be called
+ *	during a packet command.
+ */
+static void idefloppy_pc_intr (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+	idefloppy_status_reg_t status;
+	idefloppy_bcount_reg_t bcount;
+	idefloppy_ireason_reg_t ireason;
+	idefloppy_pc_t *pc=floppy->pc;
+	struct request *rq = pc->rq;
+	unsigned int temp;
+
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "ide-floppy: Reached idefloppy_pc_intr interrupt handler\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */	
+
+#ifdef CONFIG_BLK_DEV_TRITON
+	if (test_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
+		if (HWIF(drive)->dmaproc(ide_dma_status_bad, drive)) {
+			set_bit (PC_DMA_ERROR, &pc->flags);
+		} else {
+			pc->actually_transferred=pc->request_transfer;
+			idefloppy_update_buffers (drive, pc);
+		}
+		(void) (HWIF(drive)->dmaproc(ide_dma_abort, drive));	/* End DMA */
+#if IDEFLOPPY_DEBUG_LOG
+		printk (KERN_INFO "ide-floppy: DMA finished\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+	}
+#endif /* CONFIG_BLK_DEV_TRITON */
+
+	status.all = GET_STAT();					/* Clear the interrupt */
+
+	if (!status.b.drq) {						/* No more interrupts */
+#if IDEFLOPPY_DEBUG_LOG
+		printk (KERN_INFO "Packet command completed, %d bytes transferred\n", pc->actually_transferred);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+		clear_bit (PC_DMA_IN_PROGRESS, &pc->flags);
+
+		sti();
+
+		if (status.b.check || test_bit (PC_DMA_ERROR, &pc->flags)) {	/* Error detected */
+#if IDEFLOPPY_DEBUG_LOG
+			printk (KERN_INFO "ide-floppy: %s: I/O error, ",drive->name);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+			rq->errors++;
+			if (pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) {
+				printk (KERN_ERR "ide-floppy: I/O error in request sense command\n");
+				ide_do_reset (drive);
+				return;
+			}
+			idefloppy_retry_pc (drive);				/* Retry operation */
+			return;
+		}
+		pc->error = 0;
+		if (floppy->failed_pc == pc)
+			floppy->failed_pc=NULL;
+		pc->callback(drive);			/* Command finished - Call the callback function */
+		return;
+	}
+#ifdef CONFIG_BLK_DEV_TRITON
+	if (clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
+		printk (KERN_ERR "ide-floppy: The floppy wants to issue more interrupts in DMA mode\n");
+		printk (KERN_ERR "ide-floppy: DMA disabled, reverting to PIO\n");
+		drive->using_dma=0;
+		ide_do_reset (drive);
+		return;
+	}
+#endif /* CONFIG_BLK_DEV_TRITON */
+	bcount.b.high=IN_BYTE (IDE_BCOUNTH_REG);			/* Get the number of bytes to transfer */
+	bcount.b.low=IN_BYTE (IDE_BCOUNTL_REG);			/* on this interrupt */
+	ireason.all=IN_BYTE (IDE_IREASON_REG);
+
+	if (ireason.b.cod) {
+		printk (KERN_ERR "ide-floppy: CoD != 0 in idefloppy_pc_intr\n");
+		ide_do_reset (drive);
+		return;
+	}
+	if (ireason.b.io == test_bit (PC_WRITING, &pc->flags)) {	/* Hopefully, we will never get here */
+		printk (KERN_ERR "ide-floppy: We wanted to %s, ", ireason.b.io ? "Write":"Read");
+		printk (KERN_ERR "but the floppy wants us to %s !\n",ireason.b.io ? "Read":"Write");
+		ide_do_reset (drive);
+		return;
+	}
+	if (!test_bit (PC_WRITING, &pc->flags)) {			/* Reading - Check that we have enough space */
+		temp = pc->actually_transferred + bcount.all;
+		if ( temp > pc->request_transfer) {
+			if (temp > pc->buffer_size) {
+				printk (KERN_ERR "ide-floppy: The floppy wants to send us more data than expected - discarding data\n");
+				idefloppy_discard_data (drive,bcount.all);
+				ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD);
+				return;
+			}
+#if IDEFLOPPY_DEBUG_LOG
+			printk (KERN_NOTICE "ide-floppy: The floppy wants to send us more data than expected - allowing transfer\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+		}
+	}
+	if (test_bit (PC_WRITING, &pc->flags)) {
+		if (pc->buffer != NULL)
+			atapi_output_bytes (drive,pc->current_position,bcount.all);	/* Write the current buffer */
+		else
+			idefloppy_output_buffers (drive, pc, bcount.all);
+	} else {
+		if (pc->buffer != NULL)
+			atapi_input_bytes (drive,pc->current_position,bcount.all);	/* Read the current buffer */
+		else
+			idefloppy_input_buffers (drive, pc, bcount.all);
+	}
+	pc->actually_transferred+=bcount.all;				/* Update the current position */
+	pc->current_position+=bcount.all;
+
+	ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD);		/* And set the interrupt handler again */
+}
+
+static void idefloppy_transfer_pc (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+	idefloppy_ireason_reg_t ireason;
+
+	if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
+		printk (KERN_ERR "ide-floppy: Strange, packet command initiated yet DRQ isn't asserted\n");
+		return;
+	}
+	ireason.all=IN_BYTE (IDE_IREASON_REG);
+	if (!ireason.b.cod || ireason.b.io) {
+		printk (KERN_ERR "ide-floppy: (IO,CoD) != (0,1) while issuing a packet command\n");
+		ide_do_reset (drive);
+		return;
+	}
+	ide_set_handler (drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD);	/* Set the interrupt routine */
+	atapi_output_bytes (drive, floppy->pc->c, 12);		/* Send the actual packet */
+}
+
+/*
+ *	Issue a packet command
+ */
+static void idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *pc)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+	idefloppy_bcount_reg_t bcount;
+	int dma_ok = 0;
+
+#if IDEFLOPPY_DEBUG_BUGS
+	if (floppy->pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD && pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) {
+		printk (KERN_ERR "ide-floppy: possible ide-floppy.c bug - Two request sense in serial were issued\n");
+	}
+#endif /* IDEFLOPPY_DEBUG_BUGS */
+
+	if (floppy->failed_pc == NULL && pc->c[0] != IDEFLOPPY_REQUEST_SENSE_CMD)
+		floppy->failed_pc=pc;
+	floppy->pc=pc;							/* Set the current packet command */
+
+	if (pc->retries > IDEFLOPPY_MAX_PC_RETRIES || test_bit (PC_ABORT, &pc->flags)) {
+		/*
+		 *	We will "abort" retrying a packet command in case
+		 *	a legitimate error code was received.
+		 */
+		if (!test_bit (PC_ABORT, &pc->flags)) {
+			printk (KERN_ERR "ide-floppy: %s: I/O error, pc = %2x, key = %2x, asc = %2x, ascq = %2x\n",
+				drive->name, pc->c[0], floppy->sense_key, floppy->asc, floppy->ascq);
+			pc->error = IDEFLOPPY_ERROR_GENERAL;		/* Giving up */
+		}
+		floppy->failed_pc=NULL;
+		pc->callback(drive);
+		return;
+	}
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "Retry number - %d\n",pc->retries);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	pc->retries++;
+	pc->actually_transferred=0;					/* We haven't transferred any data yet */
+	pc->current_position=pc->buffer;
+	bcount.all=pc->request_transfer;				/* Request to transfer the entire buffer at once */
+
+#ifdef CONFIG_BLK_DEV_TRITON
+	if (clear_bit (PC_DMA_ERROR, &pc->flags)) {
+		printk (KERN_WARNING "ide-floppy: DMA disabled, reverting to PIO\n");
+		drive->using_dma=0;
+	}
+	if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma)
+		dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive);
+#endif /* CONFIG_BLK_DEV_TRITON */
+
+	OUT_BYTE (drive->ctl,IDE_CONTROL_REG);
+	OUT_BYTE (dma_ok ? 1:0,IDE_FEATURE_REG);			/* Use PIO/DMA */
+	OUT_BYTE (bcount.b.high,IDE_BCOUNTH_REG);
+	OUT_BYTE (bcount.b.low,IDE_BCOUNTL_REG);
+	OUT_BYTE (drive->select.all,IDE_SELECT_REG);
+
+#ifdef CONFIG_BLK_DEV_TRITON
+	if (dma_ok) {							/* Begin DMA, if necessary */
+		set_bit (PC_DMA_IN_PROGRESS, &pc->flags);
+		(void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
+	}
+#endif /* CONFIG_BLK_DEV_TRITON */
+
+	if (test_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags)) {
+		ide_set_handler (drive, &idefloppy_transfer_pc, IDEFLOPPY_WAIT_CMD);
+		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);		/* Issue the packet command */
+	} else {
+		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);
+		idefloppy_transfer_pc (drive);
+	}
+}
+
+static void idefloppy_rw_callback (ide_drive_t *drive)
+{
+#if IDEFLOPPY_DEBUG_LOG	
+	printk (KERN_INFO "ide-floppy: Reached idefloppy_rw_callback\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	idefloppy_end_request(1, HWGROUP(drive));
+	return;
+}
+
+static void idefloppy_create_prevent_cmd (idefloppy_pc_t *pc, int prevent)
+{
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "ide-floppy: creating prevent removal command, prevent = %d\n", prevent);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	idefloppy_init_pc (pc);
+	pc->c[0] = IDEFLOPPY_PREVENT_REMOVAL_CMD;
+	pc->c[4] = prevent;
+}
+
+static void idefloppy_create_read_capacity_cmd (idefloppy_pc_t *pc)
+{
+	idefloppy_init_pc (pc);
+	pc->c[0] = IDEFLOPPY_READ_CAPACITY_CMD;
+	pc->c[7] = 255;
+	pc->c[8] = 255;
+}
+
+/*
+ *	A mode sense command is used to "sense" floppy parameters.
+ */
+static void idefloppy_create_mode_sense_cmd (idefloppy_pc_t *pc, byte page_code, byte type)
+{
+	unsigned short length = sizeof (idefloppy_mode_parameter_header_t);
+	
+	idefloppy_init_pc (pc);
+	pc->c[0] = IDEFLOPPY_MODE_SENSE_CMD;
+	pc->c[1] = 0;
+	pc->c[2] = page_code + (type << 6);
+
+	switch (page_code) {
+		case IDEFLOPPY_CAPABILITIES_PAGE:
+			length += 12;
+			break;
+		case IDEFLOPPY_FLEXIBLE_DISK_PAGE:
+			length += 32;
+			break;
+		default:
+			printk (KERN_ERR "ide-floppy: unsupported page code in create_mode_sense_cmd\n");
+	}
+	put_unaligned (htons (length), (unsigned short *) &pc->c[7]);
+	pc->request_transfer = length;
+}
+
+static void idefloppy_create_start_stop_cmd (idefloppy_pc_t *pc, int start)
+{
+	idefloppy_init_pc (pc);
+	pc->c[0] = IDEFLOPPY_START_STOP_CMD;
+	pc->c[4] = start;
+}
+
+static void idefloppy_create_rw_cmd (idefloppy_floppy_t *floppy, idefloppy_pc_t *pc, struct request *rq, unsigned long sector)
+{
+	int block = sector / floppy->bs_factor;
+	int blocks = IDEFLOPPY_MIN(rq->nr_sectors / floppy->bs_factor, IDEFLOPPY_MAX_SECTORS);
+
+#if IDEFLOPPY_DEBUG_LOG
+	printk ("create_rw1%d_cmd: block == %d, blocks == %d\n",
+		2 * test_bit (IDEFLOPPY_USE_READ12, &floppy->flags), block, blocks);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	idefloppy_init_pc (pc);
+	if (test_bit (IDEFLOPPY_USE_READ12, &floppy->flags)) {
+		pc->c[0] = rq->cmd == READ ? IDEFLOPPY_READ12_CMD : IDEFLOPPY_WRITE12_CMD;
+		put_unaligned (htonl (blocks), (unsigned int *) &pc->c[6]);
+	} else {
+		pc->c[0] = rq->cmd == READ ? IDEFLOPPY_READ10_CMD : IDEFLOPPY_WRITE10_CMD;
+		put_unaligned (htons (blocks), (unsigned short *) &pc->c[7]);
+	}
+	put_unaligned (htonl (block), (unsigned int *) &pc->c[2]);
+	pc->callback = &idefloppy_rw_callback;
+	pc->rq = rq;
+	pc->b_data = rq->buffer;
+	pc->b_count = rq->cmd == READ ? 0 : rq->bh->b_size;
+	if (rq->cmd == WRITE)
+		set_bit (PC_WRITING, &pc->flags);
+	pc->buffer = NULL;
+	pc->request_transfer = pc->buffer_size = blocks * floppy->block_size;
+	set_bit (PC_DMA_RECOMMENDED, &pc->flags);
+}
+
+/*
+ *	idefloppy_do_request is our request handling function.	
+ */
+void idefloppy_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+	idefloppy_pc_t *pc;
+
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors);
+	printk (KERN_INFO "sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	if (rq->errors >= ERROR_MAX) {
+		if (floppy->failed_pc != NULL)
+			printk (KERN_ERR "ide-floppy: %s: I/O error, pc = %2x, key = %2x, asc = %2x, ascq = %2x\n",
+				drive->name, floppy->failed_pc->c[0], floppy->sense_key, floppy->asc, floppy->ascq);
+		else
+			printk (KERN_ERR "ide-floppy: %s: I/O error\n", drive->name);
+		idefloppy_end_request (0, HWGROUP(drive));
+		return;
+	}
+	switch (rq->cmd) {
+		case READ:
+		case WRITE:
+			if (rq->sector % floppy->bs_factor || rq->nr_sectors % floppy->bs_factor) {
+				printk ("%s: unsupported r/w request size\n", drive->name);
+				idefloppy_end_request (0, HWGROUP(drive));
+				return;
+			}
+			pc = idefloppy_next_pc_storage (drive);
+			idefloppy_create_rw_cmd (floppy, pc, rq, block);
+			break;
+		case IDEFLOPPY_PC_RQ:
+			pc = (idefloppy_pc_t *) rq->buffer;
+			break;
+		default:
+			printk (KERN_ERR "ide-floppy: unsupported command %x in request queue\n", rq->cmd);
+			idefloppy_end_request (0,HWGROUP (drive));
+			return;
+	}
+	pc->rq = rq;
+	idefloppy_issue_pc (drive, pc);
+}
+
+/*
+ *	idefloppy_queue_pc_tail adds a special packet command request to the
+ *	tail of the request queue, and waits for it to be serviced.
+ */
+static int idefloppy_queue_pc_tail (ide_drive_t *drive,idefloppy_pc_t *pc)
+{
+	struct request rq;
+
+	ide_init_drive_cmd (&rq);
+	rq.buffer = (char *) pc;
+	rq.cmd = IDEFLOPPY_PC_RQ;
+	return ide_do_drive_cmd (drive, &rq, ide_wait);
+}
+
+/*
+ *	Look at the flexible disk page parameters. We will ignore the CHS
+ *	capacity parameters and use the LBA parameters instead.
+ */
+static int idefloppy_get_flexible_disk_page (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+	idefloppy_pc_t pc;
+	idefloppy_mode_parameter_header_t *header;
+	idefloppy_flexible_disk_page_t *page;
+	int capacity, lba_capacity;
+
+	idefloppy_create_mode_sense_cmd (&pc, IDEFLOPPY_FLEXIBLE_DISK_PAGE, MODE_SENSE_CURRENT);
+	if (idefloppy_queue_pc_tail (drive,&pc)) {
+		printk (KERN_ERR "ide-floppy: Can't get flexible disk page parameters\n");
+		return 1;
+	}
+	header = (idefloppy_mode_parameter_header_t *) pc.buffer;
+	page = (idefloppy_flexible_disk_page_t *) (header + 1);
+
+	page->transfer_rate = ntohs (page->transfer_rate);
+	page->sector_size = ntohs (page->sector_size);
+	page->cyls = ntohs (page->cyls);
+	page->rpm = ntohs (page->rpm);
+	capacity = page->cyls * page->heads * page->sectors * page->sector_size;
+	if (memcmp (page, &floppy->flexible_disk_page, sizeof (idefloppy_flexible_disk_page_t)))
+		printk (KERN_INFO "%s: %dkB, %d/%d/%d CHS, %d kBps, %d sector size, %d rpm\n",
+			drive->name, capacity / 1024, page->cyls, page->heads, page->sectors,
+			page->transfer_rate / 8, page->sector_size, page->rpm);
+
+	floppy->flexible_disk_page = *page;
+	drive->bios_cyl = page->cyls;
+	drive->bios_head = page->heads;
+	drive->bios_sect = page->sectors;
+	lba_capacity = floppy->blocks * floppy->block_size;
+	if (capacity != lba_capacity) {
+		printk (KERN_NOTICE "%s: The drive reports both %d and %d bytes as its capacity\n",
+			drive->name, capacity, lba_capacity);
+		capacity = IDEFLOPPY_MIN(capacity, lba_capacity);
+		floppy->blocks = floppy->block_size ? capacity / floppy->block_size : 0;
+	}
+	return 0;
+}
+
+/*
+ *	Determine if a media is present in the floppy drive, and if so,
+ *	its LBA capacity.
+ */
+static int idefloppy_get_capacity (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+	idefloppy_pc_t pc;
+	idefloppy_capacity_header_t *header;
+	idefloppy_capacity_descriptor_t *descriptor;
+	int i, descriptors, rc = 1, blocks, length;
+
+	drive->bios_cyl = 0;
+	drive->bios_head = drive->bios_sect = 0;
+	floppy->blocks = floppy->bs_factor = 0;
+	drive->part[0].nr_sects = 0;
+
+	idefloppy_create_read_capacity_cmd (&pc);
+	if (idefloppy_queue_pc_tail (drive, &pc)) {
+		printk (KERN_ERR "ide-floppy: Can't get floppy parameters\n");
+		return 1;
+	}
+	header = (idefloppy_capacity_header_t *) pc.buffer;
+	descriptors = header->length / sizeof (idefloppy_capacity_descriptor_t);
+	descriptor = (idefloppy_capacity_descriptor_t *) (header + 1);
+	for (i = 0; i < descriptors; i++, descriptor++) {
+		blocks = descriptor->blocks = ntohl (descriptor->blocks);
+		length = descriptor->length = ntohs (descriptor->length);
+		if (!i && descriptor->dc == CAPACITY_CURRENT) {
+			if (memcmp (descriptor, &floppy->capacity, sizeof (idefloppy_capacity_descriptor_t)))
+				printk (KERN_INFO "%s: %dkB, %d blocks, %d sector size\n", drive->name, blocks * length / 1024, blocks, length);
+			floppy->capacity = *descriptor;
+			if (!length || length % 512)
+				printk (KERN_ERR "%s: %d bytes block size not supported\n", drive->name, length);
+			else {
+				floppy->blocks = blocks;
+				floppy->block_size = length;
+				if ((floppy->bs_factor = length / 512) != 1)
+					printk (KERN_NOTICE "%s: warning: non 512 bytes block size not fully supported\n", drive->name);
+				rc = 0;
+			}
+		}
+#if IDEFLOPPY_DEBUG_INFO
+		if (!i) printk (KERN_INFO "Descriptor 0 Code: %d\n", descriptor->dc);
+		printk (KERN_INFO "Descriptor %d: %dkB, %d blocks, %d sector size\n", i, blocks * length / 1024, blocks, length);
+#endif /* IDEFLOPPY_DEBUG_INFO */
+	}
+	(void) idefloppy_get_flexible_disk_page (drive);
+	drive->part[0].nr_sects = floppy->blocks * floppy->bs_factor;
+	return rc;
+}
+
+/*
+ *	Our special ide-floppy ioctl's.
+ *
+ *	Currently there aren't any ioctl's.
+ */
+int idefloppy_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
+			unsigned int cmd, unsigned long arg)
+{
+	return -EIO;
+}
+
+/*
+ *	Our open/release functions
+ */
+int idefloppy_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+	idefloppy_pc_t pc;
+	
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "Reached idefloppy_open\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	MOD_INC_USE_COUNT;
+	if (drive->usage == 1) {
+		idefloppy_create_start_stop_cmd (&pc, 1);
+		(void) idefloppy_queue_pc_tail (drive, &pc);
+		if (idefloppy_get_capacity (drive)) {
+			drive->usage--;
+			MOD_DEC_USE_COUNT;
+			return -EIO;
+		}
+		set_bit (IDEFLOPPY_MEDIA_CHANGED, &floppy->flags);
+		idefloppy_create_prevent_cmd (&pc, 1);
+		(void) idefloppy_queue_pc_tail (drive, &pc);
+		check_disk_change(inode->i_rdev);
+	}
+	return 0;
+}
+
+void idefloppy_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+	idefloppy_pc_t pc;
+	
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "Reached idefloppy_release\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	if (!drive->usage) {
+		invalidate_buffers (inode->i_rdev);
+		idefloppy_create_prevent_cmd (&pc, 0);
+		(void) idefloppy_queue_pc_tail (drive, &pc);
+	}
+	MOD_DEC_USE_COUNT;
+}
+
+/*
+ *	Check media change. Use a simple algorithm for now.
+ */
+int idefloppy_media_change (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+	
+	return clear_bit (IDEFLOPPY_MEDIA_CHANGED, &floppy->flags);
+}
+
+/*
+ *	Return the current floppy capacity to ide.c.
+ */
+unsigned long idefloppy_capacity (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+	unsigned long capacity = floppy->blocks * floppy->bs_factor;
+
+	return capacity;
+}
+
+/*
+ *	idefloppy_identify_device checks if we can support a drive,
+ *	based on the ATAPI IDENTIFY command results.
+ */
+int idefloppy_identify_device (ide_drive_t *drive,struct hd_driveid *id)
+{
+	struct idefloppy_id_gcw gcw;
+	idefloppy_floppy_t *floppy;
+#if IDEFLOPPY_DEBUG_INFO
+	unsigned short mask,i;
+	char buffer[80];
+#endif /* IDEFLOPPY_DEBUG_INFO */
+
+	*((unsigned short *) &gcw) = id->config;
+
+#if IDEFLOPPY_DEBUG_INFO
+	printk (KERN_INFO "Dumping ATAPI Identify Device floppy parameters\n");
+	switch (gcw.protocol) {
+		case 0: case 1: sprintf (buffer, "ATA");break;
+		case 2:	sprintf (buffer, "ATAPI");break;
+		case 3: sprintf (buffer, "Reserved (Unknown to ide-floppy)");break;
+	}
+	printk (KERN_INFO "Protocol Type: %s\n", buffer);
+	switch (gcw.device_type) {
+		case 0: sprintf (buffer, "Direct-access Device");break;
+		case 1: sprintf (buffer, "Streaming Tape Device");break;
+		case 2: case 3: case 4: sprintf (buffer, "Reserved");break;
+		case 5: sprintf (buffer, "CD-ROM Device");break;
+		case 6: sprintf (buffer, "Reserved");
+		case 7: sprintf (buffer, "Optical memory Device");break;
+		case 0x1f: sprintf (buffer, "Unknown or no Device type");break;
+		default: sprintf (buffer, "Reserved");
+	}
+	printk (KERN_INFO "Device Type: %x - %s\n", gcw.device_type, buffer);
+	printk (KERN_INFO "Removable: %s\n",gcw.removable ? "Yes":"No");	
+	switch (gcw.drq_type) {
+		case 0: sprintf (buffer, "Microprocessor DRQ");break;
+		case 1: sprintf (buffer, "Interrupt DRQ");break;
+		case 2: sprintf (buffer, "Accelerated DRQ");break;
+		case 3: sprintf (buffer, "Reserved");break;
+	}
+	printk (KERN_INFO "Command Packet DRQ Type: %s\n", buffer);
+	switch (gcw.packet_size) {
+		case 0: sprintf (buffer, "12 bytes");break;
+		case 1: sprintf (buffer, "16 bytes");break;
+		default: sprintf (buffer, "Reserved");break;
+	}
+	printk (KERN_INFO "Command Packet Size: %s\n", buffer);
+	printk (KERN_INFO "Model: %s\n",id->model);
+	printk (KERN_INFO "Firmware Revision: %s\n",id->fw_rev);
+	printk (KERN_INFO "Serial Number: %s\n",id->serial_no);
+	printk (KERN_INFO "Write buffer size(?): %d bytes\n",id->buf_size*512);
+	printk (KERN_INFO "DMA: %s",id->capability & 0x01 ? "Yes\n":"No\n");
+	printk (KERN_INFO "LBA: %s",id->capability & 0x02 ? "Yes\n":"No\n");
+	printk (KERN_INFO "IORDY can be disabled: %s",id->capability & 0x04 ? "Yes\n":"No\n");
+	printk (KERN_INFO "IORDY supported: %s",id->capability & 0x08 ? "Yes\n":"Unknown\n");
+	printk (KERN_INFO "ATAPI overlap supported: %s",id->capability & 0x20 ? "Yes\n":"No\n");
+	printk (KERN_INFO "PIO Cycle Timing Category: %d\n",id->tPIO);
+	printk (KERN_INFO "DMA Cycle Timing Category: %d\n",id->tDMA);
+	printk (KERN_INFO "Single Word DMA supported modes:\n");
+	for (i=0,mask=1;i<8;i++,mask=mask << 1) {
+		if (id->dma_1word & mask)
+			printk (KERN_INFO "   Mode %d%s\n", i, (id->dma_1word & (mask << 8)) ? " (active)" : "");
+	}
+	printk (KERN_INFO "Multi Word DMA supported modes:\n");
+	for (i=0,mask=1;i<8;i++,mask=mask << 1) {
+		if (id->dma_mword & mask)
+			printk (KERN_INFO "   Mode %d%s\n", i, (id->dma_mword & (mask << 8)) ? " (active)" : "");
+	}
+	if (id->field_valid & 0x0002) {
+		printk (KERN_INFO "Enhanced PIO Modes: %s\n",id->eide_pio_modes & 1 ? "Mode 3":"None");
+		if (id->eide_dma_min == 0)
+			sprintf (buffer, "Not supported");
+		else
+			sprintf (buffer, "%d ns",id->eide_dma_min);
+		printk (KERN_INFO "Minimum Multi-word DMA cycle per word: %s\n", buffer);
+		if (id->eide_dma_time == 0)
+			sprintf (buffer, "Not supported");
+		else
+			sprintf (buffer, "%d ns",id->eide_dma_time);
+		printk (KERN_INFO "Manufacturer\'s Recommended Multi-word cycle: %s\n", buffer);
+		if (id->eide_pio == 0)
+			sprintf (buffer, "Not supported");
+		else
+			sprintf (buffer, "%d ns",id->eide_pio);
+		printk (KERN_INFO "Minimum PIO cycle without IORDY: %s\n", buffer);
+		if (id->eide_pio_iordy == 0)
+			sprintf (buffer, "Not supported");
+		else
+			sprintf (buffer, "%d ns",id->eide_pio_iordy);
+		printk (KERN_INFO "Minimum PIO cycle with IORDY: %s\n", buffer);
+	} else
+		printk (KERN_INFO "According to the device, fields 64-70 are not valid.\n");
+#endif /* IDEFLOPPY_DEBUG_INFO */
+
+	if (gcw.protocol != 2)
+		printk (KERN_ERR "ide-floppy: Protocol is not ATAPI\n");
+	else if (gcw.device_type != 0)
+		printk (KERN_ERR "ide-floppy: Device type is not set to floppy\n");
+	else if (!gcw.removable)
+		printk (KERN_ERR "ide-floppy: The removable flag is not set\n");
+	else if (gcw.drq_type == 3)
+		printk (KERN_ERR "ide-floppy: Sorry, DRQ type %d not supported\n", gcw.drq_type);
+	else if (gcw.packet_size != 0)
+		printk (KERN_ERR "ide-floppy: Packet size is not 12 bytes long\n");
+	else if ((floppy = (idefloppy_floppy_t *) kmalloc (sizeof (idefloppy_floppy_t), GFP_KERNEL)) == NULL)
+		printk (KERN_ERR "ide-floppy: %s: Can't allocate a floppy structure\n", drive->name);
+	else {
+		drive->floppy = floppy;	
+		return 1;
+	}
+	printk (KERN_ERR "ide-floppy: %s: not supported by this version of ide-floppy\n", drive->name);
+	return 0;
+}
+
+/*
+ *	idefloppy_get_capabilities asks the floppy about its various
+ *	parameters.
+ */
+static void idefloppy_get_capabilities (ide_drive_t *drive)
+{
+	idefloppy_pc_t pc;
+	idefloppy_mode_parameter_header_t *header;
+	idefloppy_capabilities_page_t *capabilities;
+	
+	idefloppy_create_mode_sense_cmd (&pc, IDEFLOPPY_CAPABILITIES_PAGE, MODE_SENSE_CURRENT);
+	if (idefloppy_queue_pc_tail (drive,&pc)) {
+		printk (KERN_ERR "ide-floppy: Can't get drive capabilities\n");
+		return;
+	}
+	header = (idefloppy_mode_parameter_header_t *) pc.buffer;
+	capabilities = (idefloppy_capabilities_page_t *) (header + 1);
+
+	if (!capabilities->sflp)
+		printk (KERN_INFO "%s: Warning - system floppy device bit is not set\n", drive->name);
+
+#if IDEFLOPPY_DEBUG_INFO
+	printk (KERN_INFO "Dumping the results of the MODE SENSE packet command\n");
+	printk (KERN_INFO "Mode Parameter Header:\n");
+	printk (KERN_INFO "Mode Data Length - %d\n",header->mode_data_length);
+	printk (KERN_INFO "Medium Type - %d\n",header->medium_type);
+	printk (KERN_INFO "WP - %d\n",header->wp);
+
+	printk (KERN_INFO "Capabilities Page:\n");
+	printk (KERN_INFO "Page code - %d\n",capabilities->page_code);
+	printk (KERN_INFO "Page length - %d\n",capabilities->page_length);
+	printk (KERN_INFO "PS - %d\n",capabilities->ps);
+	printk (KERN_INFO "System Floppy Type device - %s\n",capabilities->sflp ? "Yes":"No");
+	printk (KERN_INFO "Supports Reporting progress of Format - %s\n",capabilities->srfp ? "Yes":"No");
+	printk (KERN_INFO "Non CD Optical device - %s\n",capabilities->ncd ? "Yes":"No");
+	printk (KERN_INFO "Multiple LUN support - %s\n",capabilities->sml ? "Yes":"No");
+	printk (KERN_INFO "Total LUN supported - %s\n",capabilities->tlun ? "Yes":"No");
+#endif /* IDEFLOPPY_DEBUG_INFO */
+}
+
+/*
+ *	Driver initialization.
+ */
+void idefloppy_setup (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+	struct idefloppy_id_gcw gcw;
+
+	*((unsigned short *) &gcw) = drive->id->config;
+	drive->ready_stat = 0;
+	memset (floppy, 0, sizeof (idefloppy_floppy_t));
+	floppy->drive = drive;
+	floppy->pc = floppy->pc_stack;
+	if (gcw.drq_type == 1)
+		set_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags);
+
+	idefloppy_get_capabilities (drive);
+	(void) idefloppy_get_capacity (drive);
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v2.0.30/linux/drivers/block/ide.c	Tue Nov 19 06:21:06 1996
+++ linux/drivers/block/ide.c	Mon Aug  4 11:45:55 1997
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide.c	Version 5.52  Sep  24, 1996
+ *  linux/drivers/block/ide.c	Version 5.53  Jun  24, 1997
  *
  *  Copyright (C) 1994-1996  Linus Torvalds & authors (see below)
  */
@@ -261,6 +261,17 @@
  *			change delay_10ms() to delay_50ms() to fix problems
  * Version 5.52		fix incorrect invalidation of removable devices
  *			add "hdx=slow" command line option
+ * Version 5.53		add ATAPI floppy drive support
+ *			change default media for type 0 to floppy
+ *			add support for Exabyte Nest
+ *			add missing set_blocksize() in revalidate_disk()
+ *			handle bad status bit sequencing in ide_wait_stat()
+ *			support partition table translations with 255 heads
+ *			probe all interfaces by default
+ *			add probe for the i82371AB chipset
+ *			acknowledge media change on removable drives
+ *			add work-around for BMI drives
+ *			remove "LBA" from boot messages
  *
  *  Some additional driver compile-time options are in ide.h
  *
@@ -367,7 +378,6 @@
 
 	/* fill in any non-zero initial values */
 	hwif->index     = index;
-	hwif->noprobe	= (index > 1);
 	hwif->io_base	= default_io_base[index];
 	hwif->ctl_port	= hwif->io_base ? hwif->io_base+0x206 : 0x000;
 #ifdef CONFIG_BLK_DEV_HD
@@ -536,6 +546,29 @@
 }
 
 /*
+ * The following routines are mainly used by the ATAPI drivers.
+ *
+ * These routines will round up any request for an odd number of bytes,
+ * so if an odd bytecount is specified, be sure that there's at least one
+ * extra byte allocated for the buffer.
+ */
+void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
+{
+	++bytecount;
+	ide_input_data (drive, buffer, bytecount / 4);
+	if ((bytecount & 0x03) >= 2)
+		insw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
+}
+
+void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
+{
+	++bytecount;
+	ide_output_data (drive, buffer, bytecount / 4);
+	if ((bytecount & 0x03) >= 2)
+		outsw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
+}
+
+/*
  * This should get invoked any time we exit the driver to
  * wait for an interrupt response from a drive.  handler() points
  * at the appropriate code to handle the next interrupt, and a
@@ -593,6 +626,10 @@
 
 	if (!drive->present)
 		return 0;
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+	if (drive->media == ide_floppy)
+		return idefloppy_capacity(drive);
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
 	if (drive->media != ide_disk)
 		return 0x7fffffff;	/* cdrom or tape */
 	drive->select.b.lba = 0;
@@ -625,8 +662,13 @@
 		if (drive->present && drive->media == ide_tape)
 			idetape_setup(drive);
 #endif /* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+		if (drive->present && drive->media == ide_floppy)
+			idefloppy_setup(drive);
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
 		drive->part[0].nr_sects = current_capacity(drive);
-		if (!drive->present || drive->media != ide_disk) {
+		if (!drive->present || (drive->media != ide_disk && drive->media != ide_floppy) ||
+		    !drive->part[0].nr_sects) {
 			drive->part[0].start_sect = -1; /* skip partition check */
 		}
 	}
@@ -1005,6 +1047,8 @@
 				rq->errors = ERROR_MAX;
 			else if (err & TRK0_ERR)	/* help it find track zero */
 				rq->errors |= ERROR_RECAL;
+			else if (err & MC_ERR)
+				drive->special.b.mc = 1;
 		}
 		if ((stat & DRQ_STAT) && rq->cmd != WRITE)
 			try_to_flush_leftover_data(drive);
@@ -1017,9 +1061,20 @@
 		if (drive->media == ide_tape) {
 			rq->errors = 0;
 			idetape_end_request(0, HWGROUP(drive));
-		}
-		else
+		} else
 #endif /* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+		if (drive->media == ide_floppy) {
+			rq->errors = 0;
+			idefloppy_end_request(0, HWGROUP(drive));
+		} else
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+#ifdef CONFIG_BLK_DEV_IDESCSI
+		if (drive->media == ide_scsi) {
+			rq->errors = 0;
+			idescsi_end_request(0, HWGROUP(drive));
+		} else
+#endif /* CONFIG_BLK_DEV_IDESCSI */
  		ide_end_request(0, HWGROUP(drive));
 	}
 	else {
@@ -1230,6 +1285,19 @@
 }
 
 /*
+ * mc_intr() is invoked on completion of a WIN_ACKMC cmd.
+ */
+static void mc_intr (ide_drive_t *drive)
+{
+	byte stat = GET_STAT();
+
+	sti();
+	if (!OK_STAT(stat,READY_STAT,BAD_STAT))
+		ide_error(drive, "mc_intr", stat);
+	drive->special.b.mc = 0;
+}
+
+/*
  * drive_cmd_intr() is invoked on completion of a special DRIVE_CMD.
  */
 static void drive_cmd_intr (ide_drive_t *drive)
@@ -1265,7 +1333,7 @@
 #endif
 	if (s->b.set_geometry) {
 		s->b.set_geometry = 0;
-		if (drive->media == ide_disk) {
+		if (drive->media == ide_disk && !drive->no_geom) {
 			OUT_BYTE(drive->sect,IDE_SECTOR_REG);
 			OUT_BYTE(drive->cyl,IDE_LCYL_REG);
 			OUT_BYTE(drive->cyl>>8,IDE_HCYL_REG);
@@ -1291,6 +1359,10 @@
 				ide_cmd(drive, WIN_SETMULT, drive->mult_req, &set_multmode_intr);
 		} else
 			drive->mult_req = 0;
+	} else if (s->b.mc) {
+		s->b.mc = 0;
+		if (drive->media == ide_disk && !IS_PROMISE_DRIVE)
+			ide_cmd(drive, WIN_ACKMC, drive->sect, &mc_intr);
 	} else if (s->all) {
 		int special = s->all;
 		s->all = 0;
@@ -1314,27 +1386,24 @@
 	byte stat;
 	unsigned long flags;
 
-test:
-	udelay(1);	/* spec allows drive 400ns to change "BUSY" */
-	if (OK_STAT((stat = GET_STAT()), good, bad))
-		return 0;	/* fast exit for most frequent case */
-	if (!(stat & BUSY_STAT)) {
-		ide_error(drive, "status error", stat);
-		return 1;
-	}
-
-	save_flags(flags);
-	sti();
-	timeout += jiffies;
-	do {
-		if (!((stat = GET_STAT()) & BUSY_STAT)) {
-			restore_flags(flags);
-			goto test;
+	udelay(1);	/* spec allows drive 400ns to assert "BUSY" */
+	if ((stat = GET_STAT()) & BUSY_STAT) {
+		save_flags(flags);
+		sti();
+		timeout += jiffies;
+		while ((stat = GET_STAT()) & BUSY_STAT) {
+			if (jiffies > timeout) {
+				restore_flags(flags);
+				ide_error(drive, "status timeout", stat);
+				return 1;
+			}
 		}
-	} while (jiffies <= timeout);
-
-	restore_flags(flags);
-	ide_error(drive, "status timeout", GET_STAT());
+		restore_flags(flags);
+	}
+	udelay(1);	/* allow status to settle, then read it again */
+	if (OK_STAT((stat = GET_STAT()), good, bad))
+		return 0;
+	ide_error(drive, "status error", stat);
 	return 1;
 }
 
@@ -1532,6 +1601,16 @@
 				idetape_do_request (drive, rq, block);
 				return;
 #endif /* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+			case ide_floppy:
+				idefloppy_do_request (drive, rq, block);
+				return;
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+#ifdef CONFIG_BLK_DEV_IDESCSI
+			case ide_scsi:
+				idescsi_do_request (drive, rq, block);
+				return;
+#endif /* CONFIG_BLK_DEV_IDESCSI */
 
 			default:
 				printk("%s: media type %d not supported\n",
@@ -1588,6 +1667,7 @@
 				if (rq != NULL && rq->rq_status != RQ_INACTIVE)
 					goto got_rq;
 			} while ((hwif = hwif->next) != hwgroup->next_hwif);
+			hwgroup->active = 0;
 			return;		/* no work left for this hwgroup */
 		}
 	got_rq:	
@@ -1612,6 +1692,7 @@
 	if (hwgroup->handler == NULL) {
 		ide_hwif_t *hgif = hwgroup->hwif;
 		ide_hwif_t *hwif = hgif;
+		hwgroup->active = 1;
 		do {
 			disable_irq(hwif->irq);
 		} while ((hwif = hwif->next) != hgif);
@@ -1859,13 +1940,8 @@
 	if (cur_rq == NULL || action == ide_preempt) {
 		rq->next = cur_rq;
 		bdev->current_request = rq;
-		if (action == ide_preempt) {
+		if (action == ide_preempt)
 			HWGROUP(drive)->rq = NULL;
-		} else
-		if (HWGROUP(drive)->rq == NULL) {  /* is this necessary (?) */
-			bdev->request_fn();
-			cli();
-		}
 	} else {
 		if (action == ide_wait || action == ide_end) {
 			while (cur_rq->next != NULL)	/* find end of list */
@@ -1874,6 +1950,10 @@
 		rq->next = cur_rq->next;
 		cur_rq->next = rq;
 	}
+	if (!HWGROUP(drive)->active) {
+		do_hwgroup_request(HWGROUP(drive));
+		cli();
+	}
 	if (action == ide_wait  && rq->rq_status != RQ_INACTIVE)
 		down(&sem);	/* wait for it to be serviced */
 	restore_flags(flags);
@@ -1901,6 +1981,14 @@
 	if (drive->media == ide_tape)
 		return idetape_blkdev_open (inode, filp, drive);
 #endif	/* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+	if (drive->media == ide_floppy)
+		return idefloppy_open (inode, filp, drive);
+#endif	/* CONFIG_BLK_DEV_IDEFLOPPY */
+#ifdef CONFIG_BLK_DEV_IDESCSI
+	if (drive->media == ide_scsi)
+		return idescsi_open (inode, filp, drive);
+#endif	/* CONFIG_BLK_DEV_IDESCSI */
 	if (drive->removable && drive->usage == 1) {
 		byte door_lock[] = {WIN_DOORLOCK,0,0,0};
 		struct request rq;
@@ -1940,6 +2028,18 @@
 			return;
 		}
 #endif	/* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+		if (drive->media == ide_floppy) {
+			idefloppy_release (inode, file, drive);
+			return;
+		}
+#endif	/* CONFIG_BLK_DEV_IDEFLOPPY */
+#ifdef CONFIG_BLK_DEV_IDESCSI
+		if (drive->media == ide_scsi) {
+			idescsi_ide_release (inode, file, drive);
+			return;
+		}
+#endif	/* CONFIG_BLK_DEV_IDESCSI */
 		if (drive->removable && !drive->usage) {
 			byte door_unlock[] = {WIN_DOORUNLOCK,0,0,0};
 			struct request rq;
@@ -1985,13 +2085,14 @@
 			fsync_dev          (devp);
 			invalidate_inodes  (devp);
 			invalidate_buffers (devp);
+			set_blocksize(devp, 1024);
 		}
 		drive->part[p].start_sect = 0;
 		drive->part[p].nr_sects   = 0;
 	};
 
 	drive->part[0].nr_sects = current_capacity(drive);
-	if (drive->media != ide_disk)
+	if ((drive->media != ide_disk && drive->media != ide_floppy) || !drive->part[0].nr_sects)
 		drive->part[0].start_sect = -1;
 	resetup_one_dev(HWIF(drive)->gd, drive->select.b.unit);
 
@@ -2029,7 +2130,7 @@
 		case HDIO_GETGEO:
 		{
 			struct hd_geometry *loc = (struct hd_geometry *) arg;
-			if (!loc || drive->media != ide_disk) return -EINVAL;
+			if (!loc || (drive->media != ide_disk && drive->media != ide_floppy)) return -EINVAL;
 			err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
 			if (err) return err;
 			put_user(drive->bios_head, (byte *) &loc->heads);
@@ -2223,6 +2324,14 @@
 			if (drive->media == ide_tape)
 				return idetape_blkdev_ioctl(drive, inode, file, cmd, arg);
 #endif /* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+			if (drive->media == ide_floppy)
+				return idefloppy_ioctl(drive, inode, file, cmd, arg);
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+#ifdef CONFIG_BLK_DEV_IDESCSI
+			if (drive->media == ide_scsi)
+				return idescsi_ioctl(drive, inode, file, cmd, arg);
+#endif /* CONFIG_BLK_DEV_IDESCSI */
 			return -EPERM;
 	}
 }
@@ -2237,6 +2346,10 @@
 	if (drive->media == ide_cdrom)
 		return ide_cdrom_check_media_change (drive);
 #endif	/* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+	if (drive->media == ide_floppy)
+		return idefloppy_media_change (drive);
+#endif	/* CONFIG_BLK_DEV_IDEFLOPPY */
 	if (drive->removable) /* for disks */
 		return 1;	/* always assume it was changed */
 	return 0;
@@ -2307,6 +2420,9 @@
 	ide_fixstring (id->fw_rev,    sizeof(id->fw_rev),    bswap);
 	ide_fixstring (id->serial_no, sizeof(id->serial_no), bswap);
 
+	if (strstr(id->model, "E X A B Y T E N E S T"))
+		return;
+
 #ifdef CONFIG_BLK_DEV_IDEATAPI
 	/*
 	 * Check for an ATAPI device
@@ -2322,7 +2438,22 @@
 		}
 #endif /* CONFIG_BLK_DEV_PROMISE */
 		switch (type) {
-			case 0:		/* Early cdrom models used zero */
+			case 0:
+				if (!strstr(id->model, "oppy") && !strstr(id->model, "poyp") && !strstr(id->model, "ZIP"))
+					printk("cdrom or floppy?, assuming ");
+				if (drive->media != ide_cdrom) {
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+					printk("FLOPPY drive\n");
+					drive->media = ide_floppy;
+					if (idefloppy_identify_device(drive, id))
+						drive->present = 1;
+					return;
+#else
+					printk("FLOPPY ");
+					break;
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+				}
+				/* Early cdrom models used zero */
 			case 5:
 #ifdef CONFIG_BLK_DEV_IDECD
 				printk ("CDROM drive\n");
@@ -2361,8 +2492,15 @@
 				printk("Type %d - Unknown device\n", type);
 				return;
 		}
+#ifdef CONFIG_BLK_DEV_IDESCSI
+		printk("drive - enabling SCSI emulation\n");
+		drive->media = ide_scsi;
+		drive->present = 1;
+		idescsi_setup(drive);
+#else
 		drive->present = 0;
 		printk("- not supported by this kernel\n");
+#endif /* CONFIG_BLK_DEV_IDESCSI */
 		return;
 	}
 #endif /* CONFIG_BLK_DEV_IDEATAPI */
@@ -2381,7 +2519,7 @@
 		    return;
 		}
 	}
-	
+
 	drive->media = ide_disk;
 	/* Extract geometry if we did not already have one for the drive */
 	if (!drive->present) {
@@ -2431,9 +2569,13 @@
 
 	(void) current_capacity (drive); /* initialize LBA selection */
 
-	printk ("%s: %.40s, %ldMB w/%dkB Cache, %sCHS=%d/%d/%d",
+	if (!strncmp(id->model, "BMI ", 4) &&
+	    strstr(id->model, " ENHANCED IDE ") &&
+	    drive->select.b.lba)
+		drive->no_geom = 1;
+
+	printk ("%s: %.40s, %ldMB w/%dkB Cache, CHS=%d/%d/%d",
 	 drive->name, id->model, current_capacity(drive)/2048L, id->buf_size/2,
-	 drive->select.b.lba ? "LBA, " : "",
 	 drive->bios_cyl, drive->bios_head, drive->bios_sect);
 
 	drive->mult_count = 0;
@@ -2607,6 +2749,34 @@
 	return rc;
 }
 
+static void enable_nest (ide_drive_t *drive)
+{
+	unsigned long timeout;
+
+	printk("%s: enabling %s -- ", HWIF(drive)->name, drive->id->model);
+	SELECT_DRIVE(HWIF(drive), drive);
+	delay_50ms();
+	OUT_BYTE(EXABYTE_ENABLE_NEST, IDE_COMMAND_REG);
+	timeout = jiffies + WAIT_WORSTCASE;
+	do {
+		if (jiffies > timeout) {
+			printk("failed (timeout)\n");
+			return;
+		}
+		delay_50ms();
+	} while (GET_STAT() & BUSY_STAT);
+	delay_50ms();
+	if (!OK_STAT(GET_STAT(), 0, BAD_STAT))
+		printk("failed (status = 0x%02x)\n", GET_STAT());
+	else
+		printk("success\n");
+	if (do_probe(drive, WIN_IDENTIFY) >= 2) { /* if !(success||timed-out) */
+#ifdef CONFIG_BLK_DEV_IDEATAPI
+		(void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */
+#endif	/* CONFIG_BLK_DEV_IDEATAPI */
+	}
+}
+
 /*
  * probe_for_drive() tests for existence of a given drive using do_probe().
  *
@@ -2622,6 +2792,8 @@
 		(void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */
 #endif	/* CONFIG_BLK_DEV_IDEATAPI */
 	}
+	if (drive->id && strstr(drive->id->model, "E X A B Y T E N E S T"))
+		enable_nest(drive);
 	if (!drive->present)
 		return 0;			/* drive not found */
 	if (drive->id == NULL) {		/* identification failed? */
@@ -3250,6 +3422,7 @@
 		else
 			hwgroup->drive = &hwif->drives[1];
 		hwgroup->poll_timeout = 0;
+		hwgroup->active = 0;
 		init_timer(&hwgroup->timer);
 		hwgroup->timer.function = &timer_expiry;
 		hwgroup->timer.data = (unsigned long) hwgroup;
@@ -3353,6 +3526,7 @@
 		 */
 		ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371_0, &ide_init_triton, 1);
 		ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, &ide_init_triton, 0);
+		ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, &ide_init_triton, 0);
 #endif /* CONFIG_BLK_DEV_TRITON */
 	}
 #endif /* CONFIG_PCI */
diff -u --recursive --new-file v2.0.30/linux/drivers/block/ide.h linux/drivers/block/ide.h
--- v2.0.30/linux/drivers/block/ide.h	Tue Mar 11 15:39:13 1997
+++ linux/drivers/block/ide.h	Sat Aug 23 10:55:01 1997
@@ -51,7 +51,8 @@
 #endif
 #endif  /* CONFIG_BLK_DEV_CMD640 */
 
-#if defined(CONFIG_BLK_DEV_IDECD) || defined(CONFIG_BLK_DEV_IDETAPE)
+#if	defined(CONFIG_BLK_DEV_IDECD) || defined(CONFIG_BLK_DEV_IDETAPE) || \
+	defined(CONFIG_BLK_DEV_IDEFLOPPY) || defined(CONFIG_BLK_DEV_IDESCSI)
 #define CONFIG_BLK_DEV_IDEATAPI 1
 #endif
 
@@ -110,6 +111,9 @@
 #define IDE_FEATURE_REG		IDE_ERROR_REG
 #define IDE_COMMAND_REG		IDE_STATUS_REG
 #define IDE_ALTSTATUS_REG	IDE_CONTROL_REG
+#define IDE_IREASON_REG		IDE_NSECTOR_REG
+#define IDE_BCOUNTL_REG		IDE_LCYL_REG
+#define IDE_BCOUNTH_REG		IDE_HCYL_REG
 
 #ifdef REALLY_FAST_IO
 #define OUT_BYTE(b,p)		outb((b),(p))
@@ -126,7 +130,7 @@
 #define BAD_W_STAT		(BAD_R_STAT  | WRERR_STAT)
 #define BAD_STAT		(BAD_R_STAT  | DRQ_STAT)
 #define DRIVE_READY		(READY_STAT  | SEEK_STAT)
-#define DATA_READY		(DRIVE_READY | DRQ_STAT)
+#define DATA_READY		(DRQ_STAT)
 
 /*
  * Some more useful definitions
@@ -285,6 +289,8 @@
 	/* The result of the last successful request sense command
 	   on this device. */
 	struct atapi_request_sense sense_data;
+
+	int max_sectors;
 };
 
 #endif /* CONFIG_BLK_DEV_IDECD */
@@ -293,7 +299,7 @@
  * Now for the data we need to maintain per-drive:  ide_drive_t
  */
 
-typedef enum {ide_disk, ide_cdrom, ide_tape} ide_media_t;
+typedef enum {ide_disk, ide_cdrom, ide_tape, ide_floppy, ide_scsi} ide_media_t;
 
 typedef union {
 	unsigned all			: 8;	/* all of the bits together */
@@ -302,7 +308,8 @@
 		unsigned recalibrate	: 1;	/* seek to cyl 0      */
 		unsigned set_multmode	: 1;	/* set multmode count */
 		unsigned set_tune	: 1;	/* tune interface for drive */
-		unsigned reserved	: 4;	/* unused */
+		unsigned mc		: 1;	/* acknowledge media change */
+		unsigned reserved	: 3;	/* unused */
 		} b;
 	} special_t;
 
@@ -335,7 +342,8 @@
 #if FAKE_FDISK_FOR_EZDRIVE
 	unsigned remap_0_to_1	: 1;	/* flag: partitioned with ezdrive */
 #endif /* FAKE_FDISK_FOR_EZDRIVE */
-	ide_media_t	media;		/* disk, cdrom, tape */
+	unsigned no_geom	: 1;	/* flag: do not set geometry */
+	ide_media_t	media;		/* disk, cdrom, tape, floppy */
 	select_t	select;		/* basic drive/head select reg value */
 	byte		ctl;		/* "normal" value for IDE_CONTROL_REG */
 	byte		ready_stat;	/* min status value for drive ready */
@@ -363,6 +371,12 @@
 #ifdef CONFIG_BLK_DEV_IDETAPE
 	idetape_tape_t	tape;		/* for ide-tape.c */
 #endif /* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+	void *floppy;			/* for ide-floppy.c */
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+#ifdef CONFIG_BLK_DEV_IDESCSI
+	void *scsi;			/* for ide-scsi.c */
+#endif /* CONFIG_BLK_DEV_IDESCSI */
 	} ide_drive_t;
 
 /*
@@ -465,6 +479,7 @@
 	struct timer_list	timer;	/* failsafe timer */
 	struct request		wrq;	/* local copy of current write rq */
 	unsigned long		poll_timeout;	/* timeout value during long polls */
+	int			active; /* set when servicing requests */
 	} ide_hwgroup_t;
 
 /*
@@ -503,6 +518,12 @@
 void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount);
 
 /*
+ * This is used for (nearly) all ATAPI data transfers from/to the IDE interface
+ */
+void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount);
+void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount);
+
+/*
  * This is used on exit from the driver, to designate the next irq handler
  * and also to start the safety timer.
  */
@@ -692,6 +713,28 @@
 void idetape_register_chrdev (void);
 
 #endif /* CONFIG_BLK_DEV_IDETAPE */
+
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+int idefloppy_identify_device (ide_drive_t *drive,struct hd_driveid *id);
+void idefloppy_setup (ide_drive_t *drive);
+void idefloppy_do_request (ide_drive_t *drive, struct request *rq, unsigned long block);
+void idefloppy_end_request (byte uptodate, ide_hwgroup_t *hwgroup);
+int idefloppy_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
+			unsigned int cmd, unsigned long arg);
+int idefloppy_open (struct inode *inode, struct file *filp, ide_drive_t *drive);
+void idefloppy_release (struct inode *inode, struct file *filp, ide_drive_t *drive);
+int idefloppy_media_change (ide_drive_t *drive);
+unsigned long idefloppy_capacity (ide_drive_t *drive);
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+
+#ifdef CONFIG_BLK_DEV_IDESCSI
+void idescsi_setup (ide_drive_t *drive);
+void idescsi_do_request (ide_drive_t *drive, struct request *rq, unsigned long block);
+void idescsi_end_request (byte uptodate, ide_hwgroup_t *hwgroup);
+int idescsi_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+int idescsi_open (struct inode *inode, struct file *filp, ide_drive_t *drive);
+void idescsi_ide_release (struct inode *inode, struct file *filp, ide_drive_t *drive);
+#endif /* CONFIG_BLK_DEV_IDESCSI */
 
 #ifdef CONFIG_BLK_DEV_TRITON
 void ide_init_triton (byte, byte);
diff -u --recursive --new-file v2.0.30/linux/drivers/block/loop.c linux/drivers/block/loop.c
--- v2.0.30/linux/drivers/block/loop.c	Mon Jul 15 03:47:39 1996
+++ linux/drivers/block/loop.c	Tue Aug 12 13:06:54 1997
@@ -12,6 +12,8 @@
  * Modularized and updated for 1.1.16 kernel - Mitch Dsouza 28th May 1994
  *
  * Adapted for 1.3.59 kernel - Andries Brouwer, 1 Feb 1996
+ *
+ * Fixed do_loop_request() re-entrancy - <Vincent.Renardias@waw.com> Mar 20, 1997
  */
 
 #include <linux/module.h>
@@ -181,12 +183,15 @@
 	char	*dest_addr;
 	struct loop_device *lo;
 	struct buffer_head *bh;
+	struct request *current_request;
 
 repeat:
 	INIT_REQUEST;
-	if (MINOR(CURRENT->rq_dev) >= MAX_LOOP)
+	current_request=CURRENT;
+	CURRENT=current_request->next;
+	if (MINOR(current_request->rq_dev) >= MAX_LOOP)
 		goto error_out;
-	lo = &loop_dev[MINOR(CURRENT->rq_dev)];
+	lo = &loop_dev[MINOR(current_request->rq_dev)];
 	if (!lo->lo_inode || !lo->transfer)
 		goto error_out;
 
@@ -197,14 +202,14 @@
 	      blksize = BLOCK_SIZE;
 	}
 
-	dest_addr = CURRENT->buffer;
+	dest_addr = current_request->buffer;
 	
 	if (blksize < 512) {
-		block = CURRENT->sector * (512/blksize);
+		block = current_request->sector * (512/blksize);
 		offset = 0;
 	} else {
-		block = CURRENT->sector / (blksize >> 9);
-		offset = (CURRENT->sector % (blksize >> 9)) << 9;
+		block = current_request->sector / (blksize >> 9);
+		offset = (current_request->sector % (blksize >> 9)) << 9;
 	}
 	block += lo->lo_offset / blksize;
 	offset += lo->lo_offset % blksize;
@@ -212,13 +217,13 @@
 		block++;
 		offset -= blksize;
 	}
-	len = CURRENT->current_nr_sectors << 9;
+	len = current_request->current_nr_sectors << 9;
 
-	if (CURRENT->cmd == WRITE) {
+	if (current_request->cmd == WRITE) {
 		if (lo->lo_flags & LO_FLAGS_READ_ONLY)
 			goto error_out;
-	} else if (CURRENT->cmd != READ) {
-		printk("unknown loop device command (%d)?!?", CURRENT->cmd);
+	} else if (current_request->cmd != READ) {
+		printk("unknown loop device command (%d)?!?", current_request->cmd);
 		goto error_out;
 	}
 	while (len > 0) {
@@ -237,7 +242,7 @@
 			       block, blksize);
 			goto error_out;
 		}
-		if (!buffer_uptodate(bh) && ((CURRENT->cmd == READ) ||
+		if (!buffer_uptodate(bh) && ((current_request->cmd == READ) ||
 					(offset || (len < blksize)))) {
 			ll_rw_block(READ, 1, &bh);
 			wait_on_buffer(bh);
@@ -250,13 +255,13 @@
 		if (size > len)
 			size = len;
 			   
-		if ((lo->transfer)(lo, CURRENT->cmd, bh->b_data + offset,
+		if ((lo->transfer)(lo, current_request->cmd, bh->b_data + offset,
 				   dest_addr, size)) {
 			printk("loop: transfer error block %d\n", block);
 			brelse(bh);
 			goto error_out;
 		}
-		if (CURRENT->cmd == WRITE) {
+		if (current_request->cmd == WRITE) {
 			mark_buffer_uptodate(bh, 1);
 			mark_buffer_dirty(bh, 1);
 		}
@@ -266,9 +271,13 @@
 		offset = 0;
 		block++;
 	}
+	current_request->next=CURRENT;
+	CURRENT=current_request;
 	end_request(1);
 	goto repeat;
 error_out:
+    current_request->next=CURRENT;
+	CURRENT=current_request;
 	end_request(0);
 	goto repeat;
 }
diff -u --recursive --new-file v2.0.30/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v2.0.30/linux/drivers/char/Config.in	Tue Apr  8 08:47:45 1997
+++ linux/drivers/char/Config.in	Tue Aug 12 13:06:54 1997
@@ -14,6 +14,10 @@
 fi
 tristate 'SDL RISCom/8 card support' CONFIG_RISCOM8
 tristate 'Parallel printer support' CONFIG_PRINTER
+tristate 'Specialix IO8+ card support' CONFIG_SPECIALIX
+if [ "$CONFIG_SPECIALIX" = "y" -o  "$CONFIG_SPECIALIX" = "m" ]; then
+  bool 'Specialix DTR/RTS pin is RTS' CONFIG_SPECIALIX_RTSCTS
+fi 
 
 
 bool 'Mouse Support (not serial mice)' CONFIG_MOUSE
diff -u --recursive --new-file v2.0.30/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v2.0.30/linux/drivers/char/Makefile	Thu Oct 31 04:31:41 1996
+++ linux/drivers/char/Makefile	Tue Aug 12 13:06:54 1997
@@ -78,6 +78,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_SPECIALIX),y)
+L_OBJS += specialix.o
+else
+  ifeq ($(CONFIG_SPECIALIX),m)
+  M_OBJS += specialix.o
+  endif
+endif
+
 ifeq ($(CONFIG_ATIXL_BUSMOUSE),y)
 M = y
 L_OBJS += atixlmouse.o
diff -u --recursive --new-file v2.0.30/linux/drivers/char/README.scc linux/drivers/char/README.scc
--- v2.0.30/linux/drivers/char/README.scc	Mon Dec 18 21:09:00 1995
+++ linux/drivers/char/README.scc	Tue Aug 12 14:47:16 1997
@@ -1,24 +1,14 @@
-
-You will find subset of the documentation in
+You will find a subset of the documentation in
 
                  linux/Documentation/networking/z8530drv.txt
 
+To use this driver you MUST have the full package "z8530drv-2.4c.dl1bke.tar.gz"
+from either ftp.pspt.fi, sunsite.unc.edu or db0bm.automation.fh-aachen.de. 
+The package includes the utilities necessary to initialize and control the driver.
 
-To use this driver you MUST have the full package from
-
-ftp.ucsd.edu:/hamradio/packet/tcpip/incoming/z8530drv-2.01.dl1bke.real.tar.gz
-
-[
-  if you can't find it there, try:
-  .../tcpip/linux/z8530drv-2.01.dl1bke.tar.gz
-
-]
-
-and various mirrors (i.e. nic.switch.ch)
-
-The package includes the utilities necessary to initialize and
-control the driver.
+Do not try to use the utilities from z8530drv-utils-3.0 as they will not work
+with the 2.4 series of the driver!
 
 Joerg Reuter	ampr-net: dl1bke@db0pra.ampr.org
-		AX-25   : DL1BKE @ DB0ACH.#NRW.DEU.EU
-		Internet: jreuter@lykos.tng.oche.de
+		WWW     : http://www.rat.de/jr
+		Internet: jreuter@poboxes.com
diff -u --recursive --new-file v2.0.30/linux/drivers/char/cd1865.h linux/drivers/char/cd1865.h
--- v2.0.30/linux/drivers/char/cd1865.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/cd1865.h	Tue Aug 12 13:06:54 1997
@@ -0,0 +1,263 @@
+/*
+ *      linux/drivers/char/cd1865.h -- Definitions relating to the CD1865
+ *                          for the Specialix IO8+ multiport serial driver.
+ *
+ *      Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl)
+ *      Copyright (C) 1994-1996  Dmitry Gorodchanin (begemot@bgm.rosprint.net)
+ *
+ *      Specialix pays for the development and support of this driver.
+ *      Please DO contact io8-linux@specialix.co.uk if you require
+ *      support.
+ *
+ *      This driver was developped in the BitWizard linux device
+ *      driver service. If you require a linux device driver for your
+ *      product, please contact devices@BitWizard.nl for a quote.
+ *
+ */
+
+/*
+ * Definitions for Driving CD180/CD1864/CD1865 based eightport serial cards.
+ */
+
+
+/* Values of choice for Interrupt ACKs */
+/* These values are "obligatory" if you use the register based
+ * interrupt acknowledgements. See page 99-101 of V2.0 of the CD1865
+ * databook */
+#define SX_ACK_MINT     0x75    /* goes to PILR1                           */
+#define SX_ACK_TINT     0x76    /* goes to PILR2                           */
+#define SX_ACK_RINT     0x77    /* goes to PILR3                           */
+
+/* Chip ID (is used when chips ar daisy chained.) */
+#define SX_ID           0x10
+
+/* Definitions for Cirrus Logic CL-CD186x 8-port async mux chip */
+ 
+#define CD186x_NCH       8       /* Total number of channels                */
+#define CD186x_TPC       16      /* Ticks per character                     */
+#define CD186x_NFIFO	 8	 /* TX FIFO size                            */
+
+
+/* Global registers */
+
+#define CD186x_GIVR      0x40    /* Global Interrupt Vector Register        */
+#define CD186x_GICR      0x41    /* Global Interrupting Channel Register    */
+#define CD186x_PILR1     0x61    /* Priority Interrupt Level Register 1     */
+#define CD186x_PILR2     0x62    /* Priority Interrupt Level Register 2     */
+#define CD186x_PILR3     0x63    /* Priority Interrupt Level Register 3     */
+#define CD186x_CAR       0x64    /* Channel Access Register                 */
+#define CD186x_SRSR      0x65    /* Channel Access Register                 */
+#define CD186x_GFRCR     0x6b    /* Global Firmware Revision Code Register  */
+#define CD186x_PPRH      0x70    /* Prescaler Period Register High          */
+#define CD186x_PPRL      0x71    /* Prescaler Period Register Low           */
+#define CD186x_RDR       0x78    /* Receiver Data Register                  */
+#define CD186x_RCSR      0x7a    /* Receiver Character Status Register      */
+#define CD186x_TDR       0x7b    /* Transmit Data Register                  */
+#define CD186x_EOIR      0x7f    /* End of Interrupt Register               */
+#define CD186x_MRAR      0x75    /* Modem Request Acknowlege register       */
+#define CD186x_TRAR      0x76    /* Transmit Request Acknowlege register    */
+#define CD186x_RRAR      0x77    /* Recieve Request Acknowlege register     */
+#define CD186x_SRCR      0x66    /* Service Request Configuration register  */
+
+/* Channel Registers */
+
+#define CD186x_CCR       0x01    /* Channel Command Register                */
+#define CD186x_IER       0x02    /* Interrupt Enable Register               */
+#define CD186x_COR1      0x03    /* Channel Option Register 1               */
+#define CD186x_COR2      0x04    /* Channel Option Register 2               */
+#define CD186x_COR3      0x05    /* Channel Option Register 3               */
+#define CD186x_CCSR      0x06    /* Channel Control Status Register         */
+#define CD186x_RDCR      0x07    /* Receive Data Count Register             */
+#define CD186x_SCHR1     0x09    /* Special Character Register 1            */
+#define CD186x_SCHR2     0x0a    /* Special Character Register 2            */
+#define CD186x_SCHR3     0x0b    /* Special Character Register 3            */
+#define CD186x_SCHR4     0x0c    /* Special Character Register 4            */
+#define CD186x_MCOR1     0x10    /* Modem Change Option 1 Register          */
+#define CD186x_MCOR2     0x11    /* Modem Change Option 2 Register          */
+#define CD186x_MCR       0x12    /* Modem Change Register                   */
+#define CD186x_RTPR      0x18    /* Receive Timeout Period Register         */
+#define CD186x_MSVR      0x28    /* Modem Signal Value Register             */
+#define CD186x_MSVRTS    0x29    /* Modem Signal Value Register             */
+#define CD186x_MSVDTR    0x2a    /* Modem Signal Value Register             */
+#define CD186x_RBPRH     0x31    /* Receive Baud Rate Period Register High  */
+#define CD186x_RBPRL     0x32    /* Receive Baud Rate Period Register Low   */
+#define CD186x_TBPRH     0x39    /* Transmit Baud Rate Period Register High */
+#define CD186x_TBPRL     0x3a    /* Transmit Baud Rate Period Register Low  */
+
+
+/* Global Interrupt Vector Register (R/W) */
+
+#define GIVR_ITMASK     0x07     /* Interrupt type mask                     */
+#define  GIVR_IT_MODEM   0x01    /* Modem Signal Change Interrupt           */
+#define  GIVR_IT_TX      0x02    /* Transmit Data Interrupt                 */
+#define  GIVR_IT_RCV     0x03    /* Receive Good Data Interrupt             */
+#define  GIVR_IT_REXC    0x07    /* Receive Exception Interrupt             */
+
+
+/* Global Interrupt Channel Register (R/W) */
+ 
+#define GICR_CHAN       0x1c    /* Channel Number Mask                     */
+#define GICR_CHAN_OFF   2       /* Channel Number shift                    */
+
+
+/* Channel Address Register (R/W) */
+
+#define CAR_CHAN        0x07    /* Channel Number Mask                     */
+#define CAR_A7          0x08    /* A7 Address Extension (unused)           */
+
+
+/* Receive Character Status Register (R/O) */
+
+#define RCSR_TOUT       0x80    /* Rx Timeout                              */
+#define RCSR_SCDET      0x70    /* Special Character Detected Mask         */
+#define  RCSR_NO_SC      0x00   /* No Special Characters Detected          */
+#define  RCSR_SC_1       0x10   /* Special Char 1 (or 1 & 3) Detected      */
+#define  RCSR_SC_2       0x20   /* Special Char 2 (or 2 & 4) Detected      */
+#define  RCSR_SC_3       0x30   /* Special Char 3 Detected                 */
+#define  RCSR_SC_4       0x40   /* Special Char 4 Detected                 */
+#define RCSR_BREAK      0x08    /* Break has been detected                 */
+#define RCSR_PE         0x04    /* Parity Error                            */
+#define RCSR_FE         0x02    /* Frame Error                             */
+#define RCSR_OE         0x01    /* Overrun Error                           */
+
+
+/* Channel Command Register (R/W) (commands in groups can be OR-ed) */
+
+#define CCR_HARDRESET   0x81    /* Reset the chip                          */
+
+#define CCR_SOFTRESET   0x80    /* Soft Channel Reset                      */
+
+#define CCR_CORCHG1     0x42    /* Channel Option Register 1 Changed       */
+#define CCR_CORCHG2     0x44    /* Channel Option Register 2 Changed       */
+#define CCR_CORCHG3     0x48    /* Channel Option Register 3 Changed       */
+
+#define CCR_SSCH1       0x21    /* Send Special Character 1                */
+
+#define CCR_SSCH2       0x22    /* Send Special Character 2                */
+
+#define CCR_SSCH3       0x23    /* Send Special Character 3                */
+
+#define CCR_SSCH4       0x24    /* Send Special Character 4                */
+
+#define CCR_TXEN        0x18    /* Enable Transmitter                      */
+#define CCR_RXEN        0x12    /* Enable Receiver                         */
+
+#define CCR_TXDIS       0x14    /* Disable Transmitter                     */
+#define CCR_RXDIS       0x11    /* Disable Receiver                        */
+
+
+/* Interrupt Enable Register (R/W) */
+
+#define IER_DSR         0x80    /* Enable interrupt on DSR change          */
+#define IER_CD          0x40    /* Enable interrupt on CD change           */
+#define IER_CTS         0x20    /* Enable interrupt on CTS change          */
+#define IER_RXD         0x10    /* Enable interrupt on Receive Data        */
+#define IER_RXSC        0x08    /* Enable interrupt on Receive Spec. Char  */
+#define IER_TXRDY       0x04    /* Enable interrupt on TX FIFO empty       */
+#define IER_TXEMPTY     0x02    /* Enable interrupt on TX completely empty */
+#define IER_RET         0x01    /* Enable interrupt on RX Exc. Timeout     */
+
+
+/* Channel Option Register 1 (R/W) */
+
+#define COR1_ODDP       0x80    /* Odd Parity                              */
+#define COR1_PARMODE    0x60    /* Parity Mode mask                        */
+#define  COR1_NOPAR      0x00   /* No Parity                               */
+#define  COR1_FORCEPAR   0x20   /* Force Parity                            */
+#define  COR1_NORMPAR    0x40   /* Normal Parity                           */
+#define COR1_IGNORE     0x10    /* Ignore Parity on RX                     */
+#define COR1_STOPBITS   0x0c    /* Number of Stop Bits                     */
+#define  COR1_1SB        0x00   /* 1 Stop Bit                              */
+#define  COR1_15SB       0x04   /* 1.5 Stop Bits                           */
+#define  COR1_2SB        0x08   /* 2 Stop Bits                             */
+#define COR1_CHARLEN    0x03    /* Character Length                        */
+#define  COR1_5BITS      0x00   /* 5 bits                                  */
+#define  COR1_6BITS      0x01   /* 6 bits                                  */
+#define  COR1_7BITS      0x02   /* 7 bits                                  */
+#define  COR1_8BITS      0x03   /* 8 bits                                  */
+
+
+/* Channel Option Register 2 (R/W) */
+
+#define COR2_IXM        0x80    /* Implied XON mode                        */
+#define COR2_TXIBE      0x40    /* Enable In-Band (XON/XOFF) Flow Control  */
+#define COR2_ETC        0x20    /* Embedded Tx Commands Enable             */
+#define COR2_LLM        0x10    /* Local Loopback Mode                     */
+#define COR2_RLM        0x08    /* Remote Loopback Mode                    */
+#define COR2_RTSAO      0x04    /* RTS Automatic Output Enable             */
+#define COR2_CTSAE      0x02    /* CTS Automatic Enable                    */
+#define COR2_DSRAE      0x01    /* DSR Automatic Enable                    */
+
+
+/* Channel Option Register 3 (R/W) */
+
+#define COR3_XONCH      0x80    /* XON is a pair of characters (1 & 3)     */
+#define COR3_XOFFCH     0x40    /* XOFF is a pair of characters (2 & 4)    */
+#define COR3_FCT        0x20    /* Flow-Control Transparency Mode          */
+#define COR3_SCDE       0x10    /* Special Character Detection Enable      */
+#define COR3_RXTH       0x0f    /* RX FIFO Threshold value (1-8)           */
+
+
+/* Channel Control Status Register (R/O) */
+
+#define CCSR_RXEN       0x80    /* Receiver Enabled                        */
+#define CCSR_RXFLOFF    0x40    /* Receive Flow Off (XOFF was sent)        */
+#define CCSR_RXFLON     0x20    /* Receive Flow On (XON was sent)          */
+#define CCSR_TXEN       0x08    /* Transmitter Enabled                     */
+#define CCSR_TXFLOFF    0x04    /* Transmit Flow Off (got XOFF)            */
+#define CCSR_TXFLON     0x02    /* Transmit Flow On (got XON)              */
+
+
+/* Modem Change Option Register 1 (R/W) */
+
+#define MCOR1_DSRZD     0x80    /* Detect 0->1 transition of DSR           */
+#define MCOR1_CDZD      0x40    /* Detect 0->1 transition of CD            */
+#define MCOR1_CTSZD     0x20    /* Detect 0->1 transition of CTS           */
+#define MCOR1_DTRTH     0x0f    /* Auto DTR flow control Threshold (1-8)   */
+#define  MCOR1_NODTRFC   0x0     /* Automatic DTR flow control disabled     */
+
+
+/* Modem Change Option Register 2 (R/W) */
+
+#define MCOR2_DSROD     0x80    /* Detect 1->0 transition of DSR           */
+#define MCOR2_CDOD      0x40    /* Detect 1->0 transition of CD            */
+#define MCOR2_CTSOD     0x20    /* Detect 1->0 transition of CTS           */
+
+/* Modem Change Register (R/W) */
+
+#define MCR_DSRCHG      0x80    /* DSR Changed                             */
+#define MCR_CDCHG       0x40    /* CD Changed                              */
+#define MCR_CTSCHG      0x20    /* CTS Changed                             */
+
+
+/* Modem Signal Value Register (R/W) */
+
+#define MSVR_DSR        0x80    /* Current state of DSR input              */
+#define MSVR_CD         0x40    /* Current state of CD input               */
+#define MSVR_CTS        0x20    /* Current state of CTS input              */
+#define MSVR_DTR        0x02    /* Current state of DTR output             */
+#define MSVR_RTS        0x01    /* Current state of RTS output             */
+
+
+/* Escape characters */
+
+#define CD186x_C_ESC     0x00    /* Escape character                        */
+#define CD186x_C_SBRK    0x81    /* Start sending BREAK                     */
+#define CD186x_C_DELAY   0x82    /* Delay output                            */
+#define CD186x_C_EBRK    0x83    /* Stop sending BREAK                      */
+
+#define SRSR_RREQint     0x10    /* This chip wants "rec" serviced          */
+#define SRSR_TREQint     0x04    /* This chip wants "transmit" serviced     */
+#define SRSR_MREQint     0x01    /* This chip wants "mdm change" serviced   */
+
+
+
+#define SRCR_PKGTYPE    0x80
+#define SRCR_REGACKEN   0x40
+#define SRCR_DAISYEN    0x20
+#define SRCR_GLOBPRI    0x10
+#define SRCR_UNFAIR     0x08
+#define SRCR_AUTOPRI    0x02
+#define SRCR_PRISEL     0x01
+
+
diff -u --recursive --new-file v2.0.30/linux/drivers/char/console.c linux/drivers/char/console.c
--- v2.0.30/linux/drivers/char/console.c	Thu Nov  7 01:25:21 1996
+++ linux/drivers/char/console.c	Tue Aug 12 13:06:54 1997
@@ -2320,3 +2320,22 @@
 {
 	return set_get_font (arg,0,video_mode_512ch);
 }
+
+/*
+ * Report the current status of the vc. This is exported to modules (ARub)
+ */
+
+int con_get_info(int *mode, int *shift, int *col, int *row,
+			struct tty_struct **tty)
+{
+	extern int shift_state;
+	extern struct tty_driver console_driver;
+	struct tty_struct **console_table=console_driver.table;
+
+	if (mode) *mode = vt_cons[fg_console]->vc_mode;
+	if (shift) *shift = shift_state;
+	if (col) *col = video_num_columns;
+	if (row) *row = video_num_lines;
+	if (tty) *tty = console_table[fg_console];
+	return fg_console;
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/char/keyboard.c linux/drivers/char/keyboard.c
--- v2.0.30/linux/drivers/char/keyboard.c	Tue Mar 11 15:55:22 1997
+++ linux/drivers/char/keyboard.c	Mon Aug 11 13:42:11 1997
@@ -1078,6 +1078,8 @@
 		reply_expected = 1;
 		outb_p(data, 0x60);
 		for(i=0; i<0x200000; i++) {
+			extern void allow_interrupts(void);
+			allow_interrupts();
 			inb_p(0x64);		/* just as a delay */
 			if (acknowledge)
 				return 1;
diff -u --recursive --new-file v2.0.30/linux/drivers/char/misc.c linux/drivers/char/misc.c
--- v2.0.30/linux/drivers/char/misc.c	Wed Nov  6 04:39:42 1996
+++ linux/drivers/char/misc.c	Tue Aug 12 13:06:54 1997
@@ -57,7 +57,7 @@
 /*
  * Assigned numbers, used for dynamic minors
  */
-#define DYNAMIC_MINORS 64 /* like dynamic majors */
+#define DYNAMIC_MINORS 64 /* like dynamic majors used to do */
 static unsigned char misc_minors[DYNAMIC_MINORS / 8];
 
 #ifndef MODULE
@@ -70,6 +70,8 @@
 extern void wdt_init(void);
 extern void pcwatchdog_init(void);
 extern int rtc_init(void);
+extern int con_get_info(int *mode, int *shift, int *col, int *row,
+			struct tty_struct **tty);
 
 #ifdef CONFIG_PROC_FS
 static int proc_misc_read(char *buf, char **start, off_t offset, int len, int unused)
@@ -181,6 +183,7 @@
 #ifndef MODULE
 	X(set_selection),   /* used by the kmouse module, can only */
 	X(paste_selection), /* be exported if misc.c is in linked in */
+	X(con_get_info),
 #endif
 #include <linux/symtab_end.h>
 };
diff -u --recursive --new-file v2.0.30/linux/drivers/char/n_tty.c linux/drivers/char/n_tty.c
--- v2.0.30/linux/drivers/char/n_tty.c	Mon Sep  2 05:18:26 1996
+++ linux/drivers/char/n_tty.c	Thu Aug  7 09:34:37 1997
@@ -89,6 +89,13 @@
  */
 int n_tty_chars_in_buffer(struct tty_struct *tty)
 {
+	if (tty->icanon) {
+		if (!tty->canon_data) return 0;
+
+		return (tty->canon_head > tty->read_tail) ?
+			tty->canon_head - tty->read_tail :
+			tty->canon_head + (N_TTY_BUF_SIZE - tty->read_tail);
+	}
 	return tty->read_cnt;
 }
 
diff -u --recursive --new-file v2.0.30/linux/drivers/char/pcwd.c linux/drivers/char/pcwd.c
--- v2.0.30/linux/drivers/char/pcwd.c	Thu Aug 29 09:15:14 1996
+++ linux/drivers/char/pcwd.c	Tue Aug 19 19:24:51 1997
@@ -15,6 +15,9 @@
  *		check_region command due to Alan's suggestion.
  * 960821	Made changes to compile in newer 2.0.x kernels.  Added
  *		"cold reboot sense" entry.
+ * 970819	Fixed use of is_open flag so pcwd_write does not always return
+ *		-EIO; this prevented tickling. Enabled board on open and
+ *		disabled on close.
  */
 
 #include <linux/module.h>
@@ -46,7 +49,11 @@
 #define WD_TIMERRESET_PORT2     0x370	/* Reset port - second choice */
 #define WD_CTLSTAT_PORT1        0x271	/* Control port - first choice */
 #define WD_CTLSTAT_PORT2        0x371	/* Control port - second choice */
-#define	WD_PORT_EXTENT		2	/* Takes up two addresses */
+#define WD_CTLSTAT2_PORT1       0x272   /* Control port#2 - first choice */
+#define WD_CTLSTAT2_PORT2       0x372   /* Control port#2 - second choice */
+#define WD_DISABLE_PORT1        0x273   /* Disable port - first choice  */
+#define WD_DISABLE_PORT2        0x373   /* Disable port - second choice  */
+#define	WD_PORT_EXTENT		4	/* Takes up four addresses */
 
 #define WD_WDRST                0x01	/* Previously reset state */
 #define WD_T110                 0x02	/* Temperature overheat sense */
@@ -54,7 +61,9 @@
 #define WD_RLY2                 0x08	/* External relay triggered */
 #define WD_SRLY2                0x80	/* Software external relay triggered */
 
+
 static int current_ctlport, current_readport;
+static int current_ctlport2, current_disport;
 static int is_open, is_eof;
 
 int pcwd_checkcard(void)
@@ -193,7 +202,7 @@
 
 	outb_p(wdrst_stat, current_ctlport);
 
-	return(1);
+	return(len);
 }
 
 static int pcwd_ioctl(struct inode *inode, struct file *file,
@@ -235,10 +244,22 @@
 static int pcwd_open(struct inode *ino, struct file *filep)
 {
 #ifdef	DEBUG
-	printk("pcwd: open request\n");
+	int before, after;
+
+	before = inb_p (current_ctlport2);
+#endif
+	if (is_open)
+		return -EIO;
+
+	/*  Enable the port  */
+	outb_p (0, current_disport);
+#ifdef	DEBUG
+	after = inb_p (current_ctlport2);
+	printk ("pcwd: open: control status #2 (b,a): %x, %x\n", before,after);
 #endif
 
 	MOD_INC_USE_COUNT;
+	is_open = 1;
 	is_eof = 0;
 	return(0);
 }
@@ -249,6 +270,10 @@
 	printk("pcwd: close request\n");
 #endif
 
+	is_open = 0;
+	/*  Disable the board  */
+	outb_p (0xa5, current_disport);
+	outb_p (0xa5, current_disport);
 	MOD_DEC_USE_COUNT;
 }
 
@@ -290,6 +315,8 @@
 
 	current_ctlport = WD_TIMERRESET_PORT1;
 	current_readport = WD_CTLSTAT_PORT1;
+	current_ctlport2 = WD_CTLSTAT2_PORT1;
+	current_disport = WD_DISABLE_PORT1;
 
 	if (!pcwd_checkcard()) {
 #ifdef	DEBUG
@@ -298,6 +325,8 @@
 
 		current_ctlport = WD_TIMERRESET_PORT2;
 		current_readport = WD_CTLSTAT_PORT2;
+		current_ctlport2 = WD_CTLSTAT2_PORT2;
+		current_disport = WD_DISABLE_PORT2;
 
 		if (!pcwd_checkcard()) {
 			printk("pcwd: No card detected, or wrong port assigned.\n");
@@ -327,8 +356,11 @@
 #ifdef	MODULE
 void cleanup_module(void)
 {
+	/*  Disable the board  */
+	outb_p (0xa5, current_disport);
+	outb_p (0xa5, current_disport);
 	misc_deregister(&pcwd_miscdev);
-	release_region(current_ctlport, 2);
+	release_region(current_ctlport, WD_PORT_EXTENT);
 #ifdef	DEBUG
 	printk("pcwd: Cleanup successful.\n");
 #endif
diff -u --recursive --new-file v2.0.30/linux/drivers/char/pty.c linux/drivers/char/pty.c
--- v2.0.30/linux/drivers/char/pty.c	Mon Sep 30 06:56:48 1996
+++ linux/drivers/char/pty.c	Thu Aug  7 09:34:37 1997
@@ -153,14 +153,32 @@
 	return to->ldisc.receive_room(to);
 }
 
+/*
+ * Modified for asymmetric master/slave behavior
+ * The chars_in_buffer() value is used by the ldisc select() function 
+ * to hold off writing when chars_in_buffer > WAKEUP_CHARS (== 256).
+ * To allow typed-ahead commands to accumulate, the master side returns 0
+ * until the buffer is half full. The slave side returns the true count.
+ */
 static int pty_chars_in_buffer(struct tty_struct *tty)
 {
 	struct tty_struct *to = tty->link;
+	int count;
 
 	if (!to || !to->ldisc.chars_in_buffer)
 		return 0;
 
-	return to->ldisc.chars_in_buffer(to);
+	/* The ldisc must report 0 if no characters available to be read */
+	count = to->ldisc.chars_in_buffer(to);
+
+	if (tty->driver.subtype == PTY_TYPE_SLAVE) return count;
+
+	/* 
+	 * Master side driver ... return 0 if the other side's read buffer 
+	 * is less than half full.  This allows room for typed-ahead commands
+	 * with a reasonable margin to avoid overflow. 
+	 */
+	return ((count < N_TTY_BUF_SIZE/2) ? 0 : count);
 }
 
 static void pty_flush_buffer(struct tty_struct *tty)
diff -u --recursive --new-file v2.0.30/linux/drivers/char/random.c linux/drivers/char/random.c
--- v2.0.30/linux/drivers/char/random.c	Tue Apr  8 08:47:45 1997
+++ linux/drivers/char/random.c	Thu Aug 14 10:05:47 1997
@@ -1071,10 +1071,8 @@
 	 * If we gave the user some bytes and we have an inode pointer,
 	 * update the access time.
 	 */
-	if (inode && count != 0) {
-		inode->i_atime = CURRENT_TIME;
-		inode->i_dirt = 1;
-	}
+	if (inode && count != 0)
+		UPDATE_ATIME(inode);
 	
 	return (count ? count : retval);
 }
@@ -1339,8 +1337,13 @@
 	do_gettimeofday(&tv);
 	seq = tmp[1] + tv.tv_usec+tv.tv_sec*1000000;
 #if 0
-	printk("init_seq(%lx, %lx, %d, %d) = %d\n",
-	       saddr, daddr, sport, dport, seq);
+	/*
+	  ugh...we can only use in_ntoa once per printk, splitting
+	  a single line of info into multiple printk's confuses klogd,
+	  and Linus says in_ntoa sucks anyway :)
+	*/
+	printk("init_seq(%d.%d.%d.%d:%d, %d.%d.%d.%d:%d) = %d\n",
+		NIPQUAD(saddr), sport, NIPQUAD(daddr), dport, seq);
 #endif
 	return (seq);
 }
@@ -1363,7 +1366,7 @@
 
 	/*
 	 * Pick a random secret the first time we open a TCP
-	 * connection, and expire secretes older than 5 minutes.
+	 * connection, and expire secrets older than 5 minutes.
 	 */
 	if (is_init == 0 || jiffies-secret_timestamp[offset] > 600*HZ) {
 		if (is_init == 0) valid_secret[0] = valid_secret[1] = 0;
@@ -1388,14 +1391,14 @@
 	if (!validate) {
 		if (seq == sseq) seq++;
 #if 0
-		printk("init_seq(%lx, %lx, %d, %d, %d) = %d\n",
-		       saddr, daddr, sport, dport, sseq, seq);
+		printk("init_seq(%d.%d.%d.%d:%d %d.%d.%d.%d:%d, %d) = %d\n",
+			NIPQUAD(saddr), sport, NIPQUAD(daddr), dport, sseq, seq);
 #endif
 		return (seq);
 	} else {
 		if (seq == sseq || (seq+1) == sseq) {
-			printk("validated probe(%lx, %lx, %d, %d, %d)\n",
-		       		saddr, daddr, sport, dport, sseq);
+			printk("validated probe(%d.%d.%d.%d:%d, %d.%d.%d.%d:%d, %d)\n",
+				NIPQUAD(saddr), sport, NIPQUAD(daddr), dport, sseq);
 			return 1;
 		}
 		if (jiffies-secret_timestamp[(offset+1)%2] <= 1200*HZ) {
@@ -1407,15 +1410,15 @@
 			seq = tmp[1];
 			if (seq == sseq || (seq+1) == sseq) {
 #ifdef 0
-				printk("validated probe(%lx, %lx, %d, %d, %d)\n",
-		       			saddr, daddr, sport, dport, sseq);
+				printk("validated probe(%d.%d.%d.%d:%d, %d.%d.%d.%d:%d, %d)\n",
+					NIPQUAD(saddr), sport, NIPQUAD(daddr), dport, sseq);
 #endif
 				return 1;
 			}
 		}
 #ifdef 0
-		printk("failed validation on probe(%lx, %lx, %d, %d, %d)\n",
-			saddr, daddr, sport, dport, sseq);
+		printk("failed validation on probe(%d.%d.%d.%d:%d, %d.%d.%d.%d:%d, %d)\n",
+			NIPQUAD(saddr), sport, NIPQUAD(daddr), dport, sseq);
 #endif
 		return 0;
 	}
diff -u --recursive --new-file v2.0.30/linux/drivers/char/scc.c linux/drivers/char/scc.c
--- v2.0.30/linux/drivers/char/scc.c	Thu Apr 11 23:49:35 1996
+++ linux/drivers/char/scc.c	Tue Aug 12 14:43:42 1997
@@ -1,6 +1,6 @@
-#define RCS_ID "$Id: scc.c,v 1.41 1995/12/17 22:36:40 jreuter Exp jreuter $"
+#define RCS_ID "$Id: scc.c,v 1.67 1997/04/10 15:24:41 jreuter Exp jreuter $"
 
-#define BANNER "Z8530 SCC driver version 2.01.dl1bke (alpha) by DL1BKE\n"
+#define BANNER "Z8530 SCC driver version 2.4c.dl1bke (experimental) by DL1BKE\n"
 
 /*
 
@@ -11,7 +11,7 @@
 
    ********************************************************************
 
-	Copyright (c) 1993, 1995 Joerg Reuter DL1BKE
+	Copyright (c) 1993, 1997 Joerg Reuter DL1BKE
 
 	portions (c) 1993 Guido ten Dolle PE1NNZ
 
@@ -48,16 +48,6 @@
    
    ******************************************************************** 
 
-
-   ...If you find any portions of the code that are copyrighted to you,
-   and you don't want to see in here, please send me a private (!) 
-   message to my internet site. I will change it as soon as possible. 
-   Please don't flame to the tcp-group or anywhere else. Thanks!
-
-   Joerg Reuter	ampr-net: dl1bke@db0pra.ampr.org
-		AX-25   : DL1BKE @ DB0ACH.#NRW.DEU.EU
-		Internet: jreuter@lykos.tng.oche.de
-		
 		
    Incomplete history of z8530drv:
    -------------------------------
@@ -74,89 +64,105 @@
    950131	- changed copyright notice to GPL without limitations.
    
      .
-     .
+     .	<snip!>
      .
       
-   950922	- using kernel timer chain
-   
-   951002	- splitting timer routine, adding support for throttle/
-   		  unthrottle calls, removed typo in kiss decoder,
-   		  corrected termios settings.
-   		  
-   951011	- at last found one (the?) problem which caused the 
-   		  driver to mess up buffers on heavy load pe1ayx was
-   		  complaining about. Quite simple to reproduce:
-   		  set a 9k6 port into audio loopback, add a route
-   		  to 44.99.99.99 on that route and do a 
-   		  ping -f 44.99.99.99
+   951114	- rewrote memory management, took first steps to allow
+		  compilation as a module. Well, it looks like a whole
+		  new driver now.
+
+   960413	- Heiko Eissfeld fixed missing verify_area() calls
+
+   960507	- the driver may be used as a network driver now.
+ 		  (for kernel AX.25). 
+
+   960512	- added scc_net_ioctl(), restructured ioctl routines
+   		  added /proc filesystem support
    		  
-   951013	- it still does not survive a flood ping...
+   960517	- fixed fullduplex mode 2 operation and waittime bug
    
-   951107	- axattach w/o "-s" option (or setting an invalid 
-   		  baudrate) does not produce "division by zero" faults 
-   		  anymore.
+   960518	- added fullduplex mode 3 to allow direct hardware
+   		  access via protocol layer. Removed kiss.not_slip,
+   		  I do not think someone really needed it.
    		  
-   951114	- rewrote memory management, took first steps to allow
-		  compilation as a module. Well, it looks like a whole
-		  new driver now. BTW: It  d o e s  survive the flood
-		  ping at last...
-		  
-   951116	- scc_config.h is gone -- the driver will be initialized
-   		  through ioctl-commands. Use sccinit.c for this purpose.
+   960607	- switching off receiver while transmission in halfduplex
+   		  mode with external MODEM clock.
    		  
-   951117	- Well --- what should I say: You can compile it as a
-   		  module now. And I solved the problem with slip.c...
+   960715	- rewrote interrupt service routine for "polling" mode,
+   		  fixed bug in grouping algorithm.
    		  
-   951120	- most ioctl() routines may be called w/o suser()
-   		  permissions, check if you've set the permissions of
-   		  /dev/scc* right! NOT 0666, you know it's evil ;-)
+   960719	- New transmit timer routines (let's see what it breaks),
+   		  some improvements regarding DCD and SYNC interrupts,
+   		  clean-up of printk().
    		  
-   951217	- found silly bug in init_channel(), some bugfixes
-   		  for the "standalone" module
+   960725	- Fixed Maxkeyup problems. Will generate HDLC abort and
+   		  recover now.
 
+   960808	- Maxkeyup will set dev->tbusy until all remaining frames
+   		  are sent.
+   		  
+   970115	- Fixed return values in scc_net_tx(), added missing
+   		  scc_enqueue_buffer()
+   		  
+   970410	- Fixed problem with new *_timer() code in sched.h for
+   		  kernel 2.0.30.
 
    Thanks to:
    ----------
    
-   PE1CHL Rob	- for a lot of good ideas from his SCC driver for DOS
-   PE1NNZ Guido - for his port of the original driver to Linux
-   KA9Q   Phil  - from whom we stole the mbuf-structure
-   PA3AYX Hans  - for some useful changes
-   DL8MBT Flori - for support
-   DG0FT  Rene  - for the BayCom USCC support
-   PA3AOU Harry - for ESCC testing, information supply and support
-   
+   PE1CHL Rob	  - for a lot of good ideas from his SCC driver for DOS
+   PE1NNZ Guido   - for his port of the original driver to Linux
+   KA9Q   Phil    - from whom we stole the mbuf-structure
+   PA3AYX Hans    - for some useful changes
+   DL8MBT Flori   - for support
+   DG0FT  Rene    - for the BayCom USCC support
+   PA3AOU Harry   - for ESCC testing, information supply and support
+   Heiko Eissfeld - verify_area() clean-up
+   DL3KHB Klaus   - fullduplex mode 2 bug-hunter
+   PA3GCU Richard - for support and finding various bugs.
+
    PE1KOX Rob, DG1RTF Thomas, ON5QK Roland, G4XYW Andy, Linus,
-   EI9GL Paul,
-   
+   GW4PTS Alan, EI9GL Paul, G0DZB Peter, G8IMB Martin
+
    and all who sent me bug reports and ideas... 
    
    
    NB -- if you find errors, change something, please let me know
       	 first before you distribute it... And please don't touch
    	 the version number. Just replace my callsign in
-   	 "v2.01.dl1bke" with your own. Just to avoid confusion...
-   	 
+   	 "v2.4c.dl1bke" with your own. Just to avoid confusion...
+
    If you want to add your modification to the linux distribution
    please (!) contact me first.
-   	  
-   Jörg Reuter DL1BKE
-  
+   
+   New versions of the driver will be announced on the linux-hams
+   mailing list on vger.rutgers.edu. To subscribe send an e-mail
+   to majordomo@vger.rutgers.edu with the following line in
+   the body of the mail:
+   
+	   subscribe linux-hams
+	   
+   The content of the "Subject" field will be ignored.
+
+   vy 73,
+   Joerg Reuter	ampr-net: dl1bke@db0pra.ampr.org
+		AX-25   : DL1BKE @ DB0ACH.#NRW.DEU.EU
+		Internet: jreuter@poboxes.com  
 */
 
 /* ----------------------------------------------------------------------- */
 
-#define DEBUG_BUFFERS	/* keep defined unless it is really stable... */
-
-#undef  SCC_DELAY 	/* perhaps a 486DX2 is a *bit* too fast */
-#undef  SCC_LDELAY 5	/* slow it even a bit more down */
+#undef  SCC_DELAY 	/* perhaps your ISA bus is a *bit* too fast? */
+#undef  SCC_LDELAY 1	/* slow it even a bit more down */
 #undef  DONT_CHECK	/* don't look if the SCCs you specified are available */
 
 #define MAXSCC          4       /* number of max. supported chips */
 #define RXBUFFERS       8       /* default number of RX buffers */
 #define TXBUFFERS       8       /* default number of TX buffers */
 #define BUFSIZE         384     /* must not exceed 4096-sizeof(mbuf) */
-#define TPS             25      /* scc_tx_timer():  Ticks Per Second */
+#undef  DISABLE_ALL_INTS	/* use cli()/sti() in ISR instead of */
+				/* enable_irq()/disable_irq()        */
+#undef	SCC_DEBUG
 
 #define DEFAULT_CLOCK	4915200 /* default pclock if nothing is specified */
 
@@ -177,12 +183,26 @@
 #include <linux/interrupt.h>
 #include <linux/ioport.h>
 #include <linux/string.h>
+#include <linux/in.h>
 #include <linux/fcntl.h>
 #include <linux/ptrace.h>
 #include <linux/malloc.h>
 #include <linux/delay.h>
+
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/socket.h>
+
+#ifdef CONFIG_SCC_STANDALONE
+#include "scc.h"
+#else
 #include <linux/scc.h>
+#endif
 
+#include <net/ax25.h>
+#include <asm/irq.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/segment.h>
@@ -193,6 +213,7 @@
 #include <ctype.h>
 #include <time.h>
 #include <linux/kernel.h>
+#include <linux/proc_fs.h>
 
 #ifdef MODULE
 int init_module(void);
@@ -203,6 +224,15 @@
 #define Z8530_MAJOR 34
 #endif
 
+#ifndef PROC_NET_Z8530
+#define PROC_NET_Z8530 PROC_NET_LAST+10		/* sorry... */
+#endif
+
+#if !defined(CONFIG_SCC_DEV) && !defined(CONFIG_SCC_TTY)
+#define CONFIG_SCC_DEV
+#define CONFIG_SCC_TTY
+#endif
+
 int scc_init(void);
 
 static struct mbuf * scc_enqueue_buffer(struct mbuf **queue, struct mbuf * buffer);
@@ -211,6 +241,7 @@
 static void free_buffer_pool(struct scc_channel *scc);
 static struct mbuf * scc_get_buffer(struct scc_channel *scc, char type);
 
+#ifdef CONFIG_SCC_TTY
 int scc_open(struct tty_struct *tty, struct file *filp);
 static void scc_close(struct tty_struct *tty, struct file *filp);
 int scc_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count);
@@ -219,81 +250,112 @@
 static int scc_write_room(struct tty_struct *tty);
 static int scc_chars_in_buffer(struct tty_struct *tty);
 static void scc_flush_buffer(struct tty_struct *tty);
-static int scc_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg);
+static int scc_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg);
 static void scc_set_termios(struct tty_struct *tty, struct termios *old_termios);
 static void scc_set_ldisc(struct tty_struct *tty);
 static void scc_throttle(struct tty_struct *tty);
 static void scc_unthrottle(struct tty_struct *tty);
 static void scc_start(struct tty_struct *tty);
 static void scc_stop(struct tty_struct *tty);
+static void kiss_encode(struct scc_channel *scc);
+static void scc_rx_timer(unsigned long);
+static void scc_change_speed(struct scc_channel *scc);
+#endif
 
-static void z8530_init(void);
+static void t_dwait(unsigned long);
+static void t_txdelay(unsigned long);
+static void t_tail(unsigned long);
+static void t_busy(unsigned long);
+static void t_maxkeyup(unsigned long);
+static void t_idle(unsigned long);
+static void scc_tx_done(struct scc_channel *);
+static void scc_start_tx_timer(struct scc_channel *, void (*)(unsigned long), unsigned long);
+static void scc_start_maxkeyup(struct scc_channel *);
+static void scc_start_defer(struct scc_channel *);
 
-static void scc_change_speed(struct scc_channel *scc);
-static void kiss_encode(struct scc_channel *scc);
+static int scc_ioctl(struct scc_channel *scc, unsigned int cmd, void * arg);
+
+static void z8530_init(void);
 
 static void init_channel(struct scc_channel *scc);
 static void scc_key_trx (struct scc_channel *scc, char tx);
-static void scc_txint(register struct scc_channel *scc);
-static void scc_exint(register struct scc_channel *scc);
-static void scc_rxint(register struct scc_channel *scc);
-static void scc_spint(register struct scc_channel *scc);
 static void scc_isr(int irq, void *dev_id, struct pt_regs *regs);
-static void scc_tx_timer(unsigned long);
-static void scc_rx_timer(unsigned long);
 static void scc_init_timer(struct scc_channel *scc);
 
-/* from serial.c */
+#ifdef CONFIG_SCC_DEV
+static int scc_net_setup(struct scc_channel *scc, unsigned char *name);
+static void scc_net_rx(struct scc_channel *scc, struct mbuf *bp);
+#endif
+
+static unsigned char *SCC_DriverName = "scc";
 
+#ifdef CONFIG_SCC_TTY
 static int baud_table[] = {
 	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
 	9600, 19200, 38400, 57600, 115200, 0 };
 
+struct semaphore scc_sem = MUTEX;
+static struct termios scc_std_termios;
+unsigned char scc_wbuf[BUFSIZE];
 
 struct tty_driver scc_driver;
-static int scc_refcount;
 static struct tty_struct *scc_table[2*MAXSCC];
 static struct termios scc_termios[2 * MAXSCC];
 static struct termios scc_termios_locked[2 * MAXSCC];
+static int scc_refcount;
+#endif
+
 
-struct irqflags { unsigned char used : 1; } Ivec[16];
+static struct irqflags { unsigned char used : 1; } Ivec[16];
 	
-struct scc_channel SCC_Info[2 * MAXSCC];         /* information per channel */
-io_port SCC_ctrl[2 * MAXSCC];			 /* Control ports */
+static struct scc_channel SCC_Info[2 * MAXSCC];	/* information per channel */
 
-unsigned char Random = 0;		/* random number for p-persist */
-unsigned char Driver_Initialized = 0;
-int Nchips = 0;
-io_port Vector_Latch = 0;
+static struct scc_ctrl {
+	io_port chan_A;
+	io_port chan_B;
+	int irq;
+} SCC_ctrl[MAXSCC+1];
 
-struct semaphore scc_sem = MUTEX;
-unsigned char scc_wbuf[BUFSIZE];
+static unsigned char Driver_Initialized = 0;
+static int Nchips = 0;
+static io_port Vector_Latch = 0;
 
-static struct termios scc_std_termios;
+#define SCC_DEVNAME (scc->stat.is_netdev? scc->dev->name:kdevname(scc->tty->device))
 
 /* ******************************************************************** */
 /* *			Port Access Functions			      * */
 /* ******************************************************************** */
 
-static inline unsigned char
-InReg(register io_port port, register unsigned char reg)
+/* These provide interrupt save 2-step access to the Z8530 registers */
+
+static __inline__ unsigned char
+InReg(io_port port, unsigned char reg)
 {
+	unsigned long flags;
+	unsigned char r;
+	
+	save_flags(flags);
+	cli();
 #ifdef SCC_LDELAY
-	register unsigned char r;
 	Outb(port, reg);
 	udelay(SCC_LDELAY);
 	r=Inb(port);
 	udelay(SCC_LDELAY);
-	return r;
 #else
 	Outb(port, reg);
-	return Inb(port);
+	r=Inb(port);
 #endif
+	restore_flags(flags);
+	return r;
 }
 
-static inline void
-OutReg(register io_port port, register unsigned char reg, register unsigned char val)
+static __inline__ void
+OutReg(io_port port, unsigned char reg, unsigned char val)
 {
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
 #ifdef SCC_LDELAY
 	Outb(port, reg); udelay(SCC_LDELAY);
 	Outb(port, val); udelay(SCC_LDELAY);
@@ -301,26 +363,40 @@
 	Outb(port, reg);
 	Outb(port, val);
 #endif
+	restore_flags(flags);
 }
 
-static inline void
-wr(register struct scc_channel *scc, register unsigned char reg, register unsigned char val)
+static __inline__ void
+wr(struct scc_channel *scc, unsigned char reg, unsigned char val)
 {
 	OutReg(scc->ctrl, reg, (scc->wreg[reg] = val));
 }
 
-static inline void
-or(register struct scc_channel *scc, register unsigned char reg, register unsigned char val)
+static __inline__ void
+or(struct scc_channel *scc, unsigned char reg, unsigned char val)
 {
 	OutReg(scc->ctrl, reg, (scc->wreg[reg] |= val));
 }
 
-static inline void
-cl(register struct scc_channel *scc, register unsigned char reg, register unsigned char val)
+static __inline__ void
+cl(struct scc_channel *scc, unsigned char reg, unsigned char val)
 {
 	OutReg(scc->ctrl, reg, (scc->wreg[reg] &= ~val));
 }
 
+#ifdef DISABLE_ALL_INTS
+static __inline__ void scc_cli(int irq)
+{ cli(); }
+static __inline__ void scc_sti(int irq)
+{ sti(); }
+#else
+static __inline__ void scc_cli(int irq)
+{ disable_irq(irq); }
+static __inline__ void scc_sti(int irq)
+{ enable_irq(irq); }
+#endif
+
+
 /* ******************************************************************** */
 /* * 			Memory Buffer Management			*/
 /* ******************************************************************** */
@@ -331,6 +407,9 @@
  * fast. It has the disadvantage to mess things up double if something
  * is wrong.
  *
+ * We could have used the socket buffer routines from skbuff.h instead,
+ * but our own routines keep the driver portable...
+ *
  * Every scc channel has its own buffer pool now with an adjustable
  * number of buffers for transmit and receive buffers. The buffer
  * size remains the same for each AX.25 frame, but is (as a semi-
@@ -354,23 +433,6 @@
 	
 	save_flags(flags); cli();		/* do not disturb! */
 
-#ifdef DEBUG_BUFFERS			
-	if (queue == NULLBUFP)			/* called with illegal parameters, notify the user */
-
-	{
-		printk("z8530drv: Pointer to queue anchor is NULL pointer [enq]\n");
-		restore_flags(flags);
-		return NULLBUF;
-	}
-	
-	if (buffer == NULLBUF)
-	{
-		printk("z8530drv: can't enqueue a NULL pointer\n");
-		restore_flags(flags);
-		return NULLBUF;
-	}
-#endif
-
 	anchor = *queue;			/* the anchor is the "start" of the chain */
 
 	if (anchor == NULLBUF)			/* found an empty list */
@@ -380,22 +442,9 @@
 	} else	
 	if (anchor->prev == NULLBUF)		/* list has one member only */
 	{
-#ifdef DEBUG_BUFFERS
-		if (anchor->prev != NULLBUF)	/* oops?! */
-			printk("weird --- anchor->prev is NULL but not anchor->next [enq]\n");
-#endif			
-		anchor->next = anchor->prev = buffer;
-		buffer->next = buffer->prev = anchor;
-	} else
-#ifdef DEBUG_BUFFERS	
-	if (anchor->next == NULLBUF)		/* this has to be an error. Okay, make the best out of it */
-	{
-		printk("z8530drv: weird --- anchor->next is NULL but not anchor->prev [enq]\n");
 		anchor->next = anchor->prev = buffer;
 		buffer->next = buffer->prev = anchor;
-	} else
-#endif
-	{					/* every other case */
+	} else {				/* every other case */
 		buffer->prev = anchor->prev;	/* self explaining, isn't it? */
 		buffer->next = anchor;
 		anchor->prev->next = buffer;
@@ -418,28 +467,12 @@
 	
 	save_flags(flags); cli();
 
-#ifdef DEBUG_BUFFERS			
-	if (queue == NULLBUFP)			/* called with illegal parameter */
-
-	{
-		printk("z8530drv: Pointer to queue anchor is NULL pointer [deq]\n");
-		restore_flags(flags);
-		return NULLBUF;
-	}
-#endif
-
 	buffer = *queue;			/* head of the chain */
 	
 	if (buffer != NULLBUF)			/* not an empty list? */
 	{
 		if (buffer->prev != NULLBUF)	/* not last buffer? */
 		{
-#ifdef DEBUG_BUFFERS
-			if (buffer->next == NULLBUF)
-			{			/* what?! */
-				printk("z8530drv: weird --- buffer->next is NULL but not buffer->prev [deq]\n");
-			} else
-#endif
 			if (buffer->prev->next == buffer->prev->prev)
 			{			/* only one buffer remaining... */
 				buffer->next->prev = NULLBUF;
@@ -449,10 +482,7 @@
 				buffer->prev->next = buffer->next;
 			}
 		} 
-#ifdef DEBUG_BUFFERS
-		else if (buffer->next != NULLBUF)
-			printk("z8530drv: weird --- buffer->prev is NULL but not buffer->next [deq]\n");
-#endif
+
 		*queue = buffer->next;		/* new head of chain */
 		
 		buffer->next = NULLBUF;		/* for security only... */
@@ -476,6 +506,12 @@
 	struct mbuf * bptr;
 	int  buflen;
 	
+	if (scc->mempool)
+	{
+		printk(KERN_DEBUG "z8530drv: alloc_buffer_pool() had buffers already allocated\n");
+		return;
+	}
+	
 	buflen = sizeof(struct mbuf) + scc->stat.bufsize;
 	
 	if (scc->stat.bufsize <  336) scc->stat.bufsize = 336;
@@ -487,7 +523,6 @@
 	
 	if (scc->stat.rxbuffers < 4)  scc->stat.rxbuffers = 4;
 	if (scc->stat.txbuffers < 4)  scc->stat.txbuffers = 4;
-	
 
 	
 	/* allocate receive buffers for this channel */
@@ -502,7 +537,7 @@
 		
 		if (bptr == NULLBUF)
 		{
-			printk("z8530drv: %s: can't allocate memory for rx buffer pool", kdevname(scc->tty->device));
+			printk(KERN_WARNING "z8530drv: %s: can't allocate memory for rx buffer pool", SCC_DEVNAME);
 			break;
 		}
 		
@@ -527,7 +562,7 @@
 		
 		if (bptr == NULLBUF)
 		{
-			printk("z8530drv: %s: can't allocate memory for tx buffer pool", kdevname(scc->tty->device));
+			printk(KERN_WARNING "z8530drv: %s: can't allocate memory for tx buffer pool", SCC_DEVNAME);
 			break;
 		}
 		
@@ -537,6 +572,8 @@
 		
 		scc_enqueue_buffer(&scc->tx_buffer_pool, bptr);
 	}
+	
+	scc->mempool = 1;
 }
 
 
@@ -545,102 +582,89 @@
 static void 
 free_buffer_pool(struct scc_channel *scc)
 {
-	struct mbuf * bptr;
 	unsigned long flags;
-	int cnt;
+	int rx_cnt, tx_cnt;
+	
+	if (!scc->mempool)
+		return;
 	
 	/* this one is a bit tricky and probably dangerous. */
+	rx_cnt = tx_cnt = 0;
 		
 	save_flags(flags); cli();
 	
 	/* esp. to free the buffers currently in use by ISR */
 	
-	bptr = scc->rx_bp;
-	if (bptr != NULLBUF)
+	if (scc->rx_bp != NULLBUF)
 	{
+		kfree(scc->rx_bp);
 		scc->rx_bp = NULLBUF;
-		scc_enqueue_buffer(&scc->rx_buffer_pool, bptr);
+		rx_cnt++;
 	}
 
-	bptr = scc->tx_bp;	
-	if (bptr != NULLBUF)
+	if (scc->tx_bp != NULLBUF)
 	{
+		kfree(scc->tx_bp);
 		scc->tx_bp = NULLBUF;
-		scc_enqueue_buffer(&scc->tx_buffer_pool, bptr);
+		tx_cnt++;
 	}
 	
-	bptr = scc->kiss_decode_bp;
-	if (bptr != NULLBUF)
+	if (scc->kiss_decode_bp != NULLBUF)
 	{
+		kfree(scc->kiss_decode_bp);
 		scc->kiss_decode_bp = NULLBUF;
-		scc_enqueue_buffer(&scc->tx_buffer_pool, bptr);
+		tx_cnt++;
 	}
-	
-	bptr = scc->kiss_encode_bp;
-	if (bptr != NULLBUF)
+
+#ifdef CONFIG_SCC_TTY
+	if (scc->kiss_encode_bp != NULLBUF)
 	{
+		kfree(scc->kiss_encode_bp);
 		scc->kiss_encode_bp = NULLBUF;
-		scc_enqueue_buffer(&scc->rx_buffer_pool, bptr);
+		rx_cnt++;
 	}
+#endif
 	
 	restore_flags(flags);
 	
 	
 	while (scc->rx_queue != NULLBUF)
 	{
-		bptr = scc_dequeue_buffer(&scc->rx_queue);
-		scc_enqueue_buffer(&scc->rx_buffer_pool, bptr);
+		kfree(scc_dequeue_buffer(&scc->rx_queue));
+		rx_cnt++;
 	}
+	scc->stat.rx_queued = 0;
 	
 	while (scc->tx_queue != NULLBUF)
 	{
-		bptr = scc_dequeue_buffer(&scc->tx_queue);
-		scc_enqueue_buffer(&scc->tx_buffer_pool, bptr);
+		kfree(scc_dequeue_buffer(&scc->tx_queue));
+		tx_cnt++;
 	}
-	
-	/* you want to know why we move every buffer back to the
-	   buffer pool? Well, good question... ;-) 
-	 */
-	
-	cnt = 0;
-	
-	/* Probably because we just want a central position in the
-	   code were we actually call free()?
-	 */
+	scc->stat.tx_queued = 0;
 	
 	while (scc->rx_buffer_pool != NULLBUF)
 	{
-		bptr = scc_dequeue_buffer(&scc->rx_buffer_pool);
-		
-		if (bptr != NULLBUF)
-		{
-			cnt++;
-			kfree(bptr);
-		}
+		kfree(scc_dequeue_buffer(&scc->rx_buffer_pool));
+		rx_cnt++;
 	}
-
-	if (cnt < scc->stat.rxbuffers)		/* hmm... hmm... :-( */
-		printk("z8530drv: oops, deallocated only %d of %d rx buffers\n", cnt, scc->stat.rxbuffers);
-	if (cnt > scc->stat.rxbuffers)		/* WHAT?!! */
-		printk("z8530drv: oops, deallocated %d instead of %d rx buffers. Very strange.\n", cnt, scc->stat.rxbuffers);
-		
-	cnt = 0;
 	
 	while (scc->tx_buffer_pool != NULLBUF)
 	{
-		bptr = scc_dequeue_buffer(&scc->tx_buffer_pool);
-		
-		if (bptr != NULLBUF)
-		{
-			cnt++;
-			kfree(bptr);
-		}
+		kfree(scc_dequeue_buffer(&scc->tx_buffer_pool));
+		tx_cnt++;
 	}
-
-	if (cnt < scc->stat.txbuffers)
-		printk("z8530drv: oops, deallocated only %d of %d tx buffers\n", cnt, scc->stat.txbuffers);
-	if (cnt > scc->stat.txbuffers)
-		printk("z8530drv: oops, deallocated %d instead of %d tx buffers. Very strange.\n", cnt, scc->stat.txbuffers);
+	
+	if (rx_cnt < scc->stat.rxbuffers)	/* hmm... hmm... :-( */
+		printk(KERN_ERR "z8530drv: oops, deallocated only %d of %d rx buffers\n", rx_cnt, scc->stat.rxbuffers);
+	if (rx_cnt > scc->stat.rxbuffers)	/* WHAT?!! */
+		printk(KERN_ERR "z8530drv: oops, deallocated %d instead of %d rx buffers. Very strange.\n", rx_cnt, scc->stat.rxbuffers);
+		
+	if (tx_cnt < scc->stat.txbuffers)
+		printk(KERN_ERR "z8530drv: oops, deallocated only %d of %d tx buffers\n", tx_cnt, scc->stat.txbuffers);
+	if (tx_cnt > scc->stat.txbuffers)
+		printk(KERN_ERR "z8530drv: oops, deallocated %d instead of %d tx buffers. Very strange.\n", tx_cnt, scc->stat.txbuffers);
+		
+	scc->mempool = 0;
 }
 		
 		
@@ -666,28 +690,33 @@
 		
 		if (bptr == NULLBUF)
 		{
-			printk("z8530drv: scc_get_buffer(%s): tx buffer pool empty\n", kdevname(scc->tty->device));
-			
+#if 0
+			printk(KERN_DEBUG "z8530drv: tx buffer pool for %s empty\n", SCC_DEVNAME);
+#endif
 			/* use the oldest from the queue instead */
 			
 			bptr = scc_dequeue_buffer(&scc->tx_queue);
+			scc->stat.tx_queued--;
+			scc->stat.nospace++;
 			
 			/* this should never, ever happen... */
 			
 			if (bptr == NULLBUF)
-				printk("z8530drv: scc_get_buffer(): panic - even no buffer found in tx queue\n");
+				printk(KERN_ERR "z8530drv: panic - all tx buffers for %s gone\n", SCC_DEVNAME);
 		}
 	} else {
 		bptr = scc_dequeue_buffer(&scc->rx_buffer_pool);
 		
 		if (bptr == NULLBUF)
 		{
-			printk("z8530drv: scc_get_buffer(%s): rx buffer pool empty\n", kdevname(scc->tty->device));
+			printk(KERN_DEBUG "z8530drv: rx buffer pool for %s empty\n", SCC_DEVNAME);
 			
 			bptr = scc_dequeue_buffer(&scc->rx_queue);
+			scc->stat.rx_queued--;
+			scc->stat.nospace++;
 			
 			if (bptr == NULLBUF)
-				printk("z8530drv: scc_get_buffer(): panic - even no buffer found in rx queue\n");
+				printk(KERN_ERR "z8530drv: panic - all rx buffers for %s gone\n", SCC_DEVNAME);
 		}	
 	}
 	
@@ -705,153 +734,117 @@
 /* *			Interrupt Service Routines		      * */
 /* ******************************************************************** */
 
-/* ----> interrupt service routine for the 8530 <---- */
 
-/* it's recommended to keep this function "inline" ;-) */
-
-static inline void
-scc_isr_dispatch(register struct scc_channel *scc, register int vector)
-{
-	switch (vector & VECTOR_MASK)
-	{
-		case TXINT: scc_txint(scc); break;
-		case EXINT: scc_exint(scc); break;
-		case RXINT: scc_rxint(scc); break;
-		case SPINT: scc_spint(scc); break;
-		default	  : printk("scc_isr(): unknown interrupt status (addr %4.4x, state %2.2x)\n",scc->ctrl,vector);
-	}
-}
 
+/* ----> subroutines for the interrupt handlers <---- */
 
 
+#ifdef CONFIG_SCC_TTY
+/* kick rx_timer (try to send received frame or a status message ASAP) */
 
-
-/* If the card has a latch for the interrupt vector (like the PA0HZP card)
-   use it to get the number of the chip that generated the int.
-   If not: poll all defined chips.
+/* of course we could define a "bottom half" routine to do the job,
+   but since its structures are saved in an array instead of a linked
+   list we would get in trouble if it clashes with another driver.
+   IMHO we are fast enough with a timer routine called on the next 
+   timer-INT... Your opinions?
  */
 
-static void
-scc_isr(int irq, void *dev_id, struct pt_regs *regs)
+static __inline__ void
+kick_rx_timer(struct scc_channel *scc)
 {
-	register unsigned char vector;	
-	register struct scc_channel *scc;
-	register io_port q;
-	register io_port *p;
-	register int k;
-
+	del_timer(&(scc->rx_t));
+	scc->rx_t.expires = jiffies + 1;
+	scc->rx_t.function = scc_rx_timer;
+	scc->rx_t.data = (unsigned long) scc;
+	add_timer(&scc->rx_t);
+}
+#endif
 
-	cli();
+static __inline__ void
+scc_notify(struct scc_channel *scc, int event)
+{
+	struct mbuf *bp;
 	
-	if (Vector_Latch)
-	{
-	    	while(1)			   /* forever...? */
-    		{ 
-			Outb(Vector_Latch, 0);      /* Generate INTACK */
-        
-			/* Read the vector */
-			if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break; 
-						/* ...not forever! */
-          
-			/* Extract channel number and status from vector. */
-			/* Isolate channel number */
-			/* Call handler */
-	        
-			if (vector & 0x01) break; 
-        	 
-		        scc=&SCC_Info[(((vector>>1)&0x7c)^0x04) >> 2];
-        
-			if (!scc->tty) break;
-
-			scc_isr_dispatch(scc, vector);
-			
-			Outb(scc->ctrl,0x38);              /* Reset Highest IUS" opcode to WR0 */
-		}  
-	    	
-		sti();	
+	if (scc->kiss.fulldup != KISS_DUPLEX_OPTIMA)
 		return;
-	}
-	
 	
-	/* Find the SCC generating the interrupt by polling all attached SCCs
-	 * reading RR3A (the interrupt pending register)
-	 */
+	bp = scc_get_buffer(scc, BT_RECEIVE);
 
-	k = 0;
-	p = SCC_ctrl;
-	
-	while (k++ < Nchips)
-	{
-		if (!(q=*p++)) break;
-		
-		Outb(q,3);
+	if (bp == NULLBUF)
+		return;
 		
-		if (Inb(q))
-		{
-			if (!(q=*p++)) break;
-			
-			Outb(q,2);
-			vector=Inb(q);                  /* Read the vector */
-        
-		        /* Extract channel number and status from vector. */
-		        /* Isolate channel number */
-		        /* Call handler */
-        
+	bp->data[0] = PARAM_HWEVENT;
+	bp->data[1] = event;
+	bp->cnt += 2;
 
-			if (vector & 1) break; 
-        
-		        scc = &SCC_Info[(((vector >> 1) & 0x7c) ^ 0x04) >> 2];
-        
-		        if (!scc->tty) break;
-
-			/* Isolate status info from vector, call handler */
-			
-			scc_isr_dispatch(scc, vector);
-
-			k = 0;
-			p = SCC_ctrl;
+	if (scc->stat.is_netdev)
+	{
+#ifdef CONFIG_SCC_DEV
+		scc_net_rx(scc, bp);
+		scc_enqueue_buffer(&scc->rx_buffer_pool, bp);
+#endif
+	} else {
+#ifdef CONFIG_SCC_TTY
+		scc_enqueue_buffer(&scc->rx_queue, bp);
+		scc->stat.rx_queued++;
+		kick_rx_timer(scc);
+#endif
+	}
+}
 
-	     	} else p++;
-	}    
+static __inline__ void
+flush_rx_FIFO(struct scc_channel *scc)
+{
+	int k;
 	
-	sti();
+	for (k=0; k<3; k++)
+		Inb(scc->data);
+		
+	if(scc->rx_bp != NULLBUF)	/* did we receive something? */
+	{
+		scc->stat.rxerrs++;  /* then count it as an error */
+		scc_enqueue_buffer(&scc->rx_buffer_pool, scc->rx_bp);
+		
+		scc->rx_bp = NULLBUF;
+	}
 }
 
 
-
 /* ----> four different interrupt handlers for Tx, Rx, changing of	*/
 /*       DCD/CTS and Rx/Tx errors					*/
 
-
 /* Transmitter interrupt handler */
-static void
-scc_txint(register struct scc_channel *scc)
+static __inline__ void
+scc_txint(struct scc_channel *scc)
 {
-	register struct mbuf *bp;
+	struct mbuf *bp;
 
 	scc->stat.txints++;
 
 	bp = scc->tx_bp;
 	
+	/* send first octet */
+	
 	if (bp == NULLBUF)
 	{
-		do 
+		do
 		{
 			if (bp != NULLBUF)
+			{
 				scc_enqueue_buffer(&scc->tx_buffer_pool, bp);
+				scc->stat.tx_queued--;
+			}
 
 			bp = scc_dequeue_buffer(&scc->tx_queue);
 			
 			if (bp == NULLBUF)
 			{
-				scc->stat.tx_state = TXS_BUSY;
-				scc->t_tail = scc->kiss.tailtime;
-				
+				scc_tx_done(scc);
 				Outb(scc->ctrl, RES_Tx_P);	/* clear int */
 				return;
 			}
 			
-			if ( scc->kiss.not_slip && (bp->cnt > 0) )
+			if (bp->cnt > 0)
 			{
 				bp->rw_ptr++;
 				bp->cnt--;
@@ -859,57 +852,47 @@
 			
 		} while (bp->cnt < 1);
 		
-		
-		Outb(scc->ctrl, RES_Tx_CRC);	/* reset CRC generator */
+		scc->tx_bp = bp;
+		scc->stat.tx_state = TXS_ACTIVE;
+
+		OutReg(scc->ctrl, R0, RES_Tx_CRC);
+						/* reset CRC generator */
 		or(scc,R10,ABUNDER);		/* re-install underrun protection */
-		Outb(scc->data,*bp->rw_ptr);	/* send byte */
+		Outb(scc->data,*bp->rw_ptr++);	/* send byte */
+
 		if (!scc->enhanced)		/* reset EOM latch */
-			Outb(scc->ctrl, RES_EOM_L);
-		
-		scc->tx_bp = bp;
-		scc->stat.tx_state = TXS_ACTIVE; /* next byte... */
-	} else
+			Outb(scc->ctrl,RES_EOM_L);
+
+		bp->cnt--;
+		return;
+	}
+	
+	/* End Of Frame... */
+	
 	if (bp->cnt <= 0)
 	{
-		if (--scc->stat.tx_queued < 0) scc->stat.tx_queued = 0;
-		
 		Outb(scc->ctrl, RES_Tx_P);	/* reset pending int */
 		cl(scc, R10, ABUNDER);		/* send CRC */
 		scc_enqueue_buffer(&scc->tx_buffer_pool, bp);
+		scc->stat.tx_queued--;
 		scc->tx_bp = NULLBUF;
 		scc->stat.tx_state = TXS_NEWFRAME; /* next frame... */
 		return;
-	} else {
-		Outb(scc->data,*bp->rw_ptr);		
-	}
+	} 
 	
+	/* send octet */
+	
+	Outb(scc->data,*bp->rw_ptr);		
 	bp->rw_ptr++;			/* increment pointer */
 	bp->cnt--;                      /* decrease byte count */
 }
 
-static inline void
-flush_FIFO(register struct scc_channel *scc)
+
+/* External/Status interrupt handler */
+static __inline__ void
+scc_exint(struct scc_channel *scc)
 {
-	register int k;
-	
-	for (k=0; k<3; k++)
-		Inb(scc->data);
-		
-	if(scc->rx_bp != NULLBUF)	/* did we receive something? */
-	{
-		scc->stat.rxerrs++;  /* then count it as an error */
-		scc_enqueue_buffer(&scc->rx_buffer_pool, scc->rx_bp);
-		
-		scc->rx_bp = NULLBUF;
-	}
-}
-
-
-/* External/Status interrupt handler */
-static void
-scc_exint(register struct scc_channel *scc)
-{
-	register unsigned char status,changes,chg_and_stat;
+	unsigned char status,changes,chg_and_stat;
 
 	scc->stat.exints++;
 
@@ -920,9 +903,9 @@
 	/* ABORT: generated whenever DCD drops while receiving */
 
 	if (chg_and_stat & BRK_ABRT)		/* Received an ABORT */
-		flush_FIFO(scc);
-			
-		
+		flush_rx_FIFO(scc);
+
+
 	/* DCD: on = start to receive packet, off = ABORT condition */
 	/* (a successfully received packet generates a special condition int) */
 	
@@ -936,69 +919,67 @@
 			or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */
 		} else {                        /* DCD is now OFF */
 			cl(scc,R3,ENT_HM|RxENABLE); /* disable the receiver */
-			flush_FIFO(scc);
+			flush_rx_FIFO(scc);
 		}
+		
+		if (!scc->kiss.softdcd)
+			scc_notify(scc, (status & DCD)? HWEV_DCD_ON:HWEV_DCD_OFF);
+	}
+	
+	/* HUNT: software DCD; on = waiting for SYNC, off = receiving frame */
+	
+	if (changes & SYNC_HUNT)
+	{
+		if (scc->kiss.softdcd)
+			scc_notify(scc, (status & SYNC_HUNT)? HWEV_DCD_OFF:HWEV_DCD_ON);
+		else
+			cl(scc,R15,SYNCIE);	/* oops, we were too lazy to disable this? */
 	}
 
 #ifdef notdef
-	/* CTS: use external TxDelay (what's that good for?!) */
-	
+	/* CTS: use external TxDelay (what's that good for?!)
+	 * Anyway: If we _could_ use it (BayCom USCC uses CTS for
+	 * own purposes) we _should_ use the "autoenable" feature
+	 * of the Z8530 and not this interrupt...
+	 */
+	 
 	if (chg_and_stat & CTS)			/* CTS is now ON */
-	{			
-		if (!Running(t_txdel) && scc->kiss.txdelay == 0) /* zero TXDELAY = wait for CTS */
-			scc->t_txdel = 0;	/* kick it! */		
-		
+	{
+		if (scc->kiss.txdelay == 0)	/* zero TXDELAY = wait for CTS */
+			scc_start_tx_timer(scc, t_txdelay, 0);
 	}
 #endif
 	
-	if ( (status & TxEOM) && (scc->stat.tx_state == TXS_ACTIVE) )
+	if (scc->stat.tx_state == TXS_ACTIVE && (status & TxEOM))
 	{
 		scc->stat.tx_under++;	  /* oops, an underrun! count 'em */
-		Outb(scc->ctrl, RES_Tx_P);
 		Outb(scc->ctrl, RES_EXT_INT);	/* reset ext/status interrupts */
-		scc->t_maxk = 1;
-		
+
 		if (scc->tx_bp != NULLBUF)
 		{
 			scc_enqueue_buffer(&scc->tx_buffer_pool, scc->tx_bp);
+			scc->stat.tx_queued--;
 			scc->tx_bp = NULLBUF;
 		}
 		
-		if (--scc->stat.tx_queued < 0) scc->stat.tx_queued = 0;
 		or(scc,R10,ABUNDER);
+		scc_start_tx_timer(scc, t_txdelay, 1);	/* restart transmission */
 	}
 		
-	if (status & ZCOUNT)		   /* Oops? */
-	{
-		scc->stat.tx_under = 9999;  /* errr... yes. */
-		Outb(scc->ctrl, RES_Tx_P); /* just to be sure */
-		scc->t_maxk = 1;
-		
-		if (scc->tx_bp != NULLBUF)
-		{
-			scc_enqueue_buffer(&scc->tx_buffer_pool, scc->tx_bp);
-			scc->tx_bp = NULLBUF;
-		}
-
-		if (--scc->stat.tx_queued < 0) scc->stat.tx_queued = 0;
-		scc->kiss.tx_inhibit = 1;	/* don't try it again! */
-	}
-		
-	
 	scc->status = status;
 	Outb(scc->ctrl,RES_EXT_INT);
 }
 
 
 /* Receiver interrupt handler */
-static void
-scc_rxint(register struct scc_channel *scc)
+static __inline__ void
+scc_rxint(struct scc_channel *scc)
 {
-	register struct mbuf *bp;
+	struct mbuf *bp;
 
 	scc->stat.rxints++;
 
-	if( Running(t_maxk) && !(scc->kiss.fulldup))
+	if((scc->wreg[5] & RTS) && scc->kiss.fulldup == KISS_DUPLEX_HALF)
 	{
 		Inb(scc->data);		/* discard char */
 		or(scc,R3,ENT_HM);	/* enter hunt mode for next flag */
@@ -1012,20 +993,22 @@
 		bp = scc_get_buffer(scc, BT_RECEIVE);
 		if (bp == NULLBUF)
 		{
-			printk("scc_rxint(): panic --- cannot get a buffer\n");
 			Inb(scc->data);
 			or(scc, R3, ENT_HM);
-			scc->stat.nospace++;
 			return;
 		}
 		
 		scc->rx_bp = bp;
+		
+		*bp->rw_ptr=0;		/* KISS data */
+		bp->rw_ptr++;
+		bp->cnt++;
 	}
 	
 	if (bp->cnt > scc->stat.bufsize)
 	{
 #ifdef notdef
-		printk("scc_rxint(): oops, received huge frame...\n");
+		printk(KERN_DEBUG "z8530drv: oops, scc_rxint() received huge frame...\n");
 #endif
 		scc_enqueue_buffer(&scc->rx_buffer_pool, bp);
 		scc->rx_bp = NULLBUF;
@@ -1040,33 +1023,13 @@
 	bp->cnt++;
 }
 
-/* kick rx_timer (try to send received frame or part of it ASAP) */
-
-/* of course we could define a "bottom half" routine to do the job,
-   but since its structures are saved in an array instead of a linked
-   list we would get in trouble if it clashes with another driver or
-   when we try to modularize the driver. IMHO we are fast enough
-   with a timer routine called on the next timer-INT... Your opinions?
- */
-
-static inline void
-kick_rx_timer(register struct scc_channel *scc)
-{
-	if (scc->rx_t.next)
-		del_timer(&(scc->rx_t));
-		
-	scc->rx_t.expires = jiffies + 1;
-	scc->rx_t.function = scc_rx_timer;
-	scc->rx_t.data = (unsigned long) scc;
-	add_timer(&scc->rx_t);
-}
 
 /* Receive Special Condition interrupt handler */
-static void
-scc_spint(register struct scc_channel *scc)
+static __inline__ void
+scc_spint(struct scc_channel *scc)
 {
-	register unsigned char status;
-	register struct mbuf *bp;
+	unsigned char status;
+	struct mbuf *bp;
 
 	scc->stat.spints++;
 
@@ -1083,7 +1046,7 @@
 		if (bp) scc_enqueue_buffer(&scc->rx_buffer_pool, bp);
 		scc->rx_bp = NULLBUF;
 	}
-	
+
 	if(status & END_FR && bp != NULLBUF)	/* end of frame */
 	{
 		/* CRC okay, frame ends on 8 bit boundary and received something ? */
@@ -1092,23 +1055,141 @@
 		{
 			/* ignore last received byte (first of the CRC bytes) */
 			bp->cnt--;
-			
-			scc_enqueue_buffer(&scc->rx_queue, bp);
+
+			if (scc->stat.is_netdev)
+			{
+#ifdef CONFIG_SCC_DEV
+				scc_net_rx(scc, bp);
+				scc_enqueue_buffer(&scc->rx_buffer_pool, bp);
+#endif
+			} else {
+#ifdef CONFIG_SCC_TTY
+				scc_enqueue_buffer(&scc->rx_queue, bp);
+				scc->stat.rx_queued++;
+				kick_rx_timer(scc);
+#endif
+			}
+
 			scc->rx_bp = NULLBUF;
 			scc->stat.rxframes++;
-			scc->stat.rx_queued++;
-			kick_rx_timer(scc);
 		} else {				/* a bad frame */
 			scc_enqueue_buffer(&scc->rx_buffer_pool, bp);
 			scc->rx_bp = NULLBUF;
 			scc->stat.rxerrs++;
 		}
+	} 
+
+	Outb(scc->ctrl,ERR_RES);
+}
+
+
+/* ----> interrupt service routine for the Z8530 <---- */
+
+static void
+scc_isr_dispatch(struct scc_channel *scc, int vector)
+{
+	switch (vector & VECTOR_MASK)
+	{
+		case TXINT: scc_txint(scc); break;
+		case EXINT: scc_exint(scc); break;
+		case RXINT: scc_rxint(scc); break;
+		case SPINT: scc_spint(scc); break;
 	}
+}
+
+/* If the card has a latch for the interrupt vector (like the PA0HZP card)
+   use it to get the number of the chip that generated the int.
+   If not: poll all defined chips.
+ */
+
+#define SCC_IRQTIMEOUT 30000
+
+static void
+scc_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned char vector;	
+	struct scc_channel *scc;
+	struct scc_ctrl *ctrl;
+	int k;
 	
-	Outb(scc->ctrl,ERR_RES);
+	scc_cli(irq);
+
+	if (Vector_Latch)
+	{
+	    	for(k=0; k < SCC_IRQTIMEOUT; k++)
+    		{
+			Outb(Vector_Latch, 0);      /* Generate INTACK */
+        
+			/* Read the vector */
+			if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break; 
+			if (vector & 0x01) break;
+        	 
+		        scc=&SCC_Info[vector >> 3 ^ 0x01];
+			if (!scc->tty && !scc->dev) break;
+
+			scc_isr_dispatch(scc, vector);
+
+			OutReg(scc->ctrl,R0,RES_H_IUS);              /* Reset Highest IUS */
+		}  
+		scc_sti(irq);
+
+		if (k == SCC_IRQTIMEOUT)
+			printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?\n");
+
+		return;
+	}
+
+	/* Find the SCC generating the interrupt by polling all attached SCCs
+	 * reading RR3A (the interrupt pending register)
+	 */
+
+	ctrl = SCC_ctrl;
+	while (ctrl->chan_A)
+	{
+		if (ctrl->irq != irq)
+		{
+			ctrl++;
+			continue;
+		}
+
+		scc = NULL;
+		for (k = 0; InReg(ctrl->chan_A,R3) && k < SCC_IRQTIMEOUT; k++)
+		{
+			vector=InReg(ctrl->chan_B,R2);	/* Read the vector */
+			if (vector & 0x01) break; 
+
+			scc = &SCC_Info[vector >> 3 ^ 0x01];
+		        if (!scc->tty && !scc->dev) break;
+
+			scc_isr_dispatch(scc, vector);
+		}
+
+		if (k == SCC_IRQTIMEOUT)
+		{
+			printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?!\n");
+			break;
+		}
+
+		/* This looks wierd and it is. At least the BayCom USCC doesn't
+		 * use the Interrupt Daisy Chain, thus we'll have to start
+		 * all over again to be sure not to miss an interrupt from 
+		 * (any of) the other chip(s)...
+		 * Honestly, the situation *is* braindamaged...
+		 */
+
+		if (scc != NULL)
+		{
+			OutReg(scc->ctrl,R0,RES_H_IUS);
+			ctrl = SCC_ctrl; 
+		} else
+			ctrl++;
+	}
+	
+	scc_sti(irq);
 }
 
 
+
 /* ******************************************************************** */
 /* *			Init Channel					*/
 /* ******************************************************************** */
@@ -1116,30 +1197,31 @@
 
 /* ----> set SCC channel speed <---- */
 
-static inline void set_brg(register struct scc_channel *scc, unsigned int tc)
+static __inline__ void 
+set_brg(struct scc_channel *scc, unsigned int tc)
 {
-	unsigned long flags;
-
- 	save_flags(flags); cli();      	/* 2-step register accesses... */
-
 	cl(scc,R14,BRENABL);		/* disable baudrate generator */
 	wr(scc,R12,tc & 255);		/* brg rate LOW */
 	wr(scc,R13,tc >> 8);   		/* brg rate HIGH */
 	or(scc,R14,BRENABL);		/* enable baudrate generator */
-
-	restore_flags(flags);
 }
 
-static inline void set_speed(register struct scc_channel *scc)
+static __inline__ void 
+set_speed(struct scc_channel *scc)
 {
+	disable_irq(scc->irq);
+
 	if (scc->modem.speed > 0)	/* paranoia... */
 		set_brg(scc, (unsigned) (scc->clock / (scc->modem.speed * 64)) - 2);
+
+	enable_irq(scc->irq);
 }
 
 
 /* ----> initialize a SCC channel <---- */
 
-static inline void init_brg(register struct scc_channel *scc)
+static __inline__ void 
+init_brg(struct scc_channel *scc)
 {
 	wr(scc, R14, BRSRC);				/* BRG source = PCLK */
 	OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]);	/* DPLL source = BRG */
@@ -1192,14 +1274,17 @@
  */
  
 static void
-init_channel(register struct scc_channel *scc)
+init_channel(struct scc_channel *scc)
 {
-	unsigned long flags;
+	disable_irq(scc->irq);
 	
-	if (scc->rx_t.next) del_timer(&(scc->rx_t));
-	if (scc->tx_t.next) del_timer(&(scc->tx_t));
+#ifdef CONFIG_SCC_TTY
+	del_timer(&scc->rx_t);
+#endif
+	del_timer(&scc->tx_t);
+	del_timer(&scc->tx_wdog);
+
 
-	save_flags(flags); cli();
 
 	wr(scc,R4,X1CLK|SDLC);		/* *1 clock, SDLC mode */
 	wr(scc,R1,0);			/* no W/REQ operation */
@@ -1273,9 +1358,12 @@
 		or(scc,R3,ENT_HM|RxENABLE);	/* enable the receiver, hunt mode */
 	}
 	
-	/* enable CTS (not for Baycom), ABORT & DCD interrupts */
-	wr(scc,R15,((scc->brand & BAYCOM) ? 0 : CTSIE)|BRKIE|DCDIE|TxUIE);
-	
+	/* enable ABORT, DCD & SYNC/HUNT interrupts */
+
+	wr(scc,R15, BRKIE|TxUIE|DCDIE);
+	if (scc->kiss.softdcd)
+		or(scc,R15, SYNCIE);
+
 	Outb(scc->ctrl,RES_EXT_INT);	/* reset ext/status interrupts */
 	Outb(scc->ctrl,RES_EXT_INT);	/* must be done twice */
 
@@ -1287,7 +1375,7 @@
 	
 	scc_init_timer(scc);
 			
-	restore_flags(flags);
+	enable_irq(scc->irq);
 }
 
 
@@ -1305,21 +1393,29 @@
 scc_key_trx(struct scc_channel *scc, char tx)
 {
 	unsigned int time_const;
-
-	if (scc->modem.speed < baud_table[1]) 
-		scc->modem.speed = 1200;
 		
 	if (scc->brand & PRIMUS)
 		Outb(scc->ctrl + 4, scc->option | (tx? 0x80 : 0));
-	
+
+	if (scc->modem.speed < 300) 
+		scc->modem.speed = 1200;
+
 	time_const = (unsigned) (scc->clock / (scc->modem.speed * (tx? 2:64))) - 2;
-	
+
+	disable_irq(scc->irq);
+
+	if (tx)
+	{
+		or(scc, R1, TxINT_ENAB);	/* t_maxkeyup may have reset these */
+		or(scc, R15, TxUIE);
+	}
+
 	if (scc->modem.clocksrc == CLK_DPLL)
-	{				/* simplex operation */
+	{				/* force simplex operation */
 		if (tx)
 		{
-			cl(scc,R3,RxENABLE|ENT_HM);	/* then switch off receiver */
-			
+			cl(scc, R3, RxENABLE|ENT_HM);	/* switch off receiver */
+			cl(scc, R15, DCDIE);		/* No DCD changes, please */
 			set_brg(scc, time_const);	/* reprogram baudrate generator */
 
 			/* DPLL -> Rx clk, BRG -> Tx CLK, TRxC mode output, TRxC = BRG */
@@ -1335,21 +1431,140 @@
 			wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP);
 			
 			or(scc,R3,RxENABLE|ENT_HM);
+			or(scc,R15, DCDIE);
 		}
 	} else {
 		if (tx)
+		{
+			if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
+			{
+				cl(scc, R3, RxENABLE);
+				cl(scc, R15, DCDIE);
+			}
+				
+				
 			or(scc,R5,RTS|TxENAB);		/* enable tx */
-		else
+		} else {
 			cl(scc,R5,RTS|TxENAB);		/* disable tx */
+			
+			if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
+			{
+				or(scc, R3, RxENABLE|ENT_HM);
+				or(scc, R15, DCDIE);
+			}
+		}
+	}
+
+	enable_irq(scc->irq);
+}
+
+
+/* ----> SCC timer interrupt handler and friends. <---- */
+
+static void 
+scc_start_tx_timer(struct scc_channel *scc, void (*handler)(unsigned long), unsigned long when)
+{
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	del_timer(&scc->tx_t);
+
+	if (when == 0)
+	{
+		handler((unsigned long) scc);
+	} else 
+	if (when != TIMER_OFF)
+	{
+		scc->tx_t.data = (unsigned long) scc;
+		scc->tx_t.function = handler;
+		scc->tx_t.expires = jiffies + (when*HZ)/100;
+		add_timer(&scc->tx_t);
+	}
+	
+	restore_flags(flags);	
+}
+
+static void
+scc_start_defer(struct scc_channel *scc)
+{
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	del_timer(&scc->tx_wdog);
+	
+	if (scc->kiss.maxdefer != 0 && scc->kiss.maxdefer != TIMER_OFF)
+	{
+		scc->tx_wdog.data = (unsigned long) scc;
+		scc->tx_wdog.function = t_busy;
+		scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxdefer;
+		add_timer(&scc->tx_wdog);
+	}
+	restore_flags(flags);
+}
+
+static void
+scc_start_maxkeyup(struct scc_channel *scc)
+{
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	del_timer(&scc->tx_wdog);
+	
+	if (scc->kiss.maxkeyup != 0 && scc->kiss.maxkeyup != TIMER_OFF)
+	{
+		scc->tx_wdog.data = (unsigned long) scc;
+		scc->tx_wdog.function = t_maxkeyup;
+		scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup;
+		add_timer(&scc->tx_wdog);
 	}
+	
+	restore_flags(flags);
 }
 
+/* 
+ * This is called from scc_txint() when there are no more frames to send.
+ * Not exactly a timer function, but it is a close friend of the family...
+ */
+
+static void
+scc_tx_done(struct scc_channel *scc)
+{
+	/* 
+	 * trx remains keyed in fulldup mode 2 until t_idle expires.
+	 */
+				 
+	switch (scc->kiss.fulldup)
+	{
+		case KISS_DUPLEX_LINK:
+			scc->stat.tx_state = TXS_IDLE2;
+			if (scc->kiss.idletime != TIMER_OFF)
+			scc_start_tx_timer(scc, t_idle, scc->kiss.idletime*100);
+			break;
+		case KISS_DUPLEX_OPTIMA:
+			scc_notify(scc, HWEV_ALL_SENT);
+			break;
+		default:
+			scc->stat.tx_state = TXS_BUSY;
+			scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
+	}
+
+#ifdef CONFIG_SCC_DEV
+	if (scc->stat.is_netdev)
+ 		scc->dev->tbusy = 0;
+#endif
+}
 
-/* ----> SCC timer interrupt handler and friends. Will be called every 1/TPS s <---- */
 
 static unsigned char Rand = 17;
 
-static inline int is_grouped(register struct scc_channel *scc)
+static __inline__ int 
+is_grouped(struct scc_channel *scc)
 {
 	int k;
 	struct scc_channel *scc2;
@@ -1362,8 +1577,8 @@
 		scc2 = &SCC_Info[k];
 		grp2 = scc2->kiss.group;
 		
-		if (scc2 == scc || !(scc2->tty && grp2)) 
-			return 0;
+		if (scc2 == scc || !(scc2->tty && scc2->dev && grp2))
+			continue;
 		
 		if ((grp1 & 0x3f) == (grp2 & 0x3f))
 		{
@@ -1376,205 +1591,244 @@
 	}
 	return 0;
 }
-		
 
-static inline void dw_slot_timeout(register struct scc_channel *scc)
+/* DWAIT and SLOTTIME expired
+ *
+ * fulldup == 0:  DCD is active or Rand > P-persistence: start t_busy timer
+ *                else key trx and start txdelay
+ * fulldup == 1:  key trx and start txdelay
+ * fulldup == 2:  mintime expired, reset status or key trx and start txdelay
+ */
+
+static void 
+t_dwait(unsigned long channel)
 {
-	scc->t_dwait = TIMER_STOPPED;
-	scc->t_slot = TIMER_STOPPED;
+	struct scc_channel *scc = (struct scc_channel *) channel;
 	
-	if (!scc->kiss.fulldup)
+	if (scc->stat.tx_state == TXS_WAIT)
+	{
+		if (scc->tx_queue == NULL)
+		{
+			scc->stat.tx_state = TXS_IDLE;
+			return;
+		}
+
+		scc->stat.tx_state = TXS_BUSY;
+	}
+
+	if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
 	{
 		Rand = Rand * 17 + 31;
 		
 		if ( (scc->kiss.softdcd? !(scc->status & SYNC_HUNT):(scc->status & DCD))  || (scc->kiss.persist) < Rand || (scc->kiss.group && is_grouped(scc)) )
 		{
-			if (scc->t_mbusy == TIMER_STOPPED)
-				scc->t_mbusy = TPS * scc->kiss.maxdefer;
-				
-			scc->t_slot = scc->kiss.slottime;
+			scc_start_defer(scc);
+			scc_start_tx_timer(scc, t_dwait, scc->kiss.slottime);
 			return ;
-			
 		}
 	}
-	
+
 	if ( !(scc->wreg[R5] & RTS) )
 	{
-		scc->t_txdel = scc->kiss.txdelay;
 		scc_key_trx(scc, TX_ON);
+		scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay);
 	} else {
-		scc->t_txdel = 0;
+		scc_start_tx_timer(scc, t_txdelay, 0);
 	}
 }
 
 
+/* TXDELAY expired
+ *
+ * kick transmission by a fake scc_txint(scc), start 'maxkeyup' watchdog.
+ */
 
-
-static inline void txdel_timeout(register struct scc_channel *scc)
+static void 
+t_txdelay(unsigned long channel)
 {
-	scc->t_txdel = TIMER_STOPPED;
-	
-	scc->t_maxk = TPS * scc->kiss.maxkeyup;
-	
+	struct scc_channel *scc = (struct scc_channel *) channel;
+
+	scc_start_maxkeyup(scc);
+
 	if (scc->tx_bp == NULLBUF)
+	{
+		disable_irq(scc->irq);
 		scc_txint(scc);	
+		enable_irq(scc->irq);
+	}
 }
 	
 
+/* TAILTIME expired
+ *
+ * switch off transmitter. If we were stopped by Maxkeyup restart
+ * transmission after 'mintime' seconds
+ */
 
-static inline void tail_timeout(register struct scc_channel *scc)
+static void 
+t_tail(unsigned long channel)
 {
-	scc->t_tail = TIMER_STOPPED;
+	struct scc_channel *scc = (struct scc_channel *) channel;
 	
-	/* when fulldup is 0 or 1, switch off the transmitter.
-	 * when frames are still queued (because of transmit time limit),
-	 * restart the procedure to get the channel after MINTIME.
-	 * when fulldup is 2, the transmitter remains keyed and we
-	 * continue sending after waiting for waittime. IDLETIME is an 
-	 * idle timeout in this case.
-	 */ 
-	 
-	 if (scc->kiss.fulldup < 2)
-	 {
-	 	if (scc->tx_bp)		/* we had a timeout? */
-	 	{
-	 		scc->stat.tx_state = TXS_BUSY;
-	 		scc->t_dwait = TPS * scc->kiss.mintime; /* try again */
-	 	}
-	 	
-	 	scc->stat.tx_state = TXS_IDLE;
-		scc->t_maxk = TIMER_STOPPED;
-	 	scc_key_trx(scc, TX_OFF);
-	 	return;
-	 }
-	 
-	 if (scc->tx_bp)			/* maxkeyup expired */ /* ?! */
-	 {
-	 	scc->stat.tx_state = TXS_BUSY;
-	 	scc->t_txdel = TPS * scc->kiss.waittime;
-	 }
-	 else
-	 
-	 	scc->t_idle = TPS * scc->kiss.idletime;
+ 	del_timer(&scc->tx_wdog);
+ 	scc_key_trx(scc, TX_OFF);
+
+ 	if (scc->stat.tx_state == TXS_TIMEOUT)		/* we had a timeout? */
+ 	{
+ 		scc->stat.tx_state = TXS_WAIT;
+ 
+  		if (scc->tx_bp != NULLBUF)
+	 		scc_enqueue_buffer(&scc->tx_queue, scc->tx_bp);
+
+ 		scc->tx_bp = NULLBUF;
+
+ 		if (scc->kiss.mintime != TIMER_OFF)	/* try it again */
+ 			scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100);
+ 		else
+ 			scc_start_tx_timer(scc, t_dwait, 0);
+ 		return;
+ 	}
+ 	
+ 	scc->stat.tx_state = TXS_IDLE;
+ 
+#ifdef CONFIG_SCC_DEV
+ 	if (scc->stat.is_netdev)
+		scc->dev->tbusy = 0;
+#endif
 }
 
 
-static inline void busy_timeout(register struct scc_channel *scc)
+/* BUSY timeout
+ *
+ * throw away send buffers if DCD remains active too long.
+ */
+
+static void 
+t_busy(unsigned long channel)
 {
-#ifdef	THROW_AWAY_AFTER_BUSY_TIMEOUT
-	register struct mbuf *bp;		/* not tested */
+	struct scc_channel *scc = (struct scc_channel *) channel;
+	struct mbuf *bp;
+
+	del_timer(&scc->tx_t);
 
-	while (bp = scc_dequeue_buffer(&scc->tx_queue))
+	while ((bp = scc_dequeue_buffer(&scc->tx_queue)) != NULLBUF)
 		scc_enqueue_buffer(&scc->tx_buffer_pool, bp);
+
+	scc->stat.tx_queued = 0;
+	scc->stat.txerrs++;
 		
 	scc->tx_queue = NULLBUF;
 	scc->stat.tx_state = TXS_IDLE;
-	
-#else
-	scc->t_txdel = scc->kiss.txdelay;	/* brute force ... */
+
+#ifdef CONFIG_SCC_DEV
+ 	if (scc->stat.is_netdev)
+ 		scc->dev->tbusy = 0;
 #endif
-	scc->t_mbusy = TIMER_STOPPED;
-	
 }
 
+/* MAXKEYUP timeout
+ *
+ * this is our watchdog.
+ */
 
-static inline void maxk_idle_timeout(register struct scc_channel *scc)
-{
-	scc->t_maxk = TIMER_STOPPED;
-	scc->t_idle = TIMER_STOPPED;
-	
-	scc->stat.tx_state = TXS_BUSY;
-	scc->t_tail = scc->kiss.tailtime;
-}
-
-static void
-scc_tx_timer(unsigned long channel)
+static void 
+t_maxkeyup(unsigned long channel)
 {
-	register struct scc_channel *scc;
+	struct scc_channel *scc = (struct scc_channel *) channel;
 	unsigned long flags;
-		
 
-	scc = (struct scc_channel *) channel;
-		
-	if (scc->tty && scc->init)
-	{		
-		save_flags(flags); cli();
-		
-		/* KISS-TNC emulation */
-		
-		if (Expired(t_dwait)) dw_slot_timeout(scc)	; else
-		if (Expired(t_slot))  dw_slot_timeout(scc)	; else
-		if (Expired(t_txdel)) txdel_timeout(scc)   	; else
-		if (Expired(t_tail))  tail_timeout(scc)	   	;
-			
-		/* watchdogs */
-		
-		if (Expired(t_mbusy)) busy_timeout(scc);
-		if (Expired(t_maxk))  maxk_idle_timeout(scc);
-		if (Expired(t_idle))  maxk_idle_timeout(scc);
-			
-		restore_flags(flags);
+	save_flags(flags);
+	cli();
+
+	if (scc->stat.is_netdev)
+	{
+#ifdef CONFIG_SCC_DEV
+		/* 
+		 * let things settle down before we start to
+		 * accept new data.
+		 */
+		scc->dev->tbusy = 1;
+		scc->dev->trans_start = jiffies;
+#endif
 	}
+
+	del_timer(&scc->tx_t);
+
+	cl(scc, R1, TxINT_ENAB);	/* force an ABORT, but don't */
+	cl(scc, R15, TxUIE);		/* count it. */
+	OutReg(scc->ctrl, R0, RES_Tx_P);
+
+	restore_flags(flags);
+
+	scc->stat.txerrs++;
+	scc->stat.tx_state = TXS_TIMEOUT;
+	scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
+}
+
+/* IDLE timeout
+ *
+ * in fulldup mode 2 it keys down the transmitter after 'idle' seconds
+ * of inactivity. We will not restart transmission before 'mintime'
+ * expires.
+ */
+
+static void 
+t_idle(unsigned long channel)
+{
+	struct scc_channel *scc = (struct scc_channel *) channel;
 	
-	scc->tx_t.expires = jiffies + HZ/TPS;
-	add_timer(&scc->tx_t);
+	del_timer(&scc->tx_wdog);
+	scc_key_trx(scc, TX_OFF);
+
+	if (scc->kiss.mintime != TIMER_OFF)
+		scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100);
+	scc->stat.tx_state = TXS_WAIT;
 }
-			
 
+
+#ifdef CONFIG_SCC_TTY
 static void
 scc_rx_timer(unsigned long channel)
 {
-	register struct scc_channel *scc;
+	struct scc_channel *scc;
 	
 	scc = (struct scc_channel *) channel;
 	
 	if (scc->rx_queue && scc->throttled)
 	{
-		scc->rx_t.expires = jiffies + HZ/TPS;
+		scc->rx_t.expires = jiffies + HZ/25;	/* 40 msec */
 		add_timer(&scc->rx_t);
 		return;
 	}
 	
 	kiss_encode(scc);
-	
+
 	if (scc->rx_queue && !scc->throttled)
 	{
 
-		printk("z8530drv: warning: %s should be throttled\n", 
-		       kdevname(scc->tty->device));
-			       
-		scc->rx_t.expires = jiffies + HZ/TPS;
+		printk(KERN_DEBUG "z8530drv: warning, %s should be throttled\n", 
+		       SCC_DEVNAME);
+		       
+		scc->rx_t.expires = jiffies + HZ/25;	/* 40 msec */
 		add_timer(&scc->rx_t);
 	}
 }
+#endif
 
 static void
 scc_init_timer(struct scc_channel *scc)
 {
 	unsigned long flags;
 	
-	save_flags(flags); cli();
-	
-	Stop_Timer(t_dwait);
-	Stop_Timer(t_slot);
-	Stop_Timer(t_txdel);
-	Stop_Timer(t_tail);
-	Stop_Timer(t_mbusy);
-	Stop_Timer(t_maxk);
-	Stop_Timer(t_idle);
+	save_flags(flags); 
+	cli();
 	
 	scc->stat.tx_state = TXS_IDLE;
 	
-	if (scc->tx_t.next) 
-		del_timer(&scc->tx_t);
-	
-	scc->tx_t.data = (unsigned long) scc;
-	scc->tx_t.function = scc_tx_timer;
-	scc->tx_t.expires = jiffies + HZ/TPS;
-	add_timer(&scc->tx_t);
-	
+#ifdef CONFIG_SCC_TTY	
 	scc->rx_t.data = (unsigned long) scc;
 	scc->rx_t.function = scc_rx_timer;
+#endif
 	
 	restore_flags(flags);
 }
@@ -1586,54 +1840,111 @@
 
 
 /*
- * this will set the "kiss" parameters through kiss itself
+ * this will set the "kiss" parameters through KISS commands or ioctl()
  */
- 
-static void
-kiss_set_param(struct scc_channel *scc,char cmd, unsigned int val)
+
+#define CAST(x) (unsigned long)(x)
+
+static unsigned int
+kiss_set_param(struct scc_channel *scc, unsigned int cmd, unsigned int arg)
 {
+	int dcd;
+
+	switch (cmd)
+	{
+		case PARAM_TXDELAY:	scc->kiss.txdelay=arg;		break;
+		case PARAM_PERSIST:	scc->kiss.persist=arg;		break;
+		case PARAM_SLOTTIME:	scc->kiss.slottime=arg;		break;
+		case PARAM_TXTAIL:	scc->kiss.tailtime=arg;		break;
+		case PARAM_FULLDUP:	scc->kiss.fulldup=arg;		break;
+		case PARAM_DTR:		break; /* does someone need this? */
+		case PARAM_GROUP:	scc->kiss.group=arg;		break;
+		case PARAM_IDLE:	scc->kiss.idletime=arg;		break;
+		case PARAM_MIN:		scc->kiss.mintime=arg;		break;
+		case PARAM_MAXKEY:	scc->kiss.maxkeyup=arg;		break;
+		case PARAM_WAIT:	scc->kiss.waittime=arg;		break;
+		case PARAM_MAXDEFER:	scc->kiss.maxdefer=arg;		break;
+		case PARAM_TX:		scc->kiss.tx_inhibit=arg;	break;
+
+		case PARAM_SOFTDCD:	
+			scc->kiss.softdcd=arg;
+			if (arg)
+				or(scc, R15, SYNCIE);
+			else
+				cl(scc, R15, SYNCIE);
+			break;
+				
+		case PARAM_SPEED:
+			if (arg < 256)
+				scc->modem.speed=arg*100;
+			else
+				scc->modem.speed=arg;
+
+			if (scc->stat.tx_state == 0)	/* only switch baudrate on rx... ;-) */
+				set_speed(scc);
+			break;
+			
+		case PARAM_RTS:	
+			if ( !(scc->wreg[R5] & RTS) )
+			{
+				if (arg != TX_OFF)
+					scc_key_trx(scc, TX_ON);
+					scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay);
+			} else {
+				if (arg == TX_OFF)
+				{
+					scc->stat.tx_state = TXS_BUSY;
+					scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
+				}
+			}
+			break;
+			
+		case PARAM_HWEVENT:
+			dcd = (scc->kiss.softdcd? !(scc->status & SYNC_HUNT):(scc->status & DCD));
+			scc_notify(scc, dcd? HWEV_DCD_ON:HWEV_DCD_OFF);
+			break;
+
+		default:		return -EINVAL;
+	}
+	
+	return 0;
+}
 
-#define  VAL val=val*TPS/100
-#define SVAL val? val:TIMER_STOPPED
 
-	switch(cmd){
-	case PARAM_TXDELAY:
-		scc->kiss.txdelay = VAL; break;
-	case PARAM_PERSIST:
-		scc->kiss.persist = val; break;
-	case PARAM_SLOTTIME:
-		scc->kiss.slottime = VAL; break;
-	case PARAM_TXTAIL:
-		scc->kiss.tailtime = VAL; break;
-	case PARAM_FULLDUP:
-		scc->kiss.fulldup = val; break;
-	case PARAM_WAIT:
-		scc->kiss.waittime = VAL; break;
-	case PARAM_MAXKEY:
-		scc->kiss.maxkeyup = SVAL; break;
-	case PARAM_MIN:
-		scc->kiss.mintime = SVAL; break;
-	case PARAM_IDLE:
-		scc->kiss.idletime = val; break;
-	case PARAM_MAXDEFER:
-		scc->kiss.maxdefer = SVAL; break;
-	case PARAM_GROUP:
-		scc->kiss.group = val;  break;
-	case PARAM_TX:
-		scc->kiss.tx_inhibit = val;
-	case PARAM_SOFTDCD:
-		scc->kiss.softdcd = val;
+ 
+static unsigned long
+kiss_get_param(struct scc_channel *scc, unsigned int cmd)
+{
+	switch (cmd)
+	{
+		case PARAM_TXDELAY:	return CAST(scc->kiss.txdelay);
+		case PARAM_PERSIST:	return CAST(scc->kiss.persist);
+		case PARAM_SLOTTIME:	return CAST(scc->kiss.slottime);
+		case PARAM_TXTAIL:	return CAST(scc->kiss.tailtime);
+		case PARAM_FULLDUP:	return CAST(scc->kiss.fulldup);
+		case PARAM_SOFTDCD:	return CAST(scc->kiss.softdcd);
+		case PARAM_DTR:		return CAST((scc->wreg[R5] & DTR)? 1:0);
+		case PARAM_RTS:		return CAST((scc->wreg[R5] & RTS)? 1:0);
+		case PARAM_SPEED:	return CAST(scc->modem.speed);
+		case PARAM_GROUP:	return CAST(scc->kiss.group);
+		case PARAM_IDLE:	return CAST(scc->kiss.idletime);
+		case PARAM_MIN:		return CAST(scc->kiss.mintime);
+		case PARAM_MAXKEY:	return CAST(scc->kiss.maxkeyup);
+		case PARAM_WAIT:	return CAST(scc->kiss.waittime);
+		case PARAM_MAXDEFER:	return CAST(scc->kiss.maxdefer);
+		case PARAM_TX:		return CAST(scc->kiss.tx_inhibit);
+		default:		return NO_SUCH_PARAM;
 	}
-	return;
 
-#undef  VAL
-#undef SVAL
 }
 
+#undef CAST
+#undef SVAL
 
 /* interpret frame: strip CRC and decode KISS */
 
-static void kiss_interpret_frame(struct scc_channel * scc)
+static void 
+kiss_interpret_frame(struct scc_channel * scc)
 {
 	unsigned char kisscmd;
 	unsigned long flags;
@@ -1642,14 +1953,6 @@
 	bp = scc->kiss_decode_bp;
 	bp->rw_ptr = bp->data;
 	
-#ifdef DEBUG_BUFFERS
-	if (bp == NULLBUF)
-	{
-		printk("kiss_interpret_frame(): weird --- nothing to do.\n");
-		return;
-	}
-#endif
-	
 	if (bp->cnt < 2)
 	{
 		scc_enqueue_buffer(&scc->tx_buffer_pool, bp);
@@ -1657,15 +1960,8 @@
 		return;
 	}
 	
-	
-	
-	if (scc->kiss.not_slip)
-	{
-		kisscmd = *bp->rw_ptr;
-		bp->rw_ptr++;
-	} else {
-		kisscmd = 0;
-	}
+	kisscmd = *bp->rw_ptr;
+	bp->rw_ptr++;
 
 	if (kisscmd & 0xa0)
 	{
@@ -1679,10 +1975,8 @@
 		}
 	}
 	
-	
 	kisscmd &= 0x1f;
-	
-		
+			
 	if (kisscmd)
 	{
 		kiss_set_param(scc, kisscmd, *bp->rw_ptr);
@@ -1692,32 +1986,47 @@
 	}
 
 	scc_enqueue_buffer(&scc->tx_queue, bp);	/* enqueue frame */
+	scc->kiss_decode_bp = NULLBUF;
 	
 	scc->stat.txframes++;
 	scc->stat.tx_queued++;
-	scc->kiss_decode_bp = NULLBUF;
 
-	save_flags(flags); cli();
+	save_flags(flags); 
+	cli();
+
+	/*
+	 * start transmission if the trx state is idle or
+	 * t_idle hasn't expired yet. Use dwait/persistance/slottime
+	 * algorithm for normal halfduplex operation.
+	 */
 
-	if(scc->stat.tx_state == TXS_IDLE)
-	{      				/* when transmitter is idle */
+	if(scc->stat.tx_state == TXS_IDLE || scc->stat.tx_state == TXS_IDLE2)
+	{
 		scc->stat.tx_state = TXS_BUSY;
-		scc->t_dwait = (scc->kiss.fulldup? 0:scc->kiss.waittime);
+		if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
+			scc_start_tx_timer(scc, t_dwait, scc->kiss.waittime);
+		else
+			scc_start_tx_timer(scc, t_dwait, 0);
 	}
-	
+
 	restore_flags(flags);
 }
 
-static inline void kiss_store_byte(struct scc_channel *scc, unsigned char ch)
+#ifdef CONFIG_SCC_TTY
+static inline void 
+kiss_store_byte(struct scc_channel *scc, unsigned char ch)
 {
-	register struct mbuf *bp = scc->kiss_decode_bp;
+	struct mbuf *bp = scc->kiss_decode_bp;
+	static int k = 0;
 	
 	if (bp != NULLBUF)
 	{
 		if (bp->cnt > scc->stat.bufsize)
-			printk("kiss_decode(): frame too long\n");
-		else
 		{
+			if (!k++)
+				printk(KERN_NOTICE "z8530drv: KISS frame too long\n");
+		} else {
+			k = 0;
 			*bp->rw_ptr = ch;
 			bp->rw_ptr++;
 			bp->cnt++;
@@ -1725,7 +2034,8 @@
 	}
 }
 
-static inline int kiss_decode(struct scc_channel *scc, unsigned char ch)
+static inline int 
+kiss_decode(struct scc_channel *scc, unsigned char ch)
 {
 	switch (scc->stat.tx_kiss_state) 
 	{
@@ -1779,7 +2089,7 @@
 /* ----> Encode received data and write it to the flip-buffer  <---- */
 
 static void
-kiss_encode(register struct scc_channel *scc)
+kiss_encode(struct scc_channel *scc)
 {
 	struct mbuf *bp;
 	struct tty_struct * tty = scc->tty;
@@ -1825,10 +2135,6 @@
 		if (scc->stat.rx_kiss_state == KISS_IDLE)
 		{
 			tty_insert_flip_char(tty, FEND, 0);
-			
-			if (scc->kiss.not_slip)
-				tty_insert_flip_char(tty, 0, 0);
-					
 			scc->stat.rx_kiss_state = KISS_RXFRAME;
 		}
 				
@@ -1852,6 +2158,7 @@
 		
  	queue_task(&tty->flip.tqueue, &tq_timer); /* kick it... */
 }
+#endif /* CONFIG_SCC_TTY */
 
 
 /* ******************************************************************* */
@@ -1868,7 +2175,7 @@
 	char *flag;
 
 
-	printk("Init Z8530 driver: %u channels, IRQ", Nchips*2);
+	printk(KERN_INFO "Init Z8530 driver: %u channels, IRQ", Nchips*2);
 	
 	flag=" ";
 	for (k = 0; k < 16; k++)
@@ -1885,9 +2192,7 @@
 	{
 		scc=&SCC_Info[2*chip];
 		if (!scc->ctrl) continue;
-			
-		save_flags(flags); cli();	/* because of 2-step accesses */
-		
+
 		/* Special SCC cards */
 
 		if(scc->brand & EAGLE)			/* this is an EAGLE card */
@@ -1901,12 +2206,15 @@
 
 		/* some general init we can do now */
 		
+		save_flags(flags);
+		cli();
+		
 		Outb(scc->ctrl, 0);
 		OutReg(scc->ctrl,R9,FHWRES);		/* force hardware reset */
 		udelay(100);				/* give it 'a bit' more time than required */
 		wr(scc, R2, chip*16);			/* interrupt vector */
 		wr(scc, R9, VIS);			/* vector includes status */
-
+		
         	restore_flags(flags);
         }
 
@@ -1920,18 +2228,17 @@
 /* ******************************************************************** */
 
 
-
 /* scc_paranoia_check(): warn user if something went wrong		*/
 
-static inline int scc_paranoia_check(struct scc_channel *scc, kdev_t device,
-				     const char *routine)
+static __inline__ int 
+scc_paranoia_check(struct scc_channel *scc, kdev_t device, const char *routine)
 {
 #ifdef SCC_PARANOIA_CHECK
 
 static const char *badmagic = 
-	"Warning: bad magic number for Z8530 SCC struct (%s) in %s\n"; 
+	KERN_ALERT "Warning: bad magic number for Z8530 SCC struct (%s) in %s\n"; 
 static const char *badinfo =  
-	"Warning: Z8530 not found for (%s) in %s\n";
+	KERN_CRIT "Warning: Z8530 not found for (%s) in %s (forgot to run sccinit?)\n";
        
 	if (!scc->init) 
 	{
@@ -1949,10 +2256,11 @@
 	return 0;
 }
 
-
+#ifdef CONFIG_SCC_TTY
 /* ----> this one is called whenever you open the device <---- */
 
-int scc_open(struct tty_struct *tty, struct file * filp)
+int 
+scc_open(struct tty_struct *tty, struct file * filp)
 {
 	struct scc_channel *scc;
 	int chan;
@@ -1976,28 +2284,30 @@
  	
 	if (scc->magic != SCC_MAGIC)
 	{
-		printk("ERROR: scc_open(): bad magic number for device (%s)",
+		printk(KERN_ALERT "z8530drv: scc_open() found bad magic number for device (%s)",
 		       kdevname(tty->device));
 		return -ENODEV;
-	}		
-	
+	}
+
 	MOD_INC_USE_COUNT;
-	
-	if(scc->tty != NULL)
+
+	if(scc->tty != NULL || scc->stat.is_netdev)
 	{
 		scc->tty_opened++;
 		return 0;
 	}
+	
+	scc->tty = tty;
 
-  	scc->tty = tty;
+ 	if(!scc->init) 
+ 		return 0;
+ 
 	alloc_buffer_pool(scc);
-	
- 	if(!scc->init) return 0;
  	
 	scc->throttled = 0;
 
-	scc->stat.tx_kiss_state = KISS_IDLE;	/* don't change this... */
-	scc->stat.rx_kiss_state = KISS_IDLE;	/* ...or this */
+	scc->stat.tx_kiss_state = KISS_IDLE;
+	scc->stat.rx_kiss_state = KISS_IDLE;
  
 	init_channel(scc);
 	return 0;
@@ -2014,10 +2324,11 @@
 
         if (!scc || (scc->magic != SCC_MAGIC))
                 return;
+
                 
         MOD_DEC_USE_COUNT;
 	
-	if(scc->tty_opened)
+	if(scc->tty_opened || scc->stat.is_netdev)
 	{
 		scc->tty_opened--;
 		return;
@@ -2028,21 +2339,26 @@
 	if (!Driver_Initialized)
 		return;
 	
-	save_flags(flags); cli();
+	save_flags(flags);
+	cli();
 	
 	Outb(scc->ctrl,0);		/* Make sure pointer is written */
 	wr(scc,R1,0);			/* disable interrupts */
 	wr(scc,R3,0);
 	
 	scc->tty = NULL;
-	
+
 	del_timer(&scc->tx_t);
+	del_timer(&scc->tx_wdog);
 	del_timer(&scc->rx_t);
 
-	free_buffer_pool(scc);
-	
 	restore_flags(flags);
 	
+	if (!scc->init)
+		return;
+
+	free_buffer_pool(scc);
+	
 	scc->throttled = 0;
 	tty->stopped = 0;
 }
@@ -2068,135 +2384,144 @@
 	if (scc->stat.tx_state == 0)	/* only switch baudrate on rx... ;-) */
 		set_speed(scc);
 }
-
+#endif /* CONFIG_SCC_TTY */
 
 /* ----> ioctl-routine of the driver <---- */
 
-/* perform ioctl on SCC (sdlc) channel
- * this is used for AX.25 mode, and will set the "kiss" parameters
- */
+
+/* sub routines to read/set KISS parameters */
  
-/* TIOCMGET 	- get modem status	arg: (unsigned long *) arg
- * TIOCMBIS 	- set PTT		arg: ---
- * TIOCMBIC 	- reset PTT		arg: ---
- * TIOCMBIC 	- set PTT		arg: ---
- * TIOCSCCINI	- initialize driver	arg: ---
- * TIOCCHANINI	- initialize channel	arg: (struct scc_modem *) arg
- * TIOCGKISS	- get level 1 parameter	arg: (struct ioctl_command *) arg
- * TIOCSKISS	- set level 1 parameter arg: (struct ioctl_command *) arg
- * TIOCSCCSTAT  - get driver status	arg: (struct scc_stat *) arg
+/* generic ioctl() functions for both TTY or network device mode:
+ *
+ * TIOCSCCCFG		- configure driver	arg: (struct scc_hw_config *) arg
+ * TIOCSCCINI		- initialize driver	arg: ---
+ * TIOCSCCCHANINI	- initialize channel	arg: (struct scc_modem *) arg
+ * TIOCSCCGKISS		- get level 1 parameter	arg: (struct scc_kiss_cmd *) arg
+ * TIOCSCCSKISS		- set level 1 parameter arg: (struct scc_kiss_cmd *) arg
+ * TIOCSCCSTAT		- get driver status	arg: (struct scc_stat *) arg
  */
  
-
 static int
-scc_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
+scc_ioctl(struct scc_channel *scc, unsigned int cmd, void *arg)
 {
-	struct scc_channel * scc = tty->driver_data;
-	unsigned long flags, r;
-	unsigned int result;
-	unsigned int value;
-	struct ioctl_command kiss_cmd;
+	unsigned long flags;
+	struct scc_kiss_cmd kiss_cmd;
 	struct scc_mem_config memcfg;
 	struct scc_hw_config hwcfg;
 	int error, chan;
-
-        if (scc->magic != SCC_MAGIC) 
-        {
-		printk("ERROR: scc_ioctl(): bad magic number for device %s",
-			kdevname(tty->device));
-			
-                return -ENODEV;
-        }
-	                                                 
-	r = NO_SUCH_PARAM;
+	unsigned char device_name[10];
 	
 	if (!Driver_Initialized)
 	{
 		if (cmd == TIOCSCCCFG)
 		{
 			int found = 1;
-			
+
 			if (!suser()) return -EPERM;
 			if (!arg) return -EFAULT;
-			
+
+			error = verify_area(VERIFY_READ, arg, sizeof(struct scc_hw_config));
+			if (error) return error;
+
 			if (Nchips >= MAXSCC) 
 				return -EINVAL;
-			
-			memcpy_fromfs(&hwcfg, (void *) arg, sizeof(hwcfg));
-			
+
+			memcpy_fromfs(&hwcfg, arg, sizeof(hwcfg));
+
 			if (hwcfg.irq == 2) hwcfg.irq = 9;
-			
+
 			if (!Ivec[hwcfg.irq].used && hwcfg.irq)
 			{
 				if (request_irq(hwcfg.irq, scc_isr, SA_INTERRUPT, "AX.25 SCC", NULL))
-					printk("z8530drv: Warning --- could not get IRQ %d\n", hwcfg.irq);
+					printk(KERN_WARNING "z8530drv: warning, cannot get IRQ %d\n", hwcfg.irq);
 				else
 					Ivec[hwcfg.irq].used = 1;
 			}
-			
+
 			if (hwcfg.vector_latch) 
 				Vector_Latch = hwcfg.vector_latch;
-				
+
 			if (hwcfg.clock == 0)
 				hwcfg.clock = DEFAULT_CLOCK;
 
 #ifndef DONT_CHECK
-			save_flags(flags); cli();
-			
+			disable_irq(hwcfg.irq);
+
 			check_region(scc->ctrl, 1);
 			Outb(hwcfg.ctrl_a, 0);
 			udelay(5);
 			OutReg(hwcfg.ctrl_a,R13,0x55);		/* is this chip really there? */
 			udelay(5);
-			
-			if (InReg(hwcfg.ctrl_a,R13) != 0x55 )
+
+			if (InReg(hwcfg.ctrl_a,R13) != 0x55)
 				found = 0;
-				
-			restore_flags(flags);
+
+			enable_irq(hwcfg.irq);
 #endif
 
 			if (found)
 			{
 				SCC_Info[2*Nchips  ].ctrl = hwcfg.ctrl_a;
 				SCC_Info[2*Nchips  ].data = hwcfg.data_a;
+				SCC_Info[2*Nchips  ].irq  = hwcfg.irq;
 				SCC_Info[2*Nchips+1].ctrl = hwcfg.ctrl_b;
 				SCC_Info[2*Nchips+1].data = hwcfg.data_b;
+				SCC_Info[2*Nchips+1].irq  = hwcfg.irq;
 			
-				SCC_ctrl[2*Nchips  ] = hwcfg.ctrl_a;
-				SCC_ctrl[2*Nchips+1] = hwcfg.ctrl_b;
+				SCC_ctrl[Nchips].chan_A = hwcfg.ctrl_a;
+				SCC_ctrl[Nchips].chan_B = hwcfg.ctrl_b;
+				SCC_ctrl[Nchips].irq    = hwcfg.irq;
 			}
-		
+
+
 			for (chan = 0; chan < 2; chan++)
 			{
+				sprintf(device_name, "%s%i", SCC_DriverName, 2*Nchips+chan);
+
 				SCC_Info[2*Nchips+chan].special = hwcfg.special;
 				SCC_Info[2*Nchips+chan].clock = hwcfg.clock;
 				SCC_Info[2*Nchips+chan].brand = hwcfg.brand;
 				SCC_Info[2*Nchips+chan].option = hwcfg.option;
 				SCC_Info[2*Nchips+chan].enhanced = hwcfg.escc;
-			
+
 #ifdef DONT_CHECK
-				printk("%s%i: data port = 0x%3.3x  control port = 0x%3.3x\n",
-					scc_driver.name, 2*Nchips+chan, 
+				printk(KERN_INFO "%s: data port = 0x%3.3x  control port = 0x%3.3x\n",
+					device_name, 
 					SCC_Info[2*Nchips+chan].data, 
 					SCC_Info[2*Nchips+chan].ctrl);
 
 #else
-				printk("%s%i: data port = 0x%3.3x  control port = 0x%3.3x -- %s\n",
-					scc_driver.name, 2*Nchips+chan, 
+				printk(KERN_INFO "%s: data port = 0x%3.3x  control port = 0x%3.3x -- %s\n",
+					device_name,
 					chan? hwcfg.data_b : hwcfg.data_a, 
 					chan? hwcfg.ctrl_b : hwcfg.ctrl_a,
 					found? "found" : "missing");
 #endif
-				
+
 				if (found)
 				{
 					request_region(SCC_Info[2*Nchips+chan].ctrl, 1, "scc ctrl");
 					request_region(SCC_Info[2*Nchips+chan].data, 1, "scc data");
+#ifdef CONFIG_SCC_DEV
+					if (Nchips+chan != 0)
+						scc_net_setup(&SCC_Info[2*Nchips+chan], device_name);
+#endif
 				}
 			}
 			
 			if (found) Nchips++;
 			
+#ifdef SCC_DEBUG
+			printk(KERN_INFO "Available modes:"
+#ifdef CONFIG_SCC_DEV
+			" dev"
+#endif
+#ifdef CONFIG_SCC_TTY
+			" tty"
+#endif
+			"\n");
+#endif
+			
 			return 0;
 		}
 		
@@ -2209,9 +2534,6 @@
 				return -EINVAL;
 			
 			z8530_init();
-			
-			scc->tty=tty;
-			alloc_buffer_pool(scc);
 			return 0;
 		}
 		
@@ -2220,263 +2542,268 @@
 	
 	if (!scc->init)
 	{
-		if (cmd == TIOCCHANINI)
+		if (cmd == TIOCSCCCHANINI)
 		{
-			if (!arg)
-				return -EFAULT;
-				
-			if (!suser())
-				return -EPERM;
-			
-			memcpy_fromfs(&scc->modem, (void *) arg, sizeof(struct scc_modem));
+			if (!suser()) return -EPERM;
+			if (!arg) return -EINVAL;
+			error = verify_area(VERIFY_READ, arg, sizeof(struct scc_modem));
+			if (error) return error;
+
+			scc->stat.rxbuffers = RXBUFFERS;
+			scc->stat.txbuffers = TXBUFFERS;
+			scc->stat.bufsize   = BUFSIZE;
+
+			memcpy_fromfs(&scc->modem, arg, sizeof(struct scc_modem));
 			
 			/* default KISS Params */
 		
 			if (scc->modem.speed < 4800)
 			{
-				scc->kiss.txdelay = 36*TPS/100;    /* 360 ms */
-				scc->kiss.persist = 42;            /* 25% persistence */			/* was 25 */
-				scc->kiss.slottime = 16*TPS/100;   /* 160 ms */
-				scc->kiss.tailtime = 4;            /* minimal reasonable value */
-				scc->kiss.fulldup = 0;             /* CSMA */
-				scc->kiss.waittime = 50*TPS/100;   /* 500 ms */
-				scc->kiss.maxkeyup = 10;           /* 10 s */
-				scc->kiss.mintime = 3;             /* 3 s */
-				scc->kiss.idletime = 30;           /* 30 s */
-				scc->kiss.maxdefer = 120;	   /* 2 min */
-				scc->kiss.not_slip = 1;		   /* KISS mode */
-				scc->kiss.softdcd = 0;		   /* hardware dcd */
+				scc->kiss.txdelay = 36;		/* 360 ms */
+				scc->kiss.persist = 42;		/* 25% persistence */			/* was 25 */
+				scc->kiss.slottime = 16;	/* 160 ms */
+				scc->kiss.tailtime = 4;		/* minimal reasonable value */
+				scc->kiss.fulldup = 0;		/* CSMA */
+				scc->kiss.waittime = 50;	/* 500 ms */
+				scc->kiss.maxkeyup = 10;	/* 10 s */
+				scc->kiss.mintime = 3;		/* 3 s */
+				scc->kiss.idletime = 30;	/* 30 s */
+				scc->kiss.maxdefer = 120;	/* 2 min */
+				scc->kiss.softdcd = 0;		/* hardware dcd */
 			} else {
-				scc->kiss.txdelay = 10*TPS/100;    /* 100 ms */
-				scc->kiss.persist = 64;            /* 25% persistence */			/* was 25 */
-				scc->kiss.slottime = 8*TPS/100;    /* 160 ms */
-				scc->kiss.tailtime = 1;            /* minimal reasonable value */
-				scc->kiss.fulldup = 0;             /* CSMA */
-				scc->kiss.waittime = 50*TPS/100;   /* 500 ms */
-				scc->kiss.maxkeyup = 7;            /* 7 s */
-				scc->kiss.mintime = 3;             /* 3 s */
-				scc->kiss.idletime = 30;           /* 30 s */
-				scc->kiss.maxdefer = 120;	   /* 2 min */
-				scc->kiss.not_slip = 1;		   /* KISS mode */
-				scc->kiss.softdcd = 0;		   /* hardware dcd */
+				scc->kiss.txdelay = 10;		/* 100 ms */
+				scc->kiss.persist = 64;		/* 25% persistence */			/* was 25 */
+				scc->kiss.slottime = 8;		/* 160 ms */
+				scc->kiss.tailtime = 1;		/* minimal reasonable value */
+				scc->kiss.fulldup = 0;		/* CSMA */
+				scc->kiss.waittime = 50;	/* 500 ms */
+				scc->kiss.maxkeyup = 7;		/* 7 s */
+				scc->kiss.mintime = 3;		/* 3 s */
+				scc->kiss.idletime = 30;	/* 30 s */
+				scc->kiss.maxdefer = 120;	/* 2 min */
+				scc->kiss.softdcd = 0;		/* hardware dcd */
 			}
 			
-			scc->init = 1;			
+			scc->init = 1;
 			
 			return 0;
 		}
 		
 		return -EINVAL;
 	}
-
-	switch(cmd){
-	case TCSBRK:
-		return 0;
-	case TIOCMGET:
-		error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(unsigned int *));
-		if (error)
-			return error;
-
-		save_flags(flags); cli();
-		
-		result =  ((scc->wreg[R5] & RTS) ? TIOCM_RTS : 0)
-			| ((scc->wreg[R5] & DTR) ? TIOCM_DTR : 0)
-			| ((InReg(scc->ctrl,R0) & DCD)  ? TIOCM_CAR : 0)
-			| ((InReg(scc->ctrl,R0) & CTS)  ? TIOCM_CTS : 0);
+	
+	switch(cmd)
+	{
+		case TIOCSCCSMEM:
+		case TIOCCHANMEM_OLD:
+			if (!suser()) return -EPERM;
+			if (!arg) return -EINVAL;
+			error = verify_area(VERIFY_READ, arg, sizeof(struct scc_mem_config));
+			if (error) return error;
+	
+			memcpy_fromfs(&memcfg, arg, sizeof(struct scc_mem_config));
+
+			save_flags(flags); 
+			cli();
+
+			free_buffer_pool(scc);
+			scc->stat.rxbuffers = memcfg.rxbuffers;
+			scc->stat.txbuffers = memcfg.txbuffers;
+			scc->stat.bufsize   = memcfg.bufsize;
+			if (scc->tty || (scc->dev && (scc->dev->flags & IFF_UP)))
+				alloc_buffer_pool(scc);
 		
-		restore_flags(flags);
+			restore_flags(flags);
+			return 0;
+		
+		case TIOCSCCGSTAT:
+		case TIOCSCCSTAT_OLD:
+			if (!arg) return -EINVAL;
+			error = verify_area(VERIFY_WRITE, arg, sizeof(struct scc_mem_config));
+			if (error) return error;
 			
-		put_user(result,(unsigned int *) arg);
-		return 0;
-	case TIOCMBIS:
-	case TIOCMBIC:
-	case TIOCMSET:
-		switch (cmd) {
-		case TIOCMBIS:
-			scc->wreg[R5] |= DTR;
-			scc->wreg[R5] |= RTS;
+			memcpy_tofs(arg, &scc->stat, sizeof(struct scc_stat));
+			return 0;
+		
+		case TIOCSCCGKISS:
+		case TIOCGKISS_OLD:
+			if (!arg) return -EINVAL;
+			error = verify_area(VERIFY_WRITE, arg, sizeof(struct scc_mem_config));
+			if (error) return error;
+		
+			memcpy_fromfs(&kiss_cmd, arg, sizeof(struct scc_kiss_cmd));
+			kiss_cmd.param = kiss_get_param(scc, kiss_cmd.command);
+			memcpy_tofs(arg, &kiss_cmd, sizeof(struct scc_kiss_cmd));
+			return 0;
 			break;
-		case TIOCMBIC:
-			scc->wreg[R5] &= ~DTR;
-			scc->wreg[R5] &= ~RTS;
+		
+		case TIOCSCCSKISS:
+		case TIOCSKISS_OLD:
+			if (!suser()) return -EPERM;
+			if (!arg) return -EINVAL;
+			error = verify_area(VERIFY_READ, arg, sizeof(struct scc_mem_config));
+			if (error) return error;
+
+			memcpy_fromfs(&kiss_cmd, arg, sizeof(struct scc_kiss_cmd));
+			return kiss_set_param(scc, kiss_cmd.command, kiss_cmd.param);
 			break;
-		case TIOCMSET:
-			value = get_user((unsigned int *) arg);
-			
-			if(value & TIOCM_DTR)
-				scc->wreg[R5] |= DTR;
-			else
-				scc->wreg[R5] &= ~DTR;
-			if(value & TIOCM_RTS)
-				scc->wreg[R5] |= RTS;
-			else
-				scc->wreg[R5] &= ~RTS;
-			break;
-		}
-		
-		save_flags(flags); cli();
+
+		default:
+			return -ENOIOCTLCMD;
 		
-		if(scc->stat.tx_state == TXS_IDLE && !Running(t_idle))
-			maxk_idle_timeout(scc);
+	}
+	
+	return 0;
+}
+
+#ifdef CONFIG_SCC_TTY
+/* addtional ioctl() for TTY driver mode:
+ *
+ * TIOCMGET 	- get modem status	arg: (unsigned long *) arg
+ * TIOCMBIS 	- set PTT		arg: ---
+ * TIOCMBIC 	- reset PTT		arg: ---
+ * TIOCMBIC 	- set PTT		arg: ---
+ */
+
+static int
+scc_tty_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
+{
+	struct scc_channel * scc = tty->driver_data;
+	unsigned long flags;
+	unsigned int result;
+	unsigned int value;
+	int error;
+
+        if (scc->magic != SCC_MAGIC) 
+        {
+		printk(KERN_ALERT "z8530drv: scc_ioctl() found bad magic number for device %s",
+			SCC_DEVNAME);
 			
-		restore_flags(flags);
-		
-		return 0;
-		
-	case TCGETS:
-		error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct termios));
-		if (error)
-			return error;
-		if (!arg) 
-			return -EFAULT;
+                return -ENODEV;
+        }
+	                                                 
+	if (!Driver_Initialized && cmd == TIOCSCCINI)
+		scc->tty=tty;
+
+	switch(cmd)
+	{
+		case TIOCSCCCFG:
+		case TIOCSCCINI:
+		case TIOCSCCCHANINI:
+		case TIOCSCCSMEM:
+		case TIOCSCCSKISS:
+		case TIOCSCCGKISS:
+
+		case TIOCSCCCFG_OLD:
+		case TIOCSCCINI_OLD:
+		case TIOCCHANINI_OLD:
+		case TIOCCHANMEM_OLD:
+		case TIOCSKISS_OLD:
+		case TIOCGKISS_OLD:
+		case TIOCSCCSTAT_OLD:
+			return scc_ioctl(scc, cmd, (void *) arg);
+
+		case TCSBRK:
+			return 0;
 			
-		memcpy_tofs((void *) arg, scc->tty->termios, sizeof(struct termios));
-		return 0;
-		
-	case TCSETS:
-	case TCSETSF:		/* should flush first, but... */
-	case TCSETSW:		/* should wait 'till flush, but... */
-		if (!arg)
-			return -EFAULT;
-		
-		memcpy_fromfs(scc->tty->termios, (void *) arg, sizeof(struct termios));
-		scc_change_speed(scc);
-		return 0;
+		case TIOCMGET:
+			error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(unsigned int *));
+			if (error)
+				return error;
+
+			save_flags(flags);
+			cli();
+		
+			result =  ((scc->wreg[R5] & RTS) ? TIOCM_RTS : 0)
+				| ((scc->wreg[R5] & DTR) ? TIOCM_DTR : 0)
+				| ((InReg(scc->ctrl,R0) & DCD)  ? TIOCM_CAR : 0)
+				| ((InReg(scc->ctrl,R0) & CTS)  ? TIOCM_CTS : 0);
 		
-	case TIOCCHANMEM:
-		if (!arg)
-			return -EFAULT;
-			
-		memcpy_fromfs(&memcfg, (void *) arg, sizeof(struct scc_mem_config));
-		
-		save_flags(flags); cli();
-		
-		free_buffer_pool(scc);
-		scc->stat.rxbuffers = memcfg.rxbuffers;
-		scc->stat.txbuffers = memcfg.txbuffers;
-		scc->stat.bufsize   = memcfg.bufsize;
-		alloc_buffer_pool(scc);
-		
-		restore_flags(flags);
-		return 0;
-		
-		
-	case TIOCSCCSTAT:
-		error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(struct scc_stat));
-		if (error)
-			return error;
-		
-		if (!arg)
-			return -EFAULT;
+			restore_flags(flags);
 			
-		memcpy_tofs((void *) arg, &scc->stat, sizeof(struct scc_stat));
-		return 0;
-		
-#define TICKS (100/TPS)
-#define CAST(x) (unsigned long)(x)
-#define  Val	kiss_cmd.param
-#define  VAL	kiss_cmd.param*TPS/100
-#define SVAL	kiss_cmd.param? kiss_cmd.param:TIMER_STOPPED
-
-	case TIOCGKISS:
-		error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(struct ioctl_command));
-		if (error)
-			return error;
-			
-		if (!arg)
-			return -EFAULT;
-			
-		memcpy_fromfs(&kiss_cmd, (void *) arg, sizeof(struct ioctl_command));
-
-		switch (kiss_cmd.command)
-		{
-			case PARAM_TXDELAY:	r = CAST(scc->kiss.txdelay*TICKS);	break;
-			case PARAM_PERSIST:	r = CAST(scc->kiss.persist); 		break;
-			case PARAM_SLOTTIME:	r = CAST(scc->kiss.slottime*TICKS);	break;
-			case PARAM_TXTAIL:	r = CAST(scc->kiss.tailtime*TICKS);	break;
-			case PARAM_FULLDUP:	r = CAST(scc->kiss.fulldup); 		break;
-			case PARAM_SOFTDCD:	r = CAST(scc->kiss.softdcd);		break;
-			case PARAM_DTR:		r = CAST((scc->wreg[R5] & DTR)? 1:0); break;
-			case PARAM_RTS:		r = CAST((scc->wreg[R5] & RTS)? 1:0); break;
-			case PARAM_SPEED:	r = CAST(scc->modem.speed);	break;
-			case PARAM_GROUP:	r = CAST(scc->kiss.group); 		break;
-			case PARAM_IDLE:	r = CAST(scc->kiss.idletime);		break;
-			case PARAM_MIN:		r = CAST(scc->kiss.mintime);		break;
-			case PARAM_MAXKEY:	r = CAST(scc->kiss.maxkeyup);		break;
-			case PARAM_WAIT:	r = CAST(scc->kiss.waittime);		break;
-			case PARAM_MAXDEFER:	r = CAST(scc->kiss.maxdefer);		break;
-			case PARAM_TX:		r = CAST(scc->kiss.tx_inhibit);	break;
-			case PARAM_SLIP:	r = CAST(!scc->kiss.not_slip);		break;
-			default:		r = NO_SUCH_PARAM;
-		}
-		
-		kiss_cmd.param = r;
+			put_user(result,(unsigned int *) arg);
+			return 0;
+			
+		case TIOCMBIS:
+		case TIOCMBIC:
+		case TIOCMSET:
+			switch (cmd) 
+			{
+				case TIOCMBIS:
+					scc->wreg[R5] |= DTR;
+					scc->wreg[R5] |= RTS;
+					break;
+				case TIOCMBIC:
+					scc->wreg[R5] &= ~DTR;
+					scc->wreg[R5] &= ~RTS;
+					break;
+				case TIOCMSET:
+					error = verify_area(VERIFY_READ, (void *) arg,sizeof(unsigned int *));
+					if (error)
+						return error;
+					value = get_user((unsigned int *) arg);
+			
+					if(value & TIOCM_DTR)
+						scc->wreg[R5] |= DTR;
+					else
+						scc->wreg[R5] &= ~DTR;
+					if(value & TIOCM_RTS)
+						scc->wreg[R5] |= RTS;
+					else
+						scc->wreg[R5] &= ~RTS;
+					break;
+			}
+			
+			OutReg(scc->ctrl, R5, scc->wreg[R5]);
+			return 0;
 		
-		memcpy_tofs((void *) arg, &kiss_cmd, sizeof(struct ioctl_command));
-		return 0;
-		break;
+		case TCGETS:
+			error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct termios));
+			if (error)
+				return error;
+			if (!arg) 
+				return -EFAULT;
+			
+			memcpy_tofs((void *) arg, scc->tty->termios, sizeof(struct termios));
+			return 0;
 		
-	case TIOCSKISS:
-		if (!arg)
-			return -EFAULT;
-
-		memcpy_fromfs(&kiss_cmd, (void *) arg, sizeof(struct ioctl_command));
-		
-		switch (kiss_cmd.command)
-		{
-			case PARAM_TXDELAY:	scc->kiss.txdelay=VAL;		break;
-			case PARAM_PERSIST:	scc->kiss.persist=Val;		break;
-			case PARAM_SLOTTIME:	scc->kiss.slottime=VAL;		break;
-			case PARAM_TXTAIL:	scc->kiss.tailtime=VAL;		break;
-			case PARAM_FULLDUP:	scc->kiss.fulldup=Val;		break;
-			case PARAM_SOFTDCD:	scc->kiss.softdcd=Val;		break;
-			case PARAM_DTR:		break; /* does someone need this? */
-			case PARAM_RTS:		break; /* or this? */
-			case PARAM_SPEED:	scc->modem.speed=Val;		break;
-			case PARAM_GROUP:	scc->kiss.group=Val;		break;
-			case PARAM_IDLE:	scc->kiss.idletime=Val;		break;
-			case PARAM_MIN:		scc->kiss.mintime=SVAL;		break;
-			case PARAM_MAXKEY:	scc->kiss.maxkeyup=SVAL;	break;
-			case PARAM_WAIT:	scc->kiss.waittime=Val;		break;
-			case PARAM_MAXDEFER:	scc->kiss.maxdefer=SVAL;	break;
-			case PARAM_TX:		scc->kiss.tx_inhibit=Val;	break;
-			case PARAM_SLIP:	scc->kiss.not_slip=!Val;	break;
-			default:		return -ENOIOCTLCMD;
-		}
+		case TCSETS:
+		case TCSETSF:		/* should flush first, but... */
+		case TCSETSW:		/* should wait 'till flush, but... */
+			if (!arg)
+				return -EFAULT;
 		
-		return 0;
-		break;
-#undef TICKS
-#undef CAST
-#undef VAL
-#undef SVAL
-#undef Val
+			error = verify_area(VERIFY_READ, (void *) arg, sizeof(struct termios));
+			if (error)
+				return error;
+			memcpy_fromfs(scc->tty->termios, (void *) arg, sizeof(struct termios));
+			scc_change_speed(scc);
+			return 0;
 		
-	default:
-		return -ENOIOCTLCMD;
-    }
+	}
+	
+	return -ENOIOCTLCMD;
 }
 
 
 /* ----> tx routine: decode KISS data and scc_enqueue it <---- */
 
 /* send raw frame to SCC. used for AX.25 */
-int scc_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
+int 
+scc_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
 {
 	struct scc_channel * scc = tty->driver_data;
 	unsigned char *p;
-	unsigned long flags;
 	int cnt, cnt2;
 	
-	if (!tty) return count;
+	if (!tty) 
+		return -EINVAL;
 	
 	if (scc_paranoia_check(scc, tty->device, "scc_write"))
-		return 0;
+		return -EINVAL;
 
 	if (scc->kiss.tx_inhibit) return count;
-	
-	save_flags(flags); cli();
-	
+
 	cnt2 = count;
 	
 	while (cnt2)
@@ -2493,8 +2820,8 @@
 		else
 			memcpy(scc_wbuf, buf, cnt);
 		
-		/* Strange thing. The timeout of the slip driver is */
-		/* very small, thus we'll wake him up now. 	    */
+		/* The timeout of the slip driver is very small, */
+		/* thus we'll wake him up now. 	    		 */
 
 		if (cnt2 == 0)
 		{
@@ -2510,34 +2837,28 @@
 		
 		while(cnt--) 
 		  if (kiss_decode(scc, *p++))
-		  {
-		  	scc->stat.nospace++;
-		  	restore_flags(flags);
-		  	return 0;
-		  }		  	
+		  	return -ENOMEM;
 	} /* while cnt2 */
 
-	restore_flags(flags);
-		
 	return count;
 }
 				
 
 /* put a single char into the buffer */
 
-static void scc_put_char(struct tty_struct * tty, unsigned char ch)
+static void 
+scc_put_char(struct tty_struct * tty, unsigned char ch)
 {
 	struct scc_channel *scc = tty->driver_data;
-	unsigned char ch2;
 	
 	if (scc_paranoia_check(scc, tty->device, "scc_put_char"))
 		return;
-		
-	ch2 = ch;
-	scc_write(tty, 0, &ch2, 1);	/* that's all */
+
+	kiss_decode(scc, ch);
 }
 
-static void scc_flush_chars(struct tty_struct * tty)
+static void 
+scc_flush_chars(struct tty_struct * tty)
 {
 	struct scc_channel *scc = tty->driver_data;
 	
@@ -2547,7 +2868,8 @@
 }
 
 
-static int scc_write_room(struct tty_struct *tty)
+static int 
+scc_write_room(struct tty_struct *tty)
 {
 	struct scc_channel *scc = tty->driver_data;
 	
@@ -2557,7 +2879,8 @@
 	return BUFSIZE;
 }
 
-static int scc_chars_in_buffer(struct tty_struct *tty)
+static int 
+scc_chars_in_buffer(struct tty_struct *tty)
 {
 	struct scc_channel *scc = tty->driver_data;
 	
@@ -2567,7 +2890,8 @@
 		return 0;
 }
 
-static void scc_flush_buffer(struct tty_struct *tty)
+static void 
+scc_flush_buffer(struct tty_struct *tty)
 {
 	struct scc_channel *scc = tty->driver_data;
 
@@ -2579,41 +2903,43 @@
 		(tty->ldisc.write_wakeup)(tty);
 }
 
-static void scc_throttle(struct tty_struct *tty)
+static void 
+scc_throttle(struct tty_struct *tty)
 {
 	struct scc_channel *scc = tty->driver_data;
 	
 	if (scc_paranoia_check(scc, tty->device, "scc_throttle"))
 		return;
 
-#ifdef DEBUG		
-	printk("scc: scc_throttle() called for device %d\n", MINOR(tty->device));
+#ifdef SCC_DEBUG		
+	printk(KERN_DEBUG "z8530drv: scc_throttle() called for device %d\n", MINOR(tty->device));
 #endif
 	scc->throttled = 1;
 	
 	del_timer(&(scc->rx_t));
-	scc->rx_t.expires = jiffies + HZ/TPS;
+	scc->rx_t.expires = jiffies + HZ/25;
 	add_timer(&scc->rx_t);
 }
 
-static void scc_unthrottle(struct tty_struct *tty)
+static void 
+scc_unthrottle(struct tty_struct *tty)
 {
 	struct scc_channel *scc = tty->driver_data;
 	
 	if (scc_paranoia_check(scc, tty->device, "scc_unthrottle"))
 		return;
 		
-#ifdef DEBUG
-	printk("scc: scc_unthrottle() called for device %d\n", MINOR(tty->device));
+#ifdef SCC_DEBUG
+	printk(KERN_DEBUG "z8350drv: scc_unthrottle() called for device %d\n", MINOR(tty->device));
 #endif		
 	scc->throttled = 0;
 	del_timer(&(scc->rx_t));
-	scc_tx_timer(scc->rx_t.data);
 }
 
 /* experimental, the easiest way to stop output is a fake scc_throttle */
 
-static void scc_start(struct tty_struct *tty)
+static void 
+scc_start(struct tty_struct *tty)
 {
 	struct scc_channel *scc = tty->driver_data;
 	
@@ -2623,7 +2949,8 @@
 	scc_unthrottle(tty);
 }
 
-static void scc_stop(struct tty_struct *tty)
+static void 
+scc_stop(struct tty_struct *tty)
 {
 	struct scc_channel *scc = tty->driver_data;
 	
@@ -2657,20 +2984,521 @@
 		
 	scc_change_speed(scc);
 }
+#endif /* CONFIG_SCC_TTY */
+
+#ifdef CONFIG_SCC_DEV
+/* ******************************************************************** */
+/* *			    Network driver routines		      * */
+/* ******************************************************************** */
+
+static unsigned char ax25_bcast[AX25_ADDR_LEN] =
+{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static unsigned char ax25_nocall[AX25_ADDR_LEN] =
+{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+static int 
+scc_net_init(struct device *dev)
+{
+	return 0;	/* dummy */
+}
+
+/* ----> open network device <---- */
+
+static int 
+scc_net_open(struct device *dev)
+{
+	struct scc_channel *scc = (struct scc_channel *) dev->priv;
+
+	if (scc == NULL || scc->magic != SCC_MAGIC)
+		return -ENODEV;
+
+	if (scc->tty != NULL)
+		return -EBUSY;
+
+ 	if (!scc->init)
+		return -EINVAL;
+
+	scc->stat.is_netdev = 1;
+
+	MOD_INC_USE_COUNT;
+
+	alloc_buffer_pool(scc);
+
+	scc->stat.tx_kiss_state = KISS_IDLE;	/* unused */
+	scc->stat.rx_kiss_state = KISS_IDLE;	/* unused */
+ 
+	init_channel(scc);
+
+	dev->tbusy = 0;
+	dev->start = 1;
+
+	return 0;
+}
+
+/* ----> close network device <---- */
+
+static int 
+scc_net_close(struct device *dev)
+{
+	struct scc_channel *scc = (struct scc_channel *) dev->priv;
+	unsigned long flags;
+
+	if (scc == NULL || scc->magic != SCC_MAGIC)
+		return -ENODEV;
+
+	if (!scc->stat.is_netdev)
+		return -ENXIO;
+
+	MOD_DEC_USE_COUNT;
+
+	scc->stat.is_netdev = 0;
+
+	save_flags(flags); 
+	cli();
+	
+	Outb(scc->ctrl,0);		/* Make sure pointer is written */
+	wr(scc,R1,0);			/* disable interrupts */
+	wr(scc,R3,0);
+
+	del_timer(&scc->tx_t);
+	del_timer(&scc->tx_wdog);
+#ifdef CONFIG_SCC_TTY
+	del_timer(&scc->rx_t);
+#endif
+	restore_flags(flags);
+
+	free_buffer_pool(scc);
+
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	return 0;
+}
+
+/* ----> receive frame, called from scc_rxint() <---- */
+
+static void 
+scc_net_rx(struct scc_channel *scc, struct mbuf *bp)
+{
+	struct sk_buff *skb;
+
+	if ( bp == NULL || bp->cnt == 0 || 
+	    scc == NULL || scc->magic != SCC_MAGIC)
+		return;
+		
+	skb = dev_alloc_skb(bp->cnt+1);
+
+	if (skb == NULL)
+	{
+		scc->dev_stat.rx_dropped++;
+		return;
+	}
+
+	scc->dev_stat.rx_packets++;
+
+	skb->dev = scc->dev;
+	skb->protocol = htons(ETH_P_AX25);
+	
+	memcpy(skb_put(skb, bp->cnt), bp->data, bp->cnt);
 
+	skb->mac.raw = skb->data;
+	netif_rx(skb);
 
+	return;
+}
+
+/* ----> transmit frame <---- */
+
+static int 
+scc_net_tx(struct sk_buff *skb, struct device *dev)
+{
+	struct scc_channel *scc = (struct scc_channel *) dev->priv;
+	struct mbuf *bp;
+	long ticks;
+	
+	/* 
+	 * We have our own queues, dev->tbusy is set only by t_maxkeyup
+	 * to avoid running the driver into maxkeup again and again.
+	 *
+	 * Hope I havn't outsmart me _again_ ;-)
+	 */
+
+	if (dev->tbusy)
+	{
+		ticks = (signed) (jiffies - dev->trans_start);
+
+		if (ticks < scc->kiss.maxdefer*HZ || scc == NULL)
+			return 1;
+
+		/* 
+		 * Arrgh... Seems to be a _very_ busy channel.
+		 * throw away transmission queue. 
+		 */
+
+		del_timer(&scc->tx_wdog);
+		t_busy((unsigned long) scc);
+
+                dev->tbusy=0;
+                dev->trans_start = jiffies;
+        }
+
+	if (skb == NULL)
+	{
+		dev_tint(dev);
+		return 0;
+	}
+
+	if (scc == NULL || scc->magic != SCC_MAGIC)
+	{
+		dev_kfree_skb(skb, FREE_WRITE);
+		return 0;
+	}
+
+	scc->dev_stat.tx_packets++;
+	bp = scc_get_buffer(scc, BT_TRANSMIT);
+
+	if (bp == NULLBUF || skb->len > scc->stat.bufsize || skb->len == 0)
+	{
+		scc->dev_stat.tx_dropped++;
+		if (bp) scc_enqueue_buffer(&scc->tx_buffer_pool, bp);
+		dev_kfree_skb(skb, FREE_WRITE);
+		return 0;
+	}
+
+	memcpy(bp->data, skb->data, skb->len);
+	bp->cnt = skb->len;
+
+	dev_kfree_skb(skb, FREE_WRITE);
+
+	scc->kiss_decode_bp = bp;
+	kiss_interpret_frame(scc);
+	dev->trans_start = jiffies;
+	return 0;
+}
+
+/* ----> set interface callsign <---- */
+
+static int 
+scc_net_set_mac_address(struct device *dev, void *addr)
+{
+	struct sockaddr *sa = (struct sockaddr *) addr;
+	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+	return 0;
+}
+
+/* ----> rebuild header <---- */
+
+static int 
+scc_net_rebuild_header(void *buff, struct device *dev, unsigned long raddr, struct sk_buff *skb)
+{
+    return ax25_rebuild_header(buff, dev, raddr, skb);
+}
+
+/* ----> "hard" header <---- */
+
+static int 
+scc_net_header(struct sk_buff *skb, struct device *dev, unsigned short type, 
+	       void *daddr, void *saddr, unsigned len)
+{
+    return ax25_encapsulate(skb, dev, type, daddr, saddr, len);
+}
+
+/* ----> get statistics <---- */
+
+static struct enet_statistics *
+scc_net_get_stats(struct device *dev)
+{
+	struct scc_channel *scc = (struct scc_channel *) dev->priv;
+	
+	if (scc == NULL || scc->magic != SCC_MAGIC)
+		return NULL;
+		
+	scc->dev_stat.rx_errors = scc->stat.rxerrs + scc->stat.rx_over;
+	scc->dev_stat.tx_errors = scc->stat.txerrs + scc->stat.tx_under;
+	scc->dev_stat.rx_fifo_errors = scc->stat.rx_over;
+	scc->dev_stat.tx_fifo_errors = scc->stat.tx_under;
+
+	return &scc->dev_stat;
+}
+
+/* ----> ioctl for network devices <---- */
+
+static int 
+scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+	struct scc_channel *scc = (struct scc_channel *) dev->priv;
+	unsigned int ncmd = 0;
+	int res;
+
+	if (scc == NULL || scc->magic != SCC_MAGIC)
+		return -EINVAL;
+
+	if (!Driver_Initialized && cmd == SIOCSCCINI)
+	{
+		scc->dev = dev;
+		scc->stat.is_netdev = 1;
+	}
+
+	switch (cmd)
+	{
+		case SIOCSCCRESERVED:	return -EINVAL;      /* unused */
+		case SIOCSCCCFG:	ncmd = TIOCSCCCFG;	break;
+		case SIOCSCCINI:	ncmd = TIOCSCCINI;	break;
+		case SIOCSCCCHANINI:	ncmd = TIOCSCCCHANINI;	break;
+		case SIOCSCCSMEM:	ncmd = TIOCSCCSMEM;	break;
+		case SIOCSCCGKISS:	ncmd = TIOCSCCGKISS;	break;
+		case SIOCSCCSKISS:	ncmd = TIOCSCCSKISS;	break;
+		case SIOCSCCGSTAT:	ncmd = TIOCSCCGSTAT;	break;
+		default:		return -EINVAL;
+	}
+
+	res = scc_ioctl(scc, ncmd, (void *) ifr->ifr_data);
+
+#ifdef CONFIG_SCC_TTY
+	if (!(dev->flags & IFF_UP))
+		scc->stat.is_netdev = 0;
+#endif
+
+	return res;
+	
+}
+
+
+/* ----> initialize interface <---- */
+
+static int 
+scc_net_setup(struct scc_channel *scc, unsigned char *name)
+{
+	int k;
+	unsigned char *buf;
+	struct device *dev;
+
+	if (dev_get(name) != NULL)
+	{
+		printk(KERN_INFO "Z8530drv: device %s already exists.\n", name);
+		return -EEXIST;
+	}
+
+	if ((scc->dev = (struct device *) kmalloc(sizeof(struct device), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+
+#ifndef CONFIG_SCC_TTY
+	scc->stat.is_netdev = 1;
+#endif
+	dev = scc->dev;
+	memset(dev, 0, sizeof(struct device));
+
+	buf = (unsigned char *) kmalloc(10, GFP_KERNEL);
+	strcpy(buf, name);
+
+	dev->priv = (void *) scc;
+	dev->name = buf;
+	dev->init = scc_net_init;
+
+	if (register_netdev(dev) != 0)
+	{
+		kfree(dev);
+		return -EIO;
+	}
+
+	for (k=0; k < DEV_NUMBUFFS; k++)
+		skb_queue_head_init(&dev->buffs[k]);
+
+	dev->open            = scc_net_open;
+	dev->stop	     = scc_net_close;
+
+	dev->hard_start_xmit = scc_net_tx;
+	dev->hard_header     = scc_net_header;
+	dev->rebuild_header  = scc_net_rebuild_header;
+	dev->set_mac_address = scc_net_set_mac_address;
+	dev->get_stats       = scc_net_get_stats;
+	dev->do_ioctl        = scc_net_ioctl;
+
+	memcpy(dev->broadcast, ax25_bcast,  AX25_ADDR_LEN);
+	memcpy(dev->dev_addr,  ax25_nocall, AX25_ADDR_LEN);
+ 
+	dev->flags      = 0;
+	dev->family     = AF_INET;
+	dev->pa_addr    = 0;
+	dev->pa_brdaddr = 0;
+	dev->pa_mask    = 0;
+	dev->pa_alen    = 4;
+
+	dev->type = ARPHRD_AX25;
+	dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
+	dev->mtu = AX25_DEF_PACLEN;
+	dev->addr_len = AX25_ADDR_LEN;
+
+	return 0;
+}
+#endif /* CONFIG_SCC_DEV */
+
+/* ******************************************************************** */
+/* *		dump statistics to /proc/net/z8530drv		      * */
+/* ******************************************************************** */
+
+
+static int 
+scc_net_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	struct scc_channel *scc;
+	char *txstate, *clksrc, *brand;
+	struct scc_kiss *kiss;
+	struct scc_stat *stat;
+	int len = 0;
+	off_t pos = 0;
+	off_t begin = 0;
+	int k;
+
+	if (!Driver_Initialized)
+	{
+		len += sprintf(buffer, "Driver not initialized.\n");
+		goto done;
+	}
+
+	if (!Nchips)
+	{
+		len += sprintf(buffer, "Z8530 chips not found\n");
+		goto done;
+	}
+
+	for (k = 0; k < Nchips*2; k++)
+	{
+		scc = &SCC_Info[k];
+		stat = &scc->stat;
+		kiss = &scc->kiss;
+
+		if (!scc->init)
+			continue;
+
+		switch(stat->tx_state)
+		{
+			case TXS_IDLE:		txstate = "idle";	break;
+			case TXS_BUSY:		txstate = "busy";	break;
+			case TXS_ACTIVE:	txstate = "active";	break;
+			case TXS_NEWFRAME:	txstate = "new";	break;
+			case TXS_IDLE2:		txstate = "keyed";	break;
+			case TXS_WAIT:		txstate = "wait";	break;
+			case TXS_TIMEOUT:	txstate = "timeout";	break;
+			default:		txstate = "???";
+		}
+		switch(scc->modem.clocksrc)
+		{
+			case CLK_DPLL:		clksrc = "dpll";	break;
+			case CLK_EXTERNAL:	clksrc = "ext";		break;
+			case CLK_DIVIDER:	clksrc = "div";		break;
+			default:		clksrc = "???";
+		}
+		switch(scc->brand)
+		{
+			case PA0HZP:	brand="pa0hzp";	break;
+			case EAGLE:	brand="eagle";	break;
+			case PC100:	brand="pc1100";	break;
+			case PRIMUS:	brand="primus";	break;
+			case DRSI:	brand="drsi";	break;
+			case BAYCOM:	brand="baycom";	break;
+			default:	brand="???";
+		}
+	
+		len += sprintf(buffer+len, "Device     : %s%d\n", SCC_DriverName, k);
+		len += sprintf(buffer+len, "Hardware   : %3.3x %3.3x %d %lu %s %s %3.3x %3.3x %d\n", 
+				scc->data, scc->ctrl, scc->irq, scc->clock, brand,
+				scc->enhanced? "enh":"norm", Vector_Latch, scc->special, 
+				scc->option);
+		len += sprintf(buffer+len, "Received   : %lu %lu %d\n", 
+				stat->rxframes, stat->rxerrs, stat->rx_over);
+		len += sprintf(buffer+len, "Transmitted: %lu %lu %d %s\n", 
+				stat->txframes, stat->txerrs, stat->tx_under, txstate);
+		len += sprintf(buffer+len, "Interrupts : %lu %lu %lu %lu\n",
+				stat->rxints, stat->txints, stat->exints, stat->spints);
+		len += sprintf(buffer+len, "Buffers    : %d %d/%d %d/%d %d\n", 
+				stat->bufsize, stat->rx_queued, stat->rxbuffers,
+				stat->tx_queued, stat->txbuffers, stat->nospace);
+		len += sprintf(buffer+len, "MODEM      : %lu %s %s %s\n", 
+				scc->modem.speed, scc->modem.nrz? "nrz":"nrzi",
+				clksrc, kiss->softdcd? "soft":"hard");
+		len += sprintf(buffer+len, "Mode       : %s\n",
+				stat->is_netdev? "dev":"tty");
+#define K(x) kiss->x
+		len += sprintf(buffer+len, "KISS params: %d %d %d %d %d %d %d %d %d %d %d %d\n",
+				K(txdelay), K(persist), K(slottime), K(tailtime),
+				K(fulldup), K(waittime), K(mintime), K(maxkeyup),
+				K(idletime), K(maxdefer), K(tx_inhibit), K(group));
+#undef K
+#ifdef SCC_DEBUG
+		{
+			int reg;
+
+			len += sprintf(buffer+len, "Z8530 wregs: ");
+			for (reg = 0; reg < 16; reg++)
+				len += sprintf(buffer+len, "%2.2x ", scc->wreg[reg]);
+			len += sprintf(buffer+len, "\n");
+			
+			len += sprintf(buffer+len, "Z8530 rregs: %2.2x %2.2x XX ", InReg(scc->ctrl,R0), InReg(scc->ctrl,R1));
+			for (reg = 3; reg < 8; reg++)
+				len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg));
+			len += sprintf(buffer+len, "XX ");
+			for (reg = 9; reg < 16; reg++)
+				len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg));
+			len += sprintf(buffer+len, "\n");
+		}
+#endif
+		len += sprintf(buffer+len, "\n");
+
+                pos = begin + len;
+
+                if (pos < offset) {
+                        len   = 0;
+                        begin = pos;
+                }
+
+                if (pos > offset + length)
+                        break;
+	}
+
+done:
+
+        *start = buffer + (offset - begin);
+        len   -= (offset - begin);
+
+        if (len > length) len = length;
+
+        return len;
+}
+
+#ifdef CONFIG_INET
+struct proc_dir_entry scc_proc_dir_entry = 
+{ 
+  PROC_NET_Z8530, 8, "z8530drv", S_IFREG | S_IRUGO, 1, 0, 0, 0, 
+  &proc_net_inode_operations, scc_net_get_info 
+};
+
+#define scc_net_procfs_init()   proc_net_register(&scc_proc_dir_entry);
+#define scc_net_procfs_remove() proc_net_unregister(PROC_NET_Z8530);
+#else
+#define scc_net_procfs_init()
+#define scc_net_procfs_remove()
+#endif
+
+  
 /* ******************************************************************** */
 /* * 			Init SCC driver 			      * */
 /* ******************************************************************** */
 
-int  scc_init (void)
+int scc_init (void)
 {
 	int chip, chan, k;
+#ifdef CONFIG_SCC_DEV
+	char devname[10];
+#endif
 	
+	printk(KERN_INFO BANNER);
+
+#ifdef CONFIG_SCC_TTY
 	memset(&scc_std_termios, 0, sizeof(struct termios));
         memset(&scc_driver, 0, sizeof(struct tty_driver));
         scc_driver.magic = TTY_DRIVER_MAGIC;
-        scc_driver.name = "scc";
+        scc_driver.name = SCC_DriverName;
         scc_driver.major = Z8530_MAJOR;
         scc_driver.minor_start = 0;
         scc_driver.num = MAXSCC*2;
@@ -2699,17 +3527,19 @@
 	scc_driver.throttle = scc_throttle;
 	scc_driver.unthrottle = scc_unthrottle;
         
-        scc_driver.ioctl = scc_ioctl;
+        scc_driver.ioctl = scc_tty_ioctl;
         scc_driver.set_termios = scc_set_termios;
         scc_driver.set_ldisc = scc_set_ldisc;
 
-	printk(BANNER);
         
         if (tty_register_driver(&scc_driver))
         {
-		printk("Failed to register Z8530 SCC driver\n");
+		printk(KERN_ERR "Failed to register Z8530 SCC driver\n");
 		return -EIO;
 	}
+#endif
+	
+	memset(&SCC_ctrl, 0, sizeof(SCC_ctrl));
 	
 	/* pre-init channel information */
 	
@@ -2719,16 +3549,19 @@
 		memset((char *) &SCC_Info[2*chip+1], 0, sizeof(struct scc_channel));
 		
 		for (chan = 0; chan < 2; chan++)
-		{
 			SCC_Info[2*chip+chan].magic    = SCC_MAGIC;
-			SCC_Info[2*chip+chan].stat.rxbuffers = RXBUFFERS;
-			SCC_Info[2*chip+chan].stat.txbuffers = TXBUFFERS;
-			SCC_Info[2*chip+chan].stat.bufsize   = BUFSIZE;
-		}
 	}
-	
+
 	for (k = 0; k < 16; k++) Ivec[k].used = 0;
 
+#ifdef CONFIG_SCC_DEV
+	sprintf(devname,"%s0", SCC_DriverName);
+	
+	if (scc_net_setup(SCC_Info, devname))
+		printk(KERN_WARNING "z8530drv: cannot setup network device\n");
+#endif
+	scc_net_procfs_init();
+
 	return 0;
 }
 
@@ -2743,9 +3576,9 @@
 	int result = 0;
 	
 	result = scc_init();
-	
+
 	if (result == 0)
-		printk("Copyright 1993,1995 Joerg Reuter DL1BKE (jreuter@lykos.tng.oche.de)\n");
+		printk(KERN_INFO "Copyright 1993,1996 Joerg Reuter DL1BKE (jreuter@lykos.tng.oche.de)\n");
 		
 	return result;
 }
@@ -2754,19 +3587,26 @@
 {
 	long flags;
 	io_port ctrl;
-	int k, errno;
+	int k;
 	struct scc_channel *scc;
 	
-	save_flags(flags); cli();
-	if ( (errno = tty_unregister_driver(&scc_driver)) )
+	save_flags(flags); 
+	cli();
+
+#ifdef CONFIG_SCC_TTY
+	if ( (k = tty_unregister_driver(&scc_driver)) )
 	{
-		printk("Failed to unregister Z8530 SCC driver (%d)", -errno);
+		printk(KERN_ERR "z8530drv: failed to unregister tty driver: (%d)\n", -k);
 		restore_flags(flags);
 		return;
 	}
-	
+#endif
+
+	if (Nchips == 0)
+		unregister_netdev(SCC_Info[0].dev);
+
 	for (k = 0; k < Nchips; k++)
-		if ( (ctrl = SCC_ctrl[k*2]) )
+		if ( (ctrl = SCC_ctrl[k].chan_A) )
 		{
 			Outb(ctrl, 0);
 			OutReg(ctrl,R9,FHWRES);	/* force hardware reset */
@@ -2780,6 +3620,11 @@
 		{
 			release_region(scc->ctrl, 1);
 			release_region(scc->data, 1);
+			if (scc->dev)
+			{
+				unregister_netdev(scc->dev);
+				kfree(scc->dev);
+			}
 		}
 	}
 	
@@ -2787,5 +3632,7 @@
 		if (Ivec[k].used) free_irq(k, NULL);
 		
 	restore_flags(flags);
+
+	scc_net_procfs_remove();
 }
 #endif
diff -u --recursive --new-file v2.0.30/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v2.0.30/linux/drivers/char/serial.c	Tue Apr  8 08:47:45 1997
+++ linux/drivers/char/serial.c	Fri Sep  5 11:21:39 1997
@@ -2960,7 +2960,7 @@
 			release_region(rs_table[i].port, 8);
 	}
 	if (tmp_buf) {
-		free_page(tmp_buf);
+		free_page((unsigned long) tmp_buf);
 		tmp_buf = NULL;
 	}
 }
diff -u --recursive --new-file v2.0.30/linux/drivers/char/specialix.c linux/drivers/char/specialix.c
--- v2.0.30/linux/drivers/char/specialix.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/specialix.c	Tue Aug 12 13:06:54 1997
@@ -0,0 +1,2330 @@
+/*
+ *      specialix.c  -- specialix IO8+ multiport serial driver.
+ *
+ *      Copyright (C) 1997  Roger Wolff (R.E.Wolff@BitWizard.nl)
+ *      Copyright (C) 1994-1996  Dmitry Gorodchanin (begemot@bgm.rosprint.net)
+ *
+ *      Specialix pays for the development and support of this driver.
+ *      Please DO contact io8-linux@specialix.co.uk if you require
+ *      support. But please read the documentation (specialix.txt)
+ *      first.
+ *
+ *      This driver was developped in the BitWizard linux device
+ *      driver service. If you require a linux device driver for your
+ *      product, please contact devices@BitWizard.nl for a quote.
+ *
+ *      This code is firmly based on the riscom/8 serial driver,
+ *      written by Dmitry Gorodchanin. The specialix IO8+ card
+ *      programming information was obtained from the CL-CD1865 Data
+ *      Book, and Specialix document number 6200059: IO8+ Hardware
+ *      Functional Specification.
+ *
+ *      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.
+ *
+ * Revision history:
+ *
+ * Revision 1.0:  April 1st 1997.
+ *                Initial release for alpha testing.
+ * Revision 1.1:  April 14th 1997. 
+ *                Incorporated Richard Hudsons suggestions, 
+ *                removed some debugging printk's.
+ * Revision 1.2:  April 15th 1997.
+ *                Ported to 2.1.x kernels.
+ * Revision 1.3:  April 17th 1997 
+ *                Backported to 2.0. (Compatibility macros). 
+ * Revision 1.4:  April 18th 1997
+ *                Fixed DTR/RTS bug that caused the card to indicate 
+ *                "don't send data" to a modem after the password prompt.  
+ *                Fixed bug for premature (fake) interrupts.
+ * Revision 1.5:  April 19th 1997
+ *                fixed a minor typo in the header file, cleanup a little. 
+ *                performance warnings are now MAXed at once per minute.
+ * Revision 1.6:  May 23 1997
+ *                Changed the specialix=... format to include interrupt.
+ * Revision 1.7:  May 27 1997
+ *                Made many more debug printk's a compile time option.
+ * 
+ */
+
+#define VERSION "1.7"
+
+
+/*
+ * There is a bunch of documentation about the card, jumpers, config
+ * settings, restrictions, cables, device names and numbers in
+ * ../../Documentation/specialix.txt 
+ */
+
+#include <linux/module.h>
+
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/mm.h>
+#include <linux/serial.h>
+#include <linux/fcntl.h>
+#include <linux/major.h>
+#include <linux/delay.h>
+#include <linux/tqueue.h>
+#include <linux/version.h>
+
+
+/* ************************************************************** */
+/* * This section can be removed when 2.0 becomes outdated....  * */
+/* ************************************************************** */
+
+#if LINUX_VERSION_CODE < 131328    /* Less than 2.1.0 */
+#define TWO_ZERO
+#else
+#if LINUX_VERSION_CODE < 131368   /* less than 2.1.40 */
+/* This has not been extensively tested yet. Sorry. */
+#warning "You're on your own between 2.1.0 and 2.1.40.... "
+#warning "Please use 2.1.40 or higher."
+#endif
+#endif
+
+
+#ifdef TWO_ZERO
+#define Get_user(a,b)         a = get_user(b)
+#define copy_from_user(a,b,c) memcpy_fromfs(a,b,c)
+#define copy_to_user(a,b,c)   memcpy_tofs(a,b,c)
+#define queue_task            queue_task_irq_off
+#else
+#define Get_user(a,b)         get_user(a,b)
+#endif
+
+/* ************************************************************** */
+/* *                End of compatibility section..              * */
+/* ************************************************************** */
+
+
+#ifndef TWO_ZERO
+#include <asm/uaccess.h>
+#endif
+
+#include "specialix_io8.h"
+#include "cd1865.h"
+
+
+
+/* Configurable options: */
+
+/* Am I paranoid or not ? ;-) */
+#define SPECIALIX_PARANOIA_CHECK
+
+/* Do I trust the IRQ from the card? (enabeling it doesn't seem to help)
+   When the IRQ routine leaves the chip in a state that is keeps on
+   requiring attention, the timer doesn't help either. */
+#undef SPECIALIX_TIMER
+
+/* 
+ * The following defines are mostly for testing purposes. But if you need
+ * some nice reporting in your syslog, you can define them also.
+ */
+#undef SX_REPORT_FIFO
+#undef SX_REPORT_OVERRUN
+
+
+
+#ifdef CONFIG_SPECIALIX_RTSCTS
+#define SX_CRTSCTS(bla) 1
+#else
+#define SX_CRTSCTS(tty) C_CRTSCTS(tty)
+#endif
+
+
+/* Used to be outb (0xff, 0x80); */
+#define short_pause() udelay (1)
+
+
+#define SPECIALIX_LEGAL_FLAGS \
+	(ASYNC_HUP_NOTIFY   | ASYNC_SAK          | ASYNC_SPLIT_TERMIOS   | \
+	 ASYNC_SPD_HI       | ASYNC_SPEED_VHI    | ASYNC_SESSION_LOCKOUT | \
+	 ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP)
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+DECLARE_TASK_QUEUE(tq_specialix);
+
+
+
+#define SPECIALIX_TYPE_NORMAL	1
+#define SPECIALIX_TYPE_CALLOUT	2
+
+static struct specialix_board * IRQ_to_board[16] = { NULL, } ;
+static struct tty_driver specialix_driver, specialix_callout_driver;
+static int    specialix_refcount = 0;
+static struct tty_struct * specialix_table[SX_NBOARD * SX_NPORT] = { NULL, };
+static struct termios * specialix_termios[SX_NBOARD * SX_NPORT] = { NULL, };
+static struct termios * specialix_termios_locked[SX_NBOARD * SX_NPORT] = { NULL, };
+static unsigned char * tmp_buf = NULL;
+static struct semaphore tmp_buf_sem = MUTEX;
+
+static unsigned long baud_table[] =  {
+	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+	9600, 19200, 38400, 57600, 115200, 0, 
+};
+
+static struct specialix_board sx_board[SX_NBOARD] =  {
+	{ 0, SX_IOBASE1,  9, },
+	{ 0, SX_IOBASE2, 11, },
+	{ 0, SX_IOBASE3, 12, },
+	{ 0, SX_IOBASE4, 15, },
+};
+
+static struct specialix_port sx_port[SX_NBOARD * SX_NPORT] =  {
+	{ 0, },
+};
+		
+
+#ifdef SPECIALIX_TIMER
+static struct timer_list missed_irq_timer;
+static void sx_interrupt(int irq, void * dev_id, struct pt_regs * regs);
+#endif
+
+
+
+static inline int sx_paranoia_check(struct specialix_port const * port,
+				    kdev_t device, const char *routine)
+{
+#ifdef SPECIALIX_PARANOIA_CHECK
+	static const char *badmagic =
+		KERN_ERR "sx: Warning: bad specialix port magic number for device %s in %s\n";
+	static const char *badinfo =
+		KERN_ERR "sx: Warning: null specialix port for device %s in %s\n";
+ 
+	if (!port) {
+		printk(badinfo, kdevname(device), routine);
+		return 1;
+	}
+	if (port->magic != SPECIALIX_MAGIC) {
+		printk(badmagic, kdevname(device), routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+
+/*
+ * 
+ *  Service functions for specialix IO8+ driver.
+ * 
+ */
+
+/* Get board number from pointer */
+extern inline int board_No (struct specialix_board * bp)
+{
+	return bp - sx_board;
+}
+
+
+/* Get port number from pointer */
+extern inline int port_No (struct specialix_port const * port)
+{
+	return SX_PORT(port - sx_port); 
+}
+
+
+/* Get pointer to board from pointer to port */
+extern inline struct specialix_board * port_Board(struct specialix_port const * port)
+{
+	return &sx_board[SX_BOARD(port - sx_port)];
+}
+
+
+/* Input Byte from CL CD186x register */
+extern inline unsigned char sx_in(struct specialix_board  * bp, unsigned short reg)
+{
+	bp->reg = reg | 0x80;
+	outb (reg | 0x80, bp->base + SX_ADDR_REG);
+	return inb  (bp->base + SX_DATA_REG);
+}
+
+
+/* Output Byte to CL CD186x register */
+extern inline void sx_out(struct specialix_board  * bp, unsigned short reg,
+			  unsigned char val)
+{
+	bp->reg = reg | 0x80;
+	outb (reg | 0x80, bp->base + SX_ADDR_REG);
+	outb (val, bp->base + SX_DATA_REG);
+}
+
+
+/* Input Byte from CL CD186x register */
+extern inline unsigned char sx_in_off(struct specialix_board  * bp, unsigned short reg)
+{
+	bp->reg = reg;
+	outb (reg, bp->base + SX_ADDR_REG);
+	return inb  (bp->base + SX_DATA_REG);
+}
+
+
+/* Output Byte to CL CD186x register */
+extern inline void sx_out_off(struct specialix_board  * bp, unsigned short reg,
+			  unsigned char val)
+{
+	bp->reg = reg;
+	outb (reg, bp->base + SX_ADDR_REG);
+	outb (val, bp->base + SX_DATA_REG);
+}
+
+
+/* Wait for Channel Command Register ready */
+extern inline void sx_wait_CCR(struct specialix_board  * bp)
+{
+	unsigned long delay;
+
+	for (delay = SX_CCR_TIMEOUT; delay; delay--) 
+		if (!sx_in(bp, CD186x_CCR))
+			return;
+	
+	printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
+}
+
+
+/* Wait for Channel Command Register ready */
+extern inline void sx_wait_CCR_off(struct specialix_board  * bp)
+{
+	unsigned long delay;
+
+	for (delay = SX_CCR_TIMEOUT; delay; delay--) 
+		if (!sx_in_off(bp, CD186x_CCR))
+			return;
+	
+	printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
+}
+
+
+/*
+ *  specialix IO8+ IO range functions.
+ */
+
+extern inline int sx_check_io_range(struct specialix_board * bp)
+{
+	return check_region (bp->base, SX_IO_SPACE);
+}
+
+
+extern inline void sx_request_io_range(struct specialix_board * bp)
+{
+	request_region(bp->base, SX_IO_SPACE, "specialix IO8+" );
+}
+
+
+extern inline void sx_release_io_range(struct specialix_board * bp)
+{
+	release_region(bp->base, SX_IO_SPACE);
+}
+
+	
+/* Must be called with enabled interrupts */
+extern inline void sx_long_delay(unsigned long delay)
+{
+	unsigned long i;
+	
+	for (i = jiffies + delay; i > jiffies; ) ;
+}
+
+
+
+/* Set the IRQ using the RTS lines that run to the PAL on the board.... */
+int sx_set_irq ( struct specialix_board *bp)
+{
+	int virq;
+	int i;
+
+	switch (bp->irq) {
+	/* In the same order as in the docs... */
+	case 15: virq = 0;break;
+	case 12: virq = 1;break;
+	case 11: virq = 2;break;
+	case 9:  virq = 3;break;
+	default: printk (KERN_ERR "Speclialix: cannot set irq to %d.\n", bp->irq);
+	         return 0;
+	}
+
+	for (i=0;i<2;i++) {
+		sx_out(bp, CD186x_CAR, i);
+		sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0);
+	}
+	return 1;
+}
+
+
+/* Reset and setup CD186x chip */
+static int sx_init_CD186x(struct specialix_board  * bp)
+{
+	unsigned long flags;
+	int scaler;
+	int rv = 1;
+	
+	save_flags(flags); cli();
+
+	sx_wait_CCR_off(bp);			   /* Wait for CCR ready        */
+	sx_out_off(bp, CD186x_CCR, CCR_HARDRESET);      /* Reset CD186x chip          */
+	sti();
+	sx_long_delay(HZ/20);                      /* Delay 0.05 sec            */
+	cli();
+	sx_out_off(bp, CD186x_GIVR, SX_ID);             /* Set ID for this chip      */
+	sx_out_off(bp, CD186x_GICR, 0);                 /* Clear all bits            */
+	sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT);      /* Prio for modem intr       */
+	sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT);      /* Prio for transmitter intr */
+	sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT);      /* Prio for receiver intr    */
+	/* Set RegAckEn */
+	sx_out_off(bp, CD186x_SRCR, sx_in (bp, CD186x_SRCR) | SRCR_REGACKEN);
+	
+	/* Setting up prescaler. We need 4 ticks per 1 ms */
+	scaler =  SX_OSCFREQ/SPECIALIX_TPS;
+
+	sx_out_off(bp, CD186x_PPRH, scaler >> 8);
+	sx_out_off(bp, CD186x_PPRL, scaler & 0xff);
+
+	if (!sx_set_irq (bp)) {
+		/* Figure out how to pass this along... */
+		printk (KERN_ERR "Cannot set irq to %d.\n", bp->irq);
+		rv = 0;
+	}
+
+	restore_flags(flags);
+	return rv;
+}
+
+
+int read_cross_byte (struct specialix_board *bp, int reg, int bit)
+{
+	int i;
+	int t;
+
+	for (i=0, t=0;i<8;i++) {
+		sx_out_off (bp, CD186x_CAR, i);
+		if (sx_in_off (bp, reg) & bit) 
+			t |= 1 << i;
+	}
+	return t;
+}
+
+
+#ifdef SPECIALIX_TIMER
+void missed_irq (unsigned long data)
+{
+	if (sx_in ((struct specialix_board *)data, CD186x_SRSR) &  
+	                                            (SRSR_RREQint |
+	                                             SRSR_TREQint |
+	                                             SRSR_MREQint)) {
+		printk (KERN_INFO "Missed interrupt... Calling int from timer. \n");
+		sx_interrupt (((struct specialix_board *)data)->irq, 
+		              NULL, NULL);
+	}
+	missed_irq_timer.expires = jiffies + HZ;
+	add_timer (&missed_irq_timer);
+}
+#endif
+
+
+
+/* Main probing routine, also sets irq. */
+static int sx_probe(struct specialix_board *bp)
+{
+	unsigned char val1, val2;
+#if 0
+	int irqs = 0;
+	int retries;
+#endif
+	int rev;
+	int chip;
+
+	if (sx_check_io_range(bp)) 
+		return 1;
+
+	/* Are the I/O ports here ? */
+	sx_out_off(bp, CD186x_PPRL, 0x5a);
+	short_pause ();
+	val1 = sx_in_off(bp, CD186x_PPRL);
+
+	sx_out_off(bp, CD186x_PPRL, 0xa5);
+	short_pause ();
+	val2 = sx_in_off(bp, CD186x_PPRL);
+
+	
+	if ((val1 != 0x5a) || (val2 != 0xa5)) {
+		printk(KERN_INFO "sx%d: specialix IO8+ Board at 0x%03x not found.\n",
+		       board_No(bp), bp->base);
+		return 1;
+	}
+
+	/* Check the DSR lines that Specialix uses as board 
+	   identification */
+	val1 = read_cross_byte (bp, CD186x_MSVR, MSVR_DSR);
+	val2 = read_cross_byte (bp, CD186x_MSVR, MSVR_RTS);
+#ifdef SPECIALIX_DEBUG
+	printk (KERN_DEBUG "sx%d: DSR lines are: %02x, rts lines are: %02x\n", 
+	        board_No(bp),  val1, val2);
+#endif
+	if (val1 != 0xb2) {
+		printk(KERN_INFO "sx%d: specialix IO8+ ID at 0x%03x not found.\n",
+		       board_No(bp), bp->base);
+		return 1;
+	}
+
+
+#if 0
+	/* It's time to find IRQ for this board */
+	for (retries = 0; retries < 5 && irqs <= 0; retries++) {
+		irqs = probe_irq_on();
+		sx_init_CD186x(bp);	       		/* Reset CD186x chip       */
+		sx_out(bp, CD186x_CAR, 2);               /* Select port 2          */
+		sx_wait_CCR(bp);
+		sx_out(bp, CD186x_CCR, CCR_TXEN);        /* Enable transmitter     */
+		sx_out(bp, CD186x_IER, IER_TXRDY);       /* Enable tx empty intr   */
+		sx_long_delay(HZ/20);	       		
+		irqs = probe_irq_off(irqs);
+
+#if SPECIALIX_DEBUG > 2
+		printk (KERN_DEBUG "SRSR = %02x, ",  sx_in(bp, CD186x_SRSR));
+		printk (           "TRAR = %02x, ",  sx_in(bp, CD186x_TRAR));
+		printk (           "GIVR = %02x, ",  sx_in(bp, CD186x_GIVR));
+		printk (           "GICR = %02x, ",  sx_in(bp, CD186x_GICR));
+		printk (           "\n");
+#endif
+		/* Reset CD186x again      */
+		if (!sx_init_CD186x(bp)) {
+			/* Hmmm. This is dead code anyway. */
+		}
+#if SPECIALIX_DEBUG > 2
+		printk (KERN_DEBUG "val1 = %02x, val2 = %02x, val3 = %02x.\n", 
+		        val1, val2, val3); 
+#endif
+	
+	}
+	
+#if 0
+	if (irqs <= 0) {
+		printk(KERN_ERR "sx%d: Can't find IRQ for specialix IO8+ board at 0x%03x.\n",
+		       board_No(bp), bp->base);
+		return 1;
+	}
+#endif
+	printk (KERN_INFO "Started with irq=%d, but now have irq=%d.\n", bp->irq, irqs);
+	if (irqs > 0)
+		bp->irq = irqs;
+#endif
+	/* Reset CD186x again  */
+	if (!sx_init_CD186x(bp)) {
+		return -EIO;
+	}
+
+	sx_request_io_range(bp);
+	bp->flags |= SX_BOARD_PRESENT;
+	
+	/* Chip           revcode   pkgtype
+	                  GFRCR     SRCR bit 7
+	   CD180 rev B    0x81      0
+	   CD180 rev C    0x82      0
+	   CD1864 rev A   0x82      1
+	   CD1865 rev A   0x83      1  -- Do not use!!! Does not work. 
+	   CD1865 rev B   0x84      1
+	 -- Thanks to Gwen Wang, Cirrus Logic.
+	 */
+
+	switch (sx_in_off(bp, CD186x_GFRCR)) {
+	case 0x82:chip = 1864;rev='A';break;
+	case 0x83:chip = 1865;rev='A';break;
+	case 0x84:chip = 1865;rev='B';break;
+	case 0x85:chip = 1865;rev='C';break; /* Does not exist at this time */
+	default:chip=-1;rev='x';
+	}
+
+#if SPECIALIX_DEBUG > 2
+	printk (KERN_DEBUG " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR) );
+#endif
+
+#ifdef SPECIALIX_TIMER
+	init_timer (&missed_irq_timer);
+	missed_irq_timer.function = missed_irq;
+	missed_irq_timer.data = (unsigned long) bp;
+	missed_irq_timer.expires = jiffies + HZ;
+	add_timer (&missed_irq_timer);
+#endif
+
+	printk(KERN_INFO"sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n",
+	       board_No(bp),
+	       bp->base, bp->irq,
+	       chip, rev);
+
+	return 0;
+}
+
+/* 
+ * 
+ *  Interrupt processing routines.
+ * */
+
+extern inline void sx_mark_event(struct specialix_port * port, int event)
+{
+	/* 
+	 * I'm not quite happy with current scheme all serial
+	 * drivers use their own BH routine.
+	 * It seems this easily can be done with one BH routine
+	 * serving for all serial drivers.
+	 * For now I must introduce another one - SPECIALIX_BH.
+	 * Still hope this will be changed in near future.
+	 * -- Dmitry.
+	 */
+	set_bit(event, &port->event);
+	queue_task(&port->tqueue, &tq_specialix);
+	mark_bh(SPECIALIX_BH);
+}
+
+
+extern inline struct specialix_port * sx_get_port(struct specialix_board * bp,
+					       unsigned char const * what)
+{
+	unsigned char channel;
+	struct specialix_port * port;
+	
+	channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF;
+	if (channel < CD186x_NCH) {
+		port = &sx_port[board_No(bp) * SX_NPORT + channel];
+		if (port->flags & ASYNC_INITIALIZED) {
+			return port;
+		}
+	}
+	printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n", 
+	       board_No(bp), what, channel);
+	return NULL;
+}
+
+
+extern inline void sx_receive_exc(struct specialix_board * bp)
+{
+	struct specialix_port *port;
+	struct tty_struct *tty;
+	unsigned char status;
+	unsigned char ch;
+
+	if (!(port = sx_get_port(bp, "Receive")))
+		return;
+
+	tty = port->tty;
+	if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+		printk(KERN_INFO "sx%d: port %d: Working around flip buffer overflow.\n",
+		       board_No(bp), port_No(port));
+		return;
+	}
+	
+#ifdef SX_REPORT_OVERRUN	
+	status = sx_in(bp, CD186x_RCSR);
+	if (status & RCSR_OE) {
+		port->overrun++;
+#if SPECIALIX_DEBUG 
+		printk(KERN_DEBUG "sx%d: port %d: Overrun. Total %ld overruns.\n", 
+		       board_No(bp), port_No(port), port->overrun);
+#endif		
+	}
+	status &= port->mark_mask;
+#else	
+	status = sx_in(bp, CD186x_RCSR) & port->mark_mask;
+#endif	
+	ch = sx_in(bp, CD186x_RDR);
+	if (!status) {
+		return;
+	}
+	if (status & RCSR_TOUT) {
+		printk(KERN_INFO "sx%d: port %d: Receiver timeout. Hardware problems ?\n", 
+		       board_No(bp), port_No(port));
+		return;
+		
+	} else if (status & RCSR_BREAK) {
+#ifdef SPECIALIX_DEBUG
+		printk(KERN_DEBUG "sx%d: port %d: Handling break...\n",
+		       board_No(bp), port_No(port));
+#endif
+		*tty->flip.flag_buf_ptr++ = TTY_BREAK;
+		if (port->flags & ASYNC_SAK)
+			do_SAK(tty);
+		
+	} else if (status & RCSR_PE) 
+		*tty->flip.flag_buf_ptr++ = TTY_PARITY;
+	
+	else if (status & RCSR_FE) 
+		*tty->flip.flag_buf_ptr++ = TTY_FRAME;
+	
+	else if (status & RCSR_OE)
+		*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
+	
+	else
+		*tty->flip.flag_buf_ptr++ = 0;
+	
+	*tty->flip.char_buf_ptr++ = ch;
+	tty->flip.count++;
+	queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+
+extern inline void sx_receive(struct specialix_board * bp)
+{
+	struct specialix_port *port;
+	struct tty_struct *tty;
+	unsigned char count;
+	
+	if (!(port = sx_get_port(bp, "Receive")))
+		return;
+	
+	tty = port->tty;
+	
+	count = sx_in(bp, CD186x_RDCR);
+	
+#ifdef SX_REPORT_FIFO
+	port->hits[count > 8 ? 9 : count]++;
+#endif	
+	
+	while (count--) {
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+			printk(KERN_INFO "sx%d: port %d: Working around flip buffer overflow.\n",
+			       board_No(bp), port_No(port));
+			break;
+		}
+		*tty->flip.char_buf_ptr++ = sx_in(bp, CD186x_RDR);
+		*tty->flip.flag_buf_ptr++ = 0;
+		tty->flip.count++;
+	}
+	queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+
+extern inline void sx_transmit(struct specialix_board * bp)
+{
+	struct specialix_port *port;
+	struct tty_struct *tty;
+	unsigned char count;
+	
+	
+	if (!(port = sx_get_port(bp, "Transmit")))
+		return;
+	
+	tty = port->tty;
+	
+	if (port->IER & IER_TXEMPTY) {
+		/* FIFO drained */
+		sx_out(bp, CD186x_CAR, port_No(port));
+		port->IER &= ~IER_TXEMPTY;
+		sx_out(bp, CD186x_IER, port->IER);
+		return;
+	}
+	
+	if ((port->xmit_cnt <= 0 && !port->break_length)
+	    || tty->stopped || tty->hw_stopped) {
+		sx_out(bp, CD186x_CAR, port_No(port));
+		port->IER &= ~IER_TXRDY;
+		sx_out(bp, CD186x_IER, port->IER);
+		return;
+	}
+	
+	if (port->break_length) {
+		if (port->break_length > 0) {
+			if (port->COR2 & COR2_ETC) {
+				sx_out(bp, CD186x_TDR, CD186x_C_ESC);
+				sx_out(bp, CD186x_TDR, CD186x_C_SBRK);
+				port->COR2 &= ~COR2_ETC;
+			}
+			count = MIN(port->break_length, 0xff);
+			sx_out(bp, CD186x_TDR, CD186x_C_ESC);
+			sx_out(bp, CD186x_TDR, CD186x_C_DELAY);
+			sx_out(bp, CD186x_TDR, count);
+			if (!(port->break_length -= count))
+				port->break_length--;
+		} else {
+			sx_out(bp, CD186x_TDR, CD186x_C_ESC);
+			sx_out(bp, CD186x_TDR, CD186x_C_EBRK);
+			sx_out(bp, CD186x_COR2, port->COR2);
+			sx_wait_CCR(bp);
+			sx_out(bp, CD186x_CCR, CCR_CORCHG2);
+			port->break_length = 0;
+		}
+		return;
+	}
+	
+	count = CD186x_NFIFO;
+	do {
+		sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]);
+		port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
+		if (--port->xmit_cnt <= 0)
+			break;
+	} while (--count > 0);
+	
+	if (port->xmit_cnt <= 0) {
+		sx_out(bp, CD186x_CAR, port_No(port));
+		port->IER &= ~IER_TXRDY;
+		sx_out(bp, CD186x_IER, port->IER);
+	}
+	if (port->xmit_cnt <= port->wakeup_chars)
+		sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
+}
+
+
+extern inline void sx_check_modem(struct specialix_board * bp)
+{
+	struct specialix_port *port;
+	struct tty_struct *tty;
+	unsigned char mcr;
+
+#ifdef SPECIALIX_DEBUG
+	printk (KERN_DEBUG "Modem intr. ");
+#endif
+	if (!(port = sx_get_port(bp, "Modem")))
+		return;
+	
+	tty = port->tty;
+	
+	mcr = sx_in(bp, CD186x_MCR);
+	printk ("mcr = %02x.\n", mcr);
+
+	if ((mcr & MCR_CDCHG)) {
+#ifdef SPECIALIX_DEBUG 
+		printk (KERN_DEBUG "CD just changed... ");
+#endif
+		if (sx_in(bp, CD186x_MSVR) & MSVR_CD) {
+#ifdef SPECIALIX_DEBUG
+			printk ( "Waking up guys in open.\n");
+#endif
+			wake_up_interruptible(&port->open_wait);
+		}
+		else if (!((port->flags & ASYNC_CALLOUT_ACTIVE) &&
+		           (port->flags & ASYNC_CALLOUT_NOHUP))) {
+#ifdef SPECIALIX_DEBUG
+			printk ( "Sending HUP.\n");
+#endif
+			queue_task(&port->tqueue_hangup,  
+			           &tq_scheduler);      
+		} else {
+#ifdef SPECIALIX_DEBUG
+			printk ( "Don't need to send HUP.\n");
+#endif
+		}
+	}
+	
+#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
+	if (mcr & MCR_CTSCHG) {
+		if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) {
+			tty->hw_stopped = 0;
+			port->IER |= IER_TXRDY;
+			if (port->xmit_cnt <= port->wakeup_chars)
+				sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
+		} else {
+			tty->hw_stopped = 1;
+			port->IER &= ~IER_TXRDY;
+		}
+		sx_out(bp, CD186x_IER, port->IER);
+	}
+	if (mcr & MCR_DSSXHG) {
+		if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) {
+			tty->hw_stopped = 0;
+			port->IER |= IER_TXRDY;
+			if (port->xmit_cnt <= port->wakeup_chars)
+				sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
+		} else {
+			tty->hw_stopped = 1;
+			port->IER &= ~IER_TXRDY;
+		}
+		sx_out(bp, CD186x_IER, port->IER);
+	}
+#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */
+	
+	/* Clear change bits */
+	sx_out(bp, CD186x_MCR, 0);
+}
+
+
+/* The main interrupt processing routine */
+static void sx_interrupt(int irq, void * dev_id, struct pt_regs * regs)
+{
+	unsigned char status;
+	unsigned char ack;
+	struct specialix_board *bp;
+	unsigned long loop = 0;
+	int saved_reg;
+
+	bp = IRQ_to_board[irq];
+	
+	if (!bp || !(bp->flags & SX_BOARD_ACTIVE)) {
+#ifdef SPECIALIX_DEBUG 
+		printk (KERN_DEBUG "sx: False interrupt. irq %d.\n", irq);
+#endif
+		return;
+	}
+
+	saved_reg = bp->reg;
+
+	while ((++loop < 16) && (status = (sx_in(bp, CD186x_SRSR) &
+	                                   (SRSR_RREQint |
+		                            SRSR_TREQint |
+	                                    SRSR_MREQint)))) {	
+		if (status & SRSR_RREQint) {
+			ack = sx_in(bp, CD186x_RRAR);
+
+			if (ack == (SX_ID | GIVR_IT_RCV))
+				sx_receive(bp);
+			else if (ack == (SX_ID | GIVR_IT_REXC))
+				sx_receive_exc(bp);
+			else
+				printk(KERN_ERR "sx%d: Bad receive ack 0x%02x.\n",
+				       board_No(bp), ack);
+		
+		} else if (status & SRSR_TREQint) {
+			ack = sx_in(bp, CD186x_TRAR);
+
+			if (ack == (SX_ID | GIVR_IT_TX))
+				sx_transmit(bp);
+			else
+				printk(KERN_ERR "sx%d: Bad transmit ack 0x%02x.\n",
+				       board_No(bp), ack);
+		} else if (status & SRSR_MREQint) {
+			ack = sx_in(bp, CD186x_MRAR);
+
+			if (ack == (SX_ID | GIVR_IT_MODEM)) 
+				sx_check_modem(bp);
+			else
+				printk(KERN_ERR "sx%d: Bad modem ack 0x%02x.\n",
+				       board_No(bp), ack);
+		
+		} 
+
+		sx_out(bp, CD186x_EOIR, 0);   /* Mark end of interrupt */
+	}
+	bp->reg = saved_reg;
+	outb (bp->reg, bp->base + SX_ADDR_REG);
+}
+
+
+/*
+ *  Routines for open & close processing.
+ */
+
+
+/* Called with disabled interrupts */
+extern inline int sx_setup_board(struct specialix_board * bp)
+{
+	int error;
+
+	if (bp->flags & SX_BOARD_ACTIVE) 
+		return 0;
+
+	error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT, "specialix IO8+", NULL);
+
+	if (error) 
+		return error;
+
+	IRQ_to_board[bp->irq] = bp;
+	(void) sx_in (bp, 0); /* Turn ON interrupts. */
+
+	bp->flags |= SX_BOARD_ACTIVE;
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+
+/* Called with disabled interrupts */
+extern inline void sx_shutdown_board(struct specialix_board *bp)
+{
+	if (!(bp->flags & SX_BOARD_ACTIVE))
+		return;
+	
+	bp->flags &= ~SX_BOARD_ACTIVE;
+	
+	free_irq(bp->irq, NULL);
+	(void) sx_in_off (bp, 0); /* Turn off interrupts. */
+
+	IRQ_to_board[bp->irq] = NULL;
+	
+	MOD_DEC_USE_COUNT;
+}
+
+
+/*
+ * Setting up port characteristics. 
+ * Must be called with disabled interrupts
+ */
+static void sx_change_speed(struct specialix_board *bp, struct specialix_port *port)
+{
+	struct tty_struct *tty;
+	unsigned long baud;
+	long tmp;
+	unsigned char cor1 = 0, cor3 = 0;
+	unsigned char mcor1 = 0, mcor2 = 0;
+	static int again=0;
+	
+	if (!(tty = port->tty) || !tty->termios)
+		return;
+
+	port->IER  = 0;
+	port->COR2 = 0;
+	/* Select port on the board */
+	sx_out(bp, CD186x_CAR, port_No(port));
+
+	/* The Specialix board doens't implement the RTS lines.
+	   They are used to set the IRQ level. Don't touch them. */
+	if (SX_CRTSCTS(tty))
+		port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
+	else
+		port->MSVR =  (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
+#ifdef DEBUG_SPECIALIX
+	printk (KERN_DEBUG "sx: got MSVR=%02x.\n", port->MSVR);
+#endif
+	baud = C_BAUD(tty);
+	
+	if (baud & CBAUDEX) {
+		baud &= ~CBAUDEX;
+		if (baud < 1 || baud > 2) 
+			port->tty->termios->c_cflag &= ~CBAUDEX;
+		else
+			baud += 15;
+	}
+	if (baud == 15) {
+		if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+			baud ++;
+		if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+			baud += 2;
+	}
+	
+	
+	if (!baud_table[baud]) {
+		/* Drop DTR & exit */
+#ifdef SPECIALIX_DEBUG
+		printk (KERN_DEBUG "Dropping DTR...  Hmm....\n");
+#endif
+		if (!SX_CRTSCTS (tty)) {
+			port -> MSVR &= ~ MSVR_DTR;
+			sx_out(bp, CD186x_MSVR, port->MSVR );
+		} 
+#ifdef DEBUG_SPECIALIX
+		else
+			printk (KERN_DEBUG "Can't drop DTR: no DTR.\n");
+#endif
+		return;
+	} else {
+		/* Set DTR on */
+		if (!SX_CRTSCTS (tty)) {
+			port ->MSVR |= MSVR_DTR;
+		}
+	}
+	
+	/*
+	 * Now we must calculate some speed depended things 
+	 */
+	
+	/* Set baud rate for port */
+	tmp = (((SX_OSCFREQ + baud_table[baud]/2) / baud_table[baud] +
+		CD186x_TPC/2) / CD186x_TPC);
+	if ((tmp < 0x10) && (again < jiffies)) { 
+		again = jiffies + HZ * 60;
+		/* Page 48 of version 2.0 of the CL-CD1865 databook */
+		if (tmp >= 12) {
+			printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
+			        "Performance degradation is possible.\n",
+			        port_No (port), tmp);
+		} else {
+			printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
+			        "Warning: overstressing Cirrus chip. "
+                                "This might not work.\n", 
+			        port_No (port), tmp);
+		}
+	}
+
+	sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff); 
+	sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff); 
+	sx_out(bp, CD186x_RBPRL, tmp & 0xff); 
+	sx_out(bp, CD186x_TBPRL, tmp & 0xff);
+	
+	baud = (baud_table[baud] + 5) / 10;   /* Estimated CPS */
+	
+	/* Two timer ticks seems enough to wakeup something like SLIP driver */
+	tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO;		
+	port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
+					      SERIAL_XMIT_SIZE - 1 : tmp);
+	
+	/* Receiver timeout will be transmission time for 1.5 chars */
+	tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud;
+	tmp = (tmp > 0xff) ? 0xff : tmp;
+	sx_out(bp, CD186x_RTPR, tmp);
+	
+	switch (C_CSIZE(tty)) {
+	 case CS5:
+		cor1 |= COR1_5BITS;
+		break;
+	 case CS6:
+		cor1 |= COR1_6BITS;
+		break;
+	 case CS7:
+		cor1 |= COR1_7BITS;
+		break;
+	 case CS8:
+		cor1 |= COR1_8BITS;
+		break;
+	}
+	
+	if (C_CSTOPB(tty)) 
+		cor1 |= COR1_2SB;
+	
+	cor1 |= COR1_IGNORE;
+	if (C_PARENB(tty)) {
+		cor1 |= COR1_NORMPAR;
+		if (C_PARODD(tty)) 
+			cor1 |= COR1_ODDP;
+		if (I_INPCK(tty)) 
+			cor1 &= ~COR1_IGNORE;
+	}
+	/* Set marking of some errors */
+	port->mark_mask = RCSR_OE | RCSR_TOUT;
+	if (I_INPCK(tty)) 
+		port->mark_mask |= RCSR_FE | RCSR_PE;
+	if (I_BRKINT(tty) || I_PARMRK(tty)) 
+		port->mark_mask |= RCSR_BREAK;
+	if (I_IGNPAR(tty)) 
+		port->mark_mask &= ~(RCSR_FE | RCSR_PE);
+	if (I_IGNBRK(tty)) {
+		port->mark_mask &= ~RCSR_BREAK;
+		if (I_IGNPAR(tty)) 
+			/* Real raw mode. Ignore all */
+			port->mark_mask &= ~RCSR_OE;
+	}
+	/* Enable Hardware Flow Control */
+	if (C_CRTSCTS(tty)) {
+#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
+		port->IER |= IER_DSR | IER_CTS;
+		mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
+		mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
+		tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR));
+#else
+		port->COR2 |= COR2_CTSAE; 
+#endif
+	}
+	/* Enable Software Flow Control. FIXME: I'm not sure about this */
+	/* Some people reported that it works, but I still doubt it */
+	if (I_IXON(tty)) {
+		port->COR2 |= COR2_TXIBE;
+		cor3 |= (COR3_FCT | COR3_SCDE);
+		if (I_IXANY(tty))
+			port->COR2 |= COR2_IXM;
+		sx_out(bp, CD186x_SCHR1, START_CHAR(tty));
+		sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty));
+		sx_out(bp, CD186x_SCHR3, START_CHAR(tty));
+		sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty));
+	}
+	if (!C_CLOCAL(tty)) {
+		/* Enable CD check */
+		port->IER |= IER_CD;
+		mcor1 |= MCOR1_CDZD;
+		mcor2 |= MCOR2_CDOD;
+	}
+	
+	if (C_CREAD(tty)) 
+		/* Enable receiver */
+		port->IER |= IER_RXD;
+	
+	/* Set input FIFO size (1-8 bytes) */
+	cor3 |= SPECIALIX_RXFIFO; 
+	/* Setting up CD186x channel registers */
+	sx_out(bp, CD186x_COR1, cor1);
+	sx_out(bp, CD186x_COR2, port->COR2);
+	sx_out(bp, CD186x_COR3, cor3);
+	/* Make CD186x know about registers change */
+	sx_wait_CCR(bp);
+	sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
+	/* Setting up modem option registers */
+#ifdef DEBUG_SPECIALIX
+	printk ("Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2);
+#endif
+	sx_out(bp, CD186x_MCOR1, mcor1);
+	sx_out(bp, CD186x_MCOR2, mcor2);
+	/* Enable CD186x transmitter & receiver */
+	sx_wait_CCR(bp);
+	sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN);
+	/* Enable interrupts */
+	sx_out(bp, CD186x_IER, port->IER);
+	/* And finally set the modem lines... */
+	sx_out(bp, CD186x_MSVR, port->MSVR);
+}
+
+
+/* Must be called with interrupts enabled */
+static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port)
+{
+	unsigned long flags;
+	
+	if (port->flags & ASYNC_INITIALIZED)
+		return 0;
+	
+	if (!port->xmit_buf) {
+		/* We may sleep in get_free_page() */
+		unsigned long tmp;
+		
+		if (!(tmp = get_free_page(GFP_KERNEL)))
+			return -ENOMEM;
+
+		if (port->xmit_buf) {
+			free_page(tmp);
+			return -ERESTARTSYS;
+		}
+		port->xmit_buf = (unsigned char *) tmp;
+	}
+		
+	save_flags(flags); cli();
+		
+	if (port->tty) 
+		clear_bit(TTY_IO_ERROR, &port->tty->flags);
+		
+	if (port->count == 1) 
+		bp->count++;
+		
+	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+	sx_change_speed(bp, port);
+	port->flags |= ASYNC_INITIALIZED;
+		
+	restore_flags(flags);
+	return 0;
+}
+
+
+/* Must be called with interrupts disabled */
+static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port *port)
+{
+	struct tty_struct *tty;
+	
+	if (!(port->flags & ASYNC_INITIALIZED)) 
+		return;
+	
+#ifdef SX_REPORT_OVERRUN
+	printk(KERN_INFO "sx%d: port %d: Total %ld overruns were detected.\n",
+	       board_No(bp), port_No(port), port->overrun);
+#endif	
+#ifdef SX_REPORT_FIFO
+	{
+		int i;
+		
+		printk(KERN_INFO "sx%d: port %d: FIFO hits [ ",
+		       board_No(bp), port_No(port));
+		for (i = 0; i < 10; i++) {
+			printk("%ld ", port->hits[i]);
+		}
+		printk("].\n");
+	}
+#endif	
+	if (port->xmit_buf) {
+		free_page((unsigned long) port->xmit_buf);
+		port->xmit_buf = NULL;
+	}
+
+	/* Select port */
+	sx_out(bp, CD186x_CAR, port_No(port));
+
+	if (!(tty = port->tty) || C_HUPCL(tty)) {
+		/* Drop DTR */
+		sx_out(bp, CD186x_MSVDTR, 0);
+	}
+	
+	/* Reset port */
+	sx_wait_CCR(bp);
+	sx_out(bp, CD186x_CCR, CCR_SOFTRESET);
+	/* Disable all interrupts from this port */
+	port->IER = 0;
+	sx_out(bp, CD186x_IER, port->IER);
+	
+	if (tty)
+		set_bit(TTY_IO_ERROR, &tty->flags);
+	port->flags &= ~ASYNC_INITIALIZED;
+	
+	if (--bp->count < 0) {
+		printk(KERN_ERR "sx%d: sx_shutdown_port: bad board count: %d\n",
+		       board_No(bp), bp->count);
+		bp->count = 0;
+	}
+	
+	/*
+	 * If this is the last opened port on the board
+	 * shutdown whole board
+	 */
+	if (!bp->count) 
+		sx_shutdown_board(bp);
+}
+
+	
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+                           struct specialix_port *port)
+{
+	struct wait_queue wait = { current, NULL };
+	struct specialix_board *bp = port_Board(port);
+	int    retval;
+	int    do_clocal = 0;
+	int    CD;
+
+	/*
+	 * If the device is in the middle of being closed, then block
+	 * until it's done, and then try again.
+	 */
+	if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
+		interruptible_sleep_on(&port->close_wait);
+		if (port->flags & ASYNC_HUP_NOTIFY)
+			return -EAGAIN;
+		else
+			return -ERESTARTSYS;
+	}
+
+	/*
+	 * If this is a callout device, then just make sure the normal
+	 * device isn't being used.
+	 */
+	if (tty->driver.subtype == SPECIALIX_TYPE_CALLOUT) {
+		if (port->flags & ASYNC_NORMAL_ACTIVE)
+			return -EBUSY;
+		if ((port->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    (port->flags & ASYNC_SESSION_LOCKOUT) &&
+		    (port->session != current->session))
+			return -EBUSY;
+		if ((port->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    (port->flags & ASYNC_PGRP_LOCKOUT) &&
+		    (port->pgrp != current->pgrp))
+			return -EBUSY;
+		port->flags |= ASYNC_CALLOUT_ACTIVE;
+		return 0;
+	}
+	
+	/*
+	 * If non-blocking mode is set, or the port is not enabled,
+	 * then make the check up front and then exit.
+	 */
+	if ((filp->f_flags & O_NONBLOCK) ||
+	    (tty->flags & (1 << TTY_IO_ERROR))) {
+		if (port->flags & ASYNC_CALLOUT_ACTIVE)
+			return -EBUSY;
+		port->flags |= ASYNC_NORMAL_ACTIVE;
+		return 0;
+	}
+
+	if (port->flags & ASYNC_CALLOUT_ACTIVE) {
+		if (port->normal_termios.c_cflag & CLOCAL) 
+			do_clocal = 1;
+	} else {
+		if (C_CLOCAL(tty))
+			do_clocal = 1;
+	}
+	
+	/*
+	 * Block waiting for the carrier detect and the line to become
+	 * free (i.e., not in use by the callout).  While we are in
+	 * this loop, info->count is dropped by one, so that
+	 * rs_close() knows when to free things.  We restore it upon
+	 * exit, either normal or abnormal.
+	 */
+	retval = 0;
+	add_wait_queue(&port->open_wait, &wait);
+	cli();
+	if (!tty_hung_up_p(filp))
+		port->count--;
+	sti();
+	port->blocked_open++;
+	while (1) {
+		cli();
+		sx_out(bp, CD186x_CAR, port_No(port));
+		CD = sx_in(bp, CD186x_MSVR) & MSVR_CD;
+		if (!(port->flags & ASYNC_CALLOUT_ACTIVE)) {
+			if (SX_CRTSCTS (tty)) {
+				/* Activate RTS */
+				port->MSVR |= MSVR_DTR;
+				sx_out (bp, CD186x_MSVR, port->MSVR);
+			} else {
+				/* Activate DTR */
+				port->MSVR |= MSVR_DTR;
+				sx_out (bp, CD186x_MSVR, port->MSVR);
+			} 
+		}
+		sti();
+		current->state = TASK_INTERRUPTIBLE;
+		if (tty_hung_up_p(filp) ||
+		    !(port->flags & ASYNC_INITIALIZED)) {
+			if (port->flags & ASYNC_HUP_NOTIFY)
+				retval = -EAGAIN;
+			else
+				retval = -ERESTARTSYS;	
+			break;
+		}
+		if (!(port->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    !(port->flags & ASYNC_CLOSING) &&
+		    (do_clocal || CD))
+			break;
+		if (current->signal & ~current->blocked) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&port->open_wait, &wait);
+	if (!tty_hung_up_p(filp))
+		port->count++;
+	port->blocked_open--;
+	if (retval)
+		return retval;
+	
+	port->flags |= ASYNC_NORMAL_ACTIVE;
+	return 0;
+}	
+
+
+static int sx_open(struct tty_struct * tty, struct file * filp)
+{
+	int board;
+	int error;
+	struct specialix_port * port;
+	struct specialix_board * bp;
+	unsigned long flags;
+	
+	board = SX_BOARD(MINOR(tty->device));
+
+	if (board > SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT))
+		return -ENODEV;
+	
+	bp = &sx_board[board];
+	port = sx_port + board * SX_NPORT + SX_PORT(MINOR(tty->device));
+
+#ifdef DEBUG_SPECIALIX
+	printk (KERN_DEBUG "Board = %d, bp = %p, port = %p, portno = %d.\n", 
+	        board, bp, port, SX_PORT(MINOR(tty->device)));
+#endif
+
+	if (sx_paranoia_check(port, tty->device, "sx_open"))
+		return -ENODEV;
+
+	if ((error = sx_setup_board(bp)))
+		return error;
+
+	port->count++;
+	tty->driver_data = port;
+	port->tty = tty;
+
+	if ((error = sx_setup_port(bp, port))) 
+		return error;
+	
+	if ((error = block_til_ready(tty, filp, port)))
+		return error;
+
+	if ((port->count == 1) && (port->flags & ASYNC_SPLIT_TERMIOS)) {
+		if (tty->driver.subtype == SPECIALIX_TYPE_NORMAL)
+			*tty->termios = port->normal_termios;
+		else
+			*tty->termios = port->callout_termios;
+		save_flags(flags); cli();
+		sx_change_speed(bp, port);
+		restore_flags(flags);
+	}
+
+	port->session = current->session;
+	port->pgrp = current->pgrp;
+	return 0;
+}
+
+
+static void sx_close(struct tty_struct * tty, struct file * filp)
+{
+	struct specialix_port *port = (struct specialix_port *) tty->driver_data;
+	struct specialix_board *bp;
+	unsigned long flags;
+	unsigned long timeout;
+	
+	if (!port || sx_paranoia_check(port, tty->device, "close"))
+		return;
+	
+	save_flags(flags); cli();
+	if (tty_hung_up_p(filp)) {
+		restore_flags(flags);
+		return;
+	}
+	
+	bp = port_Board(port);
+	if ((tty->count == 1) && (port->count != 1)) {
+		printk(KERN_ERR "sx%d: sx_close: bad port count;"
+		       " tty->count is 1, port count is %d\n",
+		       board_No(bp), port->count);
+		port->count = 1;
+	}
+	if (--port->count < 0) {
+		printk(KERN_ERR "sx%d: sx_close: bad port count for tty%d: %d\n",
+		       board_No(bp), port_No(port), port->count);
+		port->count = 0;
+	}
+	if (port->count) {
+		restore_flags(flags);
+		return;
+	}
+	port->flags |= ASYNC_CLOSING;
+	/*
+	 * Save the termios structure, since this port may have
+	 * separate termios for callout and dialin.
+	 */
+	if (port->flags & ASYNC_NORMAL_ACTIVE)
+		port->normal_termios = *tty->termios;
+	if (port->flags & ASYNC_CALLOUT_ACTIVE)
+		port->callout_termios = *tty->termios;
+	/*
+	 * Now we wait for the transmit buffer to clear; and we notify 
+	 * the line discipline to only process XON/XOFF characters.
+	 */
+	tty->closing = 1;
+	if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+		tty_wait_until_sent(tty, port->closing_wait);
+	/*
+	 * At this point we stop accepting input.  To do this, we
+	 * disable the receive line status interrupts, and tell the
+	 * interrupt driver to stop checking the data ready bit in the
+	 * line status register.
+	 */
+	port->IER &= ~IER_RXD;
+	if (port->flags & ASYNC_INITIALIZED) {
+		port->IER &= ~IER_TXRDY;
+		port->IER |= IER_TXEMPTY;
+		sx_out(bp, CD186x_CAR, port_No(port));
+		sx_out(bp, CD186x_IER, port->IER);
+		/*
+		 * Before we drop DTR, make sure the UART transmitter
+		 * has completely drained; this is especially
+		 * important if there is a transmit FIFO!
+		 */
+		timeout = jiffies+HZ;
+		while(port->IER & IER_TXEMPTY) {
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + port->timeout;
+			schedule();
+			if (jiffies > timeout) {
+				printk (KERN_INFO "Timeout waiting for close\n");
+				break;
+			}
+		}
+
+	}
+	sx_shutdown_port(bp, port);
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+	tty->closing = 0;
+	port->event = 0;
+	port->tty = 0;
+	if (port->blocked_open) {
+		if (port->close_delay) {
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + port->close_delay;
+			schedule();
+		}
+		wake_up_interruptible(&port->open_wait);
+	}
+	port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+			 ASYNC_CLOSING);
+	wake_up_interruptible(&port->close_wait);
+	restore_flags(flags);
+}
+
+
+static int sx_write(struct tty_struct * tty, int from_user, 
+                    const unsigned char *buf, int count)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	struct specialix_board *bp;
+	int c, total = 0;
+	unsigned long flags;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_write"))
+		return 0;
+	
+	bp = port_Board(port);
+
+	if (!tty || !port->xmit_buf || !tmp_buf)
+		return 0;
+
+	if (from_user)
+		down(&tmp_buf_sem);
+
+	save_flags(flags);
+	while (1) {
+		cli();		
+		c = MIN(count, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
+		                   SERIAL_XMIT_SIZE - port->xmit_head));
+		if (c <= 0)
+			break;
+
+		if (from_user) {
+			copy_from_user(tmp_buf, buf, c);
+			c = MIN(c, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
+			               SERIAL_XMIT_SIZE - port->xmit_head));
+			memcpy(port->xmit_buf + port->xmit_head, tmp_buf, c);
+		} else
+			memcpy(port->xmit_buf + port->xmit_head, buf, c);
+		port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+		port->xmit_cnt += c;
+		restore_flags(flags);
+		buf += c;
+		count -= c;
+		total += c;
+	}
+	if (from_user)
+		up(&tmp_buf_sem);
+	if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
+	    !(port->IER & IER_TXRDY)) {
+		port->IER |= IER_TXRDY;
+		sx_out(bp, CD186x_CAR, port_No(port));
+		sx_out(bp, CD186x_IER, port->IER);
+	}
+	restore_flags(flags);
+	return total;
+}
+
+
+static void sx_put_char(struct tty_struct * tty, unsigned char ch)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	unsigned long flags;
+
+	if (sx_paranoia_check(port, tty->device, "sx_put_char"))
+		return;
+
+	if (!tty || !port->xmit_buf)
+		return;
+
+	save_flags(flags); cli();
+	
+	if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
+		restore_flags(flags);
+		return;
+	}
+
+	port->xmit_buf[port->xmit_head++] = ch;
+	port->xmit_head &= SERIAL_XMIT_SIZE - 1;
+	port->xmit_cnt++;
+	restore_flags(flags);
+}
+
+
+static void sx_flush_chars(struct tty_struct * tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	unsigned long flags;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_flush_chars"))
+		return;
+	
+	if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+	    !port->xmit_buf)
+		return;
+
+	save_flags(flags); cli();
+	port->IER |= IER_TXRDY;
+	sx_out(port_Board(port), CD186x_CAR, port_No(port));
+	sx_out(port_Board(port), CD186x_IER, port->IER);
+	restore_flags(flags);
+}
+
+
+static int sx_write_room(struct tty_struct * tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	int	ret;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_write_room"))
+		return 0;
+
+	ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
+	if (ret < 0)
+		ret = 0;
+	return ret;
+}
+
+
+static int sx_chars_in_buffer(struct tty_struct *tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_chars_in_buffer"))
+		return 0;
+	
+	return port->xmit_cnt;
+}
+
+
+static void sx_flush_buffer(struct tty_struct *tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	unsigned long flags;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_flush_buffer"))
+		return;
+
+	save_flags(flags); cli();
+	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+	restore_flags(flags);
+	
+	wake_up_interruptible(&tty->write_wait);
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+}
+
+
+static int sx_get_modem_info(struct specialix_port * port, unsigned int *value)
+{
+	struct specialix_board * bp;
+	unsigned char status;
+	unsigned int result;
+	unsigned long flags;
+
+	bp = port_Board(port);
+	save_flags(flags); cli();
+	sx_out(bp, CD186x_CAR, port_No(port));
+	status = sx_in(bp, CD186x_MSVR);
+	restore_flags(flags);
+#ifdef DEBUG_SPECIALIX
+	printk (KERN_DEBUG "Got msvr[%d] = %02x, car = %d.\n", 
+		port_No(port), status, sx_in (bp, CD186x_CAR));
+	printk (KERN_DEBUG "sx_port = %p, port = %p\n", sx_port, port);
+#endif
+	if (SX_CRTSCTS(port->tty)) {
+		result  = /*   (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */ 
+		          |   ((status & MSVR_DTR) ? TIOCM_RTS : 0)
+		          |   ((status & MSVR_CD)  ? TIOCM_CAR : 0)
+		          |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
+		          |   ((status & MSVR_CTS) ? TIOCM_CTS : 0);
+	} else {
+		result  = /*   (status & MSVR_RTS) ? */ TIOCM_RTS /* : 0) */ 
+		          |   ((status & MSVR_DTR) ? TIOCM_DTR : 0)
+		          |   ((status & MSVR_CD)  ? TIOCM_CAR : 0)
+		          |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
+		          |   ((status & MSVR_CTS) ? TIOCM_CTS : 0);
+	}
+	put_user(result,(unsigned long *) value);
+	return 0;
+}
+
+
+static int sx_set_modem_info(struct specialix_port * port, unsigned int cmd,
+                             unsigned int *value)
+{
+	int error;
+	unsigned int arg;
+	unsigned long flags;
+	struct specialix_board *bp = port_Board(port);
+
+	error = verify_area(VERIFY_READ, value, sizeof(int));
+	if (error) 
+		return error;
+
+	Get_user(arg, (unsigned long *) value);
+	switch (cmd) {
+	case TIOCMBIS: 
+	   /*	if (arg & TIOCM_RTS) 
+			port->MSVR |= MSVR_RTS; */
+	   /*   if (arg & TIOCM_DTR)
+			port->MSVR |= MSVR_DTR; */
+
+		if (SX_CRTSCTS(port->tty)) {
+			if (arg & TIOCM_RTS)
+				port->MSVR |= MSVR_DTR; 
+		} else {
+			if (arg & TIOCM_DTR)
+				port->MSVR |= MSVR_DTR; 
+		}	     
+		break;
+	case TIOCMBIC:
+	  /*	if (arg & TIOCM_RTS)
+			port->MSVR &= ~MSVR_RTS; */
+	  /*    if (arg & TIOCM_DTR)
+			port->MSVR &= ~MSVR_DTR; */
+		if (SX_CRTSCTS(port->tty)) {
+			if (arg & TIOCM_RTS)
+				port->MSVR &= ~MSVR_DTR;
+		} else {
+			if (arg & TIOCM_DTR)
+				port->MSVR &= ~MSVR_DTR;
+		}
+		break;
+	case TIOCMSET:
+	  /* port->MSVR = (arg & TIOCM_RTS) ? (port->MSVR | MSVR_RTS) : 
+						 (port->MSVR & ~MSVR_RTS); */
+	  /* port->MSVR = (arg & TIOCM_DTR) ? (port->MSVR | MSVR_DTR) : 
+						 (port->MSVR & ~MSVR_DTR); */
+		if (SX_CRTSCTS(port->tty)) {
+	  		port->MSVR = (arg & TIOCM_RTS) ? 
+			                         (port->MSVR |  MSVR_DTR) : 
+			                         (port->MSVR & ~MSVR_DTR);
+		} else {
+			port->MSVR = (arg & TIOCM_DTR) ?
+			                         (port->MSVR |  MSVR_DTR):
+			                         (port->MSVR & ~MSVR_DTR);
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	save_flags(flags); cli();
+	sx_out(bp, CD186x_CAR, port_No(port));
+	sx_out(bp, CD186x_MSVR, port->MSVR);
+	restore_flags(flags);
+	return 0;
+}
+
+
+extern inline void sx_send_break(struct specialix_port * port, unsigned long length)
+{
+	struct specialix_board *bp = port_Board(port);
+	unsigned long flags;
+	
+	save_flags(flags); cli();
+	port->break_length = SPECIALIX_TPS / HZ * length;
+	port->COR2 |= COR2_ETC;
+	port->IER  |= IER_TXRDY;
+	sx_out(bp, CD186x_CAR, port_No(port));
+	sx_out(bp, CD186x_COR2, port->COR2);
+	sx_out(bp, CD186x_IER, port->IER);
+	sx_wait_CCR(bp);
+	sx_out(bp, CD186x_CCR, CCR_CORCHG2);
+	sx_wait_CCR(bp);
+	restore_flags(flags);
+}
+
+
+extern inline int sx_set_serial_info(struct specialix_port * port,
+                                     struct serial_struct * newinfo)
+{
+	struct serial_struct tmp;
+	struct specialix_board *bp = port_Board(port);
+	int change_speed;
+	unsigned long flags;
+	int error;
+	
+	error = verify_area(VERIFY_READ, (void *) newinfo, sizeof(tmp));
+	if (error)
+		return error;
+
+	copy_from_user(&tmp, newinfo, sizeof(tmp));
+	
+#if 0	
+	if ((tmp.irq != bp->irq) ||
+	    (tmp.port != bp->base) ||
+	    (tmp.type != PORT_CIRRUS) ||
+	    (tmp.baud_base != (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC) ||
+	    (tmp.custom_divisor != 0) ||
+	    (tmp.xmit_fifo_size != CD186x_NFIFO) ||
+	    (tmp.flags & ~SPECIALIX_LEGAL_FLAGS))
+		return -EINVAL;
+#endif	
+
+	change_speed = ((port->flags & ASYNC_SPD_MASK) !=
+			(tmp.flags & ASYNC_SPD_MASK));
+	
+	if (!suser()) {
+		if ((tmp.close_delay != port->close_delay) ||
+		    (tmp.closing_wait != port->closing_wait) ||
+		    ((tmp.flags & ~ASYNC_USR_MASK) !=
+		     (port->flags & ~ASYNC_USR_MASK)))
+			return -EPERM;
+		port->flags = ((port->flags & ~ASYNC_USR_MASK) |
+		                  (tmp.flags & ASYNC_USR_MASK));
+	} else {
+		port->flags = ((port->flags & ~ASYNC_FLAGS) |
+		                  (tmp.flags & ASYNC_FLAGS));
+		port->close_delay = tmp.close_delay;
+		port->closing_wait = tmp.closing_wait;
+	}
+	if (change_speed) {
+		save_flags(flags); cli();
+		sx_change_speed(bp, port);
+		restore_flags(flags);
+	}
+	return 0;
+}
+
+
+extern inline int sx_get_serial_info(struct specialix_port * port,
+				     struct serial_struct * retinfo)
+{
+	struct serial_struct tmp;
+	struct specialix_board *bp = port_Board(port);
+	int error;
+	
+	error = verify_area(VERIFY_WRITE, (void *) retinfo, sizeof(tmp));
+	if (error)
+		return error;
+
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.type = PORT_CIRRUS;
+	tmp.line = port - sx_port;
+	tmp.port = bp->base;
+	tmp.irq  = bp->irq;
+	tmp.flags = port->flags;
+	tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC;
+	tmp.close_delay = port->close_delay * HZ/100;
+	tmp.closing_wait = port->closing_wait * HZ/100;
+	tmp.xmit_fifo_size = CD186x_NFIFO;
+	copy_to_user(retinfo, &tmp, sizeof(tmp));
+	return 0;
+}
+
+
+static int sx_ioctl(struct tty_struct * tty, struct file * filp, 
+                    unsigned int cmd, unsigned long arg)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	int error;
+	int retval;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_ioctl"))
+		return -ENODEV;
+	
+	switch (cmd) {
+	 case TCSBRK:	/* SVID version: non-zero arg --> no break */
+		retval = tty_check_change(tty);
+		if (retval)
+			return retval;
+		tty_wait_until_sent(tty, 0);
+		if (!arg)
+			sx_send_break(port, HZ/4);	/* 1/4 second */
+		return 0;
+	 case TCSBRKP:	/* support for POSIX tcsendbreak() */
+		retval = tty_check_change(tty);
+		if (retval)
+			return retval;
+		tty_wait_until_sent(tty, 0);
+		sx_send_break(port, arg ? arg*(HZ/10) : HZ/4);
+		return 0;
+	 case TIOCGSOFTCAR:
+		error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
+		if (error)
+			return error;
+		put_user(C_CLOCAL(tty) ? 1 : 0,
+		         (unsigned long *) arg);
+		return 0;
+	 case TIOCSSOFTCAR:
+		Get_user(arg, (unsigned long *) arg);
+		tty->termios->c_cflag =
+			((tty->termios->c_cflag & ~CLOCAL) |
+			(arg ? CLOCAL : 0));
+		return 0;
+	 case TIOCMGET:
+		error = verify_area(VERIFY_WRITE, (void *) arg,
+		                    sizeof(unsigned int));
+		if (error)
+			return error;
+		return sx_get_modem_info(port, (unsigned int *) arg);
+	 case TIOCMBIS:
+	 case TIOCMBIC:
+	 case TIOCMSET:
+		return sx_set_modem_info(port, cmd, (unsigned int *) arg);
+	 case TIOCGSERIAL:	
+		return sx_get_serial_info(port, (struct serial_struct *) arg);
+	 case TIOCSSERIAL:	
+		return sx_set_serial_info(port, (struct serial_struct *) arg);
+	 default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+
+static void sx_throttle(struct tty_struct * tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	struct specialix_board *bp;
+	unsigned long flags;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_throttle"))
+		return;
+	
+	bp = port_Board(port);
+	
+	save_flags(flags); cli();
+
+	/* Use DTR instead of RTS ! */
+	if (SX_CRTSCTS (tty)) 
+		port->MSVR &= ~MSVR_DTR;
+	else {
+		/* Auch!!! I think the system shouldn't call this then. */
+		/* Or maybe we're supposed (allowed?) to do our side of hw
+		   handshake anyway, even when hardware handshake is off. 
+		   When you see this in your logs, please report.... */
+		printk (KERN_ERR "sx%d: Need to throttle, but can't (hardware hs is off)\n",
+	                 port_No (port));
+	}
+	sx_out(bp, CD186x_CAR, port_No(port));
+	if (I_IXOFF(tty)) {
+		sx_wait_CCR(bp);
+		sx_out(bp, CD186x_CCR, CCR_SSCH2);
+		sx_wait_CCR(bp);
+	}
+	sx_out(bp, CD186x_MSVR, port->MSVR);
+	restore_flags(flags);
+}
+
+
+static void sx_unthrottle(struct tty_struct * tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	struct specialix_board *bp;
+	unsigned long flags;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_unthrottle"))
+		return;
+	
+	bp = port_Board(port);
+	
+	save_flags(flags); cli();
+	/* XXXX Use DTR INSTEAD???? */
+	if (SX_CRTSCTS(tty)) {
+		port->MSVR |= MSVR_DTR;
+	} /* Else clause: see remark in "sx_throttle"... */
+
+	sx_out(bp, CD186x_CAR, port_No(port));
+	if (I_IXOFF(tty)) {
+		sx_wait_CCR(bp);
+		sx_out(bp, CD186x_CCR, CCR_SSCH1);
+		sx_wait_CCR(bp);
+	}
+	sx_out(bp, CD186x_MSVR, port->MSVR);
+	restore_flags(flags);
+}
+
+
+static void sx_stop(struct tty_struct * tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	struct specialix_board *bp;
+	unsigned long flags;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_stop"))
+		return;
+	
+	bp = port_Board(port);
+	
+	save_flags(flags); cli();
+	port->IER &= ~IER_TXRDY;
+	sx_out(bp, CD186x_CAR, port_No(port));
+	sx_out(bp, CD186x_IER, port->IER);
+	restore_flags(flags);
+}
+
+
+static void sx_start(struct tty_struct * tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	struct specialix_board *bp;
+	unsigned long flags;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_start"))
+		return;
+	
+	bp = port_Board(port);
+	
+	save_flags(flags); cli();
+	if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) {
+		port->IER |= IER_TXRDY;
+		sx_out(bp, CD186x_CAR, port_No(port));
+		sx_out(bp, CD186x_IER, port->IER);
+	}
+	restore_flags(flags);
+}
+
+
+/*
+ * This routine is called from the scheduler tqueue when the interrupt
+ * routine has signalled that a hangup has occurred.  The path of
+ * hangup processing is:
+ *
+ * 	serial interrupt routine -> (scheduler tqueue) ->
+ * 	do_sx_hangup() -> tty->hangup() -> sx_hangup()
+ * 
+ */
+static void do_sx_hangup(void *private_)
+{
+	struct specialix_port	*port = (struct specialix_port *) private_;
+	struct tty_struct	*tty;
+	
+	tty = port->tty;
+	if (!tty)
+		return;
+
+	tty_hangup(tty);
+}
+
+
+static void sx_hangup(struct tty_struct * tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	struct specialix_board *bp;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_hangup"))
+		return;
+	
+	bp = port_Board(port);
+	
+	sx_shutdown_port(bp, port);
+	port->event = 0;
+	port->count = 0;
+	port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+	port->tty = 0;
+	wake_up_interruptible(&port->open_wait);
+}
+
+
+static void sx_set_termios(struct tty_struct * tty, struct termios * old_termios)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	unsigned long flags;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_set_termios"))
+		return;
+	
+	if (tty->termios->c_cflag == old_termios->c_cflag &&
+	    tty->termios->c_iflag == old_termios->c_iflag)
+		return;
+
+	save_flags(flags); cli();
+	sx_change_speed(port_Board(port), port);
+	restore_flags(flags);
+
+	if ((old_termios->c_cflag & CRTSCTS) &&
+	    !(tty->termios->c_cflag & CRTSCTS)) {
+		tty->hw_stopped = 0;
+		sx_start(tty);
+	}
+}
+
+
+static void do_specialix_bh(void)
+{
+	 run_task_queue(&tq_specialix);
+}
+
+
+static void do_softint(void *private_)
+{
+	struct specialix_port	*port = (struct specialix_port *) private_;
+	struct tty_struct	*tty;
+	
+	if(!(tty = port->tty)) 
+		return;
+
+	if (clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) {
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup)
+			(tty->ldisc.write_wakeup)(tty);
+		wake_up_interruptible(&tty->write_wait);
+	}
+}
+
+
+static int sx_init_drivers(void)
+{
+	int error;
+	int i;
+
+	
+	if (!(tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL))) {
+		printk(KERN_ERR "sx: Couldn't get free page.\n");
+		return 1;
+	}
+	init_bh(SPECIALIX_BH, do_specialix_bh);
+	memset(IRQ_to_board, 0, sizeof(IRQ_to_board));
+	memset(&specialix_driver, 0, sizeof(specialix_driver));
+	specialix_driver.magic = TTY_DRIVER_MAGIC;
+	specialix_driver.name = "ttyW";
+	specialix_driver.major = SPECIALIX_NORMAL_MAJOR;
+	specialix_driver.num = SX_NBOARD * SX_NPORT;
+	specialix_driver.type = TTY_DRIVER_TYPE_SERIAL;
+	specialix_driver.subtype = SPECIALIX_TYPE_NORMAL;
+	specialix_driver.init_termios = tty_std_termios;
+	specialix_driver.init_termios.c_cflag =
+		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	specialix_driver.flags = TTY_DRIVER_REAL_RAW;
+	specialix_driver.refcount = &specialix_refcount;
+	specialix_driver.table = specialix_table;
+	specialix_driver.termios = specialix_termios;
+	specialix_driver.termios_locked = specialix_termios_locked;
+
+	specialix_driver.open  = sx_open;
+	specialix_driver.close = sx_close;
+	specialix_driver.write = sx_write;
+	specialix_driver.put_char = sx_put_char;
+	specialix_driver.flush_chars = sx_flush_chars;
+	specialix_driver.write_room = sx_write_room;
+	specialix_driver.chars_in_buffer = sx_chars_in_buffer;
+	specialix_driver.flush_buffer = sx_flush_buffer;
+	specialix_driver.ioctl = sx_ioctl;
+	specialix_driver.throttle = sx_throttle;
+	specialix_driver.unthrottle = sx_unthrottle;
+	specialix_driver.set_termios = sx_set_termios;
+	specialix_driver.stop = sx_stop;
+	specialix_driver.start = sx_start;
+	specialix_driver.hangup = sx_hangup;
+
+	specialix_callout_driver = specialix_driver;
+	specialix_callout_driver.name = "cuw";
+	specialix_callout_driver.major = SPECIALIX_CALLOUT_MAJOR;
+	specialix_callout_driver.subtype = SPECIALIX_TYPE_CALLOUT;
+	
+	if ((error = tty_register_driver(&specialix_driver))) {
+		free_page((unsigned long)tmp_buf);
+		printk(KERN_ERR "sx: Couldn't register specialix IO8+ driver, error = %d\n",
+		       error);
+		return 1;
+	}
+	if ((error = tty_register_driver(&specialix_callout_driver))) {
+		free_page((unsigned long)tmp_buf);
+		tty_unregister_driver(&specialix_driver);
+		printk(KERN_ERR "sx: Couldn't register specialix IO8+ callout driver, error = %d\n",
+		       error);
+		return 1;
+	}
+	
+	memset(sx_port, 0, sizeof(sx_port));
+	for (i = 0; i < SX_NPORT * SX_NBOARD; i++) {
+		sx_port[i].callout_termios = specialix_callout_driver.init_termios;
+		sx_port[i].normal_termios  = specialix_driver.init_termios;
+		sx_port[i].magic = SPECIALIX_MAGIC;
+		sx_port[i].tqueue.routine = do_softint;
+		sx_port[i].tqueue.data = &sx_port[i];
+		sx_port[i].tqueue_hangup.routine = do_sx_hangup;
+		sx_port[i].tqueue_hangup.data = &sx_port[i];
+		sx_port[i].close_delay = 50 * HZ/100;
+		sx_port[i].closing_wait = 3000 * HZ/100;
+	}
+	
+	return 0;
+}
+
+
+static void sx_release_drivers(void)
+{
+	free_page((unsigned long)tmp_buf);
+	tty_unregister_driver(&specialix_driver);
+	tty_unregister_driver(&specialix_callout_driver);
+}
+
+
+#ifndef MODULE
+/*
+ * Called at boot time.
+ * 
+ * You can specify IO base for up to SX_NBOARD cards,
+ * using line "specialix=0xiobase1,0xiobase2,.." at LILO prompt.
+ * Note that there will be no probing at default
+ * addresses in this case.
+ *
+ */ 
+void specialix_setup(char *str, int * ints)
+{
+	int i;
+        
+	for (i=0;i<SX_NBOARD;i++) {
+		sx_board[i].base = 0;
+	}
+
+	for (i = 1; i <= ints[0]; i++) {
+		if (i&1)
+			sx_board[i/2].base = ints[i];
+		else
+			sx_board[i/2 -1].irq = ints[i];
+	}
+}
+#endif
+
+/* 
+ * This routine must be called by kernel at boot time 
+ */
+int specialix_init(void) 
+{
+	int i;
+	int found = 0;
+
+	printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997.\n");
+	printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n");
+#ifdef CONFIG_SPECIALIX_RTSCTS
+	printk (KERN_INFO "sx: DTR/RTS pin is always RTS.\n");
+#else
+	printk (KERN_INFO "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n");
+#endif
+	
+	if (sx_init_drivers()) 
+		return -EIO;
+
+	for (i = 0; i < SX_NBOARD; i++) 
+		if (sx_board[i].base && !sx_probe(&sx_board[i]))
+			found++;
+
+	if (!found) {
+		sx_release_drivers();
+		printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+#ifdef MODULE
+int iobase[SX_NBOARD]  = {0,};
+
+int irq [SX_NBOARD] = {0,};
+
+/*
+ * You can setup up to 4 boards.
+ * by specifying "iobase=0xXXX iobase1=0xXXX ..." as insmod parameter.
+ * 
+ * More than 4 boards in one computer is not possible, as the card can
+ * only use 4 different interrupts. 
+ *
+ */
+int init_module(void) 
+{
+	int i;
+
+	if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) {
+		for(i = 0; i < SX_NBOARD; i++) {
+			sx_board[i].base = iobase[i];
+			sx_board[i].irq = irq[i];
+		}
+	}
+
+	return specialix_init();
+}
+	
+
+void cleanup_module(void)
+{
+	int i;
+	
+	sx_release_drivers();
+	for (i = 0; i < SX_NBOARD; i++)
+		if (sx_board[i].flags & SX_BOARD_PRESENT) 
+			sx_release_io_range(&sx_board[i]);
+#ifdef SPECIALIX_TIMER
+	del_timer (&missed_irq_timer);
+#endif
+	
+}
+#endif /* MODULE */
diff -u --recursive --new-file v2.0.30/linux/drivers/char/specialix_io8.h linux/drivers/char/specialix_io8.h
--- v2.0.30/linux/drivers/char/specialix_io8.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/specialix_io8.h	Tue Aug 12 13:06:54 1997
@@ -0,0 +1,146 @@
+/*
+ *      linux/drivers/char/specialix_io8.h  -- 
+ *                                   Specialix IO8+ multiport serial driver.
+ *
+ *      Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl)
+ *      Copyright (C) 1994-1996  Dmitry Gorodchanin (begemot@bgm.rosprint.net)
+ *
+ *
+ *      Specialix pays for the development and support of this driver.
+ *      Please DO contact io8-linux@specialix.co.uk if you require
+ *      support.
+ *
+ *      This driver was developped in the BitWizard linux device
+ *      driver service. If you require a linux device driver for your
+ *      product, please contact devices@BitWizard.nl for a quote.
+ *
+ *      This code is firmly based on the riscom/8 serial driver,
+ *      written by Dmitry Gorodchanin. The specialix IO8+ card
+ *      programming information was obtained from the CL-CD1865 Data
+ *      Book, and Specialix document number 6200059: IO8+ Hardware
+ *      Functional Specification.
+ *
+ *      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 __LINUX_SPECIALIX_H
+#define __LINUX_SPECIALIX_H
+
+#include <linux/serial.h>
+
+#ifdef __KERNEL__
+
+#define SX_NBOARD		4
+/* NOTE: Specialix decoder recognizes 4 addresses, but only two are used.... */
+#define SX_IO_SPACE             4
+/* eight ports per board. */
+#define SX_NPORT        	8
+#define SX_BOARD(line)		((line) / SX_NPORT)
+#define SX_PORT(line)		((line) & (SX_NPORT - 1))
+
+
+#define SX_DATA_REG 0     /* Base+0 : Data register */
+#define SX_ADDR_REG 1     /* base+1 : Address register. */
+
+#define MHz *1000000	/* I'm ashamed of myself. */
+
+/* On-board oscillator frequency */
+#define SX_OSCFREQ      (25 MHz/2)
+/* There is a 25MHz crystal on the board, but the chip is in /2 mode */
+
+
+/* Ticks per sec. Used for setting receiver timeout and break length */
+#define SPECIALIX_TPS		4000
+
+/* Yeah, after heavy testing I decided it must be 6.
+ * Sure, You can change it if needed.
+ */
+#define SPECIALIX_RXFIFO	6	/* Max. receiver FIFO size (1-8) */
+
+#define SPECIALIX_MAGIC		0x0907
+
+#define SX_CCR_TIMEOUT 10000   /* CCR timeout. You may need to wait upto
+                                  10 milliseconds before the internal
+                                  processor is available again after
+                                  you give it a command */
+
+#define SX_IOBASE1	0x100
+#define SX_IOBASE2	0x180
+#define SX_IOBASE3	0x250
+#define SX_IOBASE4	0x260
+
+struct specialix_board {
+	unsigned long   flags;
+	unsigned short	base;
+	unsigned char 	irq;
+	signed   char	count;
+	unsigned char	DTR;
+        int reg;
+};
+
+#define SX_BOARD_PRESENT	0x00000001
+#define SX_BOARD_ACTIVE		0x00000002
+
+
+struct specialix_port {
+	int			magic;
+	int			baud_base;
+	int			flags;
+	struct tty_struct 	* tty;
+	int			count;
+	int			blocked_open;
+	int			event;
+	int			timeout;
+	int			close_delay;
+	long			session;
+	long			pgrp;
+	unsigned char 		* xmit_buf;
+	int			custom_divisor;
+	int			xmit_head;
+	int			xmit_tail;
+	int			xmit_cnt;
+	struct termios          normal_termios;
+	struct termios		callout_termios;
+	struct wait_queue	*open_wait;
+	struct wait_queue	*close_wait;
+	struct tq_struct	tqueue;
+	struct tq_struct	tqueue_hangup;
+	short			wakeup_chars;
+	short			break_length;
+	unsigned short		closing_wait;
+	unsigned char		mark_mask;
+	unsigned char		IER;
+	unsigned char		MSVR;
+	unsigned char		COR2;
+#ifdef SX_REPORT_OVERRUN
+	unsigned long		overrun;
+#endif	
+#ifdef SX_REPORT_FIFO
+	unsigned long		hits[10];
+#endif
+};
+
+#endif /* __KERNEL__ */
+#endif /* __LINUX_SPECIALIX_H */
+
+
+
+
+
+
+
+
+
diff -u --recursive --new-file v2.0.30/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.0.30/linux/drivers/char/tty_io.c	Thu Oct 31 04:34:58 1996
+++ linux/drivers/char/tty_io.c	Wed Aug 13 09:56:45 1997
@@ -11,7 +11,7 @@
  * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0.
  *
  * Modified by Theodore Ts'o, 9/14/92, to dynamically allocate the
- * tty_struct and tty_queue structures.  Previously there was a array
+ * tty_struct and tty_queue structures.  Previously there was an array
  * of 256 tty_struct's which was statically allocated, and the
  * tty_queue structures were allocated at boot time.  Both are now
  * dynamically allocated only when the tty is open.
@@ -44,6 +44,9 @@
  * 
  * Restrict vt switching via ioctl()
  *      -- grif@cs.ucr.edu, 5-Dec-95
+ *
+ * Rewrote init_dev and release_dev to eliminate races.
+ *	-- Bill Hawes <whawes@star.net>, June 97
  */
 
 #include <linux/config.h>
@@ -830,14 +833,27 @@
 		(unsigned int)count);
 }
 
+/* Semaphore to protect creating and releasing a tty */
+static struct semaphore tty_sem = MUTEX;
+static void down_tty_sem(int index)
+{
+	down(&tty_sem);
+}
+static void up_tty_sem(int index)
+{
+	up(&tty_sem);
+}
+static void release_mem(struct tty_struct *tty, int idx);
+
 /*
- * This is so ripe with races that you should *really* not touch this
- * unless you know exactly what you are doing. All the changes have to be
- * made atomically, or there may be incorrect pointers all over the place.
+ * Rewritten to remove races and properly clean up after a failed open.  
+ * The new code protects the open with a semaphore, so it's really 
+ * quite straightforward.  The semaphore locking can probably be
+ * relaxed for the (most common) case of reopening a tty.
  */
 static int init_dev(kdev_t device, struct tty_struct **ret_tty)
 {
-	struct tty_struct *tty, **tty_loc, *o_tty, **o_tty_loc;
+	struct tty_struct *tty, *o_tty;
 	struct termios *tp, **tp_loc, *o_tp, **o_tp_loc;
 	struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;
 	struct tty_driver *driver;	
@@ -849,163 +865,221 @@
 		return -ENODEV;
 
 	idx = MINOR(device) - driver->minor_start;
-	tty = o_tty = NULL;
+
+	/* 
+	 * Check whether we need to acquire the tty semaphore to avoid
+	 * race conditions.  For now, play it safe.
+	 */
+	down_tty_sem(idx);
+
+	/* check whether we're reopening an existing tty */
+	tty = driver->table[idx];
+	if(tty) goto fast_track;
+
+	/*
+	 * First time open is complex, especially for PTY devices.
+	 * This code guarantees that either everything succeeds and the
+	 * TTY is ready for operation, or else the table slots are vacated
+	 * and the allocated memory released.  (Except that the termios 
+	 * and locked termios may be retained.)
+	 */
+
+	o_tty = NULL;
 	tp = o_tp = NULL;
 	ltp = o_ltp = NULL;
-	o_tty_loc = NULL;
-	o_tp_loc = o_ltp_loc = NULL;
 
-	tty_loc = &driver->table[idx];
-	tp_loc = &driver->termios[idx];
-	ltp_loc = &driver->termios_locked[idx];
+	tty = (struct tty_struct*) get_free_page(GFP_KERNEL);
+	if(!tty)
+		goto fail_no_mem;
+	initialize_tty_struct(tty);
+	tty->device = device;
+	tty->driver = *driver;
 
-repeat:
-	retval = -EIO;
-	if (driver->type == TTY_DRIVER_TYPE_PTY &&
-	    driver->subtype == PTY_TYPE_MASTER &&
-	    *tty_loc && (*tty_loc)->count)
-		goto end_init;
-	retval = -ENOMEM;
-	if (!*tty_loc && !tty) {
-		if (!(tty = (struct tty_struct*) get_free_page(GFP_KERNEL)))
-			goto end_init;
-		initialize_tty_struct(tty);
-		tty->device = device;
-		tty->driver = *driver;
-		goto repeat;
-	}
-	if (!*tp_loc && !tp) {
+	tp_loc = &driver->termios[idx];
+	if (!*tp_loc) {
 		tp = (struct termios *) kmalloc(sizeof(struct termios),
 						GFP_KERNEL);
 		if (!tp)
-			goto end_init;
+			goto free_mem_out;
 		*tp = driver->init_termios;
-		goto repeat;
 	}
-	if (!*ltp_loc && !ltp) {
+
+	ltp_loc = &driver->termios_locked[idx];
+	if (!*ltp_loc) {
 		ltp = (struct termios *) kmalloc(sizeof(struct termios),
 						 GFP_KERNEL);
 		if (!ltp)
-			goto end_init;
+			goto free_mem_out;
 		memset(ltp, 0, sizeof(struct termios));
-		goto repeat;
 	}
+
 	if (driver->type == TTY_DRIVER_TYPE_PTY) {
-		o_tty_loc = &driver->other->table[idx];
-		o_tp_loc = &driver->other->termios[idx];
-		o_ltp_loc = &driver->other->termios_locked[idx];
+		o_tty = (struct tty_struct *) get_free_page(GFP_KERNEL);
+		if (!o_tty)
+			goto free_mem_out;
+		initialize_tty_struct(o_tty);
+		o_tty->device = (kdev_t) MKDEV(driver->other->major,
+					driver->other->minor_start + idx);
+		o_tty->driver = *driver->other;
 
-		if (!*o_tty_loc && !o_tty) {
-			kdev_t 	o_device;
-			
-			o_tty = (struct tty_struct *)
-				get_free_page(GFP_KERNEL);
-			if (!o_tty)
-				goto end_init;
-			o_device = MKDEV(driver->other->major,
-					 driver->other->minor_start + idx);
-			initialize_tty_struct(o_tty);
-			o_tty->device = o_device;
-			o_tty->driver = *driver->other;
-			goto repeat;
-		}
-		if (!*o_tp_loc && !o_tp) {
+		o_tp_loc  = &driver->other->termios[idx];
+		if (!*o_tp_loc) {
 			o_tp = (struct termios *)
 				kmalloc(sizeof(struct termios), GFP_KERNEL);
 			if (!o_tp)
-				goto end_init;
+				goto free_mem_out;
 			*o_tp = driver->other->init_termios;
-			goto repeat;
 		}
-		if (!*o_ltp_loc && !o_ltp) {
+
+		o_ltp_loc = &driver->other->termios_locked[idx];
+		if (!*o_ltp_loc) {
 			o_ltp = (struct termios *)
 				kmalloc(sizeof(struct termios), GFP_KERNEL);
 			if (!o_ltp)
-				goto end_init;
+				goto free_mem_out;
 			memset(o_ltp, 0, sizeof(struct termios));
-			goto repeat;
 		}
-		
+
+		/*
+		 * Everything allocated ... set up the o_tty structure.
+		 */
+		driver->other->table[idx] = o_tty;
+		if (!*o_tp_loc)
+			*o_tp_loc = o_tp;
+		if (!*o_ltp_loc)
+			*o_ltp_loc = o_ltp;
+		o_tty->termios = *o_tp_loc;
+		o_tty->termios_locked = *o_ltp_loc;
+		(*driver->other->refcount)++;
+		if (driver->subtype == PTY_TYPE_MASTER)
+			o_tty->count++;
+
+		/* Establish the links in both directions */
+		tty->link   = o_tty;
+		o_tty->link = tty;
 	}
-	/* Now we have allocated all the structures: update all the pointers.. */
-	if (!*tp_loc) {
+
+	/* 
+	 * All structures have been allocated, so now we install them.
+	 * 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;
+	if (!*tp_loc)
 		*tp_loc = tp;
-		tp = NULL;
-	}
-	if (!*ltp_loc) {
+	if (!*ltp_loc)
 		*ltp_loc = ltp;
-		ltp = NULL;
+	tty->termios = *tp_loc;
+	tty->termios_locked = *ltp_loc;
+	(*driver->refcount)++;
+	tty->count++;
+
+	/* 
+	 * Structures all installed ... call the ldisc open routines.
+	 * If we fail here just call release_mem to clean up.  No need
+	 * to decrement the use counts, as release_mem doesn't care.
+	 */
+	if (tty->ldisc.open) {
+		retval = (tty->ldisc.open)(tty);
+		if (retval)
+			goto release_mem_out;
 	}
-	if (!*tty_loc) {
-		tty->termios = *tp_loc;
-		tty->termios_locked = *ltp_loc;
-		*tty_loc = tty;
-		(*driver->refcount)++;
-		(*tty_loc)->count++;
-		if (tty->ldisc.open) {
-			retval = (tty->ldisc.open)(tty);
-			if (retval < 0) {
-				(*tty_loc)->count--;
-				tty = NULL;
-				goto end_init;
-			}
-		}
-		tty = NULL;
-	} else {
-		if ((*tty_loc)->flags & (1 << TTY_CLOSING)) {
-			printk("Attempt to open closing tty %s.\n",
-			       tty_name(*tty_loc));
-			printk("Ack!!!!  This should never happen!!\n");
-			return -EINVAL;
+	if (o_tty && o_tty->ldisc.open) {
+		retval = (o_tty->ldisc.open)(o_tty);
+		if (retval) {
+			if (tty->ldisc.close)
+				(tty->ldisc.close)(tty);
+			goto release_mem_out;
 		}
-		(*tty_loc)->count++;
 	}
-	if (driver->type == TTY_DRIVER_TYPE_PTY) {
-		if (!*o_tp_loc) {
-			*o_tp_loc = o_tp;
-			o_tp = NULL;
-		}
-		if (!*o_ltp_loc) {
-			*o_ltp_loc = o_ltp;
-			o_ltp = NULL;
-		}
-		if (!*o_tty_loc) {
-			o_tty->termios = *o_tp_loc;
-			o_tty->termios_locked = *o_ltp_loc;
-			*o_tty_loc = o_tty;
-			(*driver->other->refcount)++;
-			if (o_tty->ldisc.open) {
-				retval = (o_tty->ldisc.open)(o_tty);
-				if (retval < 0) {
-					(*tty_loc)->count--;
-					o_tty = NULL;
-					goto end_init;
-				}
-			}
-			o_tty = NULL;
-		}
-		(*tty_loc)->link = *o_tty_loc;
-		(*o_tty_loc)->link = *tty_loc;
-		if (driver->subtype == PTY_TYPE_MASTER)
-			(*o_tty_loc)->count++;
+	goto success;
+
+	/*
+	 * This fast open can be used if the tty is already open.
+	 * No memory is allocated, and the only failures are from
+	 * attempting to open a closing tty or attempting multiple
+	 * opens on a pty master.
+	 */
+fast_track:
+	retval = -EIO;
+	if (test_bit(TTY_CLOSING, &tty->flags))
+		goto end_init;
+
+	if (driver->type == TTY_DRIVER_TYPE_PTY &&
+	    driver->subtype == PTY_TYPE_MASTER) {
+		/*
+		 * special case for PTY masters: only one open permitted, 
+		 * and the slave side open count is incremented as well.
+		 */
+		if (tty->count)
+			goto end_init;
+		tty->link->count++;
 	}
-	(*tty_loc)->driver = *driver;
-	*ret_tty = *tty_loc;
+	tty->count++;
+	tty->driver = *driver; /* N.B. why do this every time?? */
+
+success:
 	retval = 0;
+	*ret_tty = tty;
+	
+	/* All paths come through here to release the semaphore */
 end_init:
-	if (tty)
-		free_page((unsigned long) tty);
-	if (o_tty)
-		free_page((unsigned long) o_tty);
-	if (tp)
-		kfree_s(tp, sizeof(struct termios));
+	up_tty_sem(idx);
+	return retval;
+
+	/* Release locally allocated memory ... nothing placed in slots */
+free_mem_out:
 	if (o_tp)
 		kfree_s(o_tp, sizeof(struct termios));
+	if (o_tty)
+		free_page((unsigned long) o_tty);
 	if (ltp)
 		kfree_s(ltp, sizeof(struct termios));
-	if (o_ltp)
-		kfree_s(o_ltp, sizeof(struct termios));
-	return retval;
+	if (tp)
+		kfree_s(tp, sizeof(struct termios));
+	free_page((unsigned long) tty);
+
+fail_no_mem:
+	retval = -ENOMEM;
+	goto end_init;
+
+	/* call the tty release_mem routine to clean out this slot */
+release_mem_out:
+	printk("init_dev: ldisc open failed, clearing slot %d\n", idx);
+	release_mem(tty, idx);
+	goto end_init;
+}
+
+/*
+ * Releases memory associated with a tty structure, and clears out the
+ * driver table slots.
+ */
+static void release_mem(struct tty_struct *tty, int idx)
+{
+	struct tty_struct *o_tty;
+	struct termios *tp;
+
+	if ((o_tty = tty->link) != NULL) {
+		o_tty->driver.table[idx] = NULL;
+		if (o_tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) {
+			tp = o_tty->driver.termios[idx];
+			o_tty->driver.termios[idx] = NULL;
+			kfree_s(tp, sizeof(struct termios));
+		}
+		o_tty->magic = 0;
+		(*o_tty->driver.refcount)--;
+		free_page((unsigned long) o_tty);
+	}
+
+	tty->driver.table[idx] = NULL;
+	if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) {
+		tp = tty->driver.termios[idx];
+		tty->driver.termios[idx] = NULL;
+		kfree_s(tp, sizeof(struct termios));
+	}
+	tty->magic = 0;
+	(*tty->driver.refcount)--;
+	free_page((unsigned long) tty);
 }
 
 /*
@@ -1016,8 +1090,7 @@
 static void release_dev(struct file * filp)
 {
 	struct tty_struct *tty, *o_tty;
-	struct termios *tp, *o_tp, *ltp, *o_ltp;
-	struct task_struct **p;
+	int	pty_master, tty_closing, o_tty_closing, do_sleep;
 	int	idx;
 	
 	tty = (struct tty_struct *)filp->private_data;
@@ -1028,10 +1101,11 @@
 
 	tty_fasync(filp->f_inode, filp, 0);
 
-	tp = tty->termios;
-	ltp = tty->termios_locked;
-
 	idx = MINOR(tty->device) - tty->driver.minor_start;
+	pty_master = (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
+		      tty->driver.subtype == PTY_TYPE_MASTER);
+	o_tty = tty->link;
+
 #ifdef TTY_PARANOIA_CHECK
 	if (idx < 0 || idx >= tty->driver.num) {
 		printk("release_dev: bad idx when trying to free (%s)\n",
@@ -1043,15 +1117,15 @@
 		       idx, kdevname(tty->device));
 		return;
 	}
-	if (tp != tty->driver.termios[idx]) {
-		printk("release_dev: driver.termios[%d] not termios for ("
-		       "%s)\n",
+	if (tty->termios != tty->driver.termios[idx]) {
+		printk("release_dev: driver.termios[%d] not termios "
+		       "for (%s)\n",
 		       idx, kdevname(tty->device));
 		return;
 	}
-	if (ltp != tty->driver.termios_locked[idx]) {
-		printk("release_dev: driver.termios_locked[%d] not termios_locked for ("
-		       "%s)\n",
+	if (tty->termios_locked != tty->driver.termios_locked[idx]) {
+		printk("release_dev: driver.termios_locked[%d] not "
+		       "termios_locked for (%s)\n",
 		       idx, kdevname(tty->device));
 		return;
 	}
@@ -1062,10 +1136,6 @@
 	       tty->count);
 #endif
 
-	o_tty = tty->link;
-	o_tp = (o_tty) ? o_tty->termios : NULL;
-	o_ltp = (o_tty) ? o_tty->termios_locked : NULL;
-
 #ifdef TTY_PARANOIA_CHECK
 	if (tty->driver.other) {
 		if (o_tty != tty->driver.other->table[idx]) {
@@ -1074,34 +1144,90 @@
 			       idx, kdevname(tty->device));
 			return;
 		}
-		if (o_tp != tty->driver.other->termios[idx]) {
-			printk("release_dev: other->termios[%d] not o_termios for ("
-			       "%s)\n",
+		if (o_tty->termios != tty->driver.other->termios[idx]) {
+			printk("release_dev: other->termios[%d] not o_termios "
+			       "for (%s)\n",
 			       idx, kdevname(tty->device));
 			return;
 		}
-		if (o_ltp != tty->driver.other->termios_locked[idx]) {
-			printk("release_dev: other->termios_locked[%d] not o_termios_locked for ("
-			       "%s)\n",
+		if (o_tty->termios_locked != 
+		      tty->driver.other->termios_locked[idx]) {
+			printk("release_dev: other->termios_locked[%d] not "
+			       "o_termios_locked for (%s)\n",
 			       idx, kdevname(tty->device));
 			return;
 		}
-
 		if (o_tty->link != tty) {
 			printk("release_dev: bad pty pointers\n");
 			return;
 		}
 	}
 #endif
-	
+	/*
+	 * Sanity check: if tty->count is going to zero, there shouldn't be
+	 * any waiters on tty->read_wait or tty->write_wait.  We test the
+	 * wait queues and kick everyone out _before_ actually starting to
+	 * close.  This ensures that we won't block while releasing the tty
+	 * structure.
+	 *
+	 * The test for the o_tty closing is necessary, since the master and
+	 * slave sides may close in any order.  If the slave side closes out
+	 * first, its count will be one, since the master side holds an open.
+	 * Thus this test wouldn't be triggered at the time the slave closes,
+	 * so we do it now.
+	 *
+	 * Note that it's possible for the tty to be opened again while we're
+	 * flushing out waiters.  By recalculating the closing flags before
+	 * each iteration we avoid any problems.
+	 */
+	while (1) {
+		tty_closing = tty->count <= 1;
+		o_tty_closing = o_tty &&
+			(o_tty->count <= (pty_master ? 1 : 0));
+		do_sleep = 0;
+
+		if (tty_closing) {
+			if (waitqueue_active(&tty->read_wait)) {
+				wake_up(&tty->read_wait);
+				do_sleep++;
+			}
+			if (waitqueue_active(&tty->write_wait)) {
+				wake_up(&tty->write_wait);
+				do_sleep++;
+			}
+		}
+		if (o_tty_closing) {
+			if (waitqueue_active(&o_tty->read_wait)) {
+				wake_up(&o_tty->read_wait);
+				do_sleep++;
+			}
+			if (waitqueue_active(&o_tty->write_wait)) {
+				wake_up(&o_tty->write_wait);
+				do_sleep++;
+			}
+		}
+		if (!do_sleep)
+			break;
+
+		printk("release_dev: %s: read/write wait queue active!\n",
+		       tty_name(tty));
+		schedule();
+	}	
+
+	/*
+	 * The closing flags are now consistent with the open counts on 
+	 * both sides, and we've completed the last operation that could 
+	 * block, so it's safe to proceed with closing.
+	 */
+
 	if (tty->driver.close)
 		tty->driver.close(tty, filp);
-	if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
-	    tty->driver.subtype == PTY_TYPE_MASTER) {
-		if (--tty->link->count < 0) {
+
+	if (pty_master) {
+		if (--o_tty->count < 0) {
 			printk("release_dev: bad pty slave count (%d) for %s\n",
-			       tty->count, tty_name(tty));
-			tty->link->count = 0;
+			       o_tty->count, tty_name(o_tty));
+			o_tty->count = 0;
 		}
 	}
 	if (--tty->count < 0) {
@@ -1109,41 +1235,48 @@
 		       tty->count, tty_name(tty));
 		tty->count = 0;
 	}
-	if (tty->count)
-		return;
 
 	/*
-	 * We're committed; at this point, we must not block!
+	 * Perform some housekeeping before deciding whether to return.
+	 *
+	 * Set the TTY_CLOSING flag if this was the last open.  In the
+	 * case of a pty we may have to wait around for the other side
+	 * to close, and TTY_CLOSING makes sure we can't be reopened.
 	 */
-	if (o_tty) {
-		if (o_tty->count)
-			return;
-		tty->driver.other->table[idx] = NULL;
-		tty->driver.other->termios[idx] = NULL;
-		kfree_s(o_tp, sizeof(struct termios));
+	if(tty_closing)
+		set_bit(TTY_CLOSING, &tty->flags);
+	if(o_tty_closing)
+		set_bit(TTY_CLOSING, &o_tty->flags);
+
+	/*
+	 * If _either_ side is closing, make sure there aren't any
+	 * processes that still think tty or o_tty is their controlling
+	 * tty.  Also, clear redirect if it points to either tty.
+	 */
+	if (tty_closing || o_tty_closing) {
+		struct task_struct *p;
+
+		for_each_task(p) {
+			if (p->tty == tty || (o_tty && p->tty == o_tty))
+				p->tty = NULL;
+		}
+
+		if (redirect == tty || (o_tty && redirect == o_tty))
+			redirect = NULL;
 	}
+
+	/* check whether both sides are closing ... */
+	if (!tty_closing || (o_tty && !o_tty_closing))
+		return;
+	filp->private_data = 0;
 	
 #ifdef TTY_DEBUG_HANGUP
 	printk("freeing tty structure...");
 #endif
-	tty->flags |= (1 << TTY_CLOSING);
-
-	/*
-	 * Make sure there aren't any processes that still think this
-	 * tty is their controlling tty.
-	 */
-	for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
-		if (*p == 0)
-			continue;
-		if ((*p)->tty == tty)
-			(*p)->tty = NULL;
-		if (o_tty && (*p)->tty == o_tty)
-			(*p)->tty = NULL;
-	}
 
 	/*
-	 * Shutdown the current line discipline, and reset it to
-	 * N_TTY.
+	 * Shutdown the current line discipline, and reset it to N_TTY.
+	 * N.B. why reset ldisc when we're releasing the memory??
 	 */
 	if (tty->ldisc.close)
 		(tty->ldisc.close)(tty);
@@ -1155,41 +1288,34 @@
 		o_tty->ldisc = ldiscs[N_TTY];
 	}
 	
-	tty->driver.table[idx] = NULL;
-	if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) {
-		tty->driver.termios[idx] = NULL;
-		kfree_s(tp, sizeof(struct termios));
-	}
-	if (tty == redirect || o_tty == redirect)
-		redirect = NULL;
 	/*
 	 * Make sure that the tty's task queue isn't activated.  If it
-	 * is, take it out of the linked list.
+	 * is, take it out of the linked list.  The tqueue isn't used by
+	 * pty's, so skip the test for them.
 	 */
-	cli();
-	if (tty->flip.tqueue.sync) {
-		struct tq_struct *tq, *prev;
+	if (tty->driver.type != TTY_DRIVER_TYPE_PTY) {
+		cli();
+		if (tty->flip.tqueue.sync) {
+			struct tq_struct *tq, *prev;
 
-		for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) {
-			if (tq == &tty->flip.tqueue) {
-				if (prev)
-					prev->next = tq->next;
-				else
-					tq_timer = tq->next;
-				break;
+			for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) {
+				if (tq == &tty->flip.tqueue) {
+					if (prev)
+						prev->next = tq->next;
+					else
+						tq_timer = tq->next;
+					break;
+				}
 			}
 		}
+		sti();
 	}
-	sti();
-	tty->magic = 0;
-	(*tty->driver.refcount)--;
-	free_page((unsigned long) tty);
-	filp->private_data = 0;
-	if (o_tty) {
-		o_tty->magic = 0;
-		(*o_tty->driver.refcount)--;
-		free_page((unsigned long) o_tty);
-	}
+
+	/* 
+	 * The release_mem function takes care of the details of clearing
+	 * the slots and preserving the termios structure.
+	 */
+	release_mem(tty, idx);
 }
 
 /*
@@ -1274,11 +1400,6 @@
 	return 0;
 }
 
-/*
- * Note that releasing a pty master also releases the child, so
- * we have to make the redirection checks after that and on both
- * sides of a pty.
- */
 static void tty_release(struct inode * inode, struct file * filp)
 {
 	release_dev(filp);
@@ -1930,6 +2051,9 @@
 #endif
 #ifdef CONFIG_BAYCOM
 	baycom_init();
+#endif
+#ifdef CONFIG_SPECIALIX
+	specialix_init();
 #endif
 	pty_init();
 	vcs_init();
diff -u --recursive --new-file v2.0.30/linux/drivers/char/tty_ioctl.c linux/drivers/char/tty_ioctl.c
--- v2.0.30/linux/drivers/char/tty_ioctl.c	Sun Sep 17 22:54:08 1995
+++ linux/drivers/char/tty_ioctl.c	Wed Aug 13 15:40:05 1997
@@ -376,7 +376,6 @@
 {
 	struct tty_struct * real_tty;
 	int retval;
-	int opt = 0;
 
 	if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
 	    tty->driver.subtype == PTY_TYPE_MASTER)
@@ -414,19 +413,19 @@
 				    sizeof (struct termios));
 			return 0;
 		case TCSETSF:
-			opt |= TERMIOS_FLUSH;
+			return set_termios(real_tty, arg, TERMIOS_FLUSH);
 		case TCSETSW:
-			opt |= TERMIOS_WAIT;
+			return set_termios(real_tty, arg, TERMIOS_WAIT);
 		case TCSETS:
-			return set_termios(real_tty, arg, opt);
+			return set_termios(real_tty, arg, 0);
 		case TCGETA:
 			return get_termio(real_tty,(struct termio *) arg);
 		case TCSETAF:
-			opt |= TERMIOS_FLUSH;
+			return set_termios(real_tty, arg, TERMIOS_FLUSH | TERMIOS_TERMIO);
 		case TCSETAW:
-			opt |= TERMIOS_WAIT;
+			return set_termios(real_tty, arg, TERMIOS_WAIT | TERMIOS_TERMIO);
 		case TCSETA:
-			return set_termios(real_tty, arg, opt|TERMIOS_TERMIO);
+			return set_termios(real_tty, arg, TERMIOS_TERMIO);
 		case TCXONC:
 			retval = tty_check_change(tty);
 			if (retval)
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/Config.in linux/drivers/isdn/Config.in
--- v2.0.30/linux/drivers/isdn/Config.in	Sun May 19 05:29:29 1996
+++ linux/drivers/isdn/Config.in	Mon Aug  4 17:33:59 1997
@@ -11,4 +11,21 @@
 bool 'Support audio via ISDN' CONFIG_ISDN_AUDIO
 dep_tristate 'ICN 2B and 4B support' CONFIG_ISDN_DRV_ICN $CONFIG_ISDN
 dep_tristate 'PCBIT-D support' CONFIG_ISDN_DRV_PCBIT $CONFIG_ISDN
-dep_tristate 'Teles/NICCY1016PC/Creatix support' CONFIG_ISDN_DRV_TELES $CONFIG_ISDN
+dep_tristate 'HiSax SiemensChipSet driver support' CONFIG_ISDN_DRV_HISAX $CONFIG_ISDN
+if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then
+    bool 'HiSax Support for EURO/DSS1' CONFIG_HISAX_EURO
+    bool 'HiSax Support for german 1TR6' CONFIG_HISAX_1TR6
+    bool 'HiSax Support for Teles 16.0/8.0' CONFIG_HISAX_16_0
+    bool 'HiSax Support for Teles 16.3 or PNP or PCMCIA' CONFIG_HISAX_16_3
+    bool 'HiSax Support for AVM A1 (Fritz)' CONFIG_HISAX_AVM_A1
+    bool 'HiSax Support for Elsa ISA cards' CONFIG_HISAX_ELSA_PCC
+    bool 'HiSax Support for Elsa PCMCIA card' CONFIG_HISAX_ELSA_PCMCIA
+    bool 'HiSax Support for ITK ix1-micro Revision 2' CONFIG_HISAX_IX1MICROR2
+fi
+if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
+	dep_tristate 'Spellcaster support (EXPERIMENTAL)' CONFIG_ISDN_DRV_SC $CONFIG_ISDN
+fi
+dep_tristate 'AVM-B1 with CAPI2.0 support' CONFIG_ISDN_DRV_AVMB1 $CONFIG_ISDN
+if [ "$CONFIG_ISDN_DRV_AVMB1" != "n" ]; then
+    bool 'Verbose reason code reporting (kernel size +=7K)' CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON
+fi
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/Makefile linux/drivers/isdn/Makefile
--- v2.0.30/linux/drivers/isdn/Makefile	Sun May 19 05:29:29 1996
+++ linux/drivers/isdn/Makefile	Mon Aug  4 17:33:59 1997
@@ -1,6 +1,6 @@
 SUB_DIRS     :=
 MOD_SUB_DIRS :=
-ALL_SUB_DIRS := icn teles pcbit
+ALL_SUB_DIRS := icn pcbit hisax avmb1
 
 L_OBJS :=
 LX_OBJS :=
@@ -13,8 +13,8 @@
 
 ifeq ($(CONFIG_ISDN),y)
   L_TARGET := isdn.a
-  L_OBJS += isdn_net.o isdn_tty.o isdn_cards.o
-  LX_OBJS += isdn_common.o
+  L_OBJS += isdn_common.o isdn_net.o isdn_tty.o isdn_cards.o
+  LX_OBJS += isdn_syms.o
   ifdef CONFIG_ISDN_PPP
     L_OBJS += isdn_ppp.o
   endif
@@ -25,8 +25,8 @@
   ifeq ($(CONFIG_ISDN),m)
     M_OBJS += isdn.o
     O_TARGET += isdn.o
-    O_OBJS += isdn_net.o isdn_tty.o
-    OX_OBJS += isdn_common.o
+    O_OBJS += isdn_common.o isdn_net.o isdn_tty.o
+    OX_OBJS += isdn_syms.o
     ifdef CONFIG_ISDN_PPP
       O_OBJS += isdn_ppp.o
     endif
@@ -36,13 +36,13 @@
   endif
 endif
 
-ifeq ($(CONFIG_ISDN_DRV_TELES),y)
-  L_OBJS += teles/teles.o
-  SUB_DIRS += teles
-  MOD_SUB_DIRS += teles
+ifeq ($(CONFIG_ISDN_DRV_HISAX),y)
+  L_OBJS += hisax/hisax.o
+  SUB_DIRS += hisax
+  MOD_SUB_DIRS += hisax
 else
-  ifeq ($(CONFIG_ISDN_DRV_TELES),m)
-    MOD_SUB_DIRS += teles
+  ifeq ($(CONFIG_ISDN_DRV_HISAX),m)
+    MOD_SUB_DIRS += hisax
   endif
 endif
 
@@ -63,6 +63,36 @@
 else
   ifeq ($(CONFIG_ISDN_DRV_PCBIT),m)
     MOD_SUB_DIRS += pcbit
+  endif
+endif
+
+ifeq ($(CONFIG_ISDN_DRV_SC),y)
+  L_OBJS += sc/sc.o
+  SUB_DIRS += sc
+  MOD_SUB_DIRS += sc
+else
+  ifeq ($(CONFIG_ISDN_DRV_SC),m)
+    MOD_SUB_DIRS += sc
+  endif
+endif
+
+ifeq ($(CONFIG_ISDN_DRV_AVMB1),y)
+  L_OBJS += avmb1/avmb1.o
+  SUB_DIRS += avmb1
+  MOD_SUB_DIRS += avmb1
+else
+  ifeq ($(CONFIG_ISDN_DRV_AVMB1),m)
+    MOD_SUB_DIRS += avmb1
+  endif
+endif
+
+ifeq ($(CONFIG_ISDN_DRV_LOOP),y)
+  L_OBJS += isdnloop/isdnloop.o
+  SUB_DIRS += isdnloop
+  MOD_SUB_DIRS += isdnloop
+else
+  ifeq ($(CONFIG_ISDN_DRV_LOOP),m)
+    MOD_SUB_DIRS += isdnloop
   endif
 endif
 
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/Makefile linux/drivers/isdn/avmb1/Makefile
--- v2.0.30/linux/drivers/isdn/avmb1/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/avmb1/Makefile	Mon Aug  4 17:33:59 1997
@@ -0,0 +1,77 @@
+#
+# $Id: Makefile,v 1.4 1997/03/30 17:10:40 calle Exp $
+#
+# Makefile for the CAPI and AVM-B1 device drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now inherited from the
+# parent makes..
+#
+# $Log: Makefile,v $
+# Revision 1.4  1997/03/30 17:10:40  calle
+# added support for AVM-B1-PCI card.
+#
+# Revision 1.3  1997/03/22 02:00:57  fritz
+# -Reworked toplevel Makefile. From now on, no different Makefiles
+#  for standalone- and in-kernel-compilation are needed any more.
+# -Added local Rules.make for above reason.
+# -Experimental changes in teles3.c for enhanced IRQ-checking with
+#  2.1.X and SMP kernels.
+# -Removed diffstd-script, same functionality is in stddiff -r.
+# -Enhanced scripts std2kern and stddiff.
+#
+# Revision 1.1  1997/03/05 21:26:14  fritz
+# Renamed, according naming conventions in CVS tree.
+#
+# Revision 1.1  1997/03/04 21:50:26  calle
+# Frirst version in isdn4linux
+#
+# Revision 2.2  1997/02/12 09:31:39  calle
+#
+# Revision 1.1  1997/01/31 10:32:20  calle
+# Initial revision
+#
+#
+
+#
+# Objects that don't export a symtab
+#
+L_OBJS := # used as component of an L_TARGET
+O_OBJS := # used as component of an O_TARGET
+M_OBJS := # used as module
+#
+# Objects that do export a symtab
+#
+LX_OBJS := # used as component of an L_TARGET
+OX_OBJS := # used as component of an O_TARGET
+MX_OBJS := # used as module
+#
+# Targets, created by linking others
+#
+O_TARGET := # used for .o targets (from O and OX objects)
+L_TARGET := # used for .a targets (from L and LX objects)
+
+ifeq ($(CONFIG_ISDN_DRV_AVMB1),y)
+  O_TARGET += avmb1.o
+  O_OBJS   += capi.o b1lli.o
+  OX_OBJS  += capiutil.o b1capi.o capidrv.o
+  ifdef CONFIG_PCI
+  OX_OBJS  += b1pci.o
+  endif
+else
+  ifeq ($(CONFIG_ISDN_DRV_AVMB1),m)
+  O_TARGET += kernelcapi.o
+  O_OBJS   += b1lli.o
+  OX_OBJS  += b1capi.o
+  M_OBJS   += capi.o kernelcapi.o
+  MX_OBJS  += capiutil.o capidrv.o
+  ifdef CONFIG_PCI
+  MX_OBJS  += b1pci.o
+  endif
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/b1capi.c linux/drivers/isdn/avmb1/b1capi.c
--- v2.0.30/linux/drivers/isdn/avmb1/b1capi.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/avmb1/b1capi.c	Thu Aug 21 15:14:34 1997
@@ -0,0 +1,982 @@
+/*
+ * $Id: b1capi.c,v 1.4.2.1 1997/07/12 08:18:59 calle Exp $
+ * 
+ * CAPI 2.0 Module for AVM B1-card.
+ * 
+ * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
+ * 
+ * $Log: b1capi.c,v $
+ * Revision 1.4.2.1  1997/07/12 08:18:59  calle
+ * Correct bug in CARD_NR macro, so now more than one card will work.
+ * Allow card reset, even if card is in running state.
+ *
+ * Revision 1.4  1997/05/27 15:17:45  fritz
+ * Added changes for recent 2.1.x kernels:
+ *   changed return type of isdn_close
+ *   queue_task_* -> queue_task
+ *   clear/set_bit -> test_and_... where apropriate.
+ *   changed type of hard_header_cache parameter.
+ *
+ * Revision 1.3  1997/05/18 09:24:09  calle
+ * added verbose disconnect reason reporting to avmb1.
+ * some fixes in capi20 interface.
+ * changed info messages for B1-PCI
+ *
+ * Revision 1.2  1997/03/05 21:20:41  fritz
+ * Removed include of config.h (mkdep stated this is unneded).
+ *
+ * Revision 1.1  1997/03/04 21:50:27  calle
+ * Frirst version in isdn4linux
+ *
+ * Revision 2.2  1997/02/12 09:31:39  calle
+ * new version
+ *
+ * Revision 1.1  1997/01/31 10:32:20  calle
+ * Initial revision
+ *
+ * 
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <asm/segment.h>
+#include <linux/skbuff.h>
+#include <linux/tqueue.h>
+#include <linux/capi.h>
+#include <linux/b1lli.h>
+#include <linux/kernelcapi.h>
+#include "compat.h"
+#include "capicmd.h"
+#include "capiutil.h"
+
+static char *revision = "$Revision: 1.4.2.1 $";
+
+/* ------------------------------------------------------------- */
+
+int portbase = 0x150;
+int irq = 15;
+int showcapimsgs = 0;		/* used in lli.c */
+int loaddebug = 0;
+
+#ifdef HAS_NEW_SYMTAB
+MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
+MODULE_PARM(portbase, "i");
+MODULE_PARM(irq, "2-15i");
+MODULE_PARM(showcapimsgs, "0-3i");
+MODULE_PARM(loaddebug, "0-1i");
+#endif
+
+/* ------------------------------------------------------------- */
+
+struct msgidqueue {
+	struct msgidqueue *next;
+	__u16 msgid;
+};
+
+typedef struct avmb1_ncci {
+	struct avmb1_ncci *next;
+	__u16 applid;
+	__u32 ncci;
+	__u32 winsize;
+	struct msgidqueue *msgidqueue;
+	struct msgidqueue *msgidlast;
+	struct msgidqueue *msgidfree;
+	struct msgidqueue msgidpool[CAPI_MAXDATAWINDOW];
+} avmb1_ncci;
+
+typedef struct avmb1_appl {
+	__u16 applid;
+	capi_register_params rparam;
+	int releasing;
+	__u32 param;
+	void (*signal) (__u16 applid, __u32 param);
+	struct sk_buff_head recv_queue;
+	struct avmb1_ncci *nccilist;
+} avmb1_appl;
+
+/* ------------------------------------------------------------- */
+
+static struct capi_version driver_version = {2, 0, 0, 9};
+static char driver_serial[CAPI_SERIAL_LEN] = "4711";
+static char capi_manufakturer[64] = "AVM Berlin";
+
+#define APPL(a)		   (&applications[(a)-1])
+#define	VALID_APPLID(a)	   ((a) && (a) <= CAPI_MAXAPPL && APPL(a)->applid == a)
+#define APPL_IS_FREE(a)    (APPL(a)->applid == 0)
+#define APPL_MARK_FREE(a)  do{ APPL(a)->applid=0; MOD_DEC_USE_COUNT; }while(0);
+#define APPL_MARK_USED(a)  do{ APPL(a)->applid=(a); MOD_INC_USE_COUNT; }while(0);
+
+#define NCCI2CTRL(ncci)    (((ncci) >> 24) & 0x7f)
+
+#define VALID_CARD(c)	   ((c) > 0 && (c) <= ncards)
+#define CARD(c)		   (&cards[(c)-1])
+#define CARDNR(cp)	   (((cp)-cards)+1)
+
+static avmb1_appl applications[CAPI_MAXAPPL];
+static avmb1_card cards[CAPI_MAXCONTR];
+static int ncards = 0;
+static struct sk_buff_head recv_queue;
+static struct capi_interface_user *capi_users = 0;
+static long notify_up_set = 0;
+static long notify_down_set = 0;
+
+static struct tq_struct tq_state_notify;
+static struct tq_struct tq_recv_notify;
+
+/* -------- util functions ------------------------------------ */
+
+static inline int capi_cmd_valid(__u8 cmd)
+{
+	switch (cmd) {
+	case CAPI_ALERT:
+	case CAPI_CONNECT:
+	case CAPI_CONNECT_ACTIVE:
+	case CAPI_CONNECT_B3_ACTIVE:
+	case CAPI_CONNECT_B3:
+	case CAPI_CONNECT_B3_T90_ACTIVE:
+	case CAPI_DATA_B3:
+	case CAPI_DISCONNECT_B3:
+	case CAPI_DISCONNECT:
+	case CAPI_FACILITY:
+	case CAPI_INFO:
+	case CAPI_LISTEN:
+	case CAPI_MANUFACTURER:
+	case CAPI_RESET_B3:
+	case CAPI_SELECT_B_PROTOCOL:
+		return 1;
+	}
+	return 0;
+}
+
+static inline int capi_subcmd_valid(__u8 subcmd)
+{
+	switch (subcmd) {
+	case CAPI_REQ:
+	case CAPI_CONF:
+	case CAPI_IND:
+	case CAPI_RESP:
+		return 1;
+	}
+	return 0;
+}
+
+/* -------- NCCI Handling ------------------------------------- */
+
+static inline void mq_init(avmb1_ncci * np)
+{
+	int i;
+	np->msgidqueue = 0;
+	np->msgidlast = 0;
+	memset(np->msgidpool, 0, sizeof(np->msgidpool));
+	np->msgidfree = &np->msgidpool[0];
+	for (i = 1; i < np->winsize; i++) {
+		np->msgidpool[i].next = np->msgidfree;
+		np->msgidfree = &np->msgidpool[i];
+	}
+}
+
+static inline int mq_enqueue(avmb1_ncci * np, __u16 msgid)
+{
+	struct msgidqueue *mq;
+	if ((mq = np->msgidfree) == 0)
+		return 0;
+	np->msgidfree = mq->next;
+	mq->msgid = msgid;
+	mq->next = 0;
+	if (np->msgidlast)
+		np->msgidlast->next = mq;
+	np->msgidlast = mq;
+	if (!np->msgidqueue)
+		np->msgidqueue = mq;
+	return 1;
+}
+
+static inline int mq_dequeue(avmb1_ncci * np, __u16 msgid)
+{
+	struct msgidqueue **pp;
+	for (pp = &np->msgidqueue; *pp; pp = &(*pp)->next) {
+		if ((*pp)->msgid == msgid) {
+			struct msgidqueue *mq = *pp;
+			*pp = mq->next;
+			if (mq == np->msgidlast)
+				np->msgidlast = 0;
+			mq->next = np->msgidfree;
+			np->msgidfree = mq;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+void avmb1_handle_new_ncci(avmb1_card * card,
+			   __u16 appl, __u32 ncci, __u32 winsize)
+{
+	avmb1_ncci *np;
+	if (!VALID_APPLID(appl)) {
+		printk(KERN_ERR "avmb1_handle_new_ncci: illegal appl %d\n", appl);
+		return;
+	}
+	if ((np = (avmb1_ncci *) kmalloc(sizeof(avmb1_ncci), GFP_ATOMIC)) == 0) {
+		printk(KERN_ERR "avmb1_handle_new_ncci: alloc failed ncci 0x%x\n", ncci);
+		return;
+	}
+	if (winsize > CAPI_MAXDATAWINDOW) {
+		printk(KERN_ERR "avmb1_handle_new_ncci: winsize %d too big, set to %d\n",
+		       winsize, CAPI_MAXDATAWINDOW);
+		winsize = CAPI_MAXDATAWINDOW;
+	}
+	np->applid = appl;
+	np->ncci = ncci;
+	np->winsize = winsize;
+	mq_init(np);
+	np->next = APPL(appl)->nccilist;
+	APPL(appl)->nccilist = np;
+	printk(KERN_INFO "b1capi: appl %d ncci 0x%x up\n", appl, ncci);
+
+}
+
+void avmb1_handle_free_ncci(avmb1_card * card,
+			    __u16 appl, __u32 ncci)
+{
+	if (!VALID_APPLID(appl)) {
+		printk(KERN_ERR "avmb1_handle_free_ncci: illegal appl %d\n", appl);
+		return;
+	}
+	if (ncci != 0xffffffff) {
+		avmb1_ncci **pp;
+		for (pp = &APPL(appl)->nccilist; *pp; pp = &(*pp)->next) {
+			if ((*pp)->ncci == ncci) {
+				avmb1_ncci *np = *pp;
+				*pp = np->next;
+				kfree(np);
+				printk(KERN_INFO "b1capi: appl %d ncci 0x%x down\n", appl, ncci);
+				return;
+			}
+		}
+		printk(KERN_ERR "avmb1_handle_free_ncci: ncci 0x%x not found\n", ncci);
+	} else {
+		avmb1_ncci **pp, **nextpp;
+		for (pp = &APPL(appl)->nccilist; *pp; pp = nextpp) {
+			if (NCCI2CTRL((*pp)->ncci) == card->cnr) {
+				avmb1_ncci *np = *pp;
+				*pp = np->next;
+				printk(KERN_INFO "b1capi: appl %d ncci 0x%x down!\n", appl, np->ncci);
+				kfree(np);
+				nextpp = pp;
+			} else {
+				nextpp = &(*pp)->next;
+			}
+		}
+		APPL(appl)->releasing--;
+		if (APPL(appl)->releasing == 0) {
+	                APPL(appl)->signal = 0;
+			APPL_MARK_FREE(appl);
+			printk(KERN_INFO "b1capi: appl %d down\n", appl);
+		}
+	}
+}
+
+static avmb1_ncci *find_ncci(avmb1_appl * app, __u32 ncci)
+{
+	avmb1_ncci *np;
+	for (np = app->nccilist; np; np = np->next) {
+		if (np->ncci == ncci)
+			return np;
+	}
+	return 0;
+}
+
+
+
+/* -------- Receiver ------------------------------------------ */
+
+
+static void recv_handler(void *dummy)
+{
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(&recv_queue)) != 0) {
+		__u16 appl = CAPIMSG_APPID(skb->data);
+		struct avmb1_ncci *np;
+		if (!VALID_APPLID(appl)) {
+			printk(KERN_ERR "b1capi: recv_handler: applid %d ? (%s)\n",
+			       appl, capi_message2str(skb->data));
+			kfree_skb(skb, FREE_READ);
+			continue;
+		}
+		if (APPL(appl)->signal == 0) {
+			printk(KERN_ERR "b1capi: recv_handler: applid %d has no signal function\n",
+			       appl);
+			kfree_skb(skb, FREE_READ);
+			continue;
+		}
+		if (   CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3
+		    && CAPIMSG_SUBCOMMAND(skb->data) == CAPI_CONF
+	            && (np = find_ncci(APPL(appl), CAPIMSG_NCCI(skb->data))) != 0
+		    && mq_dequeue(np, CAPIMSG_MSGID(skb->data)) == 0) {
+			printk(KERN_ERR "b1capi: msgid %hu ncci 0x%x not on queue\n",
+				CAPIMSG_MSGID(skb->data), np->ncci);
+		}
+		skb_queue_tail(&APPL(appl)->recv_queue, skb);
+		(APPL(appl)->signal) (APPL(appl)->applid, APPL(appl)->param);
+	}
+}
+
+
+void avmb1_handle_capimsg(avmb1_card * card, __u16 appl, struct sk_buff *skb)
+{
+	if (card->cardstate != CARD_RUNNING) {
+		printk(KERN_INFO "b1capi: controller %d not active, got: %s",
+		       card->cnr, capi_message2str(skb->data));
+		goto error;
+		return;
+	}
+	skb_queue_tail(&recv_queue, skb);
+	queue_task(&tq_recv_notify, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+	return;
+
+      error:
+	kfree_skb(skb, FREE_READ);
+}
+
+void avmb1_interrupt(int interrupt, void *devptr, struct pt_regs *regs)
+{
+	avmb1_card *card;
+
+	card = (avmb1_card *) devptr;
+
+	if (!card) {
+		printk(KERN_WARNING "avmb1_interrupt: wrong device\n");
+		return;
+	}
+	if (card->interrupt) {
+		printk(KERN_ERR "avmb1_interrupt: reentering interrupt hander\n");
+		return;
+	}
+
+	card->interrupt = 1;
+
+	B1_handle_interrupt(card);
+
+	card->interrupt = 0;
+}
+
+/* -------- Notifier ------------------------------------------ */
+
+static void notify_up(__u16 contr)
+{
+	struct capi_interface_user *p;
+
+        printk(KERN_NOTICE "b1capi: notify up contr %d\n", contr);
+	for (p = capi_users; p; p = p->next) {
+		if (p->callback)
+			(*p->callback) (KCI_CONTRUP, contr,
+				(capi_profile *)
+					CARD(contr)->version[VER_PROFILE]);
+	}
+}
+
+static void notify_down(__u16 contr)
+{
+	struct capi_interface_user *p;
+        printk(KERN_NOTICE "b1capi: notify down contr %d\n", contr);
+	for (p = capi_users; p; p = p->next) {
+		if (p->callback)
+			(*p->callback) (KCI_CONTRDOWN, contr, 0);
+	}
+}
+
+static void notify_handler(void *dummy)
+{
+	__u16 contr;
+
+	for (contr=1; VALID_CARD(contr); contr++)
+		 if (test_and_clear_bit(contr, &notify_up_set))
+			 notify_up(contr);
+	for (contr=1; VALID_CARD(contr); contr++)
+		 if (test_and_clear_bit(contr, &notify_down_set))
+			 notify_down(contr);
+}
+
+/* -------- card ready callback ------------------------------- */
+
+void avmb1_card_ready(avmb1_card * card)
+{
+	__u16 appl;
+
+	card->cversion.majorversion = 2;
+	card->cversion.minorversion = 0;
+	card->cversion.majormanuversion = (card->version[VER_DRIVER][0] - '0') << 4;
+	card->cversion.majormanuversion |= (card->version[VER_DRIVER][2] - '0');
+	card->cversion.minormanuversion = (card->version[VER_DRIVER][3] - '0') << 4;
+	card->cversion.minormanuversion |= (card->version[VER_DRIVER][5] - '0') * 10;
+	card->cversion.minormanuversion |= (card->version[VER_DRIVER][6] - '0');
+	card->cardstate = CARD_RUNNING;
+
+
+	for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
+		if (VALID_APPLID(appl) && !APPL(appl)->releasing) {
+			B1_send_register(card->port, appl,
+				1024 * (APPL(appl)->rparam.level3cnt+1),
+				APPL(appl)->rparam.level3cnt,
+				APPL(appl)->rparam.datablkcnt,
+				APPL(appl)->rparam.datablklen);
+		}
+	}
+
+        set_bit(CARDNR(card), &notify_up_set);
+        queue_task(&tq_state_notify, &tq_scheduler);
+        printk(KERN_NOTICE "b1capi: card %d ready.\n", CARDNR(card));
+}
+
+static void avmb1_card_down(avmb1_card * card)
+{
+	__u16 appl;
+
+        card->cardstate = CARD_DETECTED;
+
+	for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
+		avmb1_ncci **pp, **nextpp;
+		for (pp = &APPL(appl)->nccilist; *pp; pp = nextpp) {
+			if (NCCI2CTRL((*pp)->ncci) == card->cnr) {
+				avmb1_ncci *np = *pp;
+				*pp = np->next;
+				printk(KERN_INFO "b1capi: appl %d ncci 0x%x forced down!\n", appl, np->ncci);
+				kfree(np);
+				nextpp = pp;
+			} else {
+				nextpp = &(*pp)->next;
+			}
+		}
+	}
+	set_bit(CARDNR(card), &notify_down_set);
+	queue_task(&tq_state_notify, &tq_scheduler);
+	printk(KERN_NOTICE "b1capi: card %d down.\n", CARDNR(card));
+}
+
+/* ------------------------------------------------------------- */
+
+int avmb1_addcard(int port, int irq)
+{
+	struct avmb1_card *card;
+	int irqval;
+
+
+	card = &cards[ncards];
+	memset(card, 0, sizeof(avmb1_card));
+	sprintf(card->name, "avmb1-%d", ncards + 1);
+
+	request_region(port, AVMB1_PORTLEN, card->name);
+
+	if ((irqval = request_irq(irq, avmb1_interrupt, SA_SHIRQ, card->name, card)) != 0) {
+		printk(KERN_ERR "b1capi: unable to get IRQ %d (irqval=%d).\n",
+		       irq, irqval);
+		release_region((unsigned short) port, AVMB1_PORTLEN);
+		return -EIO;
+	}
+	ncards++;
+	card->cnr = ncards;
+	card->port = port;
+	card->irq = irq;
+	card->cardstate = CARD_DETECTED;
+	return 0;
+}
+
+int avmb1_probecard(int port, int irq)
+{
+	int rc;
+
+	if (check_region((unsigned short) port, AVMB1_PORTLEN)) {
+		printk(KERN_WARNING
+		       "b1capi: ports 0x%03x-0x%03x in use.\n",
+		       portbase, portbase + AVMB1_PORTLEN);
+		return -EIO;
+	}
+	if (!B1_valid_irq(irq)) {
+		printk(KERN_WARNING "b1capi: irq %d not valid.\n", irq);
+		return -EIO;
+	}
+	if ((rc = B1_detect(port)) != 0) {
+		printk(KERN_NOTICE "b1capi: NO card at 0x%x (%d)\n", port, rc);
+		return -EIO;
+	}
+	B1_reset(port);
+	printk(KERN_NOTICE "b1capi: AVM-B1-Controller detected at 0x%x\n", port);
+
+	return 0;
+}
+
+/* ------------------------------------------------------------- */
+/* -------- CAPI2.0 Interface ---------------------------------- */
+/* ------------------------------------------------------------- */
+
+static int capi_installed(void)
+{
+	int i;
+	for (i = 0; i < ncards; i++) {
+		if (cards[i].cardstate == CARD_RUNNING)
+			return 1;
+	}
+	return 0;
+}
+
+static __u16 capi_register(capi_register_params * rparam, __u16 * applidp)
+{
+	int i;
+	int appl;
+
+	if (rparam->datablklen < 128)
+		return CAPI_LOGBLKSIZETOSMALL;
+
+	for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
+		if (APPL_IS_FREE(appl))
+			break;
+	}
+	if (appl > CAPI_MAXAPPL)
+		return CAPI_TOOMANYAPPLS;
+
+	APPL_MARK_USED(appl);
+	skb_queue_head_init(&APPL(appl)->recv_queue);
+
+	memcpy(&APPL(appl)->rparam, rparam, sizeof(capi_register_params));
+
+	for (i = 0; i < ncards; i++) {
+		if (cards[i].cardstate != CARD_RUNNING)
+			continue;
+		B1_send_register(cards[i].port, appl,
+			       1024 * (APPL(appl)->rparam.level3cnt + 1),
+				 APPL(appl)->rparam.level3cnt,
+				 APPL(appl)->rparam.datablkcnt,
+				 APPL(appl)->rparam.datablklen);
+	}
+	*applidp = appl;
+	printk(KERN_INFO "b1capi: appl %d up\n", appl);
+
+	return CAPI_NOERROR;
+}
+
+static __u16 capi_release(__u16 applid)
+{
+	struct sk_buff *skb;
+	int i;
+
+	if (ncards == 0)
+		return CAPI_REGNOTINSTALLED;
+	if (!VALID_APPLID(applid) || APPL(applid)->releasing)
+		return CAPI_ILLAPPNR;
+	while ((skb = skb_dequeue(&APPL(applid)->recv_queue)) != 0)
+		kfree_skb(skb, FREE_READ);
+	for (i = 0; i < ncards; i++) {
+		if (cards[i].cardstate != CARD_RUNNING) {
+			continue;
+		}
+		APPL(applid)->releasing++;
+		B1_send_release(cards[i].port, applid);
+	}
+	if (APPL(applid)->releasing == 0) {
+	        APPL(applid)->signal = 0;
+		APPL_MARK_FREE(applid);
+		printk(KERN_INFO "b1capi: appl %d down\n", applid);
+	}
+	return CAPI_NOERROR;
+}
+
+static __u16 capi_put_message(__u16 applid, struct sk_buff *skb)
+{
+	avmb1_ncci *np;
+	int contr;
+	if (ncards == 0)
+		return CAPI_REGNOTINSTALLED;
+	if (!VALID_APPLID(applid))
+		return CAPI_ILLAPPNR;
+	if (skb->len < 12
+	    || !capi_cmd_valid(CAPIMSG_COMMAND(skb->data))
+	    || !capi_subcmd_valid(CAPIMSG_SUBCOMMAND(skb->data)))
+		return CAPI_ILLCMDORSUBCMDORMSGTOSMALL;
+	contr = CAPIMSG_CONTROLLER(skb->data);
+	if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) {
+		contr = 1;
+	        if (CARD(contr)->cardstate != CARD_RUNNING) 
+			return CAPI_REGNOTINSTALLED;
+	}
+	if (CARD(contr)->blocked)
+		return CAPI_SENDQUEUEFULL;
+
+	if (   CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3
+	    && CAPIMSG_SUBCOMMAND(skb->data) == CAPI_REQ
+	    && (np = find_ncci(APPL(applid), CAPIMSG_NCCI(skb->data))) != 0
+	    && mq_enqueue(np, CAPIMSG_MSGID(skb->data)) == 0)
+		return CAPI_SENDQUEUEFULL;
+
+	B1_send_message(CARD(contr)->port, skb);
+	return CAPI_NOERROR;
+}
+
+static __u16 capi_get_message(__u16 applid, struct sk_buff **msgp)
+{
+	struct sk_buff *skb;
+
+	if (!VALID_APPLID(applid))
+		return CAPI_ILLAPPNR;
+	if ((skb = skb_dequeue(&APPL(applid)->recv_queue)) == 0)
+		return CAPI_RECEIVEQUEUEEMPTY;
+	*msgp = skb;
+	return CAPI_NOERROR;
+}
+
+static __u16 capi_set_signal(__u16 applid,
+			     void (*signal) (__u16 applid, __u32 param),
+			     __u32 param)
+{
+	if (!VALID_APPLID(applid))
+		return CAPI_ILLAPPNR;
+	APPL(applid)->signal = signal;
+	APPL(applid)->param = param;
+	return CAPI_NOERROR;
+}
+
+static __u16 capi_get_manufacturer(__u16 contr, __u8 buf[CAPI_MANUFACTURER_LEN])
+{
+	if (contr == 0) {
+		strncpy(buf, capi_manufakturer, CAPI_MANUFACTURER_LEN);
+		return CAPI_NOERROR;
+	}
+	if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) 
+		return 0x2002;
+
+	strncpy(buf, capi_manufakturer, CAPI_MANUFACTURER_LEN);
+	return CAPI_NOERROR;
+}
+
+static __u16 capi_get_version(__u16 contr, struct capi_version *verp)
+{
+	if (contr == 0) {
+		*verp = driver_version;
+		return CAPI_NOERROR;
+	}
+	if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) 
+		return 0x2002;
+
+	memcpy((void *) verp, CARD(contr)->version[VER_SERIAL],
+	       sizeof(capi_version));
+	return CAPI_NOERROR;
+}
+
+static __u16 capi_get_serial(__u16 contr, __u8 serial[CAPI_SERIAL_LEN])
+{
+	if (contr == 0) {
+		strncpy(serial, driver_serial, 8);
+		return CAPI_NOERROR;
+	}
+	if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) 
+		return 0x2002;
+
+	memcpy((void *) serial, CARD(contr)->version[VER_SERIAL],
+	       CAPI_SERIAL_LEN);
+	serial[CAPI_SERIAL_LEN - 1] = 0;
+	return CAPI_NOERROR;
+}
+
+static __u16 capi_get_profile(__u16 contr, struct capi_profile *profp)
+{
+	if (contr == 0) {
+		profp->ncontroller = ncards;
+		return CAPI_NOERROR;
+	}
+	if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) 
+		return 0x2002;
+
+	memcpy((void *) profp, CARD(contr)->version[VER_PROFILE],
+	       sizeof(struct capi_profile));
+	return CAPI_NOERROR;
+}
+
+static int capi_manufacturer(unsigned int cmd, void *data)
+{
+	unsigned long flags;
+	avmb1_loaddef ldef;
+	avmb1_carddef cdef;
+	avmb1_resetdef rdef;
+	avmb1_card *card;
+	int rc;
+
+	switch (cmd) {
+	case AVMB1_ADDCARD:
+		if ((rc = copy_from_user((void *) &cdef, data,
+					 sizeof(avmb1_carddef))))
+			return rc;
+		if (!B1_valid_irq(cdef.irq))
+			return -EINVAL;
+
+		if ((rc = avmb1_probecard(cdef.port, cdef.irq)) != 0)
+			return rc;
+
+		return avmb1_addcard(cdef.port, cdef.irq);
+
+	case AVMB1_LOAD:
+
+		if ((rc = copy_from_user((void *) &ldef, data,
+					 sizeof(avmb1_loaddef))))
+			return rc;
+		if (!VALID_CARD(ldef.contr) || ldef.t4file.len <= 0) {
+			if (loaddebug)
+				printk(KERN_DEBUG "b1capi: load: invalid parameter contr=%d len=%d\n", ldef.contr, ldef.t4file.len);
+			return -EINVAL;
+		}
+
+		card = CARD(ldef.contr);
+		save_flags(flags);
+		cli();
+		if (card->cardstate != CARD_DETECTED) {
+			restore_flags(flags);
+			if (loaddebug)
+				printk(KERN_DEBUG "b1capi: load: contr=%d not in detect state\n", ldef.contr);
+			return -EBUSY;
+		}
+		card->cardstate = CARD_LOADING;
+		restore_flags(flags);
+
+		if (loaddebug) {
+			printk(KERN_DEBUG "b1capi: load: reseting contr %d\n",
+				ldef.contr);
+		}
+
+		B1_reset(card->port);
+		if ((rc = B1_load_t4file(card->port, &ldef.t4file))) {
+			B1_reset(card->port);
+			printk(KERN_ERR "b1capi: failed to load t4file!!\n");
+			card->cardstate = CARD_DETECTED;
+			return rc;
+		}
+		B1_disable_irq(card->port);
+		if (loaddebug) {
+			printk(KERN_DEBUG "b1capi: load: ready contr %d: checking\n",
+				ldef.contr);
+		}
+
+		if (!B1_loaded(card->port)) {
+			card->cardstate = CARD_DETECTED;
+			printk(KERN_ERR "b1capi: failed to load t4file.\n");
+			return -EIO;
+		}
+		/*
+		 * enable interrupt
+		 */
+
+		card->cardstate = CARD_INITSTATE;
+		save_flags(flags);
+		cli();
+		B1_assign_irq(card->port, card->irq);
+		B1_enable_irq(card->port);
+		restore_flags(flags);
+
+		if (loaddebug) {
+			printk(KERN_DEBUG "b1capi: load: irq enabled contr %d\n",
+				ldef.contr);
+		}
+
+		/*
+		 * init card
+		 */
+		B1_send_init(card->port, AVM_NAPPS, AVM_NNCCI, card->cnr - 1);
+
+		if (loaddebug) {
+			printk(KERN_DEBUG "b1capi: load: waiting for init reply contr %d\n",
+				ldef.contr);
+		}
+
+		while (card->cardstate != CARD_RUNNING) {
+
+			current->timeout = jiffies + HZ / 10;	/* 0.1 sec */
+			current->state = TASK_INTERRUPTIBLE;
+			schedule();
+
+			if (current->signal & ~current->blocked)
+				return -EINTR;
+		}
+		return 0;
+	case AVMB1_RESETCARD:
+		if ((rc = copy_from_user((void *) &rdef, data,
+					 sizeof(avmb1_resetdef))))
+			return rc;
+
+		if (!VALID_CARD(rdef.contr))
+			return -EINVAL;
+
+		card = CARD(rdef.contr);
+
+		if (card->cardstate == CARD_RUNNING)
+			avmb1_card_down(card);
+
+		card->cardstate = CARD_DETECTED;
+
+		B1_reset(card->port);
+		B1_reset(card->port);
+
+		return 0;
+	}
+	return -EINVAL;
+}
+
+struct capi_interface avmb1_interface =
+{
+	capi_installed,
+	capi_register,
+	capi_release,
+	capi_put_message,
+	capi_get_message,
+	capi_set_signal,
+	capi_get_manufacturer,
+	capi_get_version,
+	capi_get_serial,
+	capi_get_profile,
+	capi_manufacturer
+};
+
+/* ------------------------------------------------------------- */
+/* -------- Exported Functions --------------------------------- */
+/* ------------------------------------------------------------- */
+
+struct capi_interface *attach_capi_interface(struct capi_interface_user *userp)
+{
+	struct capi_interface_user *p;
+
+	for (p = capi_users; p; p = p->next) {
+		if (p == userp) {
+			printk(KERN_ERR "b1capi: double attach from %s\n",
+			       userp->name);
+			return 0;
+		}
+	}
+	userp->next = capi_users;
+	capi_users = userp;
+	MOD_INC_USE_COUNT;
+
+	return &avmb1_interface;
+}
+
+int detach_capi_interface(struct capi_interface_user *userp)
+{
+	struct capi_interface_user **pp;
+
+	for (pp = &capi_users; *pp; pp = &(*pp)->next) {
+		if (*pp == userp) {
+			*pp = userp->next;
+			userp->next = 0;
+			MOD_DEC_USE_COUNT;
+			return 0;
+		}
+	}
+	printk(KERN_ERR "b1capi: double detach from %s\n", userp->name);
+	return -1;
+}
+
+/* ------------------------------------------------------------- */
+/* -------- Init & Cleanup ------------------------------------- */
+/* ------------------------------------------------------------- */
+
+#ifdef HAS_NEW_SYMTAB
+EXPORT_SYMBOL(attach_capi_interface);
+EXPORT_SYMBOL(detach_capi_interface);
+EXPORT_SYMBOL(avmb1_addcard);
+EXPORT_SYMBOL(avmb1_probecard);
+#else
+static struct symbol_table capidev_syms =
+{
+#include <linux/symtab_begin.h>
+	X(attach_capi_interface),
+	X(detach_capi_interface),
+	X(avmb1_addcard),
+	X(avmb1_probecard),
+#include <linux/symtab_end.h>
+};
+#endif
+
+
+/*
+ * init / exit functions
+ */
+
+#ifdef MODULE
+#define avmb1_init init_module
+#endif
+
+int avmb1_init(void)
+{
+	char *p;
+	char rev[10];
+
+
+#ifndef HAS_NEW_SYMTAB
+	/* No symbols to export, hide all symbols */
+	register_symtab(&capidev_syms);
+#endif
+	skb_queue_head_init(&recv_queue);
+	/* init_bh(CAPI_BH, do_capi_bh); */
+
+	tq_state_notify.routine = notify_handler;
+	tq_state_notify.data = 0;
+
+	tq_recv_notify.routine = recv_handler;
+	tq_recv_notify.data = 0;
+
+
+	if ((p = strchr(revision, ':'))) {
+		strcpy(rev, p + 1);
+		p = strchr(rev, '$');
+		*p = 0;
+	} else
+		strcpy(rev, " ??? ");
+
+#ifdef MODULE
+	if (portbase) {
+	        int rc;
+		if ((rc = avmb1_probecard(portbase, irq)) != 0)
+			return rc;
+		if ((rc = avmb1_addcard(portbase, irq)) != 0)
+			return rc;
+	} else {
+		printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: loaded\n", rev);
+	}
+#else
+	printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: started\n", rev);
+#endif
+	return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	char rev[10];
+	char *p;
+	int i;
+
+	if ((p = strchr(revision, ':'))) {
+		strcpy(rev, p + 1);
+		p = strchr(rev, '$');
+		*p = 0;
+	} else {
+		strcpy(rev, " ??? ");
+	}
+
+	for (i = 0; i < ncards; i++) {
+		/*
+		 * disable card
+		 */
+		B1_disable_irq(cards[i].port);
+		B1_reset(cards[i].port);
+		B1_reset(cards[i].port);
+		/*
+		 * free kernel resources
+		 */
+		free_irq(cards[i].irq, &cards[i]);
+		release_region(cards[i].port, AVMB1_PORTLEN);
+
+	}
+	printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: unloaded\n", rev);
+}
+#endif
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/b1lli.c linux/drivers/isdn/avmb1/b1lli.c
--- v2.0.30/linux/drivers/isdn/avmb1/b1lli.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/avmb1/b1lli.c	Thu Aug 21 15:14:34 1997
@@ -0,0 +1,601 @@
+/*
+ * $Id: b1lli.c,v 1.1.2.1 1997/07/13 12:16:46 calle Exp $
+ * 
+ * ISDN lowlevel-module for AVM B1-card.
+ * 
+ * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
+ * 
+ * $Log: b1lli.c,v $
+ * Revision 1.1.2.1  1997/07/13 12:16:46  calle
+ * bug fix for more than one controller in connect_req.
+ *
+ * Revision 1.1  1997/03/04 21:50:28  calle
+ * Frirst version in isdn4linux
+ *
+ * Revision 2.2  1997/02/12 09:31:39  calle
+ * new version
+ *
+ * Revision 1.1  1997/01/31 10:32:20  calle
+ * Initial revision
+ *
+ * 
+ */
+
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <linux/capi.h>
+#include <linux/b1lli.h>
+
+#include "compat.h"
+#include "capicmd.h"
+#include "capiutil.h"
+
+/*
+ * LLI Messages to the ISDN-ControllerISDN Controller 
+ */
+
+#define	SEND_POLL		0x72	/*
+					   * after load <- RECEIVE_POLL 
+					 */
+#define SEND_INIT		0x11	/*
+					   * first message <- RECEIVE_INIT
+					   * int32 NumApplications  int32
+					   * NumNCCIs int32 BoardNumber 
+					 */
+#define SEND_REGISTER		0x12	/*
+					   * register an application int32
+					   * ApplIDId int32 NumMessages
+					   * int32 NumB3Connections int32
+					   * NumB3Blocks int32 B3Size
+					   * 
+					   * AnzB3Connection != 0 &&
+					   * AnzB3Blocks >= 1 && B3Size >= 1 
+					 */
+#define SEND_RELEASE		0x14	/*
+					   * deregister an application int32 
+					   * ApplID 
+					 */
+#define SEND_MESSAGE		0x15	/*
+					   * send capi-message int32 length
+					   * capi-data ... 
+					 */
+#define SEND_DATA_B3_REQ	0x13	/*
+					   * send capi-data-message int32
+					   * MsgLength capi-data ... int32
+					   * B3Length data .... 
+					 */
+
+/*
+ * LLI Messages from the ISDN-ControllerISDN Controller 
+ */
+
+#define RECEIVE_POLL		0x32	/*
+					   * <- after SEND_POLL 
+					 */
+#define RECEIVE_INIT		0x27	/*
+					   * <- after SEND_INIT int32 length
+					   * byte total length b1struct board 
+					   * driver revision b1struct card
+					   * type b1struct reserved b1struct
+					   * serial number b1struct driver
+					   * capability b1struct d-channel
+					   * protocol b1struct CAPI-2.0
+					   * profile b1struct capi version 
+					 */
+#define RECEIVE_MESSAGE		0x21	/*
+					   * <- after SEND_MESSAGE int32
+					   * AppllID int32 Length capi-data
+					   * .... 
+					 */
+#define RECEIVE_DATA_B3_IND	0x22	/*
+					   * received data int32 AppllID
+					   * int32 Length capi-data ...
+					   * int32 B3Length data ... 
+					 */
+#define RECEIVE_START		0x23	/*
+					   * Handshake 
+					 */
+#define RECEIVE_STOP		0x24	/*
+					   * Handshake 
+					 */
+#define RECEIVE_NEW_NCCI	0x25	/*
+					   * int32 AppllID int32 NCCI int32
+					   * WindowSize 
+					 */
+#define RECEIVE_FREE_NCCI	0x26	/*
+					   * int32 AppllID int32 NCCI 
+					 */
+#define RECEIVE_RELEASE		0x26	/*
+					   * int32 AppllID int32 0xffffffff 
+					 */
+
+/*
+ * port offsets
+ */
+
+#define B1_READ			0x00
+#define B1_WRITE		0x01
+#define B1_INSTAT		0x02
+#define B1_OUTSTAT		0x03
+#define B1_RESET		0x10
+#define B1_ANALYSE		0x04
+
+
+
+static inline unsigned char b1outp(unsigned short base,
+				   unsigned short offset,
+				   unsigned char value)
+{
+	outb(value, base + offset);
+	return inb(base + B1_ANALYSE);
+}
+
+static int irq_table[16] =
+{0,
+ 0,
+ 0,
+ 192,				/* irq 3 */
+ 32,				/* irq 4 */
+ 160,				/* irq 5 */
+ 96,				/* irq 6 */
+ 224,				/* irq 7 */
+ 0,
+ 64,				/* irq 9 */
+ 80,				/* irq 10 */
+ 208,				/* irq 11 */
+ 48,				/* irq 12 */
+ 0,
+ 0,
+ 112,				/* irq 15 */
+};
+
+int B1_valid_irq(unsigned irq)
+{
+	return irq_table[irq] != 0;
+}
+
+unsigned char B1_assign_irq(unsigned short base, unsigned irq)
+{
+	return b1outp(base, B1_RESET, irq_table[irq]);
+}
+
+unsigned char B1_enable_irq(unsigned short base)
+{
+	return b1outp(base, B1_INSTAT, 0x02);
+}
+
+unsigned char B1_disable_irq(unsigned short base)
+{
+	return b1outp(base, B1_INSTAT, 0x00);
+}
+
+void B1_reset(unsigned short base)
+{
+	b1outp(base, B1_RESET, 0);
+	udelay(55 * 2 * 1000);	/* 2 TIC's */
+
+	b1outp(base, B1_RESET, 1);
+	udelay(55 * 2 * 1000);	/* 2 TIC's */
+
+	b1outp(base, B1_RESET, 0);
+	udelay(55 * 2 * 1000);	/* 2 TIC's */
+}
+
+int B1_detect(unsigned short base)
+{
+	/*
+	 * Statusregister 0000 00xx 
+	 */
+	if ((inb(base + B1_INSTAT) & 0xfc)
+	    || (inb(base + B1_OUTSTAT) & 0xfc))
+		return 1;
+
+	/*
+	 * Statusregister 0000 001x 
+	 */
+	b1outp(base, B1_INSTAT, 0x2);	/* enable irq */
+	b1outp(base, B1_OUTSTAT, 0x2);
+	if ((inb(base + B1_INSTAT) & 0xfe) != 0x2
+	    || (inb(base + B1_OUTSTAT) & 0xfe) != 0x2)
+		return 2;
+
+	/*
+	 * Statusregister 0000 000x 
+	 */
+	b1outp(base, B1_INSTAT, 0x0);	/* disable irq */
+	b1outp(base, B1_OUTSTAT, 0x0);
+	if ((inb(base + B1_INSTAT) & 0xfe)
+	    || (inb(base + B1_OUTSTAT) & 0xfe))
+		return 3;
+
+	return 0;
+}
+
+static inline int B1_rx_full(unsigned short base)
+{
+	return inb(base + B1_INSTAT) & 0x1;
+}
+
+static inline unsigned char B1_get_byte(unsigned short base)
+{
+	unsigned long i = jiffies + 5 * HZ;	/* maximum wait time 5 sec */
+	while (!B1_rx_full(base) && i > jiffies);
+	if (B1_rx_full(base))
+		return inb(base + B1_READ);
+	printk(KERN_CRIT "b1lli: rx not full after 5 second\n");
+	return 0;
+}
+
+static inline unsigned int B1_get_word(unsigned short base)
+{
+	unsigned int val = 0;
+	val |= B1_get_byte(base);
+	val |= (B1_get_byte(base) << 8);
+	val |= (B1_get_byte(base) << 16);
+	val |= (B1_get_byte(base) << 24);
+	return val;
+}
+
+static inline int B1_tx_empty(unsigned short base)
+{
+	return inb(base + B1_OUTSTAT) & 0x1;
+}
+
+static inline void B1_put_byte(unsigned short base, unsigned char val)
+{
+	while (!B1_tx_empty(base));
+	b1outp(base, B1_WRITE, val);
+}
+
+static inline void B1_put_word(unsigned short base, unsigned int val)
+{
+	B1_put_byte(base, val & 0xff);
+	B1_put_byte(base, (val >> 8) & 0xff);
+	B1_put_byte(base, (val >> 16) & 0xff);
+	B1_put_byte(base, (val >> 24) & 0xff);
+}
+
+static inline unsigned int B1_get_slice(unsigned short base,
+					unsigned char *dp)
+{
+	unsigned int len, i;
+
+	len = i = B1_get_word(base);
+	while (i-- > 0)
+		*dp++ = B1_get_byte(base);
+	return len;
+}
+
+static inline void B1_put_slice(unsigned short base,
+				unsigned char *dp, unsigned int len)
+{
+	B1_put_word(base, len);
+	while (len-- > 0)
+		B1_put_byte(base, *dp++);
+}
+
+extern int loaddebug;
+
+int B1_load_t4file(unsigned short base, avmb1_t4file * t4file)
+{
+	/*
+	 * Data is in user space !!!
+	 */
+	unsigned char buf[256];
+	unsigned char *dp;
+	int i, left, retval;
+
+
+	dp = t4file->data;
+	left = t4file->len;
+	while (left > sizeof(buf)) {
+		retval = copy_from_user(buf, dp, sizeof(buf));
+		if (retval)
+			return -EFAULT;
+		if (loaddebug)
+			printk(KERN_DEBUG "b1capi: loading: %d bytes ..", sizeof(buf));
+		for (i = 0; i < sizeof(buf); i++)
+			B1_put_byte(base, buf[i]);
+		if (loaddebug)
+		   printk("ok\n");
+		left -= sizeof(buf);
+		dp += sizeof(buf);
+	}
+	if (left) {
+		retval = copy_from_user(buf, dp, left);
+		if (retval)
+			return -EFAULT;
+		if (loaddebug)
+			printk(KERN_DEBUG "b1capi: loading: %d bytes ..", left);
+		for (i = 0; i < left; i++)
+			B1_put_byte(base, buf[i]);
+		if (loaddebug)
+		   printk("ok\n");
+	}
+	return 0;
+}
+
+int B1_loaded(unsigned short base)
+{
+	int i;
+	unsigned char ans;
+
+	if (loaddebug)
+		printk(KERN_DEBUG "b1capi: loaded: wait 1 ..\n");
+	for (i = jiffies + 10 * HZ; i > jiffies;) {
+		if (B1_tx_empty(base))
+			break;
+	}
+	if (!B1_tx_empty(base)) {
+		printk(KERN_ERR "b1lli: B1_loaded: timeout tx\n");
+		return 0;
+	}
+	B1_put_byte(base, SEND_POLL);
+	printk(KERN_DEBUG "b1capi: loaded: wait 2 ..\n");
+	for (i = jiffies + 10 * HZ; i > jiffies;) {
+		if (B1_rx_full(base)) {
+			if ((ans = B1_get_byte(base)) == RECEIVE_POLL) {
+				if (loaddebug)
+					printk(KERN_DEBUG "b1capi: loaded: ok\n");
+				return 1;
+			}
+			printk(KERN_ERR "b1lli: B1_loaded: got 0x%x ???\n", ans);
+			return 0;
+		}
+	}
+	printk(KERN_ERR "b1lli: B1_loaded: timeout rx\n");
+	return 0;
+}
+
+/*
+ * ------------------------------------------------------------------- 
+ */
+static inline void parse_version(avmb1_card * card)
+{
+	int i, j;
+	for (j = 0; j < AVM_MAXVERSION; j++)
+		card->version[j] = "\0\0" + 1;
+	for (i = 0, j = 0;
+	     j < AVM_MAXVERSION && i < card->versionlen;
+	     j++, i += card->versionbuf[i] + 1)
+		card->version[j] = &card->versionbuf[i + 1];
+}
+/*
+ * ------------------------------------------------------------------- 
+ */
+
+void B1_send_init(unsigned short port,
+	     unsigned int napps, unsigned int nncci, unsigned int cardnr)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	B1_put_byte(port, SEND_INIT);
+	B1_put_word(port, napps);
+	B1_put_word(port, nncci);
+	B1_put_word(port, cardnr);
+	restore_flags(flags);
+}
+
+void B1_send_register(unsigned short port,
+		      __u16 appid, __u32 nmsg,
+		      __u32 nb3conn, __u32 nb3blocks, __u32 b3bsize)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	B1_put_byte(port, SEND_REGISTER);
+	B1_put_word(port, appid);
+	B1_put_word(port, nmsg);
+	B1_put_word(port, nb3conn);
+	B1_put_word(port, nb3blocks);
+	B1_put_word(port, b3bsize);
+	restore_flags(flags);
+}
+
+void B1_send_release(unsigned short port,
+		     __u16 appid)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	B1_put_byte(port, SEND_RELEASE);
+	B1_put_word(port, appid);
+	restore_flags(flags);
+}
+
+extern int showcapimsgs;
+
+void B1_send_message(unsigned short port, struct sk_buff *skb)
+{
+	unsigned long flags;
+	__u16 len = CAPIMSG_LEN(skb->data);
+	__u8 cmd = CAPIMSG_COMMAND(skb->data);
+	__u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+	__u32 contr = CAPIMSG_CONTROL(skb->data);
+
+	if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
+		__u16 dlen = CAPIMSG_DATALEN(skb->data);
+
+		if (showcapimsgs > 2) {
+			if (showcapimsgs & 1) {
+				printk(KERN_DEBUG "b1lli: Put [0x%lx] id#%d %s len=%u\n",
+				       (unsigned long) contr,
+				       CAPIMSG_APPID(skb->data),
+				       capi_cmd2str(cmd, subcmd), len);
+			} else {
+				printk(KERN_DEBUG "b1lli: Put [0x%lx] %s\n",
+						(unsigned long) contr,
+						capi_message2str(skb->data));
+			}
+
+		}
+		save_flags(flags);
+		cli();
+		B1_put_byte(port, SEND_DATA_B3_REQ);
+		B1_put_slice(port, skb->data, len);
+		B1_put_slice(port, skb->data + len, dlen);
+		restore_flags(flags);
+	} else {
+		if (showcapimsgs) {
+
+			if (showcapimsgs & 1) {
+				printk(KERN_DEBUG "b1lli: Put [0x%lx] id#%d %s len=%u\n",
+				       (unsigned long) contr,
+				       CAPIMSG_APPID(skb->data),
+				       capi_cmd2str(cmd, subcmd), len);
+			} else {
+				printk(KERN_DEBUG "b1lli: Put [0x%lx] %s\n", (unsigned long)contr, capi_message2str(skb->data));
+			}
+		}
+		save_flags(flags);
+		cli();
+		B1_put_byte(port, SEND_MESSAGE);
+		B1_put_slice(port, skb->data, len);
+		restore_flags(flags);
+	}
+	dev_kfree_skb(skb, FREE_WRITE);
+}
+
+/*
+ * ------------------------------------------------------------------- 
+ */
+
+void B1_handle_interrupt(avmb1_card * card)
+{
+	unsigned char b1cmd;
+	struct sk_buff *skb;
+
+	unsigned ApplId;
+	unsigned MsgLen;
+	unsigned DataB3Len;
+	unsigned NCCI;
+	unsigned WindowSize;
+
+	if (!B1_rx_full(card->port))
+		return;
+
+	b1cmd = B1_get_byte(card->port);
+
+	switch (b1cmd) {
+
+	case RECEIVE_DATA_B3_IND:
+
+		ApplId = (unsigned) B1_get_word(card->port);
+		MsgLen = B1_get_slice(card->port, card->msgbuf);
+		DataB3Len = B1_get_slice(card->port, card->databuf);
+
+		if (showcapimsgs > 2) {
+			__u8 cmd = CAPIMSG_COMMAND(card->msgbuf);
+			__u8 subcmd = CAPIMSG_SUBCOMMAND(card->msgbuf);
+			__u32 contr = CAPIMSG_CONTROL(card->msgbuf);
+			CAPIMSG_SETDATA(card->msgbuf, card->databuf);
+			if (showcapimsgs & 1) {
+				printk(KERN_DEBUG "b1lli: Got [0x%lx] id#%d %s len=%u/%u\n",
+				       (unsigned long) contr,
+				       CAPIMSG_APPID(card->msgbuf),
+				       capi_cmd2str(cmd, subcmd),
+				       MsgLen, DataB3Len);
+			} else {
+				printk(KERN_DEBUG "b1lli: Got [0x%lx] %s\n", (unsigned long)contr, capi_message2str(card->msgbuf));
+			}
+		}
+		if (!(skb = dev_alloc_skb(DataB3Len + MsgLen))) {
+			printk(KERN_ERR "b1lli: incoming packet dropped\n");
+		} else {
+			SET_SKB_FREE(skb);
+			memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+			memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len);
+			CAPIMSG_SETDATA(skb->data, skb->data + MsgLen);
+			avmb1_handle_capimsg(card, ApplId, skb);
+		}
+		break;
+
+	case RECEIVE_MESSAGE:
+
+		ApplId = (unsigned) B1_get_word(card->port);
+		MsgLen = B1_get_slice(card->port, card->msgbuf);
+		if (showcapimsgs) {
+			__u8 cmd = CAPIMSG_COMMAND(card->msgbuf);
+			__u8 subcmd = CAPIMSG_SUBCOMMAND(card->msgbuf);
+			__u32 contr = CAPIMSG_CONTROL(card->msgbuf);
+			if (showcapimsgs & 1) {
+				printk(KERN_DEBUG "b1lli: Got [0x%lx] id#%d %s len=%u\n",
+				       (unsigned long) contr,
+				       CAPIMSG_APPID(card->msgbuf),
+				       capi_cmd2str(cmd, subcmd),
+				       MsgLen);
+			} else {
+				printk(KERN_DEBUG "b1lli: Got [0x%lx] %s\n",
+						(unsigned long) contr,
+						capi_message2str(card->msgbuf));
+			}
+
+		}
+		if (!(skb = dev_alloc_skb(MsgLen))) {
+			printk(KERN_ERR "b1lli: incoming packet dropped\n");
+		} else {
+			SET_SKB_FREE(skb);
+			memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+			avmb1_handle_capimsg(card, ApplId, skb);
+		}
+		break;
+
+	case RECEIVE_NEW_NCCI:
+
+		ApplId = B1_get_word(card->port);
+		NCCI = B1_get_word(card->port);
+		WindowSize = B1_get_word(card->port);
+
+		if (showcapimsgs)
+			printk(KERN_DEBUG "b1lli: NEW_NCCI app %u ncci 0x%x\n", ApplId, NCCI);
+
+		avmb1_handle_new_ncci(card, ApplId, NCCI, WindowSize);
+
+		break;
+
+	case RECEIVE_FREE_NCCI:
+
+		ApplId = B1_get_word(card->port);
+		NCCI = B1_get_word(card->port);
+
+		if (showcapimsgs)
+			printk(KERN_DEBUG "b1lli: FREE_NCCI app %u ncci 0x%x\n", ApplId, NCCI);
+
+		avmb1_handle_free_ncci(card, ApplId, NCCI);
+		break;
+
+	case RECEIVE_START:
+		if (card->blocked)
+			printk(KERN_DEBUG "b1lli: RESTART\n");
+		card->blocked = 0;
+		break;
+
+	case RECEIVE_STOP:
+		printk(KERN_DEBUG "b1lli: STOP\n");
+		card->blocked = 1;
+		break;
+
+	case RECEIVE_INIT:
+
+		card->versionlen = B1_get_slice(card->port, card->versionbuf);
+		card->cardstate = CARD_ACTIVE;
+		parse_version(card);
+		printk(KERN_INFO "b1lli: %s-card (%s) with %s now active\n",
+		       card->version[VER_CARDTYPE],
+		       card->version[VER_DRIVER],
+		       card->version[VER_PROTO]);
+		avmb1_card_ready(card);
+		break;
+	default:
+		printk(KERN_ERR "b1lli: B1_handle_interrupt: 0x%x ???\n", b1cmd);
+		break;
+	}
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/b1pci.c linux/drivers/isdn/avmb1/b1pci.c
--- v2.0.30/linux/drivers/isdn/avmb1/b1pci.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/avmb1/b1pci.c	Mon Aug  4 17:33:59 1997
@@ -0,0 +1,121 @@
+/*
+ * $Id: b1pci.c,v 1.2 1997/05/18 09:24:13 calle Exp $
+ * 
+ * Module for AVM B1 PCI-card.
+ * 
+ * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
+ * 
+ * $Log: b1pci.c,v $
+ * Revision 1.2  1997/05/18 09:24:13  calle
+ * added verbose disconnect reason reporting to avmb1.
+ * some fixes in capi20 interface.
+ * changed info messages for B1-PCI
+ *
+ * Revision 1.1  1997/03/30 17:10:42  calle
+ * added support for AVM-B1-PCI card.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/skbuff.h>
+#include "compat.h"
+#include <linux/capi.h>
+#include <linux/b1lli.h>
+
+#ifndef PCI_VENDOR_ID_AVM
+#define PCI_VENDOR_ID_AVM	0x1244
+#endif
+
+#ifndef PCI_DEVICE_ID_AVM_B1
+#define PCI_DEVICE_ID_AVM_B1	0x700
+#endif
+
+static char *revision = "$Revision: 1.2 $";
+
+/* ------------------------------------------------------------- */
+
+#ifdef HAS_NEW_SYMTAB
+MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
+#endif
+
+/* ------------------------------------------------------------- */
+
+/* ------------------------------------------------------------- */
+/* -------- Init & Cleanup ------------------------------------- */
+/* ------------------------------------------------------------- */
+
+/*
+ * init / exit functions
+ */
+
+#ifdef MODULE
+#define b1pci_init init_module
+#endif
+
+int b1pci_init(void)
+{
+	char *p;
+	char rev[10];
+	int rc;
+	int pci_index;
+
+	if ((p = strchr(revision, ':'))) {
+		strcpy(rev, p + 1);
+		p = strchr(rev, '$');
+		*p = 0;
+	} else
+		strcpy(rev, " ??? ");
+
+
+#ifdef CONFIG_PCI
+	if (!pcibios_present()) {
+		printk(KERN_ERR "b1pci: no PCI-BIOS present\n");
+		return -EIO;
+	}
+
+	printk(KERN_INFO "b1pci: revision %s\n", rev);
+
+	for (pci_index = 0; pci_index < 8; pci_index++) {
+		unsigned char pci_bus, pci_device_fn;
+		unsigned int ioaddr;
+		unsigned char irq;
+
+		if (pcibios_find_device (PCI_VENDOR_ID_AVM,
+					PCI_DEVICE_ID_AVM_B1, pci_index,
+					&pci_bus, &pci_device_fn) != 0) {
+			continue;
+		}
+		pcibios_read_config_byte(pci_bus, pci_device_fn,
+				PCI_INTERRUPT_LINE, &irq);
+		pcibios_read_config_dword(pci_bus, pci_device_fn,
+				PCI_BASE_ADDRESS_1, &ioaddr);
+		/* Strip the I/O address out of the returned value */
+		ioaddr &= PCI_BASE_ADDRESS_IO_MASK;
+		printk(KERN_INFO
+			"b1pci: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n",
+			ioaddr, irq);
+		if ((rc = avmb1_probecard(ioaddr, irq)) != 0) {
+		        printk(KERN_ERR
+			"b1pci: no AVM-B1 at i/o %#x, irq %d detected\n",
+			ioaddr, irq);
+			return rc;
+		}
+		if ((rc = avmb1_addcard(ioaddr, irq)) != 0)
+			return rc;
+	}
+	return 0;
+#else
+	printk(KERN_ERR "b1pci: kernel not compiled with PCI.\n");
+	return -EIO;
+#endif
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+}
+#endif
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/capi.c linux/drivers/isdn/avmb1/capi.c
--- v2.0.30/linux/drivers/isdn/avmb1/capi.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/avmb1/capi.c	Mon Aug  4 17:33:59 1997
@@ -0,0 +1,540 @@
+/*
+ * $Id: capi.c,v 1.4 1997/05/27 15:17:50 fritz Exp $
+ *
+ * CAPI 2.0 Interface for Linux
+ *
+ * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de)
+ *
+ * $Log: capi.c,v $
+ * Revision 1.4  1997/05/27 15:17:50  fritz
+ * Added changes for recent 2.1.x kernels:
+ *   changed return type of isdn_close
+ *   queue_task_* -> queue_task
+ *   clear/set_bit -> test_and_... where apropriate.
+ *   changed type of hard_header_cache parameter.
+ *
+ * Revision 1.3  1997/05/18 09:24:14  calle
+ * added verbose disconnect reason reporting to avmb1.
+ * some fixes in capi20 interface.
+ * changed info messages for B1-PCI
+ *
+ * Revision 1.2  1997/03/05 21:17:59  fritz
+ * Added capi_poll for compiling under 2.1.27
+ *
+ * Revision 1.1  1997/03/04 21:50:29  calle
+ * Frirst version in isdn4linux
+ *
+ * Revision 2.2  1997/02/12 09:31:39  calle
+ * new version
+ *
+ * Revision 1.1  1997/01/31 10:32:20  calle
+ * Initial revision
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/fcntl.h>
+#include <linux/fs.h>
+#include <linux/signal.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/skbuff.h>
+#if (LINUX_VERSION_CODE >= 0x020117)
+#include <asm/poll.h>
+#endif
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
+
+#include "compat.h"
+#include "capiutil.h"
+#include "capicmd.h"
+#include "capidev.h"
+
+#ifdef HAS_NEW_SYMTAB
+MODULE_AUTHOR("Carsten Paeth (calle@calle.in-berlin.de)");
+#endif
+
+/* -------- driver information -------------------------------------- */
+
+int capi_major = 68;		/* allocated */
+
+#ifdef HAS_NEW_SYMTAB
+MODULE_PARM(capi_major, "i");
+#endif
+
+/* -------- global variables ---------------------------------------- */
+
+static struct capidev capidevs[CAPI_MAXMINOR + 1];
+struct capi_interface *capifuncs;
+
+/* -------- function called by lower level -------------------------- */
+
+static void capi_signal(__u16 applid, __u32 minor)
+{
+	struct capidev *cdev;
+	struct sk_buff *skb = 0;
+
+	if (minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) {
+		printk(KERN_ERR "BUG: capi_signal: illegal minor %d\n", minor);
+		return;
+	}
+	cdev = &capidevs[minor];
+	(void) (*capifuncs->capi_get_message) (applid, &skb);
+	if (skb) {
+		skb_queue_tail(&cdev->recv_queue, skb);
+		wake_up_interruptible(&cdev->recv_wait);
+	} else {
+		printk(KERN_ERR "BUG: capi_signal: no skb\n");
+	}
+}
+
+/* -------- file_operations ----------------------------------------- */
+
+#if LINUX_VERSION_CODE < 0x020100
+static int capi_lseek(struct inode *inode, struct file *file,
+		      off_t offset, int origin)
+{
+	return -ESPIPE;
+}
+#else
+static long long capi_llseek(struct inode *inode, struct file *file,
+			     long long offset, int origin)
+{
+	return -ESPIPE;
+}
+#endif
+
+#if LINUX_VERSION_CODE < 0x020100
+static int capi_read(struct inode *inode, struct file *file,
+		     char *buf, int count)
+#else
+static long capi_read(struct inode *inode, struct file *file,
+		      char *buf, unsigned long count)
+#endif
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+	struct capidev *cdev;
+	struct sk_buff *skb;
+	int retval;
+	size_t copied;
+
+	if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered)
+		return -ENODEV;
+
+	cdev = &capidevs[minor];
+
+	if ((skb = skb_dequeue(&cdev->recv_queue)) == 0) {
+
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+
+		for (;;) {
+			interruptible_sleep_on(&cdev->recv_wait);
+			if ((skb = skb_dequeue(&cdev->recv_queue)) != 0)
+				break;
+			if (current->signal & ~current->blocked)
+				break;
+		}
+		if (skb == 0)
+			return -ERESTARTNOHAND;
+	}
+	if (skb->len > count) {
+		skb_queue_head(&cdev->recv_queue, skb);
+		return -EMSGSIZE;
+	}
+	if (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3
+	    && CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND)
+		CAPIMSG_SETDATA(skb->data, buf + CAPIMSG_LEN(skb->data));
+	retval = copy_to_user(buf, skb->data, skb->len);
+	if (retval) {
+		skb_queue_head(&cdev->recv_queue, skb);
+		return retval;
+	}
+	copied = skb->len;
+
+
+	kfree_skb(skb, FREE_READ);
+
+	return copied;
+}
+
+#if LINUX_VERSION_CODE < 0x020100
+static int capi_write(struct inode *inode, struct file *file,
+		      const char *buf, int count)
+#else
+static long capi_write(struct inode *inode, struct file *file,
+		       const char *buf, unsigned long count)
+#endif
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+	struct capidev *cdev;
+	struct sk_buff *skb;
+	int retval;
+	__u8 cmd;
+	__u8 subcmd;
+	__u16 mlen;
+
+	if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered)
+		return -ENODEV;
+
+	cdev = &capidevs[minor];
+
+	skb = alloc_skb(count, GFP_USER);
+
+	if ((retval = copy_from_user(skb_put(skb, count), buf, count))) {
+		dev_kfree_skb(skb, FREE_WRITE);
+		return retval;
+	}
+	cmd = CAPIMSG_COMMAND(skb->data);
+	subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+	mlen = CAPIMSG_LEN(skb->data);
+	if (cmd == CAPI_DATA_B3 && subcmd == CAPI_REQ) {
+		__u16 dlen = CAPIMSG_DATALEN(skb->data);
+		if (mlen + dlen != count) {
+			dev_kfree_skb(skb, FREE_WRITE);
+			return -EINVAL;
+		}
+	} else if (mlen != count) {
+		dev_kfree_skb(skb, FREE_WRITE);
+		return -EINVAL;
+	}
+	CAPIMSG_SETAPPID(skb->data, cdev->applid);
+
+	cdev->errcode = (*capifuncs->capi_put_message) (cdev->applid, skb);
+
+	if (cdev->errcode) {
+		dev_kfree_skb(skb, FREE_WRITE);
+		return -EIO;
+	}
+	return count;
+}
+
+#if (LINUX_VERSION_CODE < 0x020117)
+static int capi_select(struct inode *inode, struct file *file,
+		       int sel_type, select_table * wait)
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+	struct capidev *cdev;
+
+	if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered)
+		return -ENODEV;
+
+	cdev = &capidevs[minor];
+
+	switch (sel_type) {
+	case SEL_IN:
+		if (!skb_queue_empty(&cdev->recv_queue))
+			return 1;
+		/* fall througth */
+	case SEL_EX:
+		/* error conditions ? */
+
+		select_wait(&cdev->recv_wait, wait);
+		return 0;
+	case SEL_OUT:
+		/* 
+		   if (!queue_full())
+		   return 1;
+		   select_wait(&cdev->send_wait, wait);
+		   return 0;
+		 */
+		return 1;
+	}
+	return 1;
+}
+#else
+static unsigned int
+capi_poll(struct file *file, poll_table * wait)
+{
+	unsigned int mask = 0;
+	unsigned int minor = MINOR(file->f_inode->i_rdev);
+	struct capidev *cdev;
+
+	if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered)
+		return POLLERR;
+
+	cdev = &capidevs[minor];
+	poll_wait(&(cdev->recv_wait), wait);
+	mask = POLLOUT | POLLWRNORM;
+	if (!skb_queue_empty(&cdev->recv_queue))
+		mask |= POLLIN | POLLRDNORM;
+	return mask;
+}
+#endif
+
+static int capi_ioctl(struct inode *inode, struct file *file,
+		      unsigned int cmd, unsigned long arg)
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+	struct capidev *cdev;
+	capi_ioctl_struct data;
+	int retval;
+
+
+	if (minor >= CAPI_MAXMINOR || !capidevs[minor].is_open)
+		return -ENODEV;
+
+	cdev = &capidevs[minor];
+
+	switch (cmd) {
+	case CAPI_REGISTER:
+		{
+			if (!minor)
+				return -EINVAL;
+			retval = copy_from_user((void *) &data.rparams,
+						(void *) arg, sizeof(struct capi_register_params));
+			if (retval)
+				return -EFAULT;
+			if (cdev->is_registered)
+				return -EEXIST;
+			cdev->errcode = (*capifuncs->capi_register) (&data.rparams,
+							  &cdev->applid);
+			if (cdev->errcode)
+				return -EIO;
+			(void) (*capifuncs->capi_set_signal) (cdev->applid, capi_signal, minor);
+			cdev->is_registered = 1;
+		}
+		return 0;
+
+	case CAPI_GET_VERSION:
+		{
+			retval = copy_from_user((void *) &data.contr,
+						(void *) arg,
+						sizeof(data.contr));
+			if (retval)
+				return -EFAULT;
+		        cdev->errcode = (*capifuncs->capi_get_version) (data.contr, &data.version);
+			if (cdev->errcode)
+				return -EIO;
+			retval = copy_to_user((void *) arg,
+					      (void *) &data.version,
+					      sizeof(data.version));
+			if (retval)
+				return -EFAULT;
+		}
+		return 0;
+
+	case CAPI_GET_SERIAL:
+		{
+			retval = copy_from_user((void *) &data.contr,
+						(void *) arg,
+						sizeof(data.contr));
+			if (retval)
+				return -EFAULT;
+			cdev->errcode = (*capifuncs->capi_get_serial) (data.contr, data.serial);
+			if (cdev->errcode)
+				return -EIO;
+			retval = copy_to_user((void *) arg,
+					      (void *) data.serial,
+					      sizeof(data.serial));
+			if (retval)
+				return -EFAULT;
+		}
+		return 0;
+	case CAPI_GET_PROFILE:
+		{
+			retval = copy_from_user((void *) &data.contr,
+						(void *) arg,
+						sizeof(data.contr));
+			if (retval)
+				return -EFAULT;
+
+			if (data.contr == 0) {
+				cdev->errcode = (*capifuncs->capi_get_profile) (data.contr, &data.profile);
+				if (cdev->errcode)
+					return -EIO;
+
+				retval = copy_to_user((void *) arg,
+				      (void *) &data.profile.ncontroller,
+				       sizeof(data.profile.ncontroller));
+
+			} else {
+				cdev->errcode = (*capifuncs->capi_get_profile) (data.contr, &data.profile);
+				if (cdev->errcode)
+					return -EIO;
+
+				retval = copy_to_user((void *) arg,
+						  (void *) &data.profile,
+						   sizeof(data.profile));
+			}
+			if (retval)
+				return -EFAULT;
+		}
+		return 0;
+
+	case CAPI_GET_MANUFACTURER:
+		{
+			retval = copy_from_user((void *) &data.contr,
+						(void *) arg,
+						sizeof(data.contr));
+			if (retval)
+				return -EFAULT;
+			cdev->errcode = (*capifuncs->capi_get_manufacturer) (data.contr, data.manufacturer);
+			if (cdev->errcode)
+				return -EIO;
+
+			retval = copy_to_user((void *) arg, (void *) data.manufacturer,
+					      sizeof(data.manufacturer));
+			if (retval)
+				return -EFAULT;
+
+		}
+		return 0;
+	case CAPI_GET_ERRCODE:
+		data.errcode = cdev->errcode;
+		cdev->errcode = CAPI_NOERROR;
+		if (arg) {
+			retval = copy_to_user((void *) arg,
+					      (void *) &data.errcode,
+					      sizeof(data.errcode));
+			if (retval)
+				return -EFAULT;
+		}
+		return data.errcode;
+
+	case CAPI_INSTALLED:
+		if ((*capifuncs->capi_installed) ())
+			return 0;
+		return -ENXIO;
+
+	case CAPI_MANUFACTURER_CMD:
+		{
+			struct capi_manufacturer_cmd mcmd;
+			if (minor)
+				return -EINVAL;
+			if (!suser())
+				return -EPERM;
+			retval = copy_from_user((void *) &mcmd, (void *) arg,
+						sizeof(mcmd));
+			if (retval)
+				return -EFAULT;
+			return (*capifuncs->capi_manufacturer) (mcmd.cmd, mcmd.data);
+		}
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int capi_open(struct inode *inode, struct file *file)
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+
+	if (minor >= CAPI_MAXMINOR)
+		return -ENXIO;
+
+	if (minor) {
+		if (capidevs[minor].is_open)
+			return -EEXIST;
+
+		capidevs[minor].is_open = 1;
+		skb_queue_head_init(&capidevs[minor].recv_queue);
+		MOD_INC_USE_COUNT;
+
+	} else {
+
+		if (!capidevs[minor].is_open) {
+			capidevs[minor].is_open = 1;
+			MOD_INC_USE_COUNT;
+		}
+	}
+
+
+	return 0;
+}
+
+static CLOSETYPE
+capi_release(struct inode *inode, struct file *file)
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+	struct capidev *cdev;
+	struct sk_buff *skb;
+
+	if (minor >= CAPI_MAXMINOR || !capidevs[minor].is_open) {
+		printk(KERN_ERR "capi20: release minor %d ???\n", minor);
+		return CLOSEVAL;
+	}
+	cdev = &capidevs[minor];
+
+	if (minor) {
+
+		if (cdev->is_registered)
+			(*capifuncs->capi_release) (cdev->applid);
+
+		cdev->is_registered = 0;
+		cdev->applid = 0;
+
+		while ((skb = skb_dequeue(&cdev->recv_queue)) != 0)
+			kfree_skb(skb, FREE_READ);
+	}
+	cdev->is_open = 0;
+
+	MOD_DEC_USE_COUNT;
+	return CLOSEVAL;
+}
+
+static struct file_operations capi_fops =
+{
+#if LINUX_VERSION_CODE < 0x020100
+	capi_lseek,
+#else
+	capi_llseek,
+#endif
+	capi_read,
+	capi_write,
+	NULL,			/* capi_readdir */
+#if (LINUX_VERSION_CODE < 0x020117)
+	capi_select,
+#else
+	capi_poll,
+#endif
+	capi_ioctl,
+	NULL,			/* capi_mmap */
+	capi_open,
+	capi_release,
+	NULL,			/* capi_fsync */
+	NULL,			/* capi_fasync */
+};
+
+
+/* -------- init function and module interface ---------------------- */
+
+#ifdef MODULE
+#define	 capi_init	init_module
+#endif
+
+static struct capi_interface_user cuser = {
+	"capi20",
+	0,
+};
+
+int capi_init(void)
+{
+	memset(capidevs, 0, sizeof(capidevs));
+
+	if (register_chrdev(capi_major, "capi20", &capi_fops)) {
+		printk(KERN_ERR "capi20: unable to get major %d\n", capi_major);
+		return -EIO;
+	}
+	printk(KERN_NOTICE "capi20: started up with major %d\n", capi_major);
+
+	if ((capifuncs = attach_capi_interface(&cuser)) == 0) {
+		unregister_chrdev(capi_major, "capi20");
+		return -EIO;
+	}
+	return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	unregister_chrdev(capi_major, "capi20");
+	(void) detach_capi_interface(&cuser);
+}
+
+#endif
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/capicmd.h linux/drivers/isdn/avmb1/capicmd.h
--- v2.0.30/linux/drivers/isdn/avmb1/capicmd.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/avmb1/capicmd.h	Mon Aug  4 17:33:59 1997
@@ -0,0 +1,119 @@
+/*
+ * $Id: capicmd.h,v 1.1 1997/03/04 21:50:30 calle Exp $
+ * 
+ * CAPI 2.0 Interface for Linux
+ * 
+ * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
+ * 
+ * $Log: capicmd.h,v $
+ * Revision 1.1  1997/03/04 21:50:30  calle
+ * Frirst version in isdn4linux
+ *
+ * Revision 2.2  1997/02/12 09:31:39  calle
+ * new version
+ *
+ * Revision 1.1  1997/01/31 10:32:20  calle
+ * Initial revision
+ *
+ * 
+ */
+#ifndef __CAPICMD_H__
+#define __CAPICMD_H__
+
+/*----- CAPI commands -----*/
+#define CAPI_ALERT		    0x01
+#define CAPI_CONNECT		    0x02
+#define CAPI_CONNECT_ACTIVE	    0x03
+#define CAPI_CONNECT_B3_ACTIVE	    0x83
+#define CAPI_CONNECT_B3 	    0x82
+#define CAPI_CONNECT_B3_T90_ACTIVE  0x88
+#define CAPI_DATA_B3		    0x86
+#define CAPI_DISCONNECT_B3	    0x84
+#define CAPI_DISCONNECT 	    0x04
+#define CAPI_FACILITY		    0x80
+#define CAPI_INFO		    0x08
+#define CAPI_LISTEN		    0x05
+#define CAPI_MANUFACTURER	    0xff
+#define CAPI_RESET_B3		    0x87
+#define CAPI_SELECT_B_PROTOCOL	    0x41
+
+/*----- CAPI subcommands -----*/
+
+#define CAPI_REQ    0x80
+#define CAPI_CONF   0x81
+#define CAPI_IND    0x82
+#define CAPI_RESP   0x83
+
+/*----- CAPI combined commands -----*/
+
+#define CAPICMD(cmd,subcmd)	(((cmd)<<8)|(subcmd))
+
+#define CAPI_DISCONNECT_REQ		CAPICMD(CAPI_DISCONNECT,CAPI_REQ)
+#define CAPI_DISCONNECT_CONF		CAPICMD(CAPI_DISCONNECT,CAPI_CONF)
+#define CAPI_DISCONNECT_IND		CAPICMD(CAPI_DISCONNECT,CAPI_IND)
+#define CAPI_DISCONNECT_RESP		CAPICMD(CAPI_DISCONNECT,CAPI_RESP)
+
+#define CAPI_ALERT_REQ			CAPICMD(CAPI_ALERT,CAPI_REQ)
+#define CAPI_ALERT_CONF			CAPICMD(CAPI_ALERT,CAPI_CONF)
+
+#define CAPI_CONNECT_REQ		CAPICMD(CAPI_CONNECT,CAPI_REQ)
+#define CAPI_CONNECT_CONF		CAPICMD(CAPI_CONNECT,CAPI_CONF)
+#define CAPI_CONNECT_IND		CAPICMD(CAPI_CONNECT,CAPI_IND)
+#define CAPI_CONNECT_RESP		CAPICMD(CAPI_CONNECT,CAPI_RESP)
+
+#define CAPI_CONNECT_ACTIVE_REQ		CAPICMD(CAPI_CONNECT_ACTIVE,CAPI_REQ)
+#define CAPI_CONNECT_ACTIVE_CONF	CAPICMD(CAPI_CONNECT_ACTIVE,CAPI_CONF)
+#define CAPI_CONNECT_ACTIVE_IND		CAPICMD(CAPI_CONNECT_ACTIVE,CAPI_IND)
+#define CAPI_CONNECT_ACTIVE_RESP	CAPICMD(CAPI_CONNECT_ACTIVE,CAPI_RESP)
+
+#define CAPI_SELECT_B_PROTOCOL_REQ	CAPICMD(CAPI_SELECT_B_PROTOCOL,CAPI_REQ)
+#define CAPI_SELECT_B_PROTOCOL_CONF	CAPICMD(CAPI_SELECT_B_PROTOCOL,CAPI_CONF)
+
+#define CAPI_CONNECT_B3_ACTIVE_REQ	CAPICMD(CAPI_CONNECT_B3_ACTIVE,CAPI_REQ)
+#define CAPI_CONNECT_B3_ACTIVE_CONF	CAPICMD(CAPI_CONNECT_B3_ACTIVE,CAPI_CONF)
+#define CAPI_CONNECT_B3_ACTIVE_IND	CAPICMD(CAPI_CONNECT_B3_ACTIVE,CAPI_IND)
+#define CAPI_CONNECT_B3_ACTIVE_RESP	CAPICMD(CAPI_CONNECT_B3_ACTIVE,CAPI_RESP)
+
+#define CAPI_CONNECT_B3_REQ		CAPICMD(CAPI_CONNECT_B3,CAPI_REQ)
+#define CAPI_CONNECT_B3_CONF		CAPICMD(CAPI_CONNECT_B3,CAPI_CONF)
+#define CAPI_CONNECT_B3_IND		CAPICMD(CAPI_CONNECT_B3,CAPI_IND)
+#define CAPI_CONNECT_B3_RESP		CAPICMD(CAPI_CONNECT_B3,CAPI_RESP)
+
+
+#define CAPI_CONNECT_B3_T90_ACTIVE_IND	CAPICMD(CAPI_CONNECT_B3_T90_ACTIVE,CAPI_IND)
+#define CAPI_CONNECT_B3_T90_ACTIVE_RESP	CAPICMD(CAPI_CONNECT_B3_T90_ACTIVE,CAPI_RESP)
+
+#define CAPI_DATA_B3_REQ		CAPICMD(CAPI_DATA_B3,CAPI_REQ)
+#define CAPI_DATA_B3_CONF		CAPICMD(CAPI_DATA_B3,CAPI_CONF)
+#define CAPI_DATA_B3_IND		CAPICMD(CAPI_DATA_B3,CAPI_IND)
+#define CAPI_DATA_B3_RESP		CAPICMD(CAPI_DATA_B3,CAPI_RESP)
+
+#define CAPI_DISCONNECT_B3_REQ		CAPICMD(CAPI_DISCONNECT_B3,CAPI_REQ)
+#define CAPI_DISCONNECT_B3_CONF		CAPICMD(CAPI_DISCONNECT_B3,CAPI_CONF)
+#define CAPI_DISCONNECT_B3_IND		CAPICMD(CAPI_DISCONNECT_B3,CAPI_IND)
+#define CAPI_DISCONNECT_B3_RESP		CAPICMD(CAPI_DISCONNECT_B3,CAPI_RESP)
+
+#define CAPI_RESET_B3_REQ		CAPICMD(CAPI_RESET_B3,CAPI_REQ)
+#define CAPI_RESET_B3_CONF		CAPICMD(CAPI_RESET_B3,CAPI_CONF)
+#define CAPI_RESET_B3_IND		CAPICMD(CAPI_RESET_B3,CAPI_IND)
+#define CAPI_RESET_B3_RESP		CAPICMD(CAPI_RESET_B3,CAPI_RESP)
+
+#define CAPI_LISTEN_REQ			CAPICMD(CAPI_LISTEN,CAPI_REQ)
+#define CAPI_LISTEN_CONF		CAPICMD(CAPI_LISTEN,CAPI_CONF)
+
+#define CAPI_MANUFACTURER_REQ		CAPICMD(CAPI_MANUFACTURER,CAPI_REQ)
+#define CAPI_MANUFACTURER_CONF		CAPICMD(CAPI_MANUFACTURER,CAPI_CONF)
+#define CAPI_MANUFACTURER_IND		CAPICMD(CAPI_MANUFACTURER,CAPI_IND)
+#define CAPI_MANUFACTURER_RESP		CAPICMD(CAPI_MANUFACTURER,CAPI_RESP)
+
+#define CAPI_FACILITY_REQ		CAPICMD(CAPI_FACILITY,CAPI_REQ)
+#define CAPI_FACILITY_CONF		CAPICMD(CAPI_FACILITY,CAPI_CONF)
+#define CAPI_FACILITY_IND		CAPICMD(CAPI_FACILITY,CAPI_IND)
+#define CAPI_FACILITY_RESP		CAPICMD(CAPI_FACILITY,CAPI_RESP)
+
+#define CAPI_INFO_REQ			CAPICMD(CAPI_INFO,CAPI_REQ)
+#define CAPI_INFO_CONF			CAPICMD(CAPI_INFO,CAPI_CONF)
+#define CAPI_INFO_IND			CAPICMD(CAPI_INFO,CAPI_IND)
+#define CAPI_INFO_RESP			CAPICMD(CAPI_INFO,CAPI_RESP)
+
+#endif				/* __CAPICMD_H__ */
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/capidev.h linux/drivers/isdn/avmb1/capidev.h
--- v2.0.30/linux/drivers/isdn/avmb1/capidev.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/avmb1/capidev.h	Mon Aug  4 17:33:59 1997
@@ -0,0 +1,29 @@
+/*
+ * $Id: capidev.h,v 1.1 1997/03/04 21:50:30 calle Exp $
+ *
+ * CAPI 2.0 Interface for Linux
+ *
+ * (c) Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de)
+ *
+ * $Log: capidev.h,v $
+ * Revision 1.1  1997/03/04 21:50:30  calle
+ * Frirst version in isdn4linux
+ *
+ * Revision 2.2  1997/02/12 09:31:39  calle
+ * new version
+ *
+ * Revision 1.1  1997/01/31 10:32:20  calle
+ * Initial revision
+ *
+ */
+
+struct capidev {
+	int is_open;
+	int is_registered;
+	__u16 applid;
+	struct sk_buff_head recv_queue;
+	struct wait_queue *recv_wait;
+	__u16 errcode;
+};
+
+#define CAPI_MAXMINOR	CAPI_MAXAPPL
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/capidrv.c linux/drivers/isdn/avmb1/capidrv.c
--- v2.0.30/linux/drivers/isdn/avmb1/capidrv.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/avmb1/capidrv.c	Thu Aug 21 15:14:34 1997
@@ -0,0 +1,1767 @@
+/*
+ * $Id: capidrv.c,v 1.3.2.1 1997/07/13 12:16:48 calle Exp $
+ *
+ * ISDN4Linux Driver, using capi20 interface (kernelcapi)
+ *
+ * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
+ *
+ * $Log: capidrv.c,v $
+ * Revision 1.3.2.1  1997/07/13 12:16:48  calle
+ * bug fix for more than one controller in connect_req.
+ *
+ * Revision 1.3  1997/05/18 09:24:15  calle
+ * added verbose disconnect reason reporting to avmb1.
+ * some fixes in capi20 interface.
+ * changed info messages for B1-PCI
+ *
+ * Revision 1.2  1997/03/05 21:19:59  fritz
+ * Removed include of config.h (mkdep stated this is unneded).
+ *
+ * Revision 1.1  1997/03/04 21:50:31  calle
+ * Frirst version in isdn4linux
+ *
+ * Revision 2.2  1997/02/12 09:31:39  calle
+ * new version
+ *
+ * Revision 1.1  1997/01/31 10:32:20  calle
+ * Initial revision
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/fcntl.h>
+#include <linux/fs.h>
+#include <linux/signal.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/skbuff.h>
+#include <linux/isdn.h>
+#include <linux/isdnif.h>
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
+
+#include "compat.h"
+#include "capiutil.h"
+#include "capicmd.h"
+#include "capidrv.h"
+
+static char *revision = "$Revision: 1.3.2.1 $";
+int debugmode = 0;
+
+#ifdef HAS_NEW_SYMTAB
+MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
+MODULE_PARM(debugmode, "i");
+#endif
+
+/* -------- type definitions ----------------------------------------- */
+
+
+struct capidrv_contr {
+
+	struct capidrv_contr *next;
+
+	__u32 contrnr;
+	char name[20];
+
+	/*
+	 * for isdn4linux
+	 */
+	isdn_if interface;
+	int myid;
+
+	/*
+	 * LISTEN state
+	 */
+	int state;
+	__u32 cipmask;
+	__u32 cipmask2;
+
+	/*
+	 * ID of capi message sent
+	 */
+	__u16 msgid;
+
+	/*
+	 * B-Channels
+	 */
+	int nbchan;
+	struct capidrv_bchan {
+		struct capidrv_contr *contr;
+		__u8 msn[ISDN_MSNLEN];
+		int l2;
+		int l3;
+		__u8 num[ISDN_MSNLEN];
+		__u8 mynum[ISDN_MSNLEN];
+		int si1;
+		int si2;
+		int incoming;
+		int disconnecting;
+		struct capidrv_plci {
+			struct capidrv_plci *next;
+			__u32 plci;
+			__u32 ncci;	/* ncci for CONNECT_ACTIVE_IND */
+			__u16 msgid;	/* to identfy CONNECT_CONF */
+			int chan;
+			int state;
+			struct capidrv_ncci {
+				struct capidrv_ncci *next;
+				struct capidrv_plci *plcip;
+				__u32 ncci;
+				__u16 msgid;	/* to identfy CONNECT_B3_CONF */
+				int chan;
+				int state;
+				int oldstate;
+				/* */
+				__u16 datahandle;
+			} *ncci_list;
+		} *plcip;
+		struct capidrv_ncci *nccip;
+	} *bchans;
+
+	struct capidrv_plci *plci_list;
+};
+
+struct capidrv_data {
+	__u16 appid;
+	int ncontr;
+	struct capidrv_contr *contr_list;
+};
+
+typedef struct capidrv_plci capidrv_plci;
+typedef struct capidrv_ncci capidrv_ncci;
+typedef struct capidrv_contr capidrv_contr;
+typedef struct capidrv_data capidrv_data;
+typedef struct capidrv_bchan capidrv_bchan;
+
+/* -------- data definitions ----------------------------------------- */
+
+static capidrv_data global;
+static struct capi_interface *capifuncs;
+
+/* -------- convert functions ---------------------------------------- */
+
+static inline __u32 b1prot(int l2, int l3)
+{
+	switch (l2) {
+	case ISDN_PROTO_L2_X75I:
+	case ISDN_PROTO_L2_X75UI:
+	case ISDN_PROTO_L2_X75BUI:
+		return 0;
+	case ISDN_PROTO_L2_HDLC:
+	default:
+		return 0;
+	case ISDN_PROTO_L2_TRANS:
+		return 1;
+	}
+}
+
+static inline __u32 b2prot(int l2, int l3)
+{
+	switch (l2) {
+	case ISDN_PROTO_L2_X75I:
+	case ISDN_PROTO_L2_X75UI:
+	case ISDN_PROTO_L2_X75BUI:
+	default:
+		return 0;
+	case ISDN_PROTO_L2_HDLC:
+		return 1;
+	case ISDN_PROTO_L2_TRANS:
+		return 0;
+	}
+}
+
+static inline __u32 b3prot(int l2, int l3)
+{
+	switch (l2) {
+	case ISDN_PROTO_L2_X75I:
+	case ISDN_PROTO_L2_X75UI:
+	case ISDN_PROTO_L2_X75BUI:
+	case ISDN_PROTO_L2_HDLC:
+	case ISDN_PROTO_L2_TRANS:
+	default:
+		return 0;
+	}
+}
+
+static inline __u16 si2cip(__u8 si1, __u8 si2)
+{
+	static const __u8 cip[17][5] =
+	{
+	/*  0  1  2  3  4  */
+		{0, 0, 0, 0, 0},	/*0 */
+		{16, 16, 4, 26, 16},	/*1 */
+		{17, 17, 17, 4, 4},	/*2 */
+		{2, 2, 2, 2, 2},	/*3 */
+		{18, 18, 18, 18, 18},	/*4 */
+		{2, 2, 2, 2, 2},	/*5 */
+		{0, 0, 0, 0, 0},	/*6 */
+		{2, 2, 2, 2, 2},	/*7 */
+		{2, 2, 2, 2, 2},	/*8 */
+		{21, 21, 21, 21, 21},	/*9 */
+		{19, 19, 19, 19, 19},	/*10 */
+		{0, 0, 0, 0, 0},	/*11 */
+		{0, 0, 0, 0, 0},	/*12 */
+		{0, 0, 0, 0, 0},	/*13 */
+		{0, 0, 0, 0, 0},	/*14 */
+		{22, 22, 22, 22, 22},	/*15 */
+		{27, 27, 27, 28, 27}	/*16 */
+	};
+	if (si1 > 16)
+		si1 = 0;
+	if (si2 > 4)
+		si2 = 0;
+
+	return (__u16) cip[si1][si2];
+}
+
+static inline __u8 cip2si1(__u16 cipval)
+{
+	static const __u8 si[32] =
+	{7, 1, 7, 7, 1, 1, 7, 7,	/*0-7 */
+	 7, 1, 0, 0, 0, 0, 0, 0,	/*8-15 */
+	 1, 2, 4, 10, 9, 9, 15, 7,	/*16-23 */
+	 7, 7, 1, 16, 16, 0, 0, 0};	/*24-31 */
+
+	if (cipval > 31)
+		cipval = 0;	/* .... */
+	return si[cipval];
+}
+
+static inline __u8 cip2si2(__u16 cipval)
+{
+	static const __u8 si[32] =
+	{0, 0, 0, 0, 2, 3, 0, 0,	/*0-7 */
+	 0, 3, 0, 0, 0, 0, 0, 0,	/*8-15 */
+	 1, 2, 0, 0, 9, 0, 0, 0,	/*16-23 */
+	 0, 0, 3, 2, 3, 0, 0, 0};	/*24-31 */
+
+	if (cipval > 31)
+		cipval = 0;	/* .... */
+	return si[cipval];
+}
+
+
+/* -------- controller managment ------------------------------------- */
+
+static inline capidrv_contr *findcontrbydriverid(int driverid)
+{
+	capidrv_contr *p = global.contr_list;
+
+	while (p) {
+		if (p->myid == driverid)
+			return p;
+		p = p->next;
+	}
+	return (capidrv_contr *) 0;
+}
+
+static capidrv_contr *findcontrbynumber(__u32 contr)
+{
+	capidrv_contr *p = global.contr_list;
+
+	while (p) {
+		if (p->contrnr == contr)
+			return p;
+		p = p->next;
+	}
+	return (capidrv_contr *) 0;
+}
+
+
+/* -------- plci management ------------------------------------------ */
+
+static capidrv_plci *new_plci(capidrv_contr * card, int chan)
+{
+	capidrv_plci *plcip;
+
+	plcip = (capidrv_plci *) kmalloc(sizeof(capidrv_plci), GFP_ATOMIC);
+
+	if (plcip == 0)
+		return 0;
+
+	memset(plcip, 0, sizeof(capidrv_plci));
+	plcip->state = ST_PLCI_NONE;
+	plcip->plci = 0;
+	plcip->msgid = 0;
+	plcip->chan = chan;
+	plcip->next = card->plci_list;
+	card->plci_list = plcip;
+	card->bchans[chan].plcip = plcip;
+
+	return plcip;
+}
+
+static capidrv_plci *find_plci_by_plci(capidrv_contr * card, __u32 plci)
+{
+	capidrv_plci *p;
+	for (p = card->plci_list; p; p = p->next)
+		if (p->plci == plci)
+			return p;
+	return 0;
+}
+
+static capidrv_plci *find_plci_by_msgid(capidrv_contr * card, __u16 msgid)
+{
+	capidrv_plci *p;
+	for (p = card->plci_list; p; p = p->next)
+		if (p->msgid == msgid)
+			return p;
+	return 0;
+}
+
+static capidrv_plci *find_plci_by_ncci(capidrv_contr * card, __u32 ncci)
+{
+	capidrv_plci *p;
+	for (p = card->plci_list; p; p = p->next)
+		if (p->plci == (ncci & 0xffff))
+			return p;
+	return 0;
+}
+
+static void free_plci(capidrv_contr * card, capidrv_plci * plcip)
+{
+	capidrv_plci **pp;
+
+	for (pp = &card->plci_list; *pp; pp = &(*pp)->next) {
+		if (*pp == plcip) {
+			*pp = (*pp)->next;
+			card->bchans[plcip->chan].plcip = 0;
+			card->bchans[plcip->chan].disconnecting = 0;
+			card->bchans[plcip->chan].incoming = 0;
+			kfree(plcip);
+			return;
+		}
+	}
+	printk(KERN_ERR "capidrv: free_plci %p (0x%x) not found, Huh?\n",
+	       plcip, plcip->plci);
+}
+
+/* -------- ncci management ------------------------------------------ */
+
+static inline capidrv_ncci *new_ncci(capidrv_contr * card,
+				     capidrv_plci * plcip,
+				     __u32 ncci)
+{
+	capidrv_ncci *nccip;
+
+	nccip = (capidrv_ncci *) kmalloc(sizeof(capidrv_ncci), GFP_ATOMIC);
+
+	if (nccip == 0)
+		return 0;
+
+	memset(nccip, 0, sizeof(capidrv_ncci));
+	nccip->ncci = ncci;
+	nccip->state = ST_NCCI_NONE;
+	nccip->plcip = plcip;
+	nccip->chan = plcip->chan;
+	nccip->datahandle = 0;
+
+	nccip->next = plcip->ncci_list;
+	plcip->ncci_list = nccip;
+
+	card->bchans[plcip->chan].nccip = nccip;
+
+	return nccip;
+}
+
+static inline capidrv_ncci *find_ncci(capidrv_contr * card, __u32 ncci)
+{
+	capidrv_plci *plcip;
+	capidrv_ncci *p;
+
+	if ((plcip = find_plci_by_ncci(card, ncci)) == 0)
+		return 0;
+
+	for (p = plcip->ncci_list; p; p = p->next)
+		if (p->ncci == ncci)
+			return p;
+	return 0;
+}
+
+static inline capidrv_ncci *find_ncci_by_msgid(capidrv_contr * card,
+					       __u32 ncci, __u16 msgid)
+{
+	capidrv_plci *plcip;
+	capidrv_ncci *p;
+
+	if ((plcip = find_plci_by_ncci(card, ncci)) == 0)
+		return 0;
+
+	for (p = plcip->ncci_list; p; p = p->next)
+		if (p->msgid == msgid)
+			return p;
+	return 0;
+}
+
+static void free_ncci(capidrv_contr * card, struct capidrv_ncci *nccip)
+{
+	struct capidrv_ncci **pp;
+
+	for (pp = &(nccip->plcip->ncci_list); *pp; pp = &(*pp)->next) {
+		if (*pp == nccip) {
+			*pp = (*pp)->next;
+			break;
+		}
+	}
+	card->bchans[nccip->chan].nccip = 0;
+	kfree(nccip);
+}
+
+/* -------- convert and send capi message ---------------------------- */
+
+static void send_message(capidrv_contr * card, _cmsg * cmsg)
+{
+	struct sk_buff *skb;
+	size_t len;
+	capi_cmsg2message(cmsg, cmsg->buf);
+	len = CAPIMSG_LEN(cmsg->buf);
+	skb = dev_alloc_skb(len);
+        SET_SKB_FREE(skb);
+	memcpy(skb_put(skb, len), cmsg->buf, len);
+	(*capifuncs->capi_put_message) (global.appid, skb);
+}
+
+/* -------- state machine -------------------------------------------- */
+
+struct listenstatechange {
+	int actstate;
+	int nextstate;
+	int event;
+};
+
+static struct listenstatechange listentable[] =
+{
+	{ST_LISTEN_NONE, ST_LISTEN_WAIT_CONF, EV_LISTEN_REQ},
+	{ST_LISTEN_ACTIVE, ST_LISTEN_ACTIVE_WAIT_CONF, EV_LISTEN_REQ},
+	{ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_ERROR},
+    {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_ERROR},
+	{ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY},
+      {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY},
+	{ST_LISTEN_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK},
+	{ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK},
+	{},
+};
+
+static void listen_change_state(capidrv_contr * card, int event)
+{
+	struct listenstatechange *p = listentable;
+	while (p->event) {
+		if (card->state == p->actstate && p->event == event) {
+			if (debugmode)
+				printk(KERN_DEBUG "capidrv: listen_change_state %d -> %d\n",
+				       card->state, p->nextstate);
+			card->state = p->nextstate;
+			return;
+		}
+		p++;
+	}
+	printk(KERN_ERR "capidrv: listen_change_state state=%d event=%d ????\n",
+	       card->state, event);
+
+}
+
+/* ------------------------------------------------------------------ */
+
+static void p0(capidrv_contr * card, capidrv_plci * plci)
+{
+	isdn_ctrl cmd;
+
+	card->bchans[plci->chan].contr = 0;
+	cmd.command = ISDN_STAT_DHUP;
+	cmd.driver = card->myid;
+	cmd.arg = plci->chan;
+	card->interface.statcallb(&cmd);
+	free_plci(card, plci);
+}
+
+/* ------------------------------------------------------------------ */
+
+struct plcistatechange {
+	int actstate;
+	int nextstate;
+	int event;
+	void (*changefunc) (capidrv_contr * card, capidrv_plci * plci);
+};
+
+static struct plcistatechange plcitable[] =
+{
+  /* P-0 */
+	{ST_PLCI_NONE, ST_PLCI_OUTGOING, EV_PLCI_CONNECT_REQ, 0},
+	{ST_PLCI_NONE, ST_PLCI_ALLOCATED, EV_PLCI_FACILITY_IND_UP, 0},
+	{ST_PLCI_NONE, ST_PLCI_INCOMING, EV_PLCI_CONNECT_IND, 0},
+  /* P-0.1 */
+	{ST_PLCI_OUTGOING, ST_PLCI_NONE, EV_PLCI_CONNECT_CONF_ERROR, p0},
+	{ST_PLCI_OUTGOING, ST_PLCI_ALLOCATED, EV_PLCI_CONNECT_CONF_OK, 0},
+    {ST_PLCI_OUTGOING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
+ {ST_PLCI_OUTGOING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
+  /* P-1 */
+      {ST_PLCI_ALLOCATED, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, 0},
+   {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
+{ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
+    {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
+  /* P-ACT */
+      {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
+   {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
+	{ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
+  /* P-2 */
+    {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, 0},
+    {ST_PLCI_INCOMING, ST_PLCI_FACILITY_IND, EV_PLCI_FACILITY_IND_UP, 0},
+	{ST_PLCI_INCOMING, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_RESP, 0},
+    {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
+ {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
+     {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
+  /* P-3 */
+{ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, 0},
+{ST_PLCI_FACILITY_IND, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_ACTIVE_IND, 0},
+{ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
+	{ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
+ {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
+  /* P-4 */
+      {ST_PLCI_ACCEPTING, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, 0},
+   {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
+{ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
+    {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
+  /* P-5 */
+{ST_PLCI_DISCONNECTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
+  /* P-6 */
+	{ST_PLCI_DISCONNECTED, ST_PLCI_NONE, EV_PLCI_DISCONNECT_RESP, p0},
+	{},
+};
+
+static void plci_change_state(capidrv_contr * card, capidrv_plci * plci, int event)
+{
+	struct plcistatechange *p = plcitable;
+	while (p->event) {
+		if (plci->state == p->actstate && p->event == event) {
+			if (debugmode)
+				printk(KERN_DEBUG "capidrv: plci_change_state:0x%x %d -> %d\n",
+				  plci->plci, plci->state, p->nextstate);
+			plci->state = p->nextstate;
+			if (p->changefunc)
+				p->changefunc(card, plci);
+			return;
+		}
+		p++;
+	}
+	printk(KERN_ERR "capidrv: plci_change_state:0x%x state=%d event=%d ????\n",
+	       plci->plci, plci->state, event);
+}
+
+/* ------------------------------------------------------------------ */
+
+static _cmsg cmsg;
+
+static void n0(capidrv_contr * card, capidrv_ncci * ncci)
+{
+	isdn_ctrl cmd;
+
+	capi_fill_DISCONNECT_REQ(&cmsg,
+				 global.appid,
+				 card->msgid++,
+				 ncci->plcip->plci,
+				 0,	/* BChannelinformation */
+				 0,	/* Keypadfacility */
+				 0,	/* Useruserdata */
+				 0	/* Facilitydataarray */
+	);
+	send_message(card, &cmsg);
+	plci_change_state(card, ncci->plcip, EV_PLCI_DISCONNECT_REQ);
+
+	cmd.command = ISDN_STAT_BHUP;
+	cmd.driver = card->myid;
+	cmd.arg = ncci->chan;
+	card->interface.statcallb(&cmd);
+	free_ncci(card, ncci);
+}
+
+/* ------------------------------------------------------------------ */
+
+struct nccistatechange {
+	int actstate;
+	int nextstate;
+	int event;
+	void (*changefunc) (capidrv_contr * card, capidrv_ncci * ncci);
+};
+
+static struct nccistatechange nccitable[] =
+{
+  /* N-0 */
+	{ST_NCCI_NONE, ST_NCCI_OUTGOING, EV_NCCI_CONNECT_B3_REQ, 0},
+	{ST_NCCI_NONE, ST_NCCI_INCOMING, EV_NCCI_CONNECT_B3_IND, 0},
+  /* N-0.1 */
+    {ST_NCCI_OUTGOING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_CONF_OK, 0},
+      {ST_NCCI_OUTGOING, ST_NCCI_NONE, EV_NCCI_CONNECT_B3_CONF_ERROR, 0},
+  /* N-1 */
+ {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_CONNECT_B3_REJECT, 0},
+	{ST_NCCI_INCOMING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_RESP, 0},
+  {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0},
+ {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0},
+  /* N-2 */
+   {ST_NCCI_ALLOCATED, ST_NCCI_ACTIVE, EV_NCCI_CONNECT_B3_ACTIVE_IND, 0},
+ {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0},
+{ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0},
+  /* N-ACT */
+	{ST_NCCI_ACTIVE, ST_NCCI_RESETING, EV_NCCI_RESET_B3_REQ, 0},
+    {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0},
+   {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0},
+  /* N-3 */
+	{ST_NCCI_RESETING, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, 0},
+  {ST_NCCI_RESETING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0},
+ {ST_NCCI_RESETING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0},
+  /* N-4 */
+	{ST_NCCI_DISCONNECTING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0},
+	{ST_NCCI_DISCONNECTING, ST_NCCI_PREVIOUS, EV_NCCI_DISCONNECT_B3_CONF_ERROR, 0},
+  /* N-5 */
+    {ST_NCCI_DISCONNECTED, ST_NCCI_NONE, EV_NCCI_DISCONNECT_B3_RESP, n0},
+	{},
+};
+
+static void ncci_change_state(capidrv_contr * card, capidrv_ncci * ncci, int event)
+{
+	struct nccistatechange *p = nccitable;
+	while (p->event) {
+		if (ncci->state == p->actstate && p->event == event) {
+			if (debugmode)
+				printk(KERN_DEBUG "capidrv: ncci_change_state:0x%x %d -> %d\n",
+				  ncci->ncci, ncci->state, p->nextstate);
+			if (p->nextstate == ST_NCCI_PREVIOUS) {
+				ncci->state = ncci->oldstate;
+				ncci->oldstate = p->actstate;
+			} else {
+				ncci->oldstate = p->actstate;
+				ncci->state = p->nextstate;
+			}
+			if (p->changefunc)
+				p->changefunc(card, ncci);
+			return;
+		}
+		p++;
+	}
+	printk(KERN_ERR "capidrv: ncci_change_state:0x%x state=%d event=%d ????\n",
+	       ncci->ncci, ncci->state, event);
+}
+
+/* ------------------------------------------------------------------- */
+
+static inline int new_bchan(capidrv_contr * card)
+{
+	int i;
+	for (i = 0; i < card->nbchan; i++) {
+		if (card->bchans[i].plcip == 0) {
+			card->bchans[i].disconnecting = 0;
+			return i;
+		}
+	}
+	return -1;
+}
+
+/* ------------------------------------------------------------------- */
+
+static void handle_controller(_cmsg * cmsg)
+{
+	capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f);
+
+	if (!card) {
+		printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n",
+		       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+		       cmsg->adr.adrController & 0x7f);
+		return;
+	}
+	switch (CAPICMD(cmsg->Command, cmsg->Subcommand)) {
+
+	case CAPI_LISTEN_CONF:	/* Controller */
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv: listenconf Info=0x%4x (%s) cipmask=0x%x\n",
+			       cmsg->Info, capi_info2str(cmsg->Info), card->cipmask);
+		if (cmsg->Info) {
+			listen_change_state(card, EV_LISTEN_CONF_ERROR);
+		} else if (card->cipmask == 0) {
+			listen_change_state(card, EV_LISTEN_CONF_EMPTY);
+		} else {
+			listen_change_state(card, EV_LISTEN_CONF_OK);
+		}
+		break;
+
+	case CAPI_MANUFACTURER_IND:	/* Controller */
+		goto ignored;
+	case CAPI_MANUFACTURER_CONF:	/* Controller */
+		goto ignored;
+	case CAPI_FACILITY_IND:	/* Controller/plci/ncci */
+		goto ignored;
+	case CAPI_FACILITY_CONF:	/* Controller/plci/ncci */
+		goto ignored;
+	case CAPI_INFO_IND:	/* Controller/plci */
+		goto ignored;
+	case CAPI_INFO_CONF:	/* Controller/plci */
+		goto ignored;
+
+	default:
+		printk(KERN_ERR "capidrv: got %s from controller 0x%x ???",
+		       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+		       cmsg->adr.adrController);
+	}
+	return;
+
+      ignored:
+	printk(KERN_INFO "capidrv: %s from controller 0x%x ignored\n",
+	       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+	       cmsg->adr.adrController);
+}
+
+static void handle_incoming_call(capidrv_contr * card, _cmsg * cmsg)
+{
+	capidrv_plci *plcip;
+	capidrv_bchan *bchan;
+	isdn_ctrl cmd;
+	int chan;
+
+	if ((chan = new_bchan(card)) == -1) {
+		printk(KERN_ERR "capidrv: incoming call on not existing bchan ?\n");
+		return;
+	}
+	bchan = &card->bchans[chan];
+	if ((plcip = new_plci(card, chan)) == 0) {
+		printk(KERN_ERR "capidrv: incoming call: no memory, sorry.\n");
+		return;
+	}
+	bchan->incoming = 1;
+	plcip->plci = cmsg->adr.adrPLCI;
+	plci_change_state(card, plcip, EV_PLCI_CONNECT_IND);
+
+	cmd.command = ISDN_STAT_ICALL;
+	cmd.driver = card->myid;
+	cmd.arg = chan;
+	memset(&cmd.parm.setup, 0, sizeof(cmd.parm.setup));
+	strncpy(cmd.parm.setup.phone,
+	        cmsg->CallingPartyNumber + 3,
+		cmsg->CallingPartyNumber[0] - 2);
+	strncpy(cmd.parm.setup.eazmsn,
+	        cmsg->CalledPartyNumber + 2,
+		cmsg->CalledPartyNumber[0] - 1);
+	cmd.parm.setup.si1 = cip2si1(cmsg->CIPValue);
+	cmd.parm.setup.si2 = cip2si2(cmsg->CIPValue);
+	cmd.parm.setup.plan = cmsg->CallingPartyNumber[1];
+	cmd.parm.setup.screen = cmsg->CallingPartyNumber[2];
+
+	printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s\n", 
+			cmd.parm.setup.phone,
+			cmd.parm.setup.si1,
+			cmd.parm.setup.si2,
+			cmd.parm.setup.eazmsn);
+
+	switch (card->interface.statcallb(&cmd)) {
+	case 0:
+		/* No device matching this call.
+		 * and isdn_common.c has send a HANGUP command
+		 * which is ignored in state ST_PLCI_INCOMING,
+		 * so we send RESP to ignore the call
+		 */
+		capi_cmsg_answer(cmsg);
+		cmsg->Reject = 1;	/* ignore */
+		send_message(card, cmsg);
+		plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT);
+		printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s ignored\n",
+			cmd.parm.setup.phone,
+			cmd.parm.setup.si1,
+			cmd.parm.setup.si2,
+			cmd.parm.setup.eazmsn);
+		break;
+	case 1:
+		/* At least one device matching this call (RING on ttyI)
+		 * HL-driver may send ALERTING on the D-channel in this
+		 * case.
+		 * really means: RING on ttyI or a net interface
+		 * accepted this call already.
+		 *
+		 * If the call was accepted, state has already changed,
+		 * and CONNECT_RESP already sent.
+		 */
+		if (plcip->state == ST_PLCI_INCOMING) {
+			printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s tty alerting\n",
+				cmd.parm.setup.phone,
+				cmd.parm.setup.si1,
+				cmd.parm.setup.si2,
+				cmd.parm.setup.eazmsn);
+			capi_fill_ALERT_REQ(cmsg,
+					    global.appid,
+					    card->msgid++,
+					    plcip->plci,	/* adr */
+					    0,	/* BChannelinformation */
+					    0,	/* Keypadfacility */
+					    0,	/* Useruserdata */
+					    0	/* Facilitydataarray */
+			);
+			plcip->msgid = cmsg->Messagenumber;
+			send_message(card, cmsg);
+		} else {
+			printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s on netdev\n",
+				cmd.parm.setup.phone,
+				cmd.parm.setup.si1,
+				cmd.parm.setup.si2,
+				cmd.parm.setup.eazmsn);
+		}
+		break;
+
+	case 2:		/* Call will be rejected. */
+		capi_cmsg_answer(cmsg);
+		cmsg->Reject = 2;	/* reject call, normal call clearing */
+		send_message(card, cmsg);
+		plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT);
+		break;
+
+	default:
+		/* An error happened. (Invalid parameters for example.) */
+		capi_cmsg_answer(cmsg);
+		cmsg->Reject = 8;	/* reject call,
+					   destination out of order */
+		send_message(card, cmsg);
+		plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT);
+		break;
+	}
+	return;
+}
+
+static void handle_plci(_cmsg * cmsg)
+{
+	capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f);
+	capidrv_plci *plcip;
+	isdn_ctrl cmd;
+
+	if (!card) {
+		printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n",
+		       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+		       cmsg->adr.adrController & 0x7f);
+		return;
+	}
+	switch (CAPICMD(cmsg->Command, cmsg->Subcommand)) {
+
+	case CAPI_DISCONNECT_IND:	/* plci */
+		if (cmsg->Reason) {
+			printk(KERN_INFO "capidrv: %s reason 0x%x (%s) for plci 0x%x\n",
+			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+			       cmsg->Reason, capi_info2str(cmsg->Reason), cmsg->adr.adrPLCI);
+		}
+		if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI))) {
+			capi_cmsg_answer(cmsg);
+			send_message(card, cmsg);
+			goto notfound;
+		}
+		card->bchans[plcip->chan].disconnecting = 1;
+		plci_change_state(card, plcip, EV_PLCI_DISCONNECT_IND);
+		capi_cmsg_answer(cmsg);
+		send_message(card, cmsg);
+		plci_change_state(card, plcip, EV_PLCI_DISCONNECT_RESP);
+		break;
+
+	case CAPI_DISCONNECT_CONF:	/* plci */
+		if (cmsg->Info) {
+			printk(KERN_INFO "capidrv: %s info 0x%x (%s) for plci 0x%x\n",
+			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+			       cmsg->Info, capi_info2str(cmsg->Info), 
+			       cmsg->adr.adrPLCI);
+		}
+		if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI)))
+			goto notfound;
+
+		card->bchans[plcip->chan].disconnecting = 1;
+		break;
+
+	case CAPI_ALERT_CONF:	/* plci */
+		if (cmsg->Info) {
+			printk(KERN_INFO "capidrv: %s info 0x%x (%s) for plci 0x%x\n",
+			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+			       cmsg->Info, capi_info2str(cmsg->Info), 
+			       cmsg->adr.adrPLCI);
+		}
+		break;
+
+	case CAPI_CONNECT_IND:	/* plci */
+		handle_incoming_call(card, cmsg);
+		break;
+
+	case CAPI_CONNECT_CONF:	/* plci */
+		if (cmsg->Info) {
+			printk(KERN_INFO "capidrv: %s info 0x%x (%s) for plci 0x%x\n",
+			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+			       cmsg->Info, capi_info2str(cmsg->Info), 
+			       cmsg->adr.adrPLCI);
+		}
+		if (!(plcip = find_plci_by_msgid(card, cmsg->Messagenumber)))
+			goto notfound;
+
+		plcip->plci = cmsg->adr.adrPLCI;
+		if (cmsg->Info) {
+			plci_change_state(card, plcip, EV_PLCI_CONNECT_CONF_ERROR);
+		} else {
+			plci_change_state(card, plcip, EV_PLCI_CONNECT_CONF_OK);
+		}
+		break;
+
+	case CAPI_CONNECT_ACTIVE_IND:	/* plci */
+
+		if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI)))
+			goto notfound;
+
+		if (card->bchans[plcip->chan].incoming) {
+			capi_cmsg_answer(cmsg);
+			send_message(card, cmsg);
+			plci_change_state(card, plcip, EV_PLCI_CONNECT_ACTIVE_IND);
+		} else {
+			capidrv_ncci *nccip;
+			capi_cmsg_answer(cmsg);
+			send_message(card, cmsg);
+
+			nccip = new_ncci(card, plcip, cmsg->adr.adrPLCI);
+
+			if (!nccip) {
+				printk(KERN_ERR "capidrv: no mem for ncci, sorry\n");
+				break;	/* $$$$ */
+			}
+			capi_fill_CONNECT_B3_REQ(cmsg,
+						 global.appid,
+						 card->msgid++,
+						 plcip->plci,	/* adr */
+						 0	/* NCPI */
+			);
+			nccip->msgid = cmsg->Messagenumber;
+			send_message(card, cmsg);
+			cmd.command = ISDN_STAT_DCONN;
+			cmd.driver = card->myid;
+			cmd.arg = plcip->chan;
+			card->interface.statcallb(&cmd);
+			plci_change_state(card, plcip, EV_PLCI_CONNECT_ACTIVE_IND);
+			ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_REQ);
+		}
+		break;
+
+	case CAPI_INFO_IND:	/* Controller/plci */
+
+		if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI)))
+			goto notfound;
+
+		if (cmsg->InfoNumber == 0x4000) {
+			if (cmsg->InfoElement[0] == 4) {
+				cmd.command = ISDN_STAT_CINF;
+				cmd.driver = card->myid;
+				cmd.arg = plcip->chan;
+				sprintf(cmd.parm.num, "%lu",
+					(unsigned long)
+					((__u32) cmsg->InfoElement[1]
+				  | ((__u32) (cmsg->InfoElement[2]) << 8)
+				 | ((__u32) (cmsg->InfoElement[3]) << 16)
+					 | ((__u32) (cmsg->InfoElement[4]) << 24)));
+				card->interface.statcallb(&cmd);
+				break;
+			}
+		}
+		printk(KERN_ERR "capidrv: %s\n", capi_cmsg2str(cmsg));
+		break;
+
+	case CAPI_CONNECT_ACTIVE_CONF:		/* plci */
+		goto ignored;
+	case CAPI_SELECT_B_PROTOCOL_CONF:	/* plci */
+		goto ignored;
+	case CAPI_FACILITY_IND:	/* Controller/plci/ncci */
+		goto ignored;
+	case CAPI_FACILITY_CONF:	/* Controller/plci/ncci */
+		goto ignored;
+
+	case CAPI_INFO_CONF:	/* Controller/plci */
+		goto ignored;
+
+	default:
+		printk(KERN_ERR "capidrv: got %s for plci 0x%x ???",
+		       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+		       cmsg->adr.adrPLCI);
+	}
+	return;
+      ignored:
+	printk(KERN_INFO "capidrv: %s for plci 0x%x ignored\n",
+	       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+	       cmsg->adr.adrPLCI);
+	return;
+      notfound:
+	printk(KERN_ERR "capidrv: %s: plci 0x%x not found\n",
+	       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+	       cmsg->adr.adrPLCI);
+	return;
+}
+
+static void handle_ncci(_cmsg * cmsg)
+{
+	capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f);
+	capidrv_plci *plcip;
+	capidrv_ncci *nccip;
+	isdn_ctrl cmd;
+
+	if (!card) {
+		printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n",
+		       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+		       cmsg->adr.adrController & 0x7f);
+		return;
+	}
+	switch (CAPICMD(cmsg->Command, cmsg->Subcommand)) {
+
+	case CAPI_CONNECT_B3_ACTIVE_IND:	/* ncci */
+		if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
+			goto notfound;
+
+		capi_cmsg_answer(cmsg);
+		send_message(card, cmsg);
+		ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_ACTIVE_IND);
+
+		cmd.command = ISDN_STAT_BCONN;
+		cmd.driver = card->myid;
+		cmd.arg = nccip->chan;
+		card->interface.statcallb(&cmd);
+
+		printk(KERN_INFO "capidrv: chan %d up with ncci 0x%x\n",
+		       nccip->chan, nccip->ncci);
+		break;
+
+	case CAPI_CONNECT_B3_ACTIVE_CONF:	/* ncci */
+		goto ignored;
+
+	case CAPI_CONNECT_B3_IND:	/* ncci */
+
+		plcip = find_plci_by_ncci(card, cmsg->adr.adrNCCI);
+		if (plcip) {
+			nccip = new_ncci(card, plcip, cmsg->adr.adrNCCI);
+			if (nccip) {
+				ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_IND);
+				capi_fill_CONNECT_B3_RESP(cmsg,
+							  global.appid,
+							  card->msgid++,
+							  nccip->ncci,	/* adr */
+							  0,	/* Reject */
+							  0	/* NCPI */
+				);
+				send_message(card, cmsg);
+				ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_RESP);
+				break;
+			}
+			printk(KERN_ERR "capidrv: no mem for ncci, sorry\n");
+		} else {
+			printk(KERN_ERR "capidrv: %s: plci for ncci 0x%x not found\n",
+			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+			       cmsg->adr.adrNCCI);
+		}
+		capi_fill_CONNECT_B3_RESP(cmsg,
+					  global.appid,
+					  card->msgid++,
+					  cmsg->adr.adrNCCI,
+					  2,	/* Reject */
+					  0	/* NCPI */
+		);
+		send_message(card, cmsg);
+		break;
+
+	case CAPI_CONNECT_B3_CONF:	/* ncci */
+
+		if (!(nccip = find_ncci_by_msgid(card,
+						 cmsg->adr.adrNCCI,
+						 cmsg->Messagenumber)))
+			goto notfound;
+
+		nccip->ncci = cmsg->adr.adrNCCI;
+		if (cmsg->Info) {
+			printk(KERN_INFO "capidrv: %s info 0x%x (%s) for ncci 0x%x\n",
+			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+			       cmsg->Info, capi_info2str(cmsg->Info), 
+			       cmsg->adr.adrNCCI);
+		}
+
+		if (cmsg->Info)
+			ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_CONF_ERROR);
+		else
+			ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_CONF_OK);
+		break;
+
+	case CAPI_CONNECT_B3_T90_ACTIVE_IND:	/* ncci */
+		capi_cmsg_answer(cmsg);
+		send_message(card, cmsg);
+		break;
+
+	case CAPI_DATA_B3_IND:	/* ncci */
+		/* handled in handle_data() */
+		goto ignored;
+
+	case CAPI_DATA_B3_CONF:	/* ncci */
+		if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
+			goto notfound;
+
+		cmd.command = ISDN_STAT_BSENT;
+		cmd.driver = card->myid;
+		cmd.arg = nccip->chan;
+		card->interface.statcallb(&cmd);
+
+		break;
+
+	case CAPI_DISCONNECT_B3_IND:	/* ncci */
+		if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
+			goto notfound;
+
+		card->bchans[nccip->chan].disconnecting = 1;
+		ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_IND);
+		capi_cmsg_answer(cmsg);
+		send_message(card, cmsg);
+		ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_RESP);
+		break;
+
+	case CAPI_DISCONNECT_B3_CONF:	/* ncci */
+		if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
+			goto notfound;
+		if (cmsg->Info) {
+			printk(KERN_INFO "capidrv: %s info 0x%x (%s) for ncci 0x%x\n",
+			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+			       cmsg->Info, capi_info2str(cmsg->Info), 
+			       cmsg->adr.adrNCCI);
+			ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_CONF_ERROR);
+		}
+		break;
+
+	case CAPI_RESET_B3_IND:	/* ncci */
+		capi_cmsg_answer(cmsg);
+		send_message(card, cmsg);
+		break;
+
+	case CAPI_RESET_B3_CONF:	/* ncci */
+		goto ignored;	/* $$$$ */
+
+	case CAPI_FACILITY_IND:	/* Controller/plci/ncci */
+		goto ignored;
+	case CAPI_FACILITY_CONF:	/* Controller/plci/ncci */
+		goto ignored;
+
+	default:
+		printk(KERN_ERR "capidrv: got %s for ncci 0x%x ???",
+		       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+		       cmsg->adr.adrNCCI);
+	}
+	return;
+      ignored:
+	printk(KERN_INFO "capidrv: %s for ncci 0x%x ignored\n",
+	       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+	       cmsg->adr.adrNCCI);
+	return;
+      notfound:
+	printk(KERN_ERR "capidrv: %s: ncci 0x%x not found\n",
+	       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+	       cmsg->adr.adrNCCI);
+}
+
+
+static void handle_data(_cmsg * cmsg, struct sk_buff *skb)
+{
+	capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f);
+	capidrv_ncci *nccip;
+
+	if (!card) {
+		printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n",
+		       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+		       cmsg->adr.adrController & 0x7f);
+		return;
+	}
+	if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) {
+		printk(KERN_ERR "capidrv: %s: ncci 0x%x not found\n",
+		       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+		       cmsg->adr.adrNCCI);
+		kfree_skb(skb, FREE_READ);
+		return;
+	}
+	(void) skb_pull(skb, CAPIMSG_LEN(skb->data));
+	card->interface.rcvcallb_skb(card->myid, nccip->chan, skb);
+	capi_cmsg_answer(cmsg);
+	send_message(card, cmsg);
+}
+
+static _cmsg s_cmsg;
+
+static void capidrv_signal(__u16 applid, __u32 dummy)
+{
+	struct sk_buff *skb = 0;
+
+	while ((*capifuncs->capi_get_message) (global.appid, &skb) == CAPI_NOERROR) {
+		capi_message2cmsg(&s_cmsg, skb->data);
+		if (debugmode > 1)
+			printk(KERN_DEBUG "capidrv_signal: %s\n", capi_cmsg2str(&s_cmsg));
+
+		if (s_cmsg.Command == CAPI_DATA_B3
+		    && s_cmsg.Subcommand == CAPI_IND) {
+			handle_data(&s_cmsg, skb);
+			continue;
+		}
+		kfree_skb(skb, FREE_READ);
+		if ((s_cmsg.adr.adrController & 0xffffff00) == 0)
+			handle_controller(&s_cmsg);
+		else if ((s_cmsg.adr.adrPLCI & 0xffff0000) == 0)
+			handle_plci(&s_cmsg);
+		else
+			handle_ncci(&s_cmsg);
+	}
+}
+
+/* ------------------------------------------------------------------- */
+
+static _cmsg cmdcmsg;
+
+static int capidrv_ioctl(isdn_ctrl * c, capidrv_contr * card)
+{
+	switch (c->arg) {
+	default:
+		printk(KERN_DEBUG "capidrv: capidrv_ioctl(%ld) called ??\n", c->arg);
+		return -EINVAL;
+	}
+	return -EINVAL;
+}
+
+static int capidrv_command(isdn_ctrl * c, capidrv_contr * card)
+{
+	isdn_ctrl cmd;
+	struct capidrv_bchan *bchan;
+	struct capidrv_plci *plcip;
+
+	if (c->command == ISDN_CMD_IOCTL)
+		return capidrv_ioctl(c, card);
+
+	switch (c->command) {
+	case ISDN_CMD_DIAL:{
+			__u8 calling[ISDN_MSNLEN + 3];
+			__u8 called[ISDN_MSNLEN + 2];
+
+			if (debugmode)
+				printk(KERN_DEBUG "capidrv: ISDN_CMD_DIAL(ch=%ld,\"%s,%d,%d,%s\")\n",
+					c->arg,
+				        c->parm.setup.phone,
+				        c->parm.setup.si1,
+				        c->parm.setup.si2,
+				        c->parm.setup.eazmsn);
+
+			bchan = &card->bchans[c->arg % card->nbchan];
+
+			if (bchan->plcip) {
+				printk(KERN_ERR "capidrv: dail ch=%ld,\"%s,%d,%d,%s\" in use (plci=0x%x)\n",
+			        	c->arg, 
+				        c->parm.setup.phone,
+				        c->parm.setup.si1,
+				        c->parm.setup.si2,
+				        c->parm.setup.eazmsn,
+				        bchan->plcip->plci);
+				return 0;
+			}
+			bchan->si1 = c->parm.setup.si1;
+			bchan->si2 = c->parm.setup.si2;
+
+			strncpy(bchan->num, c->parm.setup.phone, sizeof(bchan->num));
+			strncpy(bchan->mynum, c->parm.setup.eazmsn, sizeof(bchan->mynum));
+
+			calling[0] = strlen(bchan->mynum) + 2;
+			calling[1] = 0;
+			calling[2] = 0x80;
+			strncpy(calling + 3, bchan->mynum, ISDN_MSNLEN);
+
+			called[0] = strlen(bchan->num) + 1;
+			called[1] = 0x80;
+			strncpy(called + 2, bchan->num, ISDN_MSNLEN);
+
+			capi_fill_CONNECT_REQ(&cmdcmsg,
+					      global.appid,
+					      card->msgid++,
+					      card->contrnr,	/* adr */
+					  si2cip(bchan->si1, bchan->si2),	/* cipvalue */
+					      called,	/* CalledPartyNumber */
+					      calling,	/* CallingPartyNumber */
+					      0,	/* CalledPartySubaddress */
+					      0,	/* CallingPartySubaddress */
+					    b1prot(bchan->l2, bchan->l3),	/* B1protocol */
+					    b2prot(bchan->l2, bchan->l3),	/* B2protocol */
+					    b3prot(bchan->l2, bchan->l3),	/* B3protocol */
+					      0,	/* B1configuration */
+					      0,	/* B2configuration */
+					      0,	/* B3configuration */
+					      0,	/* BC */
+					      0,	/* LLC */
+					      0,	/* HLC */
+					      0,	/* BChannelinformation */
+					      0,	/* Keypadfacility */
+					      0,	/* Useruserdata */
+					      0		/* Facilitydataarray */
+			    );
+			if ((plcip = new_plci(card, (c->arg % card->nbchan))) == 0) {
+				cmd.command = ISDN_STAT_DHUP;
+				cmd.driver = card->myid;
+				cmd.arg = (c->arg % card->nbchan);
+				card->interface.statcallb(&cmd);
+				return -1;
+			}
+			plcip->msgid = cmdcmsg.Messagenumber;
+			plci_change_state(card, plcip, EV_PLCI_CONNECT_REQ);
+			send_message(card, &cmdcmsg);
+			return 0;
+		}
+
+	case ISDN_CMD_ACCEPTD:
+
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv: ISDN_CMD_ACCEPTD(ch=%ld)\n",
+			       c->arg);
+		bchan = &card->bchans[c->arg % card->nbchan];
+
+		capi_fill_CONNECT_RESP(&cmdcmsg,
+				       global.appid,
+				       card->msgid++,
+				       bchan->plcip->plci,	/* adr */
+				       0,	/* Reject */
+				       b1prot(bchan->l2, bchan->l3),	/* B1protocol */
+				       b2prot(bchan->l2, bchan->l3),	/* B2protocol */
+				       b3prot(bchan->l2, bchan->l3),	/* B3protocol */
+				       0,	/* B1configuration */
+				       0,	/* B2configuration */
+				       0,	/* B3configuration */
+				       0,	/* ConnectedNumber */
+				       0,	/* ConnectedSubaddress */
+				       0,	/* LLC */
+				       0,	/* BChannelinformation */
+				       0,	/* Keypadfacility */
+				       0,	/* Useruserdata */
+				       0	/* Facilitydataarray */
+		);
+		capi_cmsg2message(&cmdcmsg, cmdcmsg.buf);
+		plci_change_state(card, bchan->plcip, EV_PLCI_CONNECT_RESP);
+		send_message(card, &cmdcmsg);
+		return 0;
+
+	case ISDN_CMD_ACCEPTB:
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv: ISDN_CMD_ACCEPTB(ch=%ld)\n",
+			       c->arg);
+		return -ENOSYS;
+
+	case ISDN_CMD_HANGUP:
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv: ISDN_CMD_HANGUP(ch=%ld)\n",
+			       c->arg);
+		bchan = &card->bchans[c->arg % card->nbchan];
+
+		if (bchan->disconnecting) {
+			if (debugmode)
+				printk(KERN_DEBUG "capidrv: chan %ld already disconnecting ...\n",
+				       c->arg);
+			return 0;
+		}
+		if (bchan->nccip) {
+			bchan->disconnecting = 1;
+			capi_fill_DISCONNECT_B3_REQ(&cmdcmsg,
+						    global.appid,
+						    card->msgid++,
+						    bchan->nccip->ncci,
+						    0	/* NCPI */
+			);
+			ncci_change_state(card, bchan->nccip, EV_NCCI_DISCONNECT_B3_REQ);
+			send_message(card, &cmdcmsg);
+		} else if (bchan->plcip) {
+			bchan->disconnecting = 1;
+			if (bchan->plcip->state == ST_PLCI_INCOMING) {
+				/* just ignore, we a called from isdn_status_callback(),
+				 * which will return 0 or 2, this is handled by the
+				 * CONNECT_IND handler
+				 */
+			} else {
+				capi_fill_DISCONNECT_REQ(&cmdcmsg,
+							 global.appid,
+							 card->msgid++,
+						      bchan->plcip->plci,
+							 0,	/* BChannelinformation */
+							 0,	/* Keypadfacility */
+							 0,	/* Useruserdata */
+							 0	/* Facilitydataarray */
+				);
+				plci_change_state(card, bchan->plcip, EV_PLCI_DISCONNECT_REQ);
+				send_message(card, &cmdcmsg);
+			}
+		}
+/* ready */
+
+	case ISDN_CMD_SETL2:
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv: set L2 on chan %ld to %ld\n",
+			       (c->arg & 0xff), (c->arg >> 8));
+		bchan = &card->bchans[c->arg % card->nbchan];
+		bchan->l2 = (c->arg >> 8);
+		return 0;
+
+	case ISDN_CMD_SETL3:
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv: set L3 on chan %ld to %ld\n",
+			       (c->arg & 0xff), (c->arg >> 8));
+		bchan = &card->bchans[c->arg % card->nbchan];
+		bchan->l3 = (c->arg >> 8);
+		return 0;
+
+	case ISDN_CMD_SETEAZ:
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv: set EAZ \"%s\" on chan %ld\n",
+			       c->parm.num, c->arg);
+		bchan = &card->bchans[c->arg % card->nbchan];
+		strncpy(bchan->msn, c->parm.num, ISDN_MSNLEN);
+		return 0;
+
+	case ISDN_CMD_CLREAZ:
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv: clearing EAZ on chan %ld\n", c->arg);
+		bchan = &card->bchans[c->arg % card->nbchan];
+		bchan->msn[0] = 0;
+		return 0;
+
+	case ISDN_CMD_LOCK:
+		if (debugmode > 1)
+			printk(KERN_DEBUG "capidrv: ISDN_CMD_LOCK (%ld)\n", c->arg);
+		MOD_INC_USE_COUNT;
+		break;
+
+	case ISDN_CMD_UNLOCK:
+		if (debugmode > 1)
+			printk(KERN_DEBUG "capidrv: ISDN_CMD_UNLOCK (%ld)\n", c->arg);
+		MOD_DEC_USE_COUNT;
+		break;
+
+/* never called */
+	case ISDN_CMD_GETL2:
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv: ISDN_CMD_GETL2\n");
+		return -ENODEV;
+	case ISDN_CMD_GETL3:
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv: ISDN_CMD_GETL3\n");
+		return -ENODEV;
+	case ISDN_CMD_GETEAZ:
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv: ISDN_CMD_GETEAZ\n");
+		return -ENODEV;
+	case ISDN_CMD_SETSIL:
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv: ISDN_CMD_SETSIL\n");
+		return -ENODEV;
+	case ISDN_CMD_GETSIL:
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv: ISDN_CMD_GETSIL\n");
+		return -ENODEV;
+	default:
+		printk(KERN_ERR "capidrv: ISDN_CMD_%d, Huh?\n", c->command);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int if_command(isdn_ctrl * c)
+{
+	capidrv_contr *card = findcontrbydriverid(c->driver);
+
+	if (card)
+		return capidrv_command(c, card);
+
+	printk(KERN_ERR
+	     "capidrv: if_command %d called with invalid driverId %d!\n",
+	       c->command, c->driver);
+	return -ENODEV;
+}
+
+static _cmsg sendcmsg;
+
+static int if_sendbuf(int id, int channel, struct sk_buff *skb)
+{
+	capidrv_contr *card = findcontrbydriverid(id);
+	capidrv_bchan *bchan;
+	capidrv_ncci *nccip;
+	int len = skb->len;
+	size_t msglen;
+	__u16 errcode;
+
+	if (!card) {
+		printk(KERN_ERR "capidrv: if_sendbuf called with invalid driverId %d!\n",
+		       id);
+		return 0;
+	}
+	bchan = &card->bchans[channel % card->nbchan];
+	nccip = bchan->nccip;
+	if (!nccip || nccip->state != ST_NCCI_ACTIVE) {
+		printk(KERN_ERR "capidrv: if_sendbuf: %s:%d: chan not up!\n",
+		       card->name, channel);
+		return 0;
+	}
+	capi_fill_DATA_B3_REQ(&sendcmsg, global.appid, card->msgid++,
+			      nccip->ncci,	/* adr */
+			      (__u32) skb->data,	/* Data */
+			      skb->len,		/* DataLength */
+			      nccip->datahandle++,	/* DataHandle */
+			      0	/* Flags */
+	    );
+	capi_cmsg2message(&sendcmsg, sendcmsg.buf);
+	msglen = CAPIMSG_LEN(sendcmsg.buf);
+	if (skb_headroom(skb) < msglen) {
+		struct sk_buff *nskb = dev_alloc_skb(msglen + skb->len);
+		if (!nskb) {
+			printk(KERN_ERR "capidrv: if_sendbuf: no memory\n");
+			return 0;
+		}
+#if 0
+		printk(KERN_DEBUG "capidrv: only %d bytes headroom\n",
+		       skb_headroom(skb));
+#endif
+                SET_SKB_FREE(nskb);
+		memcpy(skb_put(nskb, msglen), sendcmsg.buf, msglen);
+		memcpy(skb_put(nskb, skb->len), skb->data, skb->len);
+		errcode = (*capifuncs->capi_put_message) (global.appid, nskb);
+		switch (errcode) {
+		case CAPI_NOERROR:
+			dev_kfree_skb(skb, FREE_WRITE);
+			return len;
+		case CAPI_SENDQUEUEFULL:
+			dev_kfree_skb(nskb, FREE_WRITE);
+			return 0;
+		default:
+			return -1;
+		}
+	} else {
+		memcpy(skb_push(skb, msglen), sendcmsg.buf, msglen);
+		errcode = (*capifuncs->capi_put_message) (global.appid, skb);
+		switch (errcode) {
+		case CAPI_NOERROR:
+			return len;
+		case CAPI_SENDQUEUEFULL:
+			return 0;
+		default:
+			return -1;
+		}
+	}
+}
+
+static int capidrv_addcontr(__u16 contr, struct capi_profile *profp)
+{
+	capidrv_contr *card;
+	isdn_ctrl cmd;
+	char id[20];
+	int i;
+
+	sprintf(id, "capidrv-%d", contr);
+	if (!(card = (capidrv_contr *) kmalloc(sizeof(capidrv_contr), GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		 "capidrv: (%s) Could not allocate contr-struct.\n", id);
+		return -1;
+	}
+	memset(card, 0, sizeof(capidrv_contr));
+	strcpy(card->name, id);
+	card->contrnr = contr;
+	card->nbchan = profp->nbchannel;
+	card->bchans = (capidrv_bchan *) kmalloc(sizeof(capidrv_bchan) * card->nbchan, GFP_ATOMIC);
+	if (!card->bchans) {
+		printk(KERN_WARNING
+		"capidrv: (%s) Could not allocate bchan-structs.\n", id);
+		kfree(card);
+		return -1;
+	}
+	card->interface.channels = profp->nbchannel;
+	card->interface.maxbufsize = 2048;
+	card->interface.command = if_command;
+	card->interface.writebuf_skb = if_sendbuf;
+	card->interface.writecmd = 0;
+	card->interface.readstat = 0;
+	card->interface.features = ISDN_FEATURE_L2_X75I |
+	    ISDN_FEATURE_L2_X75UI |
+	    ISDN_FEATURE_L2_X75BUI |
+	    ISDN_FEATURE_L2_HDLC |
+	    ISDN_FEATURE_L2_TRANS |
+	    ISDN_FEATURE_L3_TRANS |
+	    ISDN_FEATURE_P_UNKNOWN;
+	card->interface.hl_hdrlen = 22; /* len of DATA_B3_REQ */
+	strncpy(card->interface.id, id, sizeof(card->interface.id) - 1);
+	card->next = global.contr_list;
+	global.contr_list = card;
+	global.ncontr++;
+
+	if (!register_isdn(&card->interface)) {
+		global.contr_list = global.contr_list->next;
+		printk(KERN_ERR "capidrv: Unable to register contr %s\n", id);
+		kfree(card->bchans);
+		kfree(card);
+		return -1;
+	}
+	card->myid = card->interface.channels;
+
+	memset(card->bchans, 0, sizeof(capidrv_bchan) * card->nbchan);
+	for (i = 0; i < card->nbchan; i++) {
+		card->bchans[i].contr = card;
+	}
+
+	cmd.driver = card->myid;
+	cmd.command = ISDN_STAT_RUN;
+	card->interface.statcallb(&cmd);
+
+	card->cipmask = 1;	/* any */
+	card->cipmask2 = 0;
+
+	capi_fill_LISTEN_REQ(&cmdcmsg, global.appid,
+			     card->msgid++,
+			     contr,	/* controller */
+			     1 << 6,	/* Infomask */
+			     card->cipmask,
+			     card->cipmask2,
+			     0, 0);
+	send_message(card, &cmdcmsg);
+	listen_change_state(card, EV_LISTEN_REQ);
+
+	printk(KERN_INFO "%s: now up (%d B channels)\n",
+		card->name, card->nbchan);
+
+	return 0;
+}
+
+static int capidrv_delcontr(__u16 contr)
+{
+	capidrv_contr **pp, *card;
+	isdn_ctrl cmd;
+	int i;
+
+	for (pp = &global.contr_list; *pp; pp = &(*pp)->next) {
+		if ((*pp)->contrnr == contr)
+			break;
+	}
+	if (!*pp) {
+		printk(KERN_ERR "capidrv: delcontr: no contr %u\n", contr);
+		return -1;
+	}
+	card = *pp;
+	*pp = (*pp)->next;
+	global.ncontr--;
+
+	for (i = 0; i < card->nbchan; i++) {
+		if (card->bchans[i].nccip)
+			free_ncci(card, card->bchans[i].nccip);
+		if (card->bchans[i].plcip)
+			free_plci(card, card->bchans[i].plcip);
+		if (card->plci_list)
+			printk(KERN_ERR "capidrv: bug in free_plci()\n");
+	}
+	kfree(card->bchans);
+
+	cmd.command = ISDN_STAT_UNLOAD;
+	cmd.driver = card->myid;
+	card->interface.statcallb(&cmd);
+
+	printk(KERN_INFO "%s: now down.\n", card->name);
+
+	kfree(card);
+
+	return 0;
+}
+
+
+static void lower_callback(unsigned int cmd, __u16 contr, void *data)
+{
+	switch (cmd) {
+	case KCI_CONTRUP:
+		(void) capidrv_addcontr(contr, (capi_profile *) data);
+		break;
+	case KCI_CONTRDOWN:
+		(void) capidrv_delcontr(contr);
+		break;
+	}
+}
+
+static struct capi_interface_user cuser = {
+	"capidrv",
+	lower_callback
+};
+
+#ifdef MODULE
+#define capidrv_init init_module
+#endif
+
+int capidrv_init(void)
+{
+	struct capi_register_params rparam;
+	capi_profile profile;
+	char rev[10];
+	char *p;
+	__u32 ncontr, contr;
+	__u16 errcode;
+
+	capifuncs = attach_capi_interface(&cuser);
+
+	if (!capifuncs)
+		return -EIO;
+
+#ifndef HAS_NEW_SYMTAB
+	/* No symbols to export, hide all symbols */
+	register_symtab(NULL);
+#endif
+
+	if ((p = strchr(revision, ':'))) {
+		strcpy(rev, p + 1);
+		p = strchr(rev, '$');
+		*p = 0;
+	} else
+		strcpy(rev, " ??? ");
+
+	rparam.level3cnt = 2;
+	rparam.datablkcnt = 8;
+	rparam.datablklen = 2048;
+	errcode = (*capifuncs->capi_register) (&rparam, &global.appid);
+	if (errcode) {
+		detach_capi_interface(&cuser);
+		return -EIO;
+	}
+
+	errcode = (*capifuncs->capi_get_profile) (0, &profile);
+	if (errcode != CAPI_NOERROR) {
+		(void) (*capifuncs->capi_release) (global.appid);
+		detach_capi_interface(&cuser);
+		return -EIO;
+	}
+
+	(void) (*capifuncs->capi_set_signal) (global.appid, capidrv_signal, 0);
+
+	ncontr = profile.ncontroller;
+	for (contr = 1; contr <= ncontr; contr++) {
+		errcode = (*capifuncs->capi_get_profile) (contr, &profile);
+		if (errcode != CAPI_NOERROR)
+			continue;
+		(void) capidrv_addcontr(contr, &profile);
+	}
+
+	return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	capidrv_contr *card, *next;
+	char rev[10];
+	char *p;
+
+	if ((p = strchr(revision, ':'))) {
+		strcpy(rev, p + 1);
+		p = strchr(rev, '$');
+		*p = 0;
+	} else {
+		strcpy(rev, " ??? ");
+	}
+
+	for (card = global.contr_list; card; card = next) {
+		next = card->next;
+		capidrv_delcontr(card->contrnr);
+	}
+
+	(void) (*capifuncs->capi_release) (global.appid);
+	detach_capi_interface(&cuser);
+
+	printk(KERN_NOTICE "capidrv: Rev%s: unloaded\n", rev);
+}
+
+#endif
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/capidrv.h linux/drivers/isdn/avmb1/capidrv.h
--- v2.0.30/linux/drivers/isdn/avmb1/capidrv.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/avmb1/capidrv.h	Mon Aug  4 17:33:59 1997
@@ -0,0 +1,111 @@
+/*
+ * $Id: capidrv.h,v 1.1 1997/03/04 21:50:33 calle Exp $
+ *
+ * ISDN4Linux Driver, using capi20 interface (kernelcapi)
+ *
+ * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
+ *
+ * $Log: capidrv.h,v $
+ * Revision 1.1  1997/03/04 21:50:33  calle
+ * Frirst version in isdn4linux
+ *
+ * Revision 2.2  1997/02/12 09:31:39  calle
+ * new version
+ *
+ * Revision 1.1  1997/01/31 10:32:20  calle
+ * Initial revision
+ *
+ */
+#ifndef __CAPIDRV_H__
+#define __CAPIDRV_H__
+
+/*
+ * LISTEN state machine
+ */
+#define ST_LISTEN_NONE			0	/* L-0 */
+#define ST_LISTEN_WAIT_CONF		1	/* L-0.1 */
+#define ST_LISTEN_ACTIVE		2	/* L-1 */
+#define ST_LISTEN_ACTIVE_WAIT_CONF	3	/* L-1.1 */
+
+
+#define EV_LISTEN_REQ			1	/* L-0 -> L-0.1
+						   L-1 -> L-1.1 */
+#define EV_LISTEN_CONF_ERROR		2	/* L-0.1 -> L-0
+						   L-1.1 -> L-1 */
+#define EV_LISTEN_CONF_EMPTY		3	/* L-0.1 -> L-0
+						   L-1.1 -> L-0 */
+#define EV_LISTEN_CONF_OK		4	/* L-0.1 -> L-1
+						   L-1.1 -> L.1 */
+
+/*
+ * per plci state machine
+ */
+#define ST_PLCI_NONE			0	/* P-0 */
+#define ST_PLCI_OUTGOING 		1	/* P-0.1 */
+#define ST_PLCI_ALLOCATED		2	/* P-1 */
+#define ST_PLCI_ACTIVE			3	/* P-ACT */
+#define ST_PLCI_INCOMING		4	/* P-2 */
+#define ST_PLCI_FACILITY_IND		5	/* P-3 */
+#define ST_PLCI_ACCEPTING		6	/* P-4 */
+#define ST_PLCI_DISCONNECTING		7	/* P-5 */
+#define ST_PLCI_DISCONNECTED		8	/* P-6 */
+
+#define EV_PLCI_CONNECT_REQ		1	/* P-0 -> P-0.1 */
+#define EV_PLCI_CONNECT_CONF_ERROR	2	/* P-0.1 -> P-0 */
+#define EV_PLCI_CONNECT_CONF_OK		3	/* P-0.1 -> P-1 */
+#define EV_PLCI_FACILITY_IND_UP		4	/* P-0 -> P-1 */
+#define EV_PLCI_CONNECT_IND		5	/* P-0 -> P-2 */
+#define EV_PLCI_CONNECT_ACTIVE_IND	6	/* P-1 -> P-ACT */
+#define EV_PLCI_CONNECT_REJECT		7	/* P-2 -> P-5
+						   P-3 -> P-5 */
+#define EV_PLCI_DISCONNECT_REQ		8	/* P-1 -> P-5
+						   P-2 -> P-5
+						   P-3 -> P-5
+						   P-4 -> P-5
+						   P-ACT -> P-5 */
+#define EV_PLCI_DISCONNECT_IND		9	/* P-1 -> P-6
+						   P-2 -> P-6
+						   P-3 -> P-6
+						   P-4 -> P-6
+						   P-5 -> P-6
+						   P-ACT -> P-6 */
+#define EV_PLCI_FACILITY_IND_DOWN	10	/* P-0.1 -> P-5
+						   P-1 -> P-5
+						   P-ACT -> P-5
+						   P-2 -> P-5
+						   P-3 -> P-5
+						   P-4 -> P-5 */
+#define EV_PLCI_DISCONNECT_RESP		11	/* P-6 -> P-0 */
+#define EV_PLCI_CONNECT_RESP		12	/* P-6 -> P-0 */
+
+/*
+ * per ncci state machine
+ */
+#define ST_NCCI_PREVIOUS			-1
+#define ST_NCCI_NONE				0	/* N-0 */
+#define ST_NCCI_OUTGOING			1	/* N-0.1 */
+#define ST_NCCI_INCOMING			2	/* N-1 */
+#define ST_NCCI_ALLOCATED			3	/* N-2 */
+#define ST_NCCI_ACTIVE				4	/* N-ACT */
+#define ST_NCCI_RESETING			5	/* N-3 */
+#define ST_NCCI_DISCONNECTING			6	/* N-4 */
+#define ST_NCCI_DISCONNECTED			7	/* N-5 */
+
+#define EV_NCCI_CONNECT_B3_REQ			1	/* N-0 -> N-0.1 */
+#define EV_NCCI_CONNECT_B3_IND			2	/* N-0 -> N.1 */
+#define EV_NCCI_CONNECT_B3_CONF_OK		3	/* N-0.1 -> N.2 */
+#define EV_NCCI_CONNECT_B3_CONF_ERROR		4	/* N-0.1 -> N.0 */
+#define EV_NCCI_CONNECT_B3_REJECT		5	/* N-1 -> N-4 */
+#define EV_NCCI_CONNECT_B3_RESP			6	/* N-1 -> N-2 */
+#define EV_NCCI_CONNECT_B3_ACTIVE_IND		7	/* N-2 -> N-ACT */
+#define EV_NCCI_RESET_B3_REQ			8	/* N-ACT -> N-3 */
+#define EV_NCCI_RESET_B3_IND			9	/* N-3 -> N-ACT */
+#define EV_NCCI_DISCONNECT_B3_IND		10	/* N-4 -> N.5 */
+#define EV_NCCI_DISCONNECT_B3_CONF_ERROR	11	/* N-4 -> previous */
+#define EV_NCCI_DISCONNECT_B3_REQ		12	/* N-1 -> N-4
+							   N-2 -> N-4
+							   N-3 -> N-4
+							   N-ACT -> N-4 */
+#define EV_NCCI_DISCONNECT_B3_RESP		13	/* N-5 -> N-0 */
+
+#endif				/* __CAPIDRV_H__ */
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/capiutil.c linux/drivers/isdn/avmb1/capiutil.c
--- v2.0.30/linux/drivers/isdn/avmb1/capiutil.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/avmb1/capiutil.c	Mon Aug  4 17:33:59 1997
@@ -0,0 +1,974 @@
+/*
+ * $Id: capiutil.c,v 1.3 1997/05/18 09:24:18 calle Exp $
+ *
+ * CAPI 2.0 convert capi message to capi message struct
+ *
+ * From CAPI 2.0 Development Kit AVM 1995 (msg.c)
+ * Rewritten for Linux 1996 by Carsten Paeth (calle@calle.in-berlin.de)
+ *
+ * $Log: capiutil.c,v $
+ * Revision 1.3  1997/05/18 09:24:18  calle
+ * added verbose disconnect reason reporting to avmb1.
+ * some fixes in capi20 interface.
+ * changed info messages for B1-PCI
+ *
+ * Revision 1.2  1997/03/05 21:22:13  fritz
+ * Fix: Symbols have to be exported unconditionally.
+ *
+ * Revision 1.1  1997/03/04 21:50:34  calle
+ * Frirst version in isdn4linux
+ *
+ * Revision 2.2  1997/02/12 09:31:39  calle
+ * new version
+ *
+ * Revision 1.1  1997/01/31 10:32:20  calle
+ * Initial revision
+ *
+ */
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <asm/segment.h>
+
+#include "compat.h"
+#include "capiutil.h"
+
+/* from CAPI2.0 DDK AVM Berlin GmbH */
+
+#ifndef CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON
+char *capi_info2str(__u16 reason)
+{
+    return "..";
+}
+#else
+char *capi_info2str(__u16 reason)
+{
+    switch (reason) {
+
+/*-- informative values (corresponding message was processed) -----*/
+	case 0x0001:
+	   return "NCPI not supported by current protocol, NCPI ignored";
+	case 0x0002:
+	   return "Flags not supported by current protocol, flags ignored";
+	case 0x0003:
+	   return "Alert already sent by another application";
+
+/*-- error information concerning CAPI_REGISTER -----*/
+	case 0x1001:
+	   return "Too many applications";
+	case 0x1002:
+	   return "Logical block size to small, must be at least 128 Bytes";
+	case 0x1003:
+	   return "Buffer exceeds 64 kByte";
+	case 0x1004:
+	   return "Message buffer size too small, must be at least 1024 Bytes";
+	case 0x1005:
+	   return "Max. number of logical connections not supported";
+	case 0x1006:
+	   return "Reserved";
+	case 0x1007:
+	   return "The message could not be accepted because of an internal busy condition";
+	case 0x1008:
+	   return "OS resource error (no memory ?)";
+	case 0x1009:
+	   return "CAPI not installed";
+	case 0x100A:
+	   return "Controller does not support external equipment";
+	case 0x100B:
+	   return "Controller does only support external equipment";
+
+/*-- error information concerning message exchange functions -----*/
+	case 0x1101:
+	   return "Illegal application number";
+	case 0x1102:
+	   return "Illegal command or subcommand or message length less than 12 bytes";
+	case 0x1103:
+	   return "The message could not be accepted because of a queue full condition !! The error code does not imply that CAPI cannot receive messages directed to another controller, PLCI or NCCI";
+	case 0x1104:
+	   return "Queue is empty";
+	case 0x1105:
+	   return "Queue overflow, a message was lost !! This indicates a configuration error. The only recovery from this error is to perform a CAPI_RELEASE";
+	case 0x1106:
+	   return "Unknown notification parameter";
+	case 0x1107:
+	   return "The Message could not be accepted because of an internal busy condition";
+	case 0x1108:
+	   return "OS Resource error (no memory ?)";
+	case 0x1109:
+	   return "CAPI not installed";
+	case 0x110A:
+	   return "Controller does not support external equipment";
+	case 0x110B:
+	   return "Controller does only support external equipment";
+
+/*-- error information concerning resource / coding problems -----*/
+	case 0x2001:
+	   return "Message not supported in current state";
+	case 0x2002:
+	   return "Illegal Controller / PLCI / NCCI";
+	case 0x2003:
+	   return "Out of PLCI";
+	case 0x2004:
+	   return "Out of NCCI";
+	case 0x2005:
+	   return "Out of LISTEN";
+	case 0x2006:
+	   return "Out of FAX resources (protocol T.30)";
+	case 0x2007:
+	   return "Illegal message parameter coding";
+
+/*-- error information concerning requested services  -----*/
+	case 0x3001:
+	   return "B1 protocol not supported";
+	case 0x3002: 
+	   return "B2 protocol not supported";
+	case 0x3003: 
+	   return "B3 protocol not supported";
+	case 0x3004: 
+	   return "B1 protocol parameter not supported";
+	case 0x3005: 
+	   return "B2 protocol parameter not supported";
+	case 0x3006: 
+	   return "B3 protocol parameter not supported";
+	case 0x3007: 
+	   return "B protocol combination not supported";
+	case 0x3008: 
+	   return "NCPI not supported";
+	case 0x3009: 
+	   return "CIP Value unknown";
+	case 0x300A: 
+	   return "Flags not supported (reserved bits)";
+	case 0x300B: 
+	   return "Facility not supported";
+	case 0x300C: 
+	   return "Data length not supported by current protocol";
+	case 0x300D: 
+	   return "Reset procedure not supported by current protocol";
+
+/*-- informations about the clearing of a physical connection -----*/
+	case 0x3301: 
+	   return "Protocol error layer 1 (broken line or B-channel removed by signalling protocol)";
+	case 0x3302: 
+	   return "Protocol error layer 2";
+	case 0x3303: 
+	   return "Protocol error layer 3";
+	case 0x3304: 
+	   return "Another application got that call";
+/*-- T.30 specific reasons -----*/
+	case 0x3311: 
+	   return "Connecting not successful (remote station is no FAX G3 machine)";
+	case 0x3312: 
+	   return "Connecting not successful (training error)";
+	case 0x3313: 
+	   return "Disconnected before transfer (remote station does not support transfer mode, e.g. resolution)";
+	case 0x3314: 
+	   return "Disconnected during transfer (remote abort)";
+	case 0x3315: 
+	   return "Disconnected during transfer (remote procedure error, e.g. unsuccessful repetition of T.30 commands)";
+	case 0x3316: 
+	   return "Disconnected during transfer (local tx data underrun)";
+	case 0x3317: 
+	   return "Disconnected during transfer (local rx data overflow)";
+	case 0x3318: 
+	   return "Disconnected during transfer (local abort)";
+	case 0x3319: 
+	   return "Illegal parameter coding (e.g. SFF coding error)";
+
+/*-- disconnect causes from the network according to ETS 300 102-1/Q.931 -----*/
+	case 0x3481: return "Unallocated (unassigned) number";
+	case 0x3482: return "No route to specified transit network";
+	case 0x3483: return "No route to destination";
+	case 0x3486: return "Channel unacceptable";
+	case 0x3487: 
+	   return "Call awarded and being delivered in an established channel";
+	case 0x3490: return "Normal call clearing";
+	case 0x3491: return "User busy";
+	case 0x3492: return "No user responding";
+	case 0x3493: return "No answer from user (user alerted)";
+	case 0x3495: return "Call rejected";
+	case 0x3496: return "Number changed";
+	case 0x349A: return "Non-selected user clearing";
+	case 0x349B: return "Destination out of order";
+	case 0x349C: return "Invalid number format";
+	case 0x349D: return "Facility rejected";
+	case 0x349E: return "Response to STATUS ENQUIRY";
+	case 0x349F: return "Normal, unspecified";
+	case 0x34A2: return "No circuit / channel available";
+	case 0x34A6: return "Network out of order";
+	case 0x34A9: return "Temporary failure";
+	case 0x34AA: return "Switching equipment congestion";
+	case 0x34AB: return "Access information discarded";
+	case 0x34AC: return "Requested circuit / channel not available";
+	case 0x34AF: return "Resources unavailable, unspecified";
+	case 0x34B1: return "Quality of service unavailable";
+	case 0x34B2: return "Requested facility not subscribed";
+	case 0x34B9: return "Bearer capability not authorized";
+	case 0x34BA: return "Bearer capability not presently available";
+	case 0x34BF: return "Service or option not available, unspecified";
+	case 0x34C1: return "Bearer capability not implemented";
+	case 0x34C2: return "Channel type not implemented";
+	case 0x34C5: return "Requested facility not implemented";
+	case 0x34C6: return "Only restricted digital information bearer capability is available";
+	case 0x34CF: return "Service or option not implemented, unspecified";
+	case 0x34D1: return "Invalid call reference value";
+	case 0x34D2: return "Identified channel does not exist";
+	case 0x34D3: return "A suspended call exists, but this call identity does not";
+	case 0x34D4: return "Call identity in use";
+	case 0x34D5: return "No call suspended";
+	case 0x34D6: return "Call having the requested call identity has been cleared";
+	case 0x34D8: return "Incompatible destination";
+	case 0x34DB: return "Invalid transit network selection";
+	case 0x34DF: return "Invalid message, unspecified";
+	case 0x34E0: return "Mandatory information element is missing";
+	case 0x34E1: return "Message type non-existent or not implemented";
+	case 0x34E2: return "Message not compatible with call state or message type non-existent or not implemented";
+	case 0x34E3: return "Information element non-existent or not implemented";
+	case 0x34E4: return "Invalid information element contents";
+	case 0x34E5: return "Message not compatible with call state";
+	case 0x34E6: return "Recovery on timer expiry";
+	case 0x34EF: return "Protocol error, unspecified";
+	case 0x34FF: return "Interworking, unspecified";
+
+	default: return "No additional information";
+    }
+}
+#endif
+
+typedef struct {
+	int typ;
+	size_t off;
+} _cdef;
+
+#define _CBYTE	       1
+#define _CWORD	       2
+#define _CDWORD        3
+#define _CSTRUCT       4
+#define _CMSTRUCT      5
+#define _CEND	       6
+
+static _cdef cdef[] =
+{
+    /*00 */ 
+ {_CEND},
+    /*01 */ 
+ {_CEND},
+    /*02 */ 
+ {_CEND},
+    /*03 */ 
+ {_CDWORD, offsetof(_cmsg, adr.adrController)},
+    /*04 */ 
+ {_CMSTRUCT, offsetof(_cmsg, AdditionalInfo)},
+    /*05 */ 
+ {_CSTRUCT, offsetof(_cmsg, B1configuration)},
+    /*06 */ 
+ {_CWORD, offsetof(_cmsg, B1protocol)},
+    /*07 */ 
+ {_CSTRUCT, offsetof(_cmsg, B2configuration)},
+    /*08 */ 
+ {_CWORD, offsetof(_cmsg, B2protocol)},
+    /*09 */ 
+ {_CSTRUCT, offsetof(_cmsg, B3configuration)},
+    /*0a */ 
+ {_CWORD, offsetof(_cmsg, B3protocol)},
+    /*0b */ 
+ {_CSTRUCT, offsetof(_cmsg, BC)},
+    /*0c */ 
+ {_CSTRUCT, offsetof(_cmsg, BChannelinformation)},
+    /*0d */ 
+ {_CMSTRUCT, offsetof(_cmsg, BProtocol)},
+    /*0e */ 
+ {_CSTRUCT, offsetof(_cmsg, CalledPartyNumber)},
+    /*0f */ 
+ {_CSTRUCT, offsetof(_cmsg, CalledPartySubaddress)},
+    /*10 */ 
+ {_CSTRUCT, offsetof(_cmsg, CallingPartyNumber)},
+    /*11 */ 
+ {_CSTRUCT, offsetof(_cmsg, CallingPartySubaddress)},
+    /*12 */ 
+ {_CDWORD, offsetof(_cmsg, CIPmask)},
+    /*13 */ 
+ {_CDWORD, offsetof(_cmsg, CIPmask2)},
+    /*14 */ 
+ {_CWORD, offsetof(_cmsg, CIPValue)},
+    /*15 */ 
+ {_CDWORD, offsetof(_cmsg, Class)},
+    /*16 */ 
+ {_CSTRUCT, offsetof(_cmsg, ConnectedNumber)},
+    /*17 */ 
+ {_CSTRUCT, offsetof(_cmsg, ConnectedSubaddress)},
+    /*18 */ 
+ {_CDWORD, offsetof(_cmsg, Data)},
+    /*19 */ 
+ {_CWORD, offsetof(_cmsg, DataHandle)},
+    /*1a */ 
+ {_CWORD, offsetof(_cmsg, DataLength)},
+    /*1b */ 
+ {_CSTRUCT, offsetof(_cmsg, FacilityConfirmationParameter)},
+    /*1c */ 
+ {_CSTRUCT, offsetof(_cmsg, Facilitydataarray)},
+    /*1d */ 
+ {_CSTRUCT, offsetof(_cmsg, FacilityIndicationParameter)},
+    /*1e */ 
+ {_CSTRUCT, offsetof(_cmsg, FacilityRequestParameter)},
+    /*1f */ 
+ {_CWORD, offsetof(_cmsg, FacilitySelector)},
+    /*20 */ 
+ {_CWORD, offsetof(_cmsg, Flags)},
+    /*21 */ 
+ {_CDWORD, offsetof(_cmsg, Function)},
+    /*22 */ 
+ {_CSTRUCT, offsetof(_cmsg, HLC)},
+    /*23 */ 
+ {_CWORD, offsetof(_cmsg, Info)},
+    /*24 */ 
+ {_CSTRUCT, offsetof(_cmsg, InfoElement)},
+    /*25 */ 
+ {_CDWORD, offsetof(_cmsg, InfoMask)},
+    /*26 */ 
+ {_CWORD, offsetof(_cmsg, InfoNumber)},
+    /*27 */ 
+ {_CSTRUCT, offsetof(_cmsg, Keypadfacility)},
+    /*28 */ 
+ {_CSTRUCT, offsetof(_cmsg, LLC)},
+    /*29 */ 
+ {_CSTRUCT, offsetof(_cmsg, ManuData)},
+    /*2a */ 
+ {_CDWORD, offsetof(_cmsg, ManuID)},
+    /*2b */ 
+ {_CSTRUCT, offsetof(_cmsg, NCPI)},
+    /*2c */ 
+ {_CWORD, offsetof(_cmsg, Reason)},
+    /*2d */ 
+ {_CWORD, offsetof(_cmsg, Reason_B3)},
+    /*2e */ 
+ {_CWORD, offsetof(_cmsg, Reject)},
+    /*2f */ 
+ {_CSTRUCT, offsetof(_cmsg, Useruserdata)}
+};
+
+static unsigned char *cpars[] =
+{
+    /*00 */ 0,
+    /*01 ALERT_REQ */ (unsigned char *) "\x03\x04\x0c\x27\x2f\x1c\x01\x01",
+    /*02 CONNECT_REQ */ (unsigned char *) "\x03\x14\x0e\x10\x0f\x11\x0d\x06\x08\x0a\x05\x07\x09\x01\x0b\x28\x22\x04\x0c\x27\x2f\x1c\x01\x01",
+    /*03 */ 0,
+    /*04 DISCONNECT_REQ */ (unsigned char *) "\x03\x04\x0c\x27\x2f\x1c\x01\x01",
+    /*05 LISTEN_REQ */ (unsigned char *) "\x03\x25\x12\x13\x10\x11\x01",
+    /*06 */ 0,
+    /*07 */ 0,
+    /*08 INFO_REQ */ (unsigned char *) "\x03\x0e\x04\x0c\x27\x2f\x1c\x01\x01",
+    /*09 FACILITY_REQ */ (unsigned char *) "\x03\x1f\x1e\x01",
+    /*0a SELECT_B_PROTOCOL_REQ */ (unsigned char *) "\x03\x0d\x06\x08\x0a\x05\x07\x09\x01\x01",
+    /*0b CONNECT_B3_REQ */ (unsigned char *) "\x03\x2b\x01",
+    /*0c */ 0,
+    /*0d DISCONNECT_B3_REQ */ (unsigned char *) "\x03\x2b\x01",
+    /*0e */ 0,
+    /*0f DATA_B3_REQ */ (unsigned char *) "\x03\x18\x1a\x19\x20\x01",
+    /*10 RESET_B3_REQ */ (unsigned char *) "\x03\x2b\x01",
+    /*11 */ 0,
+    /*12 */ 0,
+    /*13 ALERT_CONF */ (unsigned char *) "\x03\x23\x01",
+    /*14 CONNECT_CONF */ (unsigned char *) "\x03\x23\x01",
+    /*15 */ 0,
+    /*16 DISCONNECT_CONF */ (unsigned char *) "\x03\x23\x01",
+    /*17 LISTEN_CONF */ (unsigned char *) "\x03\x23\x01",
+    /*18 MANUFACTURER_REQ */ (unsigned char *) "\x03\x2a\x15\x21\x29\x01",
+    /*19 */ 0,
+    /*1a INFO_CONF */ (unsigned char *) "\x03\x23\x01",
+    /*1b FACILITY_CONF */ (unsigned char *) "\x03\x23\x1f\x1b\x01",
+    /*1c SELECT_B_PROTOCOL_CONF */ (unsigned char *) "\x03\x23\x01",
+    /*1d CONNECT_B3_CONF */ (unsigned char *) "\x03\x23\x01",
+    /*1e */ 0,
+    /*1f DISCONNECT_B3_CONF */ (unsigned char *) "\x03\x23\x01",
+    /*20 */ 0,
+    /*21 DATA_B3_CONF */ (unsigned char *) "\x03\x19\x23\x01",
+    /*22 RESET_B3_CONF */ (unsigned char *) "\x03\x23\x01",
+    /*23 */ 0,
+    /*24 */ 0,
+    /*25 */ 0,
+    /*26 CONNECT_IND */ (unsigned char *) "\x03\x14\x0e\x10\x0f\x11\x0b\x28\x22\x04\x0c\x27\x2f\x1c\x01\x01",
+    /*27 CONNECT_ACTIVE_IND */ (unsigned char *) "\x03\x16\x17\x28\x01",
+    /*28 DISCONNECT_IND */ (unsigned char *) "\x03\x2c\x01",
+    /*29 */ 0,
+    /*2a MANUFACTURER_CONF */ (unsigned char *) "\x03\x2a\x15\x21\x29\x01",
+    /*2b */ 0,
+    /*2c INFO_IND */ (unsigned char *) "\x03\x26\x24\x01",
+    /*2d FACILITY_IND */ (unsigned char *) "\x03\x1f\x1d\x01",
+    /*2e */ 0,
+    /*2f CONNECT_B3_IND */ (unsigned char *) "\x03\x2b\x01",
+    /*30 CONNECT_B3_ACTIVE_IND */ (unsigned char *) "\x03\x2b\x01",
+    /*31 DISCONNECT_B3_IND */ (unsigned char *) "\x03\x2d\x2b\x01",
+    /*32 */ 0,
+    /*33 DATA_B3_IND */ (unsigned char *) "\x03\x18\x1a\x19\x20\x01",
+    /*34 RESET_B3_IND */ (unsigned char *) "\x03\x2b\x01",
+    /*35 CONNECT_B3_T90_ACTIVE_IND */ (unsigned char *) "\x03\x2b\x01",
+    /*36 */ 0,
+    /*37 */ 0,
+    /*38 CONNECT_RESP */ (unsigned char *) "\x03\x2e\x0d\x06\x08\x0a\x05\x07\x09\x01\x16\x17\x28\x04\x0c\x27\x2f\x1c\x01\x01",
+    /*39 CONNECT_ACTIVE_RESP */ (unsigned char *) "\x03\x01",
+    /*3a DISCONNECT_RESP */ (unsigned char *) "\x03\x01",
+    /*3b */ 0,
+    /*3c MANUFACTURER_IND */ (unsigned char *) "\x03\x2a\x15\x21\x29\x01",
+    /*3d */ 0,
+    /*3e INFO_RESP */ (unsigned char *) "\x03\x01",
+    /*3f FACILITY_RESP */ (unsigned char *) "\x03\x1f\x01",
+    /*40 */ 0,
+    /*41 CONNECT_B3_RESP */ (unsigned char *) "\x03\x2e\x2b\x01",
+    /*42 CONNECT_B3_ACTIVE_RESP */ (unsigned char *) "\x03\x01",
+    /*43 DISCONNECT_B3_RESP */ (unsigned char *) "\x03\x01",
+    /*44 */ 0,
+    /*45 DATA_B3_RESP */ (unsigned char *) "\x03\x19\x01",
+    /*46 RESET_B3_RESP */ (unsigned char *) "\x03\x01",
+    /*47 CONNECT_B3_T90_ACTIVE_RESP */ (unsigned char *) "\x03\x01",
+    /*48 */ 0,
+    /*49 */ 0,
+    /*4a */ 0,
+    /*4b */ 0,
+    /*4c */ 0,
+    /*4d */ 0,
+    /*4e MANUFACTURER_RESP */ (unsigned char *) "\x03\x2a\x15\x21\x29\x01",
+};
+
+/*-------------------------------------------------------*/
+
+#define byteTLcpy(x,y)        *(__u8 *)(x)=*(__u8 *)(y);
+#define wordTLcpy(x,y)        *(__u16 *)(x)=*(__u16 *)(y);
+#define dwordTLcpy(x,y)       memcpy(x,y,4);
+#define structTLcpy(x,y,l)    memcpy (x,y,l)
+#define structTLcpyovl(x,y,l) memmove (x,y,l)
+
+#define byteTRcpy(x,y)        *(__u8 *)(y)=*(__u8 *)(x);
+#define wordTRcpy(x,y)        *(__u16 *)(y)=*(__u16 *)(x);
+#define dwordTRcpy(x,y)       memcpy(y,x,4);
+#define structTRcpy(x,y,l)    memcpy (y,x,l)
+#define structTRcpyovl(x,y,l) memmove (y,x,l)
+
+/*-------------------------------------------------------*/
+static unsigned command_2_index(unsigned c, unsigned sc)
+{
+	if (c & 0x80)
+		c = 0x9 + (c & 0x0f);
+	else if (c <= 0x0f);
+	else if (c == 0x41)
+		c = 0x9 + 0x1;
+	else if (c == 0xff)
+		c = 0x00;
+	return (sc & 3) * (0x9 + 0x9) + c;
+}
+
+/*-------------------------------------------------------*/
+#define TYP (cdef[cmsg->par[cmsg->p]].typ)
+#define OFF (((__u8 *)cmsg)+cdef[cmsg->par[cmsg->p]].off)
+
+static void jumpcstruct(_cmsg * cmsg)
+{
+	unsigned layer;
+	for (cmsg->p++, layer = 1; layer;) {
+		/* $$$$$ assert (cmsg->p); */
+		cmsg->p++;
+		switch (TYP) {
+		case _CMSTRUCT:
+			layer++;
+			break;
+		case _CEND:
+			layer--;
+			break;
+		}
+	}
+}
+/*-------------------------------------------------------*/
+static void pars_2_message(_cmsg * cmsg)
+{
+
+	for (; TYP != _CEND; cmsg->p++) {
+		switch (TYP) {
+		case _CBYTE:
+			byteTLcpy(cmsg->m + cmsg->l, OFF);
+			cmsg->l++;
+			break;
+		case _CWORD:
+			wordTLcpy(cmsg->m + cmsg->l, OFF);
+			cmsg->l += 2;
+			break;
+		case _CDWORD:
+			dwordTLcpy(cmsg->m + cmsg->l, OFF);
+			cmsg->l += 4;
+			break;
+		case _CSTRUCT:
+			if (*(__u8 **) OFF == 0) {
+				*(cmsg->m + cmsg->l) = '\0';
+				cmsg->l++;
+			} else if (**(_cstruct *) OFF != 0xff) {
+				structTLcpy(cmsg->m + cmsg->l, *(_cstruct *) OFF, 1 + **(_cstruct *) OFF);
+				cmsg->l += 1 + **(_cstruct *) OFF;
+			} else {
+				_cstruct s = *(_cstruct *) OFF;
+				structTLcpy(cmsg->m + cmsg->l, s, 3 + *(__u16 *) (s + 1));
+				cmsg->l += 3 + *(__u16 *) (s + 1);
+			}
+			break;
+		case _CMSTRUCT:
+/*----- Metastruktur 0 -----*/
+			if (*(_cmstruct *) OFF == CAPI_DEFAULT) {
+				*(cmsg->m + cmsg->l) = '\0';
+				cmsg->l++;
+				jumpcstruct(cmsg);
+			}
+/*----- Metastruktur wird composed -----*/
+			else {
+				unsigned _l = cmsg->l;
+				unsigned _ls;
+				cmsg->l++;
+				cmsg->p++;
+				pars_2_message(cmsg);
+				_ls = cmsg->l - _l - 1;
+				if (_ls < 255)
+					(cmsg->m + _l)[0] = (__u8) _ls;
+				else {
+					structTLcpyovl(cmsg->m + _l + 3, cmsg->m + _l + 1, _ls);
+					(cmsg->m + _l)[0] = 0xff;
+					wordTLcpy(cmsg->m + _l + 1, &_ls);
+				}
+			}
+			break;
+		}
+	}
+}
+
+/*-------------------------------------------------------*/
+unsigned capi_cmsg2message(_cmsg * cmsg, __u8 * msg)
+{
+	cmsg->m = msg;
+	cmsg->l = 8;
+	cmsg->p = 0;
+	cmsg->par = cpars[command_2_index(cmsg->Command, cmsg->Subcommand)];
+
+	pars_2_message(cmsg);
+
+	wordTLcpy(msg + 0, &cmsg->l);
+	byteTLcpy(cmsg->m + 4, &cmsg->Command);
+	byteTLcpy(cmsg->m + 5, &cmsg->Subcommand);
+	wordTLcpy(cmsg->m + 2, &cmsg->ApplId);
+	wordTLcpy(cmsg->m + 6, &cmsg->Messagenumber);
+
+	return 0;
+}
+
+/*-------------------------------------------------------*/
+static void message_2_pars(_cmsg * cmsg)
+{
+	for (; TYP != _CEND; cmsg->p++) {
+
+		switch (TYP) {
+		case _CBYTE:
+			byteTRcpy(cmsg->m + cmsg->l, OFF);
+			cmsg->l++;
+			break;
+		case _CWORD:
+			wordTRcpy(cmsg->m + cmsg->l, OFF);
+			cmsg->l += 2;
+			break;
+		case _CDWORD:
+			dwordTRcpy(cmsg->m + cmsg->l, OFF);
+			cmsg->l += 4;
+			break;
+		case _CSTRUCT:
+			*(__u8 **) OFF = cmsg->m + cmsg->l;
+
+			if (cmsg->m[cmsg->l] != 0xff)
+				cmsg->l += 1 + cmsg->m[cmsg->l];
+			else
+				cmsg->l += 3 + *(__u16 *) (cmsg->m + cmsg->l + 1);
+			break;
+		case _CMSTRUCT:
+/*----- Metastruktur 0 -----*/
+			if (cmsg->m[cmsg->l] == '\0') {
+				*(_cmstruct *) OFF = CAPI_DEFAULT;
+				cmsg->l++;
+				jumpcstruct(cmsg);
+			} else {
+				unsigned _l = cmsg->l;
+				*(_cmstruct *) OFF = CAPI_COMPOSE;
+				cmsg->l = (cmsg->m + _l)[0] == 255 ? cmsg->l + 3 : cmsg->l + 1;
+				cmsg->p++;
+				message_2_pars(cmsg);
+			}
+			break;
+		}
+	}
+}
+
+/*-------------------------------------------------------*/
+unsigned capi_message2cmsg(_cmsg * cmsg, __u8 * msg)
+{
+	memset(cmsg, 0, sizeof(_cmsg));
+	cmsg->m = msg;
+	cmsg->l = 8;
+	cmsg->p = 0;
+	byteTRcpy(cmsg->m + 4, &cmsg->Command);
+	byteTRcpy(cmsg->m + 5, &cmsg->Subcommand);
+	cmsg->par = cpars[command_2_index(cmsg->Command, cmsg->Subcommand)];
+
+	message_2_pars(cmsg);
+
+	wordTRcpy(msg + 0, &cmsg->l);
+	wordTRcpy(cmsg->m + 2, &cmsg->ApplId);
+	wordTRcpy(cmsg->m + 6, &cmsg->Messagenumber);
+
+	return 0;
+}
+
+/*-------------------------------------------------------*/
+unsigned capi_cmsg_header(_cmsg * cmsg, __u16 _ApplId,
+			  __u8 _Command, __u8 _Subcommand,
+			  __u16 _Messagenumber, __u32 _Controller)
+{
+	memset(cmsg, 0, sizeof(_cmsg));
+	cmsg->ApplId = _ApplId;
+	cmsg->Command = _Command;
+	cmsg->Subcommand = _Subcommand;
+	cmsg->Messagenumber = _Messagenumber;
+	cmsg->adr.adrController = _Controller;
+	return 0;
+}
+
+/*-------------------------------------------------------*/
+
+static char *mnames[] =
+{
+	0,
+	"ALERT_REQ",
+	"CONNECT_REQ",
+	0,
+	"DISCONNECT_REQ",
+	"LISTEN_REQ",
+	0,
+	0,
+	"INFO_REQ",
+	"FACILITY_REQ",
+	"SELECT_B_PROTOCOL_REQ",
+	"CONNECT_B3_REQ",
+	0,
+	"DISCONNECT_B3_REQ",
+	0,
+	"DATA_B3_REQ",
+	"RESET_B3_REQ",
+	0,
+	0,
+	"ALERT_CONF",
+	"CONNECT_CONF",
+	0,
+	"DISCONNECT_CONF",
+	"LISTEN_CONF",
+	"MANUFACTURER_REQ",
+	0,
+	"INFO_CONF",
+	"FACILITY_CONF",
+	"SELECT_B_PROTOCOL_CONF",
+	"CONNECT_B3_CONF",
+	0,
+	"DISCONNECT_B3_CONF",
+	0,
+	"DATA_B3_CONF",
+	"RESET_B3_CONF",
+	0,
+	0,
+	0,
+	"CONNECT_IND",
+	"CONNECT_ACTIVE_IND",
+	"DISCONNECT_IND",
+	0,
+	"MANUFACTURER_CONF",
+	0,
+	"INFO_IND",
+	"FACILITY_IND",
+	0,
+	"CONNECT_B3_IND",
+	"CONNECT_B3_ACTIVE_IND",
+	"DISCONNECT_B3_IND",
+	0,
+	"DATA_B3_IND",
+	"RESET_B3_IND",
+	"CONNECT_B3_T90_ACTIVE_IND",
+	0,
+	0,
+	"CONNECT_RESP",
+	"CONNECT_ACTIVE_RESP",
+	"DISCONNECT_RESP",
+	0,
+	"MANUFACTURER_IND",
+	0,
+	"INFO_RESP",
+	"FACILITY_RESP",
+	0,
+	"CONNECT_B3_RESP",
+	"CONNECT_B3_ACTIVE_RESP",
+	"DISCONNECT_B3_RESP",
+	0,
+	"DATA_B3_RESP",
+	"RESET_B3_RESP",
+	"CONNECT_B3_T90_ACTIVE_RESP",
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	"MANUFACTURER_RESP"
+};
+
+char *capi_cmd2str(__u8 cmd, __u8 subcmd)
+{
+	return mnames[command_2_index(cmd, subcmd)];
+}
+
+
+/*-------------------------------------------------------*/
+/*-------------------------------------------------------*/
+
+static char *pnames[] =
+{
+    /*00 */ 0,
+    /*01 */ 0,
+    /*02 */ 0,
+    /*03 */ "Controller/PLCI/NCCI",
+    /*04 */ "AdditionalInfo",
+    /*05 */ "B1configuration",
+    /*06 */ "B1protocol",
+    /*07 */ "B2configuration",
+    /*08 */ "B2protocol",
+    /*09 */ "B3configuration",
+    /*0a */ "B3protocol",
+    /*0b */ "BC",
+    /*0c */ "BChannelinformation",
+    /*0d */ "BProtocol",
+    /*0e */ "CalledPartyNumber",
+    /*0f */ "CalledPartySubaddress",
+    /*10 */ "CallingPartyNumber",
+    /*11 */ "CallingPartySubaddress",
+    /*12 */ "CIPmask",
+    /*13 */ "CIPmask2",
+    /*14 */ "CIPValue",
+    /*15 */ "Class",
+    /*16 */ "ConnectedNumber",
+    /*17 */ "ConnectedSubaddress",
+    /*18 */ "Data",
+    /*19 */ "DataHandle",
+    /*1a */ "DataLength",
+    /*1b */ "FacilityConfirmationParameter",
+    /*1c */ "Facilitydataarray",
+    /*1d */ "FacilityIndicationParameter",
+    /*1e */ "FacilityRequestParameter",
+    /*1f */ "FacilitySelector",
+    /*20 */ "Flags",
+    /*21 */ "Function",
+    /*22 */ "HLC",
+    /*23 */ "Info",
+    /*24 */ "InfoElement",
+    /*25 */ "InfoMask",
+    /*26 */ "InfoNumber",
+    /*27 */ "Keypadfacility",
+    /*28 */ "LLC",
+    /*29 */ "ManuData",
+    /*2a */ "ManuID",
+    /*2b */ "NCPI",
+    /*2c */ "Reason",
+    /*2d */ "Reason_B3",
+    /*2e */ "Reject",
+    /*2f */ "Useruserdata"
+};
+
+
+static char buf[8192];
+static char *p = 0;
+
+#include <stdarg.h>
+
+/*-------------------------------------------------------*/
+static void bufprint(char *fmt,...)
+{
+	va_list f;
+	va_start(f, fmt);
+	vsprintf(p, fmt, f);
+	va_end(f);
+	p += strlen(p);
+}
+
+static void printstructlen(__u8 * m, unsigned len)
+{
+	unsigned hex = 0;
+	for (; len; len--, m++)
+		if (isalnum(*m) || *m == ' ') {
+			if (hex)
+				bufprint(">");
+			bufprint("%c", *m);
+			hex = 0;
+		} else {
+			if (!hex)
+				bufprint("<%02x", *m);
+			else
+				bufprint(" %02x", *m);
+			hex = 1;
+		}
+	if (hex)
+		bufprint(">");
+}
+
+static void printstruct(__u8 * m)
+{
+	unsigned len;
+	if (m[0] != 0xff) {
+		len = m[0];
+		m += 1;
+	} else {
+		len = ((__u16 *) (m + 1))[0];
+		m += 3;
+	}
+	printstructlen(m, len);
+}
+
+/*-------------------------------------------------------*/
+#define NAME (pnames[cmsg->par[cmsg->p]])
+
+static void protocol_message_2_pars(_cmsg * cmsg, int level)
+{
+	for (; TYP != _CEND; cmsg->p++) {
+		int slen = 29 + 3 - level;
+		int i;
+
+		bufprint("  ");
+		for (i = 0; i < level - 1; i++)
+			bufprint(" ");
+
+		switch (TYP) {
+		case _CBYTE:
+			bufprint("%-*s = 0x%x\n", slen, NAME, *(__u8 *) (cmsg->m + cmsg->l));
+			cmsg->l++;
+			break;
+		case _CWORD:
+			bufprint("%-*s = 0x%x\n", slen, NAME, *(__u16 *) (cmsg->m + cmsg->l));
+			cmsg->l += 2;
+			break;
+		case _CDWORD:
+			if (strcmp(NAME, "Data") == 0) {
+				bufprint("%-*s = ", slen, NAME);
+				printstructlen((__u8 *) * (__u32 *) (cmsg->m + cmsg->l),
+					       *(__u16 *) (cmsg->m + cmsg->l + sizeof(__u32)));
+				bufprint("\n");
+			} else
+				bufprint("%-*s = 0x%lx\n", slen, NAME, *(__u32 *) (cmsg->m + cmsg->l));
+			cmsg->l += 4;
+			break;
+		case _CSTRUCT:
+			bufprint("%-*s = ", slen, NAME);
+			if (cmsg->m[cmsg->l] == '\0')
+				bufprint("default");
+			else
+				printstruct(cmsg->m + cmsg->l);
+			bufprint("\n");
+			if (cmsg->m[cmsg->l] != 0xff)
+				cmsg->l += 1 + cmsg->m[cmsg->l];
+			else
+				cmsg->l += 3 + *(__u16 *) (cmsg->m + cmsg->l + 1);
+
+			break;
+
+		case _CMSTRUCT:
+/*----- Metastruktur 0 -----*/
+			if (cmsg->m[cmsg->l] == '\0') {
+				bufprint("%-*s = default\n", slen, NAME);
+				cmsg->l++;
+				jumpcstruct(cmsg);
+			} else {
+				char *name = NAME;
+				unsigned _l = cmsg->l;
+				bufprint("%-*s\n", slen, name);
+				cmsg->l = (cmsg->m + _l)[0] == 255 ? cmsg->l + 3 : cmsg->l + 1;
+				cmsg->p++;
+				protocol_message_2_pars(cmsg, level + 1);
+			}
+			break;
+		}
+	}
+}
+/*-------------------------------------------------------*/
+char *capi_message2str(__u8 * msg)
+{
+
+	_cmsg cmsg;
+	p = buf;
+	p[0] = 0;
+
+	cmsg.m = msg;
+	cmsg.l = 8;
+	cmsg.p = 0;
+	byteTRcpy(cmsg.m + 4, &cmsg.Command);
+	byteTRcpy(cmsg.m + 5, &cmsg.Subcommand);
+	cmsg.par = cpars[command_2_index(cmsg.Command, cmsg.Subcommand)];
+
+	bufprint("%-26s ID=%03d #0x%04x LEN=%04d\n",
+		 mnames[command_2_index(cmsg.Command, cmsg.Subcommand)],
+		 ((unsigned short *) msg)[1],
+		 ((unsigned short *) msg)[3],
+		 ((unsigned short *) msg)[0]);
+
+	protocol_message_2_pars(&cmsg, 1);
+	return buf;
+}
+
+char *capi_cmsg2str(_cmsg * cmsg)
+{
+	p = buf;
+	p[0] = 0;
+	cmsg->l = 8;
+	cmsg->p = 0;
+	bufprint("%s ID=%03d #0x%04x LEN=%04d\n",
+		 mnames[command_2_index(cmsg->Command, cmsg->Subcommand)],
+		 ((__u16 *) cmsg->m)[1],
+		 ((__u16 *) cmsg->m)[3],
+		 ((__u16 *) cmsg->m)[0]);
+	protocol_message_2_pars(cmsg, 1);
+	return buf;
+}
+
+
+#ifdef HAS_NEW_SYMTAB
+EXPORT_SYMBOL(capi_cmsg2message);
+EXPORT_SYMBOL(capi_message2cmsg);
+EXPORT_SYMBOL(capi_cmsg_header);
+EXPORT_SYMBOL(capi_cmd2str);
+EXPORT_SYMBOL(capi_cmsg2str);
+EXPORT_SYMBOL(capi_message2str);
+#else
+static struct symbol_table capifunc_syms =
+{
+#include <linux/symtab_begin.h>
+	X(capi_cmsg2message),
+	X(capi_message2cmsg),
+	X(capi_cmsg_header),
+	X(capi_cmd2str),
+	X(capi_cmsg2str),
+	X(capi_message2str),
+	X(capi_info2str),
+#include <linux/symtab_end.h>
+};
+#endif
+
+#ifdef MODULE
+
+int init_module(void)
+{
+#ifndef HAS_NEW_SYMTAB
+	register_symtab(&capifunc_syms);
+#endif
+	return 0;
+}
+
+void cleanup_module(void)
+{
+}
+
+#endif
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/capiutil.h linux/drivers/isdn/avmb1/capiutil.h
--- v2.0.30/linux/drivers/isdn/avmb1/capiutil.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/avmb1/capiutil.h	Mon Aug  4 17:33:59 1997
@@ -0,0 +1,501 @@
+/*
+ * $Id: capiutil.h,v 1.2 1997/05/18 09:24:19 calle Exp $
+ * 
+ * CAPI 2.0 defines & types
+ * 
+ * From CAPI 2.0 Development Kit AVM 1995 (capi20.h)
+ * Rewritten for Linux 1996 by Carsten Paeth (calle@calle.in-berlin.de)
+ * 
+ * $Log: capiutil.h,v $
+ * Revision 1.2  1997/05/18 09:24:19  calle
+ * added verbose disconnect reason reporting to avmb1.
+ * some fixes in capi20 interface.
+ * changed info messages for B1-PCI
+ *
+ * Revision 1.1  1997/03/04 21:50:35  calle
+ * Frirst version in isdn4linux
+ *
+ * Revision 2.2  1997/02/12 09:31:39  calle
+ * new version
+ *
+ * Revision 1.1  1997/01/31 10:32:20  calle
+ * Initial revision
+ *
+ * 
+ */
+#ifndef __CAPIUTIL_H__
+#define __CAPIUTIL_H__
+
+#include <asm/types.h>
+
+#define	CAPIMSG_LEN(m)		(m[0] | (m[1] << 8))
+#define	CAPIMSG_APPID(m)	(m[2] | (m[3] << 8))
+#define	CAPIMSG_COMMAND(m)	(m[4])
+#define	CAPIMSG_SUBCOMMAND(m)	(m[5])
+#define	CAPIMSG_MSGID(m)	(m[6] | (m[7] << 8))
+#define CAPIMSG_CONTROLLER(m)	(m[8] & 0x7f)
+#define CAPIMSG_CONTROL(m)	(m[8]|(m[9]<<8)|(m[10]<<16)|(m[11]<<24))
+#define CAPIMSG_NCCI(m)		CAPIMSG_CONTROL(m)
+#define CAPIMSG_DATA(m)		(m[12]|(m[13]<<8)|(m[14]<<16)|(m[15]<<24))
+#define CAPIMSG_DATALEN(m)	(m[16] | (m[17]<<8))
+
+#define	CAPIMSG_SETAPPID(m, applid) \
+	do { \
+		((__u8 *)m)[2] = (__u16)(applid) & 0xff; \
+		((__u8 *)m)[3] = ((__u16)(applid) >> 8) & 0xff; \
+	} while (0)
+
+#define	CAPIMSG_SETDATA(m, data) \
+	do { \
+		((__u8 *)m)[12] = (__u32)(data) & 0xff; \
+		((__u8 *)m)[13] = ((__u32)(data) >> 8) & 0xff; \
+		((__u8 *)m)[14] = ((__u32)(data) >> 16) & 0xff; \
+		((__u8 *)m)[15] = ((__u32)(data) >> 24) & 0xff; \
+	} while (0)
+
+/*----- basic-type definitions -----*/
+
+typedef __u8 *_cstruct;
+
+typedef enum {
+	CAPI_COMPOSE,
+	CAPI_DEFAULT
+} _cmstruct;
+
+/*
+   The _cmsg structure contains all possible CAPI 2.0 parameter.
+   All parameters are stored here first. The function CAPI_CMSG_2_MESSAGE
+   assembles the parameter and builds CAPI2.0 conform messages.
+   CAPI_MESSAGE_2_CMSG disassembles CAPI 2.0 messages and stores the
+   parameter in the _cmsg structure
+ */
+
+typedef struct {
+	/* Header */
+	__u16 ApplId;
+	__u8 Command;
+	__u8 Subcommand;
+	__u16 Messagenumber;
+
+	/* Parameter */
+	union {
+		__u32 adrController;
+		__u32 adrPLCI;
+		__u32 adrNCCI;
+	} adr;
+
+	_cmstruct AdditionalInfo;
+	_cstruct B1configuration;
+	__u16 B1protocol;
+	_cstruct B2configuration;
+	__u16 B2protocol;
+	_cstruct B3configuration;
+	__u16 B3protocol;
+	_cstruct BC;
+	_cstruct BChannelinformation;
+	_cmstruct BProtocol;
+	_cstruct CalledPartyNumber;
+	_cstruct CalledPartySubaddress;
+	_cstruct CallingPartyNumber;
+	_cstruct CallingPartySubaddress;
+	__u32 CIPmask;
+	__u32 CIPmask2;
+	__u16 CIPValue;
+	__u32 Class;
+	_cstruct ConnectedNumber;
+	_cstruct ConnectedSubaddress;
+	__u32 Data;
+	__u16 DataHandle;
+	__u16 DataLength;
+	_cstruct FacilityConfirmationParameter;
+	_cstruct Facilitydataarray;
+	_cstruct FacilityIndicationParameter;
+	_cstruct FacilityRequestParameter;
+	__u16 FacilitySelector;
+	__u16 Flags;
+	__u32 Function;
+	_cstruct HLC;
+	__u16 Info;
+	_cstruct InfoElement;
+	__u32 InfoMask;
+	__u16 InfoNumber;
+	_cstruct Keypadfacility;
+	_cstruct LLC;
+	_cstruct ManuData;
+	__u32 ManuID;
+	_cstruct NCPI;
+	__u16 Reason;
+	__u16 Reason_B3;
+	__u16 Reject;
+	_cstruct Useruserdata;
+
+	/* intern */
+	unsigned l, p;
+	unsigned char *par;
+	__u8 *m;
+
+	/* buffer to construct message */
+	__u8 buf[180];
+
+} _cmsg;
+
+/*
+ * capi_cmsg2message() assembles the parameter from _cmsg to a CAPI 2.0
+ * conform message
+ */
+unsigned capi_cmsg2message(_cmsg * cmsg, __u8 * msg);
+
+/*
+ *  capi_message2cmsg disassembles a CAPI message an writes the parameter
+ *  into _cmsg for easy access
+ */
+unsigned capi_message2cmsg(_cmsg * cmsg, __u8 * msg);
+
+/*
+ * capi_cmsg_header() fills the _cmsg structure with default values, so only
+ * parameter with non default values must be changed before sending the
+ * message.
+ */
+unsigned capi_cmsg_header(_cmsg * cmsg, __u16 _ApplId,
+			  __u8 _Command, __u8 _Subcommand,
+			  __u16 _Messagenumber, __u32 _Controller);
+
+/*
+ * capi_info2str generated a readable string for Capi2.0 reasons.
+ */
+char *capi_info2str(__u16 reason);
+
+/*-----------------------------------------------------------------------*/
+
+/*
+ * Debugging / Tracing functions
+ */
+char *capi_cmd2str(__u8 cmd, __u8 subcmd);
+char *capi_cmsg2str(_cmsg * cmsg);
+char *capi_message2str(__u8 * msg);
+
+/*-----------------------------------------------------------------------*/
+
+static inline void capi_cmsg_answer(_cmsg * cmsg)
+{
+	cmsg->Subcommand |= 0x01;
+}
+
+/*-----------------------------------------------------------------------*/
+
+static inline void capi_fill_CONNECT_B3_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber,
+					    __u32 adr,
+					    _cstruct NCPI)
+{
+	capi_cmsg_header(cmsg, ApplId, 0x82, 0x80, Messagenumber, adr);
+	cmsg->NCPI = NCPI;
+}
+
+static inline void capi_fill_FACILITY_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber,
+					  __u32 adr,
+					  __u16 FacilitySelector,
+				       _cstruct FacilityRequestParameter)
+{
+	capi_cmsg_header(cmsg, ApplId, 0x80, 0x80, Messagenumber, adr);
+	cmsg->FacilitySelector = FacilitySelector;
+	cmsg->FacilityRequestParameter = FacilityRequestParameter;
+}
+
+static inline void capi_fill_INFO_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber,
+				      __u32 adr,
+				      _cstruct CalledPartyNumber,
+				      _cstruct BChannelinformation,
+				      _cstruct Keypadfacility,
+				      _cstruct Useruserdata,
+				      _cstruct Facilitydataarray)
+{
+	capi_cmsg_header(cmsg, ApplId, 0x08, 0x80, Messagenumber, adr);
+	cmsg->CalledPartyNumber = CalledPartyNumber;
+	cmsg->BChannelinformation = BChannelinformation;
+	cmsg->Keypadfacility = Keypadfacility;
+	cmsg->Useruserdata = Useruserdata;
+	cmsg->Facilitydataarray = Facilitydataarray;
+}
+
+static inline void capi_fill_LISTEN_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber,
+					__u32 adr,
+					__u32 InfoMask,
+					__u32 CIPmask,
+					__u32 CIPmask2,
+					_cstruct CallingPartyNumber,
+					_cstruct CallingPartySubaddress)
+{
+	capi_cmsg_header(cmsg, ApplId, 0x05, 0x80, Messagenumber, adr);
+	cmsg->InfoMask = InfoMask;
+	cmsg->CIPmask = CIPmask;
+	cmsg->CIPmask2 = CIPmask2;
+	cmsg->CallingPartyNumber = CallingPartyNumber;
+	cmsg->CallingPartySubaddress = CallingPartySubaddress;
+}
+
+static inline void capi_fill_ALERT_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber,
+				       __u32 adr,
+				       _cstruct BChannelinformation,
+				       _cstruct Keypadfacility,
+				       _cstruct Useruserdata,
+				       _cstruct Facilitydataarray)
+{
+	capi_cmsg_header(cmsg, ApplId, 0x01, 0x80, Messagenumber, adr);
+	cmsg->BChannelinformation = BChannelinformation;
+	cmsg->Keypadfacility = Keypadfacility;
+	cmsg->Useruserdata = Useruserdata;
+	cmsg->Facilitydataarray = Facilitydataarray;
+}
+
+static inline void capi_fill_CONNECT_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber,
+					 __u32 adr,
+					 __u16 CIPValue,
+					 _cstruct CalledPartyNumber,
+					 _cstruct CallingPartyNumber,
+					 _cstruct CalledPartySubaddress,
+					 _cstruct CallingPartySubaddress,
+					 __u16 B1protocol,
+					 __u16 B2protocol,
+					 __u16 B3protocol,
+					 _cstruct B1configuration,
+					 _cstruct B2configuration,
+					 _cstruct B3configuration,
+					 _cstruct BC,
+					 _cstruct LLC,
+					 _cstruct HLC,
+					 _cstruct BChannelinformation,
+					 _cstruct Keypadfacility,
+					 _cstruct Useruserdata,
+					 _cstruct Facilitydataarray)
+{
+
+	capi_cmsg_header(cmsg, ApplId, 0x02, 0x80, Messagenumber, adr);
+	cmsg->CIPValue = CIPValue;
+	cmsg->CalledPartyNumber = CalledPartyNumber;
+	cmsg->CallingPartyNumber = CallingPartyNumber;
+	cmsg->CalledPartySubaddress = CalledPartySubaddress;
+	cmsg->CallingPartySubaddress = CallingPartySubaddress;
+	cmsg->B1protocol = B1protocol;
+	cmsg->B2protocol = B2protocol;
+	cmsg->B3protocol = B3protocol;
+	cmsg->B1configuration = B1configuration;
+	cmsg->B2configuration = B2configuration;
+	cmsg->B3configuration = B3configuration;
+	cmsg->BC = BC;
+	cmsg->LLC = LLC;
+	cmsg->HLC = HLC;
+	cmsg->BChannelinformation = BChannelinformation;
+	cmsg->Keypadfacility = Keypadfacility;
+	cmsg->Useruserdata = Useruserdata;
+	cmsg->Facilitydataarray = Facilitydataarray;
+}
+
+static inline void capi_fill_DATA_B3_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber,
+					 __u32 adr,
+					 __u32 Data,
+					 __u16 DataLength,
+					 __u16 DataHandle,
+					 __u16 Flags)
+{
+
+	capi_cmsg_header(cmsg, ApplId, 0x86, 0x80, Messagenumber, adr);
+	cmsg->Data = Data;
+	cmsg->DataLength = DataLength;
+	cmsg->DataHandle = DataHandle;
+	cmsg->Flags = Flags;
+}
+
+static inline void capi_fill_DISCONNECT_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber,
+					    __u32 adr,
+					    _cstruct BChannelinformation,
+					    _cstruct Keypadfacility,
+					    _cstruct Useruserdata,
+					    _cstruct Facilitydataarray)
+{
+
+	capi_cmsg_header(cmsg, ApplId, 0x04, 0x80, Messagenumber, adr);
+	cmsg->BChannelinformation = BChannelinformation;
+	cmsg->Keypadfacility = Keypadfacility;
+	cmsg->Useruserdata = Useruserdata;
+	cmsg->Facilitydataarray = Facilitydataarray;
+}
+
+static inline void capi_fill_DISCONNECT_B3_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber,
+					       __u32 adr,
+					       _cstruct NCPI)
+{
+
+	capi_cmsg_header(cmsg, ApplId, 0x84, 0x80, Messagenumber, adr);
+	cmsg->NCPI = NCPI;
+}
+
+static inline void capi_fill_MANUFACTURER_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber,
+					      __u32 adr,
+					      __u32 ManuID,
+					      __u32 Class,
+					      __u32 Function,
+					      _cstruct ManuData)
+{
+
+	capi_cmsg_header(cmsg, ApplId, 0xff, 0x80, Messagenumber, adr);
+	cmsg->ManuID = ManuID;
+	cmsg->Class = Class;
+	cmsg->Function = Function;
+	cmsg->ManuData = ManuData;
+}
+
+static inline void capi_fill_RESET_B3_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber,
+					  __u32 adr,
+					  _cstruct NCPI)
+{
+
+	capi_cmsg_header(cmsg, ApplId, 0x87, 0x80, Messagenumber, adr);
+	cmsg->NCPI = NCPI;
+}
+
+static inline void capi_fill_SELECT_B_PROTOCOL_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber,
+						   __u32 adr,
+						   __u16 B1protocol,
+						   __u16 B2protocol,
+						   __u16 B3protocol,
+						_cstruct B1configuration,
+						_cstruct B2configuration,
+						_cstruct B3configuration)
+{
+
+	capi_cmsg_header(cmsg, ApplId, 0x41, 0x80, Messagenumber, adr);
+	cmsg->B1protocol = B1protocol;
+	cmsg->B2protocol = B2protocol;
+	cmsg->B3protocol = B3protocol;
+	cmsg->B1configuration = B1configuration;
+	cmsg->B2configuration = B2configuration;
+	cmsg->B3configuration = B3configuration;
+}
+
+static inline void capi_fill_CONNECT_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber,
+					  __u32 adr,
+					  __u16 Reject,
+					  __u16 B1protocol,
+					  __u16 B2protocol,
+					  __u16 B3protocol,
+					  _cstruct B1configuration,
+					  _cstruct B2configuration,
+					  _cstruct B3configuration,
+					  _cstruct ConnectedNumber,
+					  _cstruct ConnectedSubaddress,
+					  _cstruct LLC,
+					  _cstruct BChannelinformation,
+					  _cstruct Keypadfacility,
+					  _cstruct Useruserdata,
+					  _cstruct Facilitydataarray)
+{
+	capi_cmsg_header(cmsg, ApplId, 0x02, 0x83, Messagenumber, adr);
+	cmsg->Reject = Reject;
+	cmsg->B1protocol = B1protocol;
+	cmsg->B2protocol = B2protocol;
+	cmsg->B3protocol = B3protocol;
+	cmsg->B1configuration = B1configuration;
+	cmsg->B2configuration = B2configuration;
+	cmsg->B3configuration = B3configuration;
+	cmsg->ConnectedNumber = ConnectedNumber;
+	cmsg->ConnectedSubaddress = ConnectedSubaddress;
+	cmsg->LLC = LLC;
+	cmsg->BChannelinformation = BChannelinformation;
+	cmsg->Keypadfacility = Keypadfacility;
+	cmsg->Useruserdata = Useruserdata;
+	cmsg->Facilitydataarray = Facilitydataarray;
+}
+
+static inline void capi_fill_CONNECT_ACTIVE_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber,
+						 __u32 adr)
+{
+
+	capi_cmsg_header(cmsg, ApplId, 0x03, 0x83, Messagenumber, adr);
+}
+
+static inline void capi_fill_CONNECT_B3_ACTIVE_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber,
+						    __u32 adr)
+{
+
+	capi_cmsg_header(cmsg, ApplId, 0x83, 0x83, Messagenumber, adr);
+}
+
+static inline void capi_fill_CONNECT_B3_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber,
+					     __u32 adr,
+					     __u16 Reject,
+					     _cstruct NCPI)
+{
+	capi_cmsg_header(cmsg, ApplId, 0x82, 0x83, Messagenumber, adr);
+	cmsg->Reject = Reject;
+	cmsg->NCPI = NCPI;
+}
+
+static inline void capi_fill_CONNECT_B3_T90_ACTIVE_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber,
+							__u32 adr)
+{
+
+	capi_cmsg_header(cmsg, ApplId, 0x88, 0x83, Messagenumber, adr);
+}
+
+static inline void capi_fill_DATA_B3_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber,
+					  __u32 adr,
+					  __u16 DataHandle)
+{
+
+	capi_cmsg_header(cmsg, ApplId, 0x86, 0x83, Messagenumber, adr);
+	cmsg->DataHandle = DataHandle;
+}
+
+static inline void capi_fill_DISCONNECT_B3_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber,
+						__u32 adr)
+{
+
+	capi_cmsg_header(cmsg, ApplId, 0x84, 0x83, Messagenumber, adr);
+}
+
+static inline void capi_fill_DISCONNECT_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber,
+					     __u32 adr)
+{
+
+	capi_cmsg_header(cmsg, ApplId, 0x04, 0x83, Messagenumber, adr);
+}
+
+static inline void capi_fill_FACILITY_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber,
+					   __u32 adr,
+					   __u16 FacilitySelector)
+{
+
+	capi_cmsg_header(cmsg, ApplId, 0x80, 0x83, Messagenumber, adr);
+	cmsg->FacilitySelector = FacilitySelector;
+}
+
+static inline void capi_fill_INFO_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber,
+				       __u32 adr)
+{
+
+	capi_cmsg_header(cmsg, ApplId, 0x08, 0x83, Messagenumber, adr);
+}
+
+static inline void capi_fill_MANUFACTURER_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber,
+					       __u32 adr,
+					       __u32 ManuID,
+					       __u32 Class,
+					       __u32 Function,
+					       _cstruct ManuData)
+{
+
+	capi_cmsg_header(cmsg, ApplId, 0xff, 0x83, Messagenumber, adr);
+	cmsg->ManuID = ManuID;
+	cmsg->Class = Class;
+	cmsg->Function = Function;
+	cmsg->ManuData = ManuData;
+}
+
+static inline void capi_fill_RESET_B3_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber,
+					   __u32 adr)
+{
+
+	capi_cmsg_header(cmsg, ApplId, 0x87, 0x83, Messagenumber, adr);
+}
+
+#endif				/* __CAPIUTIL_H__ */
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/compat.h linux/drivers/isdn/avmb1/compat.h
--- v2.0.30/linux/drivers/isdn/avmb1/compat.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/avmb1/compat.h	Mon Aug  4 17:33:59 1997
@@ -0,0 +1,30 @@
+/*
+ * $Id: compat.h,v 1.1 1997/03/04 21:50:36 calle Exp $
+ * 
+ * Headerfile for Compartibility between different kernel versions
+ * 
+ * (c) Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de)
+ * 
+ * $Log: compat.h,v $
+ * Revision 1.1  1997/03/04 21:50:36  calle
+ * Frirst version in isdn4linux
+ *
+ * Revision 2.2  1997/02/12 09:31:39  calle
+ * new version
+ *
+ * Revision 1.1  1997/01/31 10:32:20  calle
+ * Initial revision
+ *
+ * 
+ */
+#ifndef __COMPAT_H__
+#define __COMPAT_H__
+
+#include <linux/version.h>
+#include <linux/isdnif.h>
+
+#if LINUX_VERSION_CODE >= 0x020112	/* 2.1.18 */
+#define HAS_NEW_SYMTAB
+#endif
+
+#endif				/* __COMPAT_H__ */
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/Makefile linux/drivers/isdn/hisax/Makefile
--- v2.0.30/linux/drivers/isdn/hisax/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/Makefile	Mon Aug  4 17:33:59 1997
@@ -0,0 +1,54 @@
+L_OBJS :=
+M_OBJS :=
+O_OBJS := isdnl1.o config.o tei.o isdnl2.o isdnl3.o \
+          q931.o callc.o fsm.o
+
+# EXTRA_CFLAGS += -S
+
+ifeq ($(CONFIG_HISAX_EURO),y)
+	 O_OBJS += l3dss1.o
+endif
+
+ifeq ($(CONFIG_HISAX_NI1),y)
+       O_OBJS += l3ni1.o
+endif
+
+ifeq ($(CONFIG_HISAX_1TR6),y)
+	 O_OBJS += l3_1tr6.o
+endif
+
+ifeq ($(CONFIG_HISAX_16_0),y)
+	 O_OBJS += teles0.o
+endif
+
+ifeq ($(CONFIG_HISAX_16_3),y)
+	 O_OBJS += teles3.o
+endif
+
+ifeq ($(CONFIG_HISAX_AVM_A1),y)
+	 O_OBJS += avm_a1.o
+endif
+
+ifeq ($(CONFIG_HISAX_ELSA_PCC),y)
+	 O_OBJS += elsa.o
+endif
+
+ifeq ($(CONFIG_HISAX_ELSA_PCMCIA),y)
+	 O_OBJS += elsa.o
+endif
+
+ifeq ($(CONFIG_HISAX_IX1MICROR2),y)
+	 O_OBJS += ix1_micro.o
+endif
+
+O_TARGET :=
+ifeq ($(CONFIG_ISDN_DRV_HISAX),y)
+  O_TARGET += hisax.o
+else
+  ifeq ($(CONFIG_ISDN_DRV_HISAX),m)
+    O_TARGET += hisax.o
+    M_OBJS += hisax.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/avm_a1.c linux/drivers/isdn/hisax/avm_a1.c
--- v2.0.30/linux/drivers/isdn/hisax/avm_a1.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/avm_a1.c	Tue Aug  5 09:00:12 1997
@@ -0,0 +1,960 @@
+/* $Id: avm_a1.c,v 1.6 1997/04/13 19:54:07 keil Exp $
+
+ * avm_a1.c     low level stuff for AVM A1 (Fritz) isdn cards
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: avm_a1.c,v $
+ * Revision 1.6  1997/04/13 19:54:07  keil
+ * Change in IRQ check delay for SMP
+ *
+ * Revision 1.5  1997/04/06 22:54:10  keil
+ * Using SKB's
+ *
+ * Revision 1.4  1997/01/27 15:50:21  keil
+ * SMP proof,cosmetics
+ *
+ * Revision 1.3  1997/01/21 22:14:20  keil
+ * cleanups
+ *
+ * Revision 1.2  1996/10/27 22:07:31  keil
+ * cosmetic changes
+ *
+ * Revision 1.1  1996/10/13 20:04:49  keil
+ * Initial revision
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "siemens.h"
+#include "hisax.h"
+#include "avm_a1.h"
+#include "isdnl1.h"
+#include <linux/kernel_stat.h>
+
+extern const char *CardType[];
+const char *avm_revision = "$Revision: 1.6 $";
+
+#define byteout(addr,val) outb_p(val,addr)
+#define bytein(addr) inb_p(addr)
+
+static inline u_char
+readreg(unsigned int adr, u_char off)
+{
+	return (bytein(adr + off));
+}
+
+static inline void
+writereg(unsigned int adr, u_char off, u_char data)
+{
+	byteout(adr + off, data);
+}
+
+
+static inline void
+read_fifo(unsigned int adr, u_char * data, int size)
+{
+	insb(adr - 0x400, data, size);
+}
+
+static void
+write_fifo(unsigned int adr, u_char * data, int size)
+{
+	outsb(adr - 0x400, data, size);
+}
+
+static inline void
+waitforCEC(int adr)
+{
+	int to = 50;
+
+	while ((readreg(adr, HSCX_STAR) & 0x04) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "AVM A1: waitforCEC timeout\n");
+}
+
+
+static inline void
+waitforXFW(int adr)
+{
+	int to = 50;
+
+	while ((!(readreg(adr, HSCX_STAR) & 0x44) == 0x40) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "AVM A1: waitforXFW timeout\n");
+}
+
+static inline void
+writehscxCMDR(int adr, u_char data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	waitforCEC(adr);
+	writereg(adr, HSCX_CMDR, data);
+	restore_flags(flags);
+}
+
+/*
+ * fast interrupt here
+ */
+
+static void
+hscxreport(struct IsdnCardState *sp, int hscx)
+{
+	printk(KERN_DEBUG "HSCX %d\n", hscx);
+	printk(KERN_DEBUG "ISTA %x\n", readreg(sp->hscx[hscx], HSCX_ISTA));
+	printk(KERN_DEBUG "STAR %x\n", readreg(sp->hscx[hscx], HSCX_STAR));
+	printk(KERN_DEBUG "EXIR %x\n", readreg(sp->hscx[hscx], HSCX_EXIR));
+}
+
+void
+avm_a1_report(struct IsdnCardState *sp)
+{
+	printk(KERN_DEBUG "ISAC\n");
+	printk(KERN_DEBUG "ISTA %x\n", readreg(sp->isac, ISAC_ISTA));
+	printk(KERN_DEBUG "STAR %x\n", readreg(sp->isac, ISAC_STAR));
+	printk(KERN_DEBUG "EXIR %x\n", readreg(sp->isac, ISAC_EXIR));
+	hscxreport(sp, 0);
+	hscxreport(sp, 1);
+}
+
+/*
+ * HSCX stuff goes here
+ */
+
+static void
+hscx_empty_fifo(struct HscxState *hsp, int count)
+{
+	u_char *ptr;
+	struct IsdnCardState *sp = hsp->sp;
+	long flags;
+
+	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
+		debugl1(sp, "hscx_empty_fifo");
+
+	if (hsp->rcvidx + count > HSCX_BUFMAX) {
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "hscx_empty_fifo: incoming packet too large");
+		writehscxCMDR(sp->hscx[hsp->hscx], 0x80);
+		hsp->rcvidx = 0;
+		return;
+	}
+	ptr = hsp->rcvbuf + hsp->rcvidx;
+	hsp->rcvidx += count;
+	save_flags(flags);
+	cli();
+	read_fifo(sp->hscx[hsp->hscx], ptr, count);
+	writehscxCMDR(sp->hscx[hsp->hscx], 0x80);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_HSCX_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "hscx_empty_fifo %c cnt %d",
+			     hsp->hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+hscx_fill_fifo(struct HscxState *hsp)
+{
+	struct IsdnCardState *sp = hsp->sp;
+	int more, count;
+	u_char *ptr;
+	long flags;
+
+	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
+		debugl1(sp, "hscx_fill_fifo");
+
+	if (!hsp->tx_skb)
+		return;
+	if (hsp->tx_skb->len <= 0)
+		return;
+
+	more = (hsp->mode == 1) ? 1 : 0;
+	if (hsp->tx_skb->len > 32) {
+		more = !0;
+		count = 32;
+	} else
+		count = hsp->tx_skb->len;
+
+	waitforXFW(sp->hscx[hsp->hscx]);
+	save_flags(flags);
+	cli();
+	ptr = hsp->tx_skb->data;
+	skb_pull(hsp->tx_skb, count);
+	hsp->tx_cnt -= count;
+	hsp->count += count;
+	write_fifo(sp->hscx[hsp->hscx], ptr, count);
+	writehscxCMDR(sp->hscx[hsp->hscx], more ? 0x8 : 0xa);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_HSCX_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "hscx_fill_fifo %c cnt %d",
+			     hsp->hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static inline void
+hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx)
+{
+	u_char r;
+	struct HscxState *hsp = sp->hs + hscx;
+	struct sk_buff *skb;
+	int count;
+	char tmp[32];
+
+	if (!hsp->init)
+		return;
+
+	if (val & 0x80) {	/* RME */
+
+		r = readreg(sp->hscx[hsp->hscx], HSCX_RSTA);
+		if ((r & 0xf0) != 0xa0) {
+			if (!(r & 0x80))
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "HSCX invalid frame");
+			if ((r & 0x40) && hsp->mode)
+				if (sp->debug & L1_DEB_WARN) {
+					sprintf(tmp, "HSCX RDO mode=%d",
+						hsp->mode);
+					debugl1(sp, tmp);
+				}
+			if (!(r & 0x20))
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "HSCX CRC error");
+			writehscxCMDR(sp->hscx[hsp->hscx], 0x80);
+		} else {
+			count = readreg(sp->hscx[hsp->hscx], HSCX_RBCL) & 0x1f;
+			if (count == 0)
+				count = 32;
+			hscx_empty_fifo(hsp, count);
+			if ((count = hsp->rcvidx - 1) > 0) {
+				if (!(skb = dev_alloc_skb(count)))
+					printk(KERN_WARNING "AVM: receive out of memory\n");
+				else {
+					memcpy(skb_put(skb, count), hsp->rcvbuf, count);
+					skb_queue_tail(&hsp->rqueue, skb);
+				}
+			}
+		}
+		hsp->rcvidx = 0;
+		hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		hscx_empty_fifo(hsp, 32);
+		if (hsp->mode == 1) {
+			/* receive audio data */
+			if (!(skb = dev_alloc_skb(32)))
+				printk(KERN_WARNING "AVM: receive out of memory\n");
+			else {
+				memcpy(skb_put(skb, 32), hsp->rcvbuf, 32);
+				skb_queue_tail(&hsp->rqueue, skb);
+			}
+			hsp->rcvidx = 0;
+			hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+		}
+	}
+	if (val & 0x10) {	/* XPR */
+		if (hsp->tx_skb)
+			if (hsp->tx_skb->len) {
+				hscx_fill_fifo(hsp);
+				return;
+			} else {
+				dev_kfree_skb(hsp->tx_skb, FREE_WRITE);
+				hsp->count = 0;
+				if (hsp->st->l4.l1writewakeup)
+					hsp->st->l4.l1writewakeup(hsp->st);
+				hsp->tx_skb = NULL;
+			}
+		if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) {
+			hsp->count = 0;
+			hscx_fill_fifo(hsp);
+		} else
+			hscx_sched_event(hsp, HSCX_XMTBUFREADY);
+	}
+}
+
+/*
+ * ISAC stuff goes here
+ */
+
+static void
+isac_empty_fifo(struct IsdnCardState *sp, int count)
+{
+	u_char *ptr;
+	long flags;
+
+	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
+		if (sp->debug & L1_DEB_ISAC)
+			debugl1(sp, "isac_empty_fifo");
+
+	if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) {
+		if (sp->debug & L1_DEB_WARN) {
+			char tmp[40];
+			sprintf(tmp, "isac_empty_fifo overrun %d",
+				sp->rcvidx + count);
+			debugl1(sp, tmp);
+		}
+		writereg(sp->isac, ISAC_CMDR, 0x80);
+		sp->rcvidx = 0;
+		return;
+	}
+	ptr = sp->rcvbuf + sp->rcvidx;
+	sp->rcvidx += count;
+	save_flags(flags);
+	cli();
+	read_fifo(sp->isac, ptr, count);
+	writereg(sp->isac, ISAC_CMDR, 0x80);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_ISAC_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "isac_empty_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+isac_fill_fifo(struct IsdnCardState *sp)
+{
+	int count, more;
+	u_char *ptr;
+	long flags;
+
+	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
+		debugl1(sp, "isac_fill_fifo");
+
+	if (!sp->tx_skb)
+		return;
+
+	count = sp->tx_skb->len;
+	if (count <= 0)
+		return;
+
+	more = 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	save_flags(flags);
+	cli();
+	ptr = sp->tx_skb->data;
+	skb_pull(sp->tx_skb, count);
+	sp->tx_cnt += count;
+	write_fifo(sp->isac, ptr, count);
+	writereg(sp->isac, ISAC_CMDR, more ? 0x8 : 0xa);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_ISAC_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "isac_fill_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+ph_command(struct IsdnCardState *sp, unsigned int command)
+{
+	if (sp->debug & L1_DEB_ISAC) {
+		char tmp[32];
+		sprintf(tmp, "ph_command %d", command);
+		debugl1(sp, tmp);
+	}
+	writereg(sp->isac, ISAC_CIX0, (command << 2) | 3);
+}
+
+
+static inline void
+isac_interrupt(struct IsdnCardState *sp, u_char val)
+{
+	u_char exval;
+	struct sk_buff *skb;
+	unsigned int count;
+	char tmp[32];
+
+	if (sp->debug & L1_DEB_ISAC) {
+		sprintf(tmp, "ISAC interrupt %x", val);
+		debugl1(sp, tmp);
+	}
+	if (val & 0x80) {	/* RME */
+		exval = readreg(sp->isac, ISAC_RSTA);
+		if ((exval & 0x70) != 0x20) {
+			if (exval & 0x40)
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "ISAC RDO");
+			if (!(exval & 0x20))
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "ISAC CRC error");
+			writereg(sp->isac, ISAC_CMDR, 0x80);
+		} else {
+			count = readreg(sp->isac, ISAC_RBCL) & 0x1f;
+			if (count == 0)
+				count = 32;
+			isac_empty_fifo(sp, count);
+			if ((count = sp->rcvidx) > 0) {
+				if (!(skb = alloc_skb(count, GFP_ATOMIC)))
+					printk(KERN_WARNING "AVM: D receive out of memory\n");
+				else {
+					SET_SKB_FREE(skb);
+					memcpy(skb_put(skb, count), sp->rcvbuf, count);
+					skb_queue_tail(&sp->rq, skb);
+				}
+			}
+		}
+		sp->rcvidx = 0;
+		isac_sched_event(sp, ISAC_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		isac_empty_fifo(sp, 32);
+	}
+	if (val & 0x20) {	/* RSC */
+		/* never */
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "ISAC RSC interrupt");
+	}
+	if (val & 0x10) {	/* XPR */
+		if (sp->tx_skb)
+			if (sp->tx_skb->len) {
+				isac_fill_fifo(sp);
+				goto afterXPR;
+			} else {
+				dev_kfree_skb(sp->tx_skb, FREE_WRITE);
+				sp->tx_cnt = 0;
+				sp->tx_skb = NULL;
+			}
+		if ((sp->tx_skb = skb_dequeue(&sp->sq))) {
+			sp->tx_cnt = 0;
+			isac_fill_fifo(sp);
+		} else
+			isac_sched_event(sp, ISAC_XMTBUFREADY);
+	}
+      afterXPR:
+	if (val & 0x04) {	/* CISQ */
+		sp->ph_state = (readreg(sp->isac, ISAC_CIX0) >> 2)
+		    & 0xf;
+		if (sp->debug & L1_DEB_ISAC) {
+			sprintf(tmp, "l1state %d", sp->ph_state);
+			debugl1(sp, tmp);
+		}
+		isac_new_ph(sp);
+	}
+	if (val & 0x02) {	/* SIN */
+		/* never */
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "ISAC SIN interrupt");
+	}
+	if (val & 0x01) {	/* EXI */
+		exval = readreg(sp->isac, ISAC_EXIR);
+		if (sp->debug & L1_DEB_WARN) {
+			sprintf(tmp, "ISAC EXIR %02x", exval);
+			debugl1(sp, tmp);
+		}
+	}
+}
+
+static inline void
+hscx_int_main(struct IsdnCardState *sp, u_char val)
+{
+
+	u_char exval;
+	struct HscxState *hsp;
+	char tmp[32];
+
+
+	if (val & 0x01) {
+		hsp = sp->hs + 1;
+		exval = readreg(sp->hscx[1], HSCX_EXIR);
+		if (exval == 0x40) {
+			if (hsp->mode == 1)
+				hscx_fill_fifo(hsp);
+			else {
+				/* Here we lost an TX interrupt, so
+				   * restart transmitting the whole frame.
+				 */
+				if (hsp->tx_skb) {
+					skb_push(hsp->tx_skb, hsp->count);
+					hsp->tx_cnt += hsp->count;
+					hsp->count = 0;
+				}
+				writehscxCMDR(sp->hscx[hsp->hscx], 0x01);
+				if (sp->debug & L1_DEB_WARN) {
+					sprintf(tmp, "HSCX B EXIR %x Lost TX", exval);
+					debugl1(sp, tmp);
+				}
+			}
+		} else if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX B EXIR %x", exval);
+			debugl1(sp, tmp);
+		}
+	}
+	if (val & 0xf8) {
+		if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX B interrupt %x", val);
+			debugl1(sp, tmp);
+		}
+		hscx_interrupt(sp, val, 1);
+	}
+	if (val & 0x02) {
+		hsp = sp->hs;
+		exval = readreg(sp->hscx[0], HSCX_EXIR);
+		if (exval == 0x40) {
+			if (hsp->mode == 1)
+				hscx_fill_fifo(hsp);
+			else {
+				/* Here we lost an TX interrupt, so
+				   * restart transmitting the whole frame.
+				 */
+				if (hsp->tx_skb) {
+					skb_push(hsp->tx_skb, hsp->count);
+					hsp->tx_cnt += hsp->count;
+					hsp->count = 0;
+				}
+				writehscxCMDR(sp->hscx[hsp->hscx], 0x01);
+				if (sp->debug & L1_DEB_WARN) {
+					sprintf(tmp, "HSCX A EXIR %x Lost TX", exval);
+					debugl1(sp, tmp);
+				}
+			}
+		} else if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX A EXIR %x", exval);
+			debugl1(sp, tmp);
+		}
+	}
+	if (val & 0x04) {
+		exval = readreg(sp->hscx[0], HSCX_ISTA);
+		if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX A interrupt %x", exval);
+			debugl1(sp, tmp);
+		}
+		hscx_interrupt(sp, exval, 0);
+	}
+}
+
+static void
+avm_a1_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *sp;
+	u_char val, sval, stat = 0;
+	char tmp[32];
+
+	sp = (struct IsdnCardState *) irq2dev_map[intno];
+
+	if (!sp) {
+		printk(KERN_WARNING "AVM A1: Spurious interrupt!\n");
+		return;
+	}
+	while (((sval = bytein(sp->cfg_reg)) & 0xf) != 0x7) {
+		if (!(sval & AVM_A1_STAT_TIMER)) {
+			byteout(sp->cfg_reg, 0x14);
+			byteout(sp->cfg_reg, 0x18);
+			sval = bytein(sp->cfg_reg);
+		} else if (sp->debug & L1_DEB_INTSTAT) {
+			sprintf(tmp, "avm IntStatus %x", sval);
+			debugl1(sp, tmp);
+		}
+		if (!(sval & AVM_A1_STAT_HSCX)) {
+			val = readreg(sp->hscx[1], HSCX_ISTA);
+			if (val) {
+				hscx_int_main(sp, val);
+				stat |= 1;
+			}
+		}
+		if (!(sval & AVM_A1_STAT_ISAC)) {
+			val = readreg(sp->isac, ISAC_ISTA);
+			if (val) {
+				isac_interrupt(sp, val);
+				stat |= 2;
+			}
+		}
+	}
+	if (stat & 1) {
+		writereg(sp->hscx[0], HSCX_MASK, 0xFF);
+		writereg(sp->hscx[1], HSCX_MASK, 0xFF);
+		writereg(sp->hscx[0], HSCX_MASK, 0x0);
+		writereg(sp->hscx[1], HSCX_MASK, 0x0);
+	}
+	if (stat & 2) {
+		writereg(sp->isac, ISAC_MASK, 0xFF);
+		writereg(sp->isac, ISAC_MASK, 0x0);
+	}
+}
+
+
+static void
+initisac(struct IsdnCardState *sp)
+{
+	unsigned int adr = sp->isac;
+
+	/* 16.3 IOM 2 Mode */
+	writereg(adr, ISAC_MASK, 0xff);
+	writereg(adr, ISAC_ADF2, 0x80);
+	writereg(adr, ISAC_SQXR, 0x2f);
+	writereg(adr, ISAC_SPCR, 0x0);
+	writereg(adr, ISAC_ADF1, 0x2);
+	writereg(adr, ISAC_STCR, 0x70);
+	writereg(adr, ISAC_MODE, 0xc9);
+	writereg(adr, ISAC_TIMR, 0x0);
+	writereg(adr, ISAC_ADF1, 0x0);
+	writereg(adr, ISAC_CMDR, 0x41);
+	writereg(adr, ISAC_CIX0, (1 << 2) | 3);
+	writereg(adr, ISAC_MASK, 0xff);
+	writereg(adr, ISAC_MASK, 0x0);
+}
+
+static void
+modehscx(struct HscxState *hs, int mode, int ichan)
+{
+	struct IsdnCardState *sp = hs->sp;
+	int hscx = hs->hscx;
+
+	if (sp->debug & L1_DEB_HSCX) {
+		char tmp[40];
+		sprintf(tmp, "hscx %c mode %d ichan %d",
+			'A' + hscx, mode, ichan);
+		debugl1(sp, tmp);
+	}
+	hs->mode = mode;
+	writereg(sp->hscx[hscx], HSCX_CCR1, 0x85);
+	writereg(sp->hscx[hscx], HSCX_XAD1, 0xFF);
+	writereg(sp->hscx[hscx], HSCX_XAD2, 0xFF);
+	writereg(sp->hscx[hscx], HSCX_RAH2, 0xFF);
+	writereg(sp->hscx[hscx], HSCX_XBCH, 0x0);
+	writereg(sp->hscx[hscx], HSCX_RLCR, 0x0);
+
+	switch (mode) {
+		case (0):
+			writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
+			writereg(sp->hscx[hscx], HSCX_TSAX, 0xff);
+			writereg(sp->hscx[hscx], HSCX_TSAR, 0xff);
+			writereg(sp->hscx[hscx], HSCX_XCCR, 7);
+			writereg(sp->hscx[hscx], HSCX_RCCR, 7);
+			writereg(sp->hscx[hscx], HSCX_MODE, 0x84);
+			break;
+		case (1):
+			if (ichan == 0) {
+				writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
+				writereg(sp->hscx[hscx], HSCX_TSAX, 0x2f);
+				writereg(sp->hscx[hscx], HSCX_TSAR, 0x2f);
+				writereg(sp->hscx[hscx], HSCX_XCCR, 7);
+				writereg(sp->hscx[hscx], HSCX_RCCR, 7);
+			} else {
+				writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
+				writereg(sp->hscx[hscx], HSCX_TSAX, 0x3);
+				writereg(sp->hscx[hscx], HSCX_TSAR, 0x3);
+				writereg(sp->hscx[hscx], HSCX_XCCR, 7);
+				writereg(sp->hscx[hscx], HSCX_RCCR, 7);
+			}
+			writereg(sp->hscx[hscx], HSCX_MODE, 0xe4);
+			writereg(sp->hscx[hscx], HSCX_CMDR, 0x41);
+			break;
+		case (2):
+			if (ichan == 0) {
+				writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
+				writereg(sp->hscx[hscx], HSCX_TSAX, 0x2f);
+				writereg(sp->hscx[hscx], HSCX_TSAR, 0x2f);
+				writereg(sp->hscx[hscx], HSCX_XCCR, 7);
+				writereg(sp->hscx[hscx], HSCX_RCCR, 7);
+			} else {
+				writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
+				writereg(sp->hscx[hscx], HSCX_TSAX, 0x3);
+				writereg(sp->hscx[hscx], HSCX_TSAR, 0x3);
+				writereg(sp->hscx[hscx], HSCX_XCCR, 7);
+				writereg(sp->hscx[hscx], HSCX_RCCR, 7);
+			}
+			writereg(sp->hscx[hscx], HSCX_MODE, 0x8c);
+			writereg(sp->hscx[hscx], HSCX_CMDR, 0x41);
+			break;
+	}
+	writereg(sp->hscx[hscx], HSCX_ISTA, 0x00);
+}
+
+inline static void
+release_ioregs(struct IsdnCard *card, int mask)
+{
+	release_region(card->sp->cfg_reg, 8);
+	if (mask & 1)
+		release_region(card->sp->isac, 32);
+	if (mask & 2)
+		release_region(card->sp->isac - 0x400, 1);
+	if (mask & 4)
+		release_region(card->sp->hscx[0], 32);
+	if (mask & 8)
+		release_region(card->sp->hscx[0] - 0x400, 1);
+	if (mask & 0x10)
+		release_region(card->sp->hscx[1], 32);
+	if (mask & 0x20)
+		release_region(card->sp->hscx[1] - 0x400, 1);
+}
+
+void
+release_io_avm_a1(struct IsdnCard *card)
+{
+	release_ioregs(card, 0x3f);
+}
+
+static void
+clear_pending_ints(struct IsdnCardState *sp)
+{
+	int val;
+	char tmp[64];
+
+	val = readreg(sp->hscx[1], HSCX_ISTA);
+	sprintf(tmp, "HSCX B ISTA %x", val);
+	debugl1(sp, tmp);
+	if (val & 0x01) {
+		val = readreg(sp->hscx[1], HSCX_EXIR);
+		sprintf(tmp, "HSCX B EXIR %x", val);
+		debugl1(sp, tmp);
+	} else if (val & 0x02) {
+		val = readreg(sp->hscx[0], HSCX_EXIR);
+		sprintf(tmp, "HSCX A EXIR %x", val);
+		debugl1(sp, tmp);
+	}
+	val = readreg(sp->hscx[0], HSCX_ISTA);
+	sprintf(tmp, "HSCX A ISTA %x", val);
+	debugl1(sp, tmp);
+	val = readreg(sp->hscx[1], HSCX_STAR);
+	sprintf(tmp, "HSCX B STAR %x", val);
+	debugl1(sp, tmp);
+	val = readreg(sp->hscx[0], HSCX_STAR);
+	sprintf(tmp, "HSCX A STAR %x", val);
+	debugl1(sp, tmp);
+	val = readreg(sp->isac, ISAC_STAR);
+	sprintf(tmp, "ISAC STAR %x", val);
+	debugl1(sp, tmp);
+	val = readreg(sp->isac, ISAC_MODE);
+	sprintf(tmp, "ISAC MODE %x", val);
+	debugl1(sp, tmp);
+	val = readreg(sp->isac, ISAC_ADF2);
+	sprintf(tmp, "ISAC ADF2 %x", val);
+	debugl1(sp, tmp);
+	val = readreg(sp->isac, ISAC_ISTA);
+	sprintf(tmp, "ISAC ISTA %x", val);
+	debugl1(sp, tmp);
+	if (val & 0x01) {
+		val = readreg(sp->isac, ISAC_EXIR);
+		sprintf(tmp, "ISAC EXIR %x", val);
+		debugl1(sp, tmp);
+	} else if (val & 0x04) {
+		val = readreg(sp->isac, ISAC_CIR0);
+		sprintf(tmp, "ISAC CIR0 %x", val);
+		debugl1(sp, tmp);
+	}
+	writereg(sp->isac, ISAC_MASK, 0);
+	writereg(sp->isac, ISAC_CMDR, 0x41);
+}
+
+int
+initavm_a1(struct IsdnCardState *sp)
+{
+	int ret;
+	int loop = 0;
+	char tmp[40];
+
+	sp->counter = kstat.interrupts[sp->irq];
+	sprintf(tmp, "IRQ %d count %d", sp->irq, sp->counter);
+	debugl1(sp, tmp);
+	clear_pending_ints(sp);
+	ret = get_irq(sp->cardnr, &avm_a1_interrupt);
+	if (ret) {
+		initisac(sp);
+		sp->modehscx(sp->hs, 0, 0);
+		sp->modehscx(sp->hs + 1, 0, 0);
+		while (loop++ < 10) {
+			/* At least 1-3 irqs must happen
+			 * (one from HSCX A, one from HSCX B, 3rd from ISAC)
+			 */
+			if (kstat.interrupts[sp->irq] > sp->counter)
+				break;
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + 1;
+			schedule();
+		}
+		sprintf(tmp, "IRQ %d count %d", sp->irq,
+			kstat.interrupts[sp->irq]);
+		debugl1(sp, tmp);
+		if (kstat.interrupts[sp->irq] == sp->counter) {
+			printk(KERN_WARNING
+			       "AVM A1: IRQ(%d) getting no interrupts during init\n",
+			       sp->irq);
+			irq2dev_map[sp->irq] = NULL;
+			free_irq(sp->irq, NULL);
+			return (0);
+		}
+	}
+	return (ret);
+}
+
+int
+setup_avm_a1(struct IsdnCard *card)
+{
+	u_char val, verA, verB;
+	struct IsdnCardState *sp = card->sp;
+	long flags;
+	char tmp[64];
+
+	strcpy(tmp, avm_revision);
+	printk(KERN_NOTICE "HiSax: AVM driver Rev. %s\n", HiSax_getrev(tmp));
+	if (sp->typ != ISDN_CTYPE_A1)
+		return (0);
+
+	sp->cfg_reg = card->para[1] + 0x1800;
+	sp->isac = card->para[1] + 0x1400;
+	sp->hscx[0] = card->para[1] + 0x400;
+	sp->hscx[1] = card->para[1] + 0xc00;
+	sp->irq = card->para[0];
+	if (check_region((sp->cfg_reg), 8)) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       sp->cfg_reg,
+		       sp->cfg_reg + 8);
+		return (0);
+	} else {
+		request_region(sp->cfg_reg, 8, "avm cfg");
+	}
+	if (check_region((sp->isac), 32)) {
+		printk(KERN_WARNING
+		       "HiSax: %s isac ports %x-%x already in use\n",
+		       CardType[sp->typ],
+		       sp->isac,
+		       sp->isac + 32);
+		release_ioregs(card, 0);
+		return (0);
+	} else {
+		request_region(sp->isac, 32, "HiSax isac");
+	}
+	if (check_region((sp->isac - 0x400), 1)) {
+		printk(KERN_WARNING
+		       "HiSax: %s isac fifo port %x already in use\n",
+		       CardType[sp->typ],
+		       sp->isac - 0x400);
+		release_ioregs(card, 1);
+		return (0);
+	} else {
+		request_region(sp->isac - 0x400, 1, "HiSax isac fifo");
+	}
+	if (check_region((sp->hscx[0]), 32)) {
+		printk(KERN_WARNING
+		       "HiSax: %s hscx A ports %x-%x already in use\n",
+		       CardType[sp->typ],
+		       sp->hscx[0],
+		       sp->hscx[0] + 32);
+		release_ioregs(card, 3);
+		return (0);
+	} else {
+		request_region(sp->hscx[0], 32, "HiSax hscx A");
+	}
+	if (check_region((sp->hscx[0] - 0x400), 1)) {
+		printk(KERN_WARNING
+		       "HiSax: %s hscx A fifo port %x already in use\n",
+		       CardType[sp->typ],
+		       sp->hscx[0] - 0x400);
+		release_ioregs(card, 7);
+		return (0);
+	} else {
+		request_region(sp->hscx[0] - 0x400, 1, "HiSax hscx A fifo");
+	}
+	if (check_region((sp->hscx[1]), 32)) {
+		printk(KERN_WARNING
+		       "HiSax: %s hscx B ports %x-%x already in use\n",
+		       CardType[sp->typ],
+		       sp->hscx[1],
+		       sp->hscx[1] + 32);
+		release_ioregs(card, 0xf);
+		return (0);
+	} else {
+		request_region(sp->hscx[1], 32, "HiSax hscx B");
+	}
+	if (check_region((sp->hscx[1] - 0x400), 1)) {
+		printk(KERN_WARNING
+		       "HiSax: %s hscx B fifo port %x already in use\n",
+		       CardType[sp->typ],
+		       sp->hscx[1] - 0x400);
+		release_ioregs(card, 0x1f);
+		return (0);
+	} else {
+		request_region(sp->hscx[1] - 0x400, 1, "HiSax hscx B fifo");
+	}
+	save_flags(flags);
+	byteout(sp->cfg_reg, 0x0);
+	sti();
+	HZDELAY(HZ / 5 + 1);
+	byteout(sp->cfg_reg, 0x1);
+	HZDELAY(HZ / 5 + 1);
+	byteout(sp->cfg_reg, 0x0);
+	HZDELAY(HZ / 5 + 1);
+	val = sp->irq;
+	if (val == 9)
+		val = 2;
+	byteout(sp->cfg_reg + 1, val);
+	HZDELAY(HZ / 5 + 1);
+	byteout(sp->cfg_reg, 0x0);
+	HZDELAY(HZ / 5 + 1);
+	restore_flags(flags);
+
+	val = bytein(sp->cfg_reg);
+	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+	       sp->cfg_reg, val);
+	val = bytein(sp->cfg_reg + 3);
+	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+	       sp->cfg_reg + 3, val);
+	val = bytein(sp->cfg_reg + 2);
+	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+	       sp->cfg_reg + 2, val);
+	byteout(sp->cfg_reg, 0x14);
+	byteout(sp->cfg_reg, 0x18);
+	val = bytein(sp->cfg_reg);
+	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+	       sp->cfg_reg, val);
+
+	printk(KERN_NOTICE
+	       "HiSax: %s config irq:%d cfg:%x\n",
+	       CardType[sp->typ], sp->irq,
+	       sp->cfg_reg);
+	printk(KERN_NOTICE
+	       "HiSax: isac:%x/%x\n",
+	       sp->isac, sp->isac - 0x400);
+	printk(KERN_NOTICE
+	       "HiSax: hscx A:%x/%x  hscx B:%x/%x\n",
+	       sp->hscx[0], sp->hscx[0] - 0x400,
+	       sp->hscx[1], sp->hscx[1] - 0x400);
+	verA = readreg(sp->hscx[0], HSCX_VSTR) & 0xf;
+	verB = readreg(sp->hscx[1], HSCX_VSTR) & 0xf;
+	printk(KERN_INFO "AVM A1: HSCX version A: %s  B: %s\n",
+	       HscxVersion(verA), HscxVersion(verB));
+	val = readreg(sp->isac, ISAC_RBCH);
+	printk(KERN_INFO "AVM A1: ISAC %s\n",
+	       ISACVersion(val));
+	if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) {
+		printk(KERN_WARNING
+		       "AVM A1: wrong HSCX versions check IO address\n");
+		release_io_avm_a1(card);
+		return (0);
+	}
+	sp->modehscx = &modehscx;
+	sp->ph_command = &ph_command;
+	sp->hscx_fill_fifo = &hscx_fill_fifo;
+	sp->isac_fill_fifo = &isac_fill_fifo;
+	return (1);
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/avm_a1.h linux/drivers/isdn/hisax/avm_a1.h
--- v2.0.30/linux/drivers/isdn/hisax/avm_a1.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/avm_a1.h	Mon Aug  4 17:34:00 1997
@@ -0,0 +1,25 @@
+/* $Id: avm_a1.h,v 1.2 1997/01/21 22:14:36 keil Exp $
+ *
+ * avm_a1.h   Header for AVM A1 (Fritz) ISDN card
+ *
+ * Author	Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: avm_a1.h,v $
+ * Revision 1.2  1997/01/21 22:14:36  keil
+ * cleanups
+ *
+ * Revision 1.1  1996/10/12 21:42:40  keil
+ * Initial revision
+ *
+ *
+*/
+
+#define	 AVM_A1_STAT_ISAC	0x01
+#define	 AVM_A1_STAT_HSCX	0x02
+#define	 AVM_A1_STAT_TIMER	0x04
+
+extern	void avm_a1_report(struct IsdnCardState *sp);
+extern  void release_io_avm_a1(struct IsdnCard *card);
+extern	int  setup_avm_a1(struct IsdnCard *card);
+extern  int  initavm_a1(struct IsdnCardState *sp);
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/callc.c linux/drivers/isdn/hisax/callc.c
--- v2.0.30/linux/drivers/isdn/hisax/callc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/callc.c	Mon Aug  4 17:34:00 1997
@@ -0,0 +1,1975 @@
+/* $Id: callc.c,v 1.30 1997/05/29 10:40:43 keil Exp $
+
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *              based on the teles driver from Jan den Ouden
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ * $Log: callc.c,v $
+ * Revision 1.30  1997/05/29 10:40:43  keil
+ * chanp->impair was uninitialised
+ *
+ * Revision 1.29  1997/04/23 20:09:49  fritz
+ * Removed tmp, used by removed debugging code.
+ *
+ * Revision 1.28  1997/04/21 13:42:25  keil
+ * Remove unneeded debug
+ *
+ * Revision 1.27  1997/04/16 14:21:01  keil
+ * remove unused variable
+ *
+ * Revision 1.26  1997/04/13 19:55:21  keil
+ * Changes in debugging code
+ *
+ * Revision 1.25  1997/04/06 22:54:08  keil
+ * Using SKB's
+ *
+ * Revision 1.24  1997/03/05 11:28:03  keil
+ * fixed undefined l2tei procedure
+ * a layer1 release delete now the drel timer
+ *
+ * Revision 1.23  1997/03/04 23:07:42  keil
+ * bugfix dial parameter
+ *
+ * Revision 1.22  1997/02/27 13:51:55  keil
+ * Reset B-channel (dlc) statemachine in every release
+ *
+ * Revision 1.21  1997/02/19 09:24:27  keil
+ * Bugfix: Hangup to LL if a ttyI rings
+ *
+ * Revision 1.20  1997/02/17 00:32:47  keil
+ * Bugfix: No Busy reported to LL
+ *
+ * Revision 1.19  1997/02/14 12:23:10  fritz
+ * Added support for new insmod parameter handling.
+ *
+ * Revision 1.18  1997/02/11 01:36:58  keil
+ * Changed setup-interface (incoming and outgoing), cause reporting
+ *
+ * Revision 1.17  1997/02/09 00:23:10  keil
+ * new interface handling, one interface per card
+ * some changes in debug and leased line mode
+ *
+ * Revision 1.16  1997/01/27 23:17:03  keil
+ * delete timers while unloading
+ *
+ * Revision 1.15  1997/01/27 16:00:38  keil
+ * D-channel shutdown delay; improved callback
+ *
+ * Revision 1.14  1997/01/21 22:16:39  keil
+ * new statemachine; leased line support; cleanup for 2.0
+ *
+ * Revision 1.13  1996/12/08 19:51:17  keil
+ * bugfixes from Pekka Sarnila
+ *
+ * Revision 1.12  1996/11/26 20:20:03  keil
+ * fixed warning while compile
+ *
+ * Revision 1.11  1996/11/26 18:43:17  keil
+ * change ioctl 555 --> 55 (555 didn't work)
+ *
+ * Revision 1.10  1996/11/26 18:06:07  keil
+ * fixed missing break statement,ioctl 555 reset modcount
+ *
+ * Revision 1.9  1996/11/18 20:23:19  keil
+ * log writebuf channel not open changed
+ *
+ * Revision 1.8  1996/11/06 17:43:17  keil
+ * more changes for 2.1.X;block fixed ST_PRO_W
+ *
+ * Revision 1.7  1996/11/06 15:13:51  keil
+ * typo 0x64 --->64 in debug code
+ *
+ * Revision 1.6  1996/11/05 19:40:33  keil
+ * X.75 windowsize
+ *
+ * Revision 1.5  1996/10/30 10:11:06  keil
+ * debugging LOCK changed;ST_REL_W EV_HANGUP added
+ *
+ * Revision 1.4  1996/10/27 22:20:16  keil
+ * alerting bugfixes
+ * no static b-channel<->channel mapping
+ *
+ * Revision 1.2  1996/10/16 21:29:45  keil
+ * compile bug as "not module"
+ * Callback with euro
+ *
+ * Revision 1.1  1996/10/13 20:04:50  keil
+ * Initial revision
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+
+#ifdef MODULE
+#if (LINUX_VERSION_CODE < 0x020111)
+extern long mod_use_count_;
+#define MOD_USE_COUNT mod_use_count_
+#else
+#define MOD_USE_COUNT ((&__this_module)->usecount)
+#endif
+#endif				/* MODULE */
+
+const char *l4_revision = "$Revision: 1.30 $";
+
+extern struct IsdnCard cards[];
+extern int nrcards;
+extern void HiSax_mod_dec_use_count(void);
+extern void HiSax_mod_inc_use_count(void);
+
+static int init_ds(struct Channel *chanp, int incoming);
+static void release_ds(struct Channel *chanp);
+
+static struct Fsm callcfsm =
+{NULL, 0, 0};
+static struct Fsm lcfsm =
+{NULL, 0, 0};
+
+static int chancount = 0;
+
+/* Flags for remembering action done in l4 */
+
+#define  FLG_START_D	0x0001
+#define  FLG_ESTAB_D	0x0002
+#define  FLG_CALL_SEND	0x0004
+#define  FLG_CALL_REC   0x0008
+#define  FLG_CALL_ALERT	0x0010
+#define  FLG_START_B	0x0020
+#define  FLG_CONNECT_B	0x0040
+#define  FLG_LL_DCONN	0x0080
+#define  FLG_LL_BCONN	0x0100
+#define  FLG_DISC_SEND	0x0200
+#define  FLG_DISC_REC	0x0400
+#define  FLG_REL_REC	0x0800
+
+#define  SETBIT(flg, item)  flg |= item
+#define  RESBIT(flg, item)  flg &= (~item)
+
+/*
+ * Because of callback it's a good idea to delay the shutdown of the d-channel
+ */
+#define	DREL_TIMER_VALUE 30000
+
+/*
+ * Find card with given driverId
+ */
+static inline struct IsdnCardState
+*
+hisax_findcard(int driverid)
+{
+	int i;
+
+	for (i = 0; i < nrcards; i++)
+		if (cards[i].sp)
+			if (cards[i].sp->myid == driverid)
+				return (cards[i].sp);
+	return (struct IsdnCardState *) 0;
+}
+
+static void
+link_debug(struct Channel *chanp, char *s, int direction)
+{
+	char tmp[100], tm[32];
+
+	jiftime(tm, jiffies);
+	sprintf(tmp, "%s Channel %d %s %s\n", tm, chanp->chan,
+		direction ? "LL->HL" : "HL->LL", s);
+	HiSax_putstatus(chanp->sp, tmp);
+}
+
+
+enum {
+	ST_NULL,		/*  0 inactive */
+	ST_OUT_WAIT_D,		/*  1 outgoing, awaiting d-channel establishment */
+	ST_IN_WAIT_D,		/*  2 incoming, awaiting d-channel establishment */
+	ST_OUT_DIAL,		/*  3 outgoing, SETUP send; awaiting confirm */
+	ST_IN_WAIT_LL,		/*  4 incoming call received; wait for LL confirm */
+	ST_IN_ALERT_SEND,	/*  5 incoming call received; ALERT send */
+	ST_IN_WAIT_CONN_ACK,	/*  6 incoming CONNECT send; awaiting CONN_ACK */
+	ST_WAIT_BCONN,		/*  7 CONNECT/CONN_ACK received, awaiting b-channel prot. estbl. */
+	ST_ACTIVE,		/*  8 active, b channel prot. established */
+	ST_WAIT_BRELEASE,	/*  9 call clear. (initiator), awaiting b channel prot. rel. */
+	ST_WAIT_BREL_DISC,	/* 10 call clear. (receiver), DISCONNECT req. received */
+	ST_WAIT_DCOMMAND,	/* 11 call clear. (receiver), awaiting DCHANNEL message */
+	ST_WAIT_DRELEASE,	/* 12 DISCONNECT sent, awaiting RELEASE */
+	ST_WAIT_D_REL_CNF,	/* 13 RELEASE sent, awaiting RELEASE confirm */
+	ST_WAIT_DSHUTDOWN,	/*  14 awaiting d-channel shutdown */
+};
+
+#define STATE_COUNT (ST_WAIT_DSHUTDOWN +1)
+
+static char *strState[] =
+{
+	"ST_NULL",
+	"ST_OUT_WAIT_D",
+	"ST_IN_WAIT_D",
+	"ST_OUT_DIAL",
+	"ST_IN_WAIT_LL",
+	"ST_IN_ALERT_SEND",
+	"ST_IN_WAIT_CONN_ACK",
+	"ST_WAIT_BCONN",
+	"ST_ACTIVE",
+	"ST_WAIT_BRELEASE",
+	"ST_WAIT_BREL_DISC",
+	"ST_WAIT_DCOMMAND",
+	"ST_WAIT_DRELEASE",
+	"ST_WAIT_D_REL_CNF",
+	"ST_WAIT_DSHUTDOWN",
+};
+
+enum {
+	EV_DIAL,		/*  0 */
+	EV_SETUP_CNF,		/*  1 */
+	EV_ACCEPTB,		/*  2 */
+	EV_DISCONNECT_IND,	/*  3 */
+	EV_RELEASE_CNF,		/*  4 */
+	EV_DLEST,		/*  5 */
+	EV_DLRL,		/*  6 */
+	EV_SETUP_IND,		/*  7 */
+	EV_RELEASE_IND,		/*  8 */
+	EV_ACCEPTD,		/*  9 */
+	EV_SETUP_CMPL_IND,	/* 10 */
+	EV_BC_EST,		/* 11 */
+	EV_WRITEBUF,		/* 12 */
+	EV_DATAIN,		/* 13 */
+	EV_HANGUP,		/* 14 */
+	EV_BC_REL,		/* 15 */
+	EV_CINF,		/* 16 */
+	EV_SUSPEND,		/* 17 */
+	EV_RESUME,		/* 18 */
+	EV_SHUTDOWN_D,		/* 19 */
+	EV_NOSETUP_RSP,		/* 20 */
+	EV_SETUP_ERR,		/* 21 */
+	EV_CONNECT_ERR,		/* 22 */
+	EV_RELEASE_ERR,		/* 23 */
+};
+
+#define EVENT_COUNT (EV_RELEASE_ERR +1)
+
+static char *strEvent[] =
+{
+	"EV_DIAL",
+	"EV_SETUP_CNF",
+	"EV_ACCEPTB",
+	"EV_DISCONNECT_IND",
+	"EV_RELEASE_CNF",
+	"EV_DLEST",
+	"EV_DLRL",
+	"EV_SETUP_IND",
+	"EV_RELEASE_IND",
+	"EV_ACCEPTD",
+	"EV_SETUP_CMPL_IND",
+	"EV_BC_EST",
+	"EV_WRITEBUF",
+	"EV_DATAIN",
+	"EV_HANGUP",
+	"EV_BC_REL",
+	"EV_CINF",
+	"EV_SUSPEND",
+	"EV_RESUME",
+	"EV_SHUTDOWN_D",
+	"EV_NOSETUP_RSP",
+	"EV_SETUP_ERR",
+	"EV_CONNECT_ERR",
+	"EV_RELEASE_ERR",
+};
+
+enum {
+	ST_LC_NULL,
+	ST_LC_ACTIVATE_WAIT,
+	ST_LC_DELAY,
+	ST_LC_ESTABLISH_WAIT,
+	ST_LC_CONNECTED,
+	ST_LC_FLUSH_WAIT,
+	ST_LC_FLUSH_DELAY,
+	ST_LC_RELEASE_WAIT,
+};
+
+#define LC_STATE_COUNT (ST_LC_RELEASE_WAIT+1)
+
+static char *strLcState[] =
+{
+	"ST_LC_NULL",
+	"ST_LC_ACTIVATE_WAIT",
+	"ST_LC_DELAY",
+	"ST_LC_ESTABLISH_WAIT",
+	"ST_LC_CONNECTED",
+	"ST_LC_FLUSH_WAIT",
+	"ST_LC_FLUSH_DELAY",
+	"ST_LC_RELEASE_WAIT",
+};
+
+enum {
+	EV_LC_ESTABLISH,
+	EV_LC_PH_ACTIVATE,
+	EV_LC_PH_DEACTIVATE,
+	EV_LC_DL_ESTABLISH,
+	EV_LC_TIMER,
+	EV_LC_DL_FLUSH,
+	EV_LC_DL_RELEASE,
+	EV_LC_FLUSH,
+	EV_LC_RELEASE,
+};
+
+#define LC_EVENT_COUNT (EV_LC_RELEASE+1)
+
+static char *strLcEvent[] =
+{
+	"EV_LC_ESTABLISH",
+	"EV_LC_PH_ACTIVATE",
+	"EV_LC_PH_DEACTIVATE",
+	"EV_LC_DL_ESTABLISH",
+	"EV_LC_TIMER",
+	"EV_LC_DL_FLUSH",
+	"EV_LC_DL_RELEASE",
+	"EV_LC_FLUSH",
+	"EV_LC_RELEASE",
+};
+
+#define LC_D  0
+#define LC_B  1
+
+static inline void
+l4_deliver_cause(struct Channel *chanp)
+{
+	isdn_ctrl ic;
+
+	if (chanp->para.cause < 0)
+		return;
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_CAUSE;
+	ic.arg = chanp->chan;
+	if (chanp->sp->protocol == ISDN_PTYPE_EURO)
+		sprintf(ic.parm.num, "E%02X%02X", chanp->para.loc & 0x7f,
+			chanp->para.cause & 0x7f);
+	else
+		sprintf(ic.parm.num, "%02X%02X", chanp->para.loc & 0x7f,
+			chanp->para.cause & 0x7f);
+	chanp->sp->iif.statcallb(&ic);
+}
+
+/*
+ * Dial out
+ */
+static void
+l4_prep_dialout(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_OUT_WAIT_D);
+	FsmDelTimer(&chanp->drel_timer, 60);
+	FsmDelTimer(&chanp->dial_timer, 73);
+
+	chanp->l2_active_protocol = chanp->l2_protocol;
+	chanp->incoming = 0;
+	chanp->lc_b.l2_start = !0;
+	switch (chanp->l2_active_protocol) {
+		case (ISDN_PROTO_L2_X75I):
+			chanp->lc_b.l2_establish = !0;
+			break;
+		case (ISDN_PROTO_L2_HDLC):
+		case (ISDN_PROTO_L2_TRANS):
+			chanp->lc_b.l2_establish = 0;
+			break;
+		default:
+			printk(KERN_WARNING "l4_prep_dialout unknown protocol\n");
+			break;
+	}
+	if (chanp->Flags & FLG_ESTAB_D) {
+		FsmEvent(fi, EV_DLEST, NULL);
+	} else {
+		chanp->Flags = FLG_START_D;
+		if (chanp->leased) {
+			chanp->lc_d.l2_establish = 0;
+		}
+		FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL);
+	}
+}
+
+static void
+l4_do_dialout(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_OUT_DIAL);
+	if (chanp->leased) {
+		chanp->para.bchannel = (chanp->chan & 1) + 1;
+		FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
+	} else {
+		SETBIT(chanp->Flags, FLG_ESTAB_D);
+		chanp->para.callref = chanp->outcallref;
+		chanp->outcallref++;
+		if (chanp->outcallref == 128)
+			chanp->outcallref = 64;
+		chanp->is.l4.l4l3(&chanp->is, CC_SETUP_REQ, NULL);
+		SETBIT(chanp->Flags, FLG_CALL_SEND);
+	}
+}
+
+static void
+l4_init_bchan_out(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	FsmChangeState(fi, ST_WAIT_BCONN);
+	SETBIT(chanp->Flags, FLG_LL_DCONN);
+	if (chanp->debug & 1)
+		link_debug(chanp, "STAT_DCONN", 0);
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_DCONN;
+	ic.arg = chanp->chan;
+	chanp->sp->iif.statcallb(&ic);
+	init_ds(chanp, 0);
+	SETBIT(chanp->Flags, FLG_START_B);
+	FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL);
+}
+
+static void
+l4_go_active(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	FsmChangeState(fi, ST_ACTIVE);
+	chanp->data_open = !0;
+	SETBIT(chanp->Flags, FLG_CONNECT_B);
+	if (chanp->debug & 1)
+		link_debug(chanp, "STAT_BCONN", 0);
+	SETBIT(chanp->Flags, FLG_LL_BCONN);
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_BCONN;
+	ic.arg = chanp->chan;
+	chanp->sp->iif.statcallb(&ic);
+}
+
+/* incomming call */
+
+static void
+l4_start_dchan(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_IN_WAIT_D);
+	FsmDelTimer(&chanp->drel_timer, 61);
+	if (chanp->Flags & FLG_ESTAB_D) {
+		FsmEvent(fi, EV_DLEST, NULL);
+	} else {
+		chanp->Flags = FLG_START_D;
+		FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL);
+	}
+}
+
+static void
+l4_deliver_call(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+	int ret;
+	char txt[32];
+
+	/*
+	 * Report incoming calls only once to linklevel, use CallFlags
+	 * which is set to 3 with each broadcast message in isdnl1.c
+	 * and resetted if a interface  answered the STAT_ICALL.
+	 */
+	if ((chanp->sp) && (chanp->sp->CallFlags == 3)) {
+		FsmChangeState(fi, ST_IN_WAIT_LL);
+		SETBIT(chanp->Flags, FLG_ESTAB_D);
+		SETBIT(chanp->Flags, FLG_CALL_REC);
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_ICALL", 0);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_ICALL;
+		ic.arg = chanp->chan;
+		/*
+		 * No need to return "unknown" for calls without OAD,
+		 * cause that's handled in linklevel now (replaced by '0')
+		 */
+		ic.parm.setup = chanp->para.setup;
+		ret = chanp->sp->iif.statcallb(&ic);
+		if (chanp->debug & 1) {
+			sprintf(txt, "statcallb ret=%d", ret);
+			link_debug(chanp, txt, 1);
+		}
+		if (ret)	/* if a interface knows this call, reset the CallFlag
+				   * to avoid a second Call report to the linklevel
+				 */
+			chanp->sp->CallFlags &= ~(chanp->chan + 1);
+		switch (ret) {
+			case 1:	/* OK, anybody likes this call */
+				FsmChangeState(fi, ST_IN_ALERT_SEND);
+				SETBIT(chanp->Flags, FLG_CALL_ALERT);
+				chanp->is.l4.l4l3(&chanp->is, CC_ALERTING_REQ, NULL);
+				break;
+			case 2:	/* Rejecting Call */
+				RESBIT(chanp->Flags, FLG_CALL_REC);
+				break;
+			case 0:	/* OK, nobody likes this call */
+			default:	/* statcallb problems */
+				chanp->is.l4.l4l3(&chanp->is, CC_IGNORE, NULL);
+				FsmChangeState(fi, ST_NULL);
+				chanp->Flags = FLG_ESTAB_D;
+				FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 61);
+				break;
+		}
+	} else {
+		chanp->is.l4.l4l3(&chanp->is, CC_IGNORE, NULL);
+		FsmChangeState(fi, ST_NULL);
+		chanp->Flags = FLG_ESTAB_D;
+		FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 62);
+	}
+}
+
+static void
+l4_send_dconnect(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_IN_WAIT_CONN_ACK);
+	chanp->is.l4.l4l3(&chanp->is, CC_SETUP_RSP, NULL);
+}
+
+static void
+l4_init_bchan_in(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	FsmChangeState(fi, ST_WAIT_BCONN);
+	SETBIT(chanp->Flags, FLG_LL_DCONN);
+	if (chanp->debug & 1)
+		link_debug(chanp, "STAT_DCONN", 0);
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_DCONN;
+	ic.arg = chanp->chan;
+	chanp->sp->iif.statcallb(&ic);
+	chanp->l2_active_protocol = chanp->l2_protocol;
+	chanp->incoming = !0;
+	chanp->lc_b.l2_start = 0;
+	switch (chanp->l2_active_protocol) {
+		case (ISDN_PROTO_L2_X75I):
+			chanp->lc_b.l2_establish = !0;
+			break;
+		case (ISDN_PROTO_L2_HDLC):
+		case (ISDN_PROTO_L2_TRANS):
+			chanp->lc_b.l2_establish = 0;
+			break;
+		default:
+			printk(KERN_WARNING "r9 unknown protocol\n");
+			break;
+	}
+	init_ds(chanp, !0);
+	SETBIT(chanp->Flags, FLG_START_B);
+	FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL);
+}
+
+/* Call clearing */
+
+static void
+l4_reject_call(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_WAIT_DRELEASE);
+	chanp->para.cause = 0x15;	/* Call Rejected */
+	chanp->is.l4.l4l3(&chanp->is, CC_REJECT_REQ, NULL);
+	SETBIT(chanp->Flags, FLG_DISC_SEND);
+}
+
+static void
+l4_cancel_call(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	FsmChangeState(fi, ST_WAIT_DRELEASE);
+	if (chanp->Flags & FLG_LL_BCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_BHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_BCONN);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_BHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	if (chanp->Flags & FLG_START_B) {
+		release_ds(chanp);
+		RESBIT(chanp->Flags, FLG_START_B);
+	}
+	chanp->para.cause = 0x10;	/* Normal Call Clearing */
+	chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL);
+	SETBIT(chanp->Flags, FLG_DISC_SEND);
+}
+
+static void
+l4_shutdown_d(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_WAIT_DSHUTDOWN);
+	FsmDelTimer(&chanp->drel_timer, 62);
+	RESBIT(chanp->Flags, FLG_ESTAB_D);
+	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+l4_timeout_d(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	if (chanp->Flags & FLG_LL_DCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_DHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_DCONN);
+		l4_deliver_cause(chanp);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_DHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	FsmChangeState(fi, ST_NULL);
+	chanp->Flags = FLG_ESTAB_D;
+	FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 60);
+}
+
+static void
+l4_go_null(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_NULL);
+	chanp->Flags = 0;
+	FsmDelTimer(&chanp->drel_timer, 63);
+}
+
+static void
+l4_disconn_bchan(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	chanp->data_open = 0;
+	FsmChangeState(fi, ST_WAIT_BRELEASE);
+	RESBIT(chanp->Flags, FLG_CONNECT_B);
+	FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+l4_send_d_disc(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+
+	if (chanp->Flags & (FLG_DISC_REC | FLG_REL_REC))
+		return;
+	FsmChangeState(fi, ST_WAIT_DRELEASE);
+	if (chanp->Flags & FLG_LL_BCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_BHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_BCONN);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_BHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	if (chanp->Flags & FLG_START_B) {
+		release_ds(chanp);
+		RESBIT(chanp->Flags, FLG_START_B);
+	}
+	chanp->para.cause = 0x10;	/* Normal Call Clearing */
+	chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL);
+	SETBIT(chanp->Flags, FLG_DISC_SEND);
+}
+
+static void
+l4_released_bchan(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	FsmChangeState(fi, ST_WAIT_DCOMMAND);
+	chanp->data_open = 0;
+	if (chanp->Flags & FLG_LL_BCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_BHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_BCONN);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_BHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	release_ds(chanp);
+	RESBIT(chanp->Flags, FLG_START_B);
+}
+
+
+static void
+l4_release_bchan(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	chanp->data_open = 0;
+	SETBIT(chanp->Flags, FLG_DISC_REC);
+	FsmChangeState(fi, ST_WAIT_BREL_DISC);
+	RESBIT(chanp->Flags, FLG_CONNECT_B);
+	FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+l4_received_d_rel(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	chanp->data_open = 0;
+	FsmChangeState(fi, ST_WAIT_DSHUTDOWN);
+	SETBIT(chanp->Flags, FLG_REL_REC);
+	if (chanp->Flags & FLG_CONNECT_B) {
+		chanp->lc_b.l2_establish = 0;	/* direct reset in lc_b.lcfi */
+		FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+		RESBIT(chanp->Flags, FLG_CONNECT_B);
+	}
+	if (chanp->Flags & FLG_LL_BCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_BHUP", 0);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_BHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+		RESBIT(chanp->Flags, FLG_LL_BCONN);
+	}
+	if (chanp->Flags & FLG_START_B) {
+		release_ds(chanp);
+		RESBIT(chanp->Flags, FLG_START_B);
+	}
+	if (chanp->Flags & (FLG_LL_DCONN | FLG_CALL_SEND | FLG_CALL_ALERT)) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_DHUP", 0);
+		l4_deliver_cause(chanp);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_DHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	FsmEvent(&chanp->lc_d.lcfi, EV_LC_FLUSH, NULL);
+	RESBIT(chanp->Flags, FLG_ESTAB_D);
+	RESBIT(chanp->Flags, FLG_DISC_SEND);
+	RESBIT(chanp->Flags, FLG_CALL_REC);
+	RESBIT(chanp->Flags, FLG_CALL_ALERT);
+	RESBIT(chanp->Flags, FLG_LL_DCONN);
+	RESBIT(chanp->Flags, FLG_CALL_SEND);
+}
+
+static void
+l4_received_d_relcnf(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	chanp->data_open = 0;
+	FsmChangeState(fi, ST_WAIT_DSHUTDOWN);
+	if (chanp->Flags & FLG_CONNECT_B) {
+		chanp->lc_b.l2_establish = 0;	/* direct reset in lc_b.lcfi */
+		FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+		RESBIT(chanp->Flags, FLG_CONNECT_B);
+	}
+	if (chanp->Flags & FLG_LL_BCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_BHUP", 0);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_BHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+		RESBIT(chanp->Flags, FLG_LL_BCONN);
+	}
+	if (chanp->Flags & FLG_START_B) {
+		release_ds(chanp);
+		RESBIT(chanp->Flags, FLG_START_B);
+	}
+	if (chanp->Flags & (FLG_LL_DCONN | FLG_CALL_SEND | FLG_CALL_ALERT)) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_DHUP", 0);
+		l4_deliver_cause(chanp);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_DHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+	RESBIT(chanp->Flags, FLG_ESTAB_D);
+	RESBIT(chanp->Flags, FLG_DISC_SEND);
+	RESBIT(chanp->Flags, FLG_CALL_REC);
+	RESBIT(chanp->Flags, FLG_CALL_ALERT);
+	RESBIT(chanp->Flags, FLG_LL_DCONN);
+	RESBIT(chanp->Flags, FLG_CALL_SEND);
+}
+
+static void
+l4_received_d_disc(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	chanp->data_open = 0;
+	FsmChangeState(fi, ST_WAIT_D_REL_CNF);
+	SETBIT(chanp->Flags, FLG_DISC_REC);
+	if (chanp->Flags & FLG_LL_BCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_BHUP", 0);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_BHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+		RESBIT(chanp->Flags, FLG_LL_BCONN);
+	}
+	if (chanp->Flags & FLG_START_B) {
+		release_ds(chanp);
+		RESBIT(chanp->Flags, FLG_START_B);
+	}
+	if (chanp->Flags & (FLG_LL_DCONN | FLG_CALL_SEND | FLG_CALL_ALERT)) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_DHUP", 0);
+		l4_deliver_cause(chanp);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_DHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	RESBIT(chanp->Flags, FLG_LL_DCONN);
+	RESBIT(chanp->Flags, FLG_CALL_SEND);
+	RESBIT(chanp->Flags, FLG_CALL_ALERT);
+	chanp->is.l4.l4l3(&chanp->is, CC_RELEASE_REQ, NULL);
+}
+
+/* processing charge info */
+static void
+l4_charge_info(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_CINF;
+	ic.arg = chanp->chan;
+	sprintf(ic.parm.num, "%d", chanp->para.chargeinfo);
+	chanp->sp->iif.statcallb(&ic);
+}
+
+/* error procedures */
+
+static void
+l4_no_dchan(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	if (chanp->debug & 1)
+		link_debug(chanp, "STAT_NODCH", 0);
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_NODCH;
+	ic.arg = chanp->chan;
+	chanp->sp->iif.statcallb(&ic);
+	chanp->Flags = 0;
+	FsmChangeState(fi, ST_NULL);
+	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+l4_no_dchan_ready(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	if (chanp->debug & 1)
+		link_debug(chanp, "STAT_DHUP", 0);
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_DHUP;
+	ic.arg = chanp->chan;
+	chanp->sp->iif.statcallb(&ic);
+}
+
+static void
+l4_no_dchan_in(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	chanp->is.l4.l4l3(&chanp->is, CC_DLRL, NULL);
+	chanp->Flags = 0;
+	FsmChangeState(fi, ST_NULL);
+	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+l4_no_setup_rsp(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	chanp->Flags = 0;
+	FsmChangeState(fi, ST_NULL);
+	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+	if (chanp->debug & 1)
+		link_debug(chanp, "STAT_DHUP", 0);
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_DHUP;
+	ic.arg = chanp->chan;
+	chanp->sp->iif.statcallb(&ic);
+}
+
+static void
+l4_setup_err(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	FsmChangeState(fi, ST_WAIT_DRELEASE);
+	if (chanp->Flags & FLG_LL_DCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_DHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_DCONN);
+		l4_deliver_cause(chanp);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_DHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	SETBIT(chanp->Flags, FLG_DISC_SEND);	/* DISCONN was sent from L3 */
+}
+
+static void
+l4_connect_err(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	FsmChangeState(fi, ST_WAIT_DRELEASE);
+	if (chanp->Flags & FLG_LL_DCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_DHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_DCONN);
+		l4_deliver_cause(chanp);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_DHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	SETBIT(chanp->Flags, FLG_DISC_SEND);	/* DISCONN was sent from L3 */
+}
+
+static void
+l4_active_dlrl(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	chanp->data_open = 0;
+	FsmChangeState(fi, ST_NULL);
+	if (chanp->Flags & FLG_CONNECT_B) {
+		chanp->lc_b.l2_establish = 0;	/* direct reset in lc_b.lcfi */
+		FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+		RESBIT(chanp->Flags, FLG_CONNECT_B);
+	}
+	if (chanp->Flags & FLG_LL_BCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_BHUP", 0);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_BHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+		RESBIT(chanp->Flags, FLG_LL_BCONN);
+	}
+	if (chanp->Flags & FLG_START_B) {
+		release_ds(chanp);
+		RESBIT(chanp->Flags, FLG_START_B);
+	}
+	if (chanp->Flags & FLG_LL_DCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_DHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_DCONN);
+		if (chanp->sp->protocol == ISDN_PTYPE_EURO) {
+			chanp->para.cause = 0x2f;
+			chanp->para.loc = 0;
+		} else {
+			chanp->para.cause = 0x70;
+			chanp->para.loc = 0;
+		}
+		l4_deliver_cause(chanp);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_DHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	chanp->Flags = 0;
+	chanp->is.l4.l4l3(&chanp->is, CC_DLRL, NULL);
+	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+}
+/* *INDENT-OFF* */
+static struct FsmNode fnlist[] =
+{
+	{ST_NULL,		EV_DIAL,		l4_prep_dialout},
+	{ST_NULL,		EV_SETUP_IND,		l4_start_dchan},
+	{ST_NULL,		EV_SHUTDOWN_D,		l4_shutdown_d},
+	{ST_NULL,		EV_DLRL,		l4_go_null},
+	{ST_OUT_WAIT_D,		EV_DLEST,		l4_do_dialout},
+	{ST_OUT_WAIT_D,		EV_DLRL,		l4_no_dchan},
+	{ST_OUT_WAIT_D,		EV_HANGUP,		l4_no_dchan},
+	{ST_IN_WAIT_D,		EV_DLEST,		l4_deliver_call},
+	{ST_IN_WAIT_D,		EV_DLRL,		l4_no_dchan_in},
+	{ST_IN_WAIT_D,		EV_HANGUP,		l4_no_dchan_in},
+	{ST_OUT_DIAL,		EV_SETUP_CNF,		l4_init_bchan_out},
+	{ST_OUT_DIAL,		EV_HANGUP,		l4_cancel_call},
+	{ST_OUT_DIAL,		EV_DISCONNECT_IND,	l4_received_d_disc},
+	{ST_OUT_DIAL,		EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_OUT_DIAL,		EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_OUT_DIAL,		EV_NOSETUP_RSP,		l4_no_setup_rsp},
+	{ST_OUT_DIAL,		EV_SETUP_ERR,		l4_setup_err},
+	{ST_IN_WAIT_LL,		EV_SETUP_CMPL_IND,	l4_init_bchan_in},
+	{ST_IN_WAIT_LL,		EV_ACCEPTD,		l4_send_dconnect},
+	{ST_IN_WAIT_LL,		EV_HANGUP,		l4_reject_call},
+	{ST_IN_WAIT_LL,		EV_DISCONNECT_IND,	l4_received_d_disc},
+	{ST_IN_WAIT_LL,		EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_IN_WAIT_LL,		EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_IN_ALERT_SEND,	EV_SETUP_CMPL_IND,	l4_init_bchan_in},
+	{ST_IN_ALERT_SEND,	EV_ACCEPTD,		l4_send_dconnect},
+	{ST_IN_ALERT_SEND,	EV_HANGUP,		l4_send_d_disc},
+	{ST_IN_ALERT_SEND,	EV_DISCONNECT_IND,	l4_received_d_disc},
+	{ST_IN_ALERT_SEND,	EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_IN_ALERT_SEND,	EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_IN_WAIT_CONN_ACK,	EV_SETUP_CMPL_IND,	l4_init_bchan_in},
+	{ST_IN_WAIT_CONN_ACK,	EV_HANGUP,		l4_send_d_disc},
+	{ST_IN_WAIT_CONN_ACK,	EV_DISCONNECT_IND,	l4_received_d_disc},
+	{ST_IN_WAIT_CONN_ACK,	EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_IN_WAIT_CONN_ACK,	EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_IN_WAIT_CONN_ACK,	EV_CONNECT_ERR,		l4_connect_err},
+	{ST_WAIT_BCONN,		EV_BC_EST,		l4_go_active},
+	{ST_WAIT_BCONN,		EV_BC_REL,		l4_send_d_disc},
+	{ST_WAIT_BCONN,		EV_HANGUP,		l4_send_d_disc},
+	{ST_WAIT_BCONN,		EV_DISCONNECT_IND,	l4_received_d_disc},
+	{ST_WAIT_BCONN,		EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_WAIT_BCONN,		EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_ACTIVE,		EV_CINF,		l4_charge_info},
+	{ST_ACTIVE,		EV_BC_REL,		l4_released_bchan},
+	{ST_ACTIVE,		EV_HANGUP,		l4_disconn_bchan},
+	{ST_ACTIVE,		EV_DISCONNECT_IND,	l4_release_bchan},
+	{ST_ACTIVE,		EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_ACTIVE,		EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_ACTIVE,		EV_DLRL,		l4_active_dlrl},
+	{ST_WAIT_BRELEASE,	EV_BC_REL,		l4_send_d_disc},
+	{ST_WAIT_BRELEASE,	EV_DISCONNECT_IND,	l4_received_d_disc},
+	{ST_WAIT_BRELEASE,	EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_WAIT_BRELEASE,	EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_WAIT_BREL_DISC,	EV_BC_REL,		l4_received_d_disc},
+	{ST_WAIT_BREL_DISC,	EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_WAIT_BREL_DISC,	EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_WAIT_DCOMMAND,	EV_HANGUP,		l4_send_d_disc},
+	{ST_WAIT_DCOMMAND,	EV_DISCONNECT_IND,	l4_received_d_disc},
+	{ST_WAIT_DCOMMAND,	EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_WAIT_DCOMMAND,	EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_WAIT_DRELEASE,	EV_RELEASE_IND,		l4_timeout_d},
+	{ST_WAIT_DRELEASE,	EV_RELEASE_CNF,		l4_timeout_d},
+	{ST_WAIT_DRELEASE,	EV_RELEASE_ERR,		l4_timeout_d},
+	{ST_WAIT_DRELEASE,	EV_DIAL,		l4_no_dchan_ready},
+	{ST_WAIT_D_REL_CNF,	EV_RELEASE_CNF,		l4_timeout_d},
+	{ST_WAIT_D_REL_CNF,	EV_RELEASE_ERR,		l4_timeout_d},
+	{ST_WAIT_D_REL_CNF,	EV_DIAL,		l4_no_dchan_ready},
+	{ST_WAIT_DSHUTDOWN,	EV_DLRL,		l4_go_null},
+	{ST_WAIT_DSHUTDOWN,	EV_DIAL,		l4_prep_dialout},
+	{ST_WAIT_DSHUTDOWN,	EV_SETUP_IND,		l4_start_dchan},
+};
+/* *INDENT-ON* */
+
+
+
+
+#define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode))
+
+static void
+lc_r1(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	FsmChangeState(fi, ST_LC_ACTIVATE_WAIT);
+	FsmAddTimer(&lf->act_timer, 1000, EV_LC_TIMER, NULL, 50);
+	lf->st->ma.manl1(lf->st, PH_ACTIVATE, NULL);
+
+}
+
+static void
+lc_r6(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	FsmDelTimer(&lf->act_timer, 50);
+	FsmChangeState(fi, ST_LC_DELAY);
+	FsmAddTimer(&lf->act_timer, 40, EV_LC_TIMER, NULL, 51);
+}
+
+static void
+lc_r2(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	if (lf->l2_establish) {
+		FsmChangeState(fi, ST_LC_ESTABLISH_WAIT);
+		if (lf->l2_start)
+			lf->st->ma.manl2(lf->st, DL_ESTABLISH, NULL);
+	} else {
+		FsmChangeState(fi, ST_LC_CONNECTED);
+		lf->lccall(lf, LC_ESTABLISH, NULL);
+	}
+}
+
+static void
+lc_r3(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	FsmChangeState(fi, ST_LC_CONNECTED);
+	lf->lccall(lf, LC_ESTABLISH, NULL);
+}
+
+static void
+lc_r7(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	FsmChangeState(fi, ST_LC_FLUSH_WAIT);
+	lf->st->ma.manl2(lf->st, DL_FLUSH, NULL);
+}
+
+static void
+lc_r4(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	if (lf->l2_establish) {
+		FsmChangeState(fi, ST_LC_RELEASE_WAIT);
+		lf->st->ma.manl2(lf->st, DL_RELEASE, NULL);
+		/* This timer is for releasing the channel even
+		 * there is a hang in layer 2 ; 5 sec are a try
+		 */
+		FsmAddTimer(&lf->act_timer, 5000, EV_LC_TIMER, NULL, 53);
+	} else {
+		FsmChangeState(fi, ST_LC_NULL);
+		lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL);
+		lf->lccall(lf, LC_RELEASE, NULL);
+	}
+}
+
+static void
+lc_r4_1(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	FsmChangeState(fi, ST_LC_FLUSH_DELAY);
+	FsmAddTimer(&lf->act_timer, 50, EV_LC_TIMER, NULL, 52);
+}
+
+static void
+lc_r5_1(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	FsmChangeState(fi, ST_LC_RELEASE_WAIT);
+	/* This delay is needed for send out the UA frame before
+	 * PH_DEACTIVATE the interface
+	 */
+	FsmAddTimer(&lf->act_timer, 10, EV_LC_TIMER, NULL, 54);
+}
+
+static void
+lc_r5(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	FsmDelTimer(&lf->act_timer, 54);
+	FsmChangeState(fi, ST_LC_NULL);
+	lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL);
+	lf->lccall(lf, LC_RELEASE, NULL);
+}
+/* *INDENT-OFF* */
+static struct FsmNode LcFnList[] =
+{
+	{ST_LC_NULL,  		EV_LC_ESTABLISH,	lc_r1},
+	{ST_LC_ACTIVATE_WAIT,	EV_LC_PH_ACTIVATE,	lc_r6},
+	{ST_LC_DELAY,		EV_LC_TIMER,		lc_r2},
+	{ST_LC_DELAY,		EV_LC_DL_ESTABLISH,	lc_r3},
+	{ST_LC_ESTABLISH_WAIT,	EV_LC_DL_ESTABLISH,	lc_r3},
+	{ST_LC_ESTABLISH_WAIT,	EV_LC_RELEASE,		lc_r5},
+	{ST_LC_CONNECTED,	EV_LC_FLUSH,		lc_r7},
+	{ST_LC_CONNECTED,	EV_LC_RELEASE,		lc_r4},
+	{ST_LC_CONNECTED,	EV_LC_DL_RELEASE,	lc_r5_1},
+	{ST_LC_FLUSH_WAIT,	EV_LC_DL_FLUSH,		lc_r4_1},
+	{ST_LC_FLUSH_DELAY,	EV_LC_TIMER,		lc_r4},
+	{ST_LC_RELEASE_WAIT,	EV_LC_DL_RELEASE,	lc_r5},
+	{ST_LC_RELEASE_WAIT,	EV_LC_TIMER,		lc_r5},
+	{ST_LC_ACTIVATE_WAIT,	EV_LC_TIMER,		lc_r5},
+	{ST_LC_ESTABLISH_WAIT,	EV_LC_DL_RELEASE,	lc_r5},
+};
+/* *INDENT-ON* */
+
+
+
+
+
+
+
+
+
+
+#define LC_FN_COUNT (sizeof(LcFnList)/sizeof(struct FsmNode))
+
+void
+CallcNew(void)
+{
+	callcfsm.state_count = STATE_COUNT;
+	callcfsm.event_count = EVENT_COUNT;
+	callcfsm.strEvent = strEvent;
+	callcfsm.strState = strState;
+	FsmNew(&callcfsm, fnlist, FNCOUNT);
+
+	lcfsm.state_count = LC_STATE_COUNT;
+	lcfsm.event_count = LC_EVENT_COUNT;
+	lcfsm.strEvent = strLcEvent;
+	lcfsm.strState = strLcState;
+	FsmNew(&lcfsm, LcFnList, LC_FN_COUNT);
+}
+
+void
+CallcFree(void)
+{
+	FsmFree(&lcfsm);
+	FsmFree(&callcfsm);
+}
+
+static void
+release_ds(struct Channel *chanp)
+{
+	struct PStack *st = &chanp->ds;
+	struct IsdnCardState *sp;
+	struct HscxState *hsp;
+
+	sp = st->l1.hardware;
+	hsp = sp->hs + chanp->hscx;
+
+	close_hscxstate(hsp);
+
+	switch (chanp->l2_active_protocol) {
+		case (ISDN_PROTO_L2_X75I):
+			releasestack_isdnl2(st);
+			break;
+		case (ISDN_PROTO_L2_HDLC):
+		case (ISDN_PROTO_L2_TRANS):
+			releasestack_transl2(st);
+			break;
+	}
+	/* Reset B-Channel Statemachine */
+	FsmDelTimer(&chanp->lc_b.act_timer, 79);
+	FsmChangeState(&chanp->lc_b.lcfi, ST_LC_NULL);
+}
+
+static void
+cc_l1man(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+	switch (pr) {
+		case (PH_ACTIVATE):
+			FsmEvent(&chanp->lc_d.lcfi, EV_LC_PH_ACTIVATE, NULL);
+			break;
+		case (PH_DEACTIVATE):
+			FsmEvent(&chanp->lc_d.lcfi, EV_LC_PH_DEACTIVATE, NULL);
+			break;
+	}
+}
+
+static void
+cc_l2man(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+	switch (pr) {
+		case (DL_ESTABLISH):
+			FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_ESTABLISH, NULL);
+			break;
+		case (DL_RELEASE):
+			FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_RELEASE, NULL);
+			break;
+		case (DL_FLUSH):
+			FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_FLUSH, NULL);
+			break;
+	}
+}
+
+static void
+dcc_l1man(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+	switch (pr) {
+		case (PH_ACTIVATE):
+			FsmEvent(&chanp->lc_b.lcfi, EV_LC_PH_ACTIVATE, NULL);
+			break;
+		case (PH_DEACTIVATE):
+			FsmEvent(&chanp->lc_b.lcfi, EV_LC_PH_DEACTIVATE, NULL);
+			break;
+	}
+}
+
+static void
+dcc_l2man(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+	switch (pr) {
+		case (DL_ESTABLISH):
+			FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_ESTABLISH, NULL);
+			break;
+		case (DL_RELEASE):
+			FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_RELEASE, NULL);
+			break;
+	}
+}
+
+static void
+l2tei_dummy(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+	char tmp[64], tm[32];
+
+	jiftime(tm, jiffies);
+	sprintf(tmp, "%s Channel %d Warning! Dummy l2tei called pr=%d\n", tm, chanp->chan, pr);
+	HiSax_putstatus(chanp->sp, tmp);
+}
+
+static void
+ll_handler(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+	char tmp[64], tm[32];
+
+	switch (pr) {
+		case (CC_DISCONNECT_IND):
+			FsmEvent(&chanp->fi, EV_DISCONNECT_IND, NULL);
+			break;
+		case (CC_RELEASE_CNF):
+			FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL);
+			break;
+		case (CC_SETUP_IND):
+			FsmEvent(&chanp->fi, EV_SETUP_IND, NULL);
+			break;
+		case (CC_RELEASE_IND):
+			FsmEvent(&chanp->fi, EV_RELEASE_IND, NULL);
+			break;
+		case (CC_SETUP_COMPLETE_IND):
+			FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL);
+			break;
+		case (CC_SETUP_CNF):
+			FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
+			break;
+		case (CC_INFO_CHARGE):
+			FsmEvent(&chanp->fi, EV_CINF, NULL);
+			break;
+		case (CC_NOSETUP_RSP_ERR):
+			FsmEvent(&chanp->fi, EV_NOSETUP_RSP, NULL);
+			break;
+		case (CC_SETUP_ERR):
+			FsmEvent(&chanp->fi, EV_SETUP_ERR, NULL);
+			break;
+		case (CC_CONNECT_ERR):
+			FsmEvent(&chanp->fi, EV_CONNECT_ERR, NULL);
+			break;
+		case (CC_RELEASE_ERR):
+			FsmEvent(&chanp->fi, EV_RELEASE_ERR, NULL);
+			break;
+		default:
+			if (chanp->debug & 2048) {
+				jiftime(tm, jiffies);
+				sprintf(tmp, "%s Channel %d L3->L4 unknown primitiv %d\n",
+					tm, chanp->chan, pr);
+				HiSax_putstatus(chanp->sp, tmp);
+			}
+	}
+}
+
+static void
+init_is(struct Channel *chanp, unsigned int ces)
+{
+	struct PStack *st = &chanp->is;
+	struct IsdnCardState *sp = chanp->sp;
+	char tmp[128];
+
+	setstack_HiSax(st, sp);
+	st->l2.sap = 0;
+	st->l2.tei = 255;
+	st->l2.ces = ces;
+	st->l2.extended = !0;
+	st->l2.laptype = LAPD;
+	st->l2.window = 1;
+	st->l2.orig = !0;
+	st->l2.t200 = 1000;	/* 1000 milliseconds  */
+	if (st->protocol == ISDN_PTYPE_1TR6) {
+		st->l2.n200 = 3;	/* try 3 times        */
+		st->l2.t203 = 10000;	/* 10000 milliseconds */
+	} else {
+		st->l2.n200 = 4;	/* try 4 times        */
+		st->l2.t203 = 5000;	/* 5000 milliseconds  */
+	}
+	sprintf(tmp, "Channel %d q.921", chanp->chan);
+	setstack_isdnl2(st, tmp);
+	setstack_isdnl3(st, chanp);
+	st->l4.userdata = chanp;
+	st->l4.l2writewakeup = NULL;
+	st->l3.l3l4 = ll_handler;
+	st->l1.l1man = cc_l1man;
+	st->l2.l2man = cc_l2man;
+	st->pa = &chanp->para;
+	HiSax_addlist(sp, st);
+}
+
+static void
+callc_debug(struct FsmInst *fi, char *s)
+{
+	char str[80], tm[32];
+	struct Channel *chanp = fi->userdata;
+
+	jiftime(tm, jiffies);
+	sprintf(str, "%s Channel %d callc %s\n", tm, chanp->chan, s);
+	HiSax_putstatus(chanp->sp, str);
+}
+
+static void
+lc_debug(struct FsmInst *fi, char *s)
+{
+	char str[256], tm[32];
+	struct LcFsm *lf = fi->userdata;
+
+	jiftime(tm, jiffies);
+	sprintf(str, "%s Channel %d lc %s\n", tm, lf->ch->chan, s);
+	HiSax_putstatus(lf->ch->sp, str);
+}
+
+static void
+dlc_debug(struct FsmInst *fi, char *s)
+{
+	char str[256], tm[32];
+	struct LcFsm *lf = fi->userdata;
+
+	jiftime(tm, jiffies);
+	sprintf(str, "%s Channel %d dlc %s\n", tm, lf->ch->chan, s);
+	HiSax_putstatus(lf->ch->sp, str);
+}
+
+static void
+lccall_d(struct LcFsm *lf, int pr, void *arg)
+{
+	struct Channel *chanp = lf->ch;
+
+	switch (pr) {
+		case (LC_ESTABLISH):
+			FsmEvent(&chanp->fi, EV_DLEST, NULL);
+			break;
+		case (LC_RELEASE):
+			FsmEvent(&chanp->fi, EV_DLRL, NULL);
+			break;
+	}
+}
+
+static void
+lccall_b(struct LcFsm *lf, int pr, void *arg)
+{
+	struct Channel *chanp = lf->ch;
+
+	switch (pr) {
+		case (LC_ESTABLISH):
+			FsmEvent(&chanp->fi, EV_BC_EST, NULL);
+			break;
+		case (LC_RELEASE):
+			FsmEvent(&chanp->fi, EV_BC_REL, NULL);
+			break;
+	}
+}
+
+static void
+init_chan(int chan, struct IsdnCardState *csta, int hscx,
+	  unsigned int ces)
+{
+	struct Channel *chanp = csta->channel + chan;
+
+	chanp->sp = csta;
+	chanp->hscx = hscx;
+	chanp->chan = chan;
+	chanp->incoming = 0;
+	chanp->debug = 0;
+	chanp->Flags = 0;
+	chanp->leased = 0;
+	chanp->impair = 0;
+	init_is(chanp, ces);
+
+	chanp->fi.fsm = &callcfsm;
+	chanp->fi.state = ST_NULL;
+	chanp->fi.debug = 0;
+	chanp->fi.userdata = chanp;
+	chanp->fi.printdebug = callc_debug;
+	FsmInitTimer(&chanp->fi, &chanp->dial_timer);
+	FsmInitTimer(&chanp->fi, &chanp->drel_timer);
+
+	chanp->lc_d.lcfi.fsm = &lcfsm;
+	chanp->lc_d.lcfi.state = ST_LC_NULL;
+	chanp->lc_d.lcfi.debug = 0;
+	chanp->lc_d.lcfi.userdata = &chanp->lc_d;
+	chanp->lc_d.lcfi.printdebug = lc_debug;
+	chanp->lc_d.type = LC_D;
+	chanp->lc_d.ch = chanp;
+	chanp->lc_d.st = &chanp->is;
+	chanp->lc_d.l2_establish = !0;
+	chanp->lc_d.l2_start = !0;
+	chanp->lc_d.lccall = lccall_d;
+	FsmInitTimer(&chanp->lc_d.lcfi, &chanp->lc_d.act_timer);
+
+	chanp->lc_b.lcfi.fsm = &lcfsm;
+	chanp->lc_b.lcfi.state = ST_LC_NULL;
+	chanp->lc_b.lcfi.debug = 0;
+	chanp->lc_b.lcfi.userdata = &chanp->lc_b;
+	chanp->lc_b.lcfi.printdebug = dlc_debug;
+	chanp->lc_b.type = LC_B;
+	chanp->lc_b.ch = chanp;
+	chanp->lc_b.st = &chanp->ds;
+	chanp->lc_b.l2_establish = !0;
+	chanp->lc_b.l2_start = !0;
+	chanp->lc_b.lccall = lccall_b;
+	FsmInitTimer(&chanp->lc_b.lcfi, &chanp->lc_b.act_timer);
+	chanp->outcallref = 64;
+	chanp->data_open = 0;
+}
+
+int
+CallcNewChan(struct IsdnCardState *csta)
+{
+	int ces;
+
+	chancount += 2;
+	ces = randomces();
+	init_chan(0, csta, 1, ces++);
+	ces %= 0xffff;
+	init_chan(1, csta, 0, ces++);
+	printk(KERN_INFO "HiSax: 2 channels added\n");
+	return (2);
+}
+
+static void
+release_is(struct Channel *chanp)
+{
+	struct PStack *st = &chanp->is;
+
+	releasestack_isdnl2(st);
+	releasestack_isdnl3(st);
+	HiSax_rmlist(st->l1.hardware, st);
+}
+
+void
+CallcFreeChan(struct IsdnCardState *csta)
+{
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		FsmDelTimer(&csta->channel[i].drel_timer, 74);
+		FsmDelTimer(&csta->channel[i].dial_timer, 75);
+		FsmDelTimer(&csta->channel[i].lc_b.act_timer, 76);
+		FsmDelTimer(&csta->channel[i].lc_d.act_timer, 77);
+		if (csta->channel[i].Flags & FLG_START_B) {
+			release_ds(csta->channel + i);
+		}
+		release_is(csta->channel + i);
+	}
+}
+
+static void
+lldata_handler(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+	struct sk_buff *skb = arg;
+
+	switch (pr) {
+		case (DL_DATA):
+			if (chanp->data_open)
+				chanp->sp->iif.rcvcallb_skb(chanp->sp->myid, chanp->chan, skb);
+			else {
+				SET_SKB_FREE(skb);
+				dev_kfree_skb(skb, FREE_READ);
+			}
+			break;
+		default:
+			printk(KERN_WARNING "lldata_handler unknown primitive %d\n",
+			       pr);
+			break;
+	}
+}
+
+static void
+lltrans_handler(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+	struct sk_buff *skb = arg;
+
+	switch (pr) {
+		case (PH_DATA):
+			if (chanp->data_open)
+				chanp->sp->iif.rcvcallb_skb(chanp->sp->myid, chanp->chan, skb);
+			else {
+				SET_SKB_FREE(skb);
+				dev_kfree_skb(skb, FREE_READ);
+			}
+			break;
+		default:
+			printk(KERN_WARNING "lltrans_handler unknown primitive %d\n",
+			       pr);
+			break;
+	}
+}
+
+static void
+ll_writewakeup(struct PStack *st)
+{
+	struct Channel *chanp = st->l4.userdata;
+	isdn_ctrl ic;
+
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_BSENT;
+	ic.arg = chanp->chan;
+	chanp->sp->iif.statcallb(&ic);
+}
+
+static int
+init_ds(struct Channel *chanp, int incoming)
+{
+	struct PStack *st = &chanp->ds;
+	struct IsdnCardState *sp = chanp->sp;
+	struct HscxState *hsp = sp->hs + chanp->hscx;
+	char tmp[128];
+
+	st->l1.hardware = sp;
+
+	hsp->mode = 2;
+
+	if (setstack_hscx(st, hsp))
+		return (-1);
+
+	st->l2.extended = 0;
+	st->l2.laptype = LAPB;
+	st->l2.orig = !incoming;
+	st->l2.t200 = 1000;	/* 1000 milliseconds */
+	st->l2.window = 7;
+	st->l2.n200 = 4;	/* try 4 times       */
+	st->l2.t203 = 5000;	/* 5000 milliseconds */
+
+	st->l3.debug = 0;
+	switch (chanp->l2_active_protocol) {
+		case (ISDN_PROTO_L2_X75I):
+			sprintf(tmp, "Channel %d x.75", chanp->chan);
+			setstack_isdnl2(st, tmp);
+			st->l2.l2l3 = lldata_handler;
+			st->l1.l1man = dcc_l1man;
+			st->l2.l2man = dcc_l2man;
+			st->l2.l2tei = l2tei_dummy;
+			st->l4.userdata = chanp;
+			st->l4.l1writewakeup = NULL;
+			st->l4.l2writewakeup = ll_writewakeup;
+			st->l2.l2m.debug = chanp->debug & 16;
+			st->l2.debug = chanp->debug & 64;
+			st->ma.manl2(st, MDL_NOTEIPROC, NULL);
+			st->l1.hscxmode = 2;	/* Packet-Mode ? */
+			st->l1.hscxchannel = chanp->para.bchannel - 1;
+			break;
+		case (ISDN_PROTO_L2_HDLC):
+			st->l1.l1l2 = lltrans_handler;
+			st->l1.l1man = dcc_l1man;
+			st->l4.userdata = chanp;
+			st->l4.l1writewakeup = ll_writewakeup;
+			st->l1.hscxmode = 2;
+			st->l1.hscxchannel = chanp->para.bchannel - 1;
+			break;
+		case (ISDN_PROTO_L2_TRANS):
+			st->l1.l1l2 = lltrans_handler;
+			st->l1.l1man = dcc_l1man;
+			st->l4.userdata = chanp;
+			st->l4.l1writewakeup = ll_writewakeup;
+			st->l1.hscxmode = 1;
+			st->l1.hscxchannel = chanp->para.bchannel - 1;
+			break;
+	}
+	return (0);
+}
+
+static void
+channel_report(struct Channel *chanp)
+{
+}
+
+static void
+distr_debug(struct IsdnCardState *csta, int debugflags)
+{
+	int i;
+	struct Channel *chanp = csta->channel;
+
+	for (i = 0; i < 2; i++) {
+		chanp[i].debug = debugflags;
+		chanp[i].fi.debug = debugflags & 2;
+		chanp[i].is.l2.l2m.debug = debugflags & 8;
+		chanp[i].ds.l2.l2m.debug = debugflags & 16;
+		chanp[i].is.l2.debug = debugflags & 32;
+		chanp[i].ds.l2.debug = debugflags & 64;
+		chanp[i].lc_d.lcfi.debug = debugflags & 128;
+		chanp[i].lc_b.lcfi.debug = debugflags & 256;
+	}
+	csta->dlogflag = debugflags & 4;
+	csta->teistack->l2.l2m.debug = debugflags & 512;
+}
+
+int
+HiSax_command(isdn_ctrl * ic)
+{
+	struct IsdnCardState *csta = hisax_findcard(ic->driver);
+	struct Channel *chanp;
+	char tmp[128];
+	int i;
+	unsigned int num;
+
+	if (!csta) {
+		printk(KERN_ERR
+		"HiSax: if_command %d called with invalid driverId %d!\n",
+		       ic->command, ic->driver);
+		return -ENODEV;
+	}
+	switch (ic->command) {
+		case (ISDN_CMD_SETEAZ):
+			chanp = csta->channel + ic->arg;
+			if (chanp->debug & 1)
+				link_debug(chanp, "SETEAZ", 1);
+			break;
+		case (ISDN_CMD_SETL2):
+			chanp = csta->channel + (ic->arg & 0xff);
+			if (chanp->debug & 1) {
+				sprintf(tmp, "SETL2 card %d %ld", csta->cardnr + 1,
+					ic->arg >> 8);
+				link_debug(chanp, tmp, 1);
+			}
+			chanp->l2_protocol = ic->arg >> 8;
+			break;
+		case (ISDN_CMD_DIAL):
+			chanp = csta->channel + (ic->arg & 0xff);
+			if (chanp->debug & 1) {
+				sprintf(tmp, "DIAL %s -> %s (%d,%d)",
+					ic->parm.setup.eazmsn, ic->parm.setup.phone,
+				 ic->parm.setup.si1, ic->parm.setup.si2);
+				link_debug(chanp, tmp, 1);
+			}
+			chanp->para.setup = ic->parm.setup;
+			if (!strcmp(chanp->para.setup.eazmsn, "0"))
+				chanp->para.setup.eazmsn[0] = '\0';
+			/* this solution is dirty and may be change, if
+			 * we make a callreference based callmanager */
+			if (chanp->fi.state == ST_NULL) {
+				FsmEvent(&chanp->fi, EV_DIAL, NULL);
+			} else {
+				FsmDelTimer(&chanp->dial_timer, 70);
+				FsmAddTimer(&chanp->dial_timer, 50, EV_DIAL, NULL, 71);
+			}
+			break;
+		case (ISDN_CMD_ACCEPTB):
+			chanp = csta->channel + ic->arg;
+			if (chanp->debug & 1)
+				link_debug(chanp, "ACCEPTB", 1);
+			FsmEvent(&chanp->fi, EV_ACCEPTB, NULL);
+			break;
+		case (ISDN_CMD_ACCEPTD):
+			chanp = csta->channel + ic->arg;
+			if (chanp->debug & 1)
+				link_debug(chanp, "ACCEPTD", 1);
+			FsmEvent(&chanp->fi, EV_ACCEPTD, NULL);
+			break;
+		case (ISDN_CMD_HANGUP):
+			chanp = csta->channel + ic->arg;
+			if (chanp->debug & 1)
+				link_debug(chanp, "HANGUP", 1);
+			FsmEvent(&chanp->fi, EV_HANGUP, NULL);
+			break;
+		case (ISDN_CMD_SUSPEND):
+			chanp = csta->channel + ic->arg;
+			if (chanp->debug & 1) {
+				sprintf(tmp, "SUSPEND %s", ic->parm.num);
+				link_debug(chanp, tmp, 1);
+			}
+			FsmEvent(&chanp->fi, EV_SUSPEND, ic);
+			break;
+		case (ISDN_CMD_RESUME):
+			chanp = csta->channel + ic->arg;
+			if (chanp->debug & 1) {
+				sprintf(tmp, "RESUME %s", ic->parm.num);
+				link_debug(chanp, tmp, 1);
+			}
+			FsmEvent(&chanp->fi, EV_RESUME, ic);
+			break;
+		case (ISDN_CMD_LOCK):
+			HiSax_mod_inc_use_count();
+#ifdef MODULE
+			if (csta->channel[0].debug & 1024) {
+				jiftime(tmp, jiffies);
+				i = strlen(tmp);
+				sprintf(tmp + i, "   LOCK modcnt %lx\n", MOD_USE_COUNT);
+				HiSax_putstatus(csta, tmp);
+			}
+#endif				/* MODULE */
+			break;
+		case (ISDN_CMD_UNLOCK):
+			HiSax_mod_dec_use_count();
+#ifdef MODULE
+			if (csta->channel[0].debug & 1024) {
+				jiftime(tmp, jiffies);
+				i = strlen(tmp);
+				sprintf(tmp + i, " UNLOCK modcnt %lx\n", MOD_USE_COUNT);
+				HiSax_putstatus(csta, tmp);
+			}
+#endif				/* MODULE */
+			break;
+		case (ISDN_CMD_IOCTL):
+			switch (ic->arg) {
+				case (0):
+					HiSax_reportcard(csta->cardnr);
+					for (i = 0; i < 2; i++)
+						channel_report(&csta->channel[i]);
+					break;
+				case (1):
+					num = *(unsigned int *) ic->parm.num;
+					distr_debug(csta, num);
+					sprintf(tmp, "debugging flags card %d set to %x\n",
+						csta->cardnr + 1, num);
+					HiSax_putstatus(csta, tmp);
+					printk(KERN_DEBUG "HiSax: %s", tmp);
+					break;
+				case (2):
+					num = *(unsigned int *) ic->parm.num;
+					i = num >> 8;
+					if (i >= 2)
+						break;
+					chanp = csta->channel + i;
+					chanp->impair = num & 0xff;
+					if (chanp->debug & 1) {
+						sprintf(tmp, "IMPAIR %x", chanp->impair);
+						link_debug(chanp, tmp, 1);
+					}
+					break;
+				case (3):
+					for (i = 0; i < *(unsigned int *) ic->parm.num; i++)
+						HiSax_mod_dec_use_count();
+					break;
+				case (4):
+					for (i = 0; i < *(unsigned int *) ic->parm.num; i++)
+						HiSax_mod_inc_use_count();
+					break;
+				case (5):	/* set card in leased mode */
+					csta->channel[0].leased = 1;
+					csta->channel[1].leased = 1;
+					sprintf(tmp, "card %d set into leased mode\n",
+						csta->cardnr + 1);
+					HiSax_putstatus(csta, tmp);
+					break;
+#ifdef MODULE
+				case (55):
+#if (LINUX_VERSION_CODE < 0x020111)
+					MOD_USE_COUNT = MOD_VISITED;
+#else
+					MOD_USE_COUNT = 0;
+#endif
+					HiSax_mod_inc_use_count();
+					break;
+#endif				/* MODULE */
+				case (11):
+					csta->debug = *(unsigned int *) ic->parm.num;
+					sprintf(tmp, "l1 debugging flags card %d set to %x\n",
+					  csta->cardnr + 1, csta->debug);
+					HiSax_putstatus(cards[0].sp, tmp);
+					printk(KERN_DEBUG "HiSax: %s", tmp);
+					break;
+				case (13):
+					csta->channel[0].is.l3.debug = *(unsigned int *) ic->parm.num;
+					csta->channel[1].is.l3.debug = *(unsigned int *) ic->parm.num;
+					sprintf(tmp, "l3 debugging flags card %d set to %x\n",
+						csta->cardnr + 1, *(unsigned int *) ic->parm.num);
+					HiSax_putstatus(cards[0].sp, tmp);
+					printk(KERN_DEBUG "HiSax: %s", tmp);
+					break;
+				default:
+					printk(KERN_DEBUG "HiSax: invalid ioclt %d\n",
+					       (int) ic->arg);
+					return (-EINVAL);
+			}
+			break;
+		default:
+			break;
+	}
+
+	return (0);
+}
+
+int
+HiSax_writebuf_skb(int id, int chan, struct sk_buff *skb)
+{
+	struct IsdnCardState *csta = hisax_findcard(id);
+	struct Channel *chanp;
+	struct PStack *st;
+	int len = skb->len;
+	unsigned long flags;
+	struct sk_buff *nskb;
+	char tmp[64];
+
+	if (!csta) {
+		printk(KERN_ERR
+		    "HiSax: if_sendbuf called with invalid driverId!\n");
+		return -ENODEV;
+	}
+	chanp = csta->channel + chan;
+	st = &chanp->ds;
+	if (!chanp->data_open) {
+		link_debug(chanp, "writebuf: channel not open", 1);
+		return -EIO;
+	}
+	if (len > MAX_DATA_SIZE) {
+		sprintf(tmp, "writebuf: packet too large (%d bytes)", len);
+		printk(KERN_WARNING "HiSax_%s !\n", tmp);
+		link_debug(chanp, tmp, 1);
+		return -EINVAL;
+	}
+	if (len) {
+		if ((len + csta->hs[chanp->hscx].tx_cnt) > MAX_DATA_MEM) {
+			/* Must return 0 here, since this is not an error
+			 * but a temporary lack of resources.
+			 */
+			if (chanp->debug & 2048) {
+				sprintf(tmp, "writebuf: no buffers for %d bytes", len);
+				link_debug(chanp, tmp, 1);
+			}
+			return 0;
+		}
+		save_flags(flags);
+		cli();
+		nskb = skb_clone(skb, GFP_ATOMIC);
+		if (nskb) {
+			if (chanp->lc_b.l2_establish) {
+				csta->hs[chanp->hscx].tx_cnt += len + st->l2.ihsize;
+				chanp->ds.l3.l3l2(&chanp->ds, DL_DATA, nskb);
+			} else {
+				csta->hs[chanp->hscx].tx_cnt += len;
+				chanp->ds.l2.l2l1(&chanp->ds, PH_DATA, nskb);
+			}
+			dev_kfree_skb(skb, FREE_WRITE);
+		} else
+			len = 0;
+		restore_flags(flags);
+	}
+	return (len);
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/config.c linux/drivers/isdn/hisax/config.c
--- v2.0.30/linux/drivers/isdn/hisax/config.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/config.c	Mon Aug  4 17:34:00 1997
@@ -0,0 +1,425 @@
+/* $Id: config.c,v 1.15 1997/04/06 22:57:24 keil Exp $
+
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *              based on the teles driver from Jan den Ouden
+ *
+ *
+ * $Log: config.c,v $
+ * Revision 1.15  1997/04/06 22:57:24  keil
+ * Hisax version 2.1
+ *
+ * Revision 1.14  1997/03/25 23:11:22  keil
+ * US NI-1 protocol
+ *
+ * Revision 1.13  1997/03/23 21:45:49  keil
+ * Add support for ELSA PCMCIA
+ *
+ * Revision 1.12  1997/03/11 21:01:43  keil
+ * nzproto is only used with modules
+ *
+ * Revision 1.11  1997/02/14 12:23:12  fritz
+ * Added support for new insmod parameter handling.
+ *
+ * Revision 1.10  1997/02/14 09:22:09  keil
+ * Final 2.0 version
+ *
+ * Revision 1.9  1997/02/10 11:45:09  fritz
+ * More changes for Kernel 2.1.X compatibility.
+ *
+ * Revision 1.8  1997/02/09 00:28:05  keil
+ * new interface handling, one interface per card
+ * default protocol now works again
+ *
+ * Revision 1.7  1997/01/27 15:56:57  keil
+ * Teles PCMCIA ITK ix1 micro added
+ *
+ * Revision 1.6  1997/01/21 22:17:56  keil
+ * new module load syntax
+ *
+ * Revision 1.5  1997/01/09 18:28:20  keil
+ * cosmetic cleanups
+ *
+ * Revision 1.4  1996/11/05 19:35:17  keil
+ * using config.h; some spelling fixes
+ *
+ * Revision 1.3  1996/10/23 17:23:28  keil
+ * default config changes
+ *
+ * Revision 1.2  1996/10/23 11:58:48  fritz
+ * Changed default setup to reflect user's selection of supported
+ * cards/protocols.
+ *
+ * Revision 1.1  1996/10/13 20:04:51  keil
+ * Initial revision
+ *
+ *
+ *
+ */
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/timer.h>
+#include <linux/config.h>
+#include "hisax.h"
+
+/*
+ * This structure array contains one entry per card. An entry looks
+ * like this:
+ *
+ * { type, protocol, p0, p1, p2, NULL }
+ *
+ * type
+ *    1 Teles 16.0      p0=irq p1=membase p2=iobase
+ *    2 Teles  8.0      p0=irq p1=membase
+ *    3 Teles 16.3      p0=irq p1=iobase
+ *    4 Creatix PNP     p0=irq p1=IO0 (ISAC)  p2=IO1 (HSCX)
+ *    5 AVM A1 (Fritz)  p0=irq p1=iobase
+ *    6 ELSA PC         [p0=iobase] or nothing (autodetect)
+ *    7 ELSA Quickstep  p0=irq p1=iobase
+ *      ELSA PCMCIA     p0=irq p1=iobase
+ *    8 Teles PCMCIA    p0=irq p1=iobase
+ *    9 ITK ix1-micro   p0=irq p1=iobase
+ *
+ *
+ * protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6 or ISDN_PTYPE_NI1
+ *
+ *
+ */
+
+#ifdef CONFIG_HISAX_ELSA_PCC
+#define DEFAULT_CARD ISDN_CTYPE_ELSA
+#define DEFAULT_CFG {0,0,0}
+#endif
+#ifdef CONFIG_HISAX_ELSA_PCMCIA
+#define DEFAULT_CARD ISDN_CTYPE_ELSA_QS1000
+#define DEFAULT_CFG {3,0x2f8,0}
+#endif
+#ifdef CONFIG_HISAX_AVM_A1
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_A1
+#define DEFAULT_CFG {10,0x340,0}
+#endif
+#ifdef CONFIG_HISAX_16_3
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_16_3
+#define DEFAULT_CFG {15,0x180,0}
+#endif
+#ifdef CONFIG_HISAX_16_0
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_16_0
+#define DEFAULT_CFG {15,0xd0000,0xd80}
+#endif
+
+#ifdef CONFIG_HISAX_IX1MICROR2
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_IX1MICROR2
+#define DEFAULT_CFG {5,0x390,0}
+#endif
+
+#ifdef CONFIG_HISAX_1TR6
+#define DEFAULT_PROTO ISDN_PTYPE_1TR6
+#define DEFAULT_PROTO_NAME "1TR6"
+#endif
+#ifdef CONFIG_HISAX_EURO
+#undef DEFAULT_PROTO
+#define DEFAULT_PROTO ISDN_PTYPE_EURO
+#undef DEFAULT_PROTO_NAME
+#define DEFAULT_PROTO_NAME "EURO"
+#endif
+#ifdef CONFIG_HISAX_NI1
+#undef DEFAULT_PROTO
+#define DEFAULT_PROTO ISDN_PTYPE_NI1
+#undef DEFAULT_PROTO_NAME
+#define DEFAULT_PROTO_NAME "NI1"
+#endif
+#ifndef DEFAULT_PROTO
+#define DEFAULT_PROTO ISDN_PTYPE_UNKNOWN
+#define DEFAULT_PROTO_NAME "UNKNOWN"
+#endif
+#ifndef DEFAULT_CARD
+#error "HiSax: No cards configured"
+#endif
+
+#define FIRST_CARD { \
+  DEFAULT_CARD, \
+  DEFAULT_PROTO, \
+  DEFAULT_CFG, \
+  NULL, \
+}
+
+#define EMPTY_CARD	{0, DEFAULT_PROTO, {0, 0, 0}, NULL}
+
+struct IsdnCard cards[] =
+{
+	FIRST_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+};
+
+static char HiSaxID[96] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
+"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
+"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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";
+char *HiSax_id = HiSaxID;
+#ifdef MODULE
+/* Variables for insmod */
+int type[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+int protocol[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+int io[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+#ifdef CONFIG_HISAX_16_3	/* For Creatix/Teles PnP */
+int io0[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+int io1[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+#endif
+int irq[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+int mem[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+char *id = HiSaxID;
+
+#if (LINUX_VERSION_CODE > 0x020111)
+MODULE_AUTHOR("Karsten Keil");
+MODULE_PARM(type, "1-16i");
+MODULE_PARM(protocol, "1-16i");
+MODULE_PARM(io, "1-16i");
+MODULE_PARM(irq, "1-16i");
+MODULE_PARM(mem, "1-16i");
+MODULE_PARM(id, "s");
+#ifdef CONFIG_HISAX_16_3	/* For Creatix/Teles PnP */
+MODULE_PARM(io0, "1-16i");
+MODULE_PARM(io1, "1-16i");
+#endif
+#endif
+
+#endif
+
+extern char *l1_revision;
+extern char *l2_revision;
+extern char *l3_revision;
+extern char *l4_revision;
+extern char *tei_revision;
+
+char *
+HiSax_getrev(const char *revision)
+{
+	char *rev;
+	char *p;
+
+	if ((p = strchr(revision, ':'))) {
+		rev = p + 2;
+		p = strchr(rev, '$');
+		*--p = 0;
+	} else
+		rev = "???";
+	return rev;
+}
+
+int nrcards;
+
+void
+HiSax_mod_dec_use_count(void)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+void
+HiSax_mod_inc_use_count(void)
+{
+	MOD_INC_USE_COUNT;
+}
+
+#ifdef MODULE
+#define HiSax_init init_module
+#else
+void
+HiSax_setup(char *str, int *ints)
+{
+	int i, j, argc;
+
+	argc = ints[0];
+	i = 0;
+	j = 1;
+	while (argc && (i < 16)) {
+		if (argc) {
+			cards[i].typ = ints[j];
+			j++;
+			argc--;
+		}
+		if (argc) {
+			cards[i].protocol = ints[j];
+			j++;
+			argc--;
+		}
+		if (argc) {
+			cards[i].para[0] = ints[j];
+			j++;
+			argc--;
+		}
+		if (argc) {
+			cards[i].para[1] = ints[j];
+			j++;
+			argc--;
+		}
+		if (argc) {
+			cards[i].para[2] = ints[j];
+			j++;
+			argc--;
+		}
+		i++;
+	}
+	if (strlen(str)) {
+		strcpy(HiSaxID, str);
+		HiSax_id = HiSaxID;
+	} else {
+		strcpy(HiSaxID, "HiSax");
+		HiSax_id = HiSaxID;
+	}
+}
+#endif
+
+int
+HiSax_init(void)
+{
+	int i;
+	char tmp[64], rev[64];
+	char *r = rev;
+#ifdef MODULE
+	int nzproto = 0;
+#endif
+	nrcards = 0;
+	strcpy(tmp, l1_revision);
+	r += sprintf(r, "%s/", HiSax_getrev(tmp));
+	strcpy(tmp, l2_revision);
+	r += sprintf(r, "%s/", HiSax_getrev(tmp));
+	strcpy(tmp, l3_revision);
+	r += sprintf(r, "%s/", HiSax_getrev(tmp));
+	strcpy(tmp, l4_revision);
+	r += sprintf(r, "%s/", HiSax_getrev(tmp));
+	strcpy(tmp, tei_revision);
+	r += sprintf(r, "%s", HiSax_getrev(tmp));
+
+	printk(KERN_NOTICE "HiSax: Driver for Siemens chip set ISDN cards\n");
+	printk(KERN_NOTICE "HiSax: Version 2.1\n");
+	printk(KERN_NOTICE "HiSax: Revisions %s\n", rev);
+
+#ifdef MODULE
+	if (id)			/* If id= string used */
+		HiSax_id = id;
+	for (i = 0; i < 16; i++) {
+		cards[i].typ = type[i];
+		if (protocol[i]) {
+			cards[i].protocol = protocol[i];
+			nzproto++;
+		}
+		switch (type[i]) {
+			case ISDN_CTYPE_16_0:
+				cards[i].para[0] = irq[i];
+				cards[i].para[1] = mem[i];
+				cards[i].para[2] = io[i];
+				break;
+
+			case ISDN_CTYPE_8_0:
+				cards[i].para[0] = irq[i];
+				cards[i].para[1] = mem[i];
+				break;
+
+			case ISDN_CTYPE_16_3:
+			case ISDN_CTYPE_TELESPCMCIA:
+				cards[i].para[0] = irq[i];
+				cards[i].para[1] = io[i];
+				break;
+
+#ifdef CONFIG_HISAX_16_3	/* For Creatix/Teles PnP */
+			case ISDN_CTYPE_PNP:
+				cards[i].para[0] = irq[i];
+				cards[i].para[1] = io0[i];
+				cards[i].para[2] = io1[i];
+				break;
+#endif
+			case ISDN_CTYPE_A1:
+				cards[i].para[0] = irq[i];
+				cards[i].para[1] = io[i];
+				break;
+
+			case ISDN_CTYPE_ELSA:
+				cards[i].para[0] = io[i];
+				break;
+			case ISDN_CTYPE_ELSA_QS1000:
+				cards[i].para[0] = irq[i];
+				cards[i].para[1] = io[i];
+				break;
+
+			case ISDN_CTYPE_IX1MICROR2:
+				cards[i].para[0] = irq[i];
+				cards[i].para[1] = io[i];
+				break;
+
+		}
+	}
+	if (!nzproto) {
+		printk(KERN_WARNING "HiSax: Warning - no protocol specified\n");
+		printk(KERN_WARNING "HiSax: Note! module load syntax has changed.\n");
+		printk(KERN_WARNING "HiSax: using protocol %s\n", DEFAULT_PROTO_NAME);
+	}
+#endif
+	if (!HiSax_id)
+		HiSax_id = HiSaxID;
+	if (!HiSaxID[0])
+		strcpy(HiSaxID, "HiSax");
+	for (i = 0; i < 16; i++)
+		if (cards[i].typ > 0)
+			nrcards++;
+	printk(KERN_DEBUG "HiSax: Total %d card%s defined\n",
+	       nrcards, (nrcards > 1) ? "s" : "");
+
+	CallcNew();
+	Isdnl2New();
+	if (HiSax_inithardware()) {
+		/* Install only, if at least one card found */
+		/* No symbols to export, hide all symbols */
+
+#ifdef MODULE
+#if (LINUX_VERSION_CODE < 0x020111)
+		register_symtab(NULL);
+#else
+		EXPORT_NO_SYMBOLS;
+#endif
+		printk(KERN_NOTICE "HiSax: module installed\n");
+#endif
+		return (0);
+	} else {
+		Isdnl2Free();
+		CallcFree();
+		return -EIO;
+	}
+}
+
+#ifdef MODULE
+void
+cleanup_module(void)
+{
+	HiSax_closehardware();
+	printk(KERN_NOTICE "HiSax module removed\n");
+}
+
+#endif
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/elsa.c linux/drivers/isdn/hisax/elsa.c
--- v2.0.30/linux/drivers/isdn/hisax/elsa.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/elsa.c	Tue Aug  5 09:00:12 1997
@@ -0,0 +1,1487 @@
+/* $Id: elsa.c,v 1.14 1997/04/13 19:53:25 keil Exp $
+
+ * elsa.c     low level stuff for Elsa isdn cards
+ *
+ * Author     Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ * Thanks to    Elsa GmbH for documents and informations
+ *
+ *
+ * $Log: elsa.c,v $
+ * Revision 1.14  1997/04/13 19:53:25  keil
+ * Fixed QS1000 init, change in IRQ check delay for SMP
+ *
+ * Revision 1.13  1997/04/07 22:58:07  keil
+ * need include config.h
+ *
+ * Revision 1.12  1997/04/06 22:54:14  keil
+ * Using SKB's
+ *
+ * Revision 1.11  1997/03/23 21:45:46  keil
+ * Add support for ELSA PCMCIA
+ *
+ * Revision 1.10  1997/03/12 21:42:19  keil
+ * Bugfix: IRQ hangs with QS1000
+ *
+ * Revision 1.9  1997/03/04 15:57:39  keil
+ * bugfix IRQ reset Quickstep, ELSA PC changes, some stuff for new cards
+ *
+ * Revision 1.8  1997/01/27 15:51:48  keil
+ * SMP proof,cosmetics
+ *
+ * Revision 1.7  1997/01/21 22:20:48  keil
+ * Elsa Quickstep support
+ *
+ * Revision 1.6  1997/01/09 18:22:46  keil
+ * one more PCC-8 fix
+ *
+ * Revision 1.5  1996/12/08 19:46:14  keil
+ * PCC-8 correct IRQs; starting ARCOFI support
+ *
+ * Revision 1.4  1996/11/18 20:50:54  keil
+ * with PCF Pro release 16 Byte IO
+ *
+ * Revision 1.3  1996/11/18 15:33:04  keil
+ * PCC and PCFPro support
+ *
+ * Revision 1.2  1996/10/27 22:08:03  keil
+ * cosmetic changes
+ *
+ * Revision 1.1  1996/10/13 20:04:52  keil
+ * Initial revision
+ *
+ *
+ */
+
+#define ARCOFI_USE	0
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "siemens.h"
+#include "hisax.h"
+#include "elsa.h"
+#include "isdnl1.h"
+#include <linux/kernel_stat.h>
+
+extern const char *CardType[];
+
+const char *Elsa_revision = "$Revision: 1.14 $";
+const char *Elsa_Types[] =
+{"None", "PC", "PCC-8", "PCC-16", "PCF", "PCF-Pro",
+ "PCMCIA", "QS 1000", "QS 3000"};
+
+const char *ITACVer[] =
+{"?0?", "?1?", "?2?", "?3?", "?4?", "V2.2",
+ "B1", "A1"};
+
+#define byteout(addr,val) outb_p(val,addr)
+#define bytein(addr) inb_p(addr)
+
+static inline u_char
+readhscx(unsigned int adr, int hscx, u_char off)
+{
+	register u_char ret;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(adr + CARD_ALE, off + (hscx ? 0x60 : 0x20));
+	ret = bytein(adr + CARD_HSCX);
+	restore_flags(flags);
+	return (ret);
+}
+
+static inline void
+read_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size)
+{
+	/* fifo read without cli because it's allready done  */
+
+	byteout(adr + CARD_ALE, (hscx ? 0x40 : 0));
+	insb(adr + CARD_HSCX, data, size);
+}
+
+
+static inline void
+writehscx(unsigned int adr, int hscx, u_char off, u_char data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(adr + CARD_ALE, off + (hscx ? 0x60 : 0x20));
+	byteout(adr + CARD_HSCX, data);
+	restore_flags(flags);
+}
+
+static inline void
+write_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size)
+{
+	/* fifo write without cli because it's allready done  */
+	byteout(adr + CARD_ALE, (hscx ? 0x40 : 0));
+	outsb(adr + CARD_HSCX, data, size);
+}
+
+static inline u_char
+readisac(unsigned int adr, u_char off)
+{
+	register u_char ret;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(adr + CARD_ALE, off + 0x20);
+	ret = bytein(adr + CARD_ISAC);
+	restore_flags(flags);
+	return (ret);
+}
+
+static inline void
+read_fifo_isac(unsigned int adr, u_char * data, int size)
+{
+	/* fifo read without cli because it's allready done  */
+
+	byteout(adr + CARD_ALE, 0);
+	insb(adr + CARD_ISAC, data, size);
+}
+
+
+static inline void
+writeisac(unsigned int adr, u_char off, u_char data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(adr + CARD_ALE, off + 0x20);
+	byteout(adr + CARD_ISAC, data);
+	restore_flags(flags);
+}
+
+static inline void
+write_fifo_isac(unsigned int adr, u_char * data, int size)
+{
+	/* fifo write without cli because it's allready done  */
+
+	byteout(adr + CARD_ALE, 0);
+	outsb(adr + CARD_ISAC, data, size);
+}
+
+#ifdef CONFIG_HISAX_ELSA_PCC
+static inline u_char
+readitac(unsigned int adr, u_char off)
+{
+	register u_char ret;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(adr + CARD_ALE, off);
+	ret = bytein(adr + CARD_ITAC);
+	restore_flags(flags);
+	return (ret);
+}
+
+static inline void
+writeitac(unsigned int adr, u_char off, u_char data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(adr + CARD_ALE, off);
+	byteout(adr + CARD_ITAC, data);
+	restore_flags(flags);
+}
+
+static inline int
+TimerRun(struct IsdnCardState *sp)
+{
+	register u_char val;
+
+	val = bytein(sp->cfg_reg + CARD_CONFIG);
+	if (sp->subtyp == ELSA_QS1000)
+		return (0 == (val & TIMER_RUN));
+	else if (sp->subtyp == ELSA_PCC8)
+		return (val & TIMER_RUN_PCC8);
+	return (val & TIMER_RUN);
+}
+
+static inline void
+elsa_led_handler(struct IsdnCardState *sp)
+{
+
+	u_char outval = 0xf0;
+	int stat = 0, cval;
+
+
+	if ((sp->ph_state == 0) || (sp->ph_state == 15)) {
+		stat = 1;
+	} else {
+		if (sp->hs[0].mode != 0)
+			stat |= 2;
+		if (sp->hs[1].mode != 0)
+			stat |= 4;
+	}
+	cval = (sp->counter >> 6) & 3;
+	switch (cval) {
+		case 0:
+			if (!stat)
+				outval |= STAT_LED;
+			else if (stat == 1)
+				outval |= LINE_LED | STAT_LED;
+			else {
+				if (stat & 2)
+					outval |= STAT_LED;
+				if (stat & 4)
+					outval |= LINE_LED;
+			}
+			break;
+		case 1:
+			if (!stat)
+				outval |= LINE_LED;
+			else if (stat == 1)
+				outval |= LINE_LED | STAT_LED;
+			else {
+				if (stat & 2)
+					outval |= STAT_LED;
+				if (stat & 4)
+					outval |= LINE_LED;
+			}
+			break;
+		case 2:
+			if (!stat)
+				outval |= STAT_LED;
+			else if (stat == 1)
+				outval |= 0;
+			else {
+				if (stat & 2)
+					outval |= STAT_LED;
+				if (stat & 4)
+					outval |= LINE_LED;
+			}
+			break;
+		case 3:
+			if (!stat)
+				outval |= LINE_LED;
+			break;
+	}
+	byteout(sp->cfg_reg + CARD_CONTROL, outval);
+}
+#endif
+
+static inline void
+waitforCEC(int adr, int hscx)
+{
+	int to = 50;
+
+	while ((readhscx(adr, hscx, HSCX_STAR) & 0x04) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "Elsa: waitforCEC timeout\n");
+}
+
+
+static inline void
+waitforXFW(int adr, int hscx)
+{
+	int to = 50;
+
+	while ((!(readhscx(adr, hscx, HSCX_STAR) & 0x44) == 0x40) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "Elsa: waitforXFW timeout\n");
+}
+
+static inline void
+writehscxCMDR(int adr, int hscx, u_char data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	waitforCEC(adr, hscx);
+	writehscx(adr, hscx, HSCX_CMDR, data);
+	restore_flags(flags);
+}
+
+/*
+ * fast interrupt here
+ */
+
+
+static void
+hscxreport(struct IsdnCardState *sp, int hscx)
+{
+	printk(KERN_DEBUG "HSCX %d\n", hscx);
+	printk(KERN_DEBUG "ISTA %x\n", readhscx(sp->cfg_reg, hscx, HSCX_ISTA));
+	printk(KERN_DEBUG "STAR %x\n", readhscx(sp->cfg_reg, hscx, HSCX_STAR));
+	printk(KERN_DEBUG "EXIR %x\n", readhscx(sp->cfg_reg, hscx, HSCX_EXIR));
+}
+
+void
+elsa_report(struct IsdnCardState *sp)
+{
+	printk(KERN_DEBUG "ISAC\n");
+	printk(KERN_DEBUG "ISTA %x\n", readisac(sp->cfg_reg, ISAC_ISTA));
+	printk(KERN_DEBUG "STAR %x\n", readisac(sp->cfg_reg, ISAC_STAR));
+	printk(KERN_DEBUG "EXIR %x\n", readisac(sp->cfg_reg, ISAC_EXIR));
+	hscxreport(sp, 0);
+	hscxreport(sp, 1);
+}
+
+/*
+ * HSCX stuff goes here
+ */
+
+static void
+hscx_empty_fifo(struct HscxState *hsp, int count)
+{
+	u_char *ptr;
+	struct IsdnCardState *sp = hsp->sp;
+	long flags;
+
+	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
+		debugl1(sp, "hscx_empty_fifo");
+
+	if (hsp->rcvidx + count > HSCX_BUFMAX) {
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "hscx_empty_fifo: incoming packet too large");
+		writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x80);
+		hsp->rcvidx = 0;
+		return;
+	}
+	ptr = hsp->rcvbuf + hsp->rcvidx;
+	hsp->rcvidx += count;
+	save_flags(flags);
+	cli();
+	read_fifo_hscx(sp->cfg_reg, hsp->hscx, ptr, count);
+	writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x80);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_HSCX_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "hscx_empty_fifo %c cnt %d",
+			     hsp->hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+hscx_fill_fifo(struct HscxState *hsp)
+{
+	struct IsdnCardState *sp = hsp->sp;
+	int more, count;
+	u_char *ptr;
+	long flags;
+
+
+	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
+		debugl1(sp, "hscx_fill_fifo");
+
+	if (!hsp->tx_skb)
+		return;
+	if (hsp->tx_skb->len <= 0)
+		return;
+
+	more = (hsp->mode == 1) ? 1 : 0;
+	if (hsp->tx_skb->len > 32) {
+		more = !0;
+		count = 32;
+	} else
+		count = hsp->tx_skb->len;
+
+	waitforXFW(sp->cfg_reg, hsp->hscx);
+	save_flags(flags);
+	cli();
+	ptr = hsp->tx_skb->data;
+	skb_pull(hsp->tx_skb, count);
+	hsp->tx_cnt -= count;
+	hsp->count += count;
+	write_fifo_hscx(sp->cfg_reg, hsp->hscx, ptr, count);
+	writehscxCMDR(sp->cfg_reg, hsp->hscx, more ? 0x8 : 0xa);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_HSCX_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "hscx_fill_fifo %c cnt %d",
+			     hsp->hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static inline void
+hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx)
+{
+	u_char r;
+	struct HscxState *hsp = sp->hs + hscx;
+	struct sk_buff *skb;
+	int count;
+	char tmp[32];
+
+	if (!hsp->init)
+		return;
+
+	if (val & 0x80) {	/* RME */
+
+		r = readhscx(sp->cfg_reg, hsp->hscx, HSCX_RSTA);
+		if ((r & 0xf0) != 0xa0) {
+			if (!(r & 0x80))
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "HSCX invalid frame");
+			if ((r & 0x40) && hsp->mode)
+				if (sp->debug & L1_DEB_WARN) {
+					sprintf(tmp, "HSCX RDO mode=%d",
+						hsp->mode);
+					debugl1(sp, tmp);
+				}
+			if (!(r & 0x20))
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "HSCX CRC error");
+			writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x80);
+		} else {
+			count = readhscx(sp->cfg_reg, hsp->hscx, HSCX_RBCL) & 0x1f;
+			if (count == 0)
+				count = 32;
+			hscx_empty_fifo(hsp, count);
+			if ((count = hsp->rcvidx - 1) > 0) {
+				if (sp->debug & L1_DEB_HSCX_FIFO) {
+					sprintf(tmp, "HX Frame %d", count);
+					debugl1(sp, tmp);
+				}
+				if (!(skb = dev_alloc_skb(count)))
+					printk(KERN_WARNING "Elsa: receive out of memory\n");
+				else {
+					memcpy(skb_put(skb, count), hsp->rcvbuf, count);
+					skb_queue_tail(&hsp->rqueue, skb);
+				}
+			}
+		}
+		hsp->rcvidx = 0;
+		hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		hscx_empty_fifo(hsp, 32);
+		if (hsp->mode == 1) {
+			/* receive audio data */
+			if (!(skb = dev_alloc_skb(32)))
+				printk(KERN_WARNING "elsa: receive out of memory\n");
+			else {
+				memcpy(skb_put(skb, 32), hsp->rcvbuf, 32);
+				skb_queue_tail(&hsp->rqueue, skb);
+			}
+			hsp->rcvidx = 0;
+			hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+		}
+	}
+	if (val & 0x10) {	/* XPR */
+		if (hsp->tx_skb)
+			if (hsp->tx_skb->len) {
+				hscx_fill_fifo(hsp);
+				return;
+			} else {
+				dev_kfree_skb(hsp->tx_skb, FREE_WRITE);
+				hsp->count = 0;
+				if (hsp->st->l4.l1writewakeup)
+					hsp->st->l4.l1writewakeup(hsp->st);
+				hsp->tx_skb = NULL;
+			}
+		if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) {
+			hsp->count = 0;
+			hscx_fill_fifo(hsp);
+		} else
+			hscx_sched_event(hsp, HSCX_XMTBUFREADY);
+	}
+}
+
+/*
+ * ISAC stuff goes here
+ */
+
+static void
+isac_empty_fifo(struct IsdnCardState *sp, int count)
+{
+	u_char *ptr;
+	long flags;
+
+	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
+		debugl1(sp, "isac_empty_fifo");
+
+	if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) {
+		if (sp->debug & L1_DEB_WARN) {
+			char tmp[40];
+			sprintf(tmp, "isac_empty_fifo overrun %d",
+				sp->rcvidx + count);
+			debugl1(sp, tmp);
+		}
+		writeisac(sp->cfg_reg, ISAC_CMDR, 0x80);
+		sp->rcvidx = 0;
+		return;
+	}
+	ptr = sp->rcvbuf + sp->rcvidx;
+	sp->rcvidx += count;
+	save_flags(flags);
+	cli();
+	read_fifo_isac(sp->cfg_reg, ptr, count);
+	writeisac(sp->cfg_reg, ISAC_CMDR, 0x80);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_ISAC_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "isac_empty_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+isac_fill_fifo(struct IsdnCardState *sp)
+{
+	int count, more;
+	u_char *ptr;
+	long flags;
+
+	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
+		debugl1(sp, "isac_fill_fifo");
+
+	if (!sp->tx_skb)
+		return;
+
+	count = sp->tx_skb->len;
+	if (count <= 0)
+		return;
+
+	more = 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	save_flags(flags);
+	cli();
+	ptr = sp->tx_skb->data;
+	skb_pull(sp->tx_skb, count);
+	sp->tx_cnt += count;
+	write_fifo_isac(sp->cfg_reg, ptr, count);
+	writeisac(sp->cfg_reg, ISAC_CMDR, more ? 0x8 : 0xa);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_ISAC_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "isac_fill_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+ph_command(struct IsdnCardState *sp, unsigned int command)
+{
+	if (sp->debug & L1_DEB_ISAC) {
+		char tmp[32];
+		sprintf(tmp, "ph_command %d", command);
+		debugl1(sp, tmp);
+	}
+	writeisac(sp->cfg_reg, ISAC_CIX0, (command << 2) | 3);
+}
+
+static inline void
+isac_interrupt(struct IsdnCardState *sp, u_char val)
+{
+	u_char exval, v1;
+	struct sk_buff *skb;
+	unsigned int count;
+	char tmp[32];
+#if ARCOFI_USE
+	struct BufHeader *ibh;
+	u_char *ptr;
+#endif
+
+	if (sp->debug & L1_DEB_ISAC) {
+		sprintf(tmp, "ISAC interrupt %x", val);
+		debugl1(sp, tmp);
+	}
+	if (val & 0x80) {	/* RME */
+		exval = readisac(sp->cfg_reg, ISAC_RSTA);
+		if ((exval & 0x70) != 0x20) {
+			if (exval & 0x40)
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "ISAC RDO");
+			if (!(exval & 0x20))
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "ISAC CRC error");
+			writeisac(sp->cfg_reg, ISAC_CMDR, 0x80);
+		} else {
+			count = readisac(sp->cfg_reg, ISAC_RBCL) & 0x1f;
+			if (count == 0)
+				count = 32;
+			isac_empty_fifo(sp, count);
+			if ((count = sp->rcvidx) > 0) {
+				sp->rcvidx = 0;
+				if (!(skb = alloc_skb(count, GFP_ATOMIC)))
+					printk(KERN_WARNING "Elsa: D receive out of memory\n");
+				else {
+					SET_SKB_FREE(skb);
+					memcpy(skb_put(skb, count), sp->rcvbuf, count);
+					skb_queue_tail(&sp->rq, skb);
+				}
+			}
+		}
+		sp->rcvidx = 0;
+		isac_sched_event(sp, ISAC_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		isac_empty_fifo(sp, 32);
+	}
+	if (val & 0x20) {	/* RSC */
+		/* never */
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "ISAC RSC interrupt");
+	}
+	if (val & 0x10) {	/* XPR */
+		if (sp->tx_skb)
+			if (sp->tx_skb->len) {
+				isac_fill_fifo(sp);
+				goto afterXPR;
+			} else {
+				dev_kfree_skb(sp->tx_skb, FREE_WRITE);
+				sp->tx_cnt = 0;
+				sp->tx_skb = NULL;
+			}
+		if ((sp->tx_skb = skb_dequeue(&sp->sq))) {
+			sp->tx_cnt = 0;
+			isac_fill_fifo(sp);
+		} else
+			isac_sched_event(sp, ISAC_XMTBUFREADY);
+	}
+      afterXPR:
+	if (val & 0x04) {	/* CISQ */
+		sp->ph_state = (readisac(sp->cfg_reg, ISAC_CIX0) >> 2)
+		    & 0xf;
+		if (sp->debug & L1_DEB_ISAC) {
+			sprintf(tmp, "l1state %d", sp->ph_state);
+			debugl1(sp, tmp);
+		}
+		isac_new_ph(sp);
+	}
+	if (val & 0x02) {	/* SIN */
+		/* never */
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "ISAC SIN interrupt");
+	}
+	if (val & 0x01) {	/* EXI */
+		exval = readisac(sp->cfg_reg, ISAC_EXIR);
+		if (sp->debug & L1_DEB_WARN) {
+			sprintf(tmp, "ISAC EXIR %02x", exval);
+			debugl1(sp, tmp);
+		}
+		if (exval & 0x08) {
+			v1 = readisac(sp->cfg_reg, ISAC_MOSR);
+			if (sp->debug & L1_DEB_WARN) {
+				sprintf(tmp, "ISAC MOSR %02x", v1);
+				debugl1(sp, tmp);
+			}
+#if ARCOFI_USE
+			if (v1 & 0x08) {
+				if (!sp->mon_rx)
+					if (BufPoolGet(&(sp->mon_rx), &(sp->rbufpool),
+					    GFP_ATOMIC, (void *) 1, 3)) {
+						if (sp->debug & L1_DEB_WARN)
+							debugl1(sp, "ISAC MON RX out of buffers!");
+						writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a);
+						goto afterMONR0;
+					} else
+						sp->mon_rxp = 0;
+				ibh = sp->mon_rx;
+				ptr = DATAPTR(ibh);
+				ptr += sp->mon_rxp;
+				sp->mon_rxp++;
+				if (sp->mon_rxp >= 3072) {
+					writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a);
+					sp->mon_rxp = 0;
+					if (sp->debug & L1_DEB_WARN)
+						debugl1(sp, "ISAC MON RX overflow!");
+					goto afterMONR0;
+				}
+				*ptr = readisac(sp->cfg_reg, ISAC_MOR0);
+				if (sp->debug & L1_DEB_WARN) {
+					sprintf(tmp, "ISAC MOR0 %02x", *ptr);
+					debugl1(sp, tmp);
+				}
+			}
+		      afterMONR0:
+			if (v1 & 0x80) {
+				if (!sp->mon_rx)
+					if (BufPoolGet(&(sp->mon_rx), &(sp->rbufpool),
+					    GFP_ATOMIC, (void *) 1, 3)) {
+						if (sp->debug & L1_DEB_WARN)
+							debugl1(sp, "ISAC MON RX out of buffers!");
+						writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0);
+						goto afterMONR1;
+					} else
+						sp->mon_rxp = 0;
+				ibh = sp->mon_rx;
+				ptr = DATAPTR(ibh);
+				ptr += sp->mon_rxp;
+				sp->mon_rxp++;
+				if (sp->mon_rxp >= 3072) {
+					writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0);
+					sp->mon_rxp = 0;
+					if (sp->debug & L1_DEB_WARN)
+						debugl1(sp, "ISAC MON RX overflow!");
+					goto afterMONR1;
+				}
+				*ptr = readisac(sp->cfg_reg, ISAC_MOR1);
+				if (sp->debug & L1_DEB_WARN) {
+					sprintf(tmp, "ISAC MOR1 %02x", *ptr);
+					debugl1(sp, tmp);
+				}
+			}
+		      afterMONR1:
+			if (v1 & 0x04) {
+				writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a);
+				sp->mon_rx->datasize = sp->mon_rxp;
+				sp->mon_flg |= MON0_RX;
+			}
+			if (v1 & 0x40) {
+				writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0);
+				sp->mon_rx->datasize = sp->mon_rxp;
+				sp->mon_flg |= MON1_RX;
+			}
+			if (v1 == 0x02) {
+				ibh = sp->mon_tx;
+				if (!ibh) {
+					writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a);
+					goto AfterMOX0;
+				}
+				count = ibh->datasize - sp->mon_txp;
+				if (count <= 0) {
+					writeisac(sp->cfg_reg, ISAC_MOCR, 0x0f);
+					BufPoolRelease(sp->mon_tx);
+					sp->mon_tx = NULL;
+					sp->mon_txp = 0;
+					sp->mon_flg |= MON0_TX;
+					goto AfterMOX0;
+				}
+				ptr = DATAPTR(ibh);
+				ptr += sp->mon_txp;
+				sp->mon_txp++;
+				writeisac(sp->cfg_reg, ISAC_MOX0, *ptr);
+			}
+		      AfterMOX0:
+			if (v1 == 0x20) {
+				ibh = sp->mon_tx;
+				if (!ibh) {
+					writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0);
+					goto AfterMOX1;
+				}
+				count = ibh->datasize - sp->mon_txp;
+				if (count <= 0) {
+					writeisac(sp->cfg_reg, ISAC_MOCR, 0xf0);
+					BufPoolRelease(sp->mon_tx);
+					sp->mon_tx = NULL;
+					sp->mon_txp = 0;
+					sp->mon_flg |= MON1_TX;
+					goto AfterMOX1;
+				}
+				ptr = DATAPTR(ibh);
+				ptr += sp->mon_txp;
+				sp->mon_txp++;
+				writeisac(sp->cfg_reg, ISAC_MOX1, *ptr);
+			}
+		      AfterMOX1:
+#endif
+		}
+	}
+}
+
+static inline void
+hscx_int_main(struct IsdnCardState *sp, u_char val)
+{
+
+	u_char exval;
+	struct HscxState *hsp;
+	char tmp[32];
+
+	if (val & 0x01) {
+		hsp = sp->hs + 1;
+		exval = readhscx(sp->cfg_reg, 1, HSCX_EXIR);
+		if (exval == 0x40) {
+			if (hsp->mode == 1)
+				hscx_fill_fifo(hsp);
+			else {
+				/* Here we lost an TX interrupt, so
+				   * restart transmitting the whole frame.
+				 */
+				if (hsp->tx_skb) {
+					skb_push(hsp->tx_skb, hsp->count);
+					hsp->tx_cnt += hsp->count;
+					hsp->count = 0;
+				}
+				writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x01);
+				if (sp->debug & L1_DEB_WARN) {
+					sprintf(tmp, "HSCX B EXIR %x Lost TX", exval);
+					debugl1(sp, tmp);
+				}
+			}
+		} else if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX B EXIR %x", exval);
+			debugl1(sp, tmp);
+		}
+	}
+	if (val & 0xf8) {
+		if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX B interrupt %x", val);
+			debugl1(sp, tmp);
+		}
+		hscx_interrupt(sp, val, 1);
+	}
+	if (val & 0x02) {
+		hsp = sp->hs;
+		exval = readhscx(sp->cfg_reg, 0, HSCX_EXIR);
+		if (exval == 0x40) {
+			if (hsp->mode == 1)
+				hscx_fill_fifo(hsp);
+			else {
+				/* Here we lost an TX interrupt, so
+				   * restart transmitting the whole frame.
+				 */
+				if (hsp->tx_skb) {
+					skb_push(hsp->tx_skb, hsp->count);
+					hsp->tx_cnt += hsp->count;
+					hsp->count = 0;
+				}
+				writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x01);
+				if (sp->debug & L1_DEB_WARN) {
+					sprintf(tmp, "HSCX A EXIR %x Lost TX", exval);
+					debugl1(sp, tmp);
+				}
+			}
+		} else if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX A EXIR %x", exval);
+			debugl1(sp, tmp);
+		}
+	}
+	if (val & 0x04) {
+		exval = readhscx(sp->cfg_reg, 0, HSCX_ISTA);
+		if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX A interrupt %x", exval);
+			debugl1(sp, tmp);
+		}
+		hscx_interrupt(sp, exval, 0);
+	}
+}
+
+static void
+elsa_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *sp;
+	u_char val;
+
+	sp = (struct IsdnCardState *) irq2dev_map[intno];
+
+	if (!sp) {
+		printk(KERN_WARNING "Elsa: Spurious interrupt!\n");
+		return;
+	}
+#ifdef CONFIG_HISAX_ELSA_PCC
+      INT_RESTART:
+	if (!TimerRun(sp)) {
+		/* Timer Restart */
+		byteout(sp->cfg_reg + CARD_START_TIMER, 0);
+		if (!(sp->counter++ & 0x3f)) {
+			/* Call LEDs all 64 tics */
+			elsa_led_handler(sp);
+		}
+	}
+#endif
+	val = readhscx(sp->cfg_reg, 1, HSCX_ISTA);
+      Start_HSCX:
+	if (val) {
+		hscx_int_main(sp, val);
+	}
+	val = readisac(sp->cfg_reg, ISAC_ISTA);
+      Start_ISAC:
+	if (val) {
+		isac_interrupt(sp, val);
+	}
+#ifdef CONFIG_HISAX_ELSA_PCC
+	if (!TimerRun(sp))
+		goto INT_RESTART;
+#endif
+	val = readhscx(sp->cfg_reg, 1, HSCX_ISTA);
+	if (val) {
+		if (sp->debug & L1_DEB_HSCX)
+			debugl1(sp, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readisac(sp->cfg_reg, ISAC_ISTA);
+	if (val) {
+		if (sp->debug & L1_DEB_ISAC)
+			debugl1(sp, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	writehscx(sp->cfg_reg, 0, HSCX_MASK, 0xFF);
+	writehscx(sp->cfg_reg, 1, HSCX_MASK, 0xFF);
+	writeisac(sp->cfg_reg, ISAC_MASK, 0xFF);
+#ifdef CONFIG_HISAX_ELSA_PCC
+	if (sp->subtyp == ELSA_QS1000) {
+		byteout(sp->cfg_reg + CARD_START_TIMER, 0);
+		byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff);
+	}
+#endif
+	writehscx(sp->cfg_reg, 0, HSCX_MASK, 0x0);
+	writehscx(sp->cfg_reg, 1, HSCX_MASK, 0x0);
+	writeisac(sp->cfg_reg, ISAC_MASK, 0x0);
+}
+
+
+static void
+initisac(struct IsdnCardState *sp)
+{
+	unsigned int adr = sp->cfg_reg;
+
+	/* Elsa IOM 2 Mode */
+	writeisac(adr, ISAC_MASK, 0xff);
+	writeisac(adr, ISAC_ADF2, 0x80);
+	writeisac(adr, ISAC_SQXR, 0x2f);
+	writeisac(adr, ISAC_SPCR, 0x00);
+	writeisac(adr, ISAC_STCR, 0x70);
+	writeisac(adr, ISAC_MODE, 0xc9);
+	writeisac(adr, ISAC_TIMR, 0x00);
+	writeisac(adr, ISAC_ADF1, 0x00);
+	writeisac(adr, ISAC_CIX0, (1 << 2) | 3);
+	writeisac(adr, ISAC_MASK, 0xff);
+	writeisac(adr, ISAC_MASK, 0x0);
+}
+
+static void
+modehscx(struct HscxState *hs, int mode, int ichan)
+{
+	struct IsdnCardState *sp = hs->sp;
+	int hscx = hs->hscx;
+
+	if (sp->debug & L1_DEB_HSCX) {
+		char tmp[40];
+		sprintf(tmp, "hscx %c mode %d ichan %d",
+			'A' + hscx, mode, ichan);
+		debugl1(sp, tmp);
+	}
+	hs->mode = mode;
+	writehscx(sp->cfg_reg, hscx, HSCX_CCR1, 0x85);
+	writehscx(sp->cfg_reg, hscx, HSCX_XAD1, 0xFF);
+	writehscx(sp->cfg_reg, hscx, HSCX_XAD2, 0xFF);
+	writehscx(sp->cfg_reg, hscx, HSCX_RAH2, 0xFF);
+	writehscx(sp->cfg_reg, hscx, HSCX_XBCH, 0x0);
+	writehscx(sp->cfg_reg, hscx, HSCX_RLCR, 0x0);
+	writehscx(sp->cfg_reg, hscx, HSCX_CCR2, 0x30);
+
+	switch (mode) {
+		case (0):
+			writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0xff);
+			writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0xff);
+			writehscx(sp->cfg_reg, hscx, HSCX_XCCR, 7);
+			writehscx(sp->cfg_reg, hscx, HSCX_RCCR, 7);
+			writehscx(sp->cfg_reg, hscx, HSCX_MODE, 0x84);
+			break;
+		case (1):
+			if (ichan == 0) {
+				writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0x2f);
+				writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0x2f);
+			} else {
+				writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0x3);
+				writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0x3);
+			}
+			writehscx(sp->cfg_reg, hscx, HSCX_XCCR, 7);
+			writehscx(sp->cfg_reg, hscx, HSCX_RCCR, 7);
+			writehscx(sp->cfg_reg, hscx, HSCX_MODE, 0xe4);
+			writehscx(sp->cfg_reg, hscx, HSCX_CMDR, 0x41);
+			break;
+		case (2):
+			if (ichan == 0) {
+				writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0x2f);
+				writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0x2f);
+			} else {
+				writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0x3);
+				writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0x3);
+			}
+			writehscx(sp->cfg_reg, hscx, HSCX_XCCR, 7);
+			writehscx(sp->cfg_reg, hscx, HSCX_RCCR, 7);
+			writehscx(sp->cfg_reg, hscx, HSCX_MODE, 0x8c);
+			writehscx(sp->cfg_reg, hscx, HSCX_CMDR, 0x41);
+			break;
+	}
+	writehscx(sp->cfg_reg, hscx, HSCX_ISTA, 0x00);
+}
+
+void
+release_io_elsa(struct IsdnCard *card)
+{
+	int bytecnt = 8;
+
+	if (card->sp->subtyp == ELSA_PCFPRO)
+		bytecnt = 16;
+	if (card->sp->cfg_reg)
+		release_region(card->sp->cfg_reg, bytecnt);
+}
+
+static void
+reset_elsa(struct IsdnCardState *sp)
+{
+#ifdef CONFIG_HISAX_ELSA_PCC
+	/* Wait 1 Timer */
+	byteout(sp->cfg_reg + CARD_START_TIMER, 0);
+	while (TimerRun(sp));
+	byteout(sp->cfg_reg + CARD_CONTROL, 0x00);	/* Reset On */
+	/* Wait 1 Timer */
+	byteout(sp->cfg_reg + CARD_START_TIMER, 0);
+	while (TimerRun(sp));
+	byteout(sp->cfg_reg + CARD_CONTROL, ISDN_RESET);	/* Reset Off */
+	/* Wait 1 Timer */
+	byteout(sp->cfg_reg + CARD_START_TIMER, 0);
+	while (TimerRun(sp));
+	byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff);
+#endif
+}
+
+static void
+clear_pending_ints(struct IsdnCardState *sp)
+{
+#ifdef CONFIG_HISAX_ELSA_PCMCIA
+	int val;
+	char tmp[64];
+
+	val = readhscx(sp->cfg_reg, 1, HSCX_ISTA);
+	sprintf(tmp, "HSCX B ISTA %x", val);
+	debugl1(sp, tmp);
+	if (val & 0x01) {
+		val = readhscx(sp->cfg_reg, 1, HSCX_EXIR);
+		sprintf(tmp, "HSCX B EXIR %x", val);
+		debugl1(sp, tmp);
+	} else if (val & 0x02) {
+		val = readhscx(sp->cfg_reg, 0, HSCX_EXIR);
+		sprintf(tmp, "HSCX A EXIR %x", val);
+		debugl1(sp, tmp);
+	}
+	val = readhscx(sp->cfg_reg, 0, HSCX_ISTA);
+	sprintf(tmp, "HSCX A ISTA %x", val);
+	debugl1(sp, tmp);
+	val = readhscx(sp->cfg_reg, 1, HSCX_STAR);
+	sprintf(tmp, "HSCX B STAR %x", val);
+	debugl1(sp, tmp);
+	val = readhscx(sp->cfg_reg, 0, HSCX_STAR);
+	sprintf(tmp, "HSCX A STAR %x", val);
+	debugl1(sp, tmp);
+	val = readisac(sp->cfg_reg, ISAC_STAR);
+	sprintf(tmp, "ISAC STAR %x", val);
+	debugl1(sp, tmp);
+	val = readisac(sp->cfg_reg, ISAC_MODE);
+	sprintf(tmp, "ISAC MODE %x", val);
+	debugl1(sp, tmp);
+	val = readisac(sp->cfg_reg, ISAC_ADF2);
+	sprintf(tmp, "ISAC ADF2 %x", val);
+	debugl1(sp, tmp);
+	val = readisac(sp->cfg_reg, ISAC_ISTA);
+	sprintf(tmp, "ISAC ISTA %x", val);
+	debugl1(sp, tmp);
+	if (val & 0x01) {
+		val = readisac(sp->cfg_reg, ISAC_EXIR);
+		sprintf(tmp, "ISAC EXIR %x", val);
+		debugl1(sp, tmp);
+	} else if (val & 0x04) {
+		val = readisac(sp->cfg_reg, ISAC_CIR0);
+		sprintf(tmp, "ISAC CIR0 %x", val);
+		debugl1(sp, tmp);
+	}
+#endif
+	writehscx(sp->cfg_reg, 0, HSCX_MASK, 0xFF);
+	writehscx(sp->cfg_reg, 1, HSCX_MASK, 0xFF);
+	writeisac(sp->cfg_reg, ISAC_MASK, 0xFF);
+#ifdef CONFIG_HISAX_ELSA_PCC
+	if (sp->subtyp == ELSA_QS1000) {
+		byteout(sp->cfg_reg + CARD_START_TIMER, 0);
+		byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff);
+	}
+#endif
+	writehscx(sp->cfg_reg, 0, HSCX_MASK, 0x0);
+	writehscx(sp->cfg_reg, 1, HSCX_MASK, 0x0);
+	writeisac(sp->cfg_reg, ISAC_MASK, 0x0);
+	writeisac(sp->cfg_reg, ISAC_CMDR, 0x41);
+}
+
+static void
+check_arcofi(struct IsdnCardState *sp)
+{
+#if 0
+	u_char val;
+	char tmp[40];
+	char *t;
+	long flags;
+	u_char *p;
+
+	if (BufPoolGet(&(sp->mon_tx), &(sp->sbufpool),
+		       GFP_ATOMIC, (void *) 1, 3)) {
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "ISAC MON TX out of buffers!");
+		return;
+	} else
+		sp->mon_txp = 0;
+	p = DATAPTR(sp->mon_tx);
+	*p++ = 0xa0;
+	*p++ = 0x0;
+	sp->mon_tx->datasize = 2;
+	sp->mon_txp = 1;
+	sp->mon_flg = 0;
+	writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0);
+	val = readisac(sp->cfg_reg, ISAC_MOSR);
+	writeisac(sp->cfg_reg, ISAC_MOX1, 0xa0);
+	writeisac(sp->cfg_reg, ISAC_MOCR, 0xb0);
+	save_flags(flags);
+	sti();
+	HZDELAY(3);
+	restore_flags(flags);
+	if (sp->mon_flg & MON1_TX) {
+		if (sp->mon_flg & MON1_RX) {
+			sprintf(tmp, "Arcofi response received %d bytes", sp->mon_rx->datasize);
+			debugl1(sp, tmp);
+			p = DATAPTR(sp->mon_rx);
+			t = tmp;
+			t += sprintf(tmp, "Arcofi data");
+			QuickHex(t, p, sp->mon_rx->datasize);
+			debugl1(sp, tmp);
+			BufPoolRelease(sp->mon_rx);
+			sp->mon_rx = NULL;
+			sp->mon_rxp = 0;
+			sp->mon_flg = 0;
+		}
+	} else if (sp->mon_tx) {
+		BufPoolRelease(sp->mon_tx);
+		sp->mon_tx = NULL;
+		sp->mon_txp = 0;
+		sprintf(tmp, "Arcofi not detected");
+		debugl1(sp, tmp);
+	}
+	sp->mon_flg = 0;
+#endif
+}
+
+int
+initelsa(struct IsdnCardState *sp)
+{
+	int ret, irq_cnt, cnt = 3;
+	long flags;
+
+	irq_cnt = kstat.interrupts[sp->irq];
+	printk(KERN_INFO "Elsa: IRQ %d count %d\n", sp->irq, irq_cnt);
+	ret = get_irq(sp->cardnr, &elsa_interrupt);
+#ifdef CONFIG_HISAX_ELSA_PCC
+	byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff);
+#endif
+	while (ret && cnt) {
+		sp->counter = 0;
+		clear_pending_ints(sp);
+		initisac(sp);
+		sp->modehscx(sp->hs, 0, 0);
+		sp->modehscx(sp->hs + 1, 0, 0);
+		save_flags(flags);
+		sp->counter = 0;
+		sti();
+#ifdef CONFIG_HISAX_ELSA_PCC
+		byteout(sp->cfg_reg + CARD_CONTROL, ISDN_RESET | ENABLE_TIM_INT);
+		byteout(sp->cfg_reg + CARD_START_TIMER, 0);
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = jiffies + (110 * HZ) / 1000;		/* Timeout 110ms */
+		schedule();
+		restore_flags(flags);
+		printk(KERN_INFO "Elsa: %d timer tics in 110 msek\n",
+		       sp->counter);
+		if (abs(sp->counter - 13) < 3) {
+			printk(KERN_INFO "Elsa: timer and irq OK\n");
+		} else {
+			printk(KERN_WARNING
+			       "Elsa: timer tic problem (%d/12) maybe an IRQ(%d) conflict\n",
+			       sp->counter, sp->irq);
+		}
+#endif
+		printk(KERN_INFO "Elsa: IRQ %d count %d\n", sp->irq,
+		       kstat.interrupts[sp->irq]);
+		if (kstat.interrupts[sp->irq] == irq_cnt) {
+			printk(KERN_WARNING
+			       "Elsa: IRQ(%d) getting no interrupts during init %d\n",
+			       sp->irq, 4 - cnt);
+			if (cnt == 1) {
+				irq2dev_map[sp->irq] = NULL;
+				free_irq(sp->irq, NULL);
+				return (0);
+			} else {
+				reset_elsa(sp);
+				cnt--;
+			}
+		} else {
+			check_arcofi(sp);
+			cnt = 0;
+		}
+	}
+	sp->counter = 0;
+	return (ret);
+}
+
+#ifdef CONFIG_HISAX_ELSA_PCC
+static unsigned char
+probe_elsa_adr(unsigned int adr)
+{
+	int i, in1, in2, p16_1 = 0, p16_2 = 0, p8_1 = 0, p8_2 = 0, pc_1 = 0,
+	 pc_2 = 0, pfp_1 = 0, pfp_2 = 0;
+	long flags;
+
+	if (check_region(adr, 8)) {
+		printk(KERN_WARNING
+		       "Elsa: Probing Port 0x%x: already in use\n",
+		       adr);
+		return (0);
+	}
+	save_flags(flags);
+	cli();
+	for (i = 0; i < 16; i++) {
+		in1 = inb(adr + CARD_CONFIG);	/* 'toggelt' bei */
+		in2 = inb(adr + CARD_CONFIG);	/* jedem Zugriff */
+		p16_1 += 0x04 & in1;
+		p16_2 += 0x04 & in2;
+		p8_1 += 0x02 & in1;
+		p8_2 += 0x02 & in2;
+		pc_1 += 0x01 & in1;
+		pc_2 += 0x01 & in2;
+		pfp_1 += 0x40 & in1;
+		pfp_2 += 0x40 & in2;
+	}
+	restore_flags(flags);
+	printk(KERN_INFO "Elsa: Probing IO 0x%x", adr);
+	if (65 == ++p16_1 * ++p16_2) {
+		printk(" PCC-16/PCF found\n");
+		return (ELSA_PCC16);
+	} else if (1025 == ++pfp_1 * ++pfp_2) {
+		printk(" PCF-Pro found\n");
+		return (ELSA_PCFPRO);
+	} else if (33 == ++p8_1 * ++p8_2) {
+		printk(" PCC8 found\n");
+		return (ELSA_PCC8);
+	} else if (17 == ++pc_1 * ++pc_2) {
+		printk(" PC found\n");
+		return (ELSA_PC);
+	} else {
+		printk(" failed\n");
+		return (0);
+	}
+}
+
+static unsigned int
+probe_elsa(struct IsdnCardState *sp)
+{
+	int i;
+	unsigned int CARD_portlist[] =
+	{0x160, 0x170, 0x260, 0x360, 0};
+
+	for (i = 0; CARD_portlist[i]; i++) {
+		if ((sp->subtyp = probe_elsa_adr(CARD_portlist[i])))
+			break;
+	}
+	return (CARD_portlist[i]);
+}
+#endif
+
+int
+setup_elsa(struct IsdnCard *card)
+{
+#ifdef CONFIG_HISAX_ELSA_PCC
+	long flags;
+#endif
+	int bytecnt;
+	u_char val, verA, verB;
+	struct IsdnCardState *sp = card->sp;
+	char tmp[64];
+
+	strcpy(tmp, Elsa_revision);
+	printk(KERN_NOTICE "HiSax: Elsa driver Rev. %s\n", HiSax_getrev(tmp));
+#ifdef CONFIG_HISAX_ELSA_PCC
+	if (sp->typ == ISDN_CTYPE_ELSA) {
+		sp->cfg_reg = card->para[0];
+		printk(KERN_INFO "Elsa: Microlink IO probing\n");
+		if (sp->cfg_reg) {
+			if (!(sp->subtyp = probe_elsa_adr(sp->cfg_reg))) {
+				printk(KERN_WARNING
+				     "Elsa: no Elsa Microlink at 0x%x\n",
+				       sp->cfg_reg);
+				return (0);
+			}
+		} else
+			sp->cfg_reg = probe_elsa(sp);
+		if (sp->cfg_reg) {
+			val = bytein(sp->cfg_reg + CARD_CONFIG);
+			if (sp->subtyp == ELSA_PC) {
+				const u_char CARD_IrqTab[8] =
+				{7, 3, 5, 9, 0, 0, 0, 0};
+				sp->irq = CARD_IrqTab[(val & IRQ_INDEX_PC) >> 2];
+			} else if (sp->subtyp == ELSA_PCC8) {
+				const u_char CARD_IrqTab[8] =
+				{7, 3, 5, 9, 0, 0, 0, 0};
+				sp->irq = CARD_IrqTab[(val & IRQ_INDEX_PCC8) >> 4];
+			} else {
+				const u_char CARD_IrqTab[8] =
+				{15, 10, 15, 3, 11, 5, 11, 9};
+				sp->irq = CARD_IrqTab[(val & IRQ_INDEX) >> 3];
+			}
+			val = bytein(sp->cfg_reg + CARD_ALE) & 0x7;
+			if (val < 3)
+				val |= 8;
+			val += 'A' - 3;
+			if (val == 'B' || val == 'C')
+				val ^= 1;
+			if ((sp->subtyp == ELSA_PCFPRO) && (val = 'G'))
+				val = 'C';
+			printk(KERN_INFO
+			       "Elsa: %s found at 0x%x Rev.:%c IRQ %d\n",
+			       Elsa_Types[sp->subtyp],
+			       sp->cfg_reg,
+			       val, sp->irq);
+			val = bytein(sp->cfg_reg + CARD_ALE) & 0x08;
+			if (val)
+				printk(KERN_WARNING
+				   "Elsa: Microlink S0 bus power bad\n");
+		} else {
+			printk(KERN_WARNING
+			       "No Elsa Microlink found\n");
+			return (0);
+		}
+	} else if (sp->typ == ISDN_CTYPE_ELSA_QS1000) {
+		sp->cfg_reg = card->para[1];
+		sp->irq = card->para[0];
+		sp->subtyp = ELSA_QS1000;
+		printk(KERN_INFO
+		       "Elsa: %s found at 0x%x IRQ %d\n",
+		       Elsa_Types[sp->subtyp],
+		       sp->cfg_reg,
+		       sp->irq);
+	} else
+		return (0);
+#endif
+#ifdef CONFIG_HISAX_ELSA_PCMCIA
+	if (sp->typ == ISDN_CTYPE_ELSA_QS1000) {
+		sp->cfg_reg = card->para[1];
+		sp->irq = card->para[0];
+		sp->subtyp = ELSA_PCMCIA;
+		printk(KERN_INFO
+		       "Elsa: %s found at 0x%x IRQ %d\n",
+		       Elsa_Types[sp->subtyp],
+		       sp->cfg_reg,
+		       sp->irq);
+	} else
+		return (0);
+#endif
+
+	switch (sp->subtyp) {
+		case ELSA_PC:
+			bytecnt = 8;
+			break;
+		case ELSA_PCC8:
+			bytecnt = 8;
+			break;
+		case ELSA_PCFPRO:
+			bytecnt = 16;
+			break;
+		case ELSA_PCC16:
+			bytecnt = 8;
+			break;
+		case ELSA_PCF:
+			bytecnt = 16;
+			break;
+		case ELSA_QS1000:
+			bytecnt = 8;
+			break;
+		case ELSA_PCMCIA:
+			bytecnt = 8;
+			break;
+		default:
+			printk(KERN_WARNING
+			       "Unknown ELSA subtype %d\n", sp->subtyp);
+			return (0);
+	}
+
+	if (check_region((sp->cfg_reg), bytecnt)) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       sp->cfg_reg,
+		       sp->cfg_reg + bytecnt);
+		return (0);
+	} else {
+		request_region(sp->cfg_reg, bytecnt, "elsa isdn");
+	}
+
+	/* Teste Timer */
+#ifdef CONFIG_HISAX_ELSA_PCC
+	byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff);
+	byteout(sp->cfg_reg + CARD_START_TIMER, 0);
+	if (!TimerRun(sp)) {
+		byteout(sp->cfg_reg + CARD_START_TIMER, 0);	/* 2. Versuch */
+		if (!TimerRun(sp)) {
+			printk(KERN_WARNING
+			       "Elsa: timer do not start\n");
+			release_io_elsa(card);
+			return (0);
+		}
+	}
+	save_flags(flags);
+	sti();
+	HZDELAY(1);		/* wait >=10 ms */
+	restore_flags(flags);
+	if (TimerRun(sp)) {
+		printk(KERN_WARNING "Elsa: timer do not run down\n");
+		release_io_elsa(card);
+		return (0);
+	}
+	printk(KERN_INFO "Elsa: timer OK; resetting card\n");
+	reset_elsa(sp);
+#endif
+	verA = readhscx(sp->cfg_reg, 0, HSCX_VSTR) & 0xf;
+	verB = readhscx(sp->cfg_reg, 1, HSCX_VSTR) & 0xf;
+	printk(KERN_INFO "Elsa: HSCX version A: %s  B: %s\n",
+	       HscxVersion(verA), HscxVersion(verB));
+	val = readisac(sp->cfg_reg, ISAC_RBCH);
+	printk(KERN_INFO "Elsa: ISAC %s\n",
+	       ISACVersion(val));
+
+#ifdef CONFIG_HISAX_ELSA_PCMCIA
+	if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) {
+		printk(KERN_WARNING
+		       "Elsa: wrong HSCX versions check IO address\n");
+		release_io_elsa(card);
+		return (0);
+	}
+#endif
+
+#ifdef CONFIG_HISAX_ELSA_PCC
+	if (sp->subtyp == ELSA_PC) {
+		val = readitac(sp->cfg_reg, ITAC_SYS);
+		printk(KERN_INFO "Elsa: ITAC version %s\n", ITACVer[val & 7]);
+		writeitac(sp->cfg_reg, ITAC_ISEN, 0);
+		writeitac(sp->cfg_reg, ITAC_RFIE, 0);
+		writeitac(sp->cfg_reg, ITAC_XFIE, 0);
+		writeitac(sp->cfg_reg, ITAC_SCIE, 0);
+		writeitac(sp->cfg_reg, ITAC_STIE, 0);
+	}
+#endif
+	sp->modehscx = &modehscx;
+	sp->ph_command = &ph_command;
+	sp->hscx_fill_fifo = &hscx_fill_fifo;
+	sp->isac_fill_fifo = &isac_fill_fifo;
+
+	return (1);
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/elsa.h linux/drivers/isdn/hisax/elsa.h
--- v2.0.30/linux/drivers/isdn/hisax/elsa.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/elsa.h	Mon Aug  4 17:34:00 1997
@@ -0,0 +1,90 @@
+/* $Id: elsa.h,v 1.6 1997/03/23 21:45:48 keil Exp $
+ *
+ * elsa.h   Header for Elsa ISDN cards
+ *
+ * Author	Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ * Thanks to    Elsa GmbH for documents and informations
+ *
+ *
+ * $Log: elsa.h,v $
+ * Revision 1.6  1997/03/23 21:45:48  keil
+ * Add support for ELSA PCMCIA
+ *
+ * Revision 1.5  1997/03/04 15:58:13  keil
+ * ELSA PC changes, some stuff for new cards
+ *
+ * Revision 1.4  1997/01/21 22:21:05  keil
+ * Elsa Quickstep support
+ *
+ * Revision 1.3  1996/12/08 19:47:38  keil
+ * ARCOFI support
+ *
+ * Revision 1.2  1996/11/18 15:33:35  keil
+ * PCC and PCFPro support
+ *
+ * Revision 1.1  1996/10/13 20:03:45  keil
+ * Initial revision
+ *
+ *
+*/
+#include <linux/config.h>
+
+#ifdef CONFIG_HISAX_ELSA_PCMCIA
+#define CARD_ISAC	1
+#define CARD_HSCX	2
+#define CARD_ALE	4
+#else
+#define CARD_ISAC	0
+#define CARD_ITAC	1
+#define CARD_HSCX	2
+#define CARD_ALE	3
+#define CARD_CONTROL	4
+#define CARD_CONFIG	5
+#define CARD_START_TIMER 6
+#define CARD_TRIG_IRQ	7
+#endif
+
+#define ELSA_PC      1
+#define ELSA_PCC8    2
+#define ELSA_PCC16   3
+#define ELSA_PCF     4
+#define ELSA_PCFPRO  5
+#define ELSA_PCMCIA  6
+#define ELSA_QS1000  7
+#define ELSA_QS3000  8
+
+/* ITAC Registeradressen (only Microlink PC) */
+#define ITAC_SYS	0x34
+#define ITAC_ISEN	0x48
+#define ITAC_RFIE	0x4A
+#define ITAC_XFIE	0x4C
+#define ITAC_SCIE	0x4E
+#define ITAC_STIE	0x46
+
+/***                                                                    ***
+ ***   Makros als Befehle fuer die Kartenregister                       ***
+ ***   (mehrere Befehle werden durch Bit-Oderung kombiniert)            ***
+ ***                                                                    ***/
+
+/* Config-Register (Read) */
+#define TIMER_RUN       0x02    /* Bit 1 des Config-Reg     */
+#define TIMER_RUN_PCC8  0x01    /* Bit 0 des Config-Reg  bei PCC */
+#define IRQ_INDEX       0x38    /* Bit 3,4,5 des Config-Reg */
+#define IRQ_INDEX_PCC8  0x30    /* Bit 4,5 des Config-Reg */
+#define IRQ_INDEX_PC    0x0c    /* Bit 2,3 des Config-Reg */
+
+/* Control-Register (Write) */
+#define LINE_LED        0x02    /* Bit 1 Gelbe LED */
+#define STAT_LED        0x08    /* Bit 3 Gruene LED */
+#define ISDN_RESET      0x20    /* Bit 5 Reset-Leitung */
+#define ENABLE_TIM_INT  0x80    /* Bit 7 Freigabe Timer Interrupt */
+
+/* ALE-Register (Read) */
+#define HW_RELEASE      0x07    /* Bit 0-2 Hardwarerkennung */
+#define S0_POWER_BAD    0x08    /* Bit 3 S0-Bus Spannung fehlt */
+
+extern	void elsa_report(struct IsdnCardState *sp);
+extern  void release_io_elsa(struct IsdnCard *card);
+extern	int  setup_elsa(struct IsdnCard *card);
+extern  int  initelsa(struct IsdnCardState *sp);
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/fsm.c linux/drivers/isdn/hisax/fsm.c
--- v2.0.30/linux/drivers/isdn/hisax/fsm.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/fsm.c	Mon Aug  4 17:34:00 1997
@@ -0,0 +1,183 @@
+/* $Id: fsm.c,v 1.4 1997/04/06 22:56:42 keil Exp $
+
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *              based on the teles driver from Jan den Ouden
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ * $Log: fsm.c,v $
+ * Revision 1.4  1997/04/06 22:56:42  keil
+ * Some cosmetic changes
+ *
+ * Revision 1.3  1997/02/16 01:04:08  fritz
+ * Bugfix: Changed timer handling caused hang with 2.1.X
+ *
+ * Revision 1.2  1997/01/09 20:57:27  keil
+ * cleanup & FSM_TIMER_DEBUG
+ *
+ * Revision 1.1  1996/10/13 20:04:52  keil
+ * Initial revision
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "hisax.h"
+
+#define FSM_TIMER_DEBUG 0
+
+void
+FsmNew(struct Fsm *fsm,
+       struct FsmNode *fnlist, int fncount)
+{
+	int i;
+
+	fsm->jumpmatrix = (int *)
+	    kmalloc(4L * fsm->state_count * fsm->event_count, GFP_KERNEL);
+	memset(fsm->jumpmatrix, 0, 4L * fsm->state_count * fsm->event_count);
+
+	for (i = 0; i < fncount; i++)
+		fsm->jumpmatrix[fsm->state_count * fnlist[i].event +
+			      fnlist[i].state] = (int) fnlist[i].routine;
+}
+
+void
+FsmFree(struct Fsm *fsm)
+{
+	kfree((void *) fsm->jumpmatrix);
+}
+
+int
+FsmEvent(struct FsmInst *fi, int event, void *arg)
+{
+	void (*r) (struct FsmInst *, int, void *);
+	char str[80];
+
+	r = (void (*)) fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state];
+	if (r) {
+		if (fi->debug) {
+			sprintf(str, "State %s Event %s",
+				fi->fsm->strState[fi->state],
+				fi->fsm->strEvent[event]);
+			fi->printdebug(fi, str);
+		}
+		r(fi, event, arg);
+		return (0);
+	} else {
+		if (fi->debug) {
+			sprintf(str, "State %s Event %s no routine",
+				fi->fsm->strState[fi->state],
+				fi->fsm->strEvent[event]);
+			fi->printdebug(fi, str);
+		}
+		return (!0);
+	}
+}
+
+void
+FsmChangeState(struct FsmInst *fi, int newstate)
+{
+	char str[80];
+
+	fi->state = newstate;
+	if (fi->debug) {
+		sprintf(str, "ChangeState %s",
+			fi->fsm->strState[newstate]);
+		fi->printdebug(fi, str);
+	}
+}
+
+static void
+FsmExpireTimer(struct FsmTimer *ft)
+{
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug) {
+		char str[40];
+		sprintf(str, "FsmExpireTimer %lx", (long) ft);
+		ft->fi->printdebug(ft->fi, str);
+	}
+#endif
+	FsmEvent(ft->fi, ft->event, ft->arg);
+}
+
+void
+FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft)
+{
+	ft->fi = fi;
+	ft->tl.function = (void *) FsmExpireTimer;
+	ft->tl.data = (long) ft;
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug) {
+		char str[40];
+		sprintf(str, "FsmInitTimer %lx", (long) ft);
+		ft->fi->printdebug(ft->fi, str);
+	}
+#endif
+	init_timer(&ft->tl);
+}
+
+void
+FsmDelTimer(struct FsmTimer *ft, int where)
+{
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug) {
+		char str[40];
+		sprintf(str, "FsmDelTimer %lx %d", (long) ft, where);
+		ft->fi->printdebug(ft->fi, str);
+	}
+#endif
+	del_timer(&ft->tl);
+}
+
+int
+FsmAddTimer(struct FsmTimer *ft,
+	    int millisec, int event, void *arg, int where)
+{
+
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug) {
+		char str[40];
+		sprintf(str, "FsmAddTimer %lx %d %d", (long) ft, millisec, where);
+		ft->fi->printdebug(ft->fi, str);
+	}
+#endif
+
+	if (ft->tl.next || ft->tl.prev) {
+		printk(KERN_WARNING "FsmAddTimer: timer already active!\n");
+		ft->fi->printdebug(ft->fi, "FsmAddTimer already active!");
+		return -1;
+	}
+	init_timer(&ft->tl);
+	ft->event = event;
+	ft->arg = arg;
+	ft->tl.expires = jiffies + (millisec * HZ) / 1000;
+	add_timer(&ft->tl);
+	return 0;
+}
+
+int
+FsmTimerRunning(struct FsmTimer *ft)
+{
+	return (ft->tl.next != NULL);
+}
+
+void
+jiftime(char *s, long mark)
+{
+	s += 8;
+
+	*s-- = '\0';
+	*s-- = mark % 10 + '0';
+	mark /= 10;
+	*s-- = mark % 10 + '0';
+	mark /= 10;
+	*s-- = '.';
+	*s-- = mark % 10 + '0';
+	mark /= 10;
+	*s-- = mark % 6 + '0';
+	mark /= 6;
+	*s-- = ':';
+	*s-- = mark % 10 + '0';
+	mark /= 10;
+	*s-- = mark % 10 + '0';
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/hisax.h linux/drivers/isdn/hisax/hisax.h
--- v2.0.30/linux/drivers/isdn/hisax/hisax.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hisax.h	Mon Aug  4 17:34:00 1997
@@ -0,0 +1,540 @@
+/* $Id: hisax.h,v 1.13 1997/04/06 22:54:12 keil Exp $
+
+ *   Basic declarations, defines and prototypes
+ *
+ * $Log: hisax.h,v $
+ * Revision 1.13  1997/04/06 22:54:12  keil
+ * Using SKB's
+ *
+ * Revision 1.12  1997/03/23 21:45:45  keil
+ * Add support for ELSA PCMCIA
+ *
+ * Revision 1.11  1997/02/11 01:36:02  keil
+ * New Param structure
+ *
+ * Revision 1.10  1997/02/09 00:23:52  keil
+ * new interface handling, one interface per card
+ *
+ * Revision 1.9  1997/01/27 23:18:44  keil
+ * prototype for releasestack_isdnl3
+ *
+ * Revision 1.8  1997/01/27 16:02:37  keil
+ * new cards, callc timers, HZDELAY macro, HiSax_getrev prototype
+ *
+ * Revision 1.7  1997/01/21 22:22:14  keil
+ * changes for 2.0; Elsa Quickstep support
+ *
+ * Revision 1.6  1997/01/04 13:48:28  keil
+ * primitiv for MDL_REMOVE added
+ *
+ * Revision 1.5  1996/12/08 19:49:19  keil
+ * Monitor channel support
+ *
+ * Revision 1.4  1996/11/18 15:35:39  keil
+ * some changes for ELSA cards
+ *
+ * Revision 1.3  1996/11/05 19:37:23  keil
+ * using config.h
+ *
+ * Revision 1.2  1996/10/27 22:21:52  keil
+ * CallFlags for broadcast messages
+ *
+ * Revision 1.1  1996/10/13 20:03:46  keil
+ * Initial revision
+ *
+ *
+ *
+ */
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/isdnif.h>
+#include <linux/tty.h>
+
+#define PH_ACTIVATE   1
+#define PH_DATA       2
+#define PH_DEACTIVATE 3
+
+#define MDL_ASSIGN       4
+#define DL_UNIT_DATA     5
+#define SC_STARTUP       6
+#define CC_ESTABLISH     7
+#define DL_ESTABLISH     8
+#define DL_DATA          9
+#define CC_S_STATUS_ENQ  10
+
+#define CC_CONNECT       15
+#define CC_CONNECT_ACKNOWLEDGE 16
+#define CO_EOF                 17
+#define SC_DISCONNECT          18
+#define CO_DTMF                19
+#define DL_RELEASE             20
+#define DL_FLUSH               21
+
+#define CO_ALARM               22
+#define CC_REJECT              23
+
+#define CC_SETUP_REQ           24
+#define CC_SETUP_CNF           25
+#define CC_SETUP_IND           26
+#define CC_SETUP_RSP           27
+#define CC_SETUP_COMPLETE_IND  28
+
+#define CC_DISCONNECT_REQ      29
+#define CC_DISCONNECT_IND      30
+
+#define CC_RELEASE_CNF         31
+#define CC_RELEASE_IND         32
+#define CC_RELEASE_REQ         33
+
+#define CC_REJECT_REQ          34
+
+#define CC_PROCEEDING_IND      35
+
+#define CC_DLRL                36
+#define CC_DLEST               37
+
+#define CC_ALERTING_REQ        38
+#define CC_ALERTING_IND        39
+
+#define DL_STOP                40
+#define DL_START               41
+
+#define MDL_NOTEIPROC          46
+
+#define LC_ESTABLISH           47
+#define LC_RELEASE             48
+
+#define PH_REQUEST_PULL        49
+#define PH_PULL_ACK            50
+#define	PH_DATA_PULLED	       51
+#define CC_INFO_CHARGE         52
+
+#define CC_MORE_INFO           53
+#define CC_IGNORE              54
+
+#define MDL_REMOVE             56
+#define MDL_VERIFY             57
+
+#define CC_T303                60
+#define CC_T304                61
+#define CC_T305                62
+#define CC_T308_1              64
+#define CC_T308_2              65
+#define CC_T310                66
+#define CC_T313                67
+#define CC_T318                68
+#define CC_T319                69
+
+#define CC_NOSETUP_RSP_ERR     70
+#define CC_SETUP_ERR           71
+#define CC_CONNECT_ERR         72
+#define CC_RELEASE_ERR         73
+
+/*
+ * Message-Types
+ */
+
+#define MT_ALERTING            0x01
+#define MT_CALL_PROCEEDING     0x02
+#define MT_CONNECT             0x07
+#define MT_CONNECT_ACKNOWLEDGE 0x0f
+#define MT_PROGRESS            0x03
+#define MT_SETUP               0x05
+#define MT_SETUP_ACKNOWLEDGE   0x0d
+#define MT_RESUME              0x26
+#define MT_RESUME_ACKNOWLEDGE  0x2e
+#define MT_RESUME_REJECT       0x22
+#define MT_SUSPEND             0x25
+#define MT_SUSPEND_ACKNOWLEDGE 0x2d
+#define MT_SUSPEND_REJECT      0x21
+#define MT_USER_INFORMATION    0x20
+#define MT_DISCONNECT          0x45
+#define MT_RELEASE             0x4d
+#define MT_RELEASE_COMPLETE    0x5a
+#define MT_RESTART             0x46
+#define MT_RESTART_ACKNOWLEDGE 0x4e
+#define MT_SEGMENT             0x60
+#define MT_CONGESTION_CONTROL  0x79
+#define MT_INFORMATION         0x7b
+#define MT_FACILITY            0x62
+#define MT_NOTIFY              0x6e
+#define MT_STATUS              0x7d
+#define MT_STATUS_ENQUIRY      0x75
+
+#define IE_CAUSE               0x08
+
+struct HscxIoctlArg {
+	int channel;
+	int mode;
+	int transbufsize;
+};
+
+#ifdef __KERNEL__
+
+#undef DEBUG_MAGIC
+
+#define MAX_DFRAME_LEN	3072
+#define HSCX_BUFMAX	4096
+#define MAX_DATA_SIZE	(HSCX_BUFMAX - 4)
+#define MAX_DATA_MEM    (HSCX_BUFMAX * 2)
+#define MAX_HEADER_LEN	4
+#define MAX_WINDOW	8
+
+/*
+ * Statemachine
+ */
+struct Fsm {
+	int *jumpmatrix;
+	int state_count, event_count;
+	char **strEvent, **strState;
+};
+
+struct FsmInst {
+	struct Fsm *fsm;
+	int state;
+	int debug;
+	void *userdata;
+	int userint;
+	void (*printdebug) (struct FsmInst *, char *);
+};
+
+struct FsmNode {
+	int state, event;
+	void (*routine) (struct FsmInst *, int, void *);
+};
+
+struct FsmTimer {
+	struct FsmInst *fi;
+	struct timer_list tl;
+	int event;
+	void *arg;
+};
+
+struct L3Timer {
+	struct PStack *st;
+	struct timer_list tl;
+	int event;
+};
+
+struct Layer1 {
+	void *hardware;
+	int hscx;
+	struct PStack **stlistp;
+	int act_state;
+	void (*l1l2) (struct PStack *, int, void *);
+	void (*l1man) (struct PStack *, int, void *);
+	int hscxmode, hscxchannel, requestpull;
+};
+
+struct Layer2 {
+	int sap, tei, ces;
+	int extended, laptype;
+	int uihsize, ihsize;
+	int vs, va, vr;
+	struct sk_buff_head i_queue;
+	int window, orig;
+	int rejexp;
+	int debug;
+	struct sk_buff *windowar[MAX_WINDOW];
+	int sow;
+	struct FsmInst l2m;
+	void (*l2l1) (struct PStack *, int, void *);
+	void (*l2l1discardq) (struct PStack *, int, void *, int);
+	void (*l2man) (struct PStack *, int, void *);
+	void (*l2l3) (struct PStack *, int, void *);
+	void (*l2tei) (struct PStack *, int, void *);
+	struct FsmTimer t200_timer, t203_timer;
+	int t200, n200, t203;
+	int rc, t200_running;
+	char debug_id[32];
+};
+
+struct Layer3 {
+	void (*l3l4) (struct PStack *, int, void *);
+	void (*l3l2) (struct PStack *, int, void *);
+	int state, callref;
+	struct L3Timer timer;
+	int t303, t304, t305, t308, t310, t313, t318, t319;
+	int n_t303;
+	int debug;
+	int channr;
+};
+
+struct Layer4 {
+	void (*l4l3) (struct PStack *, int, void *);
+	void *userdata;
+	void (*l1writewakeup) (struct PStack *);
+	void (*l2writewakeup) (struct PStack *);
+};
+
+struct Management {
+	void (*manl1) (struct PStack *, int, void *);
+	void (*manl2) (struct PStack *, int, void *);
+	void (*teil2) (struct PStack *, int, void *);
+};
+
+struct Param {
+	int cause;
+	int loc;
+	int bchannel;
+	int callref;		/* Callreferenz Number */
+	setup_parm setup;	/* from isdnif.h numbers and Serviceindicator */
+	int chargeinfo;		/* Charge Info - only for 1tr6 in
+				 * the moment
+				 */
+	int spv;		/* SPV Flag */
+};
+
+struct PStack {
+	struct PStack *next;
+	struct Layer1 l1;
+	struct Layer2 l2;
+	struct Layer3 l3;
+	struct Layer4 l4;
+	struct Management ma;
+	struct Param *pa;
+	int protocol;		/* EDSS1 or 1TR6 */
+};
+
+struct HscxState {
+	int inuse, init, active;
+	struct IsdnCardState *sp;
+	int hscx, mode;
+	u_char *rcvbuf;		/* B-Channel receive Buffer */
+	int rcvidx;		/* B-Channel receive Buffer Index */
+	struct sk_buff *tx_skb;	/* B-Channel transmit Buffer */
+	int tx_cnt;		/* B-Channel transmit counter */
+	int count;		/* Current skb sent count */
+	struct sk_buff_head rqueue;	/* B-Channel receive Queue */
+	struct sk_buff_head squeue;	/* B-Channel receive Queue */
+	struct PStack *st;
+	struct tq_struct tqueue;
+	int event;
+#ifdef DEBUG_MAGIC
+	int magic;		/* 301270 */
+#endif
+};
+
+struct LcFsm {
+	struct FsmInst lcfi;
+	int type;
+	struct Channel *ch;
+	void (*lccall) (struct LcFsm *, int, void *);
+	struct PStack *st;
+	int l2_establish;
+	int l2_start;
+	struct FsmTimer act_timer;
+	char debug_id[32];
+};
+
+struct Channel {
+	struct PStack ds, is;
+	struct IsdnCardState *sp;
+	int hscx;
+	int chan;
+	int incoming;
+	struct FsmInst fi;
+	struct LcFsm lc_d, lc_b;
+	struct Param para;
+	struct FsmTimer drel_timer, dial_timer;
+	int debug;
+#ifdef DEBUG_MAGIC
+	int magic;		/* 301272 */
+#endif
+	int l2_protocol, l2_active_protocol;
+	int l2_primitive, l2_headersize;
+	int data_open;
+	int outcallref;
+	int impair;
+	int Flags;		/* for remembering action done in l4 */
+	int leased;
+};
+
+struct IsdnCardState {
+#ifdef DEBUG_MAGIC
+	int magic;
+#endif
+	unsigned char typ;
+	unsigned char subtyp;
+	int protocol;
+	unsigned int irq;
+	unsigned int cfg_reg;
+	unsigned int membase;
+	unsigned int isac;
+	unsigned int hscx[2];
+	unsigned int counter;
+	int myid;
+	isdn_if iif;
+	u_char *status_buf;
+	u_char *status_read;
+	u_char *status_write;
+	u_char *status_end;
+	void (*ph_command) (struct IsdnCardState *, unsigned int);
+	void (*modehscx) (struct HscxState *, int, int);
+	void (*hscx_fill_fifo) (struct HscxState *);
+	void (*isac_fill_fifo) (struct IsdnCardState *);
+	struct Channel channel[2];
+	struct PStack *stlist;
+	u_char *rcvbuf;
+	int rcvidx;
+	struct sk_buff *tx_skb;
+	int tx_cnt;
+	int event;
+	struct tq_struct tqueue;
+	int ph_active;
+	struct sk_buff_head rq, sq; /* D-channel queues */
+	int cardnr;
+	int ph_state;
+	struct PStack *teistack;
+	struct HscxState hs[2];
+	int dlogflag;
+	char *dlogspace;
+	int debug;
+	unsigned int CallFlags;
+};
+
+#define  MON0_RX	1
+#define  MON1_RX	2
+#define  MON0_TX	4
+#define  MON1_TX	8
+
+#define  ISDN_CTYPE_16_0	1
+#define  ISDN_CTYPE_8_0		2
+#define  ISDN_CTYPE_16_3	3
+#define  ISDN_CTYPE_PNP		4
+#define  ISDN_CTYPE_A1		5
+#define  ISDN_CTYPE_ELSA	6
+#define  ISDN_CTYPE_ELSA_QS1000	7
+#define  ISDN_CTYPE_TELESPCMCIA	8
+#define  ISDN_CTYPE_IX1MICROR2	9
+
+#define  ISDN_CTYPE_COUNT	9
+
+#ifdef	CONFIG_HISAX_16_0
+#define  CARD_TELES0 (1<< ISDN_CTYPE_16_0) | (1<< ISDN_CTYPE_8_0)
+#else
+#define  CARD_TELES0  0
+#endif
+
+#ifdef	CONFIG_HISAX_16_3
+#define  CARD_TELES3 (1<< ISDN_CTYPE_16_3) | (1<< ISDN_CTYPE_PNP) | \
+		     (1<< ISDN_CTYPE_TELESPCMCIA)
+#else
+#define  CARD_TELES3  0
+#endif
+
+#ifdef	CONFIG_HISAX_AVM_A1
+#define  CARD_AVM_A1 (1<< ISDN_CTYPE_A1)
+#else
+#define  CARD_AVM_A1  0
+#endif
+
+#ifdef	CONFIG_HISAX_ELSA_PCC
+#define  CARD_ELSA (1<< ISDN_CTYPE_ELSA) | (1<< ISDN_CTYPE_ELSA_QS1000)
+#else
+#define  CARD_ELSA  0
+#endif
+
+#ifdef	CONFIG_HISAX_ELSA_PCMCIA
+#if CARD_ELSA
+#error "You can't use a ELSA ISA card and a ELSA PCMCIA card with the same driver"
+#else
+#undef CARD_ELSA
+#define CARD_ELSA (1<< ISDN_CTYPE_ELSA_QS1000)
+#endif
+#endif
+
+#ifdef	CONFIG_HISAX_IX1MICROR2
+#define	CARD_IX1MICROR2 (1 << ISDN_CTYPE_IX1MICROR2)
+#else
+#define CARD_IX1MICROR2 0
+#endif
+
+#define  SUPORTED_CARDS  (CARD_TELES0 | CARD_TELES3 | CARD_AVM_A1 | CARD_ELSA \
+			 | CARD_IX1MICROR2)
+
+struct IsdnCard {
+	int typ;
+	int protocol;		/* EDSS1 or 1TR6 */
+	unsigned int para[3];
+	struct IsdnCardState *sp;
+};
+
+
+#define LAPD 0
+#define LAPB 1
+
+void l2down(struct PStack *st, u_char pr, struct sk_buff *skb);
+void l2up(struct PStack *st, u_char pr, struct sk_buff *skb);
+void acceptph(struct PStack *st, struct sk_buff *skb);
+void setstack_isdnl2(struct PStack *st, char *debug_id);
+int HiSax_inithardware(void);
+void HiSax_closehardware(void);
+
+void setstack_HiSax(struct PStack *st, struct IsdnCardState *sp);
+unsigned int randomces(void);
+void setstack_isdnl3(struct PStack *st, struct Channel *chanp);
+void HiSax_addlist(struct IsdnCardState *sp, struct PStack *st);
+void releasestack_isdnl2(struct PStack *st);
+void releasestack_isdnl3(struct PStack *st);
+void HiSax_rmlist(struct IsdnCardState *sp, struct PStack *st);
+void newcallref(struct PStack *st);
+
+int setstack_hscx(struct PStack *st, struct HscxState *hs);
+u_char *findie(u_char * p, int size, u_char ie, int wanted_set);
+int getcallref(u_char * p);
+
+void FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount);
+void FsmFree(struct Fsm *fsm);
+int FsmEvent(struct FsmInst *fi, int event, void *arg);
+void FsmChangeState(struct FsmInst *fi, int newstate);
+void FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft);
+int FsmAddTimer(struct FsmTimer *ft, int millisec,
+		int event, void *arg, int where);
+void FsmDelTimer(struct FsmTimer *ft, int where);
+int FsmTimerRunning(struct FsmTimer *ft);
+void jiftime(char *s, long mark);
+
+int HiSax_command(isdn_ctrl * ic);
+int HiSax_writebuf_skb(int id, int chan, struct sk_buff *skb);
+void HiSax_putstatus(struct IsdnCardState *csta, char *buf);
+void HiSax_reportcard(int cardnr);
+int QuickHex(char *txt, u_char * p, int cnt);
+void LogFrame(struct IsdnCardState *sp, u_char * p, int size);
+void dlogframe(struct IsdnCardState *sp, u_char * p, int size, char *comment);
+void iecpy(u_char * dest, u_char * iestart, int ieoffset);
+void setstack_transl2(struct PStack *st);
+void releasestack_transl2(struct PStack *st);
+void close_hscxstate(struct HscxState *);
+void setstack_tei(struct PStack *st);
+
+#endif				/* __KERNEL__ */
+
+#define HZDELAY(jiffs) {int tout = jiffs; while (tout--) udelay(1000000/HZ);}
+
+int ll_run(struct IsdnCardState *csta);
+void ll_stop(struct IsdnCardState *csta);
+void CallcNew(void);
+void CallcFree(void);
+int CallcNewChan(struct IsdnCardState *csta);
+void CallcFreeChan(struct IsdnCardState *csta);
+void Isdnl2New(void);
+void Isdnl2Free(void);
+void init_tei(struct IsdnCardState *sp, int protocol);
+void release_tei(struct IsdnCardState *sp);
+char *HiSax_getrev(const char *revision);
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/isdnl1.c linux/drivers/isdn/hisax/isdnl1.c
--- v2.0.30/linux/drivers/isdn/hisax/isdnl1.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/isdnl1.c	Mon Aug  4 17:34:00 1997
@@ -0,0 +1,1378 @@
+/* $Id: isdnl1.c,v 1.15 1997/05/27 15:17:55 fritz Exp $
+
+ * isdnl1.c     common low level stuff for Siemens Chipsetbased isdn cards
+ *              based on the teles driver from Jan den Ouden
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *              Beat Doebeli
+ *
+ *
+ * $Log: isdnl1.c,v $
+ * Revision 1.15  1997/05/27 15:17:55  fritz
+ * Added changes for recent 2.1.x kernels:
+ *   changed return type of isdn_close
+ *   queue_task_* -> queue_task
+ *   clear/set_bit -> test_and_... where apropriate.
+ *   changed type of hard_header_cache parameter.
+ *
+ * Revision 1.14  1997/04/07 23:00:08  keil
+ * GFP_KERNEL ---> GFP_ATOMIC
+ *
+ * Revision 1.13  1997/04/06 22:55:50  keil
+ * Using SKB's
+ *
+ * Revision 1.12  1997/03/26 13:43:57  keil
+ * small cosmetics
+ *
+ * Revision 1.11  1997/03/25 23:11:23  keil
+ * US NI-1 protocol
+ *
+ * Revision 1.10  1997/03/13 14:45:05  keil
+ * using IRQ proof queue_task
+ *
+ * Revision 1.9  1997/03/12 21:44:21  keil
+ * change Interrupt routine from atomic quick to normal
+ *
+ * Revision 1.8  1997/02/09 00:24:31  keil
+ * new interface handling, one interface per card
+ *
+ * Revision 1.7  1997/01/27 15:56:03  keil
+ * PCMCIA Teles card and ITK ix1 micro added
+ *
+ * Revision 1.6  1997/01/21 22:20:00  keil
+ * changes for D-channel log; Elsa Quickstep support
+ *
+ * Revision 1.5  1997/01/10 12:51:19  keil
+ * cleanup; set newversion
+ *
+ * Revision 1.4  1996/12/08 19:44:53  keil
+ * L2FRAME_DEBUG and other changes from Pekka Sarnila
+ *
+ * Revision 1.3  1996/11/18 15:34:47  keil
+ * fix HSCX version code
+ *
+ * Revision 1.2  1996/10/27 22:16:54  keil
+ * ISAC/HSCX version lookup
+ *
+ * Revision 1.1  1996/10/13 20:04:53  keil
+ * Initial revision
+ *
+ *
+ *
+ */
+
+const char *l1_revision = "$Revision: 1.15 $";
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "hisax.h"
+#include "isdnl1.h"
+
+#if CARD_TELES0
+#include "teles0.h"
+#endif
+
+#if CARD_TELES3
+#include "teles3.h"
+#endif
+
+#if CARD_AVM_A1
+#include "avm_a1.h"
+#endif
+
+#if CARD_ELSA
+#include "elsa.h"
+#endif
+
+#if CARD_IX1MICROR2
+#include "ix1_micro.h"
+#endif
+
+/* #define I4L_IRQ_FLAG SA_INTERRUPT */
+#define I4L_IRQ_FLAG    0
+
+#define HISAX_STATUS_BUFSIZE 4096
+
+#define INCLUDE_INLINE_FUNCS
+#include <linux/tqueue.h>
+#include <linux/interrupt.h>
+
+const char *CardType[] =
+{"No Card", "Teles 16.0", "Teles 8.0", "Teles 16.3",
+ "Creatix/Teles PnP", "AVM A1", "Elsa ML",
+#ifdef CONFIG_HISAX_ELSA_PCMCIA
+ "Elsa PCMCIA",
+#else
+ "Elsa Quickstep",
+#endif
+ "Teles PCMCIA", "ITK ix1-micro Rev.2"};
+
+static char *HSCXVer[] =
+{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7",
+ "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"};
+
+static char *ISACVer[] =
+{"2086/2186 V1.1", "2085 B1", "2085 B2",
+ "2085 V2.3"};
+
+extern struct IsdnCard cards[];
+extern int nrcards;
+extern char *HiSax_id;
+
+/*
+ * Find card with given driverId
+ */
+static inline struct IsdnCardState
+*
+hisax_findcard(int driverid)
+{
+	int i;
+
+	for (i = 0; i < nrcards; i++)
+		if (cards[i].sp)
+			if (cards[i].sp->myid == driverid)
+				return (cards[i].sp);
+	return (struct IsdnCardState *) 0;
+}
+
+int
+HiSax_readstatus(u_char * buf, int len, int user, int id, int channel)
+{
+	int count;
+	u_char *p;
+	struct IsdnCardState *csta = hisax_findcard(id);
+
+	if (csta) {
+		for (p = buf, count = 0; count < len; p++, count++) {
+			if (user)
+				put_user(*csta->status_read++, p);
+			else
+				*p++ = *csta->status_read++;
+			if (csta->status_read > csta->status_end)
+				csta->status_read = csta->status_buf;
+		}
+		return count;
+	} else {
+		printk(KERN_ERR
+		 "HiSax: if_readstatus called with invalid driverId!\n");
+		return -ENODEV;
+	}
+}
+
+void
+HiSax_putstatus(struct IsdnCardState *csta, char *buf)
+{
+	long flags;
+	int len, count, i;
+	u_char *p;
+	isdn_ctrl ic;
+
+	save_flags(flags);
+	cli();
+	count = 0;
+	len = strlen(buf);
+
+	if (!csta) {
+		printk(KERN_WARNING "HiSax: No CardStatus for message %s", buf);
+		restore_flags(flags);
+		return;
+	}
+	for (p = buf, i = len; i > 0; i--, p++) {
+		*csta->status_write++ = *p;
+		if (csta->status_write > csta->status_end)
+			csta->status_write = csta->status_buf;
+		count++;
+	}
+	restore_flags(flags);
+	if (count) {
+		ic.command = ISDN_STAT_STAVAIL;
+		ic.driver = csta->myid;
+		ic.arg = count;
+		csta->iif.statcallb(&ic);
+	}
+}
+
+int
+ll_run(struct IsdnCardState *csta)
+{
+	long flags;
+	isdn_ctrl ic;
+
+	save_flags(flags);
+	cli();
+	ic.driver = csta->myid;
+	ic.command = ISDN_STAT_RUN;
+	csta->iif.statcallb(&ic);
+	restore_flags(flags);
+	return 0;
+}
+
+void
+ll_stop(struct IsdnCardState *csta)
+{
+	isdn_ctrl ic;
+
+	ic.command = ISDN_STAT_STOP;
+	ic.driver = csta->myid;
+	csta->iif.statcallb(&ic);
+	CallcFreeChan(csta);
+}
+
+static void
+ll_unload(struct IsdnCardState *csta)
+{
+	isdn_ctrl ic;
+
+	ic.command = ISDN_STAT_UNLOAD;
+	ic.driver = csta->myid;
+	csta->iif.statcallb(&ic);
+	if (csta->status_buf)
+		kfree(csta->status_buf);
+	csta->status_read = NULL;
+	csta->status_write = NULL;
+	csta->status_end = NULL;
+	kfree(csta->dlogspace);
+}
+
+void
+debugl1(struct IsdnCardState *sp, char *msg)
+{
+	char tmp[256], tm[32];
+
+	jiftime(tm, jiffies);
+	sprintf(tmp, "%s Card %d %s\n", tm, sp->cardnr + 1, msg);
+	HiSax_putstatus(sp, tmp);
+}
+
+/*
+ * HSCX stuff goes here
+ */
+
+
+char *
+HscxVersion(u_char v)
+{
+	return (HSCXVer[v & 0xf]);
+}
+
+void
+hscx_sched_event(struct HscxState *hsp, int event)
+{
+	hsp->event |= 1 << event;
+	queue_task(&hsp->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+/*
+ * ISAC stuff goes here
+ */
+
+char *
+ISACVersion(u_char v)
+{
+	return (ISACVer[(v >> 5) & 3]);
+}
+
+void
+isac_sched_event(struct IsdnCardState *sp, int event)
+{
+	sp->event |= 1 << event;
+	queue_task(&sp->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+int
+act_wanted(struct IsdnCardState *sp)
+{
+	struct PStack *st;
+
+	st = sp->stlist;
+	while (st)
+		if (st->l1.act_state)
+			return (!0);
+		else
+			st = st->next;
+	return (0);
+}
+
+void
+isac_new_ph(struct IsdnCardState *sp)
+{
+	int enq;
+
+	enq = act_wanted(sp);
+
+	switch (sp->ph_state) {
+		case (6):
+			sp->ph_active = 0;
+			sp->ph_command(sp, 15);
+			break;
+		case (15):
+			sp->ph_active = 0;
+			if (enq)
+				sp->ph_command(sp, 0);
+			break;
+		case (0):
+			sp->ph_active = 0;
+			if (enq)
+				sp->ph_command(sp, 0);
+#if 0
+			else
+				sp->ph_command(sp, 15);
+#endif
+			break;
+		case (7):
+			sp->ph_active = 0;
+			if (enq)
+				sp->ph_command(sp, 9);
+			break;
+		case (12):
+			sp->ph_command(sp, 8);
+			sp->ph_active = 5;
+			isac_sched_event(sp, ISAC_PHCHANGE);
+			if (!sp->tx_skb)
+				sp->tx_skb = skb_dequeue(&sp->sq);
+			if (sp->tx_skb) {
+				sp->tx_cnt = 0;
+				sp->isac_fill_fifo(sp);
+			}
+			break;
+		case (13):
+			sp->ph_command(sp, 9);
+			sp->ph_active = 5;
+			isac_sched_event(sp, ISAC_PHCHANGE);
+			if (!sp->tx_skb)
+				sp->tx_skb = skb_dequeue(&sp->sq);
+			if (sp->tx_skb) {
+				sp->tx_cnt = 0;
+				sp->isac_fill_fifo(sp);
+			}
+			break;
+		case (4):
+		case (8):
+			sp->ph_active = 0;
+			break;
+		default:
+			sp->ph_active = 0;
+			break;
+	}
+}
+
+static void
+restart_ph(struct IsdnCardState *sp)
+{
+	if (!sp->ph_active) {
+		if ((sp->ph_state == 6) || (sp->ph_state == 0)) {
+			sp->ph_command(sp, 0);
+			sp->ph_active = 2;
+		} else {
+			sp->ph_command(sp, 1);
+			sp->ph_active = 1;
+		}
+	} else if (sp->ph_active == 2) {
+		sp->ph_command(sp, 1);
+		sp->ph_active = 1;
+	}
+}
+
+
+static void
+act_ivated(struct IsdnCardState *sp)
+{
+	struct PStack *st;
+
+	st = sp->stlist;
+	while (st) {
+		if (st->l1.act_state == 1) {
+			st->l1.act_state = 2;
+			st->l1.l1man(st, PH_ACTIVATE, NULL);
+		}
+		st = st->next;
+	}
+}
+
+static void
+process_new_ph(struct IsdnCardState *sp)
+{
+	if (sp->ph_active == 5)
+		act_ivated(sp);
+}
+
+static void
+process_xmt(struct IsdnCardState *sp)
+{
+	struct PStack *stptr;
+
+	if (sp->tx_skb)
+		return;
+
+	stptr = sp->stlist;
+	while (stptr != NULL)
+		if (stptr->l1.requestpull) {
+			stptr->l1.requestpull = 0;
+			stptr->l1.l1l2(stptr, PH_PULL_ACK, NULL);
+			break;
+		} else
+			stptr = stptr->next;
+}
+
+static void
+process_rcv(struct IsdnCardState *sp)
+{
+	struct sk_buff *skb, *nskb;
+	struct PStack *stptr;
+	int found, broadc;
+	char tmp[64];
+
+	while ((skb = skb_dequeue(&sp->rq))) {
+#ifdef L2FRAME_DEBUG		/* psa */
+		if (sp->debug & L1_DEB_LAPD)
+			Logl2Frame(sp, skb, "PH_DATA", 1);
+#endif
+		stptr = sp->stlist;
+		broadc = (skb->data[1] >> 1) == 127;
+
+		if (broadc) {
+			if (!(skb->data[0] >> 2)) {	/* sapi 0 */
+				sp->CallFlags = 3;
+				if (sp->dlogflag) {
+					LogFrame(sp, skb->data, skb->len);
+					dlogframe(sp, skb->data + 3, skb->len - 3,
+						  "Q.931 frame network->user broadcast");
+				}
+			}
+			while (stptr != NULL) {
+				if ((skb->data[0] >> 2) == stptr->l2.sap)
+					if ((nskb = skb_clone(skb, GFP_ATOMIC)))
+						stptr->l1.l1l2(stptr, PH_DATA, nskb);
+					else
+						printk(KERN_WARNING "HiSax: isdn broadcast buffer shortage\n");
+				stptr = stptr->next;
+			}
+			SET_SKB_FREE(skb);
+			dev_kfree_skb(skb, FREE_READ);
+		} else {
+			found = 0;
+			while (stptr != NULL)
+				if (((skb->data[0] >> 2) == stptr->l2.sap) &&
+				((skb->data[1] >> 1) == stptr->l2.tei)) {
+					stptr->l1.l1l2(stptr, PH_DATA, skb);
+					found = !0;
+					break;
+				} else
+					stptr = stptr->next;
+			if (!found) {
+				/* BD 10.10.95
+				 * Print out D-Channel msg not processed
+				 * by isdn4linux
+				 */
+
+				if ((!(skb->data[0] >> 2)) && (!(skb->data[2] & 0x01))) {
+					sprintf(tmp,
+						"Q.931 frame network->user with tei %d (not for us)",
+						skb->data[1] >> 1);
+					LogFrame(sp, skb->data, skb->len);
+					dlogframe(sp, skb->data + 4, skb->len - 4, tmp);
+				}
+				SET_SKB_FREE(skb);
+				dev_kfree_skb(skb, FREE_READ);
+			}
+		}
+
+	}
+
+}
+
+static void
+isac_bh(struct IsdnCardState *sp)
+{
+	if (!sp)
+		return;
+
+	if (test_and_clear_bit(ISAC_PHCHANGE, &sp->event))
+		process_new_ph(sp);
+	if (test_and_clear_bit(ISAC_RCVBUFREADY, &sp->event))
+		process_rcv(sp);
+	if (test_and_clear_bit(ISAC_XMTBUFREADY, &sp->event))
+		process_xmt(sp);
+}
+
+static void
+l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+	char str[64];
+
+	switch (pr) {
+		case (PH_DATA):
+			if (sp->tx_skb) {
+				skb_queue_tail(&sp->sq, skb);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (sp->debug & L1_DEB_LAPD)
+					Logl2Frame(sp, skb, "PH_DATA Queued", 0);
+#endif
+			} else {
+				if ((sp->dlogflag) && (!(skb->data[2] & 1))) {	/* I-FRAME */
+					LogFrame(sp, skb->data, skb->len);
+					sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
+					dlogframe(sp, skb->data + st->l2.ihsize, skb->len - st->l2.ihsize,
+						  str);
+				}
+				sp->tx_skb = skb;
+				sp->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (sp->debug & L1_DEB_LAPD)
+					Logl2Frame(sp, skb, "PH_DATA", 0);
+#endif
+				sp->isac_fill_fifo(sp);
+			}
+			break;
+		case (PH_DATA_PULLED):
+			if (sp->tx_skb) {
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, " l2l1 tx_skb exist this shouldn't happen");
+				break;
+			}
+			if ((sp->dlogflag) && (!(skb->data[2] & 1))) {	/* I-FRAME */
+				LogFrame(sp, skb->data, skb->len);
+				sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
+				dlogframe(sp, skb->data + st->l2.ihsize, skb->len - st->l2.ihsize,
+					  str);
+			}
+			sp->tx_skb = skb;
+			sp->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (sp->debug & L1_DEB_LAPD)
+				Logl2Frame(sp, skb, "PH_DATA_PULLED", 0);
+#endif
+			sp->isac_fill_fifo(sp);
+			break;
+		case (PH_REQUEST_PULL):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (sp->debug & L1_DEB_LAPD)
+				debugl1(sp, "-> PH_REQUEST_PULL");
+#endif
+			if (!sp->tx_skb) {
+				st->l1.requestpull = 0;
+				st->l1.l1l2(st, PH_PULL_ACK, NULL);
+			} else
+				st->l1.requestpull = !0;
+			break;
+	}
+}
+
+
+static void
+hscx_process_xmt(struct HscxState *hsp)
+{
+	struct PStack *st = hsp->st;
+
+	if (hsp->tx_skb)
+		return;
+
+	if (st->l1.requestpull) {
+		st->l1.requestpull = 0;
+		st->l1.l1l2(st, PH_PULL_ACK, NULL);
+	}
+	if (!hsp->active)
+		if ((!hsp->tx_skb) && (!skb_queue_len(&hsp->squeue)))
+			hsp->sp->modehscx(hsp, 0, 0);
+}
+
+static void
+hscx_process_rcv(struct HscxState *hsp)
+{
+	struct sk_buff *skb;
+
+#ifdef DEBUG_MAGIC
+	if (hsp->magic != 301270) {
+		printk(KERN_DEBUG "hscx_process_rcv magic not 301270\n");
+		return;
+	}
+#endif
+	while ((skb = skb_dequeue(&hsp->rqueue))) {
+		hsp->st->l1.l1l2(hsp->st, PH_DATA, skb);
+	}
+}
+
+static void
+hscx_bh(struct HscxState *hsp)
+{
+
+	if (!hsp)
+		return;
+
+	if (test_and_clear_bit(HSCX_RCVBUFREADY, &hsp->event))
+		hscx_process_rcv(hsp);
+	if (test_and_clear_bit(HSCX_XMTBUFREADY, &hsp->event))
+		hscx_process_xmt(hsp);
+
+}
+
+/*
+ * interrupt stuff ends here
+ */
+
+void
+HiSax_addlist(struct IsdnCardState *sp,
+	      struct PStack *st)
+{
+	st->next = sp->stlist;
+	sp->stlist = st;
+}
+
+void
+HiSax_rmlist(struct IsdnCardState *sp,
+	     struct PStack *st)
+{
+	struct PStack *p;
+
+	if (sp->stlist == st)
+		sp->stlist = st->next;
+	else {
+		p = sp->stlist;
+		while (p)
+			if (p->next == st) {
+				p->next = st->next;
+				return;
+			} else
+				p = p->next;
+	}
+}
+
+static void
+check_ph_act(struct IsdnCardState *sp)
+{
+	struct PStack *st = sp->stlist;
+
+	while (st) {
+		if (st->l1.act_state)
+			return;
+		st = st->next;
+	}
+	if (sp->ph_active == 5)
+		sp->ph_active = 4;
+}
+
+static void
+HiSax_manl1(struct PStack *st, int pr,
+	    void *arg)
+{
+	struct IsdnCardState *sp = (struct IsdnCardState *)
+	st->l1.hardware;
+	long flags;
+	char tmp[32];
+
+	switch (pr) {
+		case (PH_ACTIVATE):
+			if (sp->debug) {
+				sprintf(tmp, "PH_ACT ph_active %d", sp->ph_active);
+				debugl1(sp, tmp);
+			}
+			save_flags(flags);
+			cli();
+			if (sp->ph_active & 4) {
+				sp->ph_active = 5;
+				st->l1.act_state = 2;
+				restore_flags(flags);
+				st->l1.l1man(st, PH_ACTIVATE, NULL);
+			} else {
+				st->l1.act_state = 1;
+				if (sp->ph_active == 0)
+					restart_ph(sp);
+				restore_flags(flags);
+			}
+			break;
+		case (PH_DEACTIVATE):
+			st->l1.act_state = 0;
+			if (sp->debug) {
+				sprintf(tmp, "PH_DEACT ph_active %d", sp->ph_active);
+				debugl1(sp, tmp);
+			}
+			check_ph_act(sp);
+			break;
+	}
+}
+
+static void
+HiSax_l2l1discardq(struct PStack *st, int pr,
+		   void *heldby, int releasetoo)
+{
+	struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb;
+
+#ifdef DEBUG_MAGIC
+	if (sp->magic != 301271) {
+		printk(KERN_DEBUG "isac_discardq magic not 301271\n");
+		return;
+	}
+#endif
+
+	while ((skb = skb_dequeue(&sp->sq)))
+		dev_kfree_skb(skb, FREE_WRITE);
+}
+
+void
+setstack_HiSax(struct PStack *st, struct IsdnCardState *sp)
+{
+	st->l1.hardware = sp;
+	st->protocol = sp->protocol;
+
+	setstack_tei(st);
+
+	st->l1.stlistp = &(sp->stlist);
+	st->l1.act_state = 0;
+	st->l2.l2l1 = l2l1;
+	st->l2.l2l1discardq = HiSax_l2l1discardq;
+	st->ma.manl1 = HiSax_manl1;
+	st->l1.requestpull = 0;
+}
+
+void
+init_hscxstate(struct IsdnCardState *sp,
+	       int hscx)
+{
+	struct HscxState *hsp = sp->hs + hscx;
+
+	hsp->sp = sp;
+	hsp->hscx = hscx;
+
+	hsp->tqueue.next = 0;
+	hsp->tqueue.sync = 0;
+	hsp->tqueue.routine = (void *) (void *) hscx_bh;
+	hsp->tqueue.data = hsp;
+
+	hsp->inuse = 0;
+	hsp->init = 0;
+	hsp->active = 0;
+
+#ifdef DEBUG_MAGIC
+	hsp->magic = 301270;
+#endif
+}
+
+int
+get_irq(int cardnr, void *routine)
+{
+	struct IsdnCard *card = cards + cardnr;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	if (request_irq(card->sp->irq, routine,
+			I4L_IRQ_FLAG, "HiSax", NULL)) {
+		printk(KERN_WARNING "HiSax: couldn't get interrupt %d\n",
+		       card->sp->irq);
+		restore_flags(flags);
+		return (0);
+	}
+	irq2dev_map[card->sp->irq] = (void *) card->sp;
+	restore_flags(flags);
+	return (1);
+}
+
+static void
+release_irq(int cardnr)
+{
+	struct IsdnCard *card = cards + cardnr;
+
+	irq2dev_map[card->sp->irq] = NULL;
+	free_irq(card->sp->irq, NULL);
+}
+
+void
+close_hscxstate(struct HscxState *hs)
+{
+	struct sk_buff *skb;
+
+	hs->sp->modehscx(hs, 0, 0);
+	hs->inuse = 0;
+	if (hs->init) {
+		if (hs->rcvbuf) {
+			kfree(hs->rcvbuf);
+			hs->rcvbuf = NULL;
+		}
+		while ((skb = skb_dequeue(&hs->rqueue))) {
+			SET_SKB_FREE(skb);
+			dev_kfree_skb(skb, FREE_READ);
+		}
+		while ((skb = skb_dequeue(&hs->squeue)))
+			dev_kfree_skb(skb, FREE_WRITE);
+		if (hs->tx_skb) {
+			dev_kfree_skb(hs->tx_skb, FREE_WRITE);
+			hs->tx_skb = NULL;
+		}
+	}
+	hs->init = 0;
+}
+
+static void
+closecard(int cardnr)
+{
+	struct IsdnCardState *csta = cards[cardnr].sp;
+	struct sk_buff *skb;
+
+	close_hscxstate(csta->hs + 1);
+	close_hscxstate(csta->hs);
+
+	if (csta->rcvbuf) {
+		kfree(csta->rcvbuf);
+		csta->rcvbuf = NULL;
+	}
+	while ((skb = skb_dequeue(&csta->rq))) {
+		SET_SKB_FREE(skb);
+		dev_kfree_skb(skb, FREE_READ);
+	}
+	while ((skb = skb_dequeue(&csta->sq)))
+		dev_kfree_skb(skb, FREE_WRITE);
+	if (csta->tx_skb) {
+		dev_kfree_skb(csta->tx_skb, FREE_WRITE);
+		csta->tx_skb = NULL;
+	}
+	switch (csta->typ) {
+#if CARD_TELES0
+		case ISDN_CTYPE_16_0:
+		case ISDN_CTYPE_8_0:
+			release_io_teles0(cards + cardnr);
+			break;
+#endif
+#if CARD_TELES3
+		case ISDN_CTYPE_PNP:
+		case ISDN_CTYPE_16_3:
+		case ISDN_CTYPE_TELESPCMCIA:
+			release_io_teles3(cards + cardnr);
+			break;
+#endif
+#if CARD_AVM_A1
+		case ISDN_CTYPE_A1:
+			release_io_avm_a1(cards + cardnr);
+			break;
+#endif
+#if CARD_ELSA
+		case ISDN_CTYPE_ELSA:
+		case ISDN_CTYPE_ELSA_QS1000:
+			release_io_elsa(cards + cardnr);
+			break;
+#endif
+#if CARD_IX1MICROR2
+		case ISDN_CTYPE_IX1MICROR2:
+			release_io_ix1micro(cards + cardnr);
+			break;
+#endif
+		default:
+			break;
+	}
+	ll_unload(csta);
+}
+
+static int
+checkcard(int cardnr, char *id)
+{
+	long flags;
+	int ret = 0;
+	struct IsdnCard *card = cards + cardnr;
+	struct IsdnCardState *sp;
+
+	save_flags(flags);
+	cli();
+	if (!(sp = (struct IsdnCardState *)
+	      kmalloc(sizeof(struct IsdnCardState), GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for IsdnCardState(card %d)\n",
+		       cardnr + 1);
+		restore_flags(flags);
+		return (0);
+	}
+	card->sp = sp;
+	sp->cardnr = cardnr;
+	sp->cfg_reg = 0;
+	sp->protocol = card->protocol;
+
+	if ((card->typ > 0) && (card->typ < 31)) {
+		if (!((1 << card->typ) & SUPORTED_CARDS)) {
+			printk(KERN_WARNING
+			     "HiSax: Support for %s Card not selected\n",
+			       CardType[card->typ]);
+			restore_flags(flags);
+			return (0);
+		}
+	} else {
+		printk(KERN_WARNING
+		       "HiSax: Card Type %d out of range\n",
+		       card->typ);
+		restore_flags(flags);
+		return (0);
+	}
+	if (!(sp->dlogspace = kmalloc(4096, GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for dlogspace(card %d)\n",
+		       cardnr + 1);
+		restore_flags(flags);
+		return (0);
+	}
+	if (!(sp->status_buf = kmalloc(HISAX_STATUS_BUFSIZE, GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for status_buf(card %d)\n",
+		       cardnr + 1);
+		kfree(sp->dlogspace);
+		restore_flags(flags);
+		return (0);
+	}
+	sp->status_read = sp->status_buf;
+	sp->status_write = sp->status_buf;
+	sp->status_end = sp->status_buf + HISAX_STATUS_BUFSIZE - 1;
+	sp->typ = card->typ;
+	sp->CallFlags = 0;
+	strcpy(sp->iif.id, id);
+	sp->iif.channels = 2;
+	sp->iif.maxbufsize = MAX_DATA_SIZE;
+	sp->iif.hl_hdrlen = MAX_HEADER_LEN;
+	sp->iif.features =
+	    ISDN_FEATURE_L2_X75I |
+	    ISDN_FEATURE_L2_HDLC |
+	    ISDN_FEATURE_L2_TRANS |
+	    ISDN_FEATURE_L3_TRANS |
+#ifdef	CONFIG_HISAX_1TR6
+	    ISDN_FEATURE_P_1TR6 |
+#endif
+#ifdef	CONFIG_HISAX_EURO
+	    ISDN_FEATURE_P_EURO |
+#endif
+#ifdef        CONFIG_HISAX_NI1
+	    ISDN_FEATURE_P_NI1 |
+#endif
+	    0;
+
+	sp->iif.command = HiSax_command;
+	sp->iif.writebuf = NULL;
+	sp->iif.writecmd = NULL;
+	sp->iif.writebuf_skb = HiSax_writebuf_skb;
+	sp->iif.readstat = HiSax_readstatus;
+	register_isdn(&sp->iif);
+	sp->myid = sp->iif.channels;
+	restore_flags(flags);
+	printk(KERN_NOTICE
+	       "HiSax: Card %d Protocol %s Id=%s (%d)\n", cardnr + 1,
+	       (card->protocol == ISDN_PTYPE_1TR6) ? "1TR6" :
+	       (card->protocol == ISDN_PTYPE_EURO) ? "EDSS1" :
+	       (card->protocol == ISDN_PTYPE_LEASED) ? "LEASED" :
+	       (card->protocol == ISDN_PTYPE_NI1) ? "NI1" :
+	       "NONE", sp->iif.id, sp->myid);
+	switch (card->typ) {
+#if CARD_TELES0
+		case ISDN_CTYPE_16_0:
+		case ISDN_CTYPE_8_0:
+			ret = setup_teles0(card);
+			break;
+#endif
+#if CARD_TELES3
+		case ISDN_CTYPE_16_3:
+		case ISDN_CTYPE_PNP:
+		case ISDN_CTYPE_TELESPCMCIA:
+			ret = setup_teles3(card);
+			break;
+#endif
+#if CARD_AVM_A1
+		case ISDN_CTYPE_A1:
+			ret = setup_avm_a1(card);
+			break;
+#endif
+#if CARD_ELSA
+		case ISDN_CTYPE_ELSA:
+		case ISDN_CTYPE_ELSA_QS1000:
+			ret = setup_elsa(card);
+			break;
+#endif
+#if CARD_IX1MICROR2
+		case ISDN_CTYPE_IX1MICROR2:
+			ret = setup_ix1micro(card);
+			break;
+#endif
+		default:
+			printk(KERN_WARNING "HiSax: Unknown Card Typ %d\n",
+			       card->typ);
+			ll_unload(sp);
+			return (0);
+	}
+	if (!ret) {
+		ll_unload(sp);
+		return (0);
+	}
+	if (!(sp->rcvbuf = kmalloc(MAX_DFRAME_LEN, GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for isac rcvbuf\n");
+		return (1);
+	}
+	sp->rcvidx = 0;
+	sp->tx_skb = NULL;
+	sp->tx_cnt = 0;
+	sp->event = 0;
+	sp->tqueue.next = 0;
+	sp->tqueue.sync = 0;
+	sp->tqueue.routine = (void *) (void *) isac_bh;
+	sp->tqueue.data = sp;
+
+	skb_queue_head_init(&sp->rq);
+	skb_queue_head_init(&sp->sq);
+
+	sp->stlist = NULL;
+	sp->ph_active = 0;
+	sp->dlogflag = 0;
+	sp->debug = L1_DEB_WARN;
+#ifdef DEBUG_MAGIC
+	sp->magic = 301271;
+#endif
+
+	init_hscxstate(sp, 0);
+	init_hscxstate(sp, 1);
+
+	switch (card->typ) {
+#if CARD_TELES0
+		case ISDN_CTYPE_16_0:
+		case ISDN_CTYPE_8_0:
+			ret = initteles0(sp);
+			break;
+#endif
+#if CARD_TELES3
+		case ISDN_CTYPE_16_3:
+		case ISDN_CTYPE_PNP:
+		case ISDN_CTYPE_TELESPCMCIA:
+			ret = initteles3(sp);
+			break;
+#endif
+#if CARD_AVM_A1
+		case ISDN_CTYPE_A1:
+			ret = initavm_a1(sp);
+			break;
+#endif
+#if CARD_ELSA
+		case ISDN_CTYPE_ELSA:
+		case ISDN_CTYPE_ELSA_QS1000:
+			ret = initelsa(sp);
+			break;
+#endif
+#if CARD_IX1MICROR2
+		case ISDN_CTYPE_IX1MICROR2:
+			ret = initix1micro(sp);
+			break;
+#endif
+		default:
+			ret = 0;
+			break;
+	}
+	if (!ret) {
+		closecard(cardnr);
+		return (0);
+	}
+	init_tei(sp, sp->protocol);
+	CallcNewChan(sp);
+	ll_run(sp);
+	return (1);
+}
+
+void
+HiSax_shiftcards(int idx)
+{
+	int i;
+
+	for (i = idx; i < 15; i++)
+		memcpy(&cards[i], &cards[i + 1], sizeof(cards[i]));
+}
+
+int
+HiSax_inithardware(void)
+{
+	int foundcards = 0;
+	int i = 0;
+	int t = ',';
+	int flg = 0;
+	char *id;
+	char *next_id = HiSax_id;
+	char ids[20];
+
+	if (strchr(HiSax_id, ','))
+		t = ',';
+	else if (strchr(HiSax_id, '%'))
+		t = '%';
+
+	while (i < nrcards) {
+		if (cards[i].typ < 1)
+			break;
+		id = next_id;
+		if ((next_id = strchr(id, t))) {
+			*next_id++ = 0;
+			strcpy(ids, id);
+			flg = i + 1;
+		} else {
+			next_id = id;
+			if (flg >= i)
+				strcpy(ids, id);
+			else
+				sprintf(ids, "%s%d", id, i);
+		}
+		if (checkcard(i, ids)) {
+			foundcards++;
+			i++;
+		} else {
+			printk(KERN_WARNING "HiSax: Card %s not installed !\n",
+			       CardType[cards[i].typ]);
+			if (cards[i].sp)
+				kfree((void *) cards[i].sp);
+			cards[i].sp = NULL;
+			HiSax_shiftcards(i);
+		}
+	}
+	return foundcards;
+}
+
+void
+HiSax_closehardware(void)
+{
+	int i;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	for (i = 0; i < nrcards; i++)
+		if (cards[i].sp) {
+			ll_stop(cards[i].sp);
+			CallcFreeChan(cards[i].sp);
+			release_tei(cards[i].sp);
+			release_irq(i);
+			closecard(i);
+			kfree((void *) cards[i].sp);
+			cards[i].sp = NULL;
+		}
+	Isdnl2Free();
+	CallcFree();
+	restore_flags(flags);
+}
+
+static void
+hscx_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware;
+	struct HscxState *hsp = sp->hs + st->l1.hscx;
+	long flags;
+
+	switch (pr) {
+		case (PH_DATA):
+			save_flags(flags);
+			cli();
+			if (hsp->tx_skb) {
+				skb_queue_tail(&hsp->squeue, skb);
+				restore_flags(flags);
+			} else {
+				restore_flags(flags);
+				hsp->tx_skb = skb;
+				hsp->count = 0;
+				sp->hscx_fill_fifo(hsp);
+			}
+			break;
+		case (PH_DATA_PULLED):
+			if (hsp->tx_skb) {
+				printk(KERN_WARNING "hscx_l2l1: this shouldn't happen\n");
+				break;
+			}
+			hsp->tx_skb = skb;
+			hsp->count = 0;
+			sp->hscx_fill_fifo(hsp);
+			break;
+		case (PH_REQUEST_PULL):
+			if (!hsp->tx_skb) {
+				st->l1.requestpull = 0;
+				st->l1.l1l2(st, PH_PULL_ACK, NULL);
+			} else
+				st->l1.requestpull = !0;
+			break;
+	}
+
+}
+extern struct IsdnBuffers *tracebuf;
+
+static void
+hscx_l2l1discardq(struct PStack *st, int pr, void *heldby,
+		  int releasetoo)
+{
+	struct IsdnCardState *sp = (struct IsdnCardState *)
+	st->l1.hardware;
+	struct HscxState *hsp = sp->hs + st->l1.hscx;
+	struct sk_buff *skb;
+
+#ifdef DEBUG_MAGIC
+	if (hsp->magic != 301270) {
+		printk(KERN_DEBUG "hscx_discardq magic not 301270\n");
+		return;
+	}
+#endif
+
+	while ((skb = skb_dequeue(&hsp->squeue)))
+		dev_kfree_skb(skb, FREE_WRITE);
+}
+
+static int
+open_hscxstate(struct IsdnCardState *sp,
+	       int hscx)
+{
+	struct HscxState *hsp = sp->hs + hscx;
+
+	if (!hsp->init) {
+		if (!(hsp->rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for hscx_rcvbuf\n");
+			return (1);
+		}
+		skb_queue_head_init(&hsp->rqueue);
+		skb_queue_head_init(&hsp->squeue);
+	}
+	hsp->init = !0;
+
+	hsp->tx_skb = NULL;
+	hsp->event = 0;
+	hsp->rcvidx = 0;
+	hsp->tx_cnt = 0;
+	return (0);
+}
+
+static void
+hscx_manl1(struct PStack *st, int pr,
+	   void *arg)
+{
+	struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware;
+	struct HscxState *hsp = sp->hs + st->l1.hscx;
+
+	switch (pr) {
+		case (PH_ACTIVATE):
+			hsp->active = !0;
+			sp->modehscx(hsp, st->l1.hscxmode, st->l1.hscxchannel);
+			st->l1.l1man(st, PH_ACTIVATE, NULL);
+			break;
+		case (PH_DEACTIVATE):
+			if (!hsp->tx_skb)
+				sp->modehscx(hsp, 0, 0);
+
+			hsp->active = 0;
+			break;
+	}
+}
+
+int
+setstack_hscx(struct PStack *st, struct HscxState *hs)
+{
+	if (open_hscxstate(st->l1.hardware, hs->hscx))
+		return (-1);
+
+	st->l1.hscx = hs->hscx;
+	st->l2.l2l1 = hscx_l2l1;
+	st->ma.manl1 = hscx_manl1;
+	st->l2.l2l1discardq = hscx_l2l1discardq;
+
+	st->l1.act_state = 0;
+	st->l1.requestpull = 0;
+
+	hs->st = st;
+	return (0);
+}
+
+void
+HiSax_reportcard(int cardnr)
+{
+	struct IsdnCardState *sp = cards[cardnr].sp;
+
+	printk(KERN_DEBUG "HiSax: reportcard No %d\n", cardnr + 1);
+	printk(KERN_DEBUG "HiSax: Type %s\n", CardType[sp->typ]);
+	printk(KERN_DEBUG "HiSax: debuglevel %x\n", sp->debug);
+	printk(KERN_DEBUG "HiSax: HiSax_reportcard address 0x%lX\n",
+	       (ulong) & HiSax_reportcard);
+}
+
+#ifdef L2FRAME_DEBUG		/* psa */
+
+char *
+l2cmd(u_char cmd)
+{
+	switch (cmd & ~0x10) {
+		case 1:
+			return "RR";
+		case 5:
+			return "RNR";
+		case 9:
+			return "REJ";
+		case 0x6f:
+			return "SABME";
+		case 0x0f:
+			return "DM";
+		case 3:
+			return "UI";
+		case 0x43:
+			return "DISC";
+		case 0x63:
+			return "UA";
+		case 0x87:
+			return "FRMR";
+		case 0xaf:
+			return "XID";
+		default:
+			if (!(cmd & 1))
+				return "I";
+			else
+				return "invalid command";
+	}
+}
+
+static char tmp[20];
+
+char *
+l2frames(u_char * ptr)
+{
+	switch (ptr[2] & ~0x10) {
+		case 1:
+		case 5:
+		case 9:
+			sprintf(tmp, "%s[%d](nr %d)", l2cmd(ptr[2]), ptr[3] & 1, ptr[3] >> 1);
+			break;
+		case 0x6f:
+		case 0x0f:
+		case 3:
+		case 0x43:
+		case 0x63:
+		case 0x87:
+		case 0xaf:
+			sprintf(tmp, "%s[%d]", l2cmd(ptr[2]), (ptr[2] & 0x10) >> 4);
+			break;
+		default:
+			if (!(ptr[2] & 1)) {
+				sprintf(tmp, "I[%d](ns %d, nr %d)", ptr[3] & 1, ptr[2] >> 1, ptr[3] >> 1);
+				break;
+			} else
+				return "invalid command";
+	}
+
+
+	return tmp;
+}
+
+void
+Logl2Frame(struct IsdnCardState *sp, struct sk_buff *skb, char *buf, int dir)
+{
+	char tmp[132];
+	u_char *ptr;
+
+	ptr = skb->data;
+
+	if (ptr[0] & 1 || !(ptr[1] & 1))
+		debugl1(sp, "Addres not LAPD");
+	else {
+		sprintf(tmp, "%s %s: %s%c (sapi %d, tei %d)",
+			(dir ? "<-" : "->"), buf, l2frames(ptr),
+			((ptr[0] & 2) >> 1) == dir ? 'C' : 'R', ptr[0] >> 2, ptr[1] >> 1);
+		debugl1(sp, tmp);
+	}
+}
+
+#endif
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/isdnl1.h linux/drivers/isdn/hisax/isdnl1.h
--- v2.0.30/linux/drivers/isdn/hisax/isdnl1.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/isdnl1.h	Mon Aug  4 17:34:00 1997
@@ -0,0 +1,51 @@
+/* $Id: isdnl1.h,v 1.4 1997/04/06 22:55:52 keil Exp $
+ *
+ * $Log: isdnl1.h,v $
+ * Revision 1.4  1997/04/06 22:55:52  keil
+ * Using SKB's
+ *
+ * Revision 1.3  1996/12/08 19:41:55  keil
+ * L2FRAME_DEBUG
+ *
+ * Revision 1.2  1996/10/27 22:26:27  keil
+ * ISAC/HSCX version functions
+ *
+ * Revision 1.1  1996/10/13 20:03:47  keil
+ * Initial revision
+ *
+ *
+ *
+ */
+
+
+#define L2FRAME_DEBUG
+
+/* DEBUG Level */
+
+#define	L1_DEB_WARN		0x01
+#define	L1_DEB_INTSTAT		0x02
+#define	L1_DEB_ISAC		0x04
+#define	L1_DEB_ISAC_FIFO	0x08
+#define	L1_DEB_HSCX		0x10
+#define	L1_DEB_HSCX_FIFO	0x20
+#define	L1_DEB_LAPD	        0x40
+
+
+#define ISAC_RCVBUFREADY 0
+#define ISAC_XMTBUFREADY 1
+#define ISAC_PHCHANGE    2
+
+#define HSCX_RCVBUFREADY 0
+#define HSCX_XMTBUFREADY 1
+
+extern void debugl1(struct IsdnCardState *sp, char *msg);
+extern char *HscxVersion(u_char v);
+extern char *ISACVersion(u_char v);
+extern void hscx_sched_event(struct HscxState *hsp, int event);
+extern void isac_sched_event(struct IsdnCardState *sp, int event);
+extern void isac_new_ph(struct IsdnCardState *sp);
+extern get_irq(int cardnr, void *routine);
+
+#ifdef L2FRAME_DEBUG
+extern void Logl2Frame(struct IsdnCardState *sp, struct sk_buff *skb, char *buf, int dir);
+#endif
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/isdnl2.c linux/drivers/isdn/hisax/isdnl2.c
--- v2.0.30/linux/drivers/isdn/hisax/isdnl2.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/isdnl2.c	Mon Aug  4 17:34:00 1997
@@ -0,0 +1,1325 @@
+/* $Id: isdnl2.c,v 1.10 1997/05/06 09:38:13 keil Exp $
+
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *              based on the teles driver from Jan den Ouden
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ * $Log: isdnl2.c,v $
+ * Revision 1.10  1997/05/06 09:38:13  keil
+ * Bugfixes: - clear ack queue entries after resend
+ *           - acknowlege each frame to linklevel
+ *           - UA for SABM is Response, not command
+ *           - only RR was send as supervisor frame (X.75 hangs after a
+ *             sequence error)
+ *
+ * Revision 1.9  1997/04/07 23:02:11  keil
+ * missing braces
+ *
+ * Revision 1.8  1997/04/06 22:59:59  keil
+ * Using SKB's; changing function names; some minor changes
+ *
+ * Revision 1.7  1997/02/09 00:25:44  keil
+ * new interface handling, one interface per card
+ *
+ * Revision 1.6  1997/01/21 22:23:42  keil
+ * D-channel log changed
+ *
+ * Revision 1.5  1997/01/04 13:47:06  keil
+ * handling of MDL_REMOVE added (Thanks to Sim Yskes)
+ *
+ * Revision 1.4  1996/12/08 19:51:51  keil
+ * many fixes from Pekka Sarnila
+ *
+ * Revision 1.3  1996/11/05 19:39:12  keil
+ * X.75 bugfixes Thank to Martin Maurer
+ *
+ * Revision 1.2  1996/10/30 10:20:58  keil
+ * X.75 answer of SABMX fixed to response address (AVM X.75 problem)
+ *
+ * Revision 1.1  1996/10/13 20:04:54  keil
+ * Initial revision
+ *
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isdnl2.h"
+
+const char *l2_revision = "$Revision: 1.10 $";
+
+static void l2m_debug(struct FsmInst *fi, char *s);
+
+struct Fsm l2fsm =
+{NULL, 0, 0, NULL, NULL};
+
+enum {
+	ST_L2_1,
+	ST_L2_3,
+	ST_L2_4,
+	ST_L2_5,
+	ST_L2_6,
+	ST_L2_7,
+	ST_L2_8,
+};
+
+#define L2_STATE_COUNT (ST_L2_8+1)
+
+static char *strL2State[] =
+{
+	"ST_L2_1",
+	"ST_L2_3",
+	"ST_L2_4",
+	"ST_L2_5",
+	"ST_L2_6",
+	"ST_L2_7",
+	"ST_L2_8",
+};
+
+enum {
+	EV_L2_UI,
+	EV_L2_SABMX,
+	EV_L2_UA,
+	EV_L2_DISC,
+	EV_L2_I,
+	EV_L2_RR,
+	EV_L2_REJ,
+	EV_L2_FRMR,
+	EV_L2_DL_DATA,
+	EV_L2_DL_ESTABLISH,
+	EV_L2_MDL_ASSIGN,
+	EV_L2_MDL_REMOVE,
+	EV_L2_DL_UNIT_DATA,
+	EV_L2_DL_RELEASE,
+	EV_L2_MDL_NOTEIPROC,
+	EV_L2_T200,
+	EV_L2_ACK_PULL,
+	EV_L2_T203,
+	EV_L2_RNR,
+};
+
+#define L2_EVENT_COUNT (EV_L2_RNR+1)
+
+static char *strL2Event[] =
+{
+	"EV_L2_UI",
+	"EV_L2_SABMX",
+	"EV_L2_UA",
+	"EV_L2_DISC",
+	"EV_L2_I",
+	"EV_L2_RR",
+	"EV_L2_REJ",
+	"EV_L2_FRMR",
+	"EV_L2_DL_DATA",
+	"EV_L2_DL_ESTABLISH",
+	"EV_L2_MDL_ASSIGN",
+	"EV_L2_MDL_REMOVE",
+	"EV_L2_DL_UNIT_DATA",
+	"EV_L2_DL_RELEASE",
+	"EV_L2_MDL_NOTEIPROC",
+	"EV_L2_T200",
+	"EV_L2_ACK_PULL",
+	"EV_L2_T203",
+	"EV_L2_RNR",
+};
+
+int errcount = 0;
+
+static int l2addrsize(struct Layer2 *tsp);
+
+static void
+InitWin(struct Layer2 *l2)
+{
+	int i;
+
+	for (i = 0; i < MAX_WINDOW; i++)
+		l2->windowar[i] = NULL;
+}
+
+static void
+ReleaseWin(struct Layer2 *l2)
+{
+	int i, cnt = 0;
+
+
+	for (i = 0; i < MAX_WINDOW; i++) {
+		if (l2->windowar[i]) {
+			cnt++;
+			dev_kfree_skb(l2->windowar[i], FREE_WRITE);
+			l2->windowar[i] = NULL;
+		}
+	}
+	if (cnt)
+		printk(KERN_WARNING "isdl2 freed %d skbuffs in release\n", cnt);
+}
+
+static int
+cansend(struct PStack *st)
+{
+	int p1;
+
+	p1 = (st->l2.va + st->l2.window) % (st->l2.extended ? 128 : 8);
+	return (st->l2.vs != p1);
+}
+
+static void
+discard_i_queue(struct PStack *st)
+{
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(&st->l2.i_queue))) {
+		SET_SKB_FREE(skb);
+		dev_kfree_skb(skb, FREE_READ);
+	}
+}
+
+int
+l2headersize(struct Layer2 *tsp, int ui)
+{
+	return ((tsp->extended && (!ui) ? 2 : 1) + (tsp->laptype == LAPD ? 2 : 1));
+}
+
+int
+l2addrsize(struct Layer2 *tsp)
+{
+	return (tsp->laptype == LAPD ? 2 : 1);
+}
+
+static int
+sethdraddr(struct Layer2 *tsp,
+	   u_char * header, int rsp)
+{
+	u_char *ptr = header;
+	int crbit;
+
+	if (tsp->laptype == LAPD) {
+		crbit = rsp;
+		if (!tsp->orig)
+			crbit = !crbit;
+		*ptr++ = (tsp->sap << 2) | (crbit ? 2 : 0);
+		*ptr++ = (tsp->tei << 1) | 1;
+		return (2);
+	} else {
+		crbit = rsp;
+		if (tsp->orig)
+			crbit = !crbit;
+		if (crbit)
+			*ptr++ = 1;
+		else
+			*ptr++ = 3;
+		return (1);
+	}
+}
+
+static void
+enqueue_ui(struct PStack *st,
+	   struct sk_buff *skb)
+{
+	st->l2.l2l1(st, PH_DATA, skb);
+}
+
+static void
+enqueue_super(struct PStack *st,
+	      struct sk_buff *skb)
+{
+	st->l2.l2l1(st, PH_DATA, skb);
+}
+
+static int
+legalnr(struct PStack *st, int nr)
+{
+	struct Layer2 *l2 = &st->l2;
+	int lnr, lvs;
+
+	lvs = (l2->vs >= l2->va) ? l2->vs : (l2->vs + l2->extended ? 128 : 8);
+	lnr = (nr >= l2->va) ? nr : (nr + l2->extended ? 128 : 8);
+	return (lnr <= lvs);
+}
+
+static void
+setva(struct PStack *st, int nr)
+{
+	struct Layer2 *l2 = &st->l2;
+
+	if (l2->va != nr) {
+		while (l2->va != nr) {
+			l2->va = (l2->va + 1) % (l2->extended ? 128 : 8);
+			dev_kfree_skb(l2->windowar[l2->sow], FREE_WRITE);
+			l2->windowar[l2->sow] = NULL;
+			l2->sow = (l2->sow + 1) % l2->window;
+			if (st->l4.l2writewakeup)
+				st->l4.l2writewakeup(st);
+		}
+	}
+}
+
+static void
+l2s1(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	st->l2.l2tei(st, MDL_ASSIGN, (void *) st->l2.ces);
+	FsmChangeState(fi, ST_L2_3);
+}
+
+static void
+l2_send_ui(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	u_char header[MAX_HEADER_LEN];
+	int i;
+
+	i = sethdraddr(&(st->l2), header, CMD);
+	header[i++] = UI;
+	memcpy(skb_push(skb, i), header, i);
+	enqueue_ui(st, skb);
+}
+
+static void
+l2_receive_ui(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_pull(skb, l2headersize(&st->l2, 1));
+	st->l2.l2l3(st, DL_UNIT_DATA, skb);
+}
+
+inline void
+send_uframe(struct PStack *st, u_char cmd, u_char cr)
+{
+	struct sk_buff *skb;
+	u_char tmp[MAX_HEADER_LEN];
+	int i;
+
+	i = sethdraddr(&st->l2, tmp, cr);
+	tmp[i++] = cmd;
+	if (!(skb = alloc_skb(i, GFP_ATOMIC))) {
+		printk(KERN_WARNING "isdl2 can't alloc sbbuff for send_uframe\n");
+		return;
+	}
+	SET_SKB_FREE(skb);
+	memcpy(skb_put(skb, i), tmp, i);
+	enqueue_super(st, skb);
+}
+
+static void
+establishlink(struct FsmInst *fi)
+{
+	struct PStack *st = fi->userdata;
+	u_char cmd;
+
+	FsmChangeState(fi, ST_L2_5);
+	st->l2.rc = 0;
+
+	if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 1))
+		if (st->l2.l2m.debug)
+			l2m_debug(&st->l2.l2m, "FAT 1");
+
+
+	cmd = (st->l2.extended ? SABME : SABM) | 0x10; 
+	send_uframe(st, cmd, CMD);
+}
+
+static void
+l2_establish(struct FsmInst *fi, int event, void *arg)
+{
+	establishlink(fi);
+}
+
+static void
+l2_send_disconn(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct Channel *chanp = st->l4.userdata;
+
+	FsmChangeState(fi, ST_L2_6);
+
+	FsmDelTimer(&st->l2.t203_timer, 1);
+	if (st->l2.t200_running) {
+		FsmDelTimer(&st->l2.t200_timer, 2);
+		st->l2.t200_running = 0;
+	}
+	st->l2.rc = 0;
+	if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 2))
+		if (st->l2.l2m.debug)
+			l2m_debug(&st->l2.l2m, "FAT 2");
+
+
+	if (!((chanp->impair == 2) && (st->l2.laptype == LAPB)))
+		send_uframe(st, DISC | 0x10, CMD);
+
+	discard_i_queue(st);
+}
+
+static void
+l2_got_SABMX(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int est = 1, state;
+	u_char PollFlag;
+
+	state = fi->state;
+
+	skb_pull(skb, l2addrsize(&(st->l2)));
+	PollFlag = *skb->data & 0x10;
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+
+	if (ST_L2_4 != state)
+		if (st->l2.vs != st->l2.va) {
+			discard_i_queue(st);
+			est = 1;
+		} else
+			est = 0;
+
+	st->l2.vs = 0;
+	st->l2.va = 0;
+	st->l2.vr = 0;
+	st->l2.sow = 0;
+	if (ST_L2_7 != state)
+		FsmChangeState(fi, ST_L2_7);
+
+	send_uframe(st, UA | PollFlag, RSP);
+
+	if (st->l2.t200_running) {
+		FsmDelTimer(&st->l2.t200_timer, 15);
+		st->l2.t200_running = 0;
+	}
+	if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 3))
+		if (st->l2.l2m.debug)
+			l2m_debug(&st->l2.l2m, "FAT 3");
+
+	if (est)
+		st->l2.l2man(st, DL_ESTABLISH, NULL);
+
+	if (ST_L2_8 == state)
+		if (skb_queue_len(&st->l2.i_queue) && cansend(st))
+			st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+}
+
+static void
+l2_got_disconn(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	struct Channel *chanp = st->l4.userdata;
+	u_char PollFlag;
+
+	skb_pull(skb, l2addrsize(&(st->l2)));
+	PollFlag = *skb->data & 0x10;
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+
+	FsmChangeState(fi, ST_L2_4);
+
+	FsmDelTimer(&st->l2.t203_timer, 3);
+	if (st->l2.t200_running) {
+		FsmDelTimer(&st->l2.t200_timer, 4);
+		st->l2.t200_running = 0;
+	}
+	if (!((chanp->impair == 1) && (st->l2.laptype == LAPB)))
+		send_uframe(st, UA | PollFlag, RSP);
+
+	st->l2.l2man(st, DL_RELEASE, NULL);
+}
+
+static void
+l2_got_st4_disc(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	struct Channel *chanp = st->l4.userdata;
+	u_char PollFlag;
+
+	skb_pull(skb, l2addrsize(&(st->l2)));
+	PollFlag = *skb->data & 0x10;
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+
+	if (!((chanp->impair == 1) && (st->l2.laptype == LAPB)))
+		send_uframe(st, DM | (PollFlag ? 0x10 : 0x0), RSP);
+
+}
+
+static void
+l2_got_ua_establish(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	u_char f;
+
+	skb_pull(skb, l2addrsize(&(st->l2)));
+	f = *skb->data & 0x10;
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+	if (f) {
+		st->l2.vs = 0;
+		st->l2.va = 0;
+		st->l2.vr = 0;
+		st->l2.sow = 0;
+		FsmChangeState(fi, ST_L2_7);
+
+		FsmDelTimer(&st->l2.t200_timer, 5);
+		if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 4))
+			if (st->l2.l2m.debug)
+				l2m_debug(&st->l2.l2m, "FAT 4");
+
+		st->l2.l2man(st, DL_ESTABLISH, NULL);
+	}
+}
+
+static void
+l2_got_ua_disconn(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	u_char f;
+
+	skb_pull(skb, l2addrsize(&(st->l2)));
+	f = *skb->data & 0x10;
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+	if (f) {
+		FsmDelTimer(&st->l2.t200_timer, 6);
+		FsmChangeState(fi, ST_L2_4);
+		st->l2.l2man(st, DL_RELEASE, NULL);
+	}
+}
+
+inline void
+enquiry_cr(struct PStack *st, u_char typ, u_char cr, u_char pf)
+{
+	struct sk_buff *skb;
+	struct Layer2 *l2;
+	u_char tmp[MAX_HEADER_LEN];
+	int i;
+
+	l2 = &st->l2;
+	i = sethdraddr(l2, tmp, cr);
+	if (l2->extended) {
+		tmp[i++] = typ;
+		tmp[i++] = (l2->vr << 1) | (pf ? 1 : 0);
+	} else
+		tmp[i++] = (l2->vr << 5) | typ | (pf ? 0x10 : 0);
+	if (!(skb = alloc_skb(i, GFP_ATOMIC))) {
+		printk(KERN_WARNING "isdl2 can't alloc sbbuff for enquiry_cr\n");
+		return;
+	}
+	SET_SKB_FREE(skb);
+	memcpy(skb_put(skb, i), tmp, i);
+	enqueue_super(st, skb);
+}
+
+inline void
+enquiry_response(struct PStack *st, u_char typ, u_char final)
+{
+	enquiry_cr(st, typ, RSP, final);
+}
+
+inline void
+enquiry_command(struct PStack *st, u_char typ, u_char poll)
+{
+	enquiry_cr(st, typ, CMD, poll);
+}
+
+static void
+nrerrorrecovery(struct FsmInst *fi)
+{
+	/* should log error here */
+	establishlink(fi);
+}
+
+static void
+l2_got_st7_RR(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct Channel *chanp = st->l4.userdata;
+	struct sk_buff *skb = arg;
+	int PollFlag, seq, rsp;
+	struct Layer2 *l2;
+
+	l2 = &st->l2;
+	if (l2->laptype == LAPD)
+		rsp = *skb->data & 0x2;
+	else
+		rsp = *skb->data == 0x3;
+	if (l2->orig)
+		rsp = !rsp;
+
+	skb_pull(skb, l2addrsize(l2));
+	if (l2->extended) {
+		PollFlag = (skb->data[1] & 0x1) == 0x1;
+		seq = skb->data[1] >> 1;
+	} else {
+		PollFlag = (skb->data[0] & 0x10);
+		seq = (skb->data[0] >> 5) & 0x7;
+	}
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+
+	if (!((chanp->impair == 4) && (st->l2.laptype == LAPB)))
+		if ((!rsp) && PollFlag)
+			enquiry_response(st, RR, PollFlag);
+
+	if (legalnr(st, seq)) {
+		if (seq == l2->vs) {
+			setva(st, seq);
+			FsmDelTimer(&l2->t200_timer, 7);
+			l2->t200_running = 0;
+			FsmDelTimer(&l2->t203_timer, 8);
+			if (FsmAddTimer(&l2->t203_timer, l2->t203, EV_L2_T203, NULL, 5))
+				if (l2->l2m.debug)
+					l2m_debug(&st->l2.l2m, "FAT 5");
+
+			if (skb_queue_len(&st->l2.i_queue))
+				st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+		} else if (l2->va != seq) {
+			setva(st, seq);
+			FsmDelTimer(&st->l2.t200_timer, 9);
+			if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 6))
+				if (st->l2.l2m.debug)
+					l2m_debug(&st->l2.l2m, "FAT 6");
+			if (skb_queue_len(&st->l2.i_queue))
+				st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+		}
+	} else
+		nrerrorrecovery(fi);
+
+	if ((fi->userint & LC_FLUSH_WAIT) && rsp && !(skb_queue_len(&st->l2.i_queue))) {
+		fi->userint &= ~LC_FLUSH_WAIT;
+		st->l2.l2man(st, DL_FLUSH, NULL);
+	}
+}
+
+static void
+l2_feed_iqueue(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_queue_tail(&st->l2.i_queue, skb);
+	st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+}
+
+static int
+icommandreceived(struct FsmInst *fi, int event, void *arg, int *nr)
+{
+	struct PStack *st = fi->userdata;
+	struct Channel *chanp = st->l4.userdata;
+	struct sk_buff *skb = arg;
+	struct IsdnCardState *sp = st->l1.hardware;
+	struct Layer2 *l2 = &(st->l2);
+	int i, p, seq, wasok;
+	char str[64];
+
+	i = l2addrsize(l2);
+	if (l2->extended) {
+		p = (skb->data[i + 1] & 0x1) == 0x1;
+		seq = skb->data[i] >> 1;
+		*nr = (skb->data[i + 1] >> 1) & 0x7f;
+	} else {
+		p = (skb->data[i] & 0x10);
+		seq = (skb->data[i] >> 1) & 0x7;
+		*nr = (skb->data[i] >> 5) & 0x7;
+	}
+
+	if (l2->vr == seq) {
+		wasok = !0;
+
+		l2->vr = (l2->vr + 1) % (l2->extended ? 128 : 8);
+		l2->rejexp = 0;
+
+		if (st->l2.laptype == LAPD)
+			if (sp->dlogflag) {
+				LogFrame(st->l1.hardware, skb->data, skb->len);
+				sprintf(str, "Q.931 frame network->user tei %d", st->l2.tei);
+				dlogframe(st->l1.hardware, skb->data + l2->ihsize,
+					  skb->len - l2->ihsize, str);
+			}
+		if (!((chanp->impair == 3) && (st->l2.laptype == LAPB)))
+			if (p || (!skb_queue_len(&st->l2.i_queue)))
+				enquiry_response(st, RR, p);
+		skb_pull(skb, l2headersize(l2, 0));
+	} else {
+		/* n(s)!=v(r) */
+		wasok = 0;
+		SET_SKB_FREE(skb);
+		dev_kfree_skb(skb, FREE_READ);
+		if (st->l2.rejexp) {
+			if (p)
+				if (!((chanp->impair == 3) && (st->l2.laptype == LAPB)))
+					enquiry_response(st, RR, p);
+		} else {
+			st->l2.rejexp = !0;
+			enquiry_command(st, REJ, 1);
+		}
+	}
+	return wasok;
+}
+
+static void
+l2_got_st7_data(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int nr, wasok;
+
+	wasok = icommandreceived(fi, event, arg, &nr);
+
+	if (legalnr(st, nr)) {
+		if (nr == st->l2.vs) {
+			setva(st, nr);
+			FsmDelTimer(&st->l2.t200_timer, 10);
+			st->l2.t200_running = 0;
+			FsmDelTimer(&st->l2.t203_timer, 11);
+			if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 7))
+				if (st->l2.l2m.debug)
+					l2m_debug(&st->l2.l2m, "FAT 5");
+
+			if (skb_queue_len(&st->l2.i_queue))
+				st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+		} else if (nr != st->l2.va) {
+			setva(st, nr);
+			FsmDelTimer(&st->l2.t200_timer, 12);
+			if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 8))
+				if (st->l2.l2m.debug)
+					l2m_debug(&st->l2.l2m, "FAT 6");
+
+			if (skb_queue_len(&st->l2.i_queue))
+				st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+		}
+	} else
+		nrerrorrecovery(fi);
+
+	if (wasok)
+		st->l2.l2l3(st, DL_DATA, skb);
+}
+
+static void
+l2_got_st8_data(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int nr, wasok;
+
+	wasok = icommandreceived(fi, event, arg, &nr);
+
+	if (legalnr(st, nr)) {
+		setva(st, nr);
+		if (skb_queue_len(&st->l2.i_queue))
+			st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+	} else
+		nrerrorrecovery(fi);
+
+	if (wasok)
+		st->l2.l2l3(st, DL_DATA, skb);
+}
+
+static void
+l2_got_tei(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	st->l2.tei = (int) arg;
+	establishlink(fi);
+}
+
+static void
+invoke_retransmission(struct PStack *st, int nr)
+{
+	struct Layer2 *l2 = &st->l2;
+	int p1;
+
+	if (l2->vs != nr) {
+		while (l2->vs != nr) {
+
+			l2->vs = l2->vs - 1;
+			if (l2->vs < 0)
+				l2->vs += l2->extended ? 128 : 8;
+
+			p1 = l2->vs - l2->va;
+			if (p1 < 0)
+				p1 += l2->extended ? 128 : 8;
+			p1 = (p1 + l2->sow) % l2->window;
+
+			skb_queue_head(&l2->i_queue, l2->windowar[p1]);
+			l2->windowar[p1] = NULL;
+		}
+		st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+	}
+}
+
+static void
+l2_got_st7_rej(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int PollFlag, seq, rsp;
+	struct Layer2 *l2;
+
+	l2 = &st->l2;
+	if (l2->laptype == LAPD)
+		rsp = *skb->data & 0x2;
+	else
+		rsp = *skb->data == 0x3;
+	if (l2->orig)
+		rsp = !rsp;
+
+	skb_pull(skb, l2addrsize(l2));
+	if (l2->extended) {
+		PollFlag = (skb->data[1] & 0x1) == 0x1;
+		seq = skb->data[1] >> 1;
+	} else {
+		PollFlag = (skb->data[0] & 0x10);
+		seq = (skb->data[0] >> 5) & 0x7;
+	}
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+
+	if ((!rsp) && PollFlag)
+		enquiry_response(st, RR, PollFlag);
+
+	if (!legalnr(st, seq))
+		return;
+
+	setva(st, seq);
+	invoke_retransmission(st, seq);
+}
+
+static void
+l2_no_tei(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L2_4);
+}
+
+static void
+l2_st5_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	u_char cmd;
+
+	if (st->l2.rc == st->l2.n200) {
+		FsmChangeState(fi, ST_L2_4);
+		st->l2.l2tei(st, MDL_VERIFY, (void *) st->l2.tei);
+		st->l2.l2man(st, DL_RELEASE, NULL);
+	} else {
+		st->l2.rc++;
+
+		if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 9))
+			if (st->l2.l2m.debug)
+				l2m_debug(&st->l2.l2m, "FAT 7");
+
+		cmd = (st->l2.extended ? SABME : SABM) | 0x10; 
+		send_uframe(st, cmd, CMD);
+	}
+}
+
+static void
+l2_st6_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct Channel *chanp = st->l4.userdata;
+
+	if (st->l2.rc == st->l2.n200) {
+		FsmChangeState(fi, ST_L2_4);
+		st->l2.l2man(st, DL_RELEASE, NULL);
+	} else {
+		st->l2.rc++;
+
+		if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 10))
+			if (st->l2.l2m.debug)
+				l2m_debug(&st->l2.l2m, "FAT 8");
+
+
+		if (!((chanp->impair == 2) && (st->l2.laptype == LAPB)))
+			send_uframe(st, DISC | 0x10, CMD);
+
+	}
+}
+
+static void
+l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb;
+	struct Layer2 *l2 = &st->l2;
+	u_char header[MAX_HEADER_LEN];
+	int p1, i;
+
+	if (!cansend(st))
+		return;
+
+	skb = skb_dequeue(&l2->i_queue);
+	if (!skb)
+		return;
+
+	p1 = l2->vs - l2->va;
+	if (p1 < 0)
+		p1 += l2->extended ? 128 : 8;
+	p1 = (p1 + l2->sow) % l2->window;
+	if (l2->windowar[p1]) {
+		printk(KERN_WARNING "isdnl2 try overwrite ack queue entry %d\n",
+		       p1);
+		dev_kfree_skb(l2->windowar[p1], FREE_WRITE);
+	}
+	l2->windowar[p1] = skb_clone(skb, GFP_ATOMIC);
+
+	i = sethdraddr(&st->l2, header, CMD);
+
+	if (l2->extended) {
+		header[i++] = l2->vs << 1;
+		header[i++] = l2->vr << 1;
+		l2->vs = (l2->vs + 1) % 128;
+	} else {
+		header[i++] = (l2->vr << 5) | (l2->vs << 1);
+		l2->vs = (l2->vs + 1) % 8;
+	}
+
+	memcpy(skb_push(skb, i), header, i);
+	st->l2.l2l1(st, PH_DATA_PULLED, skb);
+	if (!st->l2.t200_running) {
+		FsmDelTimer(&st->l2.t203_timer, 13);
+		if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 11))
+			if (st->l2.l2m.debug)
+				l2m_debug(&st->l2.l2m, "FAT 9");
+
+		st->l2.t200_running = !0;
+	}
+	if (skb_queue_len(&l2->i_queue) && cansend(st))
+		st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+}
+
+static void
+transmit_enquiry(struct PStack *st)
+{
+
+	enquiry_command(st, RR, 1);
+	if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 12))
+		if (st->l2.l2m.debug)
+			l2m_debug(&st->l2.l2m, "FAT 10");
+
+	st->l2.t200_running = !0;
+}
+
+static void
+l2_st7_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	st->l2.t200_running = 0;
+
+	st->l2.rc = 1;
+	FsmChangeState(fi, ST_L2_8);
+	transmit_enquiry(st);
+}
+
+static void
+l2_got_st8_rr_rej(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int PollFlag, seq, rsp;
+	struct Layer2 *l2;
+
+	l2 = &st->l2;
+	if (l2->laptype == LAPD)
+		rsp = *skb->data & 0x2;
+	else
+		rsp = *skb->data == 0x3;
+	if (l2->orig)
+		rsp = !rsp;
+
+	skb_pull(skb, l2addrsize(l2));
+	if (l2->extended) {
+		PollFlag = (skb->data[1] & 0x1) == 0x1;
+		seq = skb->data[1] >> 1;
+	} else {
+		PollFlag = (skb->data[0] & 0x10);
+		seq = (skb->data[0] >> 5) & 0x7;
+	}
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+
+	if (rsp && PollFlag) {
+		if (legalnr(st, seq)) {
+			FsmChangeState(fi, ST_L2_7);
+			setva(st, seq);
+			if (st->l2.t200_running) {
+				FsmDelTimer(&st->l2.t200_timer, 14);
+				st->l2.t200_running = 0;
+			}
+			if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 13))
+				if (st->l2.l2m.debug)
+					l2m_debug(&st->l2.l2m, "FAT 11");
+
+			invoke_retransmission(st, seq);
+
+			if (skb_queue_len(&l2->i_queue) && cansend(st))
+				st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+			else if (fi->userint & LC_FLUSH_WAIT) {
+				fi->userint &= ~LC_FLUSH_WAIT;
+				st->l2.l2man(st, DL_FLUSH, NULL);
+			}
+		}
+	} else {
+		if (!rsp && PollFlag)
+			enquiry_response(st, RR, PollFlag);
+		if (legalnr(st, seq)) {
+			setva(st, seq);
+		}
+	}
+}
+
+static void
+l2_st7_tout_203(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	st->l2.rc = 0;
+	FsmChangeState(fi, ST_L2_8);
+	transmit_enquiry(st);
+}
+
+static void
+l2_st8_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if (st->l2.rc == st->l2.n200) {
+		establishlink(fi);
+	} else {
+		st->l2.rc++;
+		transmit_enquiry(st);
+	}
+}
+
+static void
+l2_got_FRMR(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	char tmp[64];
+
+	skb_pull(skb, l2addrsize(&st->l2));
+	if (st->l2.l2m.debug) {
+		if (st->l2.extended)
+			sprintf(tmp, "FRMR information %2x %2x %2x %2x %2x",
+				skb->data[0], skb->data[1], skb->data[2],
+				skb->data[3], skb->data[4]);
+		else
+			sprintf(tmp, "FRMR information %2x %2x %2x",
+				skb->data[0], skb->data[1], skb->data[2]);
+
+		l2m_debug(&st->l2.l2m, tmp);
+	}
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+}
+
+static void
+l2_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+/*TODO
+   if( DL_RELEASE.req outstanding ) {
+   ... issue DL_RELEASE.confirm
+   } else {
+   if( fi->state != ST_L2_4 ) {
+   ... issue DL_RELEASE.indication
+   }
+   }
+   TODO */
+	discard_i_queue(st);	/* There is no UI queue in layer 2 */
+	st->l2.tei = 255;
+	if (st->l2.t200_running) {
+		FsmDelTimer(&st->l2.t200_timer, 18);
+		st->l2.t200_running = 0;
+	}
+	FsmDelTimer(&st->l2.t203_timer, 19);
+	st->l2.l2man(st, DL_RELEASE, NULL);	/* TEMP */
+	FsmChangeState(fi, ST_L2_1);
+}
+
+inline int
+IsUI(u_char * data, int ext)
+{
+	return ((data[0] & 0xef) == UI);
+}
+
+inline int
+IsUA(u_char * data, int ext)
+{
+	return ((data[0] & 0xef) == UA);
+}
+
+inline int
+IsDISC(u_char * data, int ext)
+{
+	return ((data[0] & 0xef) == DISC);
+}
+
+inline int
+IsRR(u_char * data, int ext)
+{
+	if (ext)
+		return (data[0] == RR);
+	else
+		return ((data[0] & 0xf) == 1);
+}
+
+inline int
+IsI(u_char * data, int ext)
+{
+	return ((data[0] & 0x1) == 0x0);
+}
+
+inline int
+IsSABMX(u_char * data, int ext)
+{
+	u_char d = data[0] & ~0x10;
+	
+	return (ext ? d == SABME : d == SABM);
+}
+
+inline int
+IsREJ(u_char * data, int ext)
+{
+	return (ext ? data[0] == REJ : (data[0] & 0xf) == 0x9);
+}
+
+inline int
+IsFRMR(u_char * data, int ext)
+{
+	return ((data[0] & 0xef) == FRMR);
+}
+
+inline int
+IsRNR(u_char * data, int ext)
+{
+	if (ext)
+		return (data[0] == RNR);
+	else
+		return ((data[0] & 0xf) == 5);
+}
+
+static struct FsmNode L2FnList[] =
+{
+	{ST_L2_1, EV_L2_DL_ESTABLISH, l2s1},
+	{ST_L2_1, EV_L2_MDL_NOTEIPROC, l2_no_tei},
+	{ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei},
+	{ST_L2_4, EV_L2_DL_UNIT_DATA, l2_send_ui},
+	{ST_L2_4, EV_L2_DL_ESTABLISH, l2_establish},
+	{ST_L2_7, EV_L2_DL_UNIT_DATA, l2_send_ui},
+	{ST_L2_7, EV_L2_DL_DATA, l2_feed_iqueue},
+	{ST_L2_7, EV_L2_DL_RELEASE, l2_send_disconn},
+	{ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue},
+	{ST_L2_8, EV_L2_DL_DATA, l2_feed_iqueue},
+	{ST_L2_8, EV_L2_DL_RELEASE, l2_send_disconn},
+
+	{ST_L2_1, EV_L2_UI, l2_receive_ui},
+	{ST_L2_4, EV_L2_UI, l2_receive_ui},
+	{ST_L2_4, EV_L2_SABMX, l2_got_SABMX},
+	{ST_L2_4, EV_L2_DISC, l2_got_st4_disc},
+	{ST_L2_5, EV_L2_UA, l2_got_ua_establish},
+	{ST_L2_6, EV_L2_UA, l2_got_ua_disconn},
+	{ST_L2_7, EV_L2_UI, l2_receive_ui},
+	{ST_L2_7, EV_L2_DISC, l2_got_disconn},
+	{ST_L2_7, EV_L2_I, l2_got_st7_data},
+	{ST_L2_7, EV_L2_RR, l2_got_st7_RR},
+	{ST_L2_7, EV_L2_REJ, l2_got_st7_rej},
+	{ST_L2_7, EV_L2_SABMX, l2_got_SABMX},
+	{ST_L2_7, EV_L2_FRMR, l2_got_FRMR},
+	{ST_L2_8, EV_L2_RR, l2_got_st8_rr_rej},
+	{ST_L2_8, EV_L2_REJ, l2_got_st8_rr_rej},
+	{ST_L2_8, EV_L2_SABMX, l2_got_SABMX},
+	{ST_L2_8, EV_L2_DISC, l2_got_disconn},
+	{ST_L2_8, EV_L2_FRMR, l2_got_FRMR},
+	{ST_L2_8, EV_L2_I, l2_got_st8_data},
+
+	{ST_L2_5, EV_L2_T200, l2_st5_tout_200},
+	{ST_L2_6, EV_L2_T200, l2_st6_tout_200},
+	{ST_L2_7, EV_L2_T200, l2_st7_tout_200},
+	{ST_L2_7, EV_L2_T203, l2_st7_tout_203},
+	{ST_L2_8, EV_L2_T200, l2_st8_tout_200},
+
+	{ST_L2_1, EV_L2_MDL_REMOVE, l2_tei_remove},
+	{ST_L2_3, EV_L2_MDL_REMOVE, l2_tei_remove},
+	{ST_L2_4, EV_L2_MDL_REMOVE, l2_tei_remove},
+	{ST_L2_5, EV_L2_MDL_REMOVE, l2_tei_remove},
+	{ST_L2_6, EV_L2_MDL_REMOVE, l2_tei_remove},
+	{ST_L2_7, EV_L2_MDL_REMOVE, l2_tei_remove},
+	{ST_L2_8, EV_L2_MDL_REMOVE, l2_tei_remove},
+};
+
+#define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode))
+
+static void
+isdnl2_l1l2(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	u_char *datap;
+	int ret = !0;
+
+	switch (pr) {
+		case (PH_DATA):
+			datap = skb->data;
+			datap += l2addrsize(&st->l2);
+
+			if (IsI(datap, st->l2.extended))
+				ret = FsmEvent(&st->l2.l2m, EV_L2_I, skb);
+			else if (IsRR(datap, st->l2.extended))
+				ret = FsmEvent(&st->l2.l2m, EV_L2_RR, skb);
+			else if (IsUI(datap, st->l2.extended))
+				ret = FsmEvent(&st->l2.l2m, EV_L2_UI, skb);
+			else if (IsSABMX(datap, st->l2.extended))
+				ret = FsmEvent(&st->l2.l2m, EV_L2_SABMX, skb);
+			else if (IsUA(datap, st->l2.extended))
+				ret = FsmEvent(&st->l2.l2m, EV_L2_UA, skb);
+			else if (IsDISC(datap, st->l2.extended))
+				ret = FsmEvent(&st->l2.l2m, EV_L2_DISC, skb);
+			else if (IsREJ(datap, st->l2.extended))
+				ret = FsmEvent(&st->l2.l2m, EV_L2_REJ, skb);
+			else if (IsFRMR(datap, st->l2.extended))
+				ret = FsmEvent(&st->l2.l2m, EV_L2_FRMR, skb);
+			else if (IsRNR(datap, st->l2.extended))
+				ret = FsmEvent(&st->l2.l2m, EV_L2_RNR, skb);
+
+			if (ret) {
+				SET_SKB_FREE(skb);
+				dev_kfree_skb(skb, FREE_READ);
+			}
+			break;
+		case (PH_PULL_ACK):
+			FsmEvent(&st->l2.l2m, EV_L2_ACK_PULL, arg);
+			break;
+	}
+}
+
+static void
+isdnl2_l3l2(struct PStack *st, int pr, void *arg)
+{
+	switch (pr) {
+		case (DL_DATA):
+			if (FsmEvent(&st->l2.l2m, EV_L2_DL_DATA, arg)) {
+				dev_kfree_skb((struct sk_buff *) arg, FREE_READ);
+			}
+			break;
+		case (DL_UNIT_DATA):
+			if (FsmEvent(&st->l2.l2m, EV_L2_DL_UNIT_DATA, arg)) {
+				dev_kfree_skb((struct sk_buff *) arg, FREE_READ);
+			}
+			break;
+	}
+}
+
+static void
+isdnl2_manl2(struct PStack *st, int pr, void *arg)
+{
+	switch (pr) {
+		case (DL_ESTABLISH):
+			FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH, arg);
+			break;
+		case (DL_RELEASE):
+			FsmEvent(&st->l2.l2m, EV_L2_DL_RELEASE, arg);
+			break;
+		case (MDL_NOTEIPROC):
+			FsmEvent(&st->l2.l2m, EV_L2_MDL_NOTEIPROC, NULL);
+			break;
+		case (DL_FLUSH):
+			(&st->l2.l2m)->userint |= LC_FLUSH_WAIT;
+			break;
+	}
+}
+
+static void
+isdnl2_teil2(struct PStack *st, int pr, void *arg)
+{
+	switch (pr) {
+		case (MDL_ASSIGN):
+			FsmEvent(&st->l2.l2m, EV_L2_MDL_ASSIGN, arg);
+			break;
+		case (MDL_REMOVE):
+			FsmEvent(&st->l2.l2m, EV_L2_MDL_REMOVE, arg);
+			break;
+	}
+}
+
+void
+releasestack_isdnl2(struct PStack *st)
+{
+	FsmDelTimer(&st->l2.t200_timer, 15);
+	FsmDelTimer(&st->l2.t203_timer, 16);
+	discard_i_queue(st);
+	ReleaseWin(&st->l2);
+}
+
+static void
+l2m_debug(struct FsmInst *fi, char *s)
+{
+	struct PStack *st = fi->userdata;
+	char tm[32], str[256];
+
+	jiftime(tm, jiffies);
+	sprintf(str, "%s %s %s\n", tm, st->l2.debug_id, s);
+	HiSax_putstatus(st->l1.hardware, str);
+}
+
+void
+setstack_isdnl2(struct PStack *st, char *debug_id)
+{
+	st->l1.l1l2 = isdnl2_l1l2;
+	st->l3.l3l2 = isdnl2_l3l2;
+	st->ma.manl2 = isdnl2_manl2;
+	st->ma.teil2 = isdnl2_teil2;
+
+	st->l2.uihsize = l2headersize(&st->l2, !0);
+	st->l2.ihsize = l2headersize(&st->l2, 0);
+	skb_queue_head_init(&st->l2.i_queue);
+	InitWin(&st->l2);
+	st->l2.rejexp = 0;
+	st->l2.debug = 0;
+
+	st->l2.l2m.fsm = &l2fsm;
+	st->l2.l2m.state = ST_L2_1;
+	st->l2.l2m.debug = 0;
+	st->l2.l2m.userdata = st;
+	st->l2.l2m.userint = 0;
+	st->l2.l2m.printdebug = l2m_debug;
+	strcpy(st->l2.debug_id, debug_id);
+
+	FsmInitTimer(&st->l2.l2m, &st->l2.t200_timer);
+	FsmInitTimer(&st->l2.l2m, &st->l2.t203_timer);
+	st->l2.t200_running = 0;
+}
+
+void
+setstack_transl2(struct PStack *st)
+{
+}
+
+void
+releasestack_transl2(struct PStack *st)
+{
+}
+
+void
+Isdnl2New(void)
+{
+	l2fsm.state_count = L2_STATE_COUNT;
+	l2fsm.event_count = L2_EVENT_COUNT;
+	l2fsm.strEvent = strL2Event;
+	l2fsm.strState = strL2State;
+	FsmNew(&l2fsm, L2FnList, L2_FN_COUNT);
+}
+
+void
+Isdnl2Free(void)
+{
+	FsmFree(&l2fsm);
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/isdnl2.h linux/drivers/isdn/hisax/isdnl2.h
--- v2.0.30/linux/drivers/isdn/hisax/isdnl2.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/isdnl2.h	Mon Aug  4 17:34:00 1997
@@ -0,0 +1,18 @@
+/* isdnl2.h */
+
+#define RR     0x01
+#define RNR    0x05
+#define REJ    0x09
+#define SABME  0x6f
+#define SABM   0x2f
+#define DM     0x0f
+#define UI     0x03
+#define DISC   0x43
+#define UA     0x63
+#define FRMR   0x87
+#define XID    0xaf
+
+#define CMD    0
+#define RSP    1
+
+#define LC_FLUSH_WAIT 1
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/isdnl3.c linux/drivers/isdn/hisax/isdnl3.c
--- v2.0.30/linux/drivers/isdn/hisax/isdnl3.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/isdnl3.c	Mon Aug  4 17:34:00 1997
@@ -0,0 +1,201 @@
+/* $Id: isdnl3.c,v 1.10 1997/04/06 22:54:16 keil Exp $
+
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *              based on the teles driver from Jan den Ouden
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ * $Log: isdnl3.c,v $
+ * Revision 1.10  1997/04/06 22:54:16  keil
+ * Using SKB's
+ *
+ * Revision 1.9  1997/03/25 23:11:25  keil
+ * US NI-1 protocol
+ *
+ * Revision 1.8  1997/03/21 18:53:44  keil
+ * Report no protocol error to syslog too
+ *
+ * Revision 1.7  1997/03/17 18:34:38  keil
+ * fixed oops if no protocol selected during config
+ *
+ * Revision 1.6  1997/02/16 01:04:08  fritz
+ * Bugfix: Changed timer handling caused hang with 2.1.X
+ *
+ * Revision 1.5  1997/02/09 00:26:27  keil
+ * new interface handling, one interface per card
+ * leased line changes
+ *
+ * Revision 1.4  1997/01/27 23:17:44  keil
+ * delete timers while unloading
+ *
+ * Revision 1.3  1997/01/21 22:31:12  keil
+ * new statemachine; L3 timers
+ *
+ * Revision 1.2  1996/11/05 19:42:04  keil
+ * using config.h
+ *
+ * Revision 1.1  1996/10/13 20:04:54  keil
+ * Initial revision
+ *
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isdnl3.h"
+#include <linux/config.h>
+
+const char *l3_revision = "$Revision: 1.10 $";
+
+void
+l3_debug(struct PStack *st, char *s)
+{
+	char str[256], tm[32];
+
+	jiftime(tm, jiffies);
+	sprintf(str, "%s Channel %d l3 %s\n", tm, st->l3.channr, s);
+	HiSax_putstatus(st->l1.hardware, str);
+}
+
+
+
+void
+newl3state(struct PStack *st, int state)
+{
+	char tmp[80];
+
+	if (st->l3.debug & L3_DEB_STATE) {
+		sprintf(tmp, "newstate  %d --> %d", st->l3.state, state);
+		l3_debug(st, tmp);
+	}
+	st->l3.state = state;
+}
+
+static void
+L3ExpireTimer(struct L3Timer *t)
+{
+	t->st->l4.l4l3(t->st, t->event, NULL);
+}
+
+void
+L3InitTimer(struct PStack *st, struct L3Timer *t)
+{
+	t->st = st;
+	t->tl.function = (void *) L3ExpireTimer;
+	t->tl.data = (long) t;
+	init_timer(&t->tl);
+}
+
+void
+L3DelTimer(struct L3Timer *t)
+{
+	del_timer(&t->tl);
+}
+
+int
+L3AddTimer(struct L3Timer *t,
+	   int millisec, int event)
+{
+	if (t->tl.next || t->tl.prev) {
+		printk(KERN_WARNING "L3AddTimer: timer already active!\n");
+		return -1;
+	}
+	init_timer(&t->tl);
+	t->event = event;
+	t->tl.expires = jiffies + (millisec * HZ) / 1000;
+	add_timer(&t->tl);
+	return 0;
+}
+
+void
+StopAllL3Timer(struct PStack *st)
+{
+	L3DelTimer(&st->l3.timer);
+}
+
+struct sk_buff *
+l3_alloc_skb(int len)
+{
+	struct sk_buff *skb;
+
+	if (!(skb = alloc_skb(len + MAX_HEADER_LEN, GFP_ATOMIC))) {
+		printk(KERN_WARNING "HiSax: No skb for D-channel\n");
+		return (NULL);
+	}
+	SET_SKB_FREE(skb);
+	skb_reserve(skb, MAX_HEADER_LEN);
+	return (skb);
+}
+
+static void
+no_l3_proto(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	l3_debug(st, "no protocol");
+	if (skb)
+		dev_kfree_skb(skb, FREE_READ);
+}
+
+#ifdef	CONFIG_HISAX_EURO
+extern void setstack_dss1(struct PStack *st);
+#endif
+
+#ifdef        CONFIG_HISAX_NI1
+extern void setstack_ni1(struct PStack *st);
+#endif
+
+#ifdef	CONFIG_HISAX_1TR6
+extern void setstack_1tr6(struct PStack *st);
+#endif
+
+void
+setstack_isdnl3(struct PStack *st, struct Channel *chanp)
+{
+	char tmp[64];
+
+	st->l3.debug = L3_DEB_WARN;
+	st->l3.channr = chanp->chan;
+	L3InitTimer(st, &st->l3.timer);
+
+#ifdef	CONFIG_HISAX_EURO
+	if (st->protocol == ISDN_PTYPE_EURO) {
+		setstack_dss1(st);
+	} else
+#endif
+#ifdef        CONFIG_HISAX_NI1
+	if (st->protocol == ISDN_PTYPE_NI1) {
+		setstack_ni1(st);
+	} else
+#endif
+#ifdef	CONFIG_HISAX_1TR6
+	if (st->protocol == ISDN_PTYPE_1TR6) {
+		setstack_1tr6(st);
+	} else
+#endif
+	if (st->protocol == ISDN_PTYPE_LEASED) {
+		st->l4.l4l3 = no_l3_proto;
+		st->l2.l2l3 = no_l3_proto;
+		printk(KERN_NOTICE "HiSax: Leased line mode\n");
+	} else {
+		st->l4.l4l3 = no_l3_proto;
+		st->l2.l2l3 = no_l3_proto;
+		sprintf(tmp, "protocol %s not supported",
+			(st->protocol == ISDN_PTYPE_1TR6) ? "1tr6" :
+			(st->protocol == ISDN_PTYPE_EURO) ? "euro" :
+			(st->protocol == ISDN_PTYPE_NI1) ? "ni1" :
+			"unknown");
+		printk(KERN_WARNING "HiSax: %s\n", tmp);
+		l3_debug(st, tmp);
+		st->protocol = -1;
+	}
+	st->l3.state = 0;
+	st->l3.callref = 0;
+}
+
+void
+releasestack_isdnl3(struct PStack *st)
+{
+	StopAllL3Timer(st);
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/isdnl3.h linux/drivers/isdn/hisax/isdnl3.h
--- v2.0.30/linux/drivers/isdn/hisax/isdnl3.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/isdnl3.h	Mon Aug  4 17:34:00 1997
@@ -0,0 +1,38 @@
+/* $Id: isdnl3.h,v 1.3 1997/04/06 22:54:17 keil Exp $
+ *
+ * $Log: isdnl3.h,v $
+ * Revision 1.3  1997/04/06 22:54:17  keil
+ * Using SKB's
+ *
+ * Revision 1.2  1997/01/21 22:31:28  keil
+ * new statemachine; L3 timers
+ *
+ * Revision 1.1  1996/10/13 20:03:47  keil
+ * Initial revision
+ *
+ *
+ */
+
+#define SBIT(state) (1<<state)
+#define ALL_STATES  0x00ffffff
+
+#define	PROTO_DIS_EURO	0x08
+
+#define L3_DEB_WARN	0x01
+#define	L3_DEB_PROTERR	0x02
+#define	L3_DEB_STATE	0x04
+#define	L3_DEB_CHARGE	0x08
+
+struct stateentry {
+	int	state;
+	u_char	primitive;
+	void	(*rout) (struct PStack *, u_char, void *);
+};
+
+extern void l3_debug(struct PStack *st, char *s);
+extern void newl3state(struct PStack *st, int state);
+extern void L3InitTimer(struct PStack *st, struct L3Timer *t);
+extern void L3DelTimer(struct L3Timer *t);
+extern int  L3AddTimer(struct L3Timer *t, int millisec, int event);
+extern void StopAllL3Timer(struct PStack *st);
+extern struct sk_buff *l3_alloc_skb(int len);
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/ix1_micro.c linux/drivers/isdn/hisax/ix1_micro.c
--- v2.0.30/linux/drivers/isdn/hisax/ix1_micro.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/ix1_micro.c	Tue Aug  5 09:00:12 1997
@@ -0,0 +1,937 @@
+/* $Id: ix1_micro.c,v 1.3 1997/04/13 19:54:02 keil Exp $
+
+ * ix1_micro.c  low level stuff for ITK ix1-micro Rev.2 isdn cards
+ *              derived from the original file teles3.c from Karsten Keil
+ *
+ * Copyright (C) 1997 Klaus-Peter Nischke (ITK AG) (for the modifications to
+ *                                                  the original file teles.c)
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *              Beat Doebeli
+ *
+ * $Log: ix1_micro.c,v $
+ * Revision 1.3  1997/04/13 19:54:02  keil
+ * Change in IRQ check delay for SMP
+ *
+ * Revision 1.2  1997/04/06 22:54:21  keil
+ * Using SKB's
+ *
+ * Revision 1.1  1997/01/27 15:43:10  keil
+ * first version
+ *
+ *
+ */
+
+/*
+   For the modification done by the author the following terms and conditions
+   apply (GNU PUBLIC LICENSE)
+
+
+   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.
+
+
+   You may contact Klaus-Peter Nischke by email: klaus@nischke.do.eunet.de
+   or by conventional mail:
+
+   Klaus-Peter Nischke
+   Deusener Str. 287
+   44369 Dortmund
+   Germany
+ */
+
+
+#define __NO_VERSION__
+#include "siemens.h"
+#include "hisax.h"
+#include "teles3.h"
+#include "isdnl1.h"
+#include <linux/kernel_stat.h>
+
+extern const char *CardType[];
+const char *ix1_revision = "$Revision: 1.3 $";
+
+#define byteout(addr,val) outb_p(val,addr)
+#define bytein(addr) inb_p(addr)
+
+#define SPECIAL_PORT_OFFSET 3
+
+#define ISAC_COMMAND_OFFSET 2
+#define ISAC_DATA_OFFSET 0
+#define HSCX_COMMAND_OFFSET 2
+#define HSCX_DATA_OFFSET 1
+
+#define ISAC_FIFOSIZE 16
+#define HSCX_FIFOSIZE 16
+
+#define TIMEOUT 50
+
+static inline u_char
+IsacReadReg(unsigned int adr, u_char off)
+{
+	byteout(adr + ISAC_COMMAND_OFFSET, off + 0x20);
+	return bytein(adr + ISAC_DATA_OFFSET);
+}
+
+static inline void
+IsacWriteReg(unsigned int adr, u_char off, u_char data)
+{
+	byteout(adr + ISAC_COMMAND_OFFSET, off + 0x20);
+	byteout(adr + ISAC_DATA_OFFSET, data);
+}
+
+#define HSCX_OFFSET(WhichHscx,offset) \
+( (WhichHscx) ? (offset+0x60) : (offset+0x20) )
+
+static inline u_char
+HscxReadReg(unsigned int adr, int WhichHscx, u_char off)
+{
+	byteout(adr + HSCX_COMMAND_OFFSET, HSCX_OFFSET(WhichHscx, off));
+	return bytein(adr + HSCX_DATA_OFFSET);
+}
+
+static inline void
+HscxWriteReg(unsigned int adr, int WhichHscx, u_char off, u_char data)
+{
+	byteout(adr + HSCX_COMMAND_OFFSET, HSCX_OFFSET(WhichHscx, off));
+	byteout(adr + HSCX_DATA_OFFSET, data);
+}
+
+
+static inline void
+IsacReadFifo(unsigned int adr, u_char * data, int size)
+{
+	byteout(adr + ISAC_COMMAND_OFFSET, 0);
+	while (size--)
+		*data++ = bytein(adr + ISAC_DATA_OFFSET);
+}
+
+static void
+IsacWriteFifo(unsigned int adr, u_char * data, int size)
+{
+	byteout(adr + ISAC_COMMAND_OFFSET, 0);
+	while (size--) {
+		byteout(adr + ISAC_DATA_OFFSET, *data);
+		data++;
+	}
+}
+
+static inline void
+HscxReadFifo(unsigned int adr, int WhichHscx, u_char * data, int size)
+{
+	byteout(adr + HSCX_COMMAND_OFFSET, (WhichHscx) ? 0x40 : 0x00);
+	while (size--)
+		*data++ = bytein(adr + HSCX_DATA_OFFSET);
+}
+
+static void
+HscxWriteFifo(unsigned int adr, int WhichHscx, u_char * data, int size)
+{
+	byteout(adr + HSCX_COMMAND_OFFSET, (WhichHscx) ? 0x40 : 0x00);
+	while (size--) {
+		byteout(adr + HSCX_DATA_OFFSET, *data);
+		data++;
+	}
+}
+
+static inline void
+waitforCEC(int adr, int WhichHscx)
+{
+	int to = TIMEOUT;
+
+	while ((HscxReadReg(adr, WhichHscx, HSCX_STAR) & 0x04) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "ix1-Micro: waitforCEC timeout\n");
+}
+
+
+static inline void
+waitforXFW(int adr, int WhichHscx)
+{
+	int to = TIMEOUT;
+
+	while ((!(HscxReadReg(adr, WhichHscx, HSCX_STAR) & 0x44) == 0x40) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "ix1-Micro: waitforXFW timeout\n");
+}
+
+static inline void
+writehscxCMDR(int adr, int WhichHscx, u_char data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	waitforCEC(adr, WhichHscx);
+	HscxWriteReg(adr, WhichHscx, HSCX_CMDR, data);
+	restore_flags(flags);
+}
+
+/*
+ * fast interrupt here
+ */
+
+static void
+hscxreport(struct IsdnCardState *sp, int hscx)
+{
+	printk(KERN_DEBUG "HSCX %d\n", hscx);
+	printk(KERN_DEBUG "ISTA %x\n", HscxReadReg(sp->hscx[hscx], hscx, HSCX_ISTA));
+	printk(KERN_DEBUG "STAR %x\n", HscxReadReg(sp->hscx[hscx], hscx, HSCX_STAR));
+	printk(KERN_DEBUG "EXIR %x\n", HscxReadReg(sp->hscx[hscx], hscx, HSCX_EXIR));
+}
+
+void
+ix1micro_report(struct IsdnCardState *sp)
+{
+	printk(KERN_DEBUG "ISAC\n");
+	printk(KERN_DEBUG "ISTA %x\n", IsacReadReg(sp->isac, ISAC_ISTA));
+	printk(KERN_DEBUG "STAR %x\n", IsacReadReg(sp->isac, ISAC_STAR));
+	printk(KERN_DEBUG "EXIR %x\n", IsacReadReg(sp->isac, ISAC_EXIR));
+	hscxreport(sp, 0);
+	hscxreport(sp, 1);
+}
+
+/*
+ * HSCX stuff goes here
+ */
+
+static void
+hscx_empty_fifo(struct HscxState *hsp, int count)
+{
+	u_char *ptr;
+	struct IsdnCardState *sp = hsp->sp;
+	long flags;
+
+	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
+		debugl1(sp, "hscx_empty_fifo");
+
+	if (hsp->rcvidx + count > HSCX_BUFMAX) {
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "hscx_empty_fifo: incoming packet too large");
+		writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x80);
+		hsp->rcvidx = 0;
+		return;
+	}
+	ptr = hsp->rcvbuf + hsp->rcvidx;
+	hsp->rcvidx += count;
+	save_flags(flags);
+	cli();
+	HscxReadFifo(sp->hscx[hsp->hscx], hsp->hscx, ptr, count);
+	writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x80);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_HSCX_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "hscx_empty_fifo %c cnt %d",
+			     hsp->hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+hscx_fill_fifo(struct HscxState *hsp)
+{
+	struct IsdnCardState *sp = hsp->sp;
+	int more, count;
+	u_char *ptr;
+	long flags;
+
+	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
+		debugl1(sp, "hscx_fill_fifo");
+
+	if (!hsp->tx_skb)
+		return;
+	if (hsp->tx_skb->len <= 0)
+		return;
+
+	more = (hsp->mode == 1) ? 1 : 0;
+	if (hsp->tx_skb->len > 32) {
+		more = !0;
+		count = 32;
+	} else
+		count = hsp->tx_skb->len;
+
+	waitforXFW(sp->hscx[hsp->hscx], hsp->hscx);
+	save_flags(flags);
+	cli();
+	ptr = hsp->tx_skb->data;
+	skb_pull(hsp->tx_skb, count);
+	hsp->tx_cnt -= count;
+	hsp->count += count;
+	HscxWriteFifo(sp->hscx[hsp->hscx], hsp->hscx, ptr, count);
+	writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, more ? 0x8 : 0xa);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_HSCX_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "hscx_fill_fifo %c cnt %d",
+			     hsp->hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static inline void
+hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx)
+{
+	u_char r;
+	struct HscxState *hsp = sp->hs + hscx;
+	struct sk_buff *skb;
+	int count;
+	char tmp[32];
+
+	if (!hsp->init)
+		return;
+
+	if (val & 0x80) {	/* RME */
+
+		r = HscxReadReg(sp->hscx[hsp->hscx], hscx, HSCX_RSTA);
+		if ((r & 0xf0) != 0xa0) {
+			if (!(r & 0x80))
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "HSCX invalid frame");
+			if ((r & 0x40) && hsp->mode)
+				if (sp->debug & L1_DEB_WARN) {
+					sprintf(tmp, "HSCX RDO mode=%d",
+						hsp->mode);
+					debugl1(sp, tmp);
+				}
+			if (!(r & 0x20))
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "HSCX CRC error");
+			writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x80);
+		} else {
+			count = HscxReadReg(sp->hscx[hsp->hscx], hscx, HSCX_RBCL) & 0x1f;
+			if (count == 0)
+				count = 32;
+			hscx_empty_fifo(hsp, count);
+			if ((count = hsp->rcvidx - 1) > 0) {
+				if (sp->debug & L1_DEB_HSCX_FIFO) {
+					sprintf(tmp, "HX Frame %d", count);
+					debugl1(sp, tmp);
+				}
+				if (!(skb = dev_alloc_skb(count)))
+					printk(KERN_WARNING "IX1: receive out of memory\n");
+				else {
+					memcpy(skb_put(skb, count), hsp->rcvbuf, count);
+					skb_queue_tail(&hsp->rqueue, skb);
+				}
+			}
+		}
+		hsp->rcvidx = 0;
+		hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		hscx_empty_fifo(hsp, 32);
+		if (hsp->mode == 1) {
+			/* receive audio data */
+			if (!(skb = dev_alloc_skb(32)))
+				printk(KERN_WARNING "IX1: receive out of memory\n");
+			else {
+				memcpy(skb_put(skb, 32), hsp->rcvbuf, 32);
+				skb_queue_tail(&hsp->rqueue, skb);
+			}
+			hsp->rcvidx = 0;
+			hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+		}
+	}
+	if (val & 0x10) {	/* XPR */
+		if (hsp->tx_skb)
+			if (hsp->tx_skb->len) {
+				hscx_fill_fifo(hsp);
+				return;
+			} else {
+				dev_kfree_skb(hsp->tx_skb, FREE_WRITE);
+				hsp->count = 0;
+				if (hsp->st->l4.l1writewakeup)
+					hsp->st->l4.l1writewakeup(hsp->st);
+				hsp->tx_skb = NULL;
+			}
+		if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) {
+			hsp->count = 0;
+			hscx_fill_fifo(hsp);
+		} else
+			hscx_sched_event(hsp, HSCX_XMTBUFREADY);
+	}
+}
+
+/*
+ * ISAC stuff goes here
+ */
+
+static void
+isac_empty_fifo(struct IsdnCardState *sp, int count)
+{
+	u_char *ptr;
+	long flags;
+
+	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
+		if (sp->debug & L1_DEB_ISAC)
+			debugl1(sp, "isac_empty_fifo");
+
+	if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) {
+		if (sp->debug & L1_DEB_WARN) {
+			char tmp[40];
+			sprintf(tmp, "isac_empty_fifo overrun %d",
+				sp->rcvidx + count);
+			debugl1(sp, tmp);
+		}
+		IsacWriteReg(sp->isac, ISAC_CMDR, 0x80);
+		sp->rcvidx = 0;
+		return;
+	}
+	ptr = sp->rcvbuf + sp->rcvidx;
+	sp->rcvidx += count;
+	save_flags(flags);
+	cli();
+	IsacReadFifo(sp->isac, ptr, count);
+	IsacWriteReg(sp->isac, ISAC_CMDR, 0x80);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_ISAC_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "isac_empty_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+isac_fill_fifo(struct IsdnCardState *sp)
+{
+	int count, more;
+	u_char *ptr;
+	long flags;
+
+	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
+		debugl1(sp, "isac_fill_fifo");
+
+	if (!sp->tx_skb)
+		return;
+
+	count = sp->tx_skb->len;
+	if (count <= 0)
+		return;
+
+	more = 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	save_flags(flags);
+	cli();
+	ptr = sp->tx_skb->data;
+	skb_pull(sp->tx_skb, count);
+	sp->tx_cnt += count;
+	IsacWriteFifo(sp->isac, ptr, count);
+	IsacWriteReg(sp->isac, ISAC_CMDR, more ? 0x8 : 0xa);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_ISAC_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "isac_fill_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+ph_command(struct IsdnCardState *sp, unsigned int command)
+{
+	if (sp->debug & L1_DEB_ISAC) {
+		char tmp[32];
+		sprintf(tmp, "ph_command %d", command);
+		debugl1(sp, tmp);
+	}
+	IsacWriteReg(sp->isac, ISAC_CIX0, (command << 2) | 3);
+}
+
+
+static inline void
+isac_interrupt(struct IsdnCardState *sp, u_char val)
+{
+	u_char exval;
+	struct sk_buff *skb;
+	unsigned int count;
+	char tmp[32];
+
+	if (sp->debug & L1_DEB_ISAC) {
+		sprintf(tmp, "ISAC interrupt %x", val);
+		debugl1(sp, tmp);
+	}
+	if (val & 0x80) {	/* RME */
+		exval = IsacReadReg(sp->isac, ISAC_RSTA);
+		if ((exval & 0x70) != 0x20) {
+			if (exval & 0x40)
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "ISAC RDO");
+			if (!(exval & 0x20))
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "ISAC CRC error");
+			IsacWriteReg(sp->isac, ISAC_CMDR, 0x80);
+		} else {
+			count = IsacReadReg(sp->isac, ISAC_RBCL) & 0x1f;
+			if (count == 0)
+				count = 32;
+			isac_empty_fifo(sp, count);
+			if ((count = sp->rcvidx) > 0) {
+				sp->rcvidx = 0;
+				if (!(skb = alloc_skb(count, GFP_ATOMIC)))
+					printk(KERN_WARNING "IX1: D receive out of memory\n");
+				else {
+					SET_SKB_FREE(skb);
+					memcpy(skb_put(skb, count), sp->rcvbuf, count);
+					skb_queue_tail(&sp->rq, skb);
+				}
+			}
+		}
+		sp->rcvidx = 0;
+		isac_sched_event(sp, ISAC_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		isac_empty_fifo(sp, 32);
+	}
+	if (val & 0x20) {	/* RSC */
+		/* never */
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "ISAC RSC interrupt");
+	}
+	if (val & 0x10) {	/* XPR */
+		if (sp->tx_skb)
+			if (sp->tx_skb->len) {
+				isac_fill_fifo(sp);
+				goto afterXPR;
+			} else {
+				dev_kfree_skb(sp->tx_skb, FREE_WRITE);
+				sp->tx_cnt = 0;
+				sp->tx_skb = NULL;
+			}
+		if ((sp->tx_skb = skb_dequeue(&sp->sq))) {
+			sp->tx_cnt = 0;
+			isac_fill_fifo(sp);
+		} else
+			isac_sched_event(sp, ISAC_XMTBUFREADY);
+	}
+      afterXPR:
+	if (val & 0x04) {	/* CISQ */
+		sp->ph_state = (IsacReadReg(sp->isac, ISAC_CIX0) >> 2)
+		    & 0xf;
+		if (sp->debug & L1_DEB_ISAC) {
+			sprintf(tmp, "l1state %d", sp->ph_state);
+			debugl1(sp, tmp);
+		}
+		isac_new_ph(sp);
+	}
+	if (val & 0x02) {	/* SIN */
+		/* never */
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "ISAC SIN interrupt");
+	}
+	if (val & 0x01) {	/* EXI */
+		exval = IsacReadReg(sp->isac, ISAC_EXIR);
+		if (sp->debug & L1_DEB_WARN) {
+			sprintf(tmp, "ISAC EXIR %02x", exval);
+			debugl1(sp, tmp);
+		}
+	}
+}
+
+static inline void
+hscx_int_main(struct IsdnCardState *sp, u_char val)
+{
+
+	u_char exval;
+	struct HscxState *hsp;
+	char tmp[32];
+
+
+	if (val & 0x01) {
+		hsp = sp->hs + 1;
+		exval = HscxReadReg(sp->hscx[1], 1, HSCX_EXIR);
+		if (exval == 0x40) {
+			if (hsp->mode == 1)
+				hscx_fill_fifo(hsp);
+			else {
+				/* Here we lost an TX interrupt, so
+				   * restart transmitting the whole frame.
+				 */
+				if (hsp->tx_skb) {
+					skb_push(hsp->tx_skb, hsp->count);
+					hsp->tx_cnt += hsp->count;
+					hsp->count = 0;
+				}
+				writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x01);
+				if (sp->debug & L1_DEB_WARN) {
+					sprintf(tmp, "HSCX B EXIR %x Lost TX", exval);
+					debugl1(sp, tmp);
+				}
+			}
+		} else if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX B EXIR %x", exval);
+			debugl1(sp, tmp);
+		}
+	}
+	if (val & 0xf8) {
+		if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX B interrupt %x", val);
+			debugl1(sp, tmp);
+		}
+		hscx_interrupt(sp, val, 1);
+	}
+	if (val & 0x02) {
+		hsp = sp->hs;
+		exval = HscxReadReg(sp->hscx[0], 0, HSCX_EXIR);
+		if (exval == 0x40) {
+			if (hsp->mode == 1)
+				hscx_fill_fifo(hsp);
+			else {
+				/* Here we lost an TX interrupt, so
+				   * restart transmitting the whole frame.
+				 */
+				if (hsp->tx_skb) {
+					skb_push(hsp->tx_skb, hsp->count);
+					hsp->tx_cnt += hsp->count;
+					hsp->count = 0;
+				}
+				writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x01);
+				if (sp->debug & L1_DEB_WARN) {
+					sprintf(tmp, "HSCX A EXIR %x Lost TX", exval);
+					debugl1(sp, tmp);
+				}
+			}
+		} else if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX A EXIR %x", exval);
+			debugl1(sp, tmp);
+		}
+	}
+	if (val & 0x04) {
+		exval = HscxReadReg(sp->hscx[0], 0, HSCX_ISTA);
+		if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX A interrupt %x", exval);
+			debugl1(sp, tmp);
+		}
+		hscx_interrupt(sp, exval, 0);
+	}
+}
+
+static void
+ix1micro_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *sp;
+	u_char val, stat = 0;
+
+	sp = (struct IsdnCardState *) irq2dev_map[intno];
+
+	if (!sp) {
+		printk(KERN_WARNING "Teles: Spurious interrupt!\n");
+		return;
+	}
+	val = HscxReadReg(sp->hscx[1], 1, HSCX_ISTA);
+      Start_HSCX:
+	if (val) {
+		hscx_int_main(sp, val);
+		stat |= 1;
+	}
+	val = IsacReadReg(sp->isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val) {
+		isac_interrupt(sp, val);
+		stat |= 2;
+	}
+	val = HscxReadReg(sp->hscx[1], 1, HSCX_ISTA);
+	if (val) {
+		if (sp->debug & L1_DEB_HSCX)
+			debugl1(sp, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = IsacReadReg(sp->isac, ISAC_ISTA);
+	if (val) {
+		if (sp->debug & L1_DEB_ISAC)
+			debugl1(sp, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (stat & 1) {
+		HscxWriteReg(sp->hscx[0], 0, HSCX_MASK, 0xFF);
+		HscxWriteReg(sp->hscx[1], 1, HSCX_MASK, 0xFF);
+		HscxWriteReg(sp->hscx[0], 0, HSCX_MASK, 0x0);
+		HscxWriteReg(sp->hscx[1], 1, HSCX_MASK, 0x0);
+	}
+	if (stat & 2) {
+		IsacWriteReg(sp->isac, ISAC_MASK, 0xFF);
+		IsacWriteReg(sp->isac, ISAC_MASK, 0x0);
+	}
+}
+
+
+static void
+initisac(struct IsdnCardState *sp)
+{
+	unsigned int adr = sp->isac;
+
+	/* 16.3 IOM 2 Mode */
+	IsacWriteReg(adr, ISAC_MASK, 0xff);
+	IsacWriteReg(adr, ISAC_ADF2, 0x80);
+	IsacWriteReg(adr, ISAC_SQXR, 0x2f);
+	IsacWriteReg(adr, ISAC_SPCR, 0x0);
+	IsacWriteReg(adr, ISAC_ADF1, 0x2);
+	IsacWriteReg(adr, ISAC_STCR, 0x70);
+	IsacWriteReg(adr, ISAC_MODE, 0xc9);
+	IsacWriteReg(adr, ISAC_TIMR, 0x0);
+	IsacWriteReg(adr, ISAC_ADF1, 0x0);
+	IsacWriteReg(adr, ISAC_CMDR, 0x41);
+	IsacWriteReg(adr, ISAC_CIX0, (1 << 2) | 3);
+	IsacWriteReg(adr, ISAC_MASK, 0xff);
+	IsacWriteReg(adr, ISAC_MASK, 0x0);
+}
+
+static void
+modehscx(struct HscxState *hs, int mode, int ichan)
+{
+	struct IsdnCardState *sp = hs->sp;
+	int hscx = hs->hscx;
+
+	if (sp->debug & L1_DEB_HSCX) {
+		char tmp[40];
+		sprintf(tmp, "hscx %c mode %d ichan %d",
+			'A' + hscx, mode, ichan);
+		debugl1(sp, tmp);
+	}
+	hs->mode = mode;
+	HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR1, 0x85);
+	HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XAD1, 0xFF);
+	HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XAD2, 0xFF);
+	HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RAH2, 0xFF);
+	HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XBCH, 0x0);
+	HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RLCR, 0x0);
+
+	switch (mode) {
+		case 0:
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30);
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0xff);
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0xff);
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7);
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7);
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_MODE, 0x84);
+			break;
+		case 1:
+			if (ichan == 0) {
+				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30);
+				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0x2f);
+				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0x2f);
+				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7);
+				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7);
+			} else {
+				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30);
+				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0x3);
+				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0x3);
+				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7);
+				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7);
+			}
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_MODE, 0xe4);
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CMDR, 0x41);
+			break;
+		case 2:
+			if (ichan == 0) {
+				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30);
+				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0x2f);
+				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0x2f);
+				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7);
+				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7);
+			} else {
+				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30);
+				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0x3);
+				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0x3);
+				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7);
+				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7);
+			}
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_MODE, 0x8c);
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CMDR, 0x41);
+			break;
+	}
+	HscxWriteReg(sp->hscx[hscx], hscx, HSCX_ISTA, 0x00);
+}
+
+void
+release_io_ix1micro(struct IsdnCard *card)
+{
+	if (card->sp->cfg_reg)
+		release_region(card->sp->cfg_reg, 4);
+}
+
+static void
+clear_pending_ints(struct IsdnCardState *sp)
+{
+	int val;
+	char tmp[64];
+
+	val = HscxReadReg(sp->hscx[1], 1, HSCX_ISTA);
+	sprintf(tmp, "HSCX B ISTA %x", val);
+	debugl1(sp, tmp);
+	if (val & 0x01) {
+		val = HscxReadReg(sp->hscx[1], 1, HSCX_EXIR);
+		sprintf(tmp, "HSCX B EXIR %x", val);
+		debugl1(sp, tmp);
+	} else if (val & 0x02) {
+		val = HscxReadReg(sp->hscx[0], 0, HSCX_EXIR);
+		sprintf(tmp, "HSCX A EXIR %x", val);
+		debugl1(sp, tmp);
+	}
+	val = HscxReadReg(sp->hscx[0], 0, HSCX_ISTA);
+	sprintf(tmp, "HSCX A ISTA %x", val);
+	debugl1(sp, tmp);
+	val = HscxReadReg(sp->hscx[1], 1, HSCX_STAR);
+	sprintf(tmp, "HSCX B STAR %x", val);
+	debugl1(sp, tmp);
+	val = HscxReadReg(sp->hscx[0], 0, HSCX_STAR);
+	sprintf(tmp, "HSCX A STAR %x", val);
+	debugl1(sp, tmp);
+	val = IsacReadReg(sp->isac, ISAC_STAR);
+	sprintf(tmp, "ISAC STAR %x", val);
+	debugl1(sp, tmp);
+	val = IsacReadReg(sp->isac, ISAC_MODE);
+	sprintf(tmp, "ISAC MODE %x", val);
+	debugl1(sp, tmp);
+	val = IsacReadReg(sp->isac, ISAC_ADF2);
+	sprintf(tmp, "ISAC ADF2 %x", val);
+	debugl1(sp, tmp);
+	val = IsacReadReg(sp->isac, ISAC_ISTA);
+	sprintf(tmp, "ISAC ISTA %x", val);
+	debugl1(sp, tmp);
+	if (val & 0x01) {
+		val = IsacReadReg(sp->isac, ISAC_EXIR);
+		sprintf(tmp, "ISAC EXIR %x", val);
+		debugl1(sp, tmp);
+	} else if (val & 0x04) {
+		val = IsacReadReg(sp->isac, ISAC_CIR0);
+		sprintf(tmp, "ISAC CIR0 %x", val);
+		debugl1(sp, tmp);
+	}
+	IsacWriteReg(sp->isac, ISAC_MASK, 0);
+	IsacWriteReg(sp->isac, ISAC_CMDR, 0x41);
+}
+
+int
+initix1micro(struct IsdnCardState *sp)
+{
+	int ret;
+	int loop = 0;
+	char tmp[40];
+
+	sp->counter = kstat.interrupts[sp->irq];
+	sprintf(tmp, "IRQ %d count %d", sp->irq, sp->counter);
+	debugl1(sp, tmp);
+	clear_pending_ints(sp);
+	ret = get_irq(sp->cardnr, &ix1micro_interrupt);
+	if (ret) {
+		initisac(sp);
+		sp->modehscx(sp->hs, 0, 0);
+		sp->modehscx(sp->hs + 1, 0, 0);
+		while (loop++ < 10) {
+			/* At least 1-3 irqs must happen
+			 * (one from HSCX A, one from HSCX B, 3rd from ISAC)
+			 */
+			if (kstat.interrupts[sp->irq] > sp->counter)
+				break;
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + 1;
+			schedule();
+		}
+		sprintf(tmp, "IRQ %d count %d", sp->irq,
+			kstat.interrupts[sp->irq]);
+		debugl1(sp, tmp);
+		if (kstat.interrupts[sp->irq] == sp->counter) {
+			printk(KERN_WARNING
+			       "ix1-Micro: IRQ(%d) getting no interrupts during init\n",
+			       sp->irq);
+			irq2dev_map[sp->irq] = NULL;
+			free_irq(sp->irq, NULL);
+			return (0);
+		}
+	}
+	return (ret);
+}
+
+int
+setup_ix1micro(struct IsdnCard *card)
+{
+	u_char val, verA, verB;
+	struct IsdnCardState *sp = card->sp;
+	long flags;
+	char tmp[64];
+
+	strcpy(tmp, ix1_revision);
+	printk(KERN_NOTICE "HiSax: ITK IX1 driver Rev. %s\n", HiSax_getrev(tmp));
+	if (sp->typ != ISDN_CTYPE_IX1MICROR2)
+		return (0);
+
+	/* IO-Ports */
+	sp->isac = sp->hscx[0] = sp->hscx[1] = sp->cfg_reg = card->para[1];
+	sp->irq = card->para[0];
+	if (sp->cfg_reg) {
+		if (check_region((sp->cfg_reg), 4)) {
+			printk(KERN_WARNING
+			  "HiSax: %s config port %x-%x already in use\n",
+			       CardType[card->typ],
+			       sp->cfg_reg,
+			       sp->cfg_reg + 4);
+			return (0);
+		} else
+			request_region(sp->cfg_reg, 4, "ix1micro cfg");
+	}
+	/* reset isac */
+	save_flags(flags);
+	val = 3 * (HZ / 10) + 1;
+	sti();
+	while (val--) {
+		byteout(sp->cfg_reg + SPECIAL_PORT_OFFSET, 1);
+		HZDELAY(1);	/* wait >=10 ms */
+	}
+	byteout(sp->cfg_reg + SPECIAL_PORT_OFFSET, 0);
+	restore_flags(flags);
+
+	printk(KERN_NOTICE
+	       "HiSax: %s config irq:%d io:0x%x\n",
+	       CardType[sp->typ], sp->irq,
+	       sp->cfg_reg);
+	verA = HscxReadReg(sp->hscx[0], 0, HSCX_VSTR) & 0xf;
+	verB = HscxReadReg(sp->hscx[1], 1, HSCX_VSTR) & 0xf;
+	printk(KERN_INFO "ix1-Micro: HSCX version A: %s  B: %s\n",
+	       HscxVersion(verA), HscxVersion(verB));
+	val = IsacReadReg(sp->isac, ISAC_RBCH);
+	printk(KERN_INFO "ix1-Micro: ISAC %s\n",
+	       ISACVersion(val));
+	if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) {
+		printk(KERN_WARNING
+		    "ix1-Micro: wrong HSCX versions check IO address\n");
+		release_io_ix1micro(card);
+		return (0);
+	}
+	sp->modehscx = &modehscx;
+	sp->ph_command = &ph_command;
+	sp->hscx_fill_fifo = &hscx_fill_fifo;
+	sp->isac_fill_fifo = &isac_fill_fifo;
+	return (1);
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/ix1_micro.h linux/drivers/isdn/hisax/ix1_micro.h
--- v2.0.30/linux/drivers/isdn/hisax/ix1_micro.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/ix1_micro.h	Mon Aug  4 17:34:00 1997
@@ -0,0 +1,50 @@
+/* $Id: ix1_micro.h,v 1.1 1997/01/27 15:42:48 keil Exp $
+
+ * ix1_micro.h  low level stuff for ITK ix1-micro Rev.2 isdn cards
+ *
+ *              derived from teles3.h from Karsten Keil
+ *
+ * Copyright (C) 1997 Klaus-Peter Nischke (ITK AG)   (for the modifications
+ to the original file teles.c)
+ *
+ * $Log: ix1_micro.h,v $
+ * Revision 1.1  1997/01/27 15:42:48  keil
+ * first version
+ *
+ *
+ */
+
+/*
+   For the modification done by the author the following terms and conditions
+   apply (GNU PUBLIC LICENSE)
+
+
+   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.
+
+
+   You may contact Klaus-Peter Nischke by email: klaus@nischke.do.eunet.de
+   or by conventional mail:
+
+   Klaus-Peter Nischke
+   Deusener Str. 287
+   44369 Dortmund
+   Germany
+ */
+
+
+extern void ix1micro_report(struct IsdnCardState *sp);
+extern void release_io_ix1micro(struct IsdnCard *card);
+extern int setup_ix1micro(struct IsdnCard *card);
+extern int initix1micro(struct IsdnCardState *sp);
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/l3_1tr6.c linux/drivers/isdn/hisax/l3_1tr6.c
--- v2.0.30/linux/drivers/isdn/hisax/l3_1tr6.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/l3_1tr6.c	Mon Aug  4 17:34:00 1997
@@ -0,0 +1,796 @@
+/* $Id: l3_1tr6.c,v 1.11 1997/04/06 22:54:18 keil Exp $
+
+ *  German 1TR6 D-channel protocol
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: l3_1tr6.c,v $
+ * Revision 1.11  1997/04/06 22:54:18  keil
+ * Using SKB's
+ *
+ * Revision 1.10  1997/03/13 20:37:58  keil
+ * channel request added
+ *
+ * Revision 1.9  1997/02/11 01:37:40  keil
+ * Changed setup-interface (incoming and outgoing)
+ *
+ * Revision 1.8  1997/01/27 23:20:21  keil
+ * report revision only ones
+ *
+ * Revision 1.7  1997/01/21 22:30:07  keil
+ * new statemachine; L3 timers
+ *
+ * Revision 1.6  1996/12/14 21:07:20  keil
+ * additional states for CC_REJECT
+ *
+ * Revision 1.5  1996/12/08 19:55:17  keil
+ * change CC_REJECT_REQ routine
+ *
+ * Revision 1.4  1996/10/30 10:18:01  keil
+ * bugfixes in debugging output
+ *
+ * Revision 1.3  1996/10/27 22:15:37  keil
+ * bugfix reject handling
+ *
+ * Revision 1.2  1996/10/13 23:08:56  keil
+ * added missing state for callback reject
+ *
+ * Revision 1.1  1996/10/13 20:04:55  keil
+ * Initial revision
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "l3_1tr6.h"
+#include "isdnl3.h"
+#include <linux/ctype.h>
+
+extern char *HiSax_getrev(const char *revision);
+const char *l3_1tr6_revision = "$Revision: 1.11 $";
+
+#define MsgHead(ptr, cref, mty, dis) \
+	*ptr++ = dis; \
+	*ptr++ = 0x1; \
+	*ptr++ = cref; \
+	*ptr++ = mty
+
+static void
+l3_1TR6_message(struct PStack *st, u_char mt, u_char pd)
+{
+	struct sk_buff *skb;
+	u_char *p;
+
+	if (!(skb = l3_alloc_skb(4)))
+		return;
+	p = skb_put(skb, 4);
+	MsgHead(p, st->l3.callref, mt, pd);
+	st->l3.l3l2(st, DL_DATA, skb);
+}
+
+static void
+l3_1tr6_setup_req(struct PStack *st, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[128];
+	u_char *p = tmp;
+	u_char *teln;
+	u_char *eaz;
+	u_char channel = 0;
+	int l;
+
+
+	st->l3.callref = st->pa->callref;
+	MsgHead(p, st->l3.callref, MT_N1_SETUP, PROTO_DIS_N1);
+
+	teln = st->pa->setup.phone;
+	st->pa->spv = 0;
+	if (!isdigit(*teln)) {
+		switch (0x5f & *teln) {
+			case 'S':
+				st->pa->spv = 1;
+				break;
+			case 'C':
+				channel = 0x08;
+			case 'P':
+				channel |= 0x80;
+				teln++;
+				if (*teln == '1')
+					channel |= 0x01;
+				else
+					channel |= 0x02;
+				break;
+			default:
+				if (st->l3.debug & L3_DEB_WARN)
+					l3_debug(st, "Wrong MSN Code");
+				break;
+		}
+		teln++;
+	}
+	if (channel) {
+		*p++ = 0x18;	/* channel indicator */
+		*p++ = 1;
+		*p++ = channel;
+	}
+	if (st->pa->spv) {	/* SPV ? */
+		/* NSF SPV */
+		*p++ = WE0_netSpecFac;
+		*p++ = 4;	/* Laenge */
+		*p++ = 0;
+		*p++ = FAC_SPV;	/* SPV */
+		*p++ = st->pa->setup.si1;	/* 0 for all Services */
+		*p++ = st->pa->setup.si2;	/* 0 for all Services */
+		*p++ = WE0_netSpecFac;
+		*p++ = 4;	/* Laenge */
+		*p++ = 0;
+		*p++ = FAC_Activate;	/* aktiviere SPV (default) */
+		*p++ = st->pa->setup.si1;	/* 0 for all Services */
+		*p++ = st->pa->setup.si2;	/* 0 for all Services */
+	}
+	eaz = st->pa->setup.eazmsn;
+	if (*eaz) {
+		*p++ = WE0_origAddr;
+		*p++ = strlen(eaz) + 1;
+		/* Classify as AnyPref. */
+		*p++ = 0x81;	/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+		while (*eaz)
+			*p++ = *eaz++ & 0x7f;
+	}
+	*p++ = WE0_destAddr;
+	*p++ = strlen(teln) + 1;
+	/* Classify as AnyPref. */
+	*p++ = 0x81;		/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+	while (*teln)
+		*p++ = *teln++ & 0x7f;
+
+	*p++ = WE_Shift_F6;
+	/* Codesatz 6 fuer Service */
+	*p++ = WE6_serviceInd;
+	*p++ = 2;		/* len=2 info,info2 */
+	*p++ = st->pa->setup.si1;
+	*p++ = st->pa->setup.si2;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	L3DelTimer(&st->l3.timer);
+	L3AddTimer(&st->l3.timer, st->l3.t303, CC_T303);
+	newl3state(st, 1);
+	st->l3.l3l2(st, DL_DATA, skb);
+
+}
+
+static void
+l3_1tr6_setup(struct PStack *st, u_char pr, void *arg)
+{
+	u_char *p;
+	int bcfound = 0;
+	char tmp[80];
+	struct sk_buff *skb = arg;
+
+	p = skb->data;
+	st->pa->callref = getcallref(p);
+	st->l3.callref = 0x80 + st->pa->callref;
+
+	/* Channel Identification */
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE0_chanID, 0))) {
+		st->pa->bchannel = p[2] & 0x3;
+		bcfound++;
+	} else if (st->l3.debug & L3_DEB_WARN)
+		l3_debug(st, "setup without bchannel");
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE6_serviceInd, 6))) {
+		st->pa->setup.si1 = p[2];
+		st->pa->setup.si2 = p[3];
+	} else if (st->l3.debug & L3_DEB_WARN)
+		l3_debug(st, "setup without service indicator");
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE0_destAddr, 0)))
+		iecpy(st->pa->setup.eazmsn, p, 1);
+	else
+		st->pa->setup.eazmsn[0] = 0;
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE0_origAddr, 0))) {
+		iecpy(st->pa->setup.phone, p, 1);
+	} else
+		st->pa->setup.phone[0] = 0;
+
+	p = skb->data;
+	st->pa->spv = 0;
+	if ((p = findie(p, skb->len, WE0_netSpecFac, 0))) {
+		if ((FAC_SPV == p[3]) || (FAC_Activate == p[3]))
+			st->pa->spv = 1;
+	}
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+
+	/* Signal all services, linklevel takes care of Service-Indicator */
+	if (bcfound) {
+		if ((st->pa->setup.si1 != 7) && (st->l3.debug & L3_DEB_WARN)) {
+			sprintf(tmp, "non-digital call: %s -> %s",
+				st->pa->setup.phone,
+				st->pa->setup.eazmsn);
+			l3_debug(st, tmp);
+		}
+		newl3state(st, 6);
+		st->l3.l3l4(st, CC_SETUP_IND, NULL);
+	}
+}
+
+static void
+l3_1tr6_setup_ack(struct PStack *st, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+
+	L3DelTimer(&st->l3.timer);
+	p = skb->data;
+	newl3state(st, 2);
+	if ((p = findie(p, skb->len, WE0_chanID, 0))) {
+		st->pa->bchannel = p[2] & 0x3;
+	} else if (st->l3.debug & L3_DEB_WARN)
+		l3_debug(st, "setup answer without bchannel");
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+	L3AddTimer(&st->l3.timer, st->l3.t304, CC_T304);
+	st->l3.l3l4(st, CC_MORE_INFO, NULL);
+}
+
+static void
+l3_1tr6_call_sent(struct PStack *st, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+
+	L3DelTimer(&st->l3.timer);
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE0_chanID, 0))) {
+		st->pa->bchannel = p[2] & 0x3;
+	} else if (st->l3.debug & L3_DEB_WARN)
+		l3_debug(st, "setup answer without bchannel");
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+	L3AddTimer(&st->l3.timer, st->l3.t310, CC_T310);
+	newl3state(st, 3);
+	st->l3.l3l4(st, CC_PROCEEDING_IND, NULL);
+}
+
+static void
+l3_1tr6_alert(struct PStack *st, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+	L3DelTimer(&st->l3.timer);	/* T304 */
+	newl3state(st, 4);
+	st->l3.l3l4(st, CC_ALERTING_IND, NULL);
+}
+
+static void
+l3_1tr6_info(struct PStack *st, u_char pr, void *arg)
+{
+	u_char *p;
+	int i, tmpcharge = 0;
+	char a_charge[8], tmp[32];
+	struct sk_buff *skb = arg;
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE6_chargingInfo, 6))) {
+		iecpy(a_charge, p, 1);
+		for (i = 0; i < strlen(a_charge); i++) {
+			tmpcharge *= 10;
+			tmpcharge += a_charge[i] & 0xf;
+		}
+		if (tmpcharge > st->pa->chargeinfo) {
+			st->pa->chargeinfo = tmpcharge;
+			st->l3.l3l4(st, CC_INFO_CHARGE, NULL);
+		}
+		if (st->l3.debug & L3_DEB_CHARGE) {
+			sprintf(tmp, "charging info %d", st->pa->chargeinfo);
+			l3_debug(st, tmp);
+		}
+	} else if (st->l3.debug & L3_DEB_CHARGE)
+		l3_debug(st, "charging info not found");
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+
+}
+
+static void
+l3_1tr6_info_s2(struct PStack *st, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+}
+
+static void
+l3_1tr6_connect(struct PStack *st, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	L3DelTimer(&st->l3.timer);	/* T310 */
+	newl3state(st, 10);
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+	st->pa->chargeinfo = 0;
+	st->l3.l3l4(st, CC_SETUP_CNF, NULL);
+}
+
+static void
+l3_1tr6_rel(struct PStack *st, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	u_char *p;
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE0_cause, 0))) {
+		if (p[1] > 0) {
+			st->pa->cause = p[2];
+			if (p[1] > 1)
+				st->pa->loc = p[3];
+			else
+				st->pa->loc = 0;
+		} else {
+			st->pa->cause = 0;
+			st->pa->loc = 0;
+		}
+	} else
+		st->pa->cause = -1;
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+	StopAllL3Timer(st);
+	newl3state(st, 0);
+	l3_1TR6_message(st, MT_N1_REL_ACK, PROTO_DIS_N1);
+	st->l3.l3l4(st, CC_RELEASE_IND, NULL);
+}
+
+static void
+l3_1tr6_rel_ack(struct PStack *st, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+	StopAllL3Timer(st);
+	newl3state(st, 0);
+	st->pa->cause = -1;
+	st->l3.l3l4(st, CC_RELEASE_CNF, NULL);
+}
+
+static void
+l3_1tr6_disc(struct PStack *st, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	u_char *p;
+	int i, tmpcharge = 0;
+	char a_charge[8], tmp[32];
+
+	StopAllL3Timer(st);
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE6_chargingInfo, 6))) {
+		iecpy(a_charge, p, 1);
+		for (i = 0; i < strlen(a_charge); i++) {
+			tmpcharge *= 10;
+			tmpcharge += a_charge[i] & 0xf;
+		}
+		if (tmpcharge > st->pa->chargeinfo) {
+			st->pa->chargeinfo = tmpcharge;
+			st->l3.l3l4(st, CC_INFO_CHARGE, NULL);
+		}
+		if (st->l3.debug & L3_DEB_CHARGE) {
+			sprintf(tmp, "charging info %d", st->pa->chargeinfo);
+			l3_debug(st, tmp);
+		}
+	} else if (st->l3.debug & L3_DEB_CHARGE)
+		l3_debug(st, "charging info not found");
+
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE0_cause, 0))) {
+		if (p[1] > 0) {
+			st->pa->cause = p[2];
+			if (p[1] > 1)
+				st->pa->loc = p[3];
+			else
+				st->pa->loc = 0;
+		} else {
+			st->pa->cause = 0;
+			st->pa->loc = 0;
+		}
+	} else {
+		if (st->l3.debug & L3_DEB_WARN)
+			l3_debug(st, "cause not found");
+		st->pa->cause = -1;
+	}
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+	newl3state(st, 12);
+	st->l3.l3l4(st, CC_DISCONNECT_IND, NULL);
+}
+
+
+static void
+l3_1tr6_connect_ack(struct PStack *st, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+	newl3state(st, 10);
+	st->pa->chargeinfo = 0;
+	L3DelTimer(&st->l3.timer);
+	st->l3.l3l4(st, CC_SETUP_COMPLETE_IND, NULL);
+}
+
+static void
+l3_1tr6_alert_req(struct PStack *st, u_char pr, void *arg)
+{
+	newl3state(st, 7);
+	l3_1TR6_message(st, MT_N1_ALERT, PROTO_DIS_N1);
+}
+
+static void
+l3_1tr6_setup_rsp(struct PStack *st, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[24];
+	u_char *p = tmp;
+	int l;
+
+	MsgHead(p, st->l3.callref, MT_N1_CONN, PROTO_DIS_N1);
+	if (st->pa->spv) {	/* SPV ? */
+		/* NSF SPV */
+		*p++ = WE0_netSpecFac;
+		*p++ = 4;	/* Laenge */
+		*p++ = 0;
+		*p++ = FAC_SPV;	/* SPV */
+		*p++ = st->pa->setup.si1;
+		*p++ = st->pa->setup.si2;
+		*p++ = WE0_netSpecFac;
+		*p++ = 4;	/* Laenge */
+		*p++ = 0;
+		*p++ = FAC_Activate;	/* aktiviere SPV */
+		*p++ = st->pa->setup.si1;
+		*p++ = st->pa->setup.si2;
+	}
+	newl3state(st, 8);
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	st->l3.l3l2(st, DL_DATA, skb);
+	L3DelTimer(&st->l3.timer);
+	L3AddTimer(&st->l3.timer, st->l3.t313, CC_T313);
+}
+
+static void
+l3_1tr6_reset(struct PStack *st, u_char pr, void *arg)
+{
+	newl3state(st, 0);
+}
+
+static void
+l3_1tr6_disconnect_req(struct PStack *st, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	u_char cause = 0x10;
+	u_char clen = 1;
+
+	if (st->pa->cause > 0)
+		cause = st->pa->cause;
+	/* Map DSS1 causes */
+	switch (cause & 0x7f) {
+		case 0x10:
+			clen = 0;
+			break;
+		case 0x15:
+			cause = CAUSE_CallRejected;
+			break;
+	}
+	StopAllL3Timer(st);
+	MsgHead(p, st->l3.callref, MT_N1_DISC, PROTO_DIS_N1);
+	*p++ = WE0_cause;
+	*p++ = clen;		/* Laenge */
+	if (clen)
+		*p++ = cause | 0x80;
+	newl3state(st, 11);
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	st->l3.l3l2(st, DL_DATA, skb);
+	L3AddTimer(&st->l3.timer, st->l3.t305, CC_T305);
+}
+
+static void
+l3_1tr6_release_req(struct PStack *st, u_char pr, void *arg)
+{
+	StopAllL3Timer(st);
+	newl3state(st, 19);
+	l3_1TR6_message(st, MT_N1_REL, PROTO_DIS_N1);
+	L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_1);
+}
+
+static void
+l3_1tr6_t303(struct PStack *st, u_char pr, void *arg)
+{
+	if (st->l3.n_t303 > 0) {
+		st->l3.n_t303--;
+		L3DelTimer(&st->l3.timer);
+		l3_1tr6_setup_req(st, pr, arg);
+	} else {
+		L3DelTimer(&st->l3.timer);
+		st->l3.l3l4(st, CC_NOSETUP_RSP_ERR, NULL);
+		st->l3.n_t303 = 1;
+		newl3state(st, 0);
+	}
+}
+
+static void
+l3_1tr6_t304(struct PStack *st, u_char pr, void *arg)
+{
+	L3DelTimer(&st->l3.timer);
+	st->pa->cause = 0xE6;
+	l3_1tr6_disconnect_req(st, pr, NULL);
+	st->l3.l3l4(st, CC_SETUP_ERR, NULL);
+
+}
+
+static void
+l3_1tr6_t305(struct PStack *st, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	u_char cause = 0x90;
+	u_char clen = 1;
+
+	L3DelTimer(&st->l3.timer);
+	if (st->pa->cause > 0)
+		cause = st->pa->cause;
+	/* Map DSS1 causes */
+	switch (cause & 0x7f) {
+		case 0x10:
+			clen = 0;
+			break;
+		case 0x15:
+			cause = CAUSE_CallRejected;
+			break;
+	}
+	MsgHead(p, st->l3.callref, MT_N1_REL, PROTO_DIS_N1);
+	*p++ = WE0_cause;
+	*p++ = clen;		/* Laenge */
+	if (clen)
+		*p++ = cause;
+	newl3state(st, 19);
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	st->l3.l3l2(st, DL_DATA, skb);
+	L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_1);
+}
+
+static void
+l3_1tr6_t310(struct PStack *st, u_char pr, void *arg)
+{
+	L3DelTimer(&st->l3.timer);
+	st->pa->cause = 0xE6;
+	l3_1tr6_disconnect_req(st, pr, NULL);
+	st->l3.l3l4(st, CC_SETUP_ERR, NULL);
+}
+
+static void
+l3_1tr6_t313(struct PStack *st, u_char pr, void *arg)
+{
+	L3DelTimer(&st->l3.timer);
+	st->pa->cause = 0xE6;
+	l3_1tr6_disconnect_req(st, pr, NULL);
+	st->l3.l3l4(st, CC_CONNECT_ERR, NULL);
+}
+
+static void
+l3_1tr6_t308_1(struct PStack *st, u_char pr, void *arg)
+{
+	L3DelTimer(&st->l3.timer);
+	l3_1TR6_message(st, MT_N1_REL, PROTO_DIS_N1);
+	L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_2);
+	newl3state(st, 19);
+}
+
+static void
+l3_1tr6_t308_2(struct PStack *st, u_char pr, void *arg)
+{
+	L3DelTimer(&st->l3.timer);
+	st->l3.l3l4(st, CC_RELEASE_ERR, NULL);
+	newl3state(st, 0);
+}
+/* *INDENT-OFF* */
+static struct stateentry downstl[] =
+{
+	{SBIT(0),
+	 CC_SETUP_REQ, l3_1tr6_setup_req},
+   	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) |
+    	 SBIT(10),
+    	 CC_DISCONNECT_REQ, l3_1tr6_disconnect_req},
+	{SBIT(12),
+	 CC_RELEASE_REQ, l3_1tr6_release_req},
+	{ALL_STATES,
+	 CC_DLRL, l3_1tr6_reset},
+	{SBIT(6),
+	 CC_IGNORE, l3_1tr6_reset},
+	{SBIT(6),
+	 CC_REJECT_REQ, l3_1tr6_disconnect_req},
+	{SBIT(6),
+	 CC_ALERTING_REQ, l3_1tr6_alert_req},
+	{SBIT(6) | SBIT(7),
+	 CC_SETUP_RSP, l3_1tr6_setup_rsp},
+	{SBIT(1),
+	 CC_T303, l3_1tr6_t303},
+	{SBIT(2),
+	 CC_T304, l3_1tr6_t304},
+	{SBIT(3),
+	 CC_T310, l3_1tr6_t310},
+	{SBIT(8),
+	 CC_T313, l3_1tr6_t313},
+	{SBIT(11),
+	 CC_T305, l3_1tr6_t305},
+	{SBIT(19),
+	 CC_T308_1, l3_1tr6_t308_1},
+	{SBIT(19),
+	 CC_T308_2, l3_1tr6_t308_2},
+};
+
+static int downstl_len = sizeof(downstl) /
+sizeof(struct stateentry);
+
+static struct stateentry datastln1[] =
+{
+	{SBIT(0),
+	 MT_N1_SETUP, l3_1tr6_setup},
+	{SBIT(1),
+	 MT_N1_SETUP_ACK, l3_1tr6_setup_ack},
+	{SBIT(1) | SBIT(2),
+	 MT_N1_CALL_SENT, l3_1tr6_call_sent},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10),
+	 MT_N1_DISC, l3_1tr6_disc},
+	{SBIT(2) | SBIT(3) | SBIT(4),
+	 MT_N1_ALERT, l3_1tr6_alert},
+	{SBIT(2) | SBIT(3) | SBIT(4),
+	 MT_N1_CONN, l3_1tr6_connect},
+	{SBIT(2),
+	 MT_N1_INFO, l3_1tr6_info_s2},
+	{SBIT(8),
+	 MT_N1_CONN_ACK, l3_1tr6_connect_ack},
+	{SBIT(10),
+	 MT_N1_INFO, l3_1tr6_info},
+	{SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) |
+	 SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19),
+	 MT_N1_REL, l3_1tr6_rel},
+	{SBIT(19),
+	 MT_N1_REL_ACK, l3_1tr6_rel_ack}
+};
+/* *INDENT-ON* */
+
+
+
+static int datastln1_len = sizeof(datastln1) /
+sizeof(struct stateentry);
+
+static void
+up1tr6(struct PStack *st, int pr, void *arg)
+{
+	int i, mt;
+	struct sk_buff *skb = arg;
+	char tmp[80];
+
+	if ((skb->data[0] & 0xfe) != PROTO_DIS_N0) {
+		if (st->l3.debug & L3_DEB_PROTERR) {
+			sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %ld state %d",
+				(pr == DL_DATA) ? " " : "(broadcast) ",
+				skb->data[0], skb->len, st->l3.state);
+			l3_debug(st, tmp);
+		}
+		SET_SKB_FREE(skb);
+		dev_kfree_skb(skb, FREE_READ);
+		return;
+	}
+	mt = skb->data[skb->data[1] + 2];
+	if (skb->data[0] == PROTO_DIS_N0) {
+		SET_SKB_FREE(skb);
+		dev_kfree_skb(skb, FREE_READ);
+		if (st->l3.debug & L3_DEB_STATE) {
+			sprintf(tmp, "up1tr6%s N0 state %d mt %x unhandled",
+				(pr == DL_DATA) ? " " : "(broadcast) ",
+				st->l3.state, mt);
+			l3_debug(st, tmp);
+		}
+	} else if (skb->data[0] == PROTO_DIS_N1) {
+		for (i = 0; i < datastln1_len; i++)
+			if ((mt == datastln1[i].primitive) &&
+			    ((1 << st->l3.state) & datastln1[i].state))
+				break;
+		if (i == datastln1_len) {
+			SET_SKB_FREE(skb);
+			dev_kfree_skb(skb, FREE_READ);
+			if (st->l3.debug & L3_DEB_STATE) {
+				sprintf(tmp, "up1tr6%sstate %d mt %x unhandled",
+				  (pr == DL_DATA) ? " " : "(broadcast) ",
+					st->l3.state, mt);
+				l3_debug(st, tmp);
+			}
+			return;
+		} else {
+			if (st->l3.debug & L3_DEB_STATE) {
+				sprintf(tmp, "up1tr6%sstate %d mt %x",
+				  (pr == DL_DATA) ? " " : "(broadcast) ",
+					st->l3.state, mt);
+				l3_debug(st, tmp);
+			}
+			datastln1[i].rout(st, pr, skb);
+		}
+	}
+}
+
+static void
+down1tr6(struct PStack *st, int pr, void *arg)
+{
+	int i;
+	char tmp[80];
+
+	for (i = 0; i < downstl_len; i++)
+		if ((pr == downstl[i].primitive) &&
+		    ((1 << st->l3.state) & downstl[i].state))
+			break;
+	if (i == downstl_len) {
+		if (st->l3.debug & L3_DEB_STATE) {
+			sprintf(tmp, "down1tr6 state %d prim %d unhandled",
+				st->l3.state, pr);
+			l3_debug(st, tmp);
+		}
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			sprintf(tmp, "down1tr6 state %d prim %d",
+				st->l3.state, pr);
+			l3_debug(st, tmp);
+		}
+		downstl[i].rout(st, pr, arg);
+	}
+}
+
+void
+setstack_1tr6(struct PStack *st)
+{
+	char tmp[64];
+
+	st->l4.l4l3 = down1tr6;
+	st->l2.l2l3 = up1tr6;
+	st->l3.t303 = 4000;
+	st->l3.t304 = 20000;
+	st->l3.t305 = 4000;
+	st->l3.t308 = 4000;
+	st->l3.t310 = 120000;
+	st->l3.t313 = 4000;
+	st->l3.t318 = 4000;
+	st->l3.t319 = 4000;
+	st->l3.n_t303 = 0;
+
+	if (st->l3.channr & 1) {
+		strcpy(tmp, l3_1tr6_revision);
+		printk(KERN_NOTICE "HiSax: 1TR6 Rev. %s\n", HiSax_getrev(tmp));
+	}
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/l3_1tr6.h linux/drivers/isdn/hisax/l3_1tr6.h
--- v2.0.30/linux/drivers/isdn/hisax/l3_1tr6.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/l3_1tr6.h	Mon Aug  4 17:34:00 1997
@@ -0,0 +1,160 @@
+/* $Id: l3_1tr6.h,v 1.1 1996/10/13 20:03:48 keil Exp $
+ *
+ *  German 1TR6 D-channel protocol defines
+ *
+ * $Log: l3_1tr6.h,v $
+ * Revision 1.1  1996/10/13 20:03:48  keil
+ * Initial revision
+ *
+ *
+ *
+ */
+#ifndef l3_1tr6
+#define l3_1tr6
+
+#define PROTO_DIS_N0 0x40
+#define PROTO_DIS_N1 0x41
+
+/*
+ * MsgType N0
+ */
+#define MT_N0_REG_IND 0x61
+#define MT_N0_CANC_IND 0x62
+#define MT_N0_FAC_STA 0x63
+#define MT_N0_STA_ACK 0x64
+#define MT_N0_STA_REJ 0x65
+#define MT_N0_FAC_INF 0x66
+#define MT_N0_INF_ACK 0x67
+#define MT_N0_INF_REJ 0x68
+#define MT_N0_CLOSE   0x75
+#define MT_N0_CLO_ACK 0x77
+
+
+/*
+ * MsgType N1
+ */
+
+#define MT_N1_ESC 0x00
+#define MT_N1_ALERT 0x01
+#define MT_N1_CALL_SENT 0x02
+#define MT_N1_CONN 0x07
+#define MT_N1_CONN_ACK 0x0F
+#define MT_N1_SETUP 0x05
+#define MT_N1_SETUP_ACK 0x0D
+#define MT_N1_RES 0x26
+#define MT_N1_RES_ACK 0x2E
+#define MT_N1_RES_REJ 0x22
+#define MT_N1_SUSP 0x25
+#define MT_N1_SUSP_ACK 0x2D
+#define MT_N1_SUSP_REJ 0x21
+#define MT_N1_USER_INFO 0x20
+#define MT_N1_DET 0x40
+#define MT_N1_DISC 0x45
+#define MT_N1_REL 0x4D
+#define MT_N1_REL_ACK 0x5A
+#define MT_N1_CANC_ACK 0x6E
+#define MT_N1_CANC_REJ 0x67
+#define MT_N1_CON_CON 0x69
+#define MT_N1_FAC 0x60
+#define MT_N1_FAC_ACK 0x68
+#define MT_N1_FAC_CAN 0x66
+#define MT_N1_FAC_REG 0x64
+#define MT_N1_FAC_REJ 0x65
+#define MT_N1_INFO 0x6D
+#define MT_N1_REG_ACK 0x6C
+#define MT_N1_REG_REJ 0x6F
+#define MT_N1_STAT 0x63
+
+
+
+/*
+ * W Elemente
+ */
+
+#define WE_Shift_F0 0x90
+#define WE_Shift_F6 0x96
+#define WE_Shift_OF0 0x98
+#define WE_Shift_OF6 0x9E
+
+#define WE0_cause 0x08
+#define WE0_connAddr 0x0C
+#define WE0_callID 0x10
+#define WE0_chanID 0x18
+#define WE0_netSpecFac 0x20
+#define WE0_display 0x28
+#define WE0_keypad 0x2C
+#define WE0_origAddr 0x6C
+#define WE0_destAddr 0x70
+#define WE0_userInfo 0x7E
+
+#define WE0_moreData 0xA0
+#define WE0_congestLevel 0xB0
+
+#define WE6_serviceInd 0x01
+#define WE6_chargingInfo 0x02
+#define WE6_date 0x03
+#define WE6_facSelect 0x05
+#define WE6_facStatus 0x06
+#define WE6_statusCalled 0x07
+#define WE6_addTransAttr 0x08
+
+/*
+ * FacCodes
+ */
+#define FAC_Sperre 0x01
+#define FAC_Sperre_All 0x02
+#define FAC_Sperre_Fern 0x03
+#define FAC_Sperre_Intl 0x04
+#define FAC_Sperre_Interk 0x05
+
+#define FAC_Forward1 0x02
+#define FAC_Forward2 0x03
+#define FAC_Konferenz 0x06
+#define FAC_GrabBchan 0x0F
+#define FAC_Reactivate 0x10
+#define FAC_Konferenz3 0x11
+#define FAC_Dienstwechsel1 0x12
+#define FAC_Dienstwechsel2 0x13
+#define FAC_NummernIdent 0x14
+#define FAC_GBG 0x15
+#define FAC_DisplayUebergeben 0x17
+#define FAC_DisplayUmgeleitet 0x1A
+#define FAC_Unterdruecke 0x1B
+#define FAC_Deactivate 0x1E
+#define FAC_Activate 0x1D
+#define FAC_SPV 0x1F
+#define FAC_Rueckwechsel 0x23
+#define FAC_Umleitung 0x24
+
+/*
+ * Cause codes
+ */
+#define CAUSE_InvCRef 0x01
+#define CAUSE_BearerNotImpl 0x03
+#define CAUSE_CIDunknown 0x07
+#define CAUSE_CIDinUse 0x08
+#define CAUSE_NoChans 0x0A
+#define CAUSE_FacNotImpl 0x10
+#define CAUSE_FacNotSubscr 0x11
+#define CAUSE_OutgoingBarred 0x20
+#define CAUSE_UserAccessBusy 0x21
+#define CAUSE_NegativeGBG 0x22
+#define CAUSE_UnknownGBG 0x23
+#define CAUSE_NoSPVknown 0x25
+#define CAUSE_DestNotObtain 0x35
+#define CAUSE_NumberChanged 0x38
+#define CAUSE_OutOfOrder 0x39
+#define CAUSE_NoUserResponse 0x3A
+#define CAUSE_UserBusy 0x3B
+#define CAUSE_IncomingBarred 0x3D
+#define CAUSE_CallRejected 0x3E
+#define CAUSE_NetworkCongestion 0x59
+#define CAUSE_RemoteUser 0x5A
+#define CAUSE_LocalProcErr 0x70
+#define CAUSE_RemoteProcErr 0x71
+#define CAUSE_RemoteUserSuspend 0x72
+#define CAUSE_RemoteUserResumed 0x73
+#define CAUSE_UserInfoDiscarded 0x7F
+
+
+#endif
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/l3dss1.c linux/drivers/isdn/hisax/l3dss1.c
--- v2.0.30/linux/drivers/isdn/hisax/l3dss1.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/l3dss1.c	Mon Aug  4 17:34:00 1997
@@ -0,0 +1,809 @@
+/* $Id: l3dss1.c,v 1.16 1997/06/03 20:43:46 keil Exp $
+
+ * EURO/DSS1 D-channel protocol
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *              based on the teles driver from Jan den Ouden
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ * $Log: l3dss1.c,v $
+ * Revision 1.16  1997/06/03 20:43:46  keil
+ * Display numbers as default
+ *
+ * Revision 1.15  1997/04/17 11:50:48  keil
+ * pa->loc was undefined, if it was not send by the exchange
+ *
+ * Revision 1.14  1997/04/06 22:54:20  keil
+ * Using SKB's
+ *
+ * Revision 1.13  1997/03/13 20:37:28  keil
+ * CLIR and channel request added
+ *
+ * Revision 1.12  1997/02/17 00:34:26  keil
+ * Bugfix: Wrong cause delivered
+ *
+ * Revision 1.11  1997/02/16 12:12:47  fritz
+ * Bugfix: SI2 was nont initialized on incoming calls.
+ *
+ * Revision 1.10  1997/02/11 01:37:24  keil
+ * Changed setup-interface (incoming and outgoing)
+ *
+ * Revision 1.9  1997/01/27 23:20:52  keil
+ * report revision only ones
+ *
+ * Revision 1.8  1997/01/21 22:29:41  keil
+ * new statemachine; L3 timers
+ *
+ * Revision 1.7  1996/12/14 21:06:59  keil
+ * additional states for CC_REJECT
+ *
+ * Revision 1.6  1996/12/08 22:59:16  keil
+ * fixed calling party number without octet 3a
+ *
+ * Revision 1.5  1996/12/08 19:53:31  keil
+ * fixes from Pekka Sarnila
+ *
+ * Revision 1.4  1996/11/05 19:44:36  keil
+ * some fixes from Henner Eisen
+ *
+ * Revision 1.3  1996/10/30 10:18:01  keil
+ * bugfixes in debugging output
+ *
+ * Revision 1.2  1996/10/27 22:15:16  keil
+ * bugfix reject handling
+ *
+ * Revision 1.1  1996/10/13 20:04:55  keil
+ * Initial revision
+ *
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isdnl3.h"
+#include <linux/ctype.h>
+
+extern char *HiSax_getrev(const char *revision);
+const char *dss1_revision = "$Revision: 1.16 $";
+
+#define	MsgHead(ptr, cref, mty) \
+	*ptr++ = 0x8; \
+	*ptr++ = 0x1; \
+	*ptr++ = cref; \
+	*ptr++ = mty
+
+static void
+l3dss1_message(struct PStack *st, u_char mt)
+{
+	struct sk_buff *skb;
+	u_char *p;
+
+	if (!(skb = l3_alloc_skb(4)))
+		return;
+	p = skb_put(skb, 4);
+	MsgHead(p, st->l3.callref, mt);
+	st->l3.l3l2(st, DL_DATA, skb);
+}
+
+static void
+l3dss1_release_req(struct PStack *st, u_char pr, void *arg)
+{
+	StopAllL3Timer(st);
+	newl3state(st, 19);
+	l3dss1_message(st, MT_RELEASE);
+	L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_1);
+}
+
+static void
+l3dss1_release_cmpl(struct PStack *st, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+	int cause = -1;
+
+	p = skb->data;
+	st->pa->loc = 0;
+	if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
+		p++;
+		if (*p++ == 2)
+			st->pa->loc = *p++;
+		cause = *p & 0x7f;
+	}
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+	StopAllL3Timer(st);
+	st->pa->cause = cause;
+	newl3state(st, 0);
+	st->l3.l3l4(st, CC_RELEASE_CNF, NULL);
+}
+
+static void
+l3dss1_setup_req(struct PStack *st, u_char pr,
+		 void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[128];
+	u_char *p = tmp;
+	u_char channel = 0;
+	u_char screen = 0x80;
+	u_char *teln;
+	u_char *msn;
+	int l;
+
+	st->l3.callref = st->pa->callref;
+	MsgHead(p, st->l3.callref, MT_SETUP);
+
+	/*
+	 * Set Bearer Capability, Map info from 1TR6-convention to EDSS1
+	 */
+	*p++ = 0xa1;		/* complete indicator */
+	switch (st->pa->setup.si1) {
+		case 1:	/* Telephony                               */
+			*p++ = 0x4;	/* BC-IE-code                              */
+			*p++ = 0x3;	/* Length                                  */
+			*p++ = 0x90;	/* Coding Std. CCITT, 3.1 kHz audio     */
+			*p++ = 0x90;	/* Circuit-Mode 64kbps                     */
+			*p++ = 0xa3;	/* A-Law Audio                             */
+			break;
+		case 5:	/* Datatransmission 64k, BTX               */
+		case 7:	/* Datatransmission 64k                    */
+		default:
+			*p++ = 0x4;	/* BC-IE-code                              */
+			*p++ = 0x2;	/* Length                                  */
+			*p++ = 0x88;	/* Coding Std. CCITT, unrestr. dig. Inform. */
+			*p++ = 0x90;	/* Circuit-Mode 64kbps                      */
+			break;
+	}
+	/*
+	 * What about info2? Mapping to High-Layer-Compatibility?
+	 */
+	teln = st->pa->setup.phone;
+	if (*teln) {
+		/* parse number for special things */
+		if (!isdigit(*teln)) {
+			switch (0x5f & *teln) {
+				case 'C':
+					channel = 0x08;
+				case 'P':
+					channel |= 0x80;
+					teln++;
+					if (*teln == '1')
+						channel |= 0x01;
+					else
+						channel |= 0x02;
+					break;
+				case 'R':
+					screen = 0xA0;
+					break;
+				case 'D':
+					screen = 0x80;
+					break;
+				default:
+					if (st->l3.debug & L3_DEB_WARN)
+						l3_debug(st, "Wrong MSN Code");
+					break;
+			}
+			teln++;
+		}
+	}
+	if (channel) {
+		*p++ = 0x18;	/* channel indicator */
+		*p++ = 1;
+		*p++ = channel;
+	}
+	msn = st->pa->setup.eazmsn;
+	if (*msn) {
+		*p++ = 0x6c;
+		*p++ = strlen(msn) + (screen ? 2 : 1);
+		/* Classify as AnyPref. */
+		if (screen) {
+			*p++ = 0x01;	/* Ext = '0'B, Type = '000'B, Plan = '0001'B. */
+			*p++ = screen;
+		} else
+			*p++ = 0x81;	/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+		while (*msn)
+			*p++ = *msn++ & 0x7f;
+	}
+	*p++ = 0x70;
+	*p++ = strlen(teln) + 1;
+	/* Classify as AnyPref. */
+	*p++ = 0x81;		/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+
+	while (*teln)
+		*p++ = *teln++ & 0x7f;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	L3DelTimer(&st->l3.timer);
+	L3AddTimer(&st->l3.timer, st->l3.t303, CC_T303);
+	newl3state(st, 1);
+	st->l3.l3l2(st, DL_DATA, skb);
+
+}
+
+static void
+l3dss1_call_proc(struct PStack *st, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+
+	L3DelTimer(&st->l3.timer);
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x18, 0))) {
+		st->pa->bchannel = p[2] & 0x3;
+		if ((!st->pa->bchannel) && (st->l3.debug & L3_DEB_WARN))
+			l3_debug(st, "setup answer without bchannel");
+	} else if (st->l3.debug & L3_DEB_WARN)
+		l3_debug(st, "setup answer without bchannel");
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+	newl3state(st, 3);
+	L3AddTimer(&st->l3.timer, st->l3.t310, CC_T310);
+	st->l3.l3l4(st, CC_PROCEEDING_IND, NULL);
+}
+
+static void
+l3dss1_setup_ack(struct PStack *st, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+
+	L3DelTimer(&st->l3.timer);
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x18, 0))) {
+		st->pa->bchannel = p[2] & 0x3;
+		if ((!st->pa->bchannel) && (st->l3.debug & L3_DEB_WARN))
+			l3_debug(st, "setup answer without bchannel");
+	} else if (st->l3.debug & L3_DEB_WARN)
+		l3_debug(st, "setup answer without bchannel");
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+	newl3state(st, 2);
+	L3AddTimer(&st->l3.timer, st->l3.t304, CC_T304);
+	st->l3.l3l4(st, CC_MORE_INFO, NULL);
+}
+
+static void
+l3dss1_disconnect(struct PStack *st, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+	int cause = -1;
+
+	StopAllL3Timer(st);
+	p = skb->data;
+	st->pa->loc = 0;
+	if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
+		p++;
+		if (*p++ == 2)
+			st->pa->loc = *p++;
+		cause = *p & 0x7f;
+	}
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+	newl3state(st, 12);
+	st->pa->cause = cause;
+	st->l3.l3l4(st, CC_DISCONNECT_IND, NULL);
+}
+
+static void
+l3dss1_connect(struct PStack *st, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+	L3DelTimer(&st->l3.timer);	/* T310 */
+	newl3state(st, 10);
+	st->l3.l3l4(st, CC_SETUP_CNF, NULL);
+}
+
+static void
+l3dss1_alerting(struct PStack *st, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+	L3DelTimer(&st->l3.timer);	/* T304 */
+	newl3state(st, 4);
+	st->l3.l3l4(st, CC_ALERTING_IND, NULL);
+}
+
+static void
+l3dss1_setup(struct PStack *st, u_char pr, void *arg)
+{
+	u_char *p;
+	int bcfound = 0;
+	char tmp[80];
+	struct sk_buff *skb = arg;
+
+	p = skb->data;
+	st->pa->callref = getcallref(p);
+	st->l3.callref = 0x80 + st->pa->callref;
+
+	/*
+	 * Channel Identification
+	 */
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x18, 0))) {
+		st->pa->bchannel = p[2] & 0x3;
+		if (st->pa->bchannel)
+			bcfound++;
+		else if (st->l3.debug & L3_DEB_WARN)
+			l3_debug(st, "setup without bchannel");
+	} else if (st->l3.debug & L3_DEB_WARN)
+		l3_debug(st, "setup without bchannel");
+
+	/*
+	   * Bearer Capabilities
+	 */
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x04, 0))) {
+		st->pa->setup.si2 = 0;
+		switch (p[2] & 0x1f) {
+			case 0x00:
+				/* Speech */
+			case 0x10:
+				/* 3.1 Khz audio */
+				st->pa->setup.si1 = 1;
+				break;
+			case 0x08:
+				/* Unrestricted digital information */
+				st->pa->setup.si1 = 7;
+				break;
+			case 0x09:
+				/* Restricted digital information */
+				st->pa->setup.si1 = 2;
+				break;
+			case 0x11:
+				/* Unrestr. digital information  with tones/announcements */
+				st->pa->setup.si1 = 3;
+				break;
+			case 0x18:
+				/* Video */
+				st->pa->setup.si1 = 4;
+				break;
+			default:
+				st->pa->setup.si1 = 0;
+		}
+	} else if (st->l3.debug & L3_DEB_WARN)
+		l3_debug(st, "setup without bearer capabilities");
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x70, 0)))
+		iecpy(st->pa->setup.eazmsn, p, 1);
+	else
+		st->pa->setup.eazmsn[0] = 0;
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x6c, 0))) {
+		st->pa->setup.plan = p[2];
+		if (p[2] & 0x80) {
+			iecpy(st->pa->setup.phone, p, 1);
+			st->pa->setup.screen = 0;
+		} else {
+			iecpy(st->pa->setup.phone, p, 2);
+			st->pa->setup.screen = p[3];
+		}
+	} else {
+		st->pa->setup.phone[0] = 0;
+		st->pa->setup.plan = 0;
+		st->pa->setup.screen = 0;
+	}
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+
+	if (bcfound) {
+		if ((st->pa->setup.si1 != 7) && (st->l3.debug & L3_DEB_WARN)) {
+			sprintf(tmp, "non-digital call: %s -> %s",
+				st->pa->setup.phone,
+				st->pa->setup.eazmsn);
+			l3_debug(st, tmp);
+		}
+		newl3state(st, 6);
+		st->l3.l3l4(st, CC_SETUP_IND, NULL);
+	}
+}
+
+static void
+l3dss1_reset(struct PStack *st, u_char pr, void *arg)
+{
+	StopAllL3Timer(st);
+	newl3state(st, 0);
+}
+
+static void
+l3dss1_setup_rsp(struct PStack *st, u_char pr,
+		 void *arg)
+{
+	newl3state(st, 8);
+	l3dss1_message(st, MT_CONNECT);
+	L3DelTimer(&st->l3.timer);
+	L3AddTimer(&st->l3.timer, st->l3.t313, CC_T313);
+}
+
+static void
+l3dss1_connect_ack(struct PStack *st, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+	newl3state(st, 10);
+	L3DelTimer(&st->l3.timer);
+	st->l3.l3l4(st, CC_SETUP_COMPLETE_IND, NULL);
+}
+
+static void
+l3dss1_disconnect_req(struct PStack *st, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	u_char cause = 0x10;
+
+	if (st->pa->cause > 0)
+		cause = st->pa->cause;
+
+	StopAllL3Timer(st);
+
+	MsgHead(p, st->l3.callref, MT_DISCONNECT);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause | 0x80;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	newl3state(st, 11);
+	st->l3.l3l2(st, DL_DATA, skb);
+	L3AddTimer(&st->l3.timer, st->l3.t305, CC_T305);
+}
+
+static void
+l3dss1_reject_req(struct PStack *st, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	u_char cause = 0x95;
+
+	if (st->pa->cause > 0)
+		cause = st->pa->cause;
+
+	MsgHead(p, st->l3.callref, MT_RELEASE_COMPLETE);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	newl3state(st, 0);
+	st->l3.l3l2(st, DL_DATA, skb);
+	st->l3.l3l4(st, CC_RELEASE_IND, NULL);
+}
+
+static void
+l3dss1_release(struct PStack *st, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+	int cause = -1;
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
+		p++;
+		if (*p++ == 2)
+			st->pa->loc = *p++;
+		cause = *p & 0x7f;
+	}
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+	StopAllL3Timer(st);
+	st->pa->cause = cause;
+	newl3state(st, 0);
+	l3dss1_message(st, MT_RELEASE_COMPLETE);
+	st->l3.l3l4(st, CC_RELEASE_IND, NULL);
+}
+
+static void
+l3dss1_alert_req(struct PStack *st, u_char pr,
+		 void *arg)
+{
+	newl3state(st, 7);
+	l3dss1_message(st, MT_ALERTING);
+}
+
+static void
+l3dss1_status_enq(struct PStack *st, u_char pr, void *arg)
+{
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	struct sk_buff *skb = arg;
+
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb, FREE_READ);
+
+	MsgHead(p, st->l3.callref, MT_STATUS);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = 0x9E;		/* answer status enquire */
+
+	*p++ = 0x14;		/* CallState */
+	*p++ = 0x1;
+	*p++ = st->l3.state & 0x3f;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	st->l3.l3l2(st, DL_DATA, skb);
+}
+
+static void
+l3dss1_t303(struct PStack *st, u_char pr, void *arg)
+{
+	if (st->l3.n_t303 > 0) {
+		st->l3.n_t303--;
+		L3DelTimer(&st->l3.timer);
+		l3dss1_setup_req(st, pr, arg);
+	} else {
+		newl3state(st, 0);
+		L3DelTimer(&st->l3.timer);
+		st->l3.l3l4(st, CC_NOSETUP_RSP_ERR, NULL);
+		st->l3.n_t303 = 1;
+	}
+}
+
+static void
+l3dss1_t304(struct PStack *st, u_char pr, void *arg)
+{
+	L3DelTimer(&st->l3.timer);
+	st->pa->cause = 0xE6;
+	l3dss1_disconnect_req(st, pr, NULL);
+	st->l3.l3l4(st, CC_SETUP_ERR, NULL);
+
+}
+
+static void
+l3dss1_t305(struct PStack *st, u_char pr, void *arg)
+{
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	struct sk_buff *skb;
+	u_char cause = 0x90;
+
+	L3DelTimer(&st->l3.timer);
+	if (st->pa->cause > 0)
+		cause = st->pa->cause;
+
+	MsgHead(p, st->l3.callref, MT_RELEASE);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	newl3state(st, 19);
+	st->l3.l3l2(st, DL_DATA, skb);
+	L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_1);
+}
+
+static void
+l3dss1_t310(struct PStack *st, u_char pr, void *arg)
+{
+	L3DelTimer(&st->l3.timer);
+	st->pa->cause = 0xE6;
+	l3dss1_disconnect_req(st, pr, NULL);
+	st->l3.l3l4(st, CC_SETUP_ERR, NULL);
+}
+
+static void
+l3dss1_t313(struct PStack *st, u_char pr, void *arg)
+{
+	L3DelTimer(&st->l3.timer);
+	st->pa->cause = 0xE6;
+	l3dss1_disconnect_req(st, pr, NULL);
+	st->l3.l3l4(st, CC_CONNECT_ERR, NULL);
+}
+
+static void
+l3dss1_t308_1(struct PStack *st, u_char pr, void *arg)
+{
+	newl3state(st, 19);
+	L3DelTimer(&st->l3.timer);
+	l3dss1_message(st, MT_RELEASE);
+	L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_2);
+}
+
+static void
+l3dss1_t308_2(struct PStack *st, u_char pr, void *arg)
+{
+	newl3state(st, 0);
+	L3DelTimer(&st->l3.timer);
+	st->l3.l3l4(st, CC_RELEASE_ERR, NULL);
+}
+/* *INDENT-OFF* */
+static struct stateentry downstatelist[] =
+{
+	{SBIT(0),
+	 CC_SETUP_REQ, l3dss1_setup_req},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(10),
+	 CC_DISCONNECT_REQ, l3dss1_disconnect_req},
+	{SBIT(12),
+	 CC_RELEASE_REQ, l3dss1_release_req},
+	{ALL_STATES,
+	 CC_DLRL, l3dss1_reset},
+	{SBIT(6),
+	 CC_IGNORE, l3dss1_reset},
+	{SBIT(6),
+	 CC_REJECT_REQ, l3dss1_reject_req},
+	{SBIT(6),
+	 CC_ALERTING_REQ, l3dss1_alert_req},
+	{SBIT(6) | SBIT(7),
+	 CC_SETUP_RSP, l3dss1_setup_rsp},
+	{SBIT(1),
+	 CC_T303, l3dss1_t303},
+	{SBIT(2),
+	 CC_T304, l3dss1_t304},
+	{SBIT(3),
+	 CC_T310, l3dss1_t310},
+	{SBIT(8),
+	 CC_T313, l3dss1_t313},
+	{SBIT(11),
+	 CC_T305, l3dss1_t305},
+	{SBIT(19),
+	 CC_T308_1, l3dss1_t308_1},
+	{SBIT(19),
+	 CC_T308_2, l3dss1_t308_2},
+};
+
+static int downsllen = sizeof(downstatelist) /
+sizeof(struct stateentry);
+
+static struct stateentry datastatelist[] =
+{
+	{ALL_STATES,
+	 MT_STATUS_ENQUIRY, l3dss1_status_enq},
+	{SBIT(0) | SBIT(6),
+	 MT_SETUP, l3dss1_setup},
+	{SBIT(1) | SBIT(2),
+	 MT_CALL_PROCEEDING, l3dss1_call_proc},
+	{SBIT(1),
+	 MT_SETUP_ACKNOWLEDGE, l3dss1_setup_ack},
+	{SBIT(1) | SBIT(2) | SBIT(3),
+	 MT_ALERTING, l3dss1_alerting},
+	{SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) |
+	 SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19),
+	 MT_RELEASE_COMPLETE, l3dss1_release_cmpl},
+	{SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) |
+	 SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19),
+	 MT_RELEASE, l3dss1_release},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10),
+	 MT_DISCONNECT, l3dss1_disconnect},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4),
+	 MT_CONNECT, l3dss1_connect},
+	{SBIT(8),
+	 MT_CONNECT_ACKNOWLEDGE, l3dss1_connect_ack},
+};
+/* *INDENT-ON* */
+
+
+static int datasllen = sizeof(datastatelist) /
+sizeof(struct stateentry);
+
+static void
+dss1up(struct PStack *st, int pr, void *arg)
+{
+	int i, mt;
+	struct sk_buff *skb = arg;
+	char tmp[80];
+
+	if (skb->data[0] != PROTO_DIS_EURO) {
+		if (st->l3.debug & L3_DEB_PROTERR) {
+			sprintf(tmp, "dss1up%sunexpected discriminator %x message len %ld state %d",
+				(pr == DL_DATA) ? " " : "(broadcast) ",
+				skb->data[0], skb->len, st->l3.state);
+			l3_debug(st, tmp);
+		}
+		SET_SKB_FREE(skb);
+		dev_kfree_skb(skb, FREE_READ);
+		return;
+	}
+	mt = skb->data[skb->data[1] + 2];
+	for (i = 0; i < datasllen; i++)
+		if ((mt == datastatelist[i].primitive) &&
+		    ((1 << st->l3.state) & datastatelist[i].state))
+			break;
+	if (i == datasllen) {
+		SET_SKB_FREE(skb);
+		dev_kfree_skb(skb, FREE_READ);
+		if (st->l3.debug & L3_DEB_STATE) {
+			sprintf(tmp, "dss1up%sstate %d mt %x unhandled",
+				(pr == DL_DATA) ? " " : "(broadcast) ",
+				st->l3.state, mt);
+			l3_debug(st, tmp);
+		}
+		return;
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			sprintf(tmp, "dss1up%sstate %d mt %x",
+				(pr == DL_DATA) ? " " : "(broadcast) ",
+				st->l3.state, mt);
+			l3_debug(st, tmp);
+		}
+		datastatelist[i].rout(st, pr, skb);
+	}
+}
+
+static void
+dss1down(struct PStack *st, int pr, void *arg)
+{
+	int i;
+	char tmp[80];
+
+	for (i = 0; i < downsllen; i++)
+		if ((pr == downstatelist[i].primitive) &&
+		    ((1 << st->l3.state) & downstatelist[i].state))
+			break;
+	if (i == downsllen) {
+		if (st->l3.debug & L3_DEB_STATE) {
+			sprintf(tmp, "dss1down state %d prim %d unhandled",
+				st->l3.state, pr);
+			l3_debug(st, tmp);
+		}
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			sprintf(tmp, "dss1down state %d prim %d",
+				st->l3.state, pr);
+			l3_debug(st, tmp);
+		}
+		downstatelist[i].rout(st, pr, arg);
+	}
+}
+
+void
+setstack_dss1(struct PStack *st)
+{
+	char tmp[64];
+
+	st->l4.l4l3 = dss1down;
+	st->l2.l2l3 = dss1up;
+	st->l3.t303 = 4000;
+	st->l3.t304 = 30000;
+	st->l3.t305 = 30000;
+	st->l3.t308 = 4000;
+	st->l3.t310 = 30000;
+	st->l3.t313 = 4000;
+	st->l3.t318 = 4000;
+	st->l3.t319 = 4000;
+	st->l3.n_t303 = 1;
+
+	if (st->l3.channr & 1) {
+		strcpy(tmp, dss1_revision);
+		printk(KERN_NOTICE "HiSax: DSS1 Rev. %s\n", HiSax_getrev(tmp));
+	}
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/q931.c linux/drivers/isdn/hisax/q931.c
--- v2.0.30/linux/drivers/isdn/hisax/q931.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/q931.c	Mon Aug  4 17:34:00 1997
@@ -0,0 +1,1218 @@
+/* $Id: q931.c,v 1.5 1997/04/06 22:56:43 keil Exp $
+
+ * q931.c       code to decode ITU Q.931 call control messages
+ *
+ * Author       Jan den Ouden
+ *
+ * Changelog
+ *
+ * Pauline Middelink    general improvements
+ *
+ * Beat Doebeli         cause texts, display information element
+ *
+ * Karsten Keil         cause texts, display information element for 1TR6
+ *
+ *
+ * $Log: q931.c,v $
+ * Revision 1.5  1997/04/06 22:56:43  keil
+ * Some cosmetic changes
+ *
+ * Revision 1.4  1997/02/09 00:29:11  keil
+ * new interface handling, one interface per card
+ *
+ * Revision 1.3  1997/01/21 22:24:59  keil
+ * cleanups
+ *
+ * Revision 1.2  1996/10/27 22:12:45  keil
+ * reporting unknown level 3 protocol ids
+ *
+ * Revision 1.1  1996/10/13 20:04:56  keil
+ * Initial revision
+ *
+ *
+ */
+
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "l3_1tr6.h"
+
+u_char *
+findie(u_char * p, int size, u_char ie, int wanted_set)
+{
+	int l, codeset, maincodeset;
+	u_char *pend = p + size;
+
+	/* skip protocol discriminator, callref and message type */
+	p++;
+	l = (*p++) & 0xf;
+	p += l;
+	p++;
+	codeset = 0;
+	maincodeset = 0;
+	/* while there are bytes left... */
+	while (p < pend) {
+		if ((*p & 0xf0) == 0x90) {
+			codeset = *p & 0x07;
+			if (!(*p & 0x08))
+				maincodeset = codeset;
+		}
+		if (*p & 0x80)
+			p++;
+		else {
+			if (codeset == wanted_set) {
+				if (*p == ie)
+					return (p);
+				if (*p > ie)
+					return (NULL);
+			}
+			p++;
+			l = *p++;
+			p += l;
+			codeset = maincodeset;
+		}
+	}
+	return (NULL);
+}
+
+void
+iecpy(u_char * dest, u_char * iestart, int ieoffset)
+{
+	u_char *p;
+	int l;
+
+	p = iestart + ieoffset + 2;
+	l = iestart[1] - ieoffset;
+	while (l--)
+		*dest++ = *p++;
+	*dest++ = '\0';
+}
+
+int
+getcallref(u_char * p)
+{
+	p++;			/* prot discr */
+	p++;			/* callref length */
+	return (*p);		/* assuming one-byte callref */
+}
+
+/*
+ * According to Table 4-2/Q.931
+ */
+static
+struct MessageType {
+	u_char nr;
+	char *descr;
+} mtlist[] = {
+
+	{
+		0x1, "ALERTING"
+	},
+	{
+		0x2, "CALL PROCEEDING"
+	},
+	{
+		0x7, "CONNECT"
+	},
+	{
+		0xf, "CONNECT ACKNOWLEDGE"
+	},
+	{
+		0x3, "PROGRESS"
+	},
+	{
+		0x5, "SETUP"
+	},
+	{
+		0xd, "SETUP ACKNOWLEDGE"
+	},
+	{
+		0x26, "RESUME"
+	},
+	{
+		0x2e, "RESUME ACKNOWLEDGE"
+	},
+	{
+		0x22, "RESUME REJECT"
+	},
+	{
+		0x25, "SUSPEND"
+	},
+	{
+		0x2d, "SUSPEND ACKNOWLEDGE"
+	},
+	{
+		0x21, "SUSPEND REJECT"
+	},
+	{
+		0x20, "USER INFORMATION"
+	},
+	{
+		0x45, "DISCONNECT"
+	},
+	{
+		0x4d, "RELEASE"
+	},
+	{
+		0x5a, "RELEASE COMPLETE"
+	},
+	{
+		0x46, "RESTART"
+	},
+	{
+		0x4e, "RESTART ACKNOWLEDGE"
+	},
+	{
+		0x60, "SEGMENT"
+	},
+	{
+		0x79, "CONGESTION CONTROL"
+	},
+	{
+		0x7b, "INFORMATION"
+	},
+	{
+		0x62, "FACILITY"
+	},
+	{
+		0x6e, "NOTIFY"
+	},
+	{
+		0x7d, "STATUS"
+	},
+	{
+		0x75, "STATUS ENQUIRY"
+	}
+};
+
+#define MTSIZE sizeof(mtlist)/sizeof(struct MessageType)
+
+static
+struct MessageType mt_n0[] =
+{
+	{MT_N0_REG_IND, "REGister INDication"},
+	{MT_N0_CANC_IND, "CANCel INDication"},
+	{MT_N0_FAC_STA, "FACility STAtus"},
+	{MT_N0_STA_ACK, "STAtus ACKnowledge"},
+	{MT_N0_STA_REJ, "STAtus REJect"},
+	{MT_N0_FAC_INF, "FACility INFormation"},
+	{MT_N0_INF_ACK, "INFormation ACKnowledge"},
+	{MT_N0_INF_REJ, "INFormation REJect"},
+	{MT_N0_CLOSE, "CLOSE"},
+	{MT_N0_CLO_ACK, "CLOse ACKnowledge"}
+};
+
+int mt_n0_len = (sizeof(mt_n0) / sizeof(struct MessageType));
+
+static
+struct MessageType mt_n1[] =
+{
+	{MT_N1_ESC, "ESCape"},
+	{MT_N1_ALERT, "ALERT"},
+	{MT_N1_CALL_SENT, "CALL SENT"},
+	{MT_N1_CONN, "CONNect"},
+	{MT_N1_CONN_ACK, "CONNect ACKnowledge"},
+	{MT_N1_SETUP, "SETUP"},
+	{MT_N1_SETUP_ACK, "SETUP ACKnowledge"},
+	{MT_N1_RES, "RESume"},
+	{MT_N1_RES_ACK, "RESume ACKnowledge"},
+	{MT_N1_RES_REJ, "RESume REJect"},
+	{MT_N1_SUSP, "SUSPend"},
+	{MT_N1_SUSP_ACK, "SUSPend ACKnowledge"},
+	{MT_N1_SUSP_REJ, "SUSPend REJect"},
+	{MT_N1_USER_INFO, "USER INFO"},
+	{MT_N1_DET, "DETach"},
+	{MT_N1_DISC, "DISConnect"},
+	{MT_N1_REL, "RELease"},
+	{MT_N1_REL_ACK, "RELease ACKnowledge"},
+	{MT_N1_CANC_ACK, "CANCel ACKnowledge"},
+	{MT_N1_CANC_REJ, "CANCel REJect"},
+	{MT_N1_CON_CON, "CONgestion CONtrol"},
+	{MT_N1_FAC, "FACility"},
+	{MT_N1_FAC_ACK, "FACility ACKnowledge"},
+	{MT_N1_FAC_CAN, "FACility CANcel"},
+	{MT_N1_FAC_REG, "FACility REGister"},
+	{MT_N1_FAC_REJ, "FACility REJect"},
+	{MT_N1_INFO, "INFOrmation"},
+	{MT_N1_REG_ACK, "REGister ACKnowledge"},
+	{MT_N1_REG_REJ, "REGister REJect"},
+	{MT_N1_STAT, "STATus"}
+};
+
+int mt_n1_len = (sizeof(mt_n1) / sizeof(struct MessageType));
+
+static struct MessageType fac_1tr6[] =
+{
+	{FAC_Sperre, "Sperre"},
+	{FAC_Forward1, "Forward 1"},
+	{FAC_Forward2, "Forward 2"},
+	{FAC_Konferenz, "Konferenz"},
+	{FAC_GrabBchan, "Grab Bchannel"},
+	{FAC_Reactivate, "Reactivate"},
+	{FAC_Konferenz3, "Dreier Konferenz"},
+	{FAC_Dienstwechsel1, "Einseitiger Dienstwechsel"},
+	{FAC_Dienstwechsel2, "Zweiseitiger Dienstwechsel"},
+	{FAC_NummernIdent, "Rufnummer-Identifizierung"},
+	{FAC_GBG, "GBG"},
+	{FAC_DisplayUebergeben, "Display Uebergeben"},
+	{FAC_DisplayUmgeleitet, "Display Umgeleitet"},
+	{FAC_Unterdruecke, "Unterdruecke Rufnummer"},
+	{FAC_Deactivate, "Deactivate"},
+	{FAC_Activate, "Activate"},
+	{FAC_SPV, "SPV"},
+	{FAC_Rueckwechsel, "Rueckwechsel"},
+	{FAC_Umleitung, "Umleitung"}
+};
+int fac_1tr6_len = (sizeof(fac_1tr6) / sizeof(struct MessageType));
+
+
+
+static int
+prbits(char *dest, u_char b, int start, int len)
+{
+	char *dp = dest;
+
+	b = b << (8 - start);
+	while (len--) {
+		if (b & 0x80)
+			*dp++ = '1';
+		else
+			*dp++ = '0';
+		b = b << 1;
+	}
+	return (dp - dest);
+}
+
+static
+u_char *
+skipext(u_char * p)
+{
+	while (!(*p++ & 0x80));
+	return (p);
+}
+
+/*
+ * Cause Values According to Q.850
+ * edescr: English description
+ * ddescr: German description used by Swissnet II (Swiss Telecom
+ *         not yet written...
+ */
+
+static
+struct CauseValue {
+	u_char nr;
+	char *edescr;
+	char *ddescr;
+} cvlist[] = {
+
+	{
+		0x01, "Unallocated (unassigned) number", "Nummer nicht zugeteilt"
+	},
+	{
+		0x02, "No route to specified transit network", ""
+	},
+	{
+		0x03, "No route to destination", ""
+	},
+	{
+		0x04, "Send special information tone", ""
+	},
+	{
+		0x05, "Misdialled trunk prefix", ""
+	},
+	{
+		0x06, "Channel unacceptable", "Kanal nicht akzeptierbar"
+	},
+	{
+		0x07, "Channel awarded and being delivered in an established channel", ""
+	},
+	{
+		0x08, "Preemption", ""
+	},
+	{
+		0x09, "Preemption - circuit reserved for reuse", ""
+	},
+	{
+		0x10, "Normal call clearing", "Normale Ausloesung"
+	},
+	{
+		0x11, "User busy", "TNB besetzt"
+	},
+	{
+		0x12, "No user responding", ""
+	},
+	{
+		0x13, "No answer from user (user alerted)", ""
+	},
+	{
+		0x14, "Subscriber absent", ""
+	},
+	{
+		0x15, "Call rejected", ""
+	},
+	{
+		0x16, "Number changed", ""
+	},
+	{
+		0x1a, "non-selected user clearing", ""
+	},
+	{
+		0x1b, "Destination out of order", ""
+	},
+	{
+		0x1c, "Invalid number format (address incomplete)", ""
+	},
+	{
+		0x1d, "Facility rejected", ""
+	},
+	{
+		0x1e, "Response to Status enquiry", ""
+	},
+	{
+		0x1f, "Normal, unspecified", ""
+	},
+	{
+		0x22, "No circuit/channel available", ""
+	},
+	{
+		0x26, "Network out of order", ""
+	},
+	{
+		0x27, "Permanent frame mode connection out-of-service", ""
+	},
+	{
+		0x28, "Permanent frame mode connection operational", ""
+	},
+	{
+		0x29, "Temporary failure", ""
+	},
+	{
+		0x2a, "Switching equipment congestion", ""
+	},
+	{
+		0x2b, "Access information discarded", ""
+	},
+	{
+		0x2c, "Requested circuit/channel not available", ""
+	},
+	{
+		0x2e, "Precedence call blocked", ""
+	},
+	{
+		0x2f, "Resource unavailable, unspecified", ""
+	},
+	{
+		0x31, "Quality of service unavailable", ""
+	},
+	{
+		0x32, "Requested facility not subscribed", ""
+	},
+	{
+		0x35, "Outgoing calls barred within CUG", ""
+	},
+	{
+		0x37, "Incoming calls barred within CUG", ""
+	},
+	{
+		0x39, "Bearer capability not authorized", ""
+	},
+	{
+		0x3a, "Bearer capability not presently available", ""
+	},
+	{
+		0x3e, "Inconsistency in designated outgoing access information and subscriber class ", " "
+	},
+	{
+		0x3f, "Service or option not available, unspecified", ""
+	},
+	{
+		0x41, "Bearer capability not implemented", ""
+	},
+	{
+		0x42, "Channel type not implemented", ""
+	},
+	{
+		0x43, "Requested facility not implemented", ""
+	},
+	{
+		0x44, "Only restricted digital information bearer capability is available", ""
+	},
+	{
+		0x4f, "Service or option not implemented", ""
+	},
+	{
+		0x51, "Invalid call reference value", ""
+	},
+	{
+		0x52, "Identified channel does not exist", ""
+	},
+	{
+		0x53, "A suspended call exists, but this call identity does not", ""
+	},
+	{
+		0x54, "Call identity in use", ""
+	},
+	{
+		0x55, "No call suspended", ""
+	},
+	{
+		0x56, "Call having the requested call identity has been cleared", ""
+	},
+	{
+		0x57, "User not member of CUG", ""
+	},
+	{
+		0x58, "Incompatible destination", ""
+	},
+	{
+		0x5a, "Non-existent CUG", ""
+	},
+	{
+		0x5b, "Invalid transit network selection", ""
+	},
+	{
+		0x5f, "Invalid message, unspecified", ""
+	},
+	{
+		0x60, "Mandatory information element is missing", ""
+	},
+	{
+		0x61, "Message type non-existent or not implemented", ""
+	},
+	{
+		0x62, "Message not compatible with call state or message type non-existent or not implemented ", " "
+	},
+	{
+		0x63, "Information element/parameter non-existent or not implemented", ""
+	},
+	{
+		0x64, "Invalid information element contents", ""
+	},
+	{
+		0x65, "Message not compatible with call state", ""
+	},
+	{
+		0x66, "Recovery on timer expiry", ""
+	},
+	{
+		0x67, "Parameter non-existent or not implemented - passed on", ""
+	},
+	{
+		0x6e, "Message with unrecognized parameter discarded", ""
+	},
+	{
+		0x6f, "Protocol error, unspecified", ""
+	},
+	{
+		0x7f, "Interworking, unspecified", ""
+	},
+};
+
+#define CVSIZE sizeof(cvlist)/sizeof(struct CauseValue)
+
+static
+int
+prcause(char *dest, u_char * p)
+{
+	u_char *end;
+	char *dp = dest;
+	int i, cause;
+
+	end = p + p[1] + 1;
+	p += 2;
+	dp += sprintf(dp, "    coding ");
+	dp += prbits(dp, *p, 7, 2);
+	dp += sprintf(dp, " location ");
+	dp += prbits(dp, *p, 4, 4);
+	*dp++ = '\n';
+	p = skipext(p);
+
+	cause = 0x7f & *p++;
+
+	/* locate cause value */
+	for (i = 0; i < CVSIZE; i++)
+		if (cvlist[i].nr == cause)
+			break;
+
+	/* display cause value if it exists */
+	if (i == CVSIZE)
+		dp += sprintf(dp, "Unknown cause type %x!\n", cause);
+	else
+		dp += sprintf(dp, "  cause value %x : %s \n", cause, cvlist[i].edescr);
+
+	while (!0) {
+		if (p > end)
+			break;
+		dp += sprintf(dp, "    diag attribute %d ", *p++ & 0x7f);
+		dp += sprintf(dp, " rej %d ", *p & 0x7f);
+		if (*p & 0x80) {
+			*dp++ = '\n';
+			break;
+		} else
+			dp += sprintf(dp, " av %d\n", (*++p) & 0x7f);
+	}
+	return (dp - dest);
+
+}
+
+static
+struct MessageType cause_1tr6[] =
+{
+	{CAUSE_InvCRef, "Invalid Call Reference"},
+	{CAUSE_BearerNotImpl, "Bearer Service Not Implemented"},
+	{CAUSE_CIDunknown, "Caller Identity unknown"},
+	{CAUSE_CIDinUse, "Caller Identity in Use"},
+	{CAUSE_NoChans, "No Channels available"},
+	{CAUSE_FacNotImpl, "Facility Not Implemented"},
+	{CAUSE_FacNotSubscr, "Facility Not Subscribed"},
+	{CAUSE_OutgoingBarred, "Outgoing calls barred"},
+	{CAUSE_UserAccessBusy, "User Access Busy"},
+	{CAUSE_NegativeGBG, "Negative GBG"},
+	{CAUSE_UnknownGBG, "Unknown  GBG"},
+	{CAUSE_NoSPVknown, "No SPV known"},
+	{CAUSE_DestNotObtain, "Destination not obtainable"},
+	{CAUSE_NumberChanged, "Number changed"},
+	{CAUSE_OutOfOrder, "Out Of Order"},
+	{CAUSE_NoUserResponse, "No User Response"},
+	{CAUSE_UserBusy, "User Busy"},
+	{CAUSE_IncomingBarred, "Incoming Barred"},
+	{CAUSE_CallRejected, "Call Rejected"},
+	{CAUSE_NetworkCongestion, "Network Congestion"},
+	{CAUSE_RemoteUser, "Remote User initiated"},
+	{CAUSE_LocalProcErr, "Local Procedure Error"},
+	{CAUSE_RemoteProcErr, "Remote Procedure Error"},
+	{CAUSE_RemoteUserSuspend, "Remote User Suspend"},
+	{CAUSE_RemoteUserResumed, "Remote User Resumed"},
+	{CAUSE_UserInfoDiscarded, "User Info Discarded"}
+};
+
+int cause_1tr6_len = (sizeof(cause_1tr6) / sizeof(struct MessageType));
+
+static int
+prcause_1tr6(char *dest, u_char * p)
+{
+	char *dp = dest;
+	int i, cause;
+
+	p++;
+	if (0 == *p) {
+		dp += sprintf(dp, "   OK (cause length=0)\n");
+		return (dp - dest);
+	} else if (*p > 1) {
+		dp += sprintf(dp, "    coding ");
+		dp += prbits(dp, p[2], 7, 2);
+		dp += sprintf(dp, " location ");
+		dp += prbits(dp, p[2], 4, 4);
+		*dp++ = '\n';
+	}
+	p++;
+	cause = 0x7f & *p;
+
+	/* locate cause value */
+	for (i = 0; i < cause_1tr6_len; i++)
+		if (cause_1tr6[i].nr == cause)
+			break;
+
+	/* display cause value if it exists */
+	if (i == cause_1tr6_len)
+		dp += sprintf(dp, "Unknown cause type %x!\n", cause);
+	else
+		dp += sprintf(dp, "  cause value %x : %s \n", cause, cause_1tr6[i].descr);
+
+	return (dp - dest);
+
+}
+
+static int
+prchident(char *dest, u_char * p)
+{
+	char *dp = dest;
+
+	p += 2;
+	dp += sprintf(dp, "    octet 3 ");
+	dp += prbits(dp, *p, 8, 8);
+	*dp++ = '\n';
+	return (dp - dest);
+}
+
+static int
+prcalled(char *dest, u_char * p)
+{
+	int l;
+	char *dp = dest;
+
+	p++;
+	l = *p++ - 1;
+	dp += sprintf(dp, "    octet 3 ");
+	dp += prbits(dp, *p++, 8, 8);
+	*dp++ = '\n';
+	dp += sprintf(dp, "    number digits ");
+	while (l--)
+		*dp++ = *p++;
+	*dp++ = '\n';
+	return (dp - dest);
+}
+static int
+prcalling(char *dest, u_char * p)
+{
+	int l;
+	char *dp = dest;
+
+	p++;
+	l = *p++ - 1;
+	dp += sprintf(dp, "    octet 3 ");
+	dp += prbits(dp, *p, 8, 8);
+	*dp++ = '\n';
+	if (!(*p & 0x80)) {
+		dp += sprintf(dp, "    octet 3a ");
+		dp += prbits(dp, *++p, 8, 8);
+		*dp++ = '\n';
+		l--;
+	};
+	p++;
+
+	dp += sprintf(dp, "    number digits ");
+	while (l--)
+		*dp++ = *p++;
+	*dp++ = '\n';
+	return (dp - dest);
+}
+
+static
+int
+prbearer(char *dest, u_char * p)
+{
+	char *dp = dest, ch;
+
+	p += 2;
+	dp += sprintf(dp, "    octet 3  ");
+	dp += prbits(dp, *p++, 8, 8);
+	*dp++ = '\n';
+	dp += sprintf(dp, "    octet 4  ");
+	dp += prbits(dp, *p, 8, 8);
+	*dp++ = '\n';
+	if ((*p++ & 0x1f) == 0x18) {
+		dp += sprintf(dp, "    octet 4.1 ");
+		dp += prbits(dp, *p++, 8, 8);
+		*dp++ = '\n';
+	}
+	/* check for user information layer 1 */
+	if ((*p & 0x60) == 0x20) {
+		ch = ' ';
+		do {
+			dp += sprintf(dp, "    octet 5%c ", ch);
+			dp += prbits(dp, *p, 8, 8);
+			*dp++ = '\n';
+			if (ch == ' ')
+				ch = 'a';
+			else
+				ch++;
+		}
+		while (!(*p++ & 0x80));
+	}
+	/* check for user information layer 2 */
+	if ((*p & 0x60) == 0x40) {
+		dp += sprintf(dp, "    octet 6  ");
+		dp += prbits(dp, *p++, 8, 8);
+		*dp++ = '\n';
+	}
+	/* check for user information layer 3 */
+	if ((*p & 0x60) == 0x60) {
+		dp += sprintf(dp, "    octet 7  ");
+		dp += prbits(dp, *p++, 8, 8);
+		*dp++ = '\n';
+	}
+	return (dp - dest);
+}
+
+static int
+general(char *dest, u_char * p)
+{
+	char *dp = dest;
+	char ch = ' ';
+	int l, octet = 3;
+
+	p++;
+	l = *p++;
+	/* Iterate over all octets in the information element */
+	while (l--) {
+		dp += sprintf(dp, "    octet %d%c ", octet, ch);
+		dp += prbits(dp, *p++, 8, 8);
+		*dp++ = '\n';
+
+		/* last octet in group? */
+		if (*p & 0x80) {
+			octet++;
+			ch = ' ';
+		} else if (ch == ' ')
+			ch = 'a';
+		else
+			ch++;
+	}
+	return (dp - dest);
+}
+
+static int
+prcharge(char *dest, u_char * p)
+{
+	char *dp = dest;
+	int l;
+
+	p++;
+	l = *p++ - 1;
+	dp += sprintf(dp, "    GEA ");
+	dp += prbits(dp, *p++, 8, 8);
+	dp += sprintf(dp, "  Anzahl: ");
+	/* Iterate over all octets in the * information element */
+	while (l--)
+		*dp++ = *p++;
+	*dp++ = '\n';
+	return (dp - dest);
+}
+static int
+prtext(char *dest, u_char * p)
+{
+	char *dp = dest;
+	int l;
+
+	p++;
+	l = *p++;
+	dp += sprintf(dp, "    ");
+	/* Iterate over all octets in the * information element */
+	while (l--)
+		*dp++ = *p++;
+	*dp++ = '\n';
+	return (dp - dest);
+}
+static int
+display(char *dest, u_char * p)
+{
+	char *dp = dest;
+	char ch = ' ';
+	int l, octet = 3;
+
+	p++;
+	l = *p++;
+	/* Iterate over all octets in the * display-information element */
+	dp += sprintf(dp, "   \"");
+	while (l--) {
+		dp += sprintf(dp, "%c", *p++);
+
+		/* last octet in group? */
+		if (*p & 0x80) {
+			octet++;
+			ch = ' ';
+		} else if (ch == ' ')
+			ch = 'a';
+
+		else
+			ch++;
+	}
+	*dp++ = '\"';
+	*dp++ = '\n';
+	return (dp - dest);
+}
+
+int
+prfacility(char *dest, u_char * p)
+{
+	char *dp = dest;
+	int l, l2;
+
+	p++;
+	l = *p++;
+	dp += sprintf(dp, "    octet 3 ");
+	dp += prbits(dp, *p++, 8, 8);
+	dp += sprintf(dp, "\n");
+	l -= 1;
+
+	while (l > 0) {
+		dp += sprintf(dp, "   octet 4 ");
+		dp += prbits(dp, *p++, 8, 8);
+		dp += sprintf(dp, "\n");
+		dp += sprintf(dp, "   octet 5 %d\n", l2 = *p++ & 0x7f);
+		l -= 2;
+		dp += sprintf(dp, "   contents ");
+		while (l2--) {
+			dp += sprintf(dp, "%2x ", *p++);
+			l--;
+		}
+		dp += sprintf(dp, "\n");
+	}
+
+	return (dp - dest);
+}
+
+static
+struct InformationElement {
+	u_char nr;
+	char *descr;
+	int (*f) (char *, u_char *);
+} ielist[] = {
+
+	{
+		0x00, "Segmented message", general
+	},
+	{
+		0x04, "Bearer capability", prbearer
+	},
+	{
+		0x08, "Cause", prcause
+	},
+	{
+		0x10, "Call identity", general
+	},
+	{
+		0x14, "Call state", general
+	},
+	{
+		0x18, "Channel identification", prchident
+	},
+	{
+		0x1c, "Facility", prfacility
+	},
+	{
+		0x1e, "Progress indicator", general
+	},
+	{
+		0x20, "Network-specific facilities", general
+	},
+	{
+		0x27, "Notification indicator", general
+	},
+	{
+		0x28, "Display", display
+	},
+	{
+		0x29, "Date/Time", general
+	},
+	{
+		0x2c, "Keypad facility", general
+	},
+	{
+		0x34, "Signal", general
+	},
+	{
+		0x40, "Information rate", general
+	},
+	{
+		0x42, "End-to-end delay", general
+	},
+	{
+		0x43, "Transit delay selection and indication", general
+	},
+	{
+		0x44, "Packet layer binary parameters", general
+	},
+	{
+		0x45, "Packet layer window size", general
+	},
+	{
+		0x46, "Packet size", general
+	},
+	{
+		0x47, "Closed user group", general
+	},
+	{
+		0x4a, "Reverse charge indication", general
+	},
+	{
+		0x6c, "Calling party number", prcalling
+	},
+	{
+		0x6d, "Calling party subaddress", general
+	},
+	{
+		0x70, "Called party number", prcalled
+	},
+	{
+		0x71, "Called party subaddress", general
+	},
+	{
+		0x74, "Redirecting number", general
+	},
+	{
+		0x78, "Transit network selection", general
+	},
+	{
+		0x79, "Restart indicator", general
+	},
+	{
+		0x7c, "Low layer compatibility", general
+	},
+	{
+		0x7d, "High layer compatibility", general
+	},
+	{
+		0x7e, "User-user", general
+	},
+	{
+		0x7f, "Escape for extension", general
+	},
+};
+
+
+#define IESIZE sizeof(ielist)/sizeof(struct InformationElement)
+
+static struct InformationElement we_0[] =
+{
+	{WE0_cause, "Cause", prcause_1tr6},
+	{WE0_connAddr, "Connecting Address", prcalled},
+	{WE0_callID, "Call IDentity", general},
+	{WE0_chanID, "Channel IDentity", general},
+	{WE0_netSpecFac, "Network Specific Facility", general},
+	{WE0_display, "Display", general},
+	{WE0_keypad, "Keypad", general},
+	{WE0_origAddr, "Origination Address", prcalled},
+	{WE0_destAddr, "Destination Address", prcalled},
+	{WE0_userInfo, "User Info", general}
+};
+
+static int we_0_len = (sizeof(we_0) / sizeof(struct InformationElement));
+
+static struct InformationElement we_6[] =
+{
+	{WE6_serviceInd, "Service Indicator", general},
+	{WE6_chargingInfo, "Charging Information", prcharge},
+	{WE6_date, "Date", prtext},
+	{WE6_facSelect, "Facility Select", general},
+	{WE6_facStatus, "Facility Status", general},
+	{WE6_statusCalled, "Status Called", general},
+	{WE6_addTransAttr, "Additional Transmission Attributes", general}
+};
+static int we_6_len = (sizeof(we_6) / sizeof(struct InformationElement));
+
+int
+QuickHex(char *txt, u_char * p, int cnt)
+{
+	register int i;
+	register char *t = txt;
+	register u_char w;
+
+	for (i = 0; i < cnt; i++) {
+		*t++ = ' ';
+		w = (p[i] >> 4) & 0x0f;
+		if (w < 10)
+			*t++ = '0' + w;
+		else
+			*t++ = 'A' - 10 + w;
+		w = p[i] & 0x0f;
+		if (w < 10)
+			*t++ = '0' + w;
+		else
+			*t++ = 'A' - 10 + w;
+	}
+	*t++ = 0;
+	return (t - txt);
+}
+
+void
+LogFrame(struct IsdnCardState *sp, u_char * buf, int size)
+{
+	char *dp;
+
+	if (size < 1)
+		return;
+	dp = sp->dlogspace;
+	if (size < 4096 / 3 - 10) {
+		dp += sprintf(dp, "HEX:");
+		dp += QuickHex(dp, buf, size);
+		dp--;
+		*dp++ = '\n';
+		*dp = 0;
+	} else
+		sprintf(dp, "LogFrame: warning Frame too big (%d)\n",
+			size);
+	HiSax_putstatus(sp, sp->dlogspace);
+}
+
+void
+dlogframe(struct IsdnCardState *sp, u_char * buf, int size, char *comment)
+{
+	u_char *bend = buf + size;
+	char *dp;
+	unsigned char pd, cr_l, cr, mt;
+	int i, cs = 0, cs_old = 0, cs_fest = 0;
+
+	if (size < 1)
+		return;
+	/* display header */
+	dp = sp->dlogspace;
+	dp += sprintf(dp, "%s\n", comment);
+
+	if ((0xfe & buf[0]) == PROTO_DIS_N0) {	/* 1TR6 */
+		/* locate message type */
+		pd = *buf++;
+		cr_l = *buf++;
+		if (cr_l)
+			cr = *buf++;
+		else
+			cr = 0;
+		mt = *buf++;
+		if (pd == PROTO_DIS_N0) {	/* N0 */
+			for (i = 0; i < mt_n0_len; i++)
+				if (mt_n0[i].nr == mt)
+					break;
+			/* display message type if it exists */
+			if (i == mt_n0_len)
+				dp += sprintf(dp, "callref %d %s size %d unknown message type N0 %x!\n",
+					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+					      size, mt);
+			else
+				dp += sprintf(dp, "callref %d %s size %d message type %s\n",
+					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+					      size, mt_n0[i].descr);
+		} else {	/* N1 */
+			for (i = 0; i < mt_n1_len; i++)
+				if (mt_n1[i].nr == mt)
+					break;
+			/* display message type if it exists */
+			if (i == mt_n1_len)
+				dp += sprintf(dp, "callref %d %s size %d unknown message type N1 %x!\n",
+					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+					      size, mt);
+			else
+				dp += sprintf(dp, "callref %d %s size %d message type %s\n",
+					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+					      size, mt_n1[i].descr);
+		}
+
+		/* display each information element */
+		while (buf < bend) {
+			/* Is it a single octet information element? */
+			if (*buf & 0x80) {
+				switch ((*buf >> 4) & 7) {
+					case 1:
+						dp += sprintf(dp, "  Shift %x\n", *buf & 0xf);
+						cs_old = cs;
+						cs = *buf & 7;
+						cs_fest = *buf & 8;
+						break;
+					case 3:
+						dp += sprintf(dp, "  Congestion level %x\n", *buf & 0xf);
+						break;
+					case 2:
+						if (*buf == 0xa0) {
+							dp += sprintf(dp, "  More data\n");
+							break;
+						}
+						if (*buf == 0xa1) {
+							dp += sprintf(dp, "  Sending complete\n");
+						}
+						break;
+						/* fall through */
+					default:
+						dp += sprintf(dp, "  Reserved %x\n", *buf);
+						break;
+				}
+				buf++;
+				continue;
+			}
+			/* No, locate it in the table */
+			if (cs == 0) {
+				for (i = 0; i < we_0_len; i++)
+					if (*buf == we_0[i].nr)
+						break;
+
+				/* When found, give appropriate msg */
+				if (i != we_0_len) {
+					dp += sprintf(dp, "  %s\n", we_0[i].descr);
+					dp += we_0[i].f(dp, buf);
+				} else
+					dp += sprintf(dp, "  Codeset %d attribute %x attribute size %d\n", cs, *buf, buf[1]);
+			} else if (cs == 6) {
+				for (i = 0; i < we_6_len; i++)
+					if (*buf == we_6[i].nr)
+						break;
+
+				/* When found, give appropriate msg */
+				if (i != we_6_len) {
+					dp += sprintf(dp, "  %s\n", we_6[i].descr);
+					dp += we_6[i].f(dp, buf);
+				} else
+					dp += sprintf(dp, "  Codeset %d attribute %x attribute size %d\n", cs, *buf, buf[1]);
+			} else
+				dp += sprintf(dp, "  Unknown Codeset %d attribute %x attribute size %d\n", cs, *buf, buf[1]);
+			/* Skip to next element */
+			if (cs_fest == 8) {
+				cs = cs_old;
+				cs_old = 0;
+				cs_fest = 0;
+			}
+			buf += buf[1] + 2;
+		}
+	} else if (buf[0] == 8) {	/* EURO */
+		/* locate message type */
+		buf++;
+		cr_l = *buf++;
+		if (cr_l)
+			cr = *buf++;
+		else
+			cr = 0;
+		mt = *buf++;
+		for (i = 0; i < MTSIZE; i++)
+			if (mtlist[i].nr == mt)
+				break;
+
+		/* display message type if it exists */
+		if (i == MTSIZE)
+			dp += sprintf(dp, "callref %d %s size %d unknown message type %x!\n",
+			    cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+				      size, mt);
+		else
+			dp += sprintf(dp, "callref %d %s size %d message type %s\n",
+			    cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+				      size, mtlist[i].descr);
+
+		/* display each information element */
+		while (buf < bend) {
+			/* Is it a single octet information element? */
+			if (*buf & 0x80) {
+				switch ((*buf >> 4) & 7) {
+					case 1:
+						dp += sprintf(dp, "  Shift %x\n", *buf & 0xf);
+						break;
+					case 3:
+						dp += sprintf(dp, "  Congestion level %x\n", *buf & 0xf);
+						break;
+					case 5:
+						dp += sprintf(dp, "  Repeat indicator %x\n", *buf & 0xf);
+						break;
+					case 2:
+						if (*buf == 0xa0) {
+							dp += sprintf(dp, "  More data\n");
+							break;
+						}
+						if (*buf == 0xa1) {
+							dp += sprintf(dp, "  Sending complete\n");
+						}
+						break;
+						/* fall through */
+					default:
+						dp += sprintf(dp, "  Reserved %x\n", *buf);
+						break;
+				}
+				buf++;
+				continue;
+			}
+			/* No, locate it in the table */
+			for (i = 0; i < IESIZE; i++)
+				if (*buf == ielist[i].nr)
+					break;
+
+			/* When not found, give appropriate msg */
+			if (i != IESIZE) {
+				dp += sprintf(dp, "  %s\n", ielist[i].descr);
+				dp += ielist[i].f(dp, buf);
+			} else
+				dp += sprintf(dp, "  attribute %x attribute size %d\n", *buf, buf[1]);
+
+			/* Skip to next element */
+			buf += buf[1] + 2;
+		}
+	} else {
+		dp += sprintf(dp, "Unknown protocol %x!", buf[0]);
+	}
+	dp += sprintf(dp, "\n");
+	HiSax_putstatus(sp, sp->dlogspace);
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/siemens.h linux/drivers/isdn/hisax/siemens.h
--- v2.0.30/linux/drivers/isdn/hisax/siemens.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/siemens.h	Mon Aug  4 17:34:00 1997
@@ -0,0 +1,71 @@
+/* $Id: siemens.h,v 1.4 1997/01/21 22:24:33 keil Exp $
+ *
+ * siemens.h   ISAC and HSCX spezific Macros
+ *
+ * Author	Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: siemens.h,v $
+ * Revision 1.4  1997/01/21 22:24:33  keil
+ * cleanups
+ *
+ * Revision 1.3  1996/12/08 19:48:34  keil
+ * adding Monitor channel registers
+ *
+ * Revision 1.2  1996/10/27 22:24:00  keil
+ * HSCX version code removed
+ *
+ * Revision 1.1  1996/10/12 21:39:39  keil
+ * Initial revision
+ *
+ *
+*/
+
+
+/* All Registers without FIFOs (original Siemens Spec - 20 hex) */
+
+#define ISAC_MASK 0x0
+#define ISAC_ISTA 0x0
+#define ISAC_STAR 0x1
+#define ISAC_CMDR 0x1
+#define ISAC_EXIR 0x4
+#define ISAC_RBCH 0xa
+#define ISAC_ADF2 0x19
+#define ISAC_SPCR 0x10
+#define ISAC_ADF1 0x18
+#define ISAC_CIR0 0x11
+#define ISAC_CIX0 0x11
+#define ISAC_STCR 0x17
+#define ISAC_MODE 0x2
+#define ISAC_RSTA 0x7
+#define ISAC_RBCL 0x5
+#define ISAC_TIMR 0x3
+#define ISAC_SQXR 0x1b
+#define ISAC_MOSR 0x1a
+#define ISAC_MOCR 0x1a
+#define ISAC_MOR0 0x12
+#define ISAC_MOX0 0x12
+#define ISAC_MOR1 0x14
+#define ISAC_MOX1 0x14
+
+#define HSCX_ISTA 0x0
+#define HSCX_CCR1 0xf
+#define HSCX_CCR2 0xc
+#define HSCX_TSAR 0x11
+#define HSCX_TSAX 0x10
+#define HSCX_XCCR 0x12
+#define HSCX_RCCR 0x13
+#define HSCX_MODE 0x2
+#define HSCX_CMDR 0x1
+#define HSCX_EXIR 0x4
+#define HSCX_XAD1 0x4
+#define HSCX_XAD2 0x5
+#define HSCX_RAH2 0x7
+#define HSCX_RSTA 0x7
+#define HSCX_TIMR 0x3
+#define HSCX_STAR 0x1
+#define HSCX_RBCL 0x5
+#define HSCX_XBCH 0xd
+#define HSCX_VSTR 0xe
+#define HSCX_RLCR 0xe
+#define HSCX_MASK 0x0
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/tei.c linux/drivers/isdn/hisax/tei.c
--- v2.0.30/linux/drivers/isdn/hisax/tei.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/tei.c	Mon Aug  4 17:34:00 1997
@@ -0,0 +1,317 @@
+/* $Id: tei.c,v 1.8 1997/04/07 22:59:08 keil Exp $
+
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *              based on the teles driver from Jan den Ouden
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ * $Log: tei.c,v $
+ * Revision 1.8  1997/04/07 22:59:08  keil
+ * GFP_KERNEL --> GFP_ATOMIC
+ *
+ * Revision 1.7  1997/04/06 22:54:03  keil
+ * Using SKB's
+ *
+ * Revision 1.6  1997/02/09 00:25:12  keil
+ * new interface handling, one interface per card
+ *
+ * Revision 1.5  1997/01/27 15:57:51  keil
+ * cosmetics
+ *
+ * Revision 1.4  1997/01/21 22:32:44  keil
+ * Tei verify request
+ *
+ * Revision 1.3  1997/01/04 13:45:02  keil
+ * cleanup,adding remove tei request (thanks to Sim Yskes)
+ *
+ * Revision 1.2  1996/12/08 19:52:39  keil
+ * minor debug fix
+ *
+ * Revision 1.1  1996/10/13 20:04:57  keil
+ * Initial revision
+ *
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "hisax.h"
+
+extern struct IsdnCard cards[];
+extern int nrcards;
+
+const char *tei_revision = "$Revision: 1.8 $";
+
+static struct PStack *
+findces(struct PStack *st, int ces)
+{
+	struct PStack *ptr = *(st->l1.stlistp);
+
+	while (ptr)
+		if (ptr->l2.ces == ces)
+			return (ptr);
+		else
+			ptr = ptr->next;
+	return (NULL);
+}
+
+static struct PStack *
+findtei(struct PStack *st, int tei)
+{
+	struct PStack *ptr = *(st->l1.stlistp);
+
+	if (tei == 127)
+		return (NULL);
+
+	while (ptr)
+		if (ptr->l2.tei == tei)
+			return (ptr);
+		else
+			ptr = ptr->next;
+	return (NULL);
+}
+
+static void
+mdl_unit_data_res(struct PStack *st, unsigned int ri, u_char mt, u_char ai)
+{
+	struct sk_buff *skb;
+	u_char *bp;
+
+	if (!(skb = alloc_skb(6 + MAX_HEADER_LEN, GFP_ATOMIC))) {
+		printk(KERN_WARNING "HiSax: No skb for TEI manager\n");
+		return;
+	}
+	SET_SKB_FREE(skb);
+	skb_reserve(skb, MAX_HEADER_LEN);
+	bp = skb_put(skb, 5);
+	bp[0] = 0xf;
+	bp[1] = ri >> 8;
+	bp[2] = ri & 0xff;
+	bp[3] = mt;
+	bp[4] = (ai << 1) | 1;
+	st->l3.l3l2(st, DL_UNIT_DATA, skb);
+}
+
+static void
+mdl_unit_data_ind(struct PStack *st, unsigned int ri, u_char mt, u_char ai)
+{
+	unsigned int tces;
+	struct PStack *otsp, *ptr;
+	char tmp[64];
+
+	switch (mt) {
+		case (2):
+			tces = ri;
+			if (st->l3.debug) {
+				sprintf(tmp, "identity assign ces %d ai %d", tces, ai);
+				st->l2.l2m.printdebug(&st->l2.l2m, tmp);
+			}
+			if ((otsp = findces(st, tces))) {
+				if (st->l3.debug) {
+					sprintf(tmp, "ces %d --> tei %d", tces, ai);
+					st->l2.l2m.printdebug(&st->l2.l2m, tmp);
+				}
+				otsp->ma.teil2(otsp, MDL_ASSIGN, (void *) (int) ai);
+			}
+			break;
+		case (3):
+			tces = ri;
+			if (st->l3.debug) {
+				sprintf(tmp, "identity denied for ces %d ai %d", tces, ai);
+				st->l2.l2m.printdebug(&st->l2.l2m, tmp);
+			}
+			if ((otsp = findces(st, tces))) {
+				if (st->l3.debug) {
+					sprintf(tmp, "ces %d denied tei %d", tces, ai);
+					st->l2.l2m.printdebug(&st->l2.l2m, tmp);
+				}
+				otsp->l2.tei = 255;
+				otsp->l2.ces = randomces();
+				otsp->ma.teil2(otsp, MDL_REMOVE, 0);
+			}
+			break;
+		case (4):
+			if (st->l3.debug) {
+				sprintf(tmp, "checking identity for %d", ai);
+				st->l2.l2m.printdebug(&st->l2.l2m, tmp);
+			}
+			if (ai == 0x7f) {
+				ptr = *(st->l1.stlistp);
+				while (ptr) {
+					if ((ptr->l2.tei & 0x7f) != 0x7f) {
+						if (st->l3.debug) {
+							sprintf(tmp, "check response for ces %d with tei %d",
+								ptr->l2.ces, ptr->l2.tei);
+							st->l2.l2m.printdebug(&st->l2.l2m, tmp);
+						}
+						/* send identity check response (user->network) */
+						mdl_unit_data_res(st, ptr->l2.ces, 5, ptr->l2.tei);
+					}
+					ptr = ptr->next;
+				}
+			} else {
+				otsp = findtei(st, ai);
+				if (!otsp)
+					break;
+				if (st->l3.debug) {
+					sprintf(tmp, "check response for ces %d with tei %d",
+					     otsp->l2.ces, otsp->l2.tei);
+					st->l2.l2m.printdebug(&st->l2.l2m, tmp);
+				}
+				/* send identity check response (user->network) */
+				mdl_unit_data_res(st, otsp->l2.ces, 5, otsp->l2.tei);
+			}
+			break;
+		case (6):
+			if (st->l3.debug) {
+				sprintf(tmp, "removal for %d", ai);
+				st->l2.l2m.printdebug(&st->l2.l2m, tmp);
+			}
+			if (ai == 0x7f) {
+				ptr = *(st->l1.stlistp);
+				while (ptr) {
+					if ((ptr->l2.tei & 0x7f) != 0x7f) {
+						if (st->l3.debug) {
+							sprintf(tmp, "rem ces %d with tei %d",
+								ptr->l2.ces, ptr->l2.tei);
+							st->l2.l2m.printdebug(&st->l2.l2m, tmp);
+						}
+						ptr->ma.teil2(ptr, MDL_REMOVE, 0);
+					}
+					ptr = ptr->next;
+				}
+			} else {
+				otsp = findtei(st, ai);
+				if (!otsp)
+					break;
+				if (st->l3.debug) {
+					sprintf(tmp, "rem ces %d with tei %d",
+					     otsp->l2.ces, otsp->l2.tei);
+					st->l2.l2m.printdebug(&st->l2.l2m, tmp);
+				}
+				otsp->ma.teil2(otsp, MDL_REMOVE, 0);
+			}
+			break;
+		default:
+			if (st->l3.debug) {
+				sprintf(tmp, "message unknown %d ai %d", mt, ai);
+				st->l2.l2m.printdebug(&st->l2.l2m, tmp);
+			}
+	}
+}
+
+void
+tei_handler(struct PStack *st,
+	    u_char pr, struct sk_buff *skb)
+{
+	u_char *bp;
+	unsigned int data;
+	char tmp[32];
+
+	switch (pr) {
+		case (MDL_ASSIGN):
+			data = (unsigned int) skb;
+			if (st->l3.debug) {
+				sprintf(tmp, "ces %d assign request", data);
+				st->l2.l2m.printdebug(&st->l2.l2m, tmp);
+			}
+			mdl_unit_data_res(st, data, 1, 127);
+			break;
+		case (MDL_VERIFY):
+			data = (unsigned int) skb;
+			if (st->l3.debug) {
+				sprintf(tmp, "%d id verify request", data);
+				st->l2.l2m.printdebug(&st->l2.l2m, tmp);
+			}
+			mdl_unit_data_res(st, 0, 7, data);
+			break;
+		case (DL_UNIT_DATA):
+			bp = skb->data;
+			if (bp[0] != 0xf) {
+				/* wrong management entity identifier, ignore */
+				/* shouldn't ibh be released??? */
+				printk(KERN_WARNING "tei handler wrong entity id %x\n", bp[0]);
+			} else
+				mdl_unit_data_ind(st, (bp[1] << 8) | bp[2], bp[3], bp[4] >> 1);
+			dev_kfree_skb(skb, FREE_READ);
+			break;
+		default:
+			break;
+	}
+}
+
+unsigned int
+randomces(void)
+{
+	int x = jiffies & 0xffff;
+
+	return (x);
+}
+
+static void
+tei_man(struct PStack *sp, int i, void *v)
+{
+
+	printk(KERN_DEBUG "tei_man\n");
+}
+
+static void
+tei_l2tei(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *sp = st->l1.hardware;
+
+	tei_handler(sp->teistack, pr, arg);
+}
+
+void
+setstack_tei(struct PStack *st)
+{
+	st->l2.l2tei = tei_l2tei;
+}
+
+void
+init_tei(struct IsdnCardState *sp, int protocol)
+{
+	struct PStack *st;
+	char tmp[128];
+
+	st = (struct PStack *) kmalloc(sizeof(struct PStack), GFP_ATOMIC);
+	setstack_HiSax(st, sp);
+	st->l2.extended = !0;
+	st->l2.laptype = LAPD;
+	st->l2.window = 1;
+	st->l2.orig = !0;
+	st->protocol = protocol;
+/*
+ * the following is not necessary for tei mng. (broadcast only)
+ */
+	st->l2.t200 = 500;	/* 500 milliseconds */
+	st->l2.n200 = 4;	/* try 4 times */
+
+	st->l2.sap = 63;
+	st->l2.tei = 127;
+
+	sprintf(tmp, "Card %d tei", sp->cardnr + 1);
+	setstack_isdnl2(st, tmp);
+	st->l2.debug = 0;
+	st->l3.debug = 0;
+
+	st->ma.manl2(st, MDL_NOTEIPROC, NULL);
+
+	st->l2.l2l3 = (void *) tei_handler;
+	st->l1.l1man = tei_man;
+	st->l2.l2man = tei_man;
+	st->l4.l2writewakeup = NULL;
+
+	HiSax_addlist(sp, st);
+	sp->teistack = st;
+}
+
+void
+release_tei(struct IsdnCardState *sp)
+{
+	struct PStack *st = sp->teistack;
+
+	HiSax_rmlist(sp, st);
+	kfree((void *) st);
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/teles0.c linux/drivers/isdn/hisax/teles0.c
--- v2.0.30/linux/drivers/isdn/hisax/teles0.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/teles0.c	Tue Aug  5 09:00:12 1997
@@ -0,0 +1,968 @@
+/* $Id: teles0.c,v 1.8 1997/04/13 19:54:04 keil Exp $
+
+ * teles0.c     low level stuff for Teles Memory IO isdn cards
+ *              based on the teles driver from Jan den Ouden
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *              Beat Doebeli
+ *
+ * $Log: teles0.c,v $
+ * Revision 1.8  1997/04/13 19:54:04  keil
+ * Change in IRQ check delay for SMP
+ *
+ * Revision 1.7  1997/04/06 22:54:04  keil
+ * Using SKB's
+ *
+ * Revision 1.6  1997/01/27 15:52:18  keil
+ * SMP proof,cosmetics
+ *
+ * Revision 1.5  1997/01/21 22:25:59  keil
+ * cleanups
+ *
+ * Revision 1.4  1996/11/05 19:41:27  keil
+ * more changes for 2.1
+ *
+ * Revision 1.3  1996/10/30 10:22:58  keil
+ * Changes for 2.1 kernels
+ *
+ * Revision 1.2  1996/10/27 22:08:34  keil
+ * cosmetic changes
+ *
+ * Revision 1.1  1996/10/13 20:04:58  keil
+ * Initial revision
+ *
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "siemens.h"
+#include "hisax.h"
+#include "teles0.h"
+#include "isdnl1.h"
+#include <linux/kernel_stat.h>
+
+extern const char *CardType[];
+
+const char *teles0_revision = "$Revision: 1.8 $";
+
+#define byteout(addr,val) outb_p(val,addr)
+#define bytein(addr) inb_p(addr)
+
+static inline u_char
+readisac(unsigned int adr, u_char off)
+{
+	return readb(adr + 0x120 + ((off & 1) ? 0x1ff : 0) + off);
+}
+
+static inline void
+writeisac(unsigned int adr, u_char off, u_char data)
+{
+	writeb(data, adr + 0x120 + ((off & 1) ? 0x1ff : 0) + off);
+}
+
+
+static inline u_char
+readhscx(unsigned int adr, int hscx, u_char off)
+{
+	return readb(adr + (hscx ? 0x1e0 : 0x1a0) +
+		     ((off & 1) ? 0x1ff : 0) + off);
+}
+
+static inline void
+writehscx(unsigned int adr, int hscx, u_char off, u_char data)
+{
+	writeb(data, adr + (hscx ? 0x1e0 : 0x1a0) +
+	       ((off & 1) ? 0x1ff : 0) + off);
+}
+
+static inline void
+read_fifo_isac(unsigned int adr, u_char * data, int size)
+{
+	register int i;
+	register u_char *ad = (u_char *) (adr + 0x100);
+	for (i = 0; i < size; i++)
+		data[i] = readb(ad);
+}
+
+static void
+write_fifo_isac(unsigned int adr, u_char * data, int size)
+{
+	register int i;
+	register u_char *ad = (u_char *) (adr + 0x100);
+	for (i = 0; i < size; i++)
+		writeb(data[i], ad);
+}
+
+static inline void
+read_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size)
+{
+	register int i;
+	register u_char *ad = (u_char *) (adr + (hscx ? 0x1c0 : 0x180));
+	for (i = 0; i < size; i++)
+		data[i] = readb(ad);
+}
+
+static inline void
+write_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size)
+{
+	int i;
+	register u_char *ad = (u_char *) (adr + (hscx ? 0x1c0 : 0x180));
+	for (i = 0; i < size; i++)
+		writeb(data[i], ad);
+}
+static inline void
+waitforCEC(int adr, int hscx)
+{
+	int to = 50;
+
+	while ((readhscx(adr, hscx, HSCX_STAR) & 0x04) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "Teles0: waitforCEC timeout\n");
+}
+
+
+static inline void
+waitforXFW(int adr, int hscx)
+{
+	int to = 50;
+
+	while ((!(readhscx(adr, hscx, HSCX_STAR) & 0x44) == 0x40) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "Teles0: waitforXFW timeout\n");
+}
+
+static inline void
+writehscxCMDR(int adr, int hscx, u_char data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	waitforCEC(adr, hscx);
+	writehscx(adr, hscx, HSCX_CMDR, data);
+	restore_flags(flags);
+}
+
+/*
+ * fast interrupt here
+ */
+
+
+static void
+hscxreport(struct IsdnCardState *sp, int hscx)
+{
+	printk(KERN_DEBUG "HSCX %d\n", hscx);
+	printk(KERN_DEBUG "ISTA %x\n", readhscx(sp->membase, hscx, HSCX_ISTA));
+	printk(KERN_DEBUG "STAR %x\n", readhscx(sp->membase, hscx, HSCX_STAR));
+	printk(KERN_DEBUG "EXIR %x\n", readhscx(sp->membase, hscx, HSCX_EXIR));
+}
+
+void
+teles0_report(struct IsdnCardState *sp)
+{
+	printk(KERN_DEBUG "ISAC\n");
+	printk(KERN_DEBUG "ISTA %x\n", readisac(sp->membase, ISAC_ISTA));
+	printk(KERN_DEBUG "STAR %x\n", readisac(sp->membase, ISAC_STAR));
+	printk(KERN_DEBUG "EXIR %x\n", readisac(sp->membase, ISAC_EXIR));
+	hscxreport(sp, 0);
+	hscxreport(sp, 1);
+}
+
+/*
+ * HSCX stuff goes here
+ */
+
+static void
+hscx_empty_fifo(struct HscxState *hsp, int count)
+{
+	u_char *ptr;
+	struct IsdnCardState *sp = hsp->sp;
+	long flags;
+
+	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
+		debugl1(sp, "hscx_empty_fifo");
+
+	if (hsp->rcvidx + count > HSCX_BUFMAX) {
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "hscx_empty_fifo: incoming packet too large");
+		writehscxCMDR(sp->membase, hsp->hscx, 0x80);
+		hsp->rcvidx = 0;
+		return;
+	}
+	ptr = hsp->rcvbuf + hsp->rcvidx;
+	hsp->rcvidx += count;
+	save_flags(flags);
+	cli();
+	read_fifo_hscx(sp->membase, hsp->hscx, ptr, count);
+	writehscxCMDR(sp->membase, hsp->hscx, 0x80);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_HSCX_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "hscx_empty_fifo %c cnt %d",
+			     hsp->hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+hscx_fill_fifo(struct HscxState *hsp)
+{
+	struct IsdnCardState *sp = hsp->sp;
+	int more, count;
+	u_char *ptr;
+	long flags;
+
+	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
+		debugl1(sp, "hscx_fill_fifo");
+
+	if (!hsp->tx_skb)
+		return;
+	if (hsp->tx_skb->len <= 0)
+		return;
+
+	more = (hsp->mode == 1) ? 1 : 0;
+	if (hsp->tx_skb->len > 32) {
+		more = !0;
+		count = 32;
+	} else
+		count = hsp->tx_skb->len;
+
+	waitforXFW(sp->membase, hsp->hscx);
+	save_flags(flags);
+	cli();
+	ptr = hsp->tx_skb->data;
+	skb_pull(hsp->tx_skb, count);
+	hsp->tx_cnt -= count;
+	hsp->count += count;
+	write_fifo_hscx(sp->membase, hsp->hscx, ptr, count);
+	writehscxCMDR(sp->membase, hsp->hscx, more ? 0x8 : 0xa);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_HSCX_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "hscx_fill_fifo %c cnt %d",
+			     hsp->hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static inline void
+hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx)
+{
+	u_char r;
+	struct HscxState *hsp = sp->hs + hscx;
+	struct sk_buff *skb;
+	int count;
+	char tmp[32];
+
+	if (!hsp->init)
+		return;
+
+	if (val & 0x80) {	/* RME */
+
+		r = readhscx(sp->membase, hsp->hscx, HSCX_RSTA);
+		if ((r & 0xf0) != 0xa0) {
+			if (!(r & 0x80))
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "HSCX invalid frame");
+			if ((r & 0x40) && hsp->mode)
+				if (sp->debug & L1_DEB_WARN) {
+					sprintf(tmp, "HSCX RDO mode=%d",
+						hsp->mode);
+					debugl1(sp, tmp);
+				}
+			if (!(r & 0x20))
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "HSCX CRC error");
+			writehscxCMDR(sp->membase, hsp->hscx, 0x80);
+		} else {
+			count = readhscx(sp->membase, hsp->hscx, HSCX_RBCL) & 0x1f;
+			if (count == 0)
+				count = 32;
+			hscx_empty_fifo(hsp, count);
+			if ((count = hsp->rcvidx - 1) > 0) {
+				if (!(skb = dev_alloc_skb(count)))
+					printk(KERN_WARNING "teles0: receive out of memory\n");
+				else {
+					memcpy(skb_put(skb, count), hsp->rcvbuf, count);
+					skb_queue_tail(&hsp->rqueue, skb);
+				}
+			}
+		}
+		hsp->rcvidx = 0;
+		hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		hscx_empty_fifo(hsp, 32);
+		if (hsp->mode == 1) {
+			/* receive audio data */
+			if (!(skb = dev_alloc_skb(32)))
+				printk(KERN_WARNING "teles0: receive out of memory\n");
+			else {
+				memcpy(skb_put(skb, 32), hsp->rcvbuf, 32);
+				skb_queue_tail(&hsp->rqueue, skb);
+			}
+			hsp->rcvidx = 0;
+			hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+		}
+	}
+	if (val & 0x10) {	/* XPR */
+		if (hsp->tx_skb)
+			if (hsp->tx_skb->len) {
+				hscx_fill_fifo(hsp);
+				return;
+			} else {
+				dev_kfree_skb(hsp->tx_skb, FREE_WRITE);
+				hsp->count = 0;
+				if (hsp->st->l4.l1writewakeup)
+					hsp->st->l4.l1writewakeup(hsp->st);
+				hsp->tx_skb = NULL;
+			}
+		if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) {
+			hsp->count = 0;
+			hscx_fill_fifo(hsp);
+		} else
+			hscx_sched_event(hsp, HSCX_XMTBUFREADY);
+	}
+}
+
+/*
+ * ISAC stuff goes here
+ */
+
+static void
+isac_empty_fifo(struct IsdnCardState *sp, int count)
+{
+	u_char *ptr;
+	long flags;
+
+	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
+		debugl1(sp, "isac_empty_fifo");
+
+	if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) {
+		if (sp->debug & L1_DEB_WARN) {
+			char tmp[40];
+			sprintf(tmp, "isac_empty_fifo overrun %d",
+				sp->rcvidx + count);
+			debugl1(sp, tmp);
+		}
+		writeisac(sp->membase, ISAC_CMDR, 0x80);
+		sp->rcvidx = 0;
+		return;
+	}
+	ptr = sp->rcvbuf + sp->rcvidx;
+	sp->rcvidx += count;
+	save_flags(flags);
+	cli();
+	read_fifo_isac(sp->membase, ptr, count);
+	writeisac(sp->membase, ISAC_CMDR, 0x80);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_ISAC_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "isac_empty_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+isac_fill_fifo(struct IsdnCardState *sp)
+{
+	int count, more;
+	u_char *ptr;
+	long flags;
+
+	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
+		debugl1(sp, "isac_fill_fifo");
+
+	if (!sp->tx_skb)
+		return;
+
+	count = sp->tx_skb->len;
+	if (count <= 0)
+		return;
+
+	more = 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	save_flags(flags);
+	cli();
+	ptr = sp->tx_skb->data;
+	skb_pull(sp->tx_skb, count);
+	sp->tx_cnt += count;
+	write_fifo_isac(sp->membase, ptr, count);
+	writeisac(sp->membase, ISAC_CMDR, more ? 0x8 : 0xa);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_ISAC_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "isac_fill_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+ph_command(struct IsdnCardState *sp, unsigned int command)
+{
+	if (sp->debug & L1_DEB_ISAC) {
+		char tmp[32];
+		sprintf(tmp, "ph_command %d", command);
+		debugl1(sp, tmp);
+	}
+	writeisac(sp->membase, ISAC_CIX0, (command << 2) | 3);
+}
+
+static inline void
+isac_interrupt(struct IsdnCardState *sp, u_char val)
+{
+	u_char exval;
+	struct sk_buff *skb;
+	unsigned int count;
+	char tmp[32];
+
+	if (sp->debug & L1_DEB_ISAC) {
+		sprintf(tmp, "ISAC interrupt %x", val);
+		debugl1(sp, tmp);
+	}
+	if (val & 0x80) {	/* RME */
+		exval = readisac(sp->membase, ISAC_RSTA);
+		if ((exval & 0x70) != 0x20) {
+			if (exval & 0x40)
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "ISAC RDO");
+			if (!(exval & 0x20))
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "ISAC CRC error");
+			writeisac(sp->membase, ISAC_CMDR, 0x80);
+		} else {
+			count = readisac(sp->membase, ISAC_RBCL) & 0x1f;
+			if (count == 0)
+				count = 32;
+			isac_empty_fifo(sp, count);
+			if ((count = sp->rcvidx) > 0) {
+				if (!(skb = alloc_skb(count, GFP_ATOMIC)))
+					printk(KERN_WARNING "teles0: D receive out of memory\n");
+				else {
+					SET_SKB_FREE(skb);
+					memcpy(skb_put(skb, count), sp->rcvbuf, count);
+					skb_queue_tail(&sp->rq, skb);
+				}
+			}
+		}
+		sp->rcvidx = 0;
+		isac_sched_event(sp, ISAC_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		isac_empty_fifo(sp, 32);
+	}
+	if (val & 0x20) {	/* RSC */
+		/* never */
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "ISAC RSC interrupt");
+	}
+	if (val & 0x10) {	/* XPR */
+		if (sp->tx_skb)
+			if (sp->tx_skb->len) {
+				isac_fill_fifo(sp);
+				goto afterXPR;
+			} else {
+				dev_kfree_skb(sp->tx_skb, FREE_WRITE);
+				sp->tx_cnt = 0;
+				sp->tx_skb = NULL;
+			}
+		if ((sp->tx_skb = skb_dequeue(&sp->sq))) {
+			sp->tx_cnt = 0;
+			isac_fill_fifo(sp);
+		} else
+			isac_sched_event(sp, ISAC_XMTBUFREADY);
+	}
+      afterXPR:
+	if (val & 0x04) {	/* CISQ */
+		sp->ph_state = (readisac(sp->membase, ISAC_CIX0) >> 2)
+		    & 0xf;
+		if (sp->debug & L1_DEB_ISAC) {
+			sprintf(tmp, "l1state %d", sp->ph_state);
+			debugl1(sp, tmp);
+		}
+		isac_new_ph(sp);
+	}
+	if (val & 0x02) {	/* SIN */
+		/* never */
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "ISAC SIN interrupt");
+	}
+	if (val & 0x01) {	/* EXI */
+		exval = readisac(sp->membase, ISAC_EXIR);
+		if (sp->debug & L1_DEB_WARN) {
+			sprintf(tmp, "ISAC EXIR %02x", exval);
+			debugl1(sp, tmp);
+		}
+	}
+}
+
+static inline void
+hscx_int_main(struct IsdnCardState *sp, u_char val)
+{
+
+	u_char exval;
+	struct HscxState *hsp;
+	char tmp[32];
+
+
+	if (val & 0x01) {
+		hsp = sp->hs + 1;
+		exval = readhscx(sp->membase, 1, HSCX_EXIR);
+		if (exval == 0x40) {
+			if (hsp->mode == 1)
+				hscx_fill_fifo(hsp);
+			else {
+				/* Here we lost an TX interrupt, so
+				   * restart transmitting the whole frame.
+				 */
+				if (hsp->tx_skb) {
+					skb_push(hsp->tx_skb, hsp->count);
+					hsp->tx_cnt += hsp->count;
+					hsp->count = 0;
+				}
+				writehscxCMDR(sp->membase, hsp->hscx, 0x01);
+				if (sp->debug & L1_DEB_WARN) {
+					sprintf(tmp, "HSCX B EXIR %x Lost TX", exval);
+					debugl1(sp, tmp);
+				}
+			}
+		} else if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX B EXIR %x", exval);
+			debugl1(sp, tmp);
+		}
+	}
+	if (val & 0xf8) {
+		if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX B interrupt %x", val);
+			debugl1(sp, tmp);
+		}
+		hscx_interrupt(sp, val, 1);
+	}
+	if (val & 0x02) {
+		hsp = sp->hs;
+		exval = readhscx(sp->membase, 0, HSCX_EXIR);
+		if (exval == 0x40) {
+			if (hsp->mode == 1)
+				hscx_fill_fifo(hsp);
+			else {
+				/* Here we lost an TX interrupt, so
+				   * restart transmitting the whole frame.
+				 */
+				if (hsp->tx_skb) {
+					skb_push(hsp->tx_skb, hsp->count);
+					hsp->tx_cnt += hsp->count;
+					hsp->count = 0;
+				}
+				writehscxCMDR(sp->membase, hsp->hscx, 0x01);
+				if (sp->debug & L1_DEB_WARN) {
+					sprintf(tmp, "HSCX A EXIR %x Lost TX", exval);
+					debugl1(sp, tmp);
+				}
+			}
+		} else if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX A EXIR %x", exval);
+			debugl1(sp, tmp);
+		}
+	}
+	if (val & 0x04) {
+		exval = readhscx(sp->membase, 0, HSCX_ISTA);
+		if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX A interrupt %x", exval);
+			debugl1(sp, tmp);
+		}
+		hscx_interrupt(sp, exval, 0);
+	}
+}
+
+static void
+telesS0_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *sp;
+	u_char val, stat = 0;
+
+	sp = (struct IsdnCardState *) irq2dev_map[intno];
+
+	if (!sp) {
+		printk(KERN_WARNING "Teles0: Spurious interrupt!\n");
+		return;
+	}
+	val = readhscx(sp->membase, 1, HSCX_ISTA);
+      Start_HSCX:
+	if (val) {
+		hscx_int_main(sp, val);
+		stat |= 1;
+	}
+	val = readisac(sp->membase, ISAC_ISTA);
+      Start_ISAC:
+	if (val) {
+		isac_interrupt(sp, val);
+		stat |= 2;
+	}
+	val = readhscx(sp->membase, 1, HSCX_ISTA);
+	if (val) {
+		if (sp->debug & L1_DEB_HSCX)
+			debugl1(sp, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readisac(sp->membase, ISAC_ISTA);
+	if (val) {
+		if (sp->debug & L1_DEB_ISAC)
+			debugl1(sp, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (stat & 1) {
+		writehscx(sp->membase, 0, HSCX_MASK, 0xFF);
+		writehscx(sp->membase, 1, HSCX_MASK, 0xFF);
+		writehscx(sp->membase, 0, HSCX_MASK, 0x0);
+		writehscx(sp->membase, 1, HSCX_MASK, 0x0);
+	}
+	if (stat & 2) {
+		writeisac(sp->membase, ISAC_MASK, 0xFF);
+		writeisac(sp->membase, ISAC_MASK, 0x0);
+	}
+}
+
+
+static void
+initisac(struct IsdnCardState *sp)
+{
+	unsigned int adr = sp->membase;
+
+	/* 16.0 IOM 1 Mode */
+	writeisac(adr, ISAC_MASK, 0xff);
+	writeisac(adr, ISAC_ADF2, 0x0);
+	writeisac(adr, ISAC_SPCR, 0xa);
+	writeisac(adr, ISAC_ADF1, 0x2);
+	writeisac(adr, ISAC_STCR, 0x70);
+	writeisac(adr, ISAC_MODE, 0xc9);
+	writeisac(adr, ISAC_CMDR, 0x41);
+	writeisac(adr, ISAC_CIX0, (1 << 2) | 3);
+	writeisac(adr, ISAC_MASK, 0xff);
+	writeisac(adr, ISAC_MASK, 0x0);
+}
+
+static void
+modehscx(struct HscxState *hs, int mode, int ichan)
+{
+	struct IsdnCardState *sp = hs->sp;
+	int hscx = hs->hscx;
+
+	if (sp->debug & L1_DEB_HSCX) {
+		char tmp[40];
+		sprintf(tmp, "hscx %c mode %d ichan %d",
+			'A' + hscx, mode, ichan);
+		debugl1(sp, tmp);
+	}
+	hs->mode = mode;
+	writehscx(sp->membase, hscx, HSCX_CCR1, 0x85);
+	writehscx(sp->membase, hscx, HSCX_XAD1, 0xFF);
+	writehscx(sp->membase, hscx, HSCX_XAD2, 0xFF);
+	writehscx(sp->membase, hscx, HSCX_RAH2, 0xFF);
+	writehscx(sp->membase, hscx, HSCX_XBCH, 0x0);
+
+	/* Switch IOM 1 SSI */
+	if (hscx == 0)
+		ichan = 1 - ichan;
+
+	switch (mode) {
+		case (0):
+			writehscx(sp->membase, hscx, HSCX_CCR2, 0x30);
+			writehscx(sp->membase, hscx, HSCX_TSAX, 0xff);
+			writehscx(sp->membase, hscx, HSCX_TSAR, 0xff);
+			writehscx(sp->membase, hscx, HSCX_XCCR, 7);
+			writehscx(sp->membase, hscx, HSCX_RCCR, 7);
+			writehscx(sp->membase, hscx, HSCX_MODE, 0x84);
+			break;
+		case (1):
+			if (ichan == 0) {
+				writehscx(sp->membase, hscx, HSCX_CCR2, 0x30);
+				writehscx(sp->membase, hscx, HSCX_TSAX, 0x7);
+				writehscx(sp->membase, hscx, HSCX_TSAR, 0x7);
+				writehscx(sp->membase, hscx, HSCX_XCCR, 7);
+				writehscx(sp->membase, hscx, HSCX_RCCR, 7);
+			} else {
+				writehscx(sp->membase, hscx, HSCX_CCR2, 0x30);
+				writehscx(sp->membase, hscx, HSCX_TSAX, 0x3);
+				writehscx(sp->membase, hscx, HSCX_TSAR, 0x3);
+				writehscx(sp->membase, hscx, HSCX_XCCR, 7);
+				writehscx(sp->membase, hscx, HSCX_RCCR, 7);
+			}
+			writehscx(sp->membase, hscx, HSCX_MODE, 0xe4);
+			writehscx(sp->membase, hscx, HSCX_CMDR, 0x41);
+			break;
+		case (2):
+			if (ichan == 0) {
+				writehscx(sp->membase, hscx, HSCX_CCR2, 0x30);
+				writehscx(sp->membase, hscx, HSCX_TSAX, 0x7);
+				writehscx(sp->membase, hscx, HSCX_TSAR, 0x7);
+				writehscx(sp->membase, hscx, HSCX_XCCR, 7);
+				writehscx(sp->membase, hscx, HSCX_RCCR, 7);
+			} else {
+				writehscx(sp->membase, hscx, HSCX_CCR2, 0x30);
+				writehscx(sp->membase, hscx, HSCX_TSAX, 0x3);
+				writehscx(sp->membase, hscx, HSCX_TSAR, 0x3);
+				writehscx(sp->membase, hscx, HSCX_XCCR, 7);
+				writehscx(sp->membase, hscx, HSCX_RCCR, 7);
+			}
+			writehscx(sp->membase, hscx, HSCX_MODE, 0x8c);
+			writehscx(sp->membase, hscx, HSCX_CMDR, 0x41);
+			break;
+	}
+	writehscx(sp->membase, hscx, HSCX_ISTA, 0x00);
+}
+
+void
+release_io_teles0(struct IsdnCard *card)
+{
+	if (card->sp->cfg_reg)
+		release_region(card->sp->cfg_reg, 8);
+}
+
+static void
+clear_pending_ints(struct IsdnCardState *sp)
+{
+	int val;
+	char tmp[64];
+
+	val = readhscx(sp->membase, 1, HSCX_ISTA);
+	sprintf(tmp, "HSCX B ISTA %x", val);
+	debugl1(sp, tmp);
+	if (val & 0x01) {
+		val = readhscx(sp->membase, 1, HSCX_EXIR);
+		sprintf(tmp, "HSCX B EXIR %x", val);
+		debugl1(sp, tmp);
+	} else if (val & 0x02) {
+		val = readhscx(sp->membase, 0, HSCX_EXIR);
+		sprintf(tmp, "HSCX A EXIR %x", val);
+		debugl1(sp, tmp);
+	}
+	val = readhscx(sp->membase, 0, HSCX_ISTA);
+	sprintf(tmp, "HSCX A ISTA %x", val);
+	debugl1(sp, tmp);
+	val = readhscx(sp->membase, 1, HSCX_STAR);
+	sprintf(tmp, "HSCX B STAR %x", val);
+	debugl1(sp, tmp);
+	val = readhscx(sp->membase, 0, HSCX_STAR);
+	sprintf(tmp, "HSCX A STAR %x", val);
+	debugl1(sp, tmp);
+	val = readisac(sp->membase, ISAC_STAR);
+	sprintf(tmp, "ISAC STAR %x", val);
+	debugl1(sp, tmp);
+	val = readisac(sp->membase, ISAC_MODE);
+	sprintf(tmp, "ISAC MODE %x", val);
+	debugl1(sp, tmp);
+	val = readisac(sp->membase, ISAC_ADF2);
+	sprintf(tmp, "ISAC ADF2 %x", val);
+	debugl1(sp, tmp);
+	val = readisac(sp->membase, ISAC_ISTA);
+	sprintf(tmp, "ISAC ISTA %x", val);
+	debugl1(sp, tmp);
+	if (val & 0x01) {
+		val = readisac(sp->membase, ISAC_EXIR);
+		sprintf(tmp, "ISAC EXIR %x", val);
+		debugl1(sp, tmp);
+	} else if (val & 0x04) {
+		val = readisac(sp->membase, ISAC_CIR0);
+		sprintf(tmp, "ISAC CIR0 %x", val);
+		debugl1(sp, tmp);
+	}
+	writeisac(sp->membase, ISAC_MASK, 0);
+	writeisac(sp->membase, ISAC_CMDR, 0x41);
+}
+
+int
+initteles0(struct IsdnCardState *sp)
+{
+	int ret;
+	int loop = 0;
+	char tmp[40];
+
+	sp->counter = kstat.interrupts[sp->irq];
+	sprintf(tmp, "IRQ %d count %d", sp->irq, sp->counter);
+	debugl1(sp, tmp);
+	clear_pending_ints(sp);
+	ret = get_irq(sp->cardnr, &telesS0_interrupt);
+	if (ret) {
+		initisac(sp);
+		sp->modehscx(sp->hs, 0, 0);
+		sp->modehscx(sp->hs + 1, 0, 0);
+		while (loop++ < 10) {
+			/* At least 1-3 irqs must happen
+			 * (one from HSCX A, one from HSCX B, 3rd from ISAC)
+			 */
+			if (kstat.interrupts[sp->irq] > sp->counter)
+				break;
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + 1;
+			schedule();
+		}
+		sprintf(tmp, "IRQ %d count %d", sp->irq,
+			kstat.interrupts[sp->irq]);
+		debugl1(sp, tmp);
+		if (kstat.interrupts[sp->irq] == sp->counter) {
+			printk(KERN_WARNING
+			       "Teles0: IRQ(%d) getting no interrupts during init\n",
+			       sp->irq);
+			irq2dev_map[sp->irq] = NULL;
+			free_irq(sp->irq, NULL);
+			return (0);
+		}
+	}
+	return (ret);
+}
+
+int
+setup_teles0(struct IsdnCard *card)
+{
+	u_char cfval, val, verA, verB;
+	struct IsdnCardState *sp = card->sp;
+	long flags;
+	char tmp[64];
+
+	strcpy(tmp, teles0_revision);
+	printk(KERN_NOTICE "HiSax: Teles 8.0/16.0 driver Rev. %s\n", HiSax_getrev(tmp));
+	if ((sp->typ != ISDN_CTYPE_16_0) && (sp->typ != ISDN_CTYPE_8_0))
+		return (0);
+
+	if (sp->typ == ISDN_CTYPE_16_0)
+		sp->cfg_reg = card->para[2];
+	else			/* 8.0 */
+		sp->cfg_reg = 0;
+
+	if (card->para[1] < 0x10000) {
+		card->para[1] <<= 4;
+		printk(KERN_INFO
+		   "Teles0: membase configured DOSish, assuming 0x%lx\n",
+		       (unsigned long) card->para[1]);
+	}
+	sp->membase = card->para[1];
+	sp->irq = card->para[0];
+	if (sp->cfg_reg) {
+		if (check_region((sp->cfg_reg), 8)) {
+			printk(KERN_WARNING
+			  "HiSax: %s config port %x-%x already in use\n",
+			       CardType[card->typ],
+			       sp->cfg_reg,
+			       sp->cfg_reg + 8);
+			return (0);
+		} else {
+			request_region(sp->cfg_reg, 8, "teles cfg");
+		}
+	}
+	switch (sp->irq) {
+		case 2:
+			cfval = 0x00;
+			break;
+		case 3:
+			cfval = 0x02;
+			break;
+		case 4:
+			cfval = 0x04;
+			break;
+		case 5:
+			cfval = 0x06;
+			break;
+		case 10:
+			cfval = 0x08;
+			break;
+		case 11:
+			cfval = 0x0A;
+			break;
+		case 12:
+			cfval = 0x0C;
+			break;
+		case 15:
+			cfval = 0x0E;
+			break;
+		default:
+			cfval = 0x00;
+			break;
+	}
+	cfval |= ((card->para[1] >> 9) & 0xF0);
+	if (sp->cfg_reg) {
+		if ((val = bytein(sp->cfg_reg + 0)) != 0x51) {
+			printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n",
+			       sp->cfg_reg + 0, val);
+			release_region(sp->cfg_reg, 8);
+			return (0);
+		}
+		if ((val = bytein(sp->cfg_reg + 1)) != 0x93) {
+			printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n",
+			       sp->cfg_reg + 1, val);
+			release_region(sp->cfg_reg, 8);
+			return (0);
+		}
+		val = bytein(sp->cfg_reg + 2);	/* 0x1e=without AB
+						   * 0x1f=with AB
+						   * 0x1c 16.3 ???
+						 */
+		if (val != 0x1e && val != 0x1f) {
+			printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n",
+			       sp->cfg_reg + 2, val);
+			release_region(sp->cfg_reg, 8);
+			return (0);
+		}
+		save_flags(flags);
+		byteout(sp->cfg_reg + 4, cfval);
+		sti();
+		HZDELAY(HZ / 10 + 1);
+		byteout(sp->cfg_reg + 4, cfval | 1);
+		HZDELAY(HZ / 10 + 1);
+		restore_flags(flags);
+	}
+	printk(KERN_NOTICE
+	       "HiSax: %s config irq:%d mem:%x cfg:%x\n",
+	       CardType[sp->typ], sp->irq,
+	       sp->membase, sp->cfg_reg);
+	verA = readhscx(sp->membase, 0, HSCX_VSTR) & 0xf;
+	verB = readhscx(sp->membase, 1, HSCX_VSTR) & 0xf;
+	printk(KERN_INFO "Teles0: HSCX version A: %s  B: %s\n",
+	       HscxVersion(verA), HscxVersion(verB));
+	val = readisac(sp->membase, ISAC_RBCH);
+	printk(KERN_INFO "Teles0: ISAC %s\n",
+	       ISACVersion(val));
+
+	if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) {
+		printk(KERN_WARNING
+		 "Teles0: wrong HSCX versions check IO/MEM addresses\n");
+		release_io_teles0(card);
+		return (0);
+	}
+	save_flags(flags);
+	writeb(0, sp->membase + 0x80);
+	sti();
+	HZDELAY(HZ / 5 + 1);
+	writeb(1, sp->membase + 0x80);
+	HZDELAY(HZ / 5 + 1);
+	restore_flags(flags);
+
+	sp->modehscx = &modehscx;
+	sp->ph_command = &ph_command;
+	sp->hscx_fill_fifo = &hscx_fill_fifo;
+	sp->isac_fill_fifo = &isac_fill_fifo;
+	return (1);
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/teles0.h linux/drivers/isdn/hisax/teles0.h
--- v2.0.30/linux/drivers/isdn/hisax/teles0.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/teles0.h	Mon Aug  4 17:34:00 1997
@@ -0,0 +1,21 @@
+/* $Id: teles0.h,v 1.2 1997/01/21 22:26:52 keil Exp $
+ *
+ * teles0.h   Header for Teles 16.0 8.0 & compatible
+ *
+ * Author	Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: teles0.h,v $
+ * Revision 1.2  1997/01/21 22:26:52  keil
+ * cleanups
+ *
+ * Revision 1.1  1996/10/13 20:03:48  keil
+ * Initial revision
+ *
+ *
+*/
+
+extern	void teles0_report(struct IsdnCardState *sp);
+extern  void release_io_teles0(struct IsdnCard *card);
+extern	int  setup_teles0(struct IsdnCard *card);
+extern  int  initteles0(struct IsdnCardState *sp);
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/teles3.c linux/drivers/isdn/hisax/teles3.c
--- v2.0.30/linux/drivers/isdn/hisax/teles3.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/teles3.c	Tue Aug  5 09:00:12 1997
@@ -0,0 +1,1034 @@
+/* $Id: teles3.c,v 1.11 1997/04/13 19:54:05 keil Exp $
+
+ * teles3.c     low level stuff for Teles 16.3 & PNP isdn cards
+ *
+ *              based on the teles driver from Jan den Ouden
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *              Beat Doebeli
+ *
+ * $Log: teles3.c,v $
+ * Revision 1.11  1997/04/13 19:54:05  keil
+ * Change in IRQ check delay for SMP
+ *
+ * Revision 1.10  1997/04/06 22:54:05  keil
+ * Using SKB's
+ *
+ * Revision 1.9  1997/03/22 02:01:07  fritz
+ * -Reworked toplevel Makefile. From now on, no different Makefiles
+ *  for standalone- and in-kernel-compilation are needed any more.
+ * -Added local Rules.make for above reason.
+ * -Experimental changes in teles3.c for enhanced IRQ-checking with
+ *  2.1.X and SMP kernels.
+ * -Removed diffstd-script, same functionality is in stddiff -r.
+ * -Enhanced scripts std2kern and stddiff.
+ *
+ * Revision 1.8  1997/02/23 18:43:55  fritz
+ * Added support for Teles-Vision.
+ *
+ * Revision 1.7  1997/01/28 22:48:33  keil
+ * fixes for Teles PCMCIA (Christof Petig)
+ *
+ * Revision 1.6  1997/01/27 15:52:55  keil
+ * SMP proof,cosmetics, PCMCIA added
+ *
+ * Revision 1.5  1997/01/21 22:28:32  keil
+ * cleanups
+ *
+ * Revision 1.4  1996/12/14 21:05:41  keil
+ * Reset for 16.3 PnP
+ *
+ * Revision 1.3  1996/11/05 19:56:54  keil
+ * debug output fixed
+ *
+ * Revision 1.2  1996/10/27 22:09:15  keil
+ * cosmetic changes
+ *
+ * Revision 1.1  1996/10/13 20:04:59  keil
+ * Initial revision
+ *
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "siemens.h"
+#include "hisax.h"
+#include "teles3.h"
+#include "isdnl1.h"
+#include <linux/kernel_stat.h>
+
+extern const char *CardType[];
+const char *teles3_revision = "$Revision: 1.11 $";
+
+#define byteout(addr,val) outb_p(val,addr)
+#define bytein(addr) inb_p(addr)
+
+static inline u_char
+readreg(unsigned int adr, u_char off)
+{
+	return (bytein(adr + off));
+}
+
+static inline void
+writereg(unsigned int adr, u_char off, u_char data)
+{
+	byteout(adr + off, data);
+}
+
+
+static inline void
+read_fifo(unsigned int adr, u_char * data, int size)
+{
+	insb(adr + 0x1e, data, size);
+}
+
+static void
+write_fifo(unsigned int adr, u_char * data, int size)
+{
+	outsb(adr + 0x1e, data, size);
+}
+
+static inline void
+waitforCEC(int adr)
+{
+	int to = 50;
+
+	while ((readreg(adr, HSCX_STAR) & 0x04) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "Teles3: waitforCEC timeout\n");
+}
+
+
+static inline void
+waitforXFW(int adr)
+{
+	int to = 50;
+
+	while ((!(readreg(adr, HSCX_STAR) & 0x44) == 0x40) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "Teles3: waitforXFW timeout\n");
+}
+static inline void
+writehscxCMDR(int adr, u_char data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	waitforCEC(adr);
+	writereg(adr, HSCX_CMDR, data);
+	restore_flags(flags);
+}
+
+/*
+ * fast interrupt here
+ */
+
+static void
+hscxreport(struct IsdnCardState *sp, int hscx)
+{
+	printk(KERN_DEBUG "HSCX %d\n", hscx);
+	printk(KERN_DEBUG "ISTA %x\n", readreg(sp->hscx[hscx], HSCX_ISTA));
+	printk(KERN_DEBUG "STAR %x\n", readreg(sp->hscx[hscx], HSCX_STAR));
+	printk(KERN_DEBUG "EXIR %x\n", readreg(sp->hscx[hscx], HSCX_EXIR));
+}
+
+void
+teles3_report(struct IsdnCardState *sp)
+{
+	printk(KERN_DEBUG "ISAC\n");
+	printk(KERN_DEBUG "ISTA %x\n", readreg(sp->isac, ISAC_ISTA));
+	printk(KERN_DEBUG "STAR %x\n", readreg(sp->isac, ISAC_STAR));
+	printk(KERN_DEBUG "EXIR %x\n", readreg(sp->isac, ISAC_EXIR));
+	hscxreport(sp, 0);
+	hscxreport(sp, 1);
+}
+
+/*
+ * HSCX stuff goes here
+ */
+
+static void
+hscx_empty_fifo(struct HscxState *hsp, int count)
+{
+	u_char *ptr;
+	struct IsdnCardState *sp = hsp->sp;
+	long flags;
+
+	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
+		debugl1(sp, "hscx_empty_fifo");
+
+	if (hsp->rcvidx + count > HSCX_BUFMAX) {
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "hscx_empty_fifo: incoming packet too large");
+		writehscxCMDR(sp->hscx[hsp->hscx], 0x80);
+		hsp->rcvidx = 0;
+		return;
+	}
+	ptr = hsp->rcvbuf + hsp->rcvidx;
+	hsp->rcvidx += count;
+	save_flags(flags);
+	cli();
+	read_fifo(sp->hscx[hsp->hscx], ptr, count);
+	writehscxCMDR(sp->hscx[hsp->hscx], 0x80);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_HSCX_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "hscx_empty_fifo %c cnt %d",
+			     hsp->hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+hscx_fill_fifo(struct HscxState *hsp)
+{
+	struct IsdnCardState *sp = hsp->sp;
+	int more, count;
+	u_char *ptr;
+	long flags;
+
+	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
+		debugl1(sp, "hscx_fill_fifo");
+
+	if (!hsp->tx_skb)
+		return;
+	if (hsp->tx_skb->len <= 0)
+		return;
+
+	more = (hsp->mode == 1) ? 1 : 0;
+	if (hsp->tx_skb->len > 32) {
+		more = !0;
+		count = 32;
+	} else
+		count = hsp->tx_skb->len;
+
+	waitforXFW(sp->hscx[hsp->hscx]);
+	save_flags(flags);
+	cli();
+	ptr = hsp->tx_skb->data;
+	skb_pull(hsp->tx_skb, count);
+	hsp->tx_cnt -= count;
+	hsp->count += count;
+	write_fifo(sp->hscx[hsp->hscx], ptr, count);
+	writehscxCMDR(sp->hscx[hsp->hscx], more ? 0x8 : 0xa);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_HSCX_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "hscx_fill_fifo %c cnt %d",
+			     hsp->hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static inline void
+hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx)
+{
+	u_char r;
+	struct HscxState *hsp = sp->hs + hscx;
+	struct sk_buff *skb;
+	int count;
+	char tmp[32];
+
+	if (!hsp->init)
+		return;
+
+	if (val & 0x80) {	/* RME */
+
+		r = readreg(sp->hscx[hsp->hscx], HSCX_RSTA);
+		if ((r & 0xf0) != 0xa0) {
+			if (!(r & 0x80))
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "HSCX invalid frame");
+			if ((r & 0x40) && hsp->mode)
+				if (sp->debug & L1_DEB_WARN) {
+					sprintf(tmp, "HSCX RDO mode=%d",
+						hsp->mode);
+					debugl1(sp, tmp);
+				}
+			if (!(r & 0x20))
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "HSCX CRC error");
+			writehscxCMDR(sp->hscx[hsp->hscx], 0x80);
+		} else {
+			count = readreg(sp->hscx[hsp->hscx], HSCX_RBCL) & 0x1f;
+			if (count == 0)
+				count = 32;
+			hscx_empty_fifo(hsp, count);
+			if ((count = hsp->rcvidx - 1) > 0) {
+				if (!(skb = dev_alloc_skb(count)))
+					printk(KERN_WARNING "teles3: receive out of memory\n");
+				else {
+					memcpy(skb_put(skb, count), hsp->rcvbuf, count);
+					skb_queue_tail(&hsp->rqueue, skb);
+				}
+			}
+		}
+		hsp->rcvidx = 0;
+		hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		hscx_empty_fifo(hsp, 32);
+		if (hsp->mode == 1) {
+			/* receive audio data */
+			if (!(skb = dev_alloc_skb(32)))
+				printk(KERN_WARNING "teles3: receive out of memory\n");
+			else {
+				memcpy(skb_put(skb, 32), hsp->rcvbuf, 32);
+				skb_queue_tail(&hsp->rqueue, skb);
+			}
+			hsp->rcvidx = 0;
+			hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+		}
+	}
+	if (val & 0x10) {	/* XPR */
+		if (hsp->tx_skb)
+			if (hsp->tx_skb->len) {
+				hscx_fill_fifo(hsp);
+				return;
+			} else {
+				dev_kfree_skb(hsp->tx_skb, FREE_WRITE);
+				hsp->count = 0;
+				if (hsp->st->l4.l1writewakeup)
+					hsp->st->l4.l1writewakeup(hsp->st);
+				hsp->tx_skb = NULL;
+			}
+		if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) {
+			hsp->count = 0;
+			hscx_fill_fifo(hsp);
+		} else
+			hscx_sched_event(hsp, HSCX_XMTBUFREADY);
+	}
+}
+
+/*
+ * ISAC stuff goes here
+ */
+
+static void
+isac_empty_fifo(struct IsdnCardState *sp, int count)
+{
+	u_char *ptr;
+	long flags;
+
+	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
+		if (sp->debug & L1_DEB_ISAC)
+			debugl1(sp, "isac_empty_fifo");
+
+	if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) {
+		if (sp->debug & L1_DEB_WARN) {
+			char tmp[40];
+			sprintf(tmp, "isac_empty_fifo overrun %d",
+				sp->rcvidx + count);
+			debugl1(sp, tmp);
+		}
+		writereg(sp->isac, ISAC_CMDR, 0x80);
+		sp->rcvidx = 0;
+		return;
+	}
+	ptr = sp->rcvbuf + sp->rcvidx;
+	sp->rcvidx += count;
+	save_flags(flags);
+	cli();
+	read_fifo(sp->isac, ptr, count);
+	writereg(sp->isac, ISAC_CMDR, 0x80);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_ISAC_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "isac_empty_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+isac_fill_fifo(struct IsdnCardState *sp)
+{
+	int count, more;
+	u_char *ptr;
+	long flags;
+
+	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
+		debugl1(sp, "isac_fill_fifo");
+
+	if (!sp->tx_skb)
+		return;
+
+	count = sp->tx_skb->len;
+	if (count <= 0)
+		return;
+
+	more = 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	save_flags(flags);
+	cli();
+	ptr = sp->tx_skb->data;
+	skb_pull(sp->tx_skb, count);
+	sp->tx_cnt += count;
+	write_fifo(sp->isac, ptr, count);
+	writereg(sp->isac, ISAC_CMDR, more ? 0x8 : 0xa);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_ISAC_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "isac_fill_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+ph_command(struct IsdnCardState *sp, unsigned int command)
+{
+	if (sp->debug & L1_DEB_ISAC) {
+		char tmp[32];
+		sprintf(tmp, "ph_command %d", command);
+		debugl1(sp, tmp);
+	}
+	writereg(sp->isac, ISAC_CIX0, (command << 2) | 3);
+}
+
+
+static inline void
+isac_interrupt(struct IsdnCardState *sp, u_char val)
+{
+	u_char exval;
+	struct sk_buff *skb;
+	unsigned int count;
+	char tmp[32];
+
+	if (sp->debug & L1_DEB_ISAC) {
+		sprintf(tmp, "ISAC interrupt %x", val);
+		debugl1(sp, tmp);
+	}
+	if (val & 0x80) {	/* RME */
+		exval = readreg(sp->isac, ISAC_RSTA);
+		if ((exval & 0x70) != 0x20) {
+			if (exval & 0x40)
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "ISAC RDO");
+			if (!(exval & 0x20))
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "ISAC CRC error");
+			writereg(sp->isac, ISAC_CMDR, 0x80);
+		} else {
+			count = readreg(sp->isac, ISAC_RBCL) & 0x1f;
+			if (count == 0)
+				count = 32;
+			isac_empty_fifo(sp, count);
+			if ((count = sp->rcvidx) > 0) {
+				if (!(skb = alloc_skb(count, GFP_ATOMIC)))
+					printk(KERN_WARNING "AVM: D receive out of memory\n");
+				else {
+					SET_SKB_FREE(skb);
+					memcpy(skb_put(skb, count), sp->rcvbuf, count);
+					skb_queue_tail(&sp->rq, skb);
+				}
+			}
+		}
+		sp->rcvidx = 0;
+		isac_sched_event(sp, ISAC_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		isac_empty_fifo(sp, 32);
+	}
+	if (val & 0x20) {	/* RSC */
+		/* never */
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "ISAC RSC interrupt");
+	}
+	if (val & 0x10) {	/* XPR */
+		if (sp->tx_skb)
+			if (sp->tx_skb->len) {
+				isac_fill_fifo(sp);
+				goto afterXPR;
+			} else {
+				dev_kfree_skb(sp->tx_skb, FREE_WRITE);
+				sp->tx_cnt = 0;
+				sp->tx_skb = NULL;
+			}
+		if ((sp->tx_skb = skb_dequeue(&sp->sq))) {
+			sp->tx_cnt = 0;
+			isac_fill_fifo(sp);
+		} else
+			isac_sched_event(sp, ISAC_XMTBUFREADY);
+	}
+      afterXPR:
+	if (val & 0x04) {	/* CISQ */
+		sp->ph_state = (readreg(sp->isac, ISAC_CIX0) >> 2)
+		    & 0xf;
+		if (sp->debug & L1_DEB_ISAC) {
+			sprintf(tmp, "l1state %d", sp->ph_state);
+			debugl1(sp, tmp);
+		}
+		isac_new_ph(sp);
+	}
+	if (val & 0x02) {	/* SIN */
+		/* never */
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "ISAC SIN interrupt");
+	}
+	if (val & 0x01) {	/* EXI */
+		exval = readreg(sp->isac, ISAC_EXIR);
+		if (sp->debug & L1_DEB_WARN) {
+			sprintf(tmp, "ISAC EXIR %02x", exval);
+			debugl1(sp, tmp);
+		}
+	}
+}
+
+static inline void
+hscx_int_main(struct IsdnCardState *sp, u_char val)
+{
+
+	u_char exval;
+	struct HscxState *hsp;
+	char tmp[32];
+
+
+	if (val & 0x01) {
+		hsp = sp->hs + 1;
+		exval = readreg(sp->hscx[1], HSCX_EXIR);
+		if (exval == 0x40) {
+			if (hsp->mode == 1)
+				hscx_fill_fifo(hsp);
+			else {
+				/* Here we lost an TX interrupt, so
+				   * restart transmitting the whole frame.
+				 */
+				if (hsp->tx_skb) {
+					skb_push(hsp->tx_skb, hsp->count);
+					hsp->tx_cnt += hsp->count;
+					hsp->count = 0;
+				}
+				writehscxCMDR(sp->hscx[hsp->hscx], 0x01);
+				if (sp->debug & L1_DEB_WARN) {
+					sprintf(tmp, "HSCX B EXIR %x Lost TX", exval);
+					debugl1(sp, tmp);
+				}
+			}
+		} else if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX B EXIR %x", exval);
+			debugl1(sp, tmp);
+		}
+	}
+	if (val & 0xf8) {
+		if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX B interrupt %x", val);
+			debugl1(sp, tmp);
+		}
+		hscx_interrupt(sp, val, 1);
+	}
+	if (val & 0x02) {
+		hsp = sp->hs;
+		exval = readreg(sp->hscx[0], HSCX_EXIR);
+		if (exval == 0x40) {
+			if (hsp->mode == 1)
+				hscx_fill_fifo(hsp);
+			else {
+				/* Here we lost an TX interrupt, so
+				   * restart transmitting the whole frame.
+				 */
+				if (hsp->tx_skb) {
+					skb_push(hsp->tx_skb, hsp->count);
+					hsp->tx_cnt += hsp->count;
+					hsp->count = 0;
+				}
+				writehscxCMDR(sp->hscx[hsp->hscx], 0x01);
+				if (sp->debug & L1_DEB_WARN) {
+					sprintf(tmp, "HSCX A EXIR %x Lost TX", exval);
+					debugl1(sp, tmp);
+				}
+			}
+		} else if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX A EXIR %x", exval);
+			debugl1(sp, tmp);
+		}
+	}
+	if (val & 0x04) {
+		exval = readreg(sp->hscx[0], HSCX_ISTA);
+		if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX A interrupt %x", exval);
+			debugl1(sp, tmp);
+		}
+		hscx_interrupt(sp, exval, 0);
+	}
+}
+
+static void
+teles3_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+#define MAXCOUNT 20
+	struct IsdnCardState *sp;
+	u_char val, stat = 0;
+	int count = 0;
+
+	sp = (struct IsdnCardState *) irq2dev_map[intno];
+
+	if (!sp) {
+		printk(KERN_WARNING "Teles: Spurious interrupt!\n");
+		return;
+	}
+	val = readreg(sp->hscx[1], HSCX_ISTA);
+      Start_HSCX:
+	if (val) {
+		hscx_int_main(sp, val);
+		stat |= 1;
+	}
+	val = readreg(sp->isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val) {
+		isac_interrupt(sp, val);
+		stat |= 2;
+	}
+	count++;
+	val = readreg(sp->hscx[1], HSCX_ISTA);
+	if (val && count < MAXCOUNT) {
+		if (sp->debug & L1_DEB_HSCX)
+			debugl1(sp, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(sp->isac, ISAC_ISTA);
+	if (val && count < MAXCOUNT) {
+		if (sp->debug & L1_DEB_ISAC)
+			debugl1(sp, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (count >= MAXCOUNT)
+		printk(KERN_WARNING "Teles3: more than %d loops in teles3_interrupt\n", count);
+	if (stat & 1) {
+		writereg(sp->hscx[0], HSCX_MASK, 0xFF);
+		writereg(sp->hscx[1], HSCX_MASK, 0xFF);
+		writereg(sp->hscx[0], HSCX_MASK, 0x0);
+		writereg(sp->hscx[1], HSCX_MASK, 0x0);
+	}
+	if (stat & 2) {
+		writereg(sp->isac, ISAC_MASK, 0xFF);
+		writereg(sp->isac, ISAC_MASK, 0x0);
+	}
+}
+
+
+static void
+initisac(struct IsdnCardState *sp)
+{
+	unsigned int adr = sp->isac;
+
+	/* 16.3 IOM 2 Mode */
+	writereg(adr, ISAC_MASK, 0xff);
+	writereg(adr, ISAC_ADF2, 0x80);
+	writereg(adr, ISAC_SQXR, 0x2f);
+	writereg(adr, ISAC_SPCR, 0x0);
+	writereg(adr, ISAC_ADF1, 0x2);
+	writereg(adr, ISAC_STCR, 0x70);
+	writereg(adr, ISAC_MODE, 0xc9);
+	writereg(adr, ISAC_TIMR, 0x0);
+	writereg(adr, ISAC_ADF1, 0x0);
+	writereg(adr, ISAC_CMDR, 0x41);
+	writereg(adr, ISAC_CIX0, (1 << 2) | 3);
+	writereg(adr, ISAC_MASK, 0xff);
+	writereg(adr, ISAC_MASK, 0x0);
+}
+
+static void
+modehscx(struct HscxState *hs, int mode, int ichan)
+{
+	struct IsdnCardState *sp = hs->sp;
+	int hscx = hs->hscx;
+
+	if (sp->debug & L1_DEB_HSCX) {
+		char tmp[40];
+		sprintf(tmp, "hscx %c mode %d ichan %d",
+			'A' + hscx, mode, ichan);
+		debugl1(sp, tmp);
+	}
+	hs->mode = mode;
+	writereg(sp->hscx[hscx], HSCX_CCR1, 0x85);
+	writereg(sp->hscx[hscx], HSCX_XAD1, 0xFF);
+	writereg(sp->hscx[hscx], HSCX_XAD2, 0xFF);
+	writereg(sp->hscx[hscx], HSCX_RAH2, 0xFF);
+	writereg(sp->hscx[hscx], HSCX_XBCH, 0x0);
+	writereg(sp->hscx[hscx], HSCX_RLCR, 0x0);
+
+	switch (mode) {
+		case (0):
+			writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
+			writereg(sp->hscx[hscx], HSCX_TSAX, 0xff);
+			writereg(sp->hscx[hscx], HSCX_TSAR, 0xff);
+			writereg(sp->hscx[hscx], HSCX_XCCR, 7);
+			writereg(sp->hscx[hscx], HSCX_RCCR, 7);
+			writereg(sp->hscx[hscx], HSCX_MODE, 0x84);
+			break;
+		case (1):
+			if (ichan == 0) {
+				writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
+				writereg(sp->hscx[hscx], HSCX_TSAX, 0x2f);
+				writereg(sp->hscx[hscx], HSCX_TSAR, 0x2f);
+				writereg(sp->hscx[hscx], HSCX_XCCR, 7);
+				writereg(sp->hscx[hscx], HSCX_RCCR, 7);
+			} else {
+				writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
+				writereg(sp->hscx[hscx], HSCX_TSAX, 0x3);
+				writereg(sp->hscx[hscx], HSCX_TSAR, 0x3);
+				writereg(sp->hscx[hscx], HSCX_XCCR, 7);
+				writereg(sp->hscx[hscx], HSCX_RCCR, 7);
+			}
+			writereg(sp->hscx[hscx], HSCX_MODE, 0xe4);
+			writereg(sp->hscx[hscx], HSCX_CMDR, 0x41);
+			break;
+		case (2):
+			if (ichan == 0) {
+				writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
+				writereg(sp->hscx[hscx], HSCX_TSAX, 0x2f);
+				writereg(sp->hscx[hscx], HSCX_TSAR, 0x2f);
+				writereg(sp->hscx[hscx], HSCX_XCCR, 7);
+				writereg(sp->hscx[hscx], HSCX_RCCR, 7);
+			} else {
+				writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
+				writereg(sp->hscx[hscx], HSCX_TSAX, 0x3);
+				writereg(sp->hscx[hscx], HSCX_TSAR, 0x3);
+				writereg(sp->hscx[hscx], HSCX_XCCR, 7);
+				writereg(sp->hscx[hscx], HSCX_RCCR, 7);
+			}
+			writereg(sp->hscx[hscx], HSCX_MODE, 0x8c);
+			writereg(sp->hscx[hscx], HSCX_CMDR, 0x41);
+			break;
+	}
+	writereg(sp->hscx[hscx], HSCX_ISTA, 0x00);
+}
+
+inline static void
+release_ioregs(struct IsdnCard *card, int mask)
+{
+	if (mask & 1)
+		release_region(card->sp->isac, 32);
+	if (mask & 2)
+		release_region(card->sp->hscx[0], 32);
+	if (mask & 4)
+		release_region(card->sp->hscx[1], 32);
+}
+
+void
+release_io_teles3(struct IsdnCard *card)
+{
+	if (card->sp->typ == ISDN_CTYPE_TELESPCMCIA)
+		release_region(card->sp->hscx[0], 97);
+	else {
+		if (card->sp->cfg_reg)
+			release_region(card->sp->cfg_reg, 8);
+		release_ioregs(card, 0x7);
+	}
+}
+
+static void
+clear_pending_ints(struct IsdnCardState *sp)
+{
+	int val;
+	char tmp[64];
+
+	val = readreg(sp->hscx[1], HSCX_ISTA);
+	sprintf(tmp, "HSCX B ISTA %x", val);
+	debugl1(sp, tmp);
+	if (val & 0x01) {
+		val = readreg(sp->hscx[1], HSCX_EXIR);
+		sprintf(tmp, "HSCX B EXIR %x", val);
+		debugl1(sp, tmp);
+	} else if (val & 0x02) {
+		val = readreg(sp->hscx[0], HSCX_EXIR);
+		sprintf(tmp, "HSCX A EXIR %x", val);
+		debugl1(sp, tmp);
+	}
+	val = readreg(sp->hscx[0], HSCX_ISTA);
+	sprintf(tmp, "HSCX A ISTA %x", val);
+	debugl1(sp, tmp);
+	val = readreg(sp->hscx[1], HSCX_STAR);
+	sprintf(tmp, "HSCX B STAR %x", val);
+	debugl1(sp, tmp);
+	val = readreg(sp->hscx[0], HSCX_STAR);
+	sprintf(tmp, "HSCX A STAR %x", val);
+	debugl1(sp, tmp);
+	val = readreg(sp->isac, ISAC_STAR);
+	sprintf(tmp, "ISAC STAR %x", val);
+	debugl1(sp, tmp);
+	val = readreg(sp->isac, ISAC_MODE);
+	sprintf(tmp, "ISAC MODE %x", val);
+	debugl1(sp, tmp);
+	val = readreg(sp->isac, ISAC_ADF2);
+	sprintf(tmp, "ISAC ADF2 %x", val);
+	debugl1(sp, tmp);
+	val = readreg(sp->isac, ISAC_ISTA);
+	sprintf(tmp, "ISAC ISTA %x", val);
+	debugl1(sp, tmp);
+	if (val & 0x01) {
+		val = readreg(sp->isac, ISAC_EXIR);
+		sprintf(tmp, "ISAC EXIR %x", val);
+		debugl1(sp, tmp);
+	} else if (val & 0x04) {
+		val = readreg(sp->isac, ISAC_CIR0);
+		sprintf(tmp, "ISAC CIR0 %x", val);
+		debugl1(sp, tmp);
+	}
+	writereg(sp->isac, ISAC_MASK, 0);
+	writereg(sp->isac, ISAC_CMDR, 0x41);
+}
+
+int
+initteles3(struct IsdnCardState *sp)
+{
+	int ret;
+	int loop = 0;
+	char tmp[40];
+
+	sp->counter = kstat.interrupts[sp->irq];
+	sprintf(tmp, "IRQ %d count %d", sp->irq, sp->counter);
+	debugl1(sp, tmp);
+	clear_pending_ints(sp);
+	ret = get_irq(sp->cardnr, &teles3_interrupt);
+	if (ret) {
+		initisac(sp);
+		sp->modehscx(sp->hs, 0, 0);
+		writereg(sp->hscx[sp->hs->hscx], HSCX_CMDR, 0x01);
+		sp->modehscx(sp->hs + 1, 0, 0);
+		writereg(sp->hscx[(sp->hs + 1)->hscx], HSCX_CMDR, 0x01);
+		while (loop++ < 10) {
+			/* At least 1-3 irqs must happen
+			 * (one from HSCX A, one from HSCX B, 3rd from ISAC)
+			 */
+			if (kstat.interrupts[sp->irq] > sp->counter)
+				break;
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + 1;
+			schedule();
+		}
+		sprintf(tmp, "IRQ %d count %d loop %d", sp->irq,
+			kstat.interrupts[sp->irq], loop);
+		debugl1(sp, tmp);
+		if (kstat.interrupts[sp->irq] <= sp->counter) {
+			printk(KERN_WARNING
+			       "Teles3: IRQ(%d) getting no interrupts during init\n",
+			       sp->irq);
+			irq2dev_map[sp->irq] = NULL;
+			free_irq(sp->irq, NULL);
+			return (0);
+		}
+	}
+	return (ret);
+}
+
+int
+setup_teles3(struct IsdnCard *card)
+{
+	u_char cfval = 0, val, verA, verB;
+	struct IsdnCardState *sp = card->sp;
+	long flags;
+	char tmp[64];
+
+	strcpy(tmp, teles3_revision);
+	printk(KERN_NOTICE "HiSax: Teles IO driver Rev. %s\n", HiSax_getrev(tmp));
+	if ((sp->typ != ISDN_CTYPE_16_3) && (sp->typ != ISDN_CTYPE_PNP)
+	    && (sp->typ != ISDN_CTYPE_TELESPCMCIA))
+		return (0);
+
+	if (sp->typ == ISDN_CTYPE_16_3) {
+		sp->cfg_reg = card->para[1];
+		switch (sp->cfg_reg) {
+			case 0x180:
+			case 0x280:
+			case 0x380:
+				sp->cfg_reg |= 0xc00;
+				break;
+		}
+		sp->isac = sp->cfg_reg - 0x400;
+		sp->hscx[0] = sp->cfg_reg - 0xc00;
+		sp->hscx[1] = sp->cfg_reg - 0x800;
+	} else if (sp->typ == ISDN_CTYPE_TELESPCMCIA) {
+		sp->cfg_reg = 0;
+		sp->hscx[0] = card->para[1];
+		sp->hscx[1] = card->para[1] + 0x20;
+		sp->isac = card->para[1] + 0x40;
+	} else {		/* PNP */
+		sp->cfg_reg = 0;
+		sp->isac = card->para[1];
+		sp->hscx[0] = card->para[2];
+		sp->hscx[1] = card->para[2] + 0x20;
+	}
+	sp->irq = card->para[0];
+	if (sp->typ == ISDN_CTYPE_TELESPCMCIA) {
+		if (check_region((sp->hscx[0]), 97)) {
+			printk(KERN_WARNING
+			       "HiSax: %s ports %x-%x already in use\n",
+			       CardType[sp->typ],
+			       sp->hscx[0],
+			       sp->hscx[0] + 96);
+			return (0);
+		} else
+			request_region(sp->hscx[0], 97, "HiSax Teles PCMCIA");
+	} else {
+		if (sp->cfg_reg) {
+			if (check_region((sp->cfg_reg), 8)) {
+				printk(KERN_WARNING
+				       "HiSax: %s config port %x-%x already in use\n",
+				       CardType[card->typ],
+				       sp->cfg_reg,
+				       sp->cfg_reg + 8);
+				return (0);
+			} else {
+				request_region(sp->cfg_reg, 8, "teles3 cfg");
+			}
+		}
+		if (check_region((sp->isac), 32)) {
+			printk(KERN_WARNING
+			   "HiSax: %s isac ports %x-%x already in use\n",
+			       CardType[sp->typ],
+			       sp->isac,
+			       sp->isac + 32);
+			if (sp->cfg_reg) {
+				release_region(sp->cfg_reg, 8);
+			}
+			return (0);
+		} else {
+			request_region(sp->isac, 32, "HiSax isac");
+		}
+		if (check_region((sp->hscx[0]), 32)) {
+			printk(KERN_WARNING
+			 "HiSax: %s hscx A ports %x-%x already in use\n",
+			       CardType[sp->typ],
+			       sp->hscx[0],
+			       sp->hscx[0] + 32);
+			if (sp->cfg_reg) {
+				release_region(sp->cfg_reg, 8);
+			}
+			release_ioregs(card, 1);
+			return (0);
+		} else {
+			request_region(sp->hscx[0], 32, "HiSax hscx A");
+		}
+		if (check_region((sp->hscx[1]), 32)) {
+			printk(KERN_WARNING
+			 "HiSax: %s hscx B ports %x-%x already in use\n",
+			       CardType[sp->typ],
+			       sp->hscx[1],
+			       sp->hscx[1] + 32);
+			if (sp->cfg_reg) {
+				release_region(sp->cfg_reg, 8);
+			}
+			release_ioregs(card, 3);
+			return (0);
+		} else {
+			request_region(sp->hscx[1], 32, "HiSax hscx B");
+		}
+		switch (sp->irq) {
+			case 2:
+				cfval = 0x00;
+				break;
+			case 3:
+				cfval = 0x02;
+				break;
+			case 4:
+				cfval = 0x04;
+				break;
+			case 5:
+				cfval = 0x06;
+				break;
+			case 10:
+				cfval = 0x08;
+				break;
+			case 11:
+				cfval = 0x0A;
+				break;
+			case 12:
+				cfval = 0x0C;
+				break;
+			case 15:
+				cfval = 0x0E;
+				break;
+			default:
+				cfval = 0x00;
+				break;
+		}
+	}
+	if (sp->cfg_reg) {
+		if ((val = bytein(sp->cfg_reg + 0)) != 0x51) {
+			printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n",
+			       sp->cfg_reg + 0, val);
+			release_io_teles3(card);
+			return (0);
+		}
+		if ((val = bytein(sp->cfg_reg + 1)) != 0x93) {
+			printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n",
+			       sp->cfg_reg + 1, val);
+			release_io_teles3(card);
+			return (0);
+		}
+		val = bytein(sp->cfg_reg + 2);	/* 0x1e=without AB
+						   * 0x1f=with AB
+						   * 0x1c 16.3 ???
+						   * 0x46 16.3 with AB + Video (Teles-Vision)
+						 */
+		if (val != 0x46 && val != 0x1c && val != 0x1e && val != 0x1f) {
+			printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n",
+			       sp->cfg_reg + 2, val);
+			release_io_teles3(card);
+			return (0);
+		}
+		save_flags(flags);
+		byteout(sp->cfg_reg + 4, cfval);
+		sti();
+		HZDELAY(HZ / 10 + 1);
+		byteout(sp->cfg_reg + 4, cfval | 1);
+		HZDELAY(HZ / 10 + 1);
+		restore_flags(flags);
+	} else {
+		/* Reset off for 16.3 PnP , thanks to Georg Acher */
+		save_flags(flags);
+		byteout(sp->isac + 0x1c, 1);
+		HZDELAY(2);
+		restore_flags(flags);
+	}
+	printk(KERN_NOTICE
+	       "HiSax: %s config irq:%d isac:%x  cfg:%x\n",
+	       CardType[sp->typ], sp->irq,
+	       sp->isac, sp->cfg_reg);
+	printk(KERN_NOTICE
+	       "HiSax: hscx A:%x  hscx B:%x\n",
+	       sp->hscx[0], sp->hscx[1]);
+	verA = readreg(sp->hscx[0], HSCX_VSTR) & 0xf;
+	verB = readreg(sp->hscx[1], HSCX_VSTR) & 0xf;
+	printk(KERN_INFO "Teles3: HSCX version A: %s  B: %s\n",
+	       HscxVersion(verA), HscxVersion(verB));
+	val = readreg(sp->isac, ISAC_RBCH);
+	printk(KERN_INFO "Teles3: ISAC %s\n",
+	       ISACVersion(val));
+	if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) {
+		printk(KERN_WARNING
+		       "Teles3: wrong HSCX versions check IO address\n");
+		release_io_teles3(card);
+		return (0);
+	}
+	sp->modehscx = &modehscx;
+	sp->ph_command = &ph_command;
+	sp->hscx_fill_fifo = &hscx_fill_fifo;
+	sp->isac_fill_fifo = &isac_fill_fifo;
+	return (1);
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/teles3.h linux/drivers/isdn/hisax/teles3.h
--- v2.0.30/linux/drivers/isdn/hisax/teles3.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/teles3.h	Mon Aug  4 17:34:00 1997
@@ -0,0 +1,21 @@
+/* $Id: teles3.h,v 1.2 1997/01/21 22:27:14 keil Exp $
+ *
+ * teles3.h   Header for Teles 16.3 PNP & compatible
+ *
+ * Author	Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: teles3.h,v $
+ * Revision 1.2  1997/01/21 22:27:14  keil
+ * cleanups
+ *
+ * Revision 1.1  1996/10/13 20:03:49  keil
+ * Initial revision
+ *
+ *
+*/
+
+extern	void teles3_report(struct IsdnCardState *sp);
+extern  void release_io_teles3(struct IsdnCard *card);
+extern	int  setup_teles3(struct IsdnCard *card);
+extern  int  initteles3(struct IsdnCardState *sp);
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/icn/icn.c linux/drivers/isdn/icn/icn.c
--- v2.0.30/linux/drivers/isdn/icn/icn.c	Tue Nov 12 22:36:19 1996
+++ linux/drivers/isdn/icn/icn.c	Tue Aug  5 17:10:02 1997
@@ -1,5 +1,5 @@
-/* $Id: icn.c,v 1.31 1996/11/13 02:36:25 fritz Exp $
- *
+/* $Id: icn.c,v 1.45 1997/06/21 10:42:06 fritz Exp $
+
  * ISDN low-level module for the ICN active ISDN-Card.
  *
  * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
@@ -16,9 +16,54 @@
  *
  * 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. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: icn.c,v $
+ * Revision 1.45  1997/06/21 10:42:06  fritz
+ * Added availability to select leased mode on only one channel.
+ *
+ * Revision 1.44  1997/03/30 16:51:26  calle
+ * changed calls to copy_from_user/copy_to_user and removed verify_area
+ * were possible.
+ *
+ * Revision 1.43  1997/03/21 18:27:04  fritz
+ * Corrected parsing of incoming setup.
+ *
+ * Revision 1.42  1997/03/05 21:13:18  fritz
+ * Bugfix: sndcount was not reset on hangup.
+ *
+ * Revision 1.41  1997/02/24 23:34:29  fritz
+ * Bugfix in Layer1 error-recovery.
+ *
+ * Revision 1.40  1997/02/23 23:34:45  fritz
+ * Minor bugfixes in debugging code.
+ *
+ * Revision 1.39  1997/02/23 16:21:56  fritz
+ * Bugfix: Check for NULL pointer in icn_parse_status().
+ *
+ * Revision 1.38  1997/02/11 18:29:31  fritz
+ * Bugfix in D64S initialization.
+ *
+ * Revision 1.37  1997/02/10 22:43:20  fritz
+ * Added plan and screen elements on ISDN_STAT_ICALL
+ *
+ * Revision 1.36  1997/02/10 21:31:20  fritz
+ * Changed setup-interface (incoming and outgoing).
+ *
+ * Revision 1.35  1997/02/10 10:10:28  fritz
+ * Changes for Kernel 2.1.X compatibility.
+ * Enhanced initialization, can recover from
+ * misconfiguration by other autoprobing drivers.
+ *
+ * Revision 1.34  1997/01/29 22:34:44  fritz
+ * Cleanup, Corrected D64S setup of 2nd channel.
+ *
+ * Revision 1.33  1996/12/05 20:31:48  tsbogend
+ * added handling of L2: DATA LINK LOST messages
+ *
+ * Revision 1.32  1996/11/14 23:49:18  fritz
+ * Bugfix: copy_to_user/copy_from_user mismatch in debugging-ioctl.
+ *
  * Revision 1.31  1996/11/13 02:36:25  fritz
  * Fixed a race condition in writecmd.
  * Some optimizations and cleanup.
@@ -148,21 +193,25 @@
 #undef MAP_DEBUG
 
 static char
-*revision = "$Revision: 1.31 $";
+*revision = "$Revision: 1.45 $";
 
 static int icn_addcard(int, char *, char *);
 
 /*
- * Free queue completely.
+ * Free send-queue completely.
  * Parameter:
- *   queue = pointer to queue-head
+ *   card   = pointer to card struct
+ *   channel = channel number
  */
-static void icn_free_queue(struct sk_buff_head *queue)
+static void
+icn_free_queue(icn_card * card, int channel)
 {
-        struct sk_buff *skb;
+	struct sk_buff_head *queue = &card->spqueue[channel];
+	struct sk_buff *skb;
 
-        while ((skb = skb_dequeue(queue)))
-                dev_kfree_skb(skb, FREE_WRITE);
+	while ((skb = skb_dequeue(queue)))
+		dev_kfree_skb(skb, FREE_WRITE);
+	card->sndcount[channel] = 0;
 }
 
 /* Put a value into a shift-register, highest bit first.
@@ -172,53 +221,57 @@
  *            firstbit = Bit-Number of highest bit
  *            bitcount = Number of bits to output
  */
-static inline void icn_shiftout(unsigned short port,
-                     unsigned long val,
-                     int firstbit,
-                     int bitcount)
+static inline void
+icn_shiftout(unsigned short port,
+	     unsigned long val,
+	     int firstbit,
+	     int bitcount)
 {
 
-        register u_char s;
-        register u_char c;
+	register u_char s;
+	register u_char c;
 
-        for (s = firstbit, c = bitcount; c > 0; s--, c--)
-                OUTB_P((u_char) ((val >> s) & 1) ? 0xff : 0, port);
+	for (s = firstbit, c = bitcount; c > 0; s--, c--)
+		OUTB_P((u_char) ((val >> s) & 1) ? 0xff : 0, port);
 }
 
 /*
  * disable a cards shared memory
  */
-static inline void icn_disable_ram(icn_card *card)
+static inline void
+icn_disable_ram(icn_card * card)
 {
-        OUTB_P(0, ICN_MAPRAM);
+	OUTB_P(0, ICN_MAPRAM);
 }
 
 /*
  * enable a cards shared memory
  */
-static inline void icn_enable_ram(icn_card *card)
+static inline void
+icn_enable_ram(icn_card * card)
 {
-        OUTB_P(0xff, ICN_MAPRAM);
+	OUTB_P(0xff, ICN_MAPRAM);
 }
 
 /*
  * Map a cards channel0 (Bank0/Bank8) or channel1 (Bank4/Bank12)
  */
-static inline void icn_map_channel(icn_card *card, int channel)
+static inline void
+icn_map_channel(icn_card * card, int channel)
 {
 #ifdef MAP_DEBUG
-        printk(KERN_DEBUG "icn_map_channel %d %d\n", dev->channel, channel);
+	printk(KERN_DEBUG "icn_map_channel %d %d\n", dev.channel, channel);
 #endif
-        if ((channel == dev.channel) && (card == dev.mcard))
-                return;
-        if (dev.mcard)
-                icn_disable_ram(dev.mcard);
-        icn_shiftout(ICN_BANK, chan2bank[channel], 3, 4);        /* Select Bank          */
-        icn_enable_ram(card);
-        dev.mcard = card;
-        dev.channel = channel;
+	if ((channel == dev.channel) && (card == dev.mcard))
+		return;
+	if (dev.mcard)
+		icn_disable_ram(dev.mcard);
+	icn_shiftout(ICN_BANK, chan2bank[channel], 3, 4);	/* Select Bank          */
+	icn_enable_ram(card);
+	dev.mcard = card;
+	dev.channel = channel;
 #ifdef MAP_DEBUG
-        printk(KERN_DEBUG "icn_map_channel done\n");
+	printk(KERN_DEBUG "icn_map_channel done\n");
 #endif
 }
 
@@ -227,98 +280,102 @@
  * Return 0 if requested card/channel is unmapped (failure).
  * Return 1 on success.
  */
-static inline int icn_lock_channel(icn_card *card, int channel)
+static inline int
+icn_lock_channel(icn_card * card, int channel)
 {
-        register int retval;
-        ulong flags;
+	register int retval;
+	ulong flags;
 
 #ifdef MAP_DEBUG
-        printk(KERN_DEBUG "icn_lock_channel %d\n", channel);
+	printk(KERN_DEBUG "icn_lock_channel %d\n", channel);
 #endif
-        save_flags(flags);
-        cli();
-        if ((dev.channel == channel) && (card == dev.mcard)) {
-                dev.chanlock++;
-                retval = 1;
+	save_flags(flags);
+	cli();
+	if ((dev.channel == channel) && (card == dev.mcard)) {
+		dev.chanlock++;
+		retval = 1;
 #ifdef MAP_DEBUG
-                printk(KERN_DEBUG "icn_lock_channel %d OK\n", channel);
+		printk(KERN_DEBUG "icn_lock_channel %d OK\n", channel);
 #endif
-        } else {
-                retval = 0;
+	} else {
+		retval = 0;
 #ifdef MAP_DEBUG
-                printk(KERN_DEBUG "icn_lock_channel %d FAILED, dc=%d\n", channel, device->channel);
+		printk(KERN_DEBUG "icn_lock_channel %d FAILED, dc=%d\n", channel, dev.channel);
 #endif
-        }
-        restore_flags(flags);
-        return retval;
+	}
+	restore_flags(flags);
+	return retval;
 }
 
 /*
  * Release current card/channel lock
  */
-static inline void icn_release_channel(void)
+static inline void
+icn_release_channel(void)
 {
-        ulong flags;
+	ulong flags;
 
 #ifdef MAP_DEBUG
-        printk(KERN_DEBUG "icn_release_channel l=%d\n", device->chanlock);
+	printk(KERN_DEBUG "icn_release_channel l=%d\n", dev.chanlock);
 #endif
-        save_flags(flags);
-        cli();
-        if (dev.chanlock > 0)
-                dev.chanlock--;
-        restore_flags(flags);
+	save_flags(flags);
+	cli();
+	if (dev.chanlock > 0)
+		dev.chanlock--;
+	restore_flags(flags);
 }
 
 /*
  * Try to map and lock a cards channel.
  * Return 1 on success, 0 on failure.
  */
-static inline int icn_trymaplock_channel(icn_card *card, int channel)
+static inline int
+icn_trymaplock_channel(icn_card * card, int channel)
 {
-        ulong flags;
+	ulong flags;
 
 #ifdef MAP_DEBUG
-        printk(KERN_DEBUG "trymaplock c=%d dc=%d l=%d\n", channel, dev.channel,
-               dev.chanlock);
+	printk(KERN_DEBUG "trymaplock c=%d dc=%d l=%d\n", channel, dev.channel,
+	       dev.chanlock);
 #endif
-        save_flags(flags);
-        cli();
-        if ((!dev.chanlock) ||
-            ((dev.channel == channel) && (dev.mcard == card))) {
-                dev.chanlock++;
-                icn_map_channel(card,channel);
-                restore_flags(flags);
+	save_flags(flags);
+	cli();
+	if ((!dev.chanlock) ||
+	    ((dev.channel == channel) && (dev.mcard == card))) {
+		dev.chanlock++;
+		icn_map_channel(card, channel);
+		restore_flags(flags);
 #ifdef MAP_DEBUG
-                printk(KERN_DEBUG "trymaplock %d OK\n", channel);
+		printk(KERN_DEBUG "trymaplock %d OK\n", channel);
 #endif
-                return 1;
-        }
-        restore_flags(flags);
+		return 1;
+	}
+	restore_flags(flags);
 #ifdef MAP_DEBUG
-        printk(KERN_DEBUG "trymaplock %d FAILED\n", channel);
+	printk(KERN_DEBUG "trymaplock %d FAILED\n", channel);
 #endif
-        return 0;
+	return 0;
 }
 
 /*
  * Release current card/channel lock,
  * then map same or other channel without locking.
  */
-static inline void icn_maprelease_channel(icn_card *card, int channel)
+static inline void
+icn_maprelease_channel(icn_card * card, int channel)
 {
-        ulong flags;
+	ulong flags;
 
 #ifdef MAP_DEBUG
-        printk(KERN_DEBUG "map_release c=%d l=%d\n", channel, dev.chanlock);
+	printk(KERN_DEBUG "map_release c=%d l=%d\n", channel, dev.chanlock);
 #endif
-        save_flags(flags);
-        cli();
-        if (dev.chanlock > 0)
-                dev.chanlock--;
-        if (!dev.chanlock)
-                icn_map_channel(card,channel);
-        restore_flags(flags);
+	save_flags(flags);
+	cli();
+	if (dev.chanlock > 0)
+		dev.chanlock--;
+	if (!dev.chanlock)
+		icn_map_channel(card, channel);
+	restore_flags(flags);
 }
 
 /* Get Data from the B-Channel, assemble fragmented packets and put them
@@ -326,47 +383,48 @@
  * This routine is called via timer-callback from icn_pollbchan().
  */
 
-static void icn_pollbchan_receive(int channel, icn_card *card)
+static void
+icn_pollbchan_receive(int channel, icn_card * card)
 {
-        int mch = channel + ((card->secondhalf) ? 2 : 0);
-        int eflag;
-        int cnt;
+	int mch = channel + ((card->secondhalf) ? 2 : 0);
+	int eflag;
+	int cnt;
 	struct sk_buff *skb;
 
-        if (icn_trymaplock_channel(card,mch)) {
-                while (rbavl) {
-                        cnt = readb(&rbuf_l);
-                        if ((card->rcvidx[channel] + cnt) > 4000) {
-                                printk(KERN_WARNING 
-                                       "icn: (%s) bogus packet on ch%d, dropping.\n",
-                                       CID,
-                                       channel + 1);
-                                card->rcvidx[channel] = 0;
-                                eflag = 0;
-                        } else {
-                                memcpy_fromio(&card->rcvbuf[channel][card->rcvidx[channel]],
-                                              &rbuf_d, cnt);
-                                card->rcvidx[channel] += cnt;
-                                eflag = readb(&rbuf_f);
-                        }
-                        rbnext;
-                        icn_maprelease_channel(card, mch & 2);
-                        if (!eflag) {
-                                if ((cnt = card->rcvidx[channel])) {
-                                        if (!(skb = dev_alloc_skb(cnt))) {
-                                                printk(KERN_WARNING "ïcn: receive out of memory\n");
-                                                break;
-                                        }
-                                        memcpy(skb_put(skb, cnt), card->rcvbuf[channel], cnt); 
-                                        card->rcvidx[channel] = 0;
-                                        card->interface.rcvcallb_skb(card->myid, channel, skb);
-                                }
-                        }
-                        if (!icn_trymaplock_channel(card, mch))
-                                break;
-                }
-                icn_maprelease_channel(card, mch & 2);
-        }
+	if (icn_trymaplock_channel(card, mch)) {
+		while (rbavl) {
+			cnt = readb(&rbuf_l);
+			if ((card->rcvidx[channel] + cnt) > 4000) {
+				printk(KERN_WARNING
+				       "icn: (%s) bogus packet on ch%d, dropping.\n",
+				       CID,
+				       channel + 1);
+				card->rcvidx[channel] = 0;
+				eflag = 0;
+			} else {
+				memcpy_fromio(&card->rcvbuf[channel][card->rcvidx[channel]],
+					      &rbuf_d, cnt);
+				card->rcvidx[channel] += cnt;
+				eflag = readb(&rbuf_f);
+			}
+			rbnext;
+			icn_maprelease_channel(card, mch & 2);
+			if (!eflag) {
+				if ((cnt = card->rcvidx[channel])) {
+					if (!(skb = dev_alloc_skb(cnt))) {
+						printk(KERN_WARNING "ïcn: receive out of memory\n");
+						break;
+					}
+					memcpy(skb_put(skb, cnt), card->rcvbuf[channel], cnt);
+					card->rcvidx[channel] = 0;
+					card->interface.rcvcallb_skb(card->myid, channel, skb);
+				}
+			}
+			if (!icn_trymaplock_channel(card, mch))
+				break;
+		}
+		icn_maprelease_channel(card, mch & 2);
+	}
 }
 
 /* Send data-packet to B-Channel, split it up into fragments of
@@ -376,58 +434,59 @@
  * directly from icn_sendbuf().
  */
 
-static void icn_pollbchan_send(int channel, icn_card *card)
+static void
+icn_pollbchan_send(int channel, icn_card * card)
 {
-        int mch = channel + ((card->secondhalf) ? 2 : 0);
-        int cnt;
+	int mch = channel + ((card->secondhalf) ? 2 : 0);
+	int cnt;
 	unsigned long flags;
-        struct sk_buff *skb;
-        isdn_ctrl cmd;
+	struct sk_buff *skb;
+	isdn_ctrl cmd;
 
-        if (!(card->sndcount[channel] ||
-             skb_queue_len(&card->spqueue[channel])))
-                return;
-        if (icn_trymaplock_channel(card,mch)) {
-                while (sbfree && (card->sndcount[channel] ||
-                                  skb_queue_len(&card->spqueue[channel]))) {
-                        save_flags(flags);
-                        cli();
-                        if (card->xmit_lock[channel]) {
-                                restore_flags(flags);
-                                break;
-                        }
-                        card->xmit_lock[channel]++;
-                        restore_flags(flags);
-                        skb = skb_dequeue(&card->spqueue[channel]);
-                        if (!skb)
-                                break;
-		        if (skb->len > ICN_FRAGSIZE) {
-			    writeb (0xff, &sbuf_f);
-			    cnt = ICN_FRAGSIZE;
+	if (!(card->sndcount[channel] ||
+	      skb_queue_len(&card->spqueue[channel])))
+		return;
+	if (icn_trymaplock_channel(card, mch)) {
+		while (sbfree && (card->sndcount[channel] ||
+			       skb_queue_len(&card->spqueue[channel]))) {
+			save_flags(flags);
+			cli();
+			if (card->xmit_lock[channel]) {
+				restore_flags(flags);
+				break;
+			}
+			card->xmit_lock[channel]++;
+			restore_flags(flags);
+			skb = skb_dequeue(&card->spqueue[channel]);
+			if (!skb)
+				break;
+			if (skb->len > ICN_FRAGSIZE) {
+				writeb(0xff, &sbuf_f);
+				cnt = ICN_FRAGSIZE;
 			} else {
-			    writeb (0x0, &sbuf_f);
-			    cnt = skb->len;
+				writeb(0x0, &sbuf_f);
+				cnt = skb->len;
 			}
-		        writeb (cnt, &sbuf_l);		    
-                        memcpy_toio(&sbuf_d, skb->data, cnt);
-                        skb_pull(skb, cnt);
-                        card->sndcount[channel] -= cnt;
-                        sbnext;        /* switch to next buffer        */
-                        icn_maprelease_channel(card, mch & 2);
-                        if (!skb->len) {
-                                dev_kfree_skb(skb, FREE_WRITE);
-                                cmd.command = ISDN_STAT_BSENT;
-                                cmd.driver = card->myid;
-                                cmd.arg = channel;
-                                card->interface.statcallb(&cmd);
-                        } else
-                                skb_queue_head(&card->spqueue[channel], skb);
-                        card->xmit_lock[channel] = 0;
-                        if (!icn_trymaplock_channel(card, mch))
-                                break;
-                }
-                icn_maprelease_channel(card, mch & 2);
-        }
+			writeb(cnt, &sbuf_l);
+			memcpy_toio(&sbuf_d, skb->data, cnt);
+			skb_pull(skb, cnt);
+			card->sndcount[channel] -= cnt;
+			sbnext; /* switch to next buffer        */
+			icn_maprelease_channel(card, mch & 2);
+			if (!skb->len) {
+				dev_kfree_skb(skb, FREE_WRITE);
+				cmd.command = ISDN_STAT_BSENT;
+				cmd.driver = card->myid;
+				cmd.arg = channel;
+				card->interface.statcallb(&cmd);
+			} else
+				skb_queue_head(&card->spqueue[channel], skb);
+			card->xmit_lock[channel] = 0;
+			if (!icn_trymaplock_channel(card, mch))
+				break;
+		}
+		icn_maprelease_channel(card, mch & 2);
+	}
 }
 
 /* Send/Receive Data to/from the B-Channel.
@@ -435,54 +494,61 @@
  * It schedules itself while any B-Channel is open.
  */
 
-static void icn_pollbchan(unsigned long data)
+static void
+icn_pollbchan(unsigned long data)
 {
-        icn_card *card = (icn_card *)data;
-        unsigned long flags;
+	icn_card *card = (icn_card *) data;
+	unsigned long flags;
 
-                if (card->flags & ICN_FLAGS_B1ACTIVE) {
-                        icn_pollbchan_receive(0, card);
-                        icn_pollbchan_send(0, card);
-                }
-                if (card->flags & ICN_FLAGS_B2ACTIVE) {
-                        icn_pollbchan_receive(1, card);
-                        icn_pollbchan_send(1, card);
-                }
-                if (card->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE)) {
-                        /* schedule b-channel polling again */
-                        save_flags(flags);
-                        cli();
-                        del_timer(&card->rb_timer);
-                        card->rb_timer.expires = jiffies + ICN_TIMER_BCREAD;
-                        add_timer(&card->rb_timer);
-                        card->flags |= ICN_FLAGS_RBTIMER;
-                        restore_flags(flags);
-                } else
-                        card->flags &= ~ICN_FLAGS_RBTIMER;
+	if (card->flags & ICN_FLAGS_B1ACTIVE) {
+		icn_pollbchan_receive(0, card);
+		icn_pollbchan_send(0, card);
+	}
+	if (card->flags & ICN_FLAGS_B2ACTIVE) {
+		icn_pollbchan_receive(1, card);
+		icn_pollbchan_send(1, card);
+	}
+	if (card->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE)) {
+		/* schedule b-channel polling again */
+		save_flags(flags);
+		cli();
+		del_timer(&card->rb_timer);
+		card->rb_timer.expires = jiffies + ICN_TIMER_BCREAD;
+		add_timer(&card->rb_timer);
+		card->flags |= ICN_FLAGS_RBTIMER;
+		restore_flags(flags);
+	} else
+		card->flags &= ~ICN_FLAGS_RBTIMER;
 }
 
 typedef struct icn_stat {
-        char *statstr;
-        int  command;
-        int  action;
+	char *statstr;
+	int command;
+	int action;
 } icn_stat;
-
-static icn_stat icn_stat_table[] = {
-        {"BCON_",          ISDN_STAT_BCONN, 1}, /* B-Channel connected        */
-        {"BDIS_",          ISDN_STAT_BHUP,  2}, /* B-Channel disconnected     */
-        {"DCON_",          ISDN_STAT_DCONN, 0}, /* D-Channel connected        */
-        {"DDIS_",          ISDN_STAT_DHUP,  0}, /* D-Channel disconnected     */
-        {"DCAL_I",         ISDN_STAT_ICALL, 3}, /* Incoming call dialup-line  */
-        {"DSCA_I",         ISDN_STAT_ICALL, 3}, /* Incoming call 1TR6-SPV     */
-        {"FCALL",          ISDN_STAT_ICALL, 4}, /* Leased line connection up  */
-        {"CIF",            ISDN_STAT_CINF,  5}, /* Charge-info, 1TR6-type     */
-        {"AOC",            ISDN_STAT_CINF,  6}, /* Charge-info, DSS1-type     */
-        {"CAU",            ISDN_STAT_CAUSE, 7}, /* Cause code                 */
-        {"TEI OK",         ISDN_STAT_RUN,   0}, /* Card connected to wallplug */
-        {"NO D-CHAN",      ISDN_STAT_NODCH, 0}, /* No D-channel available     */
-        {"E_L1: ACT FAIL", ISDN_STAT_BHUP,  8}, /* Layer-1 activation failed  */
-        {NULL,             0             , -1}
+/* *INDENT-OFF* */
+static icn_stat icn_stat_table[] =
+{
+	{"BCON_",          ISDN_STAT_BCONN, 1},	/* B-Channel connected        */
+	{"BDIS_",          ISDN_STAT_BHUP,  2},	/* B-Channel disconnected     */
+	{"DCON_",          ISDN_STAT_DCONN, 0},	/* D-Channel connected        */
+	{"DDIS_",          ISDN_STAT_DHUP,  0},	/* D-Channel disconnected     */
+	{"DCAL_I",         ISDN_STAT_ICALL, 3},	/* Incoming call dialup-line  */
+	{"DSCA_I",         ISDN_STAT_ICALL, 3},	/* Incoming call 1TR6-SPV     */
+	{"FCALL",          ISDN_STAT_ICALL, 4},	/* Leased line connection up  */
+	{"CIF",            ISDN_STAT_CINF,  5},	/* Charge-info, 1TR6-type     */
+	{"AOC",            ISDN_STAT_CINF,  6},	/* Charge-info, DSS1-type     */
+	{"CAU",            ISDN_STAT_CAUSE, 7},	/* Cause code                 */
+	{"TEI OK",         ISDN_STAT_RUN,   0},	/* Card connected to wallplug */
+	{"NO D-CHAN",      ISDN_STAT_NODCH, 0},	/* No D-channel available     */
+	{"E_L1: ACT FAIL", ISDN_STAT_BHUP,  8},	/* Layer-1 activation failed  */
+	{"E_L2: DATA LIN", ISDN_STAT_BHUP,  8},	/* Layer-2 data link lost     */
+	{"E_L1: ACTIVATION FAILED",
+					   ISDN_STAT_BHUP,  8},	/* Layer-1 activation failed  */
+	{NULL, 0, -1}
 };
+/* *INDENT-ON* */
+
 
 /*
  * Check Statusqueue-Pointer from isdn-cards.
@@ -494,199 +560,244 @@
  * This routine is called periodically via timer.
  */
 
-static int icn_parse_status(u_char *status, int channel, icn_card *card)
+static int
+icn_parse_status(u_char * status, int channel, icn_card * card)
+{
+	icn_stat *s = icn_stat_table;
+	int action = -1;
+	int dflag = 0;
+	unsigned long flags;
+	isdn_ctrl cmd;
+
+	while (s->statstr) {
+		if (!strncmp(status, s->statstr, strlen(s->statstr))) {
+			cmd.command = s->command;
+			action = s->action;
+			break;
+		}
+		s++;
+	}
+	if (action == -1)
+		return 0;
+	cmd.driver = card->myid;
+	cmd.arg = channel;
+	switch (action) {
+		case 1:
+			card->flags |= (channel) ?
+			    ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE;
+			break;
+		case 2:
+			card->flags &= ~((channel) ?
+				ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE);
+			icn_free_queue(card, channel);
+			save_flags(flags);
+			cli();
+			card->rcvidx[channel] = 0;
+			restore_flags(flags);
+			dflag |= (channel + 1);
+			break;
+		case 3:
+			{
+				char *t = status + 6;
+				char *s = strpbrk(t, ",");
+
+				*s++ = '\0';
+				strncpy(cmd.parm.setup.phone, t,
+					sizeof(cmd.parm.setup.phone));
+				s = strpbrk(t = s, ",");
+				*s++ = '\0';
+				if (!strlen(t))
+					cmd.parm.setup.si1 = 0;
+				else
+					cmd.parm.setup.si1 =
+					    simple_strtoul(t, NULL, 10);
+				s = strpbrk(t = s, ",");
+				*s++ = '\0';
+				if (!strlen(t))
+					cmd.parm.setup.si2 = 0;
+				else
+					cmd.parm.setup.si2 =
+					    simple_strtoul(t, NULL, 10);
+				strncpy(cmd.parm.setup.eazmsn, s,
+					sizeof(cmd.parm.setup.eazmsn));
+			}
+			cmd.parm.setup.plan = 0;
+			cmd.parm.setup.screen = 0;
+			break;
+		case 4:
+			sprintf(cmd.parm.setup.phone, "LEASED%d", card->myid);
+			sprintf(cmd.parm.setup.eazmsn, "%d", channel + 1);
+			cmd.parm.setup.si1 = 7;
+			cmd.parm.setup.si2 = 0;
+			cmd.parm.setup.plan = 0;
+			cmd.parm.setup.screen = 0;
+			break;
+		case 5:
+			strncpy(cmd.parm.num, status + 3, sizeof(cmd.parm.num) - 1);
+			break;
+		case 6:
+			sprintf(cmd.parm.num, "%d",
+			     (int) simple_strtoul(status + 7, NULL, 16));
+			break;
+		case 7:
+			status += 3;
+			if (strlen(status) == 4)
+				sprintf(cmd.parm.num, "%s%c%c",
+				     status + 2, *status, *(status + 1));
+			else
+				strncpy(cmd.parm.num, status + 1, sizeof(cmd.parm.num) - 1);
+			break;
+		case 8:
+			dflag = 3;
+			card->flags &= ~ICN_FLAGS_B1ACTIVE;
+			icn_free_queue(card, 0);
+			save_flags(flags);
+			cli();
+			card->rcvidx[0] = 0;
+			restore_flags(flags);
+			cmd.arg = 0;
+			cmd.driver = card->myid;
+			card->interface.statcallb(&cmd);
+			cmd.command = ISDN_STAT_DHUP;
+			cmd.arg = 0;
+			cmd.driver = card->myid;
+			card->interface.statcallb(&cmd);
+			cmd.command = ISDN_STAT_BHUP;
+			card->flags &= ~ICN_FLAGS_B2ACTIVE;
+			icn_free_queue(card, 1);
+			save_flags(flags);
+			cli();
+			card->rcvidx[1] = 0;
+			restore_flags(flags);
+			cmd.arg = 1;
+			cmd.driver = card->myid;
+			card->interface.statcallb(&cmd);
+			cmd.command = ISDN_STAT_DHUP;
+			cmd.arg = 1;
+			cmd.driver = card->myid;
+			break;
+	}
+	card->interface.statcallb(&cmd);
+	return dflag;
+}
+
+static void
+icn_putmsg(icn_card * card, unsigned char c)
+{
+	ulong flags;
+
+	save_flags(flags);
+	cli();
+	*card->msg_buf_write++ = (c == 0xff) ? '\n' : c;
+	if (card->msg_buf_write == card->msg_buf_read) {
+		if (++card->msg_buf_read > card->msg_buf_end)
+			card->msg_buf_read = card->msg_buf;
+	}
+	if (card->msg_buf_write > card->msg_buf_end)
+		card->msg_buf_write = card->msg_buf;
+	restore_flags(flags);
+}
+
+static void
+icn_polldchan(unsigned long data)
 {
-        icn_stat  *s = icn_stat_table;
-        int       action = -1;
-        int       dflag  = 0;
-        unsigned long flags;
-        isdn_ctrl cmd;
-
-        while (s->statstr) {
-                if (!strncmp(status,s->statstr,strlen(s->statstr))) {
-                        cmd.command = s->command;
-                        action = s->action;
-                        break;
-                }
-                s++;
-        }
-        if (action==-1)
-                return 0;
-        cmd.driver = card->myid;
-        cmd.arg = channel;
-        switch (action) {
-                case 1:
-                        card->flags |= (channel)?
-                                ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE;
-                        break;
-                case 2:
-                        card->flags &= ~((channel)?
-                                ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE);
-                        icn_free_queue(&card->spqueue[channel]);
-                        save_flags(flags);
-                        cli();
-                        card->rcvidx[channel] = 0;
-                        restore_flags(flags);
-                        dflag |= (channel+1);
-                        break;
-                case 3:
-                        strncpy(cmd.num, status + 6, sizeof(cmd.num) - 1);
-                        break;
-                case 4:
-                        sprintf(cmd.num,"LEASED%d,07,00,%d",
-                                 card->myid,channel+1);
-                        break;
-                case 5:
-                        strncpy(cmd.num, status + 3, sizeof(cmd.num) - 1);
-                        break;
-                case 6:
-                        sprintf(cmd.num,"%d",
-                                (int)simple_strtoul(status + 7,NULL,16));
-                        break;
-                case 7:
-                        status += 3;
-                        if (strlen(status)==4)
-                                sprintf(cmd.num,"%s%c%c",
-                                        status+2,*status,*(status+1));
-                        else
-                                strncpy(cmd.num, status+1, sizeof(cmd.num) - 1);
-                        break;
-                case 8:
-                        cmd.arg = 0;
-                        cmd.driver = card->myid;
-                        card->interface.statcallb(&cmd);
-                        cmd.command = ISDN_STAT_DHUP;
-                        cmd.arg = 0;
-                        cmd.driver = card->myid;
-                        card->interface.statcallb(&cmd);
-                        cmd.command = ISDN_STAT_BHUP;
-                        cmd.arg = 1;
-                        cmd.driver = card->myid;
-                        card->interface.statcallb(&cmd);
-                        cmd.command = ISDN_STAT_DHUP;
-                        cmd.arg = 1;
-                        cmd.driver = card->myid;
-                        break;
-        }
-        card->interface.statcallb(&cmd);
-        return dflag;
-}
-
-static void icn_putmsg(icn_card *card, unsigned char c)
-{
-        ulong flags;
-
-        save_flags(flags);
-        cli();
-        *card->msg_buf_write++ = (c == 0xff) ? '\n' : c;
-        if (card->msg_buf_write == card->msg_buf_read) {
-                if (++card->msg_buf_read > card->msg_buf_end)
-                card->msg_buf_read = card->msg_buf;
-        }
-        if (card->msg_buf_write > card->msg_buf_end)
-                card->msg_buf_write = card->msg_buf;
-        restore_flags(flags);
-}
-
-static void icn_polldchan(unsigned long data)
-{
-        icn_card *card = (icn_card *)data;
-        int mch = card->secondhalf ? 2 : 0;
-        int avail = 0;
-        int dflag = 0;
-        int left;
-        u_char c;
-        int ch;
-        int flags;
-        int i;
-        u_char *p;
-        isdn_ctrl cmd;
-
-        if (icn_trymaplock_channel(card,mch)) {
-                avail = msg_avail;
-                for (left = avail, i = readb(&msg_o); left > 0; i++, left--) {
-                        c = readb(&dev.shmem->comm_buffers.iopc_buf[i & 0xff]);
-                        icn_putmsg(card, c);
-                        if (c == 0xff) {
-                                card->imsg[card->iptr] = 0;
-                                card->iptr = 0;
-                                if (card->imsg[0] == '0' && card->imsg[1] >= '0' &&
-                                    card->imsg[1] <= '2' && card->imsg[2] == ';') {
-                                        ch = (card->imsg[1] - '0') - 1;
-                                        p = &card->imsg[3];
-                                        dflag |= icn_parse_status(p, ch, card);
-                                } else {
-                                        p = card->imsg;
-                                        if (!strncmp(p, "DRV1.", 5)) {
-                                                u_char vstr[10];
-                                                u_char *q = vstr;
-
-                                                printk(KERN_INFO "icn: (%s) %s\n",CID,p);
-                                                if (!strncmp(p + 7, "TC", 2)) {
-                                                        card->ptype = ISDN_PTYPE_1TR6;
-                                                        card->interface.features |= ISDN_FEATURE_P_1TR6;
-                                                        printk(KERN_INFO
-                                                               "icn: (%s) 1TR6-Protocol loaded and running\n",CID);
-                                                }
-                                                if (!strncmp(p + 7, "EC", 2)) {
-                                                        card->ptype = ISDN_PTYPE_EURO;
-                                                        card->interface.features |= ISDN_FEATURE_P_EURO;
-                                                        printk(KERN_INFO
-                                                               "icn: (%s) Euro-Protocol loaded and running\n",CID);
-                                                }
-                                                p = strstr(card->imsg,"BRV") + 3;
-                                                while (*p) {
-                                                        if (*p>='0' && *p<='9')
-                                                                *q++ = *p;
-                                                        p++;
-                                                }
-                                                *q = '\0';
-                                                strcat(vstr,"000");
-                                                vstr[3] = '\0';
-                                                card->fw_rev = (int)simple_strtoul(vstr,NULL,10);
-                                                continue;
-                                                
-                                        }
-                                }
-                        } else {
-                                card->imsg[card->iptr] = c;
-                                if (card->iptr < 59)
-                                        card->iptr++;
-                        }
-                }
-                writeb((readb(&msg_o) + avail) & 0xff, &msg_o);
-                icn_release_channel();
-        }
-        if (avail) {
-                cmd.command = ISDN_STAT_STAVAIL;
-                cmd.driver = card->myid;
-                cmd.arg = avail;
-                card->interface.statcallb(&cmd);
-        }
-        if (dflag & 1)
-                card->interface.rcvcallb(card->myid, 0, card->rcvbuf[0], 0);
-        if (dflag & 2)
-                card->interface.rcvcallb(card->myid, 1, card->rcvbuf[1], 0);
-        if (card->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE))
-                if (!(card->flags & ICN_FLAGS_RBTIMER)) {
-                        /* schedule b-channel polling */
-                        card->flags |= ICN_FLAGS_RBTIMER;
-                        save_flags(flags);
-                        cli();
-                        del_timer(&card->rb_timer);
-                        card->rb_timer.function = icn_pollbchan;
-                        card->rb_timer.data = (unsigned long)card;
-                        card->rb_timer.expires = jiffies + ICN_TIMER_BCREAD;
-                        add_timer(&card->rb_timer);
-                        restore_flags(flags);
-                }
-        /* schedule again */
-        save_flags(flags);
-        cli();
-        del_timer(&card->st_timer);
-        card->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
-        add_timer(&card->st_timer);
-        restore_flags(flags);
+	icn_card *card = (icn_card *) data;
+	int mch = card->secondhalf ? 2 : 0;
+	int avail = 0;
+	int dflag = 0;
+	int left;
+	u_char c;
+	int ch;
+	int flags;
+	int i;
+	u_char *p;
+	isdn_ctrl cmd;
+
+	if (icn_trymaplock_channel(card, mch)) {
+		avail = msg_avail;
+		for (left = avail, i = readb(&msg_o); left > 0; i++, left--) {
+			c = readb(&dev.shmem->comm_buffers.iopc_buf[i & 0xff]);
+			icn_putmsg(card, c);
+			if (c == 0xff) {
+				card->imsg[card->iptr] = 0;
+				card->iptr = 0;
+				if (card->imsg[0] == '0' && card->imsg[1] >= '0' &&
+				    card->imsg[1] <= '2' && card->imsg[2] == ';') {
+					ch = (card->imsg[1] - '0') - 1;
+					p = &card->imsg[3];
+					dflag |= icn_parse_status(p, ch, card);
+				} else {
+					p = card->imsg;
+					if (!strncmp(p, "DRV1.", 5)) {
+						u_char vstr[10];
+						u_char *q = vstr;
+
+						printk(KERN_INFO "icn: (%s) %s\n", CID, p);
+						if (!strncmp(p + 7, "TC", 2)) {
+							card->ptype = ISDN_PTYPE_1TR6;
+							card->interface.features |= ISDN_FEATURE_P_1TR6;
+							printk(KERN_INFO
+							       "icn: (%s) 1TR6-Protocol loaded and running\n", CID);
+						}
+						if (!strncmp(p + 7, "EC", 2)) {
+							card->ptype = ISDN_PTYPE_EURO;
+							card->interface.features |= ISDN_FEATURE_P_EURO;
+							printk(KERN_INFO
+							       "icn: (%s) Euro-Protocol loaded and running\n", CID);
+						}
+						p = strstr(card->imsg, "BRV") + 3;
+						while (*p) {
+							if (*p >= '0' && *p <= '9')
+								*q++ = *p;
+							p++;
+						}
+						*q = '\0';
+						strcat(vstr, "000");
+						vstr[3] = '\0';
+						card->fw_rev = (int) simple_strtoul(vstr, NULL, 10);
+						continue;
+
+					}
+				}
+			} else {
+				card->imsg[card->iptr] = c;
+				if (card->iptr < 59)
+					card->iptr++;
+			}
+		}
+		writeb((readb(&msg_o) + avail) & 0xff, &msg_o);
+		icn_release_channel();
+	}
+	if (avail) {
+		cmd.command = ISDN_STAT_STAVAIL;
+		cmd.driver = card->myid;
+		cmd.arg = avail;
+		card->interface.statcallb(&cmd);
+	}
+	if (dflag & 1)
+		card->interface.rcvcallb(card->myid, 0, card->rcvbuf[0], 0);
+	if (dflag & 2)
+		card->interface.rcvcallb(card->myid, 1, card->rcvbuf[1], 0);
+	if (card->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE))
+		if (!(card->flags & ICN_FLAGS_RBTIMER)) {
+			/* schedule b-channel polling */
+			card->flags |= ICN_FLAGS_RBTIMER;
+			save_flags(flags);
+			cli();
+			del_timer(&card->rb_timer);
+			card->rb_timer.function = icn_pollbchan;
+			card->rb_timer.data = (unsigned long) card;
+			card->rb_timer.expires = jiffies + ICN_TIMER_BCREAD;
+			add_timer(&card->rb_timer);
+			restore_flags(flags);
+		}
+	/* schedule again */
+	save_flags(flags);
+	cli();
+	del_timer(&card->st_timer);
+	card->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
+	add_timer(&card->st_timer);
+	restore_flags(flags);
 }
 
 /* Append a packet to the transmit buffer-queue.
@@ -698,34 +809,35 @@
  *   Number of bytes transferred, -E??? on error
  */
 
-static int icn_sendbuf(int channel, struct sk_buff *skb, icn_card * card)
+static int
+icn_sendbuf(int channel, struct sk_buff *skb, icn_card * card)
 {
-        int len = skb->len;
-        unsigned long flags;
-        struct sk_buff *nskb;
-
-        if (len > 4000) {
-                printk(KERN_WARNING
-                       "icn: Send packet too large\n");
-                return -EINVAL;
-        }
-        if (len) {
-                if (!(card->flags & (channel)?ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE))
-                        return 0;
-                if (card->sndcount[channel] > ICN_MAX_SQUEUE)
-                        return 0;
-                save_flags(flags);
-                cli();
-                nskb = skb_clone(skb, GFP_ATOMIC);
-                if (nskb) {
-                        skb_queue_tail(&card->spqueue[channel], nskb);
-                        dev_kfree_skb(skb, FREE_WRITE);
-                } else
-                        len = 0;
-                card->sndcount[channel] += len;
-                restore_flags(flags);
-        }
-        return len;
+	int len = skb->len;
+	unsigned long flags;
+	struct sk_buff *nskb;
+
+	if (len > 4000) {
+		printk(KERN_WARNING
+		       "icn: Send packet too large\n");
+		return -EINVAL;
+	}
+	if (len) {
+		if (!(card->flags & (channel) ? ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE))
+			return 0;
+		if (card->sndcount[channel] > ICN_MAX_SQUEUE)
+			return 0;
+		save_flags(flags);
+		cli();
+		nskb = skb_clone(skb, GFP_ATOMIC);
+		if (nskb) {
+			skb_queue_tail(&card->spqueue[channel], nskb);
+			dev_kfree_skb(skb, FREE_WRITE);
+		} else
+			len = 0;
+		card->sndcount[channel] += len;
+		restore_flags(flags);
+	}
+	return len;
 }
 
 /*
@@ -735,42 +847,43 @@
  *   0 on success (Boot loader ready)
  *   -EIO on failure (timeout)
  */
-static int icn_check_loader(int cardnumber)
+static int
+icn_check_loader(int cardnumber)
 {
-        int timer = 0;
+	int timer = 0;
 
-        while (1) {
+	while (1) {
 #ifdef BOOT_DEBUG
-                printk(KERN_DEBUG "Loader %d ?\n", cardnumber);
+		printk(KERN_DEBUG "Loader %d ?\n", cardnumber);
 #endif
-                if (readb(&dev.shmem->data_control.scns) ||
-                    readb(&dev.shmem->data_control.scnr)) {
-                        if (timer++ > 5) {
-                                printk(KERN_WARNING
-                                       "icn: Boot-Loader %d timed out.\n",
-                                       cardnumber);
-                                icn_release_channel();
-                                return -EIO;
-                        }
+		if (readb(&dev.shmem->data_control.scns) ||
+		    readb(&dev.shmem->data_control.scnr)) {
+			if (timer++ > 5) {
+				printk(KERN_WARNING
+				       "icn: Boot-Loader %d timed out.\n",
+				       cardnumber);
+				icn_release_channel();
+				return -EIO;
+			}
 #ifdef BOOT_DEBUG
-                        printk(KERN_DEBUG "Loader %d TO?\n", cardnumber);
+			printk(KERN_DEBUG "Loader %d TO?\n", cardnumber);
 #endif
-                        current->state = TASK_INTERRUPTIBLE;
-                        current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
-                        schedule();
-                } else {
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
+			schedule();
+		} else {
 #ifdef BOOT_DEBUG
-                        printk(KERN_DEBUG "Loader %d OK\n", cardnumber);
+			printk(KERN_DEBUG "Loader %d OK\n", cardnumber);
 #endif
-                        icn_release_channel();
-                        return 0;
-                }
-        }
+			icn_release_channel();
+			return 0;
+		}
+	}
 }
 
 /* Load the boot-code into the interface-card's memory and start it.
  * Always called from user-process.
- * 
+ *
  * Parameters:
  *            buffer = pointer to packet
  * Return:
@@ -792,891 +905,937 @@
 #define SLEEP(sec)
 #endif
 
-static int icn_loadboot(u_char * buffer, icn_card * card)
+static int
+icn_loadboot(u_char * buffer, icn_card * card)
 {
-        int ret;
-        ulong flags;
+	int ret;
+	ulong flags;
 	u_char *codebuf;
 
 #ifdef BOOT_DEBUG
-        printk(KERN_DEBUG "icn_loadboot called, buffaddr=%08lx\n", (ulong) buffer);
+	printk(KERN_DEBUG "icn_loadboot called, buffaddr=%08lx\n", (ulong) buffer);
 #endif
-        if ((ret = verify_area(VERIFY_READ, (void *) buffer, ICN_CODE_STAGE1)))
-                return ret;
-        if (!(codebuf = kmalloc(ICN_CODE_STAGE1,GFP_KERNEL))) {
-                printk(KERN_WARNING "icn: Could not allocate code buffer\n");
-                return -ENOMEM;
-        }
-        save_flags(flags);
-        cli();
-        if (!card->rvalid) {
-                if (check_region(card->port, ICN_PORTLEN)) {
-                        printk(KERN_WARNING
-                               "icn: (%s) ports 0x%03x-0x%03x in use.\n",
-                               CID,
-                               card->port,
-                               card->port + ICN_PORTLEN);
-                        restore_flags(flags);
-                        kfree(codebuf);
-                        return -EBUSY;
-                }
-                request_region(card->port, ICN_PORTLEN, card->regname);
-                card->rvalid = 1;
-                if (card->doubleS0)
-                        card->other->rvalid = 1;
-        }
-        if (!dev.mvalid) {
-                if (check_shmem((ulong) dev.shmem, 0x4000)) {
-                        printk(KERN_WARNING
-                               "icn: memory at 0x%08lx in use.\n",
-                               (ulong) dev.shmem);
-                        restore_flags(flags);
-                        return -EBUSY;
-                }
-                request_shmem((ulong) dev.shmem, 0x4000, "icn");
-                dev.mvalid = 1;
-        }
-        restore_flags(flags);
-        OUTB_P(0, ICN_RUN);                                       /* Reset Controller */
-        OUTB_P(0, ICN_MAPRAM);                                    /* Disable RAM      */
-        icn_shiftout(ICN_CFG, 0x0f, 3, 4);                        /* Windowsize= 16k  */
-        icn_shiftout(ICN_CFG, (unsigned long) dev.shmem, 23, 10); /* Set RAM-Addr.    */
+	if (!(codebuf = kmalloc(ICN_CODE_STAGE1, GFP_KERNEL))) {
+		printk(KERN_WARNING "icn: Could not allocate code buffer\n");
+		return -ENOMEM;
+	}
+	if ((ret = copy_from_user(codebuf, buffer, ICN_CODE_STAGE1))) {
+		kfree(codebuf);
+		return ret;
+	}
+	save_flags(flags);
+	cli();
+	if (!card->rvalid) {
+		if (check_region(card->port, ICN_PORTLEN)) {
+			printk(KERN_WARNING
+			       "icn: (%s) ports 0x%03x-0x%03x in use.\n",
+			       CID,
+			       card->port,
+			       card->port + ICN_PORTLEN);
+			restore_flags(flags);
+			kfree(codebuf);
+			return -EBUSY;
+		}
+		request_region(card->port, ICN_PORTLEN, card->regname);
+		card->rvalid = 1;
+		if (card->doubleS0)
+			card->other->rvalid = 1;
+	}
+	if (!dev.mvalid) {
+		if (check_shmem((ulong) dev.shmem, 0x4000)) {
+			printk(KERN_WARNING
+			       "icn: memory at 0x%08lx in use.\n",
+			       (ulong) dev.shmem);
+			restore_flags(flags);
+			return -EBUSY;
+		}
+		request_shmem((ulong) dev.shmem, 0x4000, "icn");
+		dev.mvalid = 1;
+	}
+	restore_flags(flags);
+	OUTB_P(0, ICN_RUN);     /* Reset Controller */
+	OUTB_P(0, ICN_MAPRAM);  /* Disable RAM      */
+	icn_shiftout(ICN_CFG, 0x0f, 3, 4);	/* Windowsize= 16k  */
+	icn_shiftout(ICN_CFG, (unsigned long) dev.shmem, 23, 10);	/* Set RAM-Addr.    */
 #ifdef BOOT_DEBUG
-        printk(KERN_DEBUG "shmem=%08lx\n", (ulong) dev.shmem);
+	printk(KERN_DEBUG "shmem=%08lx\n", (ulong) dev.shmem);
 #endif
-        SLEEP(1);
+	SLEEP(1);
 #ifdef BOOT_DEBUG
-        printk(KERN_DEBUG "Map Bank 0\n");
+	printk(KERN_DEBUG "Map Bank 0\n");
 #endif
-        save_flags(flags);
-        cli();
-        icn_map_channel(card,0);                                   /* Select Bank 0    */
-        icn_lock_channel(card,0);                                  /* Lock Bank 0      */
-        restore_flags(flags);
-        SLEEP(1);
-        copy_from_user(codebuf, buffer, ICN_CODE_STAGE1);
-        memcpy_toio(dev.shmem, codebuf, ICN_CODE_STAGE1);           /* Copy code        */
+	save_flags(flags);
+	cli();
+	icn_map_channel(card, 0);	/* Select Bank 0    */
+	icn_lock_channel(card, 0);	/* Lock Bank 0      */
+	restore_flags(flags);
+	SLEEP(1);
+	memcpy_toio(dev.shmem, codebuf, ICN_CODE_STAGE1);	/* Copy code        */
 #ifdef BOOT_DEBUG
-        printk(KERN_DEBUG "Bootloader transfered\n");
+	printk(KERN_DEBUG "Bootloader transfered\n");
 #endif
-        if (card->doubleS0) {
-                SLEEP(1);
+	if (card->doubleS0) {
+		SLEEP(1);
 #ifdef BOOT_DEBUG
-                printk(KERN_DEBUG "Map Bank 8\n");
+		printk(KERN_DEBUG "Map Bank 8\n");
 #endif
-                save_flags(flags);
-                cli();
-                icn_release_channel();
-                icn_map_channel(card,2);                           /* Select Bank 8   */
-                icn_lock_channel(card,2);                          /* Lock Bank 8     */
-                restore_flags(flags);
-                SLEEP(1);
-                memcpy_toio(dev.shmem, codebuf, ICN_CODE_STAGE1);           /* Copy code        */
+		save_flags(flags);
+		cli();
+		icn_release_channel();
+		icn_map_channel(card, 2);	/* Select Bank 8   */
+		icn_lock_channel(card, 2);	/* Lock Bank 8     */
+		restore_flags(flags);
+		SLEEP(1);
+		memcpy_toio(dev.shmem, codebuf, ICN_CODE_STAGE1);	/* Copy code        */
 #ifdef BOOT_DEBUG
-                printk(KERN_DEBUG "Bootloader transfered\n");
+		printk(KERN_DEBUG "Bootloader transfered\n");
 #endif
-        }
-        kfree(codebuf);
-        SLEEP(1);
-        OUTB_P(0xff, ICN_RUN);                                     /* Start Boot-Code */
-        if ((ret = icn_check_loader(card->doubleS0 ? 2 : 1)))
-                return ret;
-        if (!card->doubleS0)
-                return 0;
-        /* reached only, if we have a Double-S0-Card */
+	}
+	kfree(codebuf);
+	SLEEP(1);
+	OUTB_P(0xff, ICN_RUN);  /* Start Boot-Code */
+	if ((ret = icn_check_loader(card->doubleS0 ? 2 : 1)))
+		return ret;
+	if (!card->doubleS0)
+		return 0;
+	/* reached only, if we have a Double-S0-Card */
 #ifdef BOOT_DEBUG
-        printk(KERN_DEBUG "Map Bank 0\n");
+	printk(KERN_DEBUG "Map Bank 0\n");
 #endif
-        save_flags(flags);
-        cli();
-        icn_map_channel(card,0);                                   /* Select Bank 0   */
-        icn_lock_channel(card,0);                                  /* Lock Bank 0     */
-        restore_flags(flags);
-        SLEEP(1);
-        return (icn_check_loader(1));
-}
-
-static int icn_loadproto(u_char * buffer, icn_card * card)
-{
-        register u_char *p = buffer;
-        u_char codebuf[256];
-        uint left = ICN_CODE_STAGE2;
-        uint cnt;
-        int timer;
-        int ret;
-        unsigned long flags;
+	save_flags(flags);
+	cli();
+	icn_map_channel(card, 0);	/* Select Bank 0   */
+	icn_lock_channel(card, 0);	/* Lock Bank 0     */
+	restore_flags(flags);
+	SLEEP(1);
+	return (icn_check_loader(1));
+}
+
+static int
+icn_loadproto(u_char * buffer, icn_card * card)
+{
+	register u_char *p = buffer;
+	u_char codebuf[256];
+	uint left = ICN_CODE_STAGE2;
+	uint cnt;
+	int timer;
+	int ret;
+	unsigned long flags;
 
 #ifdef BOOT_DEBUG
-        printk(KERN_DEBUG "icn_loadproto called\n");
+	printk(KERN_DEBUG "icn_loadproto called\n");
 #endif
-        if ((ret = verify_area(VERIFY_READ, (void *) buffer, ICN_CODE_STAGE2)))
-                return ret;
-        timer = 0;
-        save_flags(flags);
-        cli();
-        if (card->secondhalf) {
-                icn_map_channel(card, 2);
-                icn_lock_channel(card, 2);
-        } else {
-                icn_map_channel(card, 0);
-                icn_lock_channel(card, 0);
-        }
-        restore_flags(flags);
-        while (left) {
-                if (sbfree) {                           /* If there is a free buffer...  */
-                        cnt = MIN(256, left);
-                        copy_from_user(codebuf, p, cnt);
-                        memcpy_toio(&sbuf_l, codebuf, cnt); /* copy data                     */ 
-                        sbnext;                         /* switch to next buffer         */
-                        p += cnt;
-                        left -= cnt;
-                        timer = 0;
-                } else {
+	if ((ret = verify_area(VERIFY_READ, (void *) buffer, ICN_CODE_STAGE2)))
+		return ret;
+	timer = 0;
+	save_flags(flags);
+	cli();
+	if (card->secondhalf) {
+		icn_map_channel(card, 2);
+		icn_lock_channel(card, 2);
+	} else {
+		icn_map_channel(card, 0);
+		icn_lock_channel(card, 0);
+	}
+	restore_flags(flags);
+	while (left) {
+		if (sbfree) {   /* If there is a free buffer...  */
+			cnt = MIN(256, left);
+			if (copy_from_user(codebuf, p, cnt)) {
+				icn_maprelease_channel(card, 0);
+				return -EFAULT;
+			}
+			memcpy_toio(&sbuf_l, codebuf, cnt);	/* copy data                     */
+			sbnext; /* switch to next buffer         */
+			p += cnt;
+			left -= cnt;
+			timer = 0;
+		} else {
 #ifdef BOOT_DEBUG
-                        printk(KERN_DEBUG "boot 2 !sbfree\n");
+			printk(KERN_DEBUG "boot 2 !sbfree\n");
 #endif
-                        if (timer++ > 5) {
-                                icn_maprelease_channel(card, 0);
-                                return -EIO;
-                        }
-                        current->state = TASK_INTERRUPTIBLE;
-                        current->timeout = jiffies + 10;
-                        schedule();
-                }
-        }
-        writeb (0x20, &sbuf_n);
-        timer = 0;
-        while (1) {
-                if (readb(&cmd_o) || readb(&cmd_i)) {
+			if (timer++ > 5) {
+				icn_maprelease_channel(card, 0);
+				return -EIO;
+			}
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + 10;
+			schedule();
+		}
+	}
+	writeb(0x20, &sbuf_n);
+	timer = 0;
+	while (1) {
+		if (readb(&cmd_o) || readb(&cmd_i)) {
 #ifdef BOOT_DEBUG
-                        printk(KERN_DEBUG "Proto?\n");
+			printk(KERN_DEBUG "Proto?\n");
 #endif
-                        if (timer++ > 5) {
-                                printk(KERN_WARNING
-                                       "icn: (%s) Protocol timed out.\n",
-                                       CID);
+			if (timer++ > 5) {
+				printk(KERN_WARNING
+				       "icn: (%s) Protocol timed out.\n",
+				       CID);
 #ifdef BOOT_DEBUG
-                                printk(KERN_DEBUG "Proto TO!\n");
+				printk(KERN_DEBUG "Proto TO!\n");
 #endif
-                                icn_maprelease_channel(card, 0);
-                                return -EIO;
-                        }
+				icn_maprelease_channel(card, 0);
+				return -EIO;
+			}
 #ifdef BOOT_DEBUG
-                        printk(KERN_DEBUG "Proto TO?\n");
+			printk(KERN_DEBUG "Proto TO?\n");
 #endif
-                        current->state = TASK_INTERRUPTIBLE;
-                        current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
-                        schedule();
-                } else {
-                        if ((card->secondhalf) || (!card->doubleS0)) {
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
+			schedule();
+		} else {
+			if ((card->secondhalf) || (!card->doubleS0)) {
 #ifdef BOOT_DEBUG
-                                printk(KERN_DEBUG "Proto loaded, install poll-timer %d\n",
-                                       card->secondhalf);
+				printk(KERN_DEBUG "Proto loaded, install poll-timer %d\n",
+				       card->secondhalf);
 #endif
-                                save_flags(flags);
-                                cli();
-                                init_timer(&card->st_timer);
-                                card->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
-                                card->st_timer.function = icn_polldchan;
-                                card->st_timer.data = (unsigned long)card;
-                                add_timer(&card->st_timer);
-                                card->flags |= ICN_FLAGS_RUNNING;
-                                if (card->doubleS0) {
-                                        init_timer(&card->other->st_timer);
-                                        card->other->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
-                                        card->other->st_timer.function = icn_polldchan;
-                                        card->other->st_timer.data = (unsigned long)card->other;
-                                        add_timer(&card->other->st_timer);
-                                        card->other->flags |= ICN_FLAGS_RUNNING;
-                                }
-                                restore_flags(flags);
-                        }
-                        icn_maprelease_channel(card, 0);
-                        return 0;
-                }
-        }
+				save_flags(flags);
+				cli();
+				init_timer(&card->st_timer);
+				card->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
+				card->st_timer.function = icn_polldchan;
+				card->st_timer.data = (unsigned long) card;
+				add_timer(&card->st_timer);
+				card->flags |= ICN_FLAGS_RUNNING;
+				if (card->doubleS0) {
+					init_timer(&card->other->st_timer);
+					card->other->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
+					card->other->st_timer.function = icn_polldchan;
+					card->other->st_timer.data = (unsigned long) card->other;
+					add_timer(&card->other->st_timer);
+					card->other->flags |= ICN_FLAGS_RUNNING;
+				}
+				restore_flags(flags);
+			}
+			icn_maprelease_channel(card, 0);
+			return 0;
+		}
+	}
 }
 
 /* Read the Status-replies from the Interface */
-static int icn_readstatus(u_char * buf, int len, int user, icn_card * card)
+static int
+icn_readstatus(u_char * buf, int len, int user, icn_card * card)
 {
-        int count;
-        u_char *p;
+	int count;
+	u_char *p;
 
-        for (p = buf, count = 0; count < len; p++, count++) {
-                if (card->msg_buf_read == card->msg_buf_write)
-                        return count;
-                if (user)
-                        put_user(*card->msg_buf_read++, p);
-                else
-                        *p = *card->msg_buf_read++;
-                if (card->msg_buf_read > card->msg_buf_end)
-                        card->msg_buf_read = card->msg_buf;
-        }
-        return count;
+	for (p = buf, count = 0; count < len; p++, count++) {
+		if (card->msg_buf_read == card->msg_buf_write)
+			return count;
+		if (user)
+			put_user(*card->msg_buf_read++, p);
+		else
+			*p = *card->msg_buf_read++;
+		if (card->msg_buf_read > card->msg_buf_end)
+			card->msg_buf_read = card->msg_buf;
+	}
+	return count;
 }
 
 /* Put command-strings into the command-queue of the Interface */
-static int icn_writecmd(const u_char * buf, int len, int user, icn_card * card)
+static int
+icn_writecmd(const u_char * buf, int len, int user, icn_card * card)
 {
 	int mch = card->secondhalf ? 2 : 0;
-        int avail;
-        int pp;
-        int i;
-        int count;
-        int xcount;
-        int ocount;
-        int loop;
-        unsigned long flags;
-        int lastmap_channel;
-        struct icn_card *lastmap_card;
-        u_char *p;
-        isdn_ctrl cmd;
-        u_char msg[0x100];
-
-        ocount = 1;
-        xcount = loop = 0;
-        while (len) {
-                save_flags(flags);
-                cli();
-                lastmap_card = dev.mcard;
-                lastmap_channel = dev.channel;
-                icn_map_channel(card, mch);
-		
-                avail = cmd_free;
-                count = MIN(avail, len);
-                if (user)
-                        copy_from_user(msg, buf, count);
-                else
-                        memcpy(msg, buf, count);
-                icn_putmsg(card, '>');
-                for (p = msg, pp = readb(&cmd_i), i = count; i > 0; i--, p++, pp
-			     ++) {
-                        writeb((*p == '\n') ? 0xff : *p,
-                               &dev.shmem->comm_buffers.pcio_buf[pp & 0xff]);
-                        len--;
-                        xcount++;
-                        icn_putmsg(card, *p);
-                        if ((*p == '\n') && (i > 1)) {
-                                icn_putmsg(card, '>');
-                                ocount++;
-                        }
-                        ocount++;
-                }
-                writeb((readb(&cmd_i) + count) & 0xff, &cmd_i);
-                if (lastmap_card)
-                        icn_map_channel(lastmap_card, lastmap_channel);
-                restore_flags(flags);
-                if (len) {
-                        udelay(1000);
-                        if (loop++ > 20)
-                                break;
-                } else
-                        break;
-        }
-        if (len && (!user))
-                printk(KERN_WARNING "icn: writemsg incomplete!\n");
-        cmd.command = ISDN_STAT_STAVAIL;
-        cmd.driver = card->myid;
-        cmd.arg = ocount;
-        card->interface.statcallb(&cmd);
-        return xcount;
+	int avail;
+	int pp;
+	int i;
+	int count;
+	int xcount;
+	int ocount;
+	int loop;
+	unsigned long flags;
+	int lastmap_channel;
+	struct icn_card *lastmap_card;
+	u_char *p;
+	isdn_ctrl cmd;
+	u_char msg[0x100];
+
+	ocount = 1;
+	xcount = loop = 0;
+	while (len) {
+		save_flags(flags);
+		cli();
+		lastmap_card = dev.mcard;
+		lastmap_channel = dev.channel;
+		icn_map_channel(card, mch);
+
+		avail = cmd_free;
+		count = MIN(avail, len);
+		if (user)
+			copy_from_user(msg, buf, count);
+		else
+			memcpy(msg, buf, count);
+		icn_putmsg(card, '>');
+		for (p = msg, pp = readb(&cmd_i), i = count; i > 0; i--, p++, pp
+		     ++) {
+			writeb((*p == '\n') ? 0xff : *p,
+			   &dev.shmem->comm_buffers.pcio_buf[pp & 0xff]);
+			len--;
+			xcount++;
+			icn_putmsg(card, *p);
+			if ((*p == '\n') && (i > 1)) {
+				icn_putmsg(card, '>');
+				ocount++;
+			}
+			ocount++;
+		}
+		writeb((readb(&cmd_i) + count) & 0xff, &cmd_i);
+		if (lastmap_card)
+			icn_map_channel(lastmap_card, lastmap_channel);
+		restore_flags(flags);
+		if (len) {
+			udelay(1000);
+			if (loop++ > 20)
+				break;
+		} else
+			break;
+	}
+	if (len && (!user))
+		printk(KERN_WARNING "icn: writemsg incomplete!\n");
+	cmd.command = ISDN_STAT_STAVAIL;
+	cmd.driver = card->myid;
+	cmd.arg = ocount;
+	card->interface.statcallb(&cmd);
+	return xcount;
 }
 
 /*
  * Delete card's pending timers, send STOP to linklevel
  */
-static void icn_stopcard(icn_card * card)
+static void
+icn_stopcard(icn_card * card)
 {
-        unsigned long flags;
-        isdn_ctrl cmd;
+	unsigned long flags;
+	isdn_ctrl cmd;
 
-        save_flags(flags);
-        cli();
-        if (card->flags & ICN_FLAGS_RUNNING) {
-                card->flags &= ~ICN_FLAGS_RUNNING;
-                del_timer(&card->st_timer);
-                del_timer(&card->rb_timer);
-                cmd.command = ISDN_STAT_STOP;
-                cmd.driver  = card->myid;
-                card->interface.statcallb(&cmd);
-                if (card->doubleS0)
-                        icn_stopcard(card->other);
-        }
-        restore_flags(flags);
-}
-
-static void icn_stopallcards(void)
-{
-        icn_card *p = cards;
-
-        while (p) {
-                icn_stopcard(p);
-                p = p->next;
-        }
-}
-
-static int icn_command(isdn_ctrl * c, icn_card * card)
-{
-        ulong a;
-        ulong flags;
-        int i;
-        char cbuf[60];
-        isdn_ctrl cmd;
-        icn_cdef cdef;
-
-        switch (c->command) {
-        case ISDN_CMD_IOCTL:
-                memcpy(&a, c->num, sizeof(ulong));
-                switch (c->arg) {
-                        case ICN_IOCTL_SETMMIO:
-                                if ((unsigned long) dev.shmem != (a & 0x0ffc000)) {
-                                        if (check_shmem((ulong) (a & 0x0ffc000), 0x4000)) {
-                                                printk(KERN_WARNING
-                                                       "icn: memory at 0x%08lx in use.\n",
-                                                       (ulong) (a & 0x0ffc000));
-                                                return -EINVAL;
-                                        }
-                                        icn_stopallcards();
-                                        save_flags(flags);
-                                        cli();
-                                        if (dev.mvalid)
-                                                release_shmem((ulong) dev.shmem, 0x4000);
-                                        dev.mvalid = 0;
-                                        dev.shmem = (icn_shmem *) (a & 0x0ffc000);
-                                        restore_flags(flags);
-                                        printk(KERN_INFO
-                                               "icn: (%s) mmio set to 0x%08lx\n",
-                                               CID,
-                                               (unsigned long) dev.shmem);
-                                }
-                                break;
-                        case ICN_IOCTL_GETMMIO:
-                                return (long) dev.shmem;
-                        case ICN_IOCTL_SETPORT:
-                                if (a == 0x300 || a == 0x310 || a == 0x320 || a == 0x330
-                                    || a == 0x340 || a == 0x350 || a == 0x360 ||
-                                    a == 0x308 || a == 0x318 || a == 0x328 || a == 0x338
-                                    || a == 0x348 || a == 0x358 || a == 0x368) {
-                                        if (card->port != (unsigned short) a) {
-                                                if (check_region((unsigned short) a, ICN_PORTLEN)) {
-                                                        printk(KERN_WARNING
-                                                               "icn: (%s) ports 0x%03x-0x%03x in use.\n",
-                                                               CID, (int) a, (int) a + ICN_PORTLEN);
-                                                        return -EINVAL;
-                                                }
-                                                icn_stopcard(card);
-                                                save_flags(flags);
-                                                cli();
-                                                if (card->rvalid)
-                                                        release_region(card->port, ICN_PORTLEN);
-                                                card->port = (unsigned short) a;
-                                                card->rvalid = 0;
-                                                if (card->doubleS0) {
-                                                        card->other->port = (unsigned short) a;
-                                                        card->other->rvalid = 0;
-                                                }
-                                                restore_flags(flags);
-                                                printk(KERN_INFO
-                                                       "icn: (%s) port set to 0x%03x\n",
-                                                       CID, card->port);
-                                        }
-                                } else
-                                        return -EINVAL;
-                                break;
-                        case ICN_IOCTL_GETPORT:
-                                return (int) card->port;
-                        case ICN_IOCTL_GETDOUBLE:
-                                return (int) card->doubleS0;
-                        case ICN_IOCTL_DEBUGVAR:
-				if ((i = verify_area(VERIFY_WRITE,
-                                                    (void *) a,
-                                                    sizeof(ulong) * 2)))
-                                        return i;
-                                copy_from_user((char *)a,
-                                            (char *)&card, sizeof(ulong));
-				a += sizeof(ulong);
-				{
-                                        ulong l = (ulong)&dev;
-                                        copy_from_user((char *)a,
-                                                    (char *)&l, sizeof(ulong));
-                                }
-                                return 0;
-                        case ICN_IOCTL_LOADBOOT:
-                                icn_stopcard(card);
-                                return (icn_loadboot((u_char *) a, card));
-                        case ICN_IOCTL_LOADPROTO:
-                                icn_stopcard(card);
-                                if ((i = (icn_loadproto((u_char *) a, card))))
-                                        return i;
-                                if (card->doubleS0)
-                                        i = icn_loadproto((u_char *) (a + ICN_CODE_STAGE2), card->other);
-                                return i;
-                                break;
-                        case ICN_IOCTL_ADDCARD:
-                                if ((i = verify_area(VERIFY_READ, (void *) a, sizeof(icn_cdef))))
-                                        return i;
-                                copy_from_user((char *)&cdef, (char *)a, sizeof(cdef));
-                                return (icn_addcard(cdef.port, cdef.id1, cdef.id2));
-                                break;
-                        case ICN_IOCTL_LEASEDCFG:
-                                if (a) {
-                                        if (!card->leased) {
-                                                card->leased = 1;
-                                                while (card->ptype == ISDN_PTYPE_UNKNOWN) {
-                                                        current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
-                                                        schedule();
-                                                }
-                                                current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
-                                                schedule();
-                                                sprintf(cbuf, "00;FV2ON\n01;EAZ1\n");
-                                                i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
-                                                printk(KERN_INFO
-                                                       "icn: (%s) Leased-line mode enabled\n",
-                                                       CID);
-                                                cmd.command = ISDN_STAT_RUN;
-                                                cmd.driver = card->myid;
-                                                cmd.arg = 0;
-                                                card->interface.statcallb(&cmd);
-                                        }
-                                } else {
-                                        if (card->leased) {
-                                                card->leased = 0;
-                                                sprintf(cbuf, "00;FV2OFF\n");
-                                                i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
-                                                printk(KERN_INFO
-                                                       "icn: (%s) Leased-line mode disabled\n",
-                                                       CID);
-                                                cmd.command = ISDN_STAT_RUN;
-                                                cmd.driver = card->myid;
-                                                cmd.arg = 0;
-                                                card->interface.statcallb(&cmd);
-                                        }
-                                }
-                                return 0;
-                        default:
-                                return -EINVAL;
-                }
-                break;
-                case ISDN_CMD_DIAL:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        if (card->leased)
-                                break;
-                        if ((c->arg & 255) < ICN_BCH) {
-                                char *p;
-                                char *p2;
-                                char dial[50];
-                                char sis[50];
-                                char dcode[4];
-                                int si1, si2;
-                                
-                                a = c->arg;
-                                strcpy(sis, c->num);
-                                p = strrchr(sis, ',');
-                                *p++ = '\0';
-                                si2 = simple_strtoul(p,NULL,10);
-                                p = strrchr(sis, ',') + 1;
-                                si1 = simple_strtoul(p,NULL,10);
-                                p = c->num;
-                                if (*p == 's' || *p == 'S') {
-                                        /* Dial for SPV */
-                                        p++;
-                                        strcpy(dcode, "SCA");
-                                } else
-                                        /* Normal Dial */
-                                        strcpy(dcode, "CAL");
-                                strcpy(dial, p);
-                                p = strchr(dial, ',');
-                                *p++ = '\0';
-                                p2 = strchr(p, ',');
-                                *p2 = '\0';
-                                sprintf(cbuf, "%02d;D%s_R%s,%02d,%02d,%s\n", (int) (a + 1), dcode, dial, si1,
-                                        si2, p);
-                                i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
-                        }
-                        break;
-                case ISDN_CMD_ACCEPTD:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        if (c->arg < ICN_BCH) {
-                                a = c->arg + 1;
-                                if (card->fw_rev >= 300) {
-                                        switch (card->l2_proto[a-1]) {
-                                                case ISDN_PROTO_L2_X75I:
-                                                        sprintf(cbuf, "%02d;BX75\n", (int) a);
-                                                        break;
-                                                case ISDN_PROTO_L2_HDLC:
-                                                        sprintf(cbuf, "%02d;BTRA\n", (int) a);
-                                                        break;
-                                        }
-                                        i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
-                                }
-                                sprintf(cbuf, "%02d;DCON_R\n", (int) a);
-                                i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
-                        }
-                        break;
-                case ISDN_CMD_ACCEPTB:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        if (c->arg < ICN_BCH) {
-                                a = c->arg + 1;
-                                if (card->fw_rev >= 300)
-                                        switch (card->l2_proto[a-1]) {
-                                                case ISDN_PROTO_L2_X75I:
-                                                        sprintf(cbuf, "%02d;BCON_R,BX75\n", (int) a);
-                                                        break;
-                                                case ISDN_PROTO_L2_HDLC:
-                                                        sprintf(cbuf, "%02d;BCON_R,BTRA\n", (int) a);
-                                                        break;
-                                        }
-                                else
-                                        sprintf(cbuf, "%02d;BCON_R\n", (int) a);
-                                i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
-                        }
-                        break;
-                case ISDN_CMD_HANGUP:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        if (c->arg < ICN_BCH) {
-                                a = c->arg + 1;
-                                sprintf(cbuf, "%02d;BDIS_R\n%02d;DDIS_R\n", (int) a, (int) a);
-                                i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
-                        }
-                        break;
-                case ISDN_CMD_SETEAZ:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        if (card->leased)
-                                break;
-                        if (c->arg < ICN_BCH) {
-                                a = c->arg + 1;
-                                if (card->ptype == ISDN_PTYPE_EURO) {
-                                        sprintf(cbuf, "%02d;MS%s%s\n", (int) a,
-                                                c->num[0] ? "N" : "ALL", c->num);
-                                } else
-                                        sprintf(cbuf, "%02d;EAZ%s\n", (int) a,
-                                                c->num[0] ? c->num : "0123456789");
-                                i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
-                        }
-                        break;
-                case ISDN_CMD_CLREAZ:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        if (card->leased)
-                                break;
-                        if (c->arg < ICN_BCH) {
-                                a = c->arg + 1;
-                                if (card->ptype == ISDN_PTYPE_EURO)
-                                        sprintf(cbuf, "%02d;MSNC\n", (int) a);
-                                else
-                                        sprintf(cbuf, "%02d;EAZC\n", (int) a);
-                                i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
-                        }
-                        break;
-                case ISDN_CMD_SETL2:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        if ((c->arg & 255) < ICN_BCH) {
-                                a = c->arg;
-                                switch (a >> 8) {
-                                        case ISDN_PROTO_L2_X75I:
-                                                sprintf(cbuf, "%02d;BX75\n", (int) (a & 255) + 1);
-                                                break;
-                                        case ISDN_PROTO_L2_HDLC:
-                                                sprintf(cbuf, "%02d;BTRA\n", (int) (a & 255) + 1);
-                                                break;
-                                        default:
-                                                return -EINVAL;
-                                }
-                                i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
-                                card->l2_proto[a & 255] = (a >> 8);
-                        }
-                        break;
-                case ISDN_CMD_GETL2:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        if ((c->arg & 255) < ICN_BCH)
-                                return card->l2_proto[c->arg & 255];
-                        else
-                                return -ENODEV;
-                case ISDN_CMD_SETL3:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        return 0;
-                case ISDN_CMD_GETL3:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        if ((c->arg & 255) < ICN_BCH)
-                                return ISDN_PROTO_L3_TRANS;
-                        else
-                                return -ENODEV;
-                case ISDN_CMD_GETEAZ:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        break;
-                case ISDN_CMD_SETSIL:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        break;
-                case ISDN_CMD_GETSIL:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        break;
-                case ISDN_CMD_LOCK:
-                        MOD_INC_USE_COUNT;
-                        break;
-                case ISDN_CMD_UNLOCK:
-                        MOD_DEC_USE_COUNT;
-                        break;
-                default:
-                        return -EINVAL;
-        }
-        return 0;
+	save_flags(flags);
+	cli();
+	if (card->flags & ICN_FLAGS_RUNNING) {
+		card->flags &= ~ICN_FLAGS_RUNNING;
+		del_timer(&card->st_timer);
+		del_timer(&card->rb_timer);
+		cmd.command = ISDN_STAT_STOP;
+		cmd.driver = card->myid;
+		card->interface.statcallb(&cmd);
+		if (card->doubleS0)
+			icn_stopcard(card->other);
+	}
+	restore_flags(flags);
+}
+
+static void
+icn_stopallcards(void)
+{
+	icn_card *p = cards;
+
+	while (p) {
+		icn_stopcard(p);
+		p = p->next;
+	}
 }
 
 /*
- * Find card with given driverId
+ * Unmap all cards, because some of them may be mapped accidetly during
+ * autoprobing of some network drivers (SMC-driver?)
  */
-static inline icn_card *
-  icn_findcard(int driverid)
+static void
+icn_disable_cards(void)
 {
-        icn_card *p = cards;
+	icn_card *card = cards;
+	unsigned long flags;
 
-        while (p) {
-                if (p->myid == driverid)
-                        return p;
-                p = p->next;
-        }
-        return (icn_card *)0;
+	save_flags(flags);
+	cli();
+	while (card) {
+		if (check_region(card->port, ICN_PORTLEN)) {
+			printk(KERN_WARNING
+			       "icn: (%s) ports 0x%03x-0x%03x in use.\n",
+			       CID,
+			       card->port,
+			       card->port + ICN_PORTLEN);
+			cli();
+		} else {
+			OUTB_P(0, ICN_RUN);	/* Reset Controller     */
+			OUTB_P(0, ICN_MAPRAM);	/* Disable RAM          */
+		}
+		card = card->next;
+	}
+	restore_flags(flags);
+}
+
+static int
+icn_command(isdn_ctrl * c, icn_card * card)
+{
+	ulong a;
+	ulong flags;
+	int i;
+	char cbuf[60];
+	isdn_ctrl cmd;
+	icn_cdef cdef;
+
+	switch (c->command) {
+		case ISDN_CMD_IOCTL:
+			memcpy(&a, c->parm.num, sizeof(ulong));
+			switch (c->arg) {
+				case ICN_IOCTL_SETMMIO:
+					if ((unsigned long) dev.shmem != (a & 0x0ffc000)) {
+						if (check_shmem((ulong) (a & 0x0ffc000), 0x4000)) {
+							printk(KERN_WARNING
+							       "icn: memory at 0x%08lx in use.\n",
+							       (ulong) (a & 0x0ffc000));
+							return -EINVAL;
+						}
+						icn_stopallcards();
+						save_flags(flags);
+						cli();
+						if (dev.mvalid)
+							release_shmem((ulong) dev.shmem, 0x4000);
+						dev.mvalid = 0;
+						dev.shmem = (icn_shmem *) (a & 0x0ffc000);
+						restore_flags(flags);
+						printk(KERN_INFO
+						       "icn: (%s) mmio set to 0x%08lx\n",
+						       CID,
+						       (unsigned long) dev.shmem);
+					}
+					break;
+				case ICN_IOCTL_GETMMIO:
+					return (long) dev.shmem;
+				case ICN_IOCTL_SETPORT:
+					if (a == 0x300 || a == 0x310 || a == 0x320 || a == 0x330
+					    || a == 0x340 || a == 0x350 || a == 0x360 ||
+					    a == 0x308 || a == 0x318 || a == 0x328 || a == 0x338
+					    || a == 0x348 || a == 0x358 || a == 0x368) {
+						if (card->port != (unsigned short) a) {
+							if (check_region((unsigned short) a, ICN_PORTLEN)) {
+								printk(KERN_WARNING
+								       "icn: (%s) ports 0x%03x-0x%03x in use.\n",
+								       CID, (int) a, (int) a + ICN_PORTLEN);
+								return -EINVAL;
+							}
+							icn_stopcard(card);
+							save_flags(flags);
+							cli();
+							if (card->rvalid)
+								release_region(card->port, ICN_PORTLEN);
+							card->port = (unsigned short) a;
+							card->rvalid = 0;
+							if (card->doubleS0) {
+								card->other->port = (unsigned short) a;
+								card->other->rvalid = 0;
+							}
+							restore_flags(flags);
+							printk(KERN_INFO
+							       "icn: (%s) port set to 0x%03x\n",
+							CID, card->port);
+						}
+					} else
+						return -EINVAL;
+					break;
+				case ICN_IOCTL_GETPORT:
+					return (int) card->port;
+				case ICN_IOCTL_GETDOUBLE:
+					return (int) card->doubleS0;
+				case ICN_IOCTL_DEBUGVAR:
+					if ((i = copy_to_user((char *) a,
+					  (char *) &card, sizeof(ulong))))
+						return i;
+					a += sizeof(ulong);
+					{
+						ulong l = (ulong) & dev;
+						if ((i = copy_to_user((char *) a,
+							     (char *) &l, sizeof(ulong))))
+							return i;
+					}
+					return 0;
+				case ICN_IOCTL_LOADBOOT:
+					if (dev.firstload) {
+						icn_disable_cards();
+						dev.firstload = 0;
+					}
+					icn_stopcard(card);
+					return (icn_loadboot((u_char *) a, card));
+				case ICN_IOCTL_LOADPROTO:
+					icn_stopcard(card);
+					if ((i = (icn_loadproto((u_char *) a, card))))
+						return i;
+					if (card->doubleS0)
+						i = icn_loadproto((u_char *) (a + ICN_CODE_STAGE2), card->other);
+					return i;
+					break;
+				case ICN_IOCTL_ADDCARD:
+					if (!dev.firstload)
+						return -EBUSY;
+					if ((i = copy_from_user((char *) &cdef, (char *) a, sizeof(cdef))))
+						return i;
+					return (icn_addcard(cdef.port, cdef.id1, cdef.id2));
+					break;
+				case ICN_IOCTL_LEASEDCFG:
+					if (a) {
+						if (!card->leased) {
+							card->leased = 1;
+							while (card->ptype == ISDN_PTYPE_UNKNOWN) {
+								current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
+								schedule();
+							}
+							current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
+							schedule();
+							sprintf(cbuf, "00;FV2ON\n01;EAZ%c\n02;EAZ%c\n",
+								(a & 1)?'1':'C', (a & 2)?'2':'C');
+							i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+							printk(KERN_INFO
+							       "icn: (%s) Leased-line mode enabled\n",
+							       CID);
+							cmd.command = ISDN_STAT_RUN;
+							cmd.driver = card->myid;
+							cmd.arg = 0;
+							card->interface.statcallb(&cmd);
+						}
+					} else {
+						if (card->leased) {
+							card->leased = 0;
+							sprintf(cbuf, "00;FV2OFF\n");
+							i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+							printk(KERN_INFO
+							       "icn: (%s) Leased-line mode disabled\n",
+							       CID);
+							cmd.command = ISDN_STAT_RUN;
+							cmd.driver = card->myid;
+							cmd.arg = 0;
+							card->interface.statcallb(&cmd);
+						}
+					}
+					return 0;
+				default:
+					return -EINVAL;
+			}
+			break;
+		case ISDN_CMD_DIAL:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if (card->leased)
+				break;
+			if ((c->arg & 255) < ICN_BCH) {
+				char *p;
+				char dial[50];
+				char dcode[4];
+
+				a = c->arg;
+				p = c->parm.setup.phone;
+				if (*p == 's' || *p == 'S') {
+					/* Dial for SPV */
+					p++;
+					strcpy(dcode, "SCA");
+				} else
+					/* Normal Dial */
+					strcpy(dcode, "CAL");
+				strcpy(dial, p);
+				sprintf(cbuf, "%02d;D%s_R%s,%02d,%02d,%s\n", (int) (a + 1),
+					dcode, dial, c->parm.setup.si1,
+				c->parm.setup.si2, c->parm.setup.eazmsn);
+				i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+			}
+			break;
+		case ISDN_CMD_ACCEPTD:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if (c->arg < ICN_BCH) {
+				a = c->arg + 1;
+				if (card->fw_rev >= 300) {
+					switch (card->l2_proto[a - 1]) {
+						case ISDN_PROTO_L2_X75I:
+							sprintf(cbuf, "%02d;BX75\n", (int) a);
+							break;
+						case ISDN_PROTO_L2_HDLC:
+							sprintf(cbuf, "%02d;BTRA\n", (int) a);
+							break;
+					}
+					i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+				}
+				sprintf(cbuf, "%02d;DCON_R\n", (int) a);
+				i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+			}
+			break;
+		case ISDN_CMD_ACCEPTB:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if (c->arg < ICN_BCH) {
+				a = c->arg + 1;
+				if (card->fw_rev >= 300)
+					switch (card->l2_proto[a - 1]) {
+						case ISDN_PROTO_L2_X75I:
+							sprintf(cbuf, "%02d;BCON_R,BX75\n", (int) a);
+							break;
+						case ISDN_PROTO_L2_HDLC:
+							sprintf(cbuf, "%02d;BCON_R,BTRA\n", (int) a);
+							break;
+				} else
+					sprintf(cbuf, "%02d;BCON_R\n", (int) a);
+				i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+			}
+			break;
+		case ISDN_CMD_HANGUP:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if (c->arg < ICN_BCH) {
+				a = c->arg + 1;
+				sprintf(cbuf, "%02d;BDIS_R\n%02d;DDIS_R\n", (int) a, (int) a);
+				i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+			}
+			break;
+		case ISDN_CMD_SETEAZ:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if (card->leased)
+				break;
+			if (c->arg < ICN_BCH) {
+				a = c->arg + 1;
+				if (card->ptype == ISDN_PTYPE_EURO) {
+					sprintf(cbuf, "%02d;MS%s%s\n", (int) a,
+						c->parm.num[0] ? "N" : "ALL", c->parm.num);
+				} else
+					sprintf(cbuf, "%02d;EAZ%s\n", (int) a,
+						c->parm.num[0] ? c->parm.num : "0123456789");
+				i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+			}
+			break;
+		case ISDN_CMD_CLREAZ:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if (card->leased)
+				break;
+			if (c->arg < ICN_BCH) {
+				a = c->arg + 1;
+				if (card->ptype == ISDN_PTYPE_EURO)
+					sprintf(cbuf, "%02d;MSNC\n", (int) a);
+				else
+					sprintf(cbuf, "%02d;EAZC\n", (int) a);
+				i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+			}
+			break;
+		case ISDN_CMD_SETL2:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if ((c->arg & 255) < ICN_BCH) {
+				a = c->arg;
+				switch (a >> 8) {
+					case ISDN_PROTO_L2_X75I:
+						sprintf(cbuf, "%02d;BX75\n", (int) (a & 255) + 1);
+						break;
+					case ISDN_PROTO_L2_HDLC:
+						sprintf(cbuf, "%02d;BTRA\n", (int) (a & 255) + 1);
+						break;
+					default:
+						return -EINVAL;
+				}
+				i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+				card->l2_proto[a & 255] = (a >> 8);
+			}
+			break;
+		case ISDN_CMD_GETL2:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if ((c->arg & 255) < ICN_BCH)
+				return card->l2_proto[c->arg & 255];
+			else
+				return -ENODEV;
+		case ISDN_CMD_SETL3:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			return 0;
+		case ISDN_CMD_GETL3:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if ((c->arg & 255) < ICN_BCH)
+				return ISDN_PROTO_L3_TRANS;
+			else
+				return -ENODEV;
+		case ISDN_CMD_GETEAZ:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			break;
+		case ISDN_CMD_SETSIL:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			break;
+		case ISDN_CMD_GETSIL:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			break;
+		case ISDN_CMD_LOCK:
+			MOD_INC_USE_COUNT;
+			break;
+		case ISDN_CMD_UNLOCK:
+			MOD_DEC_USE_COUNT;
+			break;
+		default:
+			return -EINVAL;
+	}
+	return 0;
 }
 
 /*
- * Wrapper functions for interface to linklevel
+ * Find card with given driverId
  */
-static int if_command(isdn_ctrl * c)
-{
-        icn_card *card = icn_findcard(c->driver);
-
-        if (card)
-                return (icn_command(c, card));
-        printk(KERN_ERR
-               "icn: if_command %d called with invalid driverId %d!\n",
-		c->command, c->driver);
-        return -ENODEV;
-}
-
-static int if_writecmd(const u_char * buf, int len, int user, int id, int channel)
-{
-        icn_card *card = icn_findcard(id);
-
-        if (card) {
-                if (!card->flags & ICN_FLAGS_RUNNING)
-                        return -ENODEV;
-                return (icn_writecmd(buf, len, user, card));
-        }
-        printk(KERN_ERR
-               "icn: if_writecmd called with invalid driverId!\n");
-        return -ENODEV;
-}
-
-static int if_readstatus(u_char * buf, int len, int user, int id, int channel)
+static inline icn_card *
+icn_findcard(int driverid)
 {
-        icn_card *card = icn_findcard(id);
+	icn_card *p = cards;
 
-        if (card) {
-                if (!card->flags & ICN_FLAGS_RUNNING)
-                        return -ENODEV;
-                return (icn_readstatus(buf, len, user, card));
-        }
-        printk(KERN_ERR
-               "icn: if_readstatus called with invalid driverId!\n");
-        return -ENODEV;
+	while (p) {
+		if (p->myid == driverid)
+			return p;
+		p = p->next;
+	}
+	return (icn_card *) 0;
 }
 
-static int if_sendbuf(int id, int channel, struct sk_buff *skb)
+/*
+ * Wrapper functions for interface to linklevel
+ */
+static int
+if_command(isdn_ctrl * c)
 {
-        icn_card *card = icn_findcard(id);
+	icn_card *card = icn_findcard(c->driver);
 
-        if (card) {
-                if (!card->flags & ICN_FLAGS_RUNNING)
-                        return -ENODEV;
-                return (icn_sendbuf(channel, skb, card));
-        }
-        printk(KERN_ERR
-               "icn: if_sendbuf called with invalid driverId!\n");
-        return -ENODEV;
+	if (card)
+		return (icn_command(c, card));
+	printk(KERN_ERR
+	       "icn: if_command %d called with invalid driverId %d!\n",
+	       c->command, c->driver);
+	return -ENODEV;
+}
+
+static int
+if_writecmd(const u_char * buf, int len, int user, int id, int channel)
+{
+	icn_card *card = icn_findcard(id);
+
+	if (card) {
+		if (!card->flags & ICN_FLAGS_RUNNING)
+			return -ENODEV;
+		return (icn_writecmd(buf, len, user, card));
+	}
+	printk(KERN_ERR
+	       "icn: if_writecmd called with invalid driverId!\n");
+	return -ENODEV;
+}
+
+static int
+if_readstatus(u_char * buf, int len, int user, int id, int channel)
+{
+	icn_card *card = icn_findcard(id);
+
+	if (card) {
+		if (!card->flags & ICN_FLAGS_RUNNING)
+			return -ENODEV;
+		return (icn_readstatus(buf, len, user, card));
+	}
+	printk(KERN_ERR
+	       "icn: if_readstatus called with invalid driverId!\n");
+	return -ENODEV;
+}
+
+static int
+if_sendbuf(int id, int channel, struct sk_buff *skb)
+{
+	icn_card *card = icn_findcard(id);
+
+	if (card) {
+		if (!card->flags & ICN_FLAGS_RUNNING)
+			return -ENODEV;
+		return (icn_sendbuf(channel, skb, card));
+	}
+	printk(KERN_ERR
+	       "icn: if_sendbuf called with invalid driverId!\n");
+	return -ENODEV;
 }
 
 /*
  * Allocate a new card-struct, initialize it
  * link it into cards-list and register it at linklevel.
  */
-static icn_card *icn_initcard(int port, char *id) {
-        icn_card *card;
-        int i;
-
-        if (!(card = (icn_card *) kmalloc(sizeof(icn_card), GFP_KERNEL))) {
-                printk(KERN_WARNING
-                       "icn: (%s) Could not allocate card-struct.\n", id);
-                return (icn_card *)0;
-        }
-        memset((char *) card, 0, sizeof(icn_card));
-        card->port = port;
-        card->interface.channels = ICN_BCH;
-        card->interface.maxbufsize = 4000;
-        card->interface.command = if_command;
+static icn_card *
+icn_initcard(int port, char *id)
+{
+	icn_card *card;
+	int i;
+
+	if (!(card = (icn_card *) kmalloc(sizeof(icn_card), GFP_KERNEL))) {
+		printk(KERN_WARNING
+		       "icn: (%s) Could not allocate card-struct.\n", id);
+		return (icn_card *) 0;
+	}
+	memset((char *) card, 0, sizeof(icn_card));
+	card->port = port;
+	card->interface.channels = ICN_BCH;
+	card->interface.maxbufsize = 4000;
+	card->interface.command = if_command;
 	card->interface.writebuf_skb = if_sendbuf;
-        card->interface.writecmd = if_writecmd;
-        card->interface.readstat = if_readstatus;
-        card->interface.features = ISDN_FEATURE_L2_X75I |
-                ISDN_FEATURE_L2_HDLC |
-                ISDN_FEATURE_L3_TRANS |
-                ISDN_FEATURE_P_UNKNOWN;
-        card->ptype = ISDN_PTYPE_UNKNOWN;
-        strncpy(card->interface.id, id, sizeof(card->interface.id) - 1);
-        card->msg_buf_write = card->msg_buf;
-        card->msg_buf_read = card->msg_buf;
-        card->msg_buf_end = &card->msg_buf[sizeof(card->msg_buf) - 1];
-        for (i=0;i<ICN_BCH;i++) {
-                card->l2_proto[i] = ISDN_PROTO_L2_X75I;
-                skb_queue_head_init(&card->spqueue[i]);
-        }
-        card->next = cards;
-        cards = card;
-        if (!register_isdn(&card->interface)) {
-                cards = cards->next;
-                printk(KERN_WARNING
-                       "icn: Unable to register %s\n", id);
-                kfree(card);
-                return (icn_card*)0;
-        }
-        card->myid = card->interface.channels;
-        sprintf(card->regname, "icn-isdn (%s)", card->interface.id);
-        return card;
-}
-
-static int icn_addcard(int port, char *id1, char *id2)
-{
-        ulong flags;
-        icn_card *card;
-        icn_card *card2;
-
-        save_flags(flags);
-        cli();
-        if (!(card = icn_initcard(port,id1))) {
-                restore_flags(flags);
-                return -EIO;
-        }
-        if (!strlen(id2)) {
-                restore_flags(flags);
-                printk(KERN_INFO
-                       "icn: (%s) ICN-2B, port 0x%x added\n",
-                       card->interface.id, port);
-                return 0;
-        }
-        if (!(card2 = icn_initcard(port,id2))) {
-                restore_flags(flags);
-                printk(KERN_INFO
-                       "icn: (%s) half ICN-4B, port 0x%x added\n",
-                       card2->interface.id, port);
-                return 0;
-        }
-        card->doubleS0 = 1;
-        card->secondhalf = 0;
-        card->other = card2;
-        card2->doubleS0 = 1;
-        card2->secondhalf = 1;
-        card2->other = card;
-        restore_flags(flags);
-        printk(KERN_INFO
-               "icn: (%s and %s) ICN-4B, port 0x%x added\n",
-               card->interface.id, card2->interface.id, port);
-        return 0;
+	card->interface.writecmd = if_writecmd;
+	card->interface.readstat = if_readstatus;
+	card->interface.features = ISDN_FEATURE_L2_X75I |
+	    ISDN_FEATURE_L2_HDLC |
+	    ISDN_FEATURE_L3_TRANS |
+	    ISDN_FEATURE_P_UNKNOWN;
+	card->ptype = ISDN_PTYPE_UNKNOWN;
+	strncpy(card->interface.id, id, sizeof(card->interface.id) - 1);
+	card->msg_buf_write = card->msg_buf;
+	card->msg_buf_read = card->msg_buf;
+	card->msg_buf_end = &card->msg_buf[sizeof(card->msg_buf) - 1];
+	for (i = 0; i < ICN_BCH; i++) {
+		card->l2_proto[i] = ISDN_PROTO_L2_X75I;
+		skb_queue_head_init(&card->spqueue[i]);
+	}
+	card->next = cards;
+	cards = card;
+	if (!register_isdn(&card->interface)) {
+		cards = cards->next;
+		printk(KERN_WARNING
+		       "icn: Unable to register %s\n", id);
+		kfree(card);
+		return (icn_card *) 0;
+	}
+	card->myid = card->interface.channels;
+	sprintf(card->regname, "icn-isdn (%s)", card->interface.id);
+	return card;
+}
+
+static int
+icn_addcard(int port, char *id1, char *id2)
+{
+	ulong flags;
+	icn_card *card;
+	icn_card *card2;
+
+	save_flags(flags);
+	cli();
+	if (!(card = icn_initcard(port, id1))) {
+		restore_flags(flags);
+		return -EIO;
+	}
+	if (!strlen(id2)) {
+		restore_flags(flags);
+		printk(KERN_INFO
+		       "icn: (%s) ICN-2B, port 0x%x added\n",
+		       card->interface.id, port);
+		return 0;
+	}
+	if (!(card2 = icn_initcard(port, id2))) {
+		restore_flags(flags);
+		printk(KERN_INFO
+		       "icn: (%s) half ICN-4B, port 0x%x added\n",
+		       card2->interface.id, port);
+		return 0;
+	}
+	card->doubleS0 = 1;
+	card->secondhalf = 0;
+	card->other = card2;
+	card2->doubleS0 = 1;
+	card2->secondhalf = 1;
+	card2->other = card;
+	restore_flags(flags);
+	printk(KERN_INFO
+	       "icn: (%s and %s) ICN-4B, port 0x%x added\n",
+	       card->interface.id, card2->interface.id, port);
+	return 0;
 }
 
 #ifdef MODULE
 #define icn_init init_module
 #else
-void icn_setup(char *str, int *ints)
+void
+icn_setup(char *str, int *ints)
 {
-        char *p;
-        static char sid[20];
-        static char sid2[20];
-
-        if (ints[0])
-                portbase = ints[1];
-        if (ints[0]>1)
-                membase = ints[2];
-        if (strlen(str)) {
-                strcpy(sid,str);
-                icn_id = sid;
-                if ((p = strchr(sid,','))) {
-                        *p++ = 0;
-                        strcpy(sid2,p);
-                        icn_id2 = sid2;
-                }
-        }
+	char *p;
+	static char sid[20];
+	static char sid2[20];
+
+	if (ints[0])
+		portbase = ints[1];
+	if (ints[0] > 1)
+		membase = ints[2];
+	if (strlen(str)) {
+		strcpy(sid, str);
+		icn_id = sid;
+		if ((p = strchr(sid, ','))) {
+			*p++ = 0;
+			strcpy(sid2, p);
+			icn_id2 = sid2;
+		}
+	}
 }
 #endif
 
-int icn_init(void)
-{
-        char *p;
-        char rev[10];
-
-        memset(&dev, 0, sizeof(icn_dev));
-        dev.shmem = (icn_shmem *) ((unsigned long)membase & 0x0ffc000);
-        dev.channel = -1;
-        dev.mcard   = NULL;
-
-        /* No symbols to export, hide all symbols */
-        register_symtab(NULL);
-
-        if ((p = strchr(revision, ':'))) {
-                strcpy(rev, p + 1);
-                p = strchr(rev, '$');
-                *p = 0;
-        } else
-                strcpy(rev, " ??? ");
-        printk(KERN_NOTICE "ICN-ISDN-driver Rev%smem=0x%08lx\n", rev,
-               (ulong) dev.shmem);
-        return (icn_addcard(portbase,icn_id,icn_id2));
+int
+icn_init(void)
+{
+	char *p;
+	char rev[10];
+
+	memset(&dev, 0, sizeof(icn_dev));
+	dev.shmem = (icn_shmem *) ((unsigned long) membase & 0x0ffc000);
+	dev.channel = -1;
+	dev.mcard = NULL;
+	dev.firstload = 1;
+
+	/* No symbols to export, hide all symbols */
+#if (LINUX_VERSION_CODE < 0x020111)
+	register_symtab(NULL);
+#else
+	EXPORT_NO_SYMBOLS;
+#endif
+
+	if ((p = strchr(revision, ':'))) {
+		strcpy(rev, p + 1);
+		p = strchr(rev, '$');
+		*p = 0;
+	} else
+		strcpy(rev, " ??? ");
+	printk(KERN_NOTICE "ICN-ISDN-driver Rev%smem=0x%08lx\n", rev,
+	       (ulong) dev.shmem);
+	return (icn_addcard(portbase, icn_id, icn_id2));
 }
 
 #ifdef MODULE
-void cleanup_module(void)
+void
+cleanup_module(void)
 {
-        isdn_ctrl cmd;
-        icn_card *card = cards;
-        icn_card *last;
-        int i;
-
-        icn_stopallcards();
-        while (card) {
-                cmd.command = ISDN_STAT_UNLOAD;
-                cmd.driver = card->myid;
-                card->interface.statcallb(&cmd);
-                if (card->rvalid) {
-                        OUTB_P(0, ICN_RUN);    /* Reset Controller     */
-                        OUTB_P(0, ICN_MAPRAM); /* Disable RAM          */
-                        if (card->secondhalf || (!card->doubleS0)) {
-                                release_region(card->port, ICN_PORTLEN);
-                                card->rvalid = 0;
-                        }
-                        for (i = 0; i < ICN_BCH; i++)
-                                icn_free_queue(&card->spqueue[i]);
-                }
-                card = card->next;
-        }
-        card = cards;
-        while (card) {
-                last = card;
-                card = card->next;
-                kfree(last);
-        }
-        if (dev.mvalid)
-                release_shmem((ulong) dev.shmem, 0x4000);
-        printk(KERN_NOTICE "ICN-ISDN-driver unloaded\n");
+	isdn_ctrl cmd;
+	icn_card *card = cards;
+	icn_card *last;
+	int i;
+
+	icn_stopallcards();
+	while (card) {
+		cmd.command = ISDN_STAT_UNLOAD;
+		cmd.driver = card->myid;
+		card->interface.statcallb(&cmd);
+		if (card->rvalid) {
+			OUTB_P(0, ICN_RUN);	/* Reset Controller     */
+			OUTB_P(0, ICN_MAPRAM);	/* Disable RAM          */
+			if (card->secondhalf || (!card->doubleS0)) {
+				release_region(card->port, ICN_PORTLEN);
+				card->rvalid = 0;
+			}
+			for (i = 0; i < ICN_BCH; i++)
+				icn_free_queue(card, i);
+		}
+		card = card->next;
+	}
+	card = cards;
+	while (card) {
+		last = card;
+		card = card->next;
+		kfree(last);
+	}
+	if (dev.mvalid)
+		release_shmem((ulong) dev.shmem, 0x4000);
+	printk(KERN_NOTICE "ICN-ISDN-driver unloaded\n");
 }
 #endif
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/icn/icn.h linux/drivers/isdn/icn/icn.h
--- v2.0.30/linux/drivers/isdn/icn/icn.h	Tue Nov 12 22:36:19 1996
+++ linux/drivers/isdn/icn/icn.h	Mon Aug  4 17:34:00 1997
@@ -1,5 +1,5 @@
-/* $Id: icn.h,v 1.22 1996/11/13 02:37:33 fritz Exp $
- *
+/* $Id: icn.h,v 1.26 1997/02/14 12:23:16 fritz Exp $
+
  * ISDN lowlevel-module for the ICN active ISDN-Card.
  *
  * Copyright 1994 by Fritz Elfert (fritz@wuemaus.franken.de)
@@ -16,9 +16,23 @@
  *
  * 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. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: icn.h,v $
+ * Revision 1.26  1997/02/14 12:23:16  fritz
+ * Added support for new insmod parameter handling.
+ *
+ * Revision 1.25  1997/02/10 10:10:31  fritz
+ * Changes for Kernel 2.1.X compatibility.
+ * Enhanced initialization, can recover from
+ * misconfiguration by other autoprobing drivers.
+ *
+ * Revision 1.24  1997/01/29 22:34:46  fritz
+ * Cleanup, Corrected D64S setup of 2nd channel.
+ *
+ * Revision 1.23  1996/12/17 18:47:55  tsbogend
+ * changed timeouts from absolute numbers to HZ based values
+ *
  * Revision 1.22  1996/11/13 02:37:33  fritz
  * Added delay include.
  *
@@ -113,9 +127,9 @@
 
 /* Struct for adding new cards */
 typedef struct icn_cdef {
-        int port;
-        char id1[10];
-        char id2[10];
+	int port;
+	char id1[10];
+	char id2[10];
 } icn_cdef;
 
 #if defined(__KERNEL__) || defined(__DEBUGVAR__)
@@ -141,7 +155,7 @@
 #include <linux/delay.h>
 #include <linux/isdnif.h>
 
-#endif /* __KERNEL__ */
+#endif                          /* __KERNEL__ */
 
 /* some useful macros for debugging */
 #ifdef ICN_DEBUG_PORT
@@ -155,138 +169,154 @@
 #define ICN_PORTLEN (0x04)
 #define ICN_MEMADDR 0x0d0000
 
-#define ICN_FLAGS_B1ACTIVE 1     /* B-Channel-1 is open                     */
-#define ICN_FLAGS_B2ACTIVE 2     /* B-Channel-2 is open                     */
-#define ICN_FLAGS_RUNNING  4     /* Cards driver activated                  */
-#define ICN_FLAGS_RBTIMER  8     /* cyclic scheduling of B-Channel-poll     */
-
-#define ICN_BOOT_TIMEOUT1  100   /* Delay for Boot-download (jiffies)       */
-#define ICN_CHANLOCK_DELAY  10   /* Delay for Channel-mapping (jiffies)     */
-
-#define ICN_TIMER_BCREAD 1       /* B-Channel poll-cycle                    */
-#define ICN_TIMER_DCREAD 50      /* D-Channel poll-cycle                    */
-
-#define ICN_CODE_STAGE1 4096     /* Size of bootcode                        */
-#define ICN_CODE_STAGE2 65536    /* Size of protocol-code                   */
-
-#define ICN_MAX_SQUEUE 8000      /* Max. outstanding send-data (2* hw-buf.) */
-#define ICN_FRAGSIZE (250)       /* Max. size of send-fragments             */
-#define ICN_BCH 2                /* Number of supported channels per card   */
+#define ICN_FLAGS_B1ACTIVE 1    /* B-Channel-1 is open                     */
+#define ICN_FLAGS_B2ACTIVE 2    /* B-Channel-2 is open                     */
+#define ICN_FLAGS_RUNNING  4    /* Cards driver activated                  */
+#define ICN_FLAGS_RBTIMER  8    /* cyclic scheduling of B-Channel-poll     */
+
+#define ICN_BOOT_TIMEOUT1  (HZ) /* Delay for Boot-download (jiffies)       */
+#define ICN_CHANLOCK_DELAY (HZ/10)	/* Delay for Channel-mapping (jiffies)     */
+
+#define ICN_TIMER_BCREAD (HZ/100)	/* B-Channel poll-cycle                    */
+#define ICN_TIMER_DCREAD (HZ/2) /* D-Channel poll-cycle                    */
+
+#define ICN_CODE_STAGE1 4096    /* Size of bootcode                        */
+#define ICN_CODE_STAGE2 65536   /* Size of protocol-code                   */
+
+#define ICN_MAX_SQUEUE 8000     /* Max. outstanding send-data (2* hw-buf.) */
+#define ICN_FRAGSIZE (250)      /* Max. size of send-fragments             */
+#define ICN_BCH 2               /* Number of supported channels per card   */
 
 /* type-definitions for accessing the mmap-io-areas */
 
-#define SHM_DCTL_OFFSET (0)      /* Offset to data-controlstructures in shm */
-#define SHM_CCTL_OFFSET (0x1d2)  /* Offset to comm-controlstructures in shm */
-#define SHM_CBUF_OFFSET (0x200)  /* Offset to comm-buffers in shm           */
-#define SHM_DBUF_OFFSET (0x2000) /* Offset to data-buffers in shm           */
+#define SHM_DCTL_OFFSET (0)     /* Offset to data-controlstructures in shm */
+#define SHM_CCTL_OFFSET (0x1d2) /* Offset to comm-controlstructures in shm */
+#define SHM_CBUF_OFFSET (0x200) /* Offset to comm-buffers in shm           */
+#define SHM_DBUF_OFFSET (0x2000)	/* Offset to data-buffers in shm           */
 
 /*
  * Layout of card's data buffers
  */
 typedef struct {
-        unsigned char length;             /* Bytecount of fragment (max 250)    */
-        unsigned char endflag;            /* 0=last frag., 0xff=frag. continued */
-        unsigned char data[ICN_FRAGSIZE]; /* The data                           */
-        /* Fill to 256 bytes */
-        char unused[0x100 - ICN_FRAGSIZE - 2];
+	unsigned char length;   /* Bytecount of fragment (max 250)    */
+	unsigned char endflag;  /* 0=last frag., 0xff=frag. continued */
+	unsigned char data[ICN_FRAGSIZE];	/* The data                           */
+	/* Fill to 256 bytes */
+	char unused[0x100 - ICN_FRAGSIZE - 2];
 } frag_buf;
 
 /*
  * Layout of card's shared memory
  */
 typedef union {
-        struct {
-                unsigned char scns;            /* Index to free SendFrag.             */
-                unsigned char scnr;            /* Index to active SendFrag   READONLY */
-                unsigned char ecns;            /* Index to free RcvFrag.     READONLY */
-                unsigned char ecnr;            /* Index to valid RcvFrag              */
-                char unused[6];
-                unsigned short fuell1;         /* Internal Buf Bytecount              */
-        } data_control;
-        struct {
-                char unused[SHM_CCTL_OFFSET];
-                unsigned char iopc_i;          /* Read-Ptr Status-Queue      READONLY */
-                unsigned char iopc_o;          /* Write-Ptr Status-Queue              */
-                unsigned char pcio_i;          /* Write-Ptr Command-Queue             */
-                unsigned char pcio_o;          /* Read-Ptr Command Queue     READONLY */
-        } comm_control;
-        struct {
-                char unused[SHM_CBUF_OFFSET];
-                unsigned char pcio_buf[0x100]; /* Ring-Buffer Command-Queue           */
-                unsigned char iopc_buf[0x100]; /* Ring-Buffer Status-Queue            */
-        } comm_buffers;
-        struct {
-                char unused[SHM_DBUF_OFFSET];
-                frag_buf receive_buf[0x10];
-                frag_buf send_buf[0x10];
-        } data_buffers;
+	struct {
+		unsigned char scns;	/* Index to free SendFrag.             */
+		unsigned char scnr;	/* Index to active SendFrag   READONLY */
+		unsigned char ecns;	/* Index to free RcvFrag.     READONLY */
+		unsigned char ecnr;	/* Index to valid RcvFrag              */
+		char unused[6];
+		unsigned short fuell1;	/* Internal Buf Bytecount              */
+	} data_control;
+	struct {
+		char unused[SHM_CCTL_OFFSET];
+		unsigned char iopc_i;	/* Read-Ptr Status-Queue      READONLY */
+		unsigned char iopc_o;	/* Write-Ptr Status-Queue              */
+		unsigned char pcio_i;	/* Write-Ptr Command-Queue             */
+		unsigned char pcio_o;	/* Read-Ptr Command Queue     READONLY */
+	} comm_control;
+	struct {
+		char unused[SHM_CBUF_OFFSET];
+		unsigned char pcio_buf[0x100];	/* Ring-Buffer Command-Queue           */
+		unsigned char iopc_buf[0x100];	/* Ring-Buffer Status-Queue            */
+	} comm_buffers;
+	struct {
+		char unused[SHM_DBUF_OFFSET];
+		frag_buf receive_buf[0x10];
+		frag_buf send_buf[0x10];
+	} data_buffers;
 } icn_shmem;
 
 /*
  * Per card driver data
  */
 typedef struct icn_card {
-        struct icn_card *next;        /* Pointer to next device struct    */
-        struct icn_card *other;       /* Pointer to other card for ICN4B  */
-        unsigned short port;          /* Base-port-address                */
-        int myid;                     /* Driver-Nr. assigned by linklevel */
-        int rvalid;                   /* IO-portregion has been requested */
-        int leased;                   /* Flag: This Adapter is connected  */
-                                      /*       to a leased line           */
-        unsigned short flags;         /* Statusflags                      */
-        int doubleS0;                 /* Flag: ICN4B                      */
-        int secondhalf;               /* Flag: Second half of a doubleS0  */
-        int fw_rev;                   /* Firmware revision loaded         */
-        int ptype;                    /* Protocol type (1TR6 or Euro)     */
-        struct timer_list st_timer;   /* Timer for Status-Polls           */
-        struct timer_list rb_timer;   /* Timer for B-Channel-Polls        */
-        u_char rcvbuf[ICN_BCH][4096]; /* B-Channel-Receive-Buffers        */
-        int rcvidx[ICN_BCH];          /* Index for above buffers          */
-        int l2_proto[ICN_BCH];        /* Current layer-2-protocol         */
-        isdn_if interface;            /* Interface to upper layer         */
-        int iptr;                     /* Index to imsg-buffer             */
-        char imsg[60];                /* Internal buf for status-parsing  */
-        char msg_buf[2048];           /* Buffer for status-messages       */
-        char *msg_buf_write;          /* Writepointer for statusbuffer    */
-        char *msg_buf_read;           /* Readpointer for statusbuffer     */
-        char *msg_buf_end;            /* Pointer to end of statusbuffer   */
-        int sndcount[ICN_BCH];        /* Byte-counters for B-Ch.-send     */
-        struct sk_buff_head
-                spqueue[ICN_BCH];     /* Sendqueue                        */
-        char regname[35];             /* Name used for request_region     */
-        u_char xmit_lock[ICN_BCH];    /* Semaphore for pollbchan_send()   */
+	struct icn_card *next;  /* Pointer to next device struct    */
+	struct icn_card *other; /* Pointer to other card for ICN4B  */
+	unsigned short port;    /* Base-port-address                */
+	int myid;               /* Driver-Nr. assigned by linklevel */
+	int rvalid;             /* IO-portregion has been requested */
+	int leased;             /* Flag: This Adapter is connected  */
+	/*       to a leased line           */
+	unsigned short flags;   /* Statusflags                      */
+	int doubleS0;           /* Flag: ICN4B                      */
+	int secondhalf;         /* Flag: Second half of a doubleS0  */
+	int fw_rev;             /* Firmware revision loaded         */
+	int ptype;              /* Protocol type (1TR6 or Euro)     */
+	struct timer_list st_timer;	/* Timer for Status-Polls           */
+	struct timer_list rb_timer;	/* Timer for B-Channel-Polls        */
+	u_char rcvbuf[ICN_BCH][4096];	/* B-Channel-Receive-Buffers        */
+	int rcvidx[ICN_BCH];    /* Index for above buffers          */
+	int l2_proto[ICN_BCH];  /* Current layer-2-protocol         */
+	isdn_if interface;      /* Interface to upper layer         */
+	int iptr;               /* Index to imsg-buffer             */
+	char imsg[60];          /* Internal buf for status-parsing  */
+	char msg_buf[2048];     /* Buffer for status-messages       */
+	char *msg_buf_write;    /* Writepointer for statusbuffer    */
+	char *msg_buf_read;     /* Readpointer for statusbuffer     */
+	char *msg_buf_end;      /* Pointer to end of statusbuffer   */
+	int sndcount[ICN_BCH];  /* Byte-counters for B-Ch.-send     */
+	struct sk_buff_head
+	 spqueue[ICN_BCH];      /* Sendqueue                        */
+	char regname[35];       /* Name used for request_region     */
+	u_char xmit_lock[ICN_BCH];	/* Semaphore for pollbchan_send()   */
 } icn_card;
 
 /*
  * Main driver data
  */
 typedef struct icn_dev {
-        icn_shmem *shmem;             /* Pointer to memory-mapped-buffers */
-        int mvalid;                   /* IO-shmem has been requested      */
-        int channel;                  /* Currently mapped channel         */
-        struct icn_card *mcard;       /* Currently mapped card            */
-        int chanlock;                 /* Semaphore for channel-mapping    */
+	icn_shmem *shmem;       /* Pointer to memory-mapped-buffers */
+	int mvalid;             /* IO-shmem has been requested      */
+	int channel;            /* Currently mapped channel         */
+	struct icn_card *mcard; /* Currently mapped card            */
+	int chanlock;           /* Semaphore for channel-mapping    */
+	int firstload;          /* Flag: firmware never loaded      */
 } icn_dev;
 
 typedef icn_dev *icn_devptr;
 
 #ifdef __KERNEL__
 
-static icn_card *cards  = (icn_card *) 0;
-static u_char   chan2bank[] = { 0, 4, 8, 12 }; /* for icn_map_channel() */ 
+static icn_card *cards = (icn_card *) 0;
+static u_char chan2bank[] =
+{0, 4, 8, 12};                  /* for icn_map_channel() */
 
-static icn_dev  dev;
+static icn_dev dev;
 
 /* With modutils >= 1.1.67 Integers can be changed while loading a
  * module. For this reason define the Port-Base an Shmem-Base as
  * integers.
  */
-int portbase  = ICN_BASEADDR;
-int membase   = ICN_MEMADDR;
-char *icn_id  = "\0";
-char *icn_id2 = "\0";
+static int portbase = ICN_BASEADDR;
+static int membase = ICN_MEMADDR;
+static char *icn_id = "\0";
+static char *icn_id2 = "\0";
+
+#ifdef MODULE
+#if (LINUX_VERSION_CODE > 0x020111)
+MODULE_AUTHOR("Fritz Elfert");
+MODULE_PARM(portbase, "i");
+MODULE_PARM_DESC(portbase, "Port adress of first card");
+MODULE_PARM(membase, "i");
+MODULE_PARM_DESC(membase, "Shared memory adress of all cards");
+MODULE_PARM(icn_id, "s");
+MODULE_PARM_DESC(icn_id, "ID-String of first card");
+MODULE_PARM(icn_id2, "s");
+MODULE_PARM_DESC(icn_id2, "ID-String of first card, second S0 (4B only)");
+#endif
+#endif
 
-#endif                                /* __KERNEL__ */
+#endif                          /* __KERNEL__ */
 
 /* Utility-Macros */
 
@@ -298,11 +328,11 @@
 
 /* Return true, if there is a free transmit-buffer */
 #define sbfree (((readb(&dev.shmem->data_control.scns)+1) & 0xf) != \
-                readb(&dev.shmem->data_control.scnr))
+		readb(&dev.shmem->data_control.scnr))
 
 /* Switch to next transmit-buffer */
 #define sbnext (writeb((readb(&dev.shmem->data_control.scns)+1) & 0xf, \
-                       &dev.shmem->data_control.scns))
+		       &dev.shmem->data_control.scns))
 
 /* Shortcuts for transmit-buffer-access */
 #define sbuf_n dev.shmem->data_control.scns
@@ -312,11 +342,11 @@
 
 /* Return true, if there is receive-data is available */
 #define rbavl  (readb(&dev.shmem->data_control.ecnr) != \
-                readb(&dev.shmem->data_control.ecns))
+		readb(&dev.shmem->data_control.ecns))
 
 /* Switch to next receive-buffer */
 #define rbnext (writeb((readb(&dev.shmem->data_control.ecnr)+1) & 0xf, \
-                       &dev.shmem->data_control.ecnr))
+		       &dev.shmem->data_control.ecnr))
 
 /* Shortcuts for receive-buffer-access */
 #define rbuf_n dev.shmem->data_control.ecnr
@@ -330,8 +360,8 @@
 
 /* Return free space in command-buffer */
 #define cmd_free ((readb(&cmd_i)>=readb(&cmd_o))? \
-                  0x100-readb(&cmd_i)+readb(&cmd_o): \
-                  readb(&cmd_o)-readb(&cmd_i))
+		  0x100-readb(&cmd_i)+readb(&cmd_o): \
+		  readb(&cmd_o)-readb(&cmd_i))
 
 /* Shortcuts for message-buffer-access */
 #define msg_o (dev.shmem->comm_control.iopc_o)
@@ -339,8 +369,8 @@
 
 /* Return length of Message, if avail. */
 #define msg_avail ((readb(&msg_o)>readb(&msg_i))? \
-                   0x100-readb(&msg_o)+readb(&msg_i): \
-                   readb(&msg_i)-readb(&msg_o))
+		   0x100-readb(&msg_o)+readb(&msg_i): \
+		   readb(&msg_i)-readb(&msg_o))
 
 #define CID (card->interface.id)
 
@@ -355,5 +385,5 @@
 #define release_shmem release_region
 #define request_shmem request_region
 
-#endif /* defined(__KERNEL__) || defined(__DEBUGVAR__) */
-#endif /* icn_h */
+#endif                          /* defined(__KERNEL__) || defined(__DEBUGVAR__) */
+#endif                          /* icn_h */
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_audio.c linux/drivers/isdn/isdn_audio.c
--- v2.0.30/linux/drivers/isdn/isdn_audio.c	Fri Jun  7 06:02:40 1996
+++ linux/drivers/isdn/isdn_audio.c	Mon Aug  4 17:34:00 1997
@@ -1,10 +1,10 @@
-/* $Id: isdn_audio.c,v 1.6 1996/06/06 14:43:31 fritz Exp $
- *
+/* $Id: isdn_audio.c,v 1.8 1997/03/02 14:29:16 fritz Exp $
+
  * Linux ISDN subsystem, audio conversion and compression (linklevel).
  *
  * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
  * DTMF code (c) 1996 by Christian Mock (cm@kukuruz.ping.at)
- * 
+ *
  * 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)
@@ -17,9 +17,15 @@
  *
  * 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. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_audio.c,v $
+ * Revision 1.8  1997/03/02 14:29:16  fritz
+ * More ttyI related cleanup.
+ *
+ * Revision 1.7  1997/02/03 22:44:11  fritz
+ * Reformatted according CodingStyle
+ *
  * Revision 1.6  1996/06/06 14:43:31  fritz
  * Changed to support DTMF decoding on audio playback also.
  *
@@ -47,154 +53,158 @@
 #include "isdn_audio.h"
 #include "isdn_common.h"
 
-char *isdn_audio_revision        = "$Revision: 1.6 $";
+char *isdn_audio_revision = "$Revision: 1.8 $";
 
 /*
  * Misc. lookup-tables.
  */
 
 /* ulaw -> signed 16-bit */
-static short isdn_audio_ulaw_to_s16[] = {
-        0x8284, 0x8684, 0x8a84, 0x8e84, 0x9284, 0x9684, 0x9a84, 0x9e84,
-        0xa284, 0xa684, 0xaa84, 0xae84, 0xb284, 0xb684, 0xba84, 0xbe84,
-        0xc184, 0xc384, 0xc584, 0xc784, 0xc984, 0xcb84, 0xcd84, 0xcf84,
-        0xd184, 0xd384, 0xd584, 0xd784, 0xd984, 0xdb84, 0xdd84, 0xdf84,
-        0xe104, 0xe204, 0xe304, 0xe404, 0xe504, 0xe604, 0xe704, 0xe804,
-        0xe904, 0xea04, 0xeb04, 0xec04, 0xed04, 0xee04, 0xef04, 0xf004,
-        0xf0c4, 0xf144, 0xf1c4, 0xf244, 0xf2c4, 0xf344, 0xf3c4, 0xf444,
-        0xf4c4, 0xf544, 0xf5c4, 0xf644, 0xf6c4, 0xf744, 0xf7c4, 0xf844,
-        0xf8a4, 0xf8e4, 0xf924, 0xf964, 0xf9a4, 0xf9e4, 0xfa24, 0xfa64,
-        0xfaa4, 0xfae4, 0xfb24, 0xfb64, 0xfba4, 0xfbe4, 0xfc24, 0xfc64,
-        0xfc94, 0xfcb4, 0xfcd4, 0xfcf4, 0xfd14, 0xfd34, 0xfd54, 0xfd74,
-        0xfd94, 0xfdb4, 0xfdd4, 0xfdf4, 0xfe14, 0xfe34, 0xfe54, 0xfe74,
-        0xfe8c, 0xfe9c, 0xfeac, 0xfebc, 0xfecc, 0xfedc, 0xfeec, 0xfefc,
-        0xff0c, 0xff1c, 0xff2c, 0xff3c, 0xff4c, 0xff5c, 0xff6c, 0xff7c,
-        0xff88, 0xff90, 0xff98, 0xffa0, 0xffa8, 0xffb0, 0xffb8, 0xffc0,
-        0xffc8, 0xffd0, 0xffd8, 0xffe0, 0xffe8, 0xfff0, 0xfff8, 0x0000,
-        0x7d7c, 0x797c, 0x757c, 0x717c, 0x6d7c, 0x697c, 0x657c, 0x617c,
-        0x5d7c, 0x597c, 0x557c, 0x517c, 0x4d7c, 0x497c, 0x457c, 0x417c,
-        0x3e7c, 0x3c7c, 0x3a7c, 0x387c, 0x367c, 0x347c, 0x327c, 0x307c,
-        0x2e7c, 0x2c7c, 0x2a7c, 0x287c, 0x267c, 0x247c, 0x227c, 0x207c,
-        0x1efc, 0x1dfc, 0x1cfc, 0x1bfc, 0x1afc, 0x19fc, 0x18fc, 0x17fc,
-        0x16fc, 0x15fc, 0x14fc, 0x13fc, 0x12fc, 0x11fc, 0x10fc, 0x0ffc,
-        0x0f3c, 0x0ebc, 0x0e3c, 0x0dbc, 0x0d3c, 0x0cbc, 0x0c3c, 0x0bbc,
-        0x0b3c, 0x0abc, 0x0a3c, 0x09bc, 0x093c, 0x08bc, 0x083c, 0x07bc,
-        0x075c, 0x071c, 0x06dc, 0x069c, 0x065c, 0x061c, 0x05dc, 0x059c,
-        0x055c, 0x051c, 0x04dc, 0x049c, 0x045c, 0x041c, 0x03dc, 0x039c,
-        0x036c, 0x034c, 0x032c, 0x030c, 0x02ec, 0x02cc, 0x02ac, 0x028c,
-        0x026c, 0x024c, 0x022c, 0x020c, 0x01ec, 0x01cc, 0x01ac, 0x018c,
-        0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104,
-        0x00f4, 0x00e4, 0x00d4, 0x00c4, 0x00b4, 0x00a4, 0x0094, 0x0084,
-        0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040,
-        0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
+static short isdn_audio_ulaw_to_s16[] =
+{
+	0x8284, 0x8684, 0x8a84, 0x8e84, 0x9284, 0x9684, 0x9a84, 0x9e84,
+	0xa284, 0xa684, 0xaa84, 0xae84, 0xb284, 0xb684, 0xba84, 0xbe84,
+	0xc184, 0xc384, 0xc584, 0xc784, 0xc984, 0xcb84, 0xcd84, 0xcf84,
+	0xd184, 0xd384, 0xd584, 0xd784, 0xd984, 0xdb84, 0xdd84, 0xdf84,
+	0xe104, 0xe204, 0xe304, 0xe404, 0xe504, 0xe604, 0xe704, 0xe804,
+	0xe904, 0xea04, 0xeb04, 0xec04, 0xed04, 0xee04, 0xef04, 0xf004,
+	0xf0c4, 0xf144, 0xf1c4, 0xf244, 0xf2c4, 0xf344, 0xf3c4, 0xf444,
+	0xf4c4, 0xf544, 0xf5c4, 0xf644, 0xf6c4, 0xf744, 0xf7c4, 0xf844,
+	0xf8a4, 0xf8e4, 0xf924, 0xf964, 0xf9a4, 0xf9e4, 0xfa24, 0xfa64,
+	0xfaa4, 0xfae4, 0xfb24, 0xfb64, 0xfba4, 0xfbe4, 0xfc24, 0xfc64,
+	0xfc94, 0xfcb4, 0xfcd4, 0xfcf4, 0xfd14, 0xfd34, 0xfd54, 0xfd74,
+	0xfd94, 0xfdb4, 0xfdd4, 0xfdf4, 0xfe14, 0xfe34, 0xfe54, 0xfe74,
+	0xfe8c, 0xfe9c, 0xfeac, 0xfebc, 0xfecc, 0xfedc, 0xfeec, 0xfefc,
+	0xff0c, 0xff1c, 0xff2c, 0xff3c, 0xff4c, 0xff5c, 0xff6c, 0xff7c,
+	0xff88, 0xff90, 0xff98, 0xffa0, 0xffa8, 0xffb0, 0xffb8, 0xffc0,
+	0xffc8, 0xffd0, 0xffd8, 0xffe0, 0xffe8, 0xfff0, 0xfff8, 0x0000,
+	0x7d7c, 0x797c, 0x757c, 0x717c, 0x6d7c, 0x697c, 0x657c, 0x617c,
+	0x5d7c, 0x597c, 0x557c, 0x517c, 0x4d7c, 0x497c, 0x457c, 0x417c,
+	0x3e7c, 0x3c7c, 0x3a7c, 0x387c, 0x367c, 0x347c, 0x327c, 0x307c,
+	0x2e7c, 0x2c7c, 0x2a7c, 0x287c, 0x267c, 0x247c, 0x227c, 0x207c,
+	0x1efc, 0x1dfc, 0x1cfc, 0x1bfc, 0x1afc, 0x19fc, 0x18fc, 0x17fc,
+	0x16fc, 0x15fc, 0x14fc, 0x13fc, 0x12fc, 0x11fc, 0x10fc, 0x0ffc,
+	0x0f3c, 0x0ebc, 0x0e3c, 0x0dbc, 0x0d3c, 0x0cbc, 0x0c3c, 0x0bbc,
+	0x0b3c, 0x0abc, 0x0a3c, 0x09bc, 0x093c, 0x08bc, 0x083c, 0x07bc,
+	0x075c, 0x071c, 0x06dc, 0x069c, 0x065c, 0x061c, 0x05dc, 0x059c,
+	0x055c, 0x051c, 0x04dc, 0x049c, 0x045c, 0x041c, 0x03dc, 0x039c,
+	0x036c, 0x034c, 0x032c, 0x030c, 0x02ec, 0x02cc, 0x02ac, 0x028c,
+	0x026c, 0x024c, 0x022c, 0x020c, 0x01ec, 0x01cc, 0x01ac, 0x018c,
+	0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104,
+	0x00f4, 0x00e4, 0x00d4, 0x00c4, 0x00b4, 0x00a4, 0x0094, 0x0084,
+	0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040,
+	0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
 };
 
 /* alaw -> signed 16-bit */
-static short isdn_audio_alaw_to_s16[] = {
-        0x13fc, 0xec04, 0x0144, 0xfebc, 0x517c, 0xae84, 0x051c, 0xfae4,
-        0x0a3c, 0xf5c4, 0x0048, 0xffb8, 0x287c, 0xd784, 0x028c, 0xfd74,
-        0x1bfc, 0xe404, 0x01cc, 0xfe34, 0x717c, 0x8e84, 0x071c, 0xf8e4,
-        0x0e3c, 0xf1c4, 0x00c4, 0xff3c, 0x387c, 0xc784, 0x039c, 0xfc64,
-        0x0ffc, 0xf004, 0x0104, 0xfefc, 0x417c, 0xbe84, 0x041c, 0xfbe4,
-        0x083c, 0xf7c4, 0x0008, 0xfff8, 0x207c, 0xdf84, 0x020c, 0xfdf4,
-        0x17fc, 0xe804, 0x018c, 0xfe74, 0x617c, 0x9e84, 0x061c, 0xf9e4,
-        0x0c3c, 0xf3c4, 0x0084, 0xff7c, 0x307c, 0xcf84, 0x030c, 0xfcf4,
-        0x15fc, 0xea04, 0x0164, 0xfe9c, 0x597c, 0xa684, 0x059c, 0xfa64,
-        0x0b3c, 0xf4c4, 0x0068, 0xff98, 0x2c7c, 0xd384, 0x02cc, 0xfd34,
-        0x1dfc, 0xe204, 0x01ec, 0xfe14, 0x797c, 0x8684, 0x07bc, 0xf844,
-        0x0f3c, 0xf0c4, 0x00e4, 0xff1c, 0x3c7c, 0xc384, 0x03dc, 0xfc24,
-        0x11fc, 0xee04, 0x0124, 0xfedc, 0x497c, 0xb684, 0x049c, 0xfb64,
-        0x093c, 0xf6c4, 0x0028, 0xffd8, 0x247c, 0xdb84, 0x024c, 0xfdb4,
-        0x19fc, 0xe604, 0x01ac, 0xfe54, 0x697c, 0x9684, 0x069c, 0xf964,
-        0x0d3c, 0xf2c4, 0x00a4, 0xff5c, 0x347c, 0xcb84, 0x034c, 0xfcb4,
-        0x12fc, 0xed04, 0x0134, 0xfecc, 0x4d7c, 0xb284, 0x04dc, 0xfb24,
-        0x09bc, 0xf644, 0x0038, 0xffc8, 0x267c, 0xd984, 0x026c, 0xfd94,
-        0x1afc, 0xe504, 0x01ac, 0xfe54, 0x6d7c, 0x9284, 0x06dc, 0xf924,
-        0x0dbc, 0xf244, 0x00b4, 0xff4c, 0x367c, 0xc984, 0x036c, 0xfc94,
-        0x0f3c, 0xf0c4, 0x00f4, 0xff0c, 0x3e7c, 0xc184, 0x03dc, 0xfc24,
-        0x07bc, 0xf844, 0x0008, 0xfff8, 0x1efc, 0xe104, 0x01ec, 0xfe14,
-        0x16fc, 0xe904, 0x0174, 0xfe8c, 0x5d7c, 0xa284, 0x05dc, 0xfa24,
-        0x0bbc, 0xf444, 0x0078, 0xff88, 0x2e7c, 0xd184, 0x02ec, 0xfd14,
-        0x14fc, 0xeb04, 0x0154, 0xfeac, 0x557c, 0xaa84, 0x055c, 0xfaa4,
-        0x0abc, 0xf544, 0x0058, 0xffa8, 0x2a7c, 0xd584, 0x02ac, 0xfd54,
-        0x1cfc, 0xe304, 0x01cc, 0xfe34, 0x757c, 0x8a84, 0x075c, 0xf8a4,
-        0x0ebc, 0xf144, 0x00d4, 0xff2c, 0x3a7c, 0xc584, 0x039c, 0xfc64,
-        0x10fc, 0xef04, 0x0114, 0xfeec, 0x457c, 0xba84, 0x045c, 0xfba4,
-        0x08bc, 0xf744, 0x0018, 0xffe8, 0x227c, 0xdd84, 0x022c, 0xfdd4,
-        0x18fc, 0xe704, 0x018c, 0xfe74, 0x657c, 0x9a84, 0x065c, 0xf9a4,
-        0x0cbc, 0xf344, 0x0094, 0xff6c, 0x327c, 0xcd84, 0x032c, 0xfcd4
+static short isdn_audio_alaw_to_s16[] =
+{
+	0x13fc, 0xec04, 0x0144, 0xfebc, 0x517c, 0xae84, 0x051c, 0xfae4,
+	0x0a3c, 0xf5c4, 0x0048, 0xffb8, 0x287c, 0xd784, 0x028c, 0xfd74,
+	0x1bfc, 0xe404, 0x01cc, 0xfe34, 0x717c, 0x8e84, 0x071c, 0xf8e4,
+	0x0e3c, 0xf1c4, 0x00c4, 0xff3c, 0x387c, 0xc784, 0x039c, 0xfc64,
+	0x0ffc, 0xf004, 0x0104, 0xfefc, 0x417c, 0xbe84, 0x041c, 0xfbe4,
+	0x083c, 0xf7c4, 0x0008, 0xfff8, 0x207c, 0xdf84, 0x020c, 0xfdf4,
+	0x17fc, 0xe804, 0x018c, 0xfe74, 0x617c, 0x9e84, 0x061c, 0xf9e4,
+	0x0c3c, 0xf3c4, 0x0084, 0xff7c, 0x307c, 0xcf84, 0x030c, 0xfcf4,
+	0x15fc, 0xea04, 0x0164, 0xfe9c, 0x597c, 0xa684, 0x059c, 0xfa64,
+	0x0b3c, 0xf4c4, 0x0068, 0xff98, 0x2c7c, 0xd384, 0x02cc, 0xfd34,
+	0x1dfc, 0xe204, 0x01ec, 0xfe14, 0x797c, 0x8684, 0x07bc, 0xf844,
+	0x0f3c, 0xf0c4, 0x00e4, 0xff1c, 0x3c7c, 0xc384, 0x03dc, 0xfc24,
+	0x11fc, 0xee04, 0x0124, 0xfedc, 0x497c, 0xb684, 0x049c, 0xfb64,
+	0x093c, 0xf6c4, 0x0028, 0xffd8, 0x247c, 0xdb84, 0x024c, 0xfdb4,
+	0x19fc, 0xe604, 0x01ac, 0xfe54, 0x697c, 0x9684, 0x069c, 0xf964,
+	0x0d3c, 0xf2c4, 0x00a4, 0xff5c, 0x347c, 0xcb84, 0x034c, 0xfcb4,
+	0x12fc, 0xed04, 0x0134, 0xfecc, 0x4d7c, 0xb284, 0x04dc, 0xfb24,
+	0x09bc, 0xf644, 0x0038, 0xffc8, 0x267c, 0xd984, 0x026c, 0xfd94,
+	0x1afc, 0xe504, 0x01ac, 0xfe54, 0x6d7c, 0x9284, 0x06dc, 0xf924,
+	0x0dbc, 0xf244, 0x00b4, 0xff4c, 0x367c, 0xc984, 0x036c, 0xfc94,
+	0x0f3c, 0xf0c4, 0x00f4, 0xff0c, 0x3e7c, 0xc184, 0x03dc, 0xfc24,
+	0x07bc, 0xf844, 0x0008, 0xfff8, 0x1efc, 0xe104, 0x01ec, 0xfe14,
+	0x16fc, 0xe904, 0x0174, 0xfe8c, 0x5d7c, 0xa284, 0x05dc, 0xfa24,
+	0x0bbc, 0xf444, 0x0078, 0xff88, 0x2e7c, 0xd184, 0x02ec, 0xfd14,
+	0x14fc, 0xeb04, 0x0154, 0xfeac, 0x557c, 0xaa84, 0x055c, 0xfaa4,
+	0x0abc, 0xf544, 0x0058, 0xffa8, 0x2a7c, 0xd584, 0x02ac, 0xfd54,
+	0x1cfc, 0xe304, 0x01cc, 0xfe34, 0x757c, 0x8a84, 0x075c, 0xf8a4,
+	0x0ebc, 0xf144, 0x00d4, 0xff2c, 0x3a7c, 0xc584, 0x039c, 0xfc64,
+	0x10fc, 0xef04, 0x0114, 0xfeec, 0x457c, 0xba84, 0x045c, 0xfba4,
+	0x08bc, 0xf744, 0x0018, 0xffe8, 0x227c, 0xdd84, 0x022c, 0xfdd4,
+	0x18fc, 0xe704, 0x018c, 0xfe74, 0x657c, 0x9a84, 0x065c, 0xf9a4,
+	0x0cbc, 0xf344, 0x0094, 0xff6c, 0x327c, 0xcd84, 0x032c, 0xfcd4
 };
 
 /* alaw -> ulaw */
-static char isdn_audio_alaw_to_ulaw[] = {
-        0xab, 0x2b, 0xe3, 0x63, 0x8b, 0x0b, 0xc9, 0x49,
-        0xba, 0x3a, 0xf6, 0x76, 0x9b, 0x1b, 0xd7, 0x57,
-        0xa3, 0x23, 0xdd, 0x5d, 0x83, 0x03, 0xc1, 0x41,
-        0xb2, 0x32, 0xeb, 0x6b, 0x93, 0x13, 0xcf, 0x4f,
-        0xaf, 0x2f, 0xe7, 0x67, 0x8f, 0x0f, 0xcd, 0x4d,
-        0xbe, 0x3e, 0xfe, 0x7e, 0x9f, 0x1f, 0xdb, 0x5b,
-        0xa7, 0x27, 0xdf, 0x5f, 0x87, 0x07, 0xc5, 0x45,
-        0xb6, 0x36, 0xef, 0x6f, 0x97, 0x17, 0xd3, 0x53,
-        0xa9, 0x29, 0xe1, 0x61, 0x89, 0x09, 0xc7, 0x47,
-        0xb8, 0x38, 0xf2, 0x72, 0x99, 0x19, 0xd5, 0x55,
-        0xa1, 0x21, 0xdc, 0x5c, 0x81, 0x01, 0xbf, 0x3f,
-        0xb0, 0x30, 0xe9, 0x69, 0x91, 0x11, 0xce, 0x4e,
-        0xad, 0x2d, 0xe5, 0x65, 0x8d, 0x0d, 0xcb, 0x4b,
-        0xbc, 0x3c, 0xfa, 0x7a, 0x9d, 0x1d, 0xd9, 0x59,
-        0xa5, 0x25, 0xde, 0x5e, 0x85, 0x05, 0xc3, 0x43,
-        0xb4, 0x34, 0xed, 0x6d, 0x95, 0x15, 0xd1, 0x51,
-        0xac, 0x2c, 0xe4, 0x64, 0x8c, 0x0c, 0xca, 0x4a,
-        0xbb, 0x3b, 0xf8, 0x78, 0x9c, 0x1c, 0xd8, 0x58,
-        0xa4, 0x24, 0xde, 0x5e, 0x84, 0x04, 0xc2, 0x42,
-        0xb3, 0x33, 0xec, 0x6c, 0x94, 0x14, 0xd0, 0x50,
-        0xb0, 0x30, 0xe8, 0x68, 0x90, 0x10, 0xce, 0x4e,
-        0xbf, 0x3f, 0xfe, 0x7e, 0xa0, 0x20, 0xdc, 0x5c,
-        0xa8, 0x28, 0xe0, 0x60, 0x88, 0x08, 0xc6, 0x46,
-        0xb7, 0x37, 0xf0, 0x70, 0x98, 0x18, 0xd4, 0x54,
-        0xaa, 0x2a, 0xe2, 0x62, 0x8a, 0x0a, 0xc8, 0x48,
-        0xb9, 0x39, 0xf4, 0x74, 0x9a, 0x1a, 0xd6, 0x56,
-        0xa2, 0x22, 0xdd, 0x5d, 0x82, 0x02, 0xc0, 0x40,
-        0xb1, 0x31, 0xea, 0x6a, 0x92, 0x12, 0xcf, 0x4f,
-        0xae, 0x2e, 0xe6, 0x66, 0x8e, 0x0e, 0xcc, 0x4c,
-        0xbd, 0x3d, 0xfc, 0x7c, 0x9e, 0x1e, 0xda, 0x5a,
-        0xa6, 0x26, 0xdf, 0x5f, 0x86, 0x06, 0xc4, 0x44,
-        0xb5, 0x35, 0xee, 0x6e, 0x96, 0x16, 0xd2, 0x52
+static char isdn_audio_alaw_to_ulaw[] =
+{
+	0xab, 0x2b, 0xe3, 0x63, 0x8b, 0x0b, 0xc9, 0x49,
+	0xba, 0x3a, 0xf6, 0x76, 0x9b, 0x1b, 0xd7, 0x57,
+	0xa3, 0x23, 0xdd, 0x5d, 0x83, 0x03, 0xc1, 0x41,
+	0xb2, 0x32, 0xeb, 0x6b, 0x93, 0x13, 0xcf, 0x4f,
+	0xaf, 0x2f, 0xe7, 0x67, 0x8f, 0x0f, 0xcd, 0x4d,
+	0xbe, 0x3e, 0xfe, 0x7e, 0x9f, 0x1f, 0xdb, 0x5b,
+	0xa7, 0x27, 0xdf, 0x5f, 0x87, 0x07, 0xc5, 0x45,
+	0xb6, 0x36, 0xef, 0x6f, 0x97, 0x17, 0xd3, 0x53,
+	0xa9, 0x29, 0xe1, 0x61, 0x89, 0x09, 0xc7, 0x47,
+	0xb8, 0x38, 0xf2, 0x72, 0x99, 0x19, 0xd5, 0x55,
+	0xa1, 0x21, 0xdc, 0x5c, 0x81, 0x01, 0xbf, 0x3f,
+	0xb0, 0x30, 0xe9, 0x69, 0x91, 0x11, 0xce, 0x4e,
+	0xad, 0x2d, 0xe5, 0x65, 0x8d, 0x0d, 0xcb, 0x4b,
+	0xbc, 0x3c, 0xfa, 0x7a, 0x9d, 0x1d, 0xd9, 0x59,
+	0xa5, 0x25, 0xde, 0x5e, 0x85, 0x05, 0xc3, 0x43,
+	0xb4, 0x34, 0xed, 0x6d, 0x95, 0x15, 0xd1, 0x51,
+	0xac, 0x2c, 0xe4, 0x64, 0x8c, 0x0c, 0xca, 0x4a,
+	0xbb, 0x3b, 0xf8, 0x78, 0x9c, 0x1c, 0xd8, 0x58,
+	0xa4, 0x24, 0xde, 0x5e, 0x84, 0x04, 0xc2, 0x42,
+	0xb3, 0x33, 0xec, 0x6c, 0x94, 0x14, 0xd0, 0x50,
+	0xb0, 0x30, 0xe8, 0x68, 0x90, 0x10, 0xce, 0x4e,
+	0xbf, 0x3f, 0xfe, 0x7e, 0xa0, 0x20, 0xdc, 0x5c,
+	0xa8, 0x28, 0xe0, 0x60, 0x88, 0x08, 0xc6, 0x46,
+	0xb7, 0x37, 0xf0, 0x70, 0x98, 0x18, 0xd4, 0x54,
+	0xaa, 0x2a, 0xe2, 0x62, 0x8a, 0x0a, 0xc8, 0x48,
+	0xb9, 0x39, 0xf4, 0x74, 0x9a, 0x1a, 0xd6, 0x56,
+	0xa2, 0x22, 0xdd, 0x5d, 0x82, 0x02, 0xc0, 0x40,
+	0xb1, 0x31, 0xea, 0x6a, 0x92, 0x12, 0xcf, 0x4f,
+	0xae, 0x2e, 0xe6, 0x66, 0x8e, 0x0e, 0xcc, 0x4c,
+	0xbd, 0x3d, 0xfc, 0x7c, 0x9e, 0x1e, 0xda, 0x5a,
+	0xa6, 0x26, 0xdf, 0x5f, 0x86, 0x06, 0xc4, 0x44,
+	0xb5, 0x35, 0xee, 0x6e, 0x96, 0x16, 0xd2, 0x52
 };
 
 /* ulaw -> alaw */
-static char isdn_audio_ulaw_to_alaw[] = {
-        0xab, 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35,
-        0xb5, 0x45, 0xc5, 0x05, 0x85, 0x65, 0xe5, 0x25,
-        0xa5, 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d,
-        0xbd, 0x4d, 0xcd, 0x0d, 0x8d, 0x6d, 0xed, 0x2d,
-        0xad, 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31,
-        0xb1, 0x41, 0xc1, 0x01, 0x81, 0x61, 0xe1, 0x21,
-        0x59, 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9,
-        0x49, 0xc9, 0x09, 0x89, 0x69, 0xe9, 0x29, 0xa9,
-        0xd7, 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47,
-        0xc7, 0x07, 0x87, 0x67, 0xe7, 0x27, 0xa7, 0xdf,
-        0x9f, 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f,
-        0x8f, 0x6f, 0xef, 0x2f, 0x53, 0x13, 0x73, 0x33,
-        0xb3, 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23,
-        0xa3, 0x5b, 0xdb, 0x1b, 0x9b, 0x7b, 0xfb, 0x3b,
-        0xbb, 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b,
-        0x8b, 0x8b, 0x6b, 0x6b, 0xeb, 0xeb, 0x2b, 0x2b,
-        0xab, 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34,
-        0xb4, 0x44, 0xc4, 0x04, 0x84, 0x64, 0xe4, 0x24,
-        0xa4, 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c,
-        0xbc, 0x4c, 0xcc, 0x0c, 0x8c, 0x6c, 0xec, 0x2c,
-        0xac, 0x50, 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30,
-        0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x20,
-        0x58, 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8,
-        0x48, 0xc8, 0x08, 0x88, 0x68, 0xe8, 0x28, 0xa8,
-        0xd6, 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46,
-        0xc6, 0x06, 0x86, 0x66, 0xe6, 0x26, 0xa6, 0xde,
-        0x9e, 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e,
-        0x8e, 0x6e, 0xee, 0x2e, 0x52, 0x12, 0x72, 0x32,
-        0xb2, 0x42, 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22,
-        0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a, 0xfa, 0x3a,
-        0xba, 0xba, 0x4a, 0x4a, 0xca, 0xca, 0x0a, 0x0a,
-        0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a
+static char isdn_audio_ulaw_to_alaw[] =
+{
+	0xab, 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35,
+	0xb5, 0x45, 0xc5, 0x05, 0x85, 0x65, 0xe5, 0x25,
+	0xa5, 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d,
+	0xbd, 0x4d, 0xcd, 0x0d, 0x8d, 0x6d, 0xed, 0x2d,
+	0xad, 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31,
+	0xb1, 0x41, 0xc1, 0x01, 0x81, 0x61, 0xe1, 0x21,
+	0x59, 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9,
+	0x49, 0xc9, 0x09, 0x89, 0x69, 0xe9, 0x29, 0xa9,
+	0xd7, 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47,
+	0xc7, 0x07, 0x87, 0x67, 0xe7, 0x27, 0xa7, 0xdf,
+	0x9f, 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f,
+	0x8f, 0x6f, 0xef, 0x2f, 0x53, 0x13, 0x73, 0x33,
+	0xb3, 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23,
+	0xa3, 0x5b, 0xdb, 0x1b, 0x9b, 0x7b, 0xfb, 0x3b,
+	0xbb, 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b,
+	0x8b, 0x8b, 0x6b, 0x6b, 0xeb, 0xeb, 0x2b, 0x2b,
+	0xab, 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34,
+	0xb4, 0x44, 0xc4, 0x04, 0x84, 0x64, 0xe4, 0x24,
+	0xa4, 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c,
+	0xbc, 0x4c, 0xcc, 0x0c, 0x8c, 0x6c, 0xec, 0x2c,
+	0xac, 0x50, 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30,
+	0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x20,
+	0x58, 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8,
+	0x48, 0xc8, 0x08, 0x88, 0x68, 0xe8, 0x28, 0xa8,
+	0xd6, 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46,
+	0xc6, 0x06, 0x86, 0x66, 0xe6, 0x26, 0xa6, 0xde,
+	0x9e, 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e,
+	0x8e, 0x6e, 0xee, 0x2e, 0x52, 0x12, 0x72, 0x32,
+	0xb2, 0x42, 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22,
+	0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a, 0xfa, 0x3a,
+	0xba, 0xba, 0x4a, 0x4a, 0xca, 0xca, 0x0a, 0x0a,
+	0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a
 };
 
 #define NCOEFF           16     /* number of frequencies to be analyzed       */
@@ -206,68 +216,72 @@
 #define HIGRP             1
 
 typedef struct {
-        int grp;        /* low/high group     */
-        int k;          /* k                  */
-        int k2;         /* k fuer 2. harmonic */
+	int grp;                /* low/high group     */
+	int k;                  /* k                  */
+	int k2;                 /* k fuer 2. harmonic */
 } dtmf_t;
 
 /* For DTMF recognition:
  * 2 * cos(2 * PI * k / N) precalculated for all k
  */
-static int cos2pik[NCOEFF] = {
-        55812,  29528, 53603,  24032, 51193,  14443, 48590,   6517,
-        38113, -21204, 33057, -32186, 25889, -45081, 18332, -55279
+static int cos2pik[NCOEFF] =
+{
+	55812, 29528, 53603, 24032, 51193, 14443, 48590, 6517,
+	38113, -21204, 33057, -32186, 25889, -45081, 18332, -55279
 };
 
-static dtmf_t dtmf_tones[8] = {
-        { LOGRP,  0,  1 }, /*  697 Hz */
-        { LOGRP,  2,  3 }, /*  770 Hz */
-        { LOGRP,  4,  5 }, /*  852 Hz */
-        { LOGRP,  6,  7 }, /*  941 Hz */
-        { HIGRP,  8,  9 }, /* 1209 Hz */
-        { HIGRP, 10, 11 }, /* 1336 Hz */
-        { HIGRP, 12, 13 }, /* 1477 Hz */
-        { HIGRP, 14, 15 }  /* 1633 Hz */
+static dtmf_t dtmf_tones[8] =
+{
+	{LOGRP, 0, 1},          /*  697 Hz */
+	{LOGRP, 2, 3},          /*  770 Hz */
+	{LOGRP, 4, 5},          /*  852 Hz */
+	{LOGRP, 6, 7},          /*  941 Hz */
+	{HIGRP, 8, 9},          /* 1209 Hz */
+	{HIGRP, 10, 11},        /* 1336 Hz */
+	{HIGRP, 12, 13},        /* 1477 Hz */
+	{HIGRP, 14, 15}         /* 1633 Hz */
 };
 
-static char dtmf_matrix[4][4] = {
-        {'1', '2', '3', 'A'},
-        {'4', '5', '6', 'B'},
-        {'7', '8', '9', 'C'},
-        {'*', '0', '#', 'D'}
+static char dtmf_matrix[4][4] =
+{
+	{'1', '2', '3', 'A'},
+	{'4', '5', '6', 'B'},
+	{'7', '8', '9', 'C'},
+	{'*', '0', '#', 'D'}
 };
 
 #if ((CPU == 386) || (CPU == 486) || (CPU == 586))
 static inline void
 isdn_audio_tlookup(const void *table, void *buff, unsigned long n)
 {
-        __asm__("cld\n"
-                "1:\tlodsb\n\t"
-                "xlatb\n\t"
-                "stosb\n\t"
-                "loop 1b\n\t"
-                ::"b" ((long)table), "c" (n), "D" ((long)buff), "S" ((long)buff)
-                :"bx","cx","di","si","ax");
+	__asm__("cld\n"
+		"1:\tlodsb\n\t"
+		"xlatb\n\t"
+		"stosb\n\t"
+		"loop 1b\n\t"
+      : :  "b"((long) table), "c"(n), "D"((long) buff), "S"((long) buff)
+      :        "bx", "cx", "di", "si", "ax");
 }
+
 #else
 static inline void
 isdn_audio_tlookup(const char *table, char *buff, unsigned long n)
 {
-        while (n--) 
-                *buff++ = table[*buff];
+	while (n--)
+		*buff++ = table[*buff];
 }
 #endif
 
 void
 isdn_audio_ulaw2alaw(unsigned char *buff, unsigned long len)
 {
-        isdn_audio_tlookup(isdn_audio_ulaw_to_alaw, buff, len);
+	isdn_audio_tlookup(isdn_audio_ulaw_to_alaw, buff, len);
 }
 
 void
 isdn_audio_alaw2ulaw(unsigned char *buff, unsigned long len)
 {
-        isdn_audio_tlookup(isdn_audio_alaw_to_ulaw, buff, len);
+	isdn_audio_tlookup(isdn_audio_alaw_to_ulaw, buff, len);
 }
 
 /*
@@ -278,207 +292,218 @@
  */
 
 
-#define ZEROTRAP    /* turn on the trap as per the MIL-STD */
+#define ZEROTRAP                /* turn on the trap as per the MIL-STD */
 #undef ZEROTRAP
-#define BIAS 0x84   /* define the add-in bias for 16 bit samples */
+#define BIAS 0x84               /* define the add-in bias for 16 bit samples */
 #define CLIP 32635
 
 static unsigned char
-isdn_audio_linear2ulaw(int sample) {
-        static int exp_lut[256] = {
-                0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
-                4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
-                5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
-                5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
-                6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
-                6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
-                6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
-                6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
-                7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-                7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-                7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-                7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-                7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-                7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-                7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-                7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
-        };
-        int sign, exponent, mantissa;
-        unsigned char ulawbyte;
-
-        /* Get the sample into sign-magnitude. */
-        sign = (sample >> 8) & 0x80;      /* set aside the sign  */
-        if(sign != 0) sample = -sample;   /* get magnitude       */
-        if(sample > CLIP) sample = CLIP;  /* clip the magnitude  */
-
-        /* Convert from 16 bit linear to ulaw. */
-        sample = sample + BIAS;
-        exponent = exp_lut[( sample >> 7 ) & 0xFF];
-        mantissa = (sample >> (exponent + 3)) & 0x0F;
-        ulawbyte = ~(sign | (exponent << 4) | mantissa);
+isdn_audio_linear2ulaw(int sample)
+{
+	static int exp_lut[256] =
+	{
+		0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+		4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+	};
+	int sign,
+	 exponent,
+	 mantissa;
+	unsigned char ulawbyte;
+
+	/* Get the sample into sign-magnitude. */
+	sign = (sample >> 8) & 0x80;	/* set aside the sign  */
+	if (sign != 0)
+		sample = -sample;	/* get magnitude       */
+	if (sample > CLIP)
+		sample = CLIP;  /* clip the magnitude  */
+
+	/* Convert from 16 bit linear to ulaw. */
+	sample = sample + BIAS;
+	exponent = exp_lut[(sample >> 7) & 0xFF];
+	mantissa = (sample >> (exponent + 3)) & 0x0F;
+	ulawbyte = ~(sign | (exponent << 4) | mantissa);
 #ifdef ZEROTRAP
-        /* optional CCITT trap */
-        if (ulawbyte == 0) ulawbyte = 0x02;
+	/* optional CCITT trap */
+	if (ulawbyte == 0)
+		ulawbyte = 0x02;
 #endif
-        return(ulawbyte);
+	return (ulawbyte);
 }
 
 
-static int Mx[3][8] = {
-        { 0x3800, 0x5600, 0,0,0,0,0,0 },
-        { 0x399a, 0x3a9f, 0x4d14, 0x6607, 0,0,0,0 },
-        { 0x3556, 0x3556, 0x399A, 0x3A9F, 0x4200, 0x4D14, 0x6607, 0x6607 },
+static int Mx[3][8] =
+{
+	{0x3800, 0x5600, 0, 0, 0, 0, 0, 0},
+	{0x399a, 0x3a9f, 0x4d14, 0x6607, 0, 0, 0, 0},
+	{0x3556, 0x3556, 0x399A, 0x3A9F, 0x4200, 0x4D14, 0x6607, 0x6607},
 };
 
-static int bitmask[9] = {
-        0, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff
-}; 
+static int bitmask[9] =
+{
+	0, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff
+};
 
 static int
-isdn_audio_get_bits (adpcm_state *s, unsigned char **in, int *len)
+isdn_audio_get_bits(adpcm_state * s, unsigned char **in, int *len)
 {
-        while( s->nleft < s->nbits) {
-                int d = *((*in)++);
-                (*len)--;
-                s->word = (s->word << 8) | d;
-                s->nleft += 8;
-        }
-        s->nleft -= s->nbits;
-        return (s->word >> s->nleft) & bitmask[s->nbits];
+	while (s->nleft < s->nbits) {
+		int d = *((*in)++);
+		(*len)--;
+		s->word = (s->word << 8) | d;
+		s->nleft += 8;
+	}
+	s->nleft -= s->nbits;
+	return (s->word >> s->nleft) & bitmask[s->nbits];
 }
 
 static void
-isdn_audio_put_bits (int data, int nbits, adpcm_state *s,
-                     unsigned char **out, int *len)
+isdn_audio_put_bits(int data, int nbits, adpcm_state * s,
+		    unsigned char **out, int *len)
 {
-        s->word = (s->word << nbits) | (data & bitmask[nbits]);
-        s->nleft += nbits;
-        while(s->nleft >= 8) {
-                int d = (s->word >> (s->nleft-8));
-                *(out[0]++) = d & 255;
-                (*len)++;
-                s->nleft -= 8;
-        }
+	s->word = (s->word << nbits) | (data & bitmask[nbits]);
+	s->nleft += nbits;
+	while (s->nleft >= 8) {
+		int d = (s->word >> (s->nleft - 8));
+		*(out[0]++) = d & 255;
+		(*len)++;
+		s->nleft -= 8;
+	}
 }
 
 adpcm_state *
-isdn_audio_adpcm_init(adpcm_state *s, int nbits)
+isdn_audio_adpcm_init(adpcm_state * s, int nbits)
 {
-        if (!s)
-                s = (adpcm_state *) kmalloc(sizeof(adpcm_state), GFP_ATOMIC);
-        if (s) {
-                s->a     = 0;
-                s->d     = 5;
-                s->word  = 0;
-                s->nleft = 0;
-                s->nbits = nbits;
-        }
-        return s;
+	if (!s)
+		s = (adpcm_state *) kmalloc(sizeof(adpcm_state), GFP_ATOMIC);
+	if (s) {
+		s->a = 0;
+		s->d = 5;
+		s->word = 0;
+		s->nleft = 0;
+		s->nbits = nbits;
+	}
+	return s;
 }
 
 dtmf_state *
-isdn_audio_dtmf_init(dtmf_state *s)
+isdn_audio_dtmf_init(dtmf_state * s)
 {
-        if (!s)
-                s = (dtmf_state *) kmalloc(sizeof(dtmf_state), GFP_ATOMIC);
-        if (s) {
-                s->idx   = 0;
-                s->last  = ' ';
-        }
-        return s;
+	if (!s)
+		s = (dtmf_state *) kmalloc(sizeof(dtmf_state), GFP_ATOMIC);
+	if (s) {
+		s->idx = 0;
+		s->last = ' ';
+	}
+	return s;
 }
 
 /*
  * Decompression of adpcm data to a/u-law
  *
  */
- 
+
 int
-isdn_audio_adpcm2xlaw (adpcm_state *s, int fmt, unsigned char *in,
-                      unsigned char *out, int len)
+isdn_audio_adpcm2xlaw(adpcm_state * s, int fmt, unsigned char *in,
+		      unsigned char *out, int len)
 {
-        int a = s->a;
-        int d = s->d;
-        int nbits = s->nbits;
-        int olen = 0;
-        
-        while (len) {
-                int e = isdn_audio_get_bits(s, &in, &len);
-                int sign;
-
-                if (nbits == 4 && e == 0)
-                        d = 4;
-                sign = (e >> (nbits-1))?-1:1;
-                e &= bitmask[nbits-1];
-                a += sign * ((e << 1) + 1) * d >> 1;
-                if (d & 1)
-                        a++;
-                if (fmt)
-                        *out++ = isdn_audio_ulaw_to_alaw[
-                                 isdn_audio_linear2ulaw(a << 2)];
-                else
-                        *out++ = isdn_audio_linear2ulaw(a << 2);
-                olen++;
-                d = (d * Mx[nbits-2][ e ] + 0x2000) >> 14;
-                if ( d < 5 )
-                        d = 5;     
-        }
-        s->a = a;
-        s->d = d;
-        return olen;
+	int a = s->a;
+	int d = s->d;
+	int nbits = s->nbits;
+	int olen = 0;
+
+	while (len) {
+		int e = isdn_audio_get_bits(s, &in, &len);
+		int sign;
+
+		if (nbits == 4 && e == 0)
+			d = 4;
+		sign = (e >> (nbits - 1)) ? -1 : 1;
+		e &= bitmask[nbits - 1];
+		a += sign * ((e << 1) + 1) * d >> 1;
+		if (d & 1)
+			a++;
+		if (fmt)
+			*out++ = isdn_audio_ulaw_to_alaw[
+					 isdn_audio_linear2ulaw(a << 2)];
+		else
+			*out++ = isdn_audio_linear2ulaw(a << 2);
+		olen++;
+		d = (d * Mx[nbits - 2][e] + 0x2000) >> 14;
+		if (d < 5)
+			d = 5;
+	}
+	s->a = a;
+	s->d = d;
+	return olen;
 }
 
 int
-isdn_audio_2adpcm_flush (adpcm_state *s, unsigned char *out)
+isdn_audio_2adpcm_flush(adpcm_state * s, unsigned char *out)
 {
 	int olen = 0;
 
-        if (s->nleft)
-                isdn_audio_put_bits(0, 8-s->nleft, s, &out, &olen);
-        return olen;
+	if (s->nleft)
+		isdn_audio_put_bits(0, 8 - s->nleft, s, &out, &olen);
+	return olen;
 }
 
 int
-isdn_audio_xlaw2adpcm (adpcm_state *s, int fmt, unsigned char *in,
-                      unsigned char *out, int len)
+isdn_audio_xlaw2adpcm(adpcm_state * s, int fmt, unsigned char *in,
+		      unsigned char *out, int len)
 {
-        int a = s->a;
-        int d = s->d;
-        int nbits = s->nbits;
-        int olen = 0;
-
-        while (len--) {
-                int e = 0, nmax = 1 << (nbits - 1);
-                int sign, delta;
-              
-                if (fmt)
-                        delta = (isdn_audio_alaw_to_s16[*in++] >> 2) - a;
-                else
-                        delta = (isdn_audio_ulaw_to_s16[*in++] >> 2) - a;
-                if (delta < 0) {
-                        e = nmax;
-                        delta = -delta;
-                }
-                while( --nmax && delta > d ) {
-                        delta -= d;
-                        e++;
-                }
-                if (nbits == 4 && ((e & 0x0f) == 0))
-                        e = 8;
-                isdn_audio_put_bits(e, nbits, s, &out, &olen);
-                sign = (e >> (nbits-1))?-1:1 ;
-                e &= bitmask[nbits-1];
-                
-                a += sign * ((e << 1) + 1) * d >> 1;
-                if (d & 1)
-                        a++;
-                d = (d * Mx[nbits-2][ e ] + 0x2000) >> 14;
-                if (d < 5)
-                        d=5;
-        }
+	int a = s->a;
+	int d = s->d;
+	int nbits = s->nbits;
+	int olen = 0;
+
+	while (len--) {
+		int e = 0,
+		 nmax = 1 << (nbits - 1);
+		int sign,
+		 delta;
+
+		if (fmt)
+			delta = (isdn_audio_alaw_to_s16[*in++] >> 2) - a;
+		else
+			delta = (isdn_audio_ulaw_to_s16[*in++] >> 2) - a;
+		if (delta < 0) {
+			e = nmax;
+			delta = -delta;
+		}
+		while (--nmax && delta > d) {
+			delta -= d;
+			e++;
+		}
+		if (nbits == 4 && ((e & 0x0f) == 0))
+			e = 8;
+		isdn_audio_put_bits(e, nbits, s, &out, &olen);
+		sign = (e >> (nbits - 1)) ? -1 : 1;
+		e &= bitmask[nbits - 1];
+
+		a += sign * ((e << 1) + 1) * d >> 1;
+		if (d & 1)
+			a++;
+		d = (d * Mx[nbits - 2][e] + 0x2000) >> 14;
+		if (d < 5)
+			d = 5;
+	}
 	s->a = a;
 	s->d = d;
-        return olen;
+	return olen;
 }
 
 /*
@@ -488,99 +513,109 @@
  * Result is stored into an sk_buff and queued up for later
  * evaluation.
  */
-void
-isdn_audio_goertzel(int *sample, modem_info *info) {
-        int sk, sk1, sk2;
-        int k, n;
-        struct sk_buff *skb;
-        int *result;
-
-        skb = dev_alloc_skb(sizeof(int) * NCOEFF);
-        if (!skb) {
-                printk(KERN_WARNING
-                       "isdn_audio: Could not alloc DTMF result for ttyI%d\n",
-                       info->line);
-                return;
-        }
-        result = (int *)skb_put(skb, sizeof(int) * NCOEFF);
-        skb->free = 1;
-        skb->users = 0;
-        for (k = 0; k < NCOEFF; k++) {
-                sk = sk1 = sk2 = 0;
-                for (n = 0; n < DTMF_NPOINTS; n++) {
-                        sk = sample[n] + ((cos2pik[k] * sk1) >> 15) - sk2;
-                        sk2 = sk1;
-                        sk1 = sk;
-                }
-                result[k] =
-                        ((sk * sk) >> AMP_BITS) - 
-                        ((((cos2pik[k] * sk) >> 15) * sk2) >> AMP_BITS) +
-                        ((sk2 * sk2) >> AMP_BITS);
-        }
-        skb_queue_tail(&info->dtmf_queue, skb);
-        isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+static void
+isdn_audio_goertzel(int *sample, modem_info * info)
+{
+	int sk,
+	 sk1,
+	 sk2;
+	int k,
+	 n;
+	struct sk_buff *skb;
+	int *result;
+
+	skb = dev_alloc_skb(sizeof(int) * NCOEFF);
+	if (!skb) {
+		printk(KERN_WARNING
+		  "isdn_audio: Could not alloc DTMF result for ttyI%d\n",
+		       info->line);
+		return;
+	}
+	SET_SKB_FREE(skb);
+	result = (int *) skb_put(skb, sizeof(int) * NCOEFF);
+	for (k = 0; k < NCOEFF; k++) {
+		sk = sk1 = sk2 = 0;
+		for (n = 0; n < DTMF_NPOINTS; n++) {
+			sk = sample[n] + ((cos2pik[k] * sk1) >> 15) - sk2;
+			sk2 = sk1;
+			sk1 = sk;
+		}
+		result[k] =
+		    ((sk * sk) >> AMP_BITS) -
+		    ((((cos2pik[k] * sk) >> 15) * sk2) >> AMP_BITS) +
+		    ((sk2 * sk2) >> AMP_BITS);
+	}
+	skb_queue_tail(&info->dtmf_queue, skb);
+	isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
 }
 
 void
-isdn_audio_eval_dtmf(modem_info *info)
+isdn_audio_eval_dtmf(modem_info * info)
 {
-        struct sk_buff *skb;
-        int *result;
-        dtmf_state *s;
-        int silence;
-        int i;
-        int di;
-        int ch;
-        unsigned long flags;
-        int grp[2];
-        char what;
-        char *p;
-
-        while ((skb = skb_dequeue(&info->dtmf_queue))) {
-                result = (int *)skb->data;
-                s = info->dtmf_state;
-                grp[LOGRP] = grp[HIGRP] = -2;
-                silence = 0;
-                for(i = 0; i < 8; i++) {
-                        if ((result[dtmf_tones[i].k] > DTMF_TRESH) &&
-                            (result[dtmf_tones[i].k2] < H2_TRESH)    )
-                                grp[dtmf_tones[i].grp] = (grp[dtmf_tones[i].grp] == -2)?i:-1;
-                        else
-                                if ((result[dtmf_tones[i].k] < SILENCE_TRESH) &&
-                                    (result[dtmf_tones[i].k2] < SILENCE_TRESH)  )
-                                        silence++;
-                }
-                if(silence == 8)
-                        what = ' ';
-                else {
-                        if((grp[LOGRP] >= 0) && (grp[HIGRP] >= 0)) {
-                                what = dtmf_matrix[grp[LOGRP]][grp[HIGRP] - 4];
-                                if(s->last != ' ' && s->last != '.')
-                                        s->last = what;  /* min. 1 non-DTMF between DTMF */
-                        } else
-                                what = '.';
-                }
-                if ((what != s->last) && (what != ' ') && (what != '.')) {
-                        printk(KERN_DEBUG "dtmf: tt='%c'\n", what);
-                        p = skb->data;
-                        *p++ = 0x10;
-                        *p = what;
-                        skb_trim(skb, 2);
-                        save_flags(flags);
-                        cli();
-                        di = info->isdn_driver;
-                        ch = info->isdn_channel;
-                        __skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb);
-                        dev->drv[di]->rcvcount[ch] += 2;
-                        restore_flags(flags);
-                        /* Schedule dequeuing */
-                        if ((dev->modempoll) && (info->rcvsched))
-                                isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
-                        wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]);
-                } else
-                        kfree_skb(skb, FREE_READ);
-                s->last = what;
-        }
+	struct sk_buff *skb;
+	int *result;
+	dtmf_state *s;
+	int silence;
+	int i;
+	int di;
+	int ch;
+	unsigned long flags;
+	int grp[2];
+	char what;
+	char *p;
+
+	while ((skb = skb_dequeue(&info->dtmf_queue))) {
+		result = (int *) skb->data;
+		s = info->dtmf_state;
+		grp[LOGRP] = grp[HIGRP] = -2;
+		silence = 0;
+		for (i = 0; i < 8; i++) {
+			if ((result[dtmf_tones[i].k] > DTMF_TRESH) &&
+			    (result[dtmf_tones[i].k2] < H2_TRESH))
+				grp[dtmf_tones[i].grp] = (grp[dtmf_tones[i].grp] == -2) ? i : -1;
+			else if ((result[dtmf_tones[i].k] < SILENCE_TRESH) &&
+			      (result[dtmf_tones[i].k2] < SILENCE_TRESH))
+				silence++;
+		}
+		if (silence == 8)
+			what = ' ';
+		else {
+			if ((grp[LOGRP] >= 0) && (grp[HIGRP] >= 0)) {
+				what = dtmf_matrix[grp[LOGRP]][grp[HIGRP] - 4];
+				if (s->last != ' ' && s->last != '.')
+					s->last = what;	/* min. 1 non-DTMF between DTMF */
+			} else
+				what = '.';
+		}
+		if ((what != s->last) && (what != ' ') && (what != '.')) {
+			printk(KERN_DEBUG "dtmf: tt='%c'\n", what);
+			p = skb->data;
+			*p++ = 0x10;
+			*p = what;
+			skb_trim(skb, 2);
+			if (skb_headroom(skb) < sizeof(isdn_audio_skb)) {
+				printk(KERN_WARNING
+				       "isdn_audio: insufficient skb_headroom, dropping\n");
+				kfree_skb(skb, FREE_READ);
+				return;
+			}
+			ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
+			ISDN_AUDIO_SKB_LOCK(skb) = 0;
+			save_flags(flags);
+			cli();
+			di = info->isdn_driver;
+			ch = info->isdn_channel;
+			__skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb);
+			dev->drv[di]->rcvcount[ch] += 2;
+			restore_flags(flags);
+			/* Schedule dequeuing */
+			if ((dev->modempoll) && (info->rcvsched))
+				isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+			wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]);
+		} else
+			kfree_skb(skb, FREE_READ);
+		s->last = what;
+	}
 }
 
 /*
@@ -593,30 +628,28 @@
  *   fmt  = audio data format (0 = ulaw, 1 = alaw)
  */
 void
-isdn_audio_calc_dtmf(modem_info *info, unsigned char *buf, int len, int fmt)
+isdn_audio_calc_dtmf(modem_info * info, unsigned char *buf, int len, int fmt)
 {
-        dtmf_state *s = info->dtmf_state;
-        int i;
-        int c;
-
-        while (len) {
-                c = MIN(len, (DTMF_NPOINTS - s->idx));
-                if (c <= 0)
-                        break;
-                for (i = 0; i < c; i++) {
-                        if (fmt)
-                                s->buf[s->idx++] =
-                                        isdn_audio_alaw_to_s16[*buf++] >> (15 - AMP_BITS);
-                        else
-                                s->buf[s->idx++] =
-                                        isdn_audio_ulaw_to_s16[*buf++] >> (15 - AMP_BITS);
-                }
-                if (s->idx == DTMF_NPOINTS) {
-                        isdn_audio_goertzel(s->buf, info);
-                        s->idx = 0;
-                }
-                len -= c;
-        }
+	dtmf_state *s = info->dtmf_state;
+	int i;
+	int c;
+
+	while (len) {
+		c = MIN(len, (DTMF_NPOINTS - s->idx));
+		if (c <= 0)
+			break;
+		for (i = 0; i < c; i++) {
+			if (fmt)
+				s->buf[s->idx++] =
+				    isdn_audio_alaw_to_s16[*buf++] >> (15 - AMP_BITS);
+			else
+				s->buf[s->idx++] =
+				    isdn_audio_ulaw_to_s16[*buf++] >> (15 - AMP_BITS);
+		}
+		if (s->idx == DTMF_NPOINTS) {
+			isdn_audio_goertzel(s->buf, info);
+			s->idx = 0;
+		}
+		len -= c;
+	}
 }
-
-
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_audio.h linux/drivers/isdn/isdn_audio.h
--- v2.0.30/linux/drivers/isdn/isdn_audio.h	Fri Jun  7 06:02:40 1996
+++ linux/drivers/isdn/isdn_audio.h	Mon Aug  4 17:34:00 1997
@@ -1,9 +1,9 @@
-/* $Id: isdn_audio.h,v 1.4 1996/06/06 14:43:32 fritz Exp $
- *
+/* $Id: isdn_audio.h,v 1.5 1997/02/03 22:45:21 fritz Exp $
+
  * Linux ISDN subsystem, audio conversion and compression (linklevel).
  *
  * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
- * 
+ *
  * 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)
@@ -16,9 +16,12 @@
  *
  * 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. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_audio.h,v $
+ * Revision 1.5  1997/02/03 22:45:21  fritz
+ * Reformatted according CodingStyle
+ *
  * Revision 1.4  1996/06/06 14:43:32  fritz
  * Changed to support DTMF decoding on audio playback also.
  *
@@ -33,27 +36,27 @@
  *
  */
 
-#define DTMF_NPOINTS 205       /* Number of samples for DTMF recognition */
+#define DTMF_NPOINTS 205        /* Number of samples for DTMF recognition */
 typedef struct adpcm_state {
-        int  a;
-        int  d;
-        int  word;
-        int  nleft;
-        int  nbits;
+	int a;
+	int d;
+	int word;
+	int nleft;
+	int nbits;
 } adpcm_state;
 
 typedef struct dtmf_state {
-        char last;
-        int  idx;
-        int  buf[DTMF_NPOINTS];
+	char last;
+	int idx;
+	int buf[DTMF_NPOINTS];
 } dtmf_state;
 
 extern void isdn_audio_ulaw2alaw(unsigned char *, unsigned long);
 extern void isdn_audio_alaw2ulaw(unsigned char *, unsigned long);
 extern adpcm_state *isdn_audio_adpcm_init(adpcm_state *, int);
-extern int  isdn_audio_adpcm2xlaw(adpcm_state *, int, unsigned char *, unsigned char *, int);
-extern int  isdn_audio_xlaw2adpcm(adpcm_state *, int, unsigned char *, unsigned char *, int);
-extern int  isdn_audio_2adpcm_flush(adpcm_state *s, unsigned char *out);
+extern int isdn_audio_adpcm2xlaw(adpcm_state *, int, unsigned char *, unsigned char *, int);
+extern int isdn_audio_xlaw2adpcm(adpcm_state *, int, unsigned char *, unsigned char *, int);
+extern int isdn_audio_2adpcm_flush(adpcm_state * s, unsigned char *out);
 extern void isdn_audio_calc_dtmf(modem_info *, unsigned char *, int, int);
 extern void isdn_audio_eval_dtmf(modem_info *);
 dtmf_state *isdn_audio_dtmf_init(dtmf_state *);
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_cards.c linux/drivers/isdn/isdn_cards.c
--- v2.0.30/linux/drivers/isdn/isdn_cards.c	Tue Nov 12 22:36:19 1996
+++ linux/drivers/isdn/isdn_cards.c	Mon Aug  4 17:34:00 1997
@@ -1,9 +1,9 @@
-/* $Id: isdn_cards.c,v 1.2 1996/10/13 19:52:17 keil Exp $
- *
+/* $Id: isdn_cards.c,v 1.6 1997/04/23 18:56:03 fritz Exp $
+
  * Linux ISDN subsystem, initialization for non-modularized drivers.
  *
  * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
- * 
+ *
  * 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)
@@ -16,9 +16,21 @@
  *
  * 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. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_cards.c,v $
+ * Revision 1.6  1997/04/23 18:56:03  fritz
+ * Old Teles driver removed, Changed doc and scripts accordingly.
+ *
+ * Revision 1.5  1997/03/30 17:10:36  calle
+ * added support for AVM-B1-PCI card.
+ *
+ * Revision 1.4  1997/03/04 21:59:44  calle
+ * Added AVM-B1-CAPI2.0 driver
+ *
+ * Revision 1.3  1997/02/03 23:31:14  fritz
+ * Reformatted according CodingStyle
+ *
  * Revision 1.2  1996/10/13 19:52:17  keil
  * HiSax support
  *
@@ -33,10 +45,6 @@
 extern void icn_init(void);
 #endif
 
-#ifdef CONFIG_ISDN_DRV_TELES
-extern void teles_init(void);
-#endif
-
 #ifdef CONFIG_ISDN_DRV_HISAX
 extern void HiSax_init(void);
 #endif
@@ -45,19 +53,33 @@
 extern void pcbit_init(void);
 #endif
 
-void isdn_cards_init(void)
+#ifdef CONFIG_ISDN_DRV_AVMB1
+extern void avmb1_init(void);
+extern void capi_init(void);
+extern void capidrv_init(void);
+#ifdef CONFIG_PCI
+extern int b1pci_init(void);
+#endif
+#endif
+
+void
+isdn_cards_init(void)
 {
 #if CONFIG_ISDN_DRV_ICN
-        icn_init();
-#endif
-#if CONFIG_ISDN_DRV_TELES
-        teles_init();
+	icn_init();
 #endif
 #ifdef CONFIG_ISDN_DRV_HISAX
 	HiSax_init();
 #endif
 #if CONFIG_ISDN_DRV_PCBIT
-        pcbit_init();
+	pcbit_init();
+#endif
+#ifdef CONFIG_ISDN_DRV_AVMB1
+	avmb1_init();
+#ifdef CONFIG_PCI
+	b1pci_init();
+#endif
+	capi_init();
+	capidrv_init();
 #endif
 }
-
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_cards.h linux/drivers/isdn/isdn_cards.h
--- v2.0.30/linux/drivers/isdn/isdn_cards.h	Sun Apr 21 01:56:14 1996
+++ linux/drivers/isdn/isdn_cards.h	Mon Aug  4 17:34:00 1997
@@ -1,9 +1,9 @@
-/* $Id: isdn_cards.h,v 1.1 1996/04/20 16:04:03 fritz Exp $
- *
+/* $Id: isdn_cards.h,v 1.2 1997/02/03 23:31:55 fritz Exp $
+
  * Linux ISDN subsystem, initialization for non-modularized drivers.
  *
  * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
- * 
+ *
  * 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)
@@ -16,13 +16,15 @@
  *
  * 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. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_cards.h,v $
+ * Revision 1.2  1997/02/03 23:31:55  fritz
+ * Reformatted according CodingStyle
+ *
  * Revision 1.1  1996/04/20 16:04:03  fritz
  * Initial revision
  *
  */
 
 extern void isdn_cards_init(void);
-
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c
--- v2.0.30/linux/drivers/isdn/isdn_common.c	Tue Nov 12 22:36:19 1996
+++ linux/drivers/isdn/isdn_common.c	Tue Aug  5 11:05:16 1997
@@ -1,11 +1,11 @@
-/* $Id: isdn_common.c,v 1.28 1996/11/13 02:33:19 fritz Exp $
- *
+/* $Id: isdn_common.c,v 1.44 1997/05/27 15:17:23 fritz Exp $
+
  * Linux ISDN subsystem, common used functions (linklevel).
  *
  * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
  * Copyright 1995,96    Thinking Objects Software GmbH Wuerzburg
  * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
- * 
+ *
  * 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)
@@ -18,9 +18,72 @@
  *
  * 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. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_common.c,v $
+ * Revision 1.44  1997/05/27 15:17:23  fritz
+ * Added changes for recent 2.1.x kernels:
+ *   changed return type of isdn_close
+ *   queue_task_* -> queue_task
+ *   clear/set_bit -> test_and_... where apropriate.
+ *   changed type of hard_header_cache parameter.
+ *
+ * Revision 1.43  1997/03/31 14:09:43  fritz
+ * Fixed memory leak in isdn_close().
+ *
+ * Revision 1.42  1997/03/30 16:51:08  calle
+ * changed calls to copy_from_user/copy_to_user and removed verify_area
+ * were possible.
+ *
+ * Revision 1.41  1997/03/24 22:54:41  fritz
+ * Some small fixes in debug code.
+ *
+ * Revision 1.40  1997/03/08 08:13:51  fritz
+ * Bugfix: IIOCSETMAP (Set mapping) was broken.
+ *
+ * Revision 1.39  1997/03/07 01:32:54  fritz
+ * Added proper ifdef's for CONFIG_ISDN_AUDIO
+ *
+ * Revision 1.38  1997/03/05 21:15:02  fritz
+ * Fix: reduced stack usage of isdn_ioctl() and isdn_set_allcfg()
+ *
+ * Revision 1.37  1997/03/02 14:29:18  fritz
+ * More ttyI related cleanup.
+ *
+ * Revision 1.36  1997/02/28 02:32:40  fritz
+ * Cleanup: Moved some tty related stuff from isdn_common.c
+ *          to isdn_tty.c
+ * Bugfix:  Bisync protocol did not behave like documented.
+ *
+ * Revision 1.35  1997/02/21 13:01:19  fritz
+ * Changes CAUSE message output in kernel log.
+ *
+ * Revision 1.34  1997/02/10 20:12:43  fritz
+ * Changed interface for reporting incoming calls.
+ *
+ * Revision 1.33  1997/02/10 10:05:42  fritz
+ * More changes for Kernel 2.1.X
+ * Symbol information moved to isdn_syms.c
+ *
+ * Revision 1.32  1997/02/03 22:55:26  fritz
+ * Reformatted according CodingStyle.
+ * Changed isdn_writebuf_stub static.
+ * Slow down tty-RING counter.
+ * skb->free stuff replaced by macro.
+ * Bugfix in audio-skb locking.
+ * Bugfix in HL-driver locking.
+ *
+ * Revision 1.31  1997/01/17 01:19:18  fritz
+ * Applied chargeint patch.
+ *
+ * Revision 1.30  1997/01/14 01:27:47  fritz
+ * Changed audio receive not to rely on skb->users and skb->lock.
+ * Added ATI2 and related variables.
+ * Started adding full-duplex audio capability.
+ *
+ * Revision 1.29  1997/01/12 23:33:03  fritz
+ * Made isdn_all_eaz foolproof.
+ *
  * Revision 1.28  1996/11/13 02:33:19  fritz
  * Fixed a race condition.
  *
@@ -134,11 +197,13 @@
  */
 
 #include <linux/config.h>
+#define __NO_VERSION__
 #include <linux/module.h>
 #include <linux/version.h>
-#ifndef __GENKSYMS__      /* Don't want genksyms report unneeded structs */
-#include <linux/isdn.h>
+#if (LINUX_VERSION_CODE >= 0x020117)
+#include <asm/poll.h>
 #endif
+#include <linux/isdn.h>
 #include "isdn_common.h"
 #include "isdn_tty.h"
 #include "isdn_net.h"
@@ -149,13 +214,11 @@
 #include "isdn_cards.h"
 
 /* Debugflags */
-#undef  ISDN_DEBUG_STATCALLB
-#define NEW_ISDN_TIMER_CTRL
+#undef ISDN_DEBUG_STATCALLB
 
 isdn_dev *dev = (isdn_dev *) 0;
 
-static int  has_exported = 0;
-static char *isdn_revision      = "$Revision: 1.28 $";
+static char *isdn_revision = "$Revision: 1.44 $";
 
 extern char *isdn_net_revision;
 extern char *isdn_tty_revision;
@@ -170,18 +233,23 @@
 static char *isdn_audio_revision = ": none $";
 #endif
 
-void isdn_MOD_INC_USE_COUNT(void)
+static int isdn_writebuf_stub(int, int, const u_char *, int, int);
+
+void
+isdn_MOD_INC_USE_COUNT(void)
 {
 	MOD_INC_USE_COUNT;
 }
 
-void isdn_MOD_DEC_USE_COUNT(void)
+void
+isdn_MOD_DEC_USE_COUNT(void)
 {
 	MOD_DEC_USE_COUNT;
 }
 
 #if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
-void isdn_dumppkt(char *s, u_char * p, int len, int dumplen)
+void
+isdn_dumppkt(char *s, u_char * p, int len, int dumplen)
 {
 	int dumpc;
 
@@ -192,26 +260,29 @@
 }
 #endif
 
-static __inline void isdn_trash_skb(struct sk_buff *skb, int rw)
+static __inline void
+isdn_trash_skb(struct sk_buff *skb, int rw)
 {
-        skb->free = 1;
-        kfree_skb(skb, rw);
+	SET_SKB_FREE(skb);
+	kfree_skb(skb, rw);
 }
 
-static void isdn_free_queue(struct sk_buff_head *queue)
+static void
+isdn_free_queue(struct sk_buff_head *queue)
 {
-        struct sk_buff *skb;
-        unsigned long flags;
+	struct sk_buff *skb;
+	unsigned long flags;
 
-        save_flags(flags);
-        cli();
-        if (skb_queue_len(queue))
-                while ((skb = skb_dequeue(queue)))
-                        isdn_trash_skb(skb, FREE_READ);
-        restore_flags(flags);
+	save_flags(flags);
+	cli();
+	if (skb_queue_len(queue))
+		while ((skb = skb_dequeue(queue)))
+			isdn_trash_skb(skb, FREE_READ);
+	restore_flags(flags);
 }
 
-int isdn_dc2minor(int di, int ch)
+int
+isdn_dc2minor(int di, int ch)
 {
 	int i;
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
@@ -222,8 +293,10 @@
 
 static int isdn_timer_cnt1 = 0;
 static int isdn_timer_cnt2 = 0;
+static int isdn_timer_cnt3 = 0;
 
-static void isdn_timer_funct(ulong dummy)
+static void
+isdn_timer_funct(ulong dummy)
 {
 	int tf = dev->tflags;
 
@@ -241,34 +314,35 @@
 			if (tf & ISDN_TIMER_NETDIAL)
 				isdn_net_dial();
 		}
-                if (++isdn_timer_cnt2 >= ISDN_TIMER_1SEC) {
-                        isdn_timer_cnt2 = 0;
-                        if (tf & ISDN_TIMER_NETHANGUP)
-                                isdn_net_autohup();
-                        if (tf & ISDN_TIMER_MODEMRING)
-                                isdn_tty_modem_ring();
+		if (++isdn_timer_cnt2 >= ISDN_TIMER_1SEC) {
+			isdn_timer_cnt2 = 0;
+			if (tf & ISDN_TIMER_NETHANGUP)
+				isdn_net_autohup();
+			if (++isdn_timer_cnt3 > ISDN_TIMER_RINGING) {
+				isdn_timer_cnt3 = 0;
+				if (tf & ISDN_TIMER_MODEMRING)
+					isdn_tty_modem_ring();
+			}
 #if (defined CONFIG_ISDN_PPP) && (defined CONFIG_ISDN_MPP)
-                        if (tf & ISDN_TIMER_IPPP)
-                                isdn_ppp_timer_timeout();
+			if (tf & ISDN_TIMER_IPPP)
+				isdn_ppp_timer_timeout();
 #endif
-                }
+		}
 	}
 	if (tf) {
-                int flags;
+		int flags;
 
 		save_flags(flags);
 		cli();
-                del_timer(&dev->timer);
-#ifndef NEW_ISDN_TIMER_CTRL
-                dev->timer.function = isdn_timer_funct;
-#endif
+		del_timer(&dev->timer);
 		dev->timer.expires = jiffies + ISDN_TIMER_RES;
 		add_timer(&dev->timer);
 		restore_flags(flags);
 	}
 }
 
-void isdn_timer_ctrl(int tf, int onoff)
+void
+isdn_timer_ctrl(int tf, int onoff)
 {
 	int flags;
 
@@ -283,447 +357,287 @@
 		dev->tflags |= tf;
 	else
 		dev->tflags &= ~tf;
-#ifdef NEW_ISDN_TIMER_CTRL
 	if (dev->tflags) {
-                if (!del_timer(&dev->timer)) /* del_timer is 1, when active */
-                        dev->timer.expires = jiffies + ISDN_TIMER_RES;
+		if (!del_timer(&dev->timer))	/* del_timer is 1, when active */
+			dev->timer.expires = jiffies + ISDN_TIMER_RES;
 		add_timer(&dev->timer);
 	}
-#else
-	if (dev->tflags) {
-                del_timer(&dev->timer);
-                dev->timer.function = isdn_timer_funct;
-		dev->timer.expires = jiffies + ISDN_TIMER_RES;
-		add_timer(&dev->timer);
-	}
-#endif
 	restore_flags(flags);
 }
 
 /*
  * Receive a packet from B-Channel. (Called from low-level-module)
  */
-static void isdn_receive_skb_callback(int di, int channel, struct sk_buff *skb)
+static void
+isdn_receive_skb_callback(int di, int channel, struct sk_buff *skb)
 {
-	ulong flags;
 	int i;
-        int midx;
-#ifdef CONFIG_ISDN_AUDIO
-        int ifmt;
-#endif
-	modem_info *info;
-        
-        if ((i = isdn_dc2minor(di,channel))==-1) {
-                isdn_trash_skb(skb, FREE_READ);
-                return;
-        }
+
+	if ((i = isdn_dc2minor(di, channel)) == -1) {
+		isdn_trash_skb(skb, FREE_READ);
+		return;
+	}
 	/* Update statistics */
-        dev->ibytes[i] += skb->len;
+	dev->ibytes[i] += skb->len;
 	/* First, try to deliver data to network-device */
 	if (isdn_net_rcv_skb(i, skb))
 		return;
 	/* No network-device found, deliver to tty or raw-channel */
-        skb->free = 1;
+	SET_SKB_FREE(skb);
 	if (skb->len) {
-                if ((midx = dev->m_idx[i])<0) {
-                        /* if midx is invalid, drop packet */
-                        isdn_trash_skb(skb, FREE_READ);
-                        return;
-                }
-                info  = &dev->mdm.info[midx];
-#ifdef CONFIG_ISDN_AUDIO
-                ifmt = 1;
-
-                if (info->vonline)
-                        isdn_audio_calc_dtmf(info, skb->data, skb->len, ifmt);
-#endif
-                if ((info->online < 2) &&
-                    (!(info->vonline & 1))) {
-                        /* If Modem not listening, drop data */
-                        isdn_trash_skb(skb, FREE_READ);
-                        return;
-                }
-                if (info->emu.mdmreg[13] & 2)
-                        /* T.70 decoding: Simply throw away the T.70 header (4 bytes) */
-                        if ((skb->data[0] == 1) && ((skb->data[1] == 0) || (skb->data[1] == 1)))
-                                skb_pull(skb,4);
-                /* The users field of an sk_buff is used in a special way
-                 * with tty's incoming data:
-                 *   users is set to the number of DLE codes when in audio mode.
-                 */
-                skb->users = 0;
-#ifdef CONFIG_ISDN_AUDIO
-                if (info->vonline & 1) {
-                        /* voice conversion/compression */
-                        switch (info->emu.vpar[3]) {
-                                case 2:
-                                case 3:
-                                case 4:
-                                        /* adpcm
-                                         * Since compressed data takes less
-                                         * space, we can overwrite the buffer.
-                                         */
-                                        skb_trim(skb,isdn_audio_xlaw2adpcm(info->adpcmr,
-                                                                           ifmt,
-                                                                           skb->data,
-                                                                           skb->data,
-                                                                           skb->len));
-                                        break;
-                                case 5:
-                                        /* a-law */
-                                        if (!ifmt)
-                                                isdn_audio_ulaw2alaw(skb->data,skb->len);
-                                        break;
-                                case 6:
-                                        /* u-law */
-                                        if (ifmt)
-                                                isdn_audio_alaw2ulaw(skb->data,skb->len);
-                                        break;
-                        }
-                        skb->users = isdn_tty_countDLE(skb->data,skb->len);
-                }
-#endif
-                /* Try to deliver directly via tty-flip-buf if queue is empty */
-                save_flags(flags);
-                cli();
-                if (skb_queue_empty(&dev->drv[di]->rpqueue[channel]))
-                        if (isdn_tty_try_read(info, skb)) {
-                                restore_flags(flags);
-                                return;
-                        }
-                /* Direct deliver failed or queue wasn't empty.
-                 * Queue up for later dequeueing via timer-irq.
-                 */
-                __skb_queue_tail(&dev->drv[di]->rpqueue[channel], skb);
-                dev->drv[di]->rcvcount[channel] += (skb->len + skb->users);
-                restore_flags(flags);
-                /* Schedule dequeuing */
-                if ((dev->modempoll) && (info->rcvsched))
-                        isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
-                wake_up_interruptible(&dev->drv[di]->rcv_waitq[channel]);
+		if (isdn_tty_rcv_skb(i, di, channel, skb))
+			return;
+		wake_up_interruptible(&dev->drv[di]->rcv_waitq[channel]);
 	} else
-                isdn_trash_skb(skb, FREE_READ);
+		isdn_trash_skb(skb, FREE_READ);
 }
 
-void isdn_all_eaz(int di, int ch)
+void
+isdn_all_eaz(int di, int ch)
 {
 	isdn_ctrl cmd;
 
+	if (di < 0)
+		return;
 	cmd.driver = di;
 	cmd.arg = ch;
 	cmd.command = ISDN_CMD_SETEAZ;
-	cmd.num[0] = '\0';
+	cmd.parm.num[0] = '\0';
 	(void) dev->drv[di]->interface->command(&cmd);
 }
 
-static int isdn_status_callback(isdn_ctrl * c)
+static int
+isdn_status_callback(isdn_ctrl * c)
 {
 	int di;
-	int mi;
 	ulong flags;
 	int i;
 	int r;
-	int retval=0;
-        modem_info *info;
+	int retval = 0;
 	isdn_ctrl cmd;
 
 	di = c->driver;
-        i = isdn_dc2minor(di, c->arg);
+	i = isdn_dc2minor(di, c->arg);
 	switch (c->command) {
-                case ISDN_STAT_BSENT:
-			if (i<0)
+		case ISDN_STAT_BSENT:
+			if (i < 0)
 				return -1;
-                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
-                                return 0;
-                        if (isdn_net_stat_callback(i, c->command))
-                                return 0;
-                        isdn_tty_bsent(di, c->arg);
-                        wake_up_interruptible(&dev->drv[di]->snd_waitq[c->arg]);
-                        break;
-                case ISDN_STAT_STAVAIL:
-                        save_flags(flags);
-                        cli();
-                        dev->drv[di]->stavail += c->arg;
-                        restore_flags(flags);
-                        wake_up_interruptible(&dev->drv[di]->st_waitq);
-                        break;
-                case ISDN_STAT_RUN:
-                        dev->drv[di]->running = 1;
-                        for (i = 0; i < ISDN_MAX_CHANNELS; i++)
-                                if (dev->drvmap[i] == di)
-                                        isdn_all_eaz(di, dev->chanmap[i]);
-                        break;
-                case ISDN_STAT_STOP:
-                        dev->drv[di]->running = 0;
-                        break;
-                case ISDN_STAT_ICALL:
-			if (i<0)
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			if (isdn_net_stat_callback(i, c->command))
+				return 0;
+			if (isdn_tty_stat_callback(i, c))
+				return 0;
+			wake_up_interruptible(&dev->drv[di]->snd_waitq[c->arg]);
+			break;
+		case ISDN_STAT_STAVAIL:
+			save_flags(flags);
+			cli();
+			dev->drv[di]->stavail += c->arg;
+			restore_flags(flags);
+			wake_up_interruptible(&dev->drv[di]->st_waitq);
+			break;
+		case ISDN_STAT_RUN:
+			dev->drv[di]->running = 1;
+			for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+				if (dev->drvmap[i] == di)
+					isdn_all_eaz(di, dev->chanmap[i]);
+			break;
+		case ISDN_STAT_STOP:
+			dev->drv[di]->running = 0;
+			break;
+		case ISDN_STAT_ICALL:
+			if (i < 0)
 				return -1;
 #ifdef ISDN_DEBUG_STATCALLB
-                        printk(KERN_DEBUG "ICALL (net): %d %ld %s\n", di, c->arg, c->num);
+			printk(KERN_DEBUG "ICALL (net): %d %ld %s\n", di, c->arg, c->parm.num);
 #endif
-                        if (dev->global_flags & ISDN_GLOBAL_STOPPED) {
-                                cmd.driver = di;
-                                cmd.arg = c->arg;
-                                cmd.command = ISDN_CMD_HANGUP;
-                                dev->drv[di]->interface->command(&cmd);
-                                return 0;
-                        }
-
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED) {
+				cmd.driver = di;
+				cmd.arg = c->arg;
+				cmd.command = ISDN_CMD_HANGUP;
+				dev->drv[di]->interface->command(&cmd);
+				return 0;
+			}
 			/* Try to find a network-interface which will accept incoming call */
-                        cmd.driver = di;
-                        cmd.arg = c->arg;
-                        cmd.command = ISDN_CMD_LOCK;
-                        dev->drv[di]->interface->command(&cmd);
-			r = isdn_net_find_icall(di, c->arg, i, c->num);
+			cmd.driver = di;
+			cmd.arg = c->arg;
+			cmd.command = ISDN_CMD_LOCK;
+			dev->drv[di]->interface->command(&cmd);
+			r = isdn_net_find_icall(di, c->arg, i, c->parm.setup);
 			switch (r) {
-                                case 0:
-                                        /* No network-device replies. Schedule RING-message to
-                                         * tty and set RI-bit of modem-status.
-                                         */
-                                        if ((mi = isdn_tty_find_icall(di, c->arg, c->num)) >= 0) {
-                                                info = &dev->mdm.info[mi];
-                                                info->msr |= UART_MSR_RI;
-                                                isdn_tty_modem_result(2, info);
-                                                isdn_timer_ctrl(ISDN_TIMER_MODEMRING, 1);
-                                                return 1;
-                                        } else if (dev->drv[di]->reject_bus) {
-                                                cmd.driver = di;
-                                                cmd.arg = c->arg;
-                                                cmd.command = ISDN_CMD_HANGUP;
-                                                dev->drv[di]->interface->command(&cmd);
-                                                retval=2;
-                                        }
-                                        break;
-                                case 1:
-                                        /* Schedule connection-setup */
-                                        isdn_net_dial();
-                                        cmd.driver = di;
-                                        cmd.arg = c->arg;
-                                        cmd.command = ISDN_CMD_ACCEPTD;
-                                        dev->drv[di]->interface->command(&cmd);
-                                        return 1;
-                                        break;
-                                case 2:	/* For calling back, first reject incoming call ... */
-                                case 3:	/* Interface found, but down, reject call actively  */
-                                	retval=2;
-                                        printk(KERN_INFO "isdn: Rejecting Call\n");
-                                        cmd.driver = di;
-                                        cmd.arg = c->arg;
-                                        cmd.command = ISDN_CMD_HANGUP;
-                                        dev->drv[di]->interface->command(&cmd);
-                                        if (r == 3)
-                                                break;
-                                        /* Fall through */
-                                case 4:
-                                        /* ... then start callback. */
-                                        isdn_net_dial();
-                                        return 2;
+				case 0:
+					/* No network-device replies.
+					 * Try ttyI's
+					 */
+					if (isdn_tty_find_icall(di, c->arg, c->parm.setup) >= 0)
+						retval = 1;
+					else if (dev->drv[di]->reject_bus) {
+						cmd.driver = di;
+						cmd.arg = c->arg;
+						cmd.command = ISDN_CMD_HANGUP;
+						dev->drv[di]->interface->command(&cmd);
+						retval = 2;
+					}
+					break;
+				case 1:
+					/* Schedule connection-setup */
+					isdn_net_dial();
+					cmd.driver = di;
+					cmd.arg = c->arg;
+					cmd.command = ISDN_CMD_ACCEPTD;
+					dev->drv[di]->interface->command(&cmd);
+					retval = 1;
+					break;
+				case 2:	/* For calling back, first reject incoming call ... */
+				case 3:	/* Interface found, but down, reject call actively  */
+					retval = 2;
+					printk(KERN_INFO "isdn: Rejecting Call\n");
+					cmd.driver = di;
+					cmd.arg = c->arg;
+					cmd.command = ISDN_CMD_HANGUP;
+					dev->drv[di]->interface->command(&cmd);
+					if (r == 3)
+						break;
+					/* Fall through */
+				case 4:
+					/* ... then start callback. */
+					isdn_net_dial();
+					break;
 			}
-                        cmd.driver = di;
-                        cmd.arg = c->arg;
-                        cmd.command = ISDN_CMD_UNLOCK;
-                        dev->drv[di]->interface->command(&cmd);
-                        return retval;
-                        break;
-                case ISDN_STAT_CINF:
-			if (i<0)
+			if (retval != 1) {
+				cmd.driver = di;
+				cmd.arg = c->arg;
+				cmd.command = ISDN_CMD_UNLOCK;
+				dev->drv[di]->interface->command(&cmd);
+			}
+			return retval;
+			break;
+		case ISDN_STAT_CINF:
+			if (i < 0)
 				return -1;
 #ifdef ISDN_DEBUG_STATCALLB
-                        printk(KERN_DEBUG "CINF: %ld %s\n", c->arg, c->num);
+			printk(KERN_DEBUG "CINF: %ld %s\n", c->arg, c->parm.num);
 #endif
-                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
-                                return 0;
-                        if (strcmp(c->num, "0"))
-                                isdn_net_stat_callback(i, c->command);
-                        break;
-                case ISDN_STAT_CAUSE:
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			if (strcmp(c->parm.num, "0"))
+				isdn_net_stat_callback(i, c->command);
+			break;
+		case ISDN_STAT_CAUSE:
 #ifdef ISDN_DEBUG_STATCALLB
-                        printk(KERN_DEBUG "CAUSE: %ld %s\n", c->arg, c->num);
+			printk(KERN_DEBUG "CAUSE: %ld %s\n", c->arg, c->parm.num);
 #endif
-                        printk(KERN_INFO "isdn: cause: %s\n", c->num);
-                        break;
-                case ISDN_STAT_DCONN:
-			if (i<0)
+			printk(KERN_INFO "isdn: %s,ch%ld cause: %s\n",
+			       dev->drvid[di], c->arg, c->parm.num);
+			isdn_tty_stat_callback(i, c);
+			break;
+		case ISDN_STAT_DCONN:
+			if (i < 0)
 				return -1;
 #ifdef ISDN_DEBUG_STATCALLB
-                        printk(KERN_DEBUG "DCONN: %ld\n", c->arg);
+			printk(KERN_DEBUG "DCONN: %ld\n", c->arg);
 #endif
-                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
-                                return 0;
-                        /* Find any network-device, waiting for D-channel setup */
-                        if (isdn_net_stat_callback(i, c->command))
-                                break;
-
-			if ((mi = dev->m_idx[i]) >= 0) {
-				/* If any tty has just dialed-out, setup B-Channel */
-                                info = &dev->mdm.info[mi];
-				if (info->flags &
-				    (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
-					if (info->dialing == 1) {
-						info->dialing = 2;
-						cmd.driver = di;
-						cmd.arg = c->arg;
-						cmd.command = ISDN_CMD_ACCEPTB;
-						dev->drv[di]->interface->command(&cmd);
-						return 0;
-					}
-				}
-                        }
-                        break;
-                case ISDN_STAT_DHUP:
-			if (i<0)
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			/* Find any net-device, waiting for D-channel setup */
+			if (isdn_net_stat_callback(i, c->command))
+				break;
+			/* Find any ttyI, waiting for D-channel setup */
+			if (isdn_tty_stat_callback(i, c)) {
+				cmd.driver = di;
+				cmd.arg = c->arg;
+				cmd.command = ISDN_CMD_ACCEPTB;
+				dev->drv[di]->interface->command(&cmd);
+				break;
+			}
+			break;
+		case ISDN_STAT_DHUP:
+			if (i < 0)
 				return -1;
 #ifdef ISDN_DEBUG_STATCALLB
-                        printk(KERN_DEBUG "DHUP: %ld\n", c->arg);
-#endif
-                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
-                                return 0;
-                        dev->drv[di]->flags &= ~(1 << (c->arg));
-                        isdn_info_update();
-                        /* Signal hangup to network-devices */
-                        if (isdn_net_stat_callback(i, c->command))
-                                break;
-			if ((mi = dev->m_idx[i]) >= 0) {
-				/* Signal hangup to tty-device */
-                                info = &dev->mdm.info[mi];
-				if (info->flags &
-				    (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
-					if (info->dialing == 1) {
-						info->dialing = 0;
-						isdn_tty_modem_result(7, info);
-					}
-					if (info->online)
-						isdn_tty_modem_result(3, info);
-#ifdef ISDN_DEBUG_MODEM_HUP
-					printk(KERN_DEBUG "Mhup in ISDN_STAT_DHUP\n");
+			printk(KERN_DEBUG "DHUP: %ld\n", c->arg);
 #endif
-					isdn_tty_modem_hup(info);
-					return 0;
-				}
-			}
-                        break;
-                case ISDN_STAT_BCONN:
-			if (i<0)
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			dev->drv[di]->flags &= ~(1 << (c->arg));
+			isdn_info_update();
+			/* Signal hangup to network-devices */
+			if (isdn_net_stat_callback(i, c->command))
+				break;
+			if (isdn_tty_stat_callback(i, c))
+				break;
+			break;
+		case ISDN_STAT_BCONN:
+			if (i < 0)
 				return -1;
 #ifdef ISDN_DEBUG_STATCALLB
-                        printk(KERN_DEBUG "BCONN: %ld\n", c->arg);
+			printk(KERN_DEBUG "BCONN: %ld\n", c->arg);
 #endif
-                        /* Signal B-channel-connect to network-devices */
-                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
-                                return 0;
-                        dev->drv[di]->flags |= (1 << (c->arg));
-                        isdn_info_update();
-                        if (isdn_net_stat_callback(i, c->command))
-                                break;
-			if ((mi = dev->m_idx[i]) >= 0) {
-				/* Schedule CONNECT-Message to any tty, waiting for it and
-				 * set DCD-bit of its modem-status.
-				 */
-                                info = &dev->mdm.info[mi];
-				if (info->flags &
-				    (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
-					info->msr |= UART_MSR_DCD;
-					if (info->dialing)
-						info->dialing = 0;
-					info->rcvsched = 1;
-                                        if (USG_MODEM(dev->usage[i]))
-                                          isdn_tty_modem_result(5, info);
-                                        if (USG_VOICE(dev->usage[i]))
-                                          isdn_tty_modem_result(11, info);
-				}
-			}
-                        break;
-                case ISDN_STAT_BHUP:
-			if (i<0)
+			/* Signal B-channel-connect to network-devices */
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			dev->drv[di]->flags |= (1 << (c->arg));
+			isdn_info_update();
+			if (isdn_net_stat_callback(i, c->command))
+				break;
+			if (isdn_tty_stat_callback(i, c))
+				break;
+			break;
+		case ISDN_STAT_BHUP:
+			if (i < 0)
 				return -1;
 #ifdef ISDN_DEBUG_STATCALLB
-                        printk(KERN_DEBUG "BHUP: %ld\n", c->arg);
-#endif
-                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
-                                return 0;
-                        dev->drv[di]->flags &= ~(1 << (c->arg));
-                        isdn_info_update();
-			if ((mi = dev->m_idx[i]) >= 0) {
-				/* Signal hangup to tty-device, schedule NO CARRIER-message */
-                                info = &dev->mdm.info[mi];
-				if (info->flags &
-				    (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
-					if (info->msr & UART_MSR_DCD)
-						isdn_tty_modem_result(3, info);
-					info->msr &= ~(UART_MSR_DCD | UART_MSR_RI);
-#ifdef ISDN_DEBUG_MODEM_HUP
-					printk(KERN_DEBUG "Mhup in ISDN_STAT_BHUP\n");
+			printk(KERN_DEBUG "BHUP: %ld\n", c->arg);
 #endif
-					isdn_tty_modem_hup(info);
-				}
-			}
-                        break;
-                case ISDN_STAT_NODCH:
-			if (i<0)
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			dev->drv[di]->flags &= ~(1 << (c->arg));
+			isdn_info_update();
+			if (isdn_tty_stat_callback(i, c))
+				break;
+			break;
+		case ISDN_STAT_NODCH:
+			if (i < 0)
 				return -1;
 #ifdef ISDN_DEBUG_STATCALLB
-                        printk(KERN_DEBUG "NODCH: %ld\n", c->arg);
+			printk(KERN_DEBUG "NODCH: %ld\n", c->arg);
 #endif
-                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
-                                return 0;
-                        if (isdn_net_stat_callback(i, c->command))
-                                break;
-			if ((mi = dev->m_idx[i]) >= 0) {
-                                info = &dev->mdm.info[mi];
-				if (info->flags &
-				    (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
-					if (info->dialing) {
-						info->dialing = 0;
-						isdn_tty_modem_result(6, info);
-					}
-					info->msr &= ~UART_MSR_DCD;
-					if (info->online) {
-						isdn_tty_modem_result(3, info);
-						info->online = 0;
-					}
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			if (isdn_net_stat_callback(i, c->command))
+				break;
+			if (isdn_tty_stat_callback(i, c))
+				break;
+			break;
+		case ISDN_STAT_ADDCH:
+			break;
+		case ISDN_STAT_UNLOAD:
+			save_flags(flags);
+			cli();
+			isdn_tty_stat_callback(i, c);
+			for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+				if (dev->drvmap[i] == di) {
+					dev->drvmap[i] = -1;
+					dev->chanmap[i] = -1;
 				}
-			}
-                        break;
-                case ISDN_STAT_ADDCH:
-                        break;
-                case ISDN_STAT_UNLOAD:
-                        save_flags(flags);
-                        cli();
-                        for (i = 0; i < ISDN_MAX_CHANNELS; i++)
-                                if (dev->drvmap[i] == di) {
-                                        dev->drvmap[i] = -1;
-                                        dev->chanmap[i] = -1;
-                                }
-                        for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-                                modem_info *info = &dev->mdm.info[i];
-                                
-                                if (info->isdn_driver == di) {
-                                        info->isdn_driver = -1;
-                                        info->isdn_channel = -1;
-                                        if (info->online) {
-                                                isdn_tty_modem_result(3, info);
-                                                isdn_tty_modem_hup(info);
-                                        }
-                                }
-                        }
-                        dev->drivers--;
-                        dev->channels -= dev->drv[di]->channels;
-                        kfree(dev->drv[di]->rcverr);
-                        kfree(dev->drv[di]->rcvcount);
-                        for (i = 0; i < dev->drv[di]->channels; i++)
-                                isdn_free_queue(&dev->drv[di]->rpqueue[i]);
-                        kfree(dev->drv[di]->rpqueue);
-                        kfree(dev->drv[di]->rcv_waitq);
-                        kfree(dev->drv[di]->snd_waitq);
-                        kfree(dev->drv[di]);
-                        dev->drv[di] = NULL;
-                        dev->drvid[di][0] = '\0';
-                        isdn_info_update();
-                        restore_flags(flags);
-                        return 0;
-                default:
-                        return -1;
+			dev->drivers--;
+			dev->channels -= dev->drv[di]->channels;
+			kfree(dev->drv[di]->rcverr);
+			kfree(dev->drv[di]->rcvcount);
+			for (i = 0; i < dev->drv[di]->channels; i++)
+				isdn_free_queue(&dev->drv[di]->rpqueue[i]);
+			kfree(dev->drv[di]->rpqueue);
+			kfree(dev->drv[di]->rcv_waitq);
+			kfree(dev->drv[di]->snd_waitq);
+			kfree(dev->drv[di]);
+			dev->drv[di] = NULL;
+			dev->drvid[di][0] = '\0';
+			isdn_info_update();
+			restore_flags(flags);
+			return 0;
+		default:
+			return -1;
 	}
 	return 0;
 }
@@ -731,7 +645,8 @@
 /*
  * Get integer from char-pointer, set pointer to end of number
  */
-int isdn_getnum(char **p)
+int
+isdn_getnum(char **p)
 {
 	int v = -1;
 
@@ -746,14 +661,15 @@
  * isdn_readbchan() tries to get data from the read-queue.
  * It MUST be called with interrupts off.
  */
-int isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, int user)
+int
+isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, int user)
 {
 	int left;
 	int count;
 	int count_pull;
-        int count_put;
+	int count_put;
 	int dflag;
-        struct sk_buff *skb;
+	struct sk_buff *skb;
 	u_char *cp;
 
 	if (!dev->drv[di])
@@ -768,99 +684,106 @@
 	cp = buf;
 	count = 0;
 	while (left) {
-                if (!(skb = skb_peek(&dev->drv[di]->rpqueue[channel])))
-                        break;
-                if (skb->lock)
-                        break;
-                skb->lock = 1;
-                if (skb->users) {
-                        /* users is the count of DLE's in
-                         * this buff when in voice mode.
-                         */
-                        char *p = skb->data;
-                        unsigned long DLEmask = (1 << channel);
-
-                        dflag = 0;
-                        count_pull = count_put = 0;
-                        while ((count_pull < skb->len) && (left-- > 0)) {
-                                if (dev->drv[di]->DLEflag & DLEmask) {
-                                        if (user)
-                                                put_user(DLE,cp++);
-                                        else
-                                                *cp++ = DLE;
-                                        dev->drv[di]->DLEflag &= ~DLEmask;
-                                } else {
-                                        if (user)
-                                                put_user(*p,cp++);
-                                        else
-                                                *cp++ = *p;
-                                        if (*p == DLE) {
-                                                dev->drv[di]->DLEflag |= DLEmask;
-                                                skb->users--;
-                                        }
-                                        p++;
-                                        count_pull++;
-                                }
-                                count_put++;
-                        }
-                        if (count_pull >= skb->len)
-                                dflag = 1;
-                } else {
-                        /* No DLE's in buff, so simply copy it */
-                        dflag = 1;
-                        if ((count_pull = skb->len) > left) {
-                                count_pull = left;
-                                dflag = 0;
-                        }
-                        count_put = count_pull;
-                        if (user)
-                                copy_to_user(cp, skb->data, count_put);
-                        else
-                                memcpy(cp, skb->data, count_put);
-                        cp += count_put;
-                        left -= count_put;
-                }
-                count += count_put;
-                if (fp) {
-                        memset(fp, 0, count_put);
-                        fp += count_put;
-                }
-                if (dflag) {
-                        /* We got all the data in this buff.
-                         * Now we can dequeue it.
-                         */
+		if (!(skb = skb_peek(&dev->drv[di]->rpqueue[channel])))
+			break;
+#ifdef CONFIG_ISDN_AUDIO
+		if (ISDN_AUDIO_SKB_LOCK(skb))
+			break;
+		ISDN_AUDIO_SKB_LOCK(skb) = 1;
+		if (ISDN_AUDIO_SKB_DLECOUNT(skb)) {
+			char *p = skb->data;
+			unsigned long DLEmask = (1 << channel);
+
+			dflag = 0;
+			count_pull = count_put = 0;
+			while ((count_pull < skb->len) && (left-- > 0)) {
+				if (dev->drv[di]->DLEflag & DLEmask) {
+					if (user)
+						put_user(DLE, cp++);
+					else
+						*cp++ = DLE;
+					dev->drv[di]->DLEflag &= ~DLEmask;
+				} else {
+					if (user)
+						put_user(*p, cp++);
+					else
+						*cp++ = *p;
+					if (*p == DLE) {
+						dev->drv[di]->DLEflag |= DLEmask;
+						(ISDN_AUDIO_SKB_DLECOUNT(skb))--;
+					}
+					p++;
+					count_pull++;
+				}
+				count_put++;
+			}
+			if (count_pull >= skb->len)
+				dflag = 1;
+		} else {
+#endif
+			/* No DLE's in buff, so simply copy it */
+			dflag = 1;
+			if ((count_pull = skb->len) > left) {
+				count_pull = left;
+				dflag = 0;
+			}
+			count_put = count_pull;
+			if (user)
+				copy_to_user(cp, skb->data, count_put);
+			else
+				memcpy(cp, skb->data, count_put);
+			cp += count_put;
+			left -= count_put;
+#ifdef CONFIG_ISDN_AUDIO
+		}
+#endif
+		count += count_put;
+		if (fp) {
+			memset(fp, 0, count_put);
+			fp += count_put;
+		}
+		if (dflag) {
+			/* We got all the data in this buff.
+			 * Now we can dequeue it.
+			 */
 			if (fp)
 				*(fp - 1) = 0xff;
-                        skb->lock = 0;
-                        skb = skb_dequeue(&dev->drv[di]->rpqueue[channel]);
-                        isdn_trash_skb(skb, FREE_READ);
+#ifdef CONFIG_ISDN_AUDIO
+			ISDN_AUDIO_SKB_LOCK(skb) = 0;
+#endif
+			skb = skb_dequeue(&dev->drv[di]->rpqueue[channel]);
+			isdn_trash_skb(skb, FREE_READ);
 		} else {
-                        /* Not yet emptied this buff, so it
-                         * must stay in the queue, for further calls
-                         * but we pull off the data we got until now.
-                         */
-			skb_pull(skb,count_pull);
-                        skb->lock = 0;
-                }
+			/* Not yet emptied this buff, so it
+			 * must stay in the queue, for further calls
+			 * but we pull off the data we got until now.
+			 */
+			skb_pull(skb, count_pull);
+#ifdef CONFIG_ISDN_AUDIO
+			ISDN_AUDIO_SKB_LOCK(skb) = 0;
+#endif
+		}
 		dev->drv[di]->rcvcount[channel] -= count_put;
 	}
 	return count;
 }
 
-static __inline int isdn_minor2drv(int minor)
+static __inline int
+isdn_minor2drv(int minor)
 {
 	return (dev->drvmap[minor]);
 }
 
-static __inline int isdn_minor2chan(int minor)
+static __inline int
+isdn_minor2chan(int minor)
 {
 	return (dev->chanmap[minor]);
 }
 
-#define INF_DV 0x01 /* Data version for /dev/isdninfo */
+#define INF_DV 0x01             /* Data version for /dev/isdninfo */
 
 static char *
- isdn_statstr(void)
+isdn_statstr(void)
 {
 	static char istatbuf[2048];
 	char *p;
@@ -913,7 +836,8 @@
 
 /* Module interface-code */
 
-void isdn_info_update(void)
+void
+isdn_info_update(void)
 {
 	infostruct *p = dev->infochain;
 
@@ -924,7 +848,8 @@
 	wake_up_interruptible(&(dev->info_waitq));
 }
 
-static RWTYPE isdn_read(struct inode *inode, struct file *file, char *buf, RWARG count)
+static RWTYPE
+isdn_read(struct inode *inode, struct file *file, char *buf, RWARG count)
 {
 	uint minor = MINOR(inode->i_rdev);
 	int len = 0;
@@ -935,14 +860,15 @@
 	if (minor == ISDN_MINOR_STATUS) {
 		char *p;
 		if (!file->private_data) {
-                        if (file->f_flags & O_NONBLOCK)
-                                return -EAGAIN;
+			if (file->f_flags & O_NONBLOCK)
+				return -EAGAIN;
 			interruptible_sleep_on(&(dev->info_waitq));
-                }
+		}
 		p = isdn_statstr();
 		file->private_data = 0;
 		if ((len = strlen(p)) <= count) {
-			copy_to_user(buf, p, len);
+			if (copy_to_user(buf, p, len))
+				return -EFAULT;
 			file->f_pos += len;
 			return len;
 		}
@@ -957,11 +883,11 @@
 		if (!dev->drv[drvidx]->running)
 			return -ENODEV;
 		chidx = isdn_minor2chan(minor);
-                save_flags(flags);
-                cli();
+		save_flags(flags);
+		cli();
 		len = isdn_readbchan(drvidx, chidx, buf, 0, count, 1);
 		file->f_pos += len;
-                restore_flags(flags);
+		restore_flags(flags);
 		return len;
 	}
 	if (minor <= ISDN_MINOR_CTRLMAX) {
@@ -969,10 +895,10 @@
 		if (drvidx < 0)
 			return -ENODEV;
 		if (!dev->drv[drvidx]->stavail) {
-                        if (file->f_flags & O_NONBLOCK)
-                                return -EAGAIN;
+			if (file->f_flags & O_NONBLOCK)
+				return -EAGAIN;
 			interruptible_sleep_on(&(dev->drv[drvidx]->st_waitq));
-                }
+		}
 		if (dev->drv[drvidx]->interface->readstat)
 			len = dev->drv[drvidx]->interface->
 			    readstat(buf, MIN(count, dev->drv[drvidx]->stavail),
@@ -981,10 +907,10 @@
 			len = 0;
 		save_flags(flags);
 		cli();
-                if (len)
-                        dev->drv[drvidx]->stavail -= len;
-                else
-                        dev->drv[drvidx]->stavail = 0;
+		if (len)
+			dev->drv[drvidx]->stavail -= len;
+		else
+			dev->drv[drvidx]->stavail = 0;
 		restore_flags(flags);
 		file->f_pos += len;
 		return len;
@@ -996,12 +922,14 @@
 	return -ENODEV;
 }
 
-static LSTYPE isdn_lseek(struct inode *inode, struct file *file, LSARG offset, int orig)
+static LSTYPE
+isdn_lseek(struct inode *inode, struct file *file, LSARG offset, int orig)
 {
 	return -ESPIPE;
 }
 
-static RWTYPE isdn_write(struct inode *inode, struct file *file, const char *buf, RWARG count)
+static RWTYPE
+isdn_write(struct inode *inode, struct file *file, const char *buf, RWARG count)
 {
 	uint minor = MINOR(inode->i_rdev);
 	int drvidx;
@@ -1045,10 +973,12 @@
 	return -ENODEV;
 }
 
-static int isdn_select(struct inode *inode, struct file *file, int type, select_table * st)
+#if (LINUX_VERSION_CODE < 0x020117)
+static int
+isdn_select(struct inode *inode, struct file *file, int type, select_table * st)
 {
 	uint minor = MINOR(inode->i_rdev);
-        int drvidx  = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+	int drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
 
 	if (minor == ISDN_MINOR_STATUS) {
 		if (file->private_data)
@@ -1063,49 +993,81 @@
 		if (drvidx < 0)
 			return -ENODEV;
 		if (dev->drv[drvidx]->stavail)
-                        return 1;
-                else {
-                        if (st)
-                                select_wait(&(dev->drv[drvidx]->st_waitq), st);
-                        return 0;
-                }
+			return 1;
+		else {
+			if (st)
+				select_wait(&(dev->drv[drvidx]->st_waitq), st);
+			return 0;
+		}
 		return 1;
-	 }
+	}
 #ifdef CONFIG_ISDN_PPP
 	if (minor <= ISDN_MINOR_PPPMAX)
 		return (isdn_ppp_select(minor - ISDN_MINOR_PPP, file, type, st));
 #endif
 	return -ENODEV;
 }
+#else
+static unsigned int
+isdn_poll(struct file *file, poll_table * wait)
+{
+	unsigned int mask = 0;
+	unsigned int minor = MINOR(file->f_inode->i_rdev);
+	int drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+
+	if (minor == ISDN_MINOR_STATUS) {
+		poll_wait(&(dev->info_waitq), wait);
+		/* mask = POLLOUT | POLLWRNORM; */
+		if (file->private_data) {
+			mask |= POLLIN | POLLRDNORM;
+		}
+		return mask;
+	}
+	if (minor >= ISDN_MINOR_CTRL && minor <= ISDN_MINOR_CTRLMAX) {
+		poll_wait(&(dev->drv[drvidx]->st_waitq), wait);
+		if (drvidx < 0) {
+			printk(KERN_ERR "isdn_common: isdn_poll 1 -> what the hell\n");
+			return POLLERR;
+		}
+		mask = POLLOUT | POLLWRNORM;
+		if (dev->drv[drvidx]->stavail) {
+			mask |= POLLIN | POLLRDNORM;
+		}
+		return mask;
+	}
+#ifdef CONFIG_ISDN_PPP
+	if (minor <= ISDN_MINOR_PPPMAX)
+		return (isdn_ppp_poll(file, wait));
+#endif
+	printk(KERN_ERR "isdn_common: isdn_poll 2 -> what the hell\n");
+	return POLLERR;
+}
+#endif
 
-static int isdn_set_allcfg(char *src)
+static int
+isdn_set_allcfg(char *src)
 {
 	int ret;
 	int i;
 	ulong flags;
-	char buf[1024];
 	isdn_net_ioctl_cfg cfg;
 	isdn_net_ioctl_phone phone;
 
 	if ((ret = isdn_net_rmall()))
 		return ret;
+	if ((ret = copy_from_user((char *) &i, src, sizeof(int))))
+		return ret;
 	save_flags(flags);
 	cli();
-	if ((ret = verify_area(VERIFY_READ, (void *) src, sizeof(int)))) {
-		restore_flags(flags);
-		return ret;
-	}
-	copy_from_user((char *) &i, src, sizeof(int));
-        src += sizeof(int);
+	src += sizeof(int);
 	while (i) {
-		char *c;
-		char *c2;
+		int phone_len;
+		int out_flag;
 
-		if ((ret = verify_area(VERIFY_READ, (void *) src, sizeof(cfg)))) {
+		if ((ret = copy_from_user((char *) &cfg, src, sizeof(cfg)))) {
 			restore_flags(flags);
 			return ret;
 		}
-		copy_from_user((char *) &cfg, src, sizeof(cfg));
 		src += sizeof(cfg);
 		if (!isdn_net_new(cfg.name, NULL)) {
 			restore_flags(flags);
@@ -1115,49 +1077,31 @@
 			restore_flags(flags);
 			return ret;
 		}
-		if ((ret = verify_area(VERIFY_READ, (void *) src, sizeof(buf)))) {
-			restore_flags(flags);
-			return ret;
-		}
-		copy_from_user(buf, src, sizeof(buf));
-		src += sizeof(buf);
-		c = buf;
-		while (*c) {
-			if ((c2 = strchr(c, ' ')))
-				*c2++ = '\0';
-			strcpy(phone.phone, c);
-			strcpy(phone.name, cfg.name);
-			phone.outgoing = 0;
-			if ((ret = isdn_net_addphone(&phone))) {
+		phone_len = out_flag = 0;
+		while (out_flag < 2) {
+			if ((ret = verify_area(VERIFY_READ, src, 1))) {
 				restore_flags(flags);
 				return ret;
 			}
-			if (c2)
-				c = c2;
-			else
-				c += strlen(c);
-		}
-		if ((ret = verify_area(VERIFY_READ, (void *) src, sizeof(buf)))) {
-			restore_flags(flags);
-			return ret;
-		}
-		copy_from_user(buf, src, sizeof(buf));
-		src += sizeof(buf);
-		c = buf;
-		while (*c) {
-			if ((c2 = strchr(c, ' ')))
-				*c2++ = '\0';
-			strcpy(phone.phone, c);
-			strcpy(phone.name, cfg.name);
-			phone.outgoing = 1;
-			if ((ret = isdn_net_addphone(&phone))) {
-				restore_flags(flags);
-				return ret;
+			GET_USER(phone.phone[phone_len], src++);
+			if ((phone.phone[phone_len] == ' ') ||
+			    (phone.phone[phone_len] == '\0')) {
+				if (phone_len) {
+					phone.phone[phone_len] = '\0';
+					strcpy(phone.name, cfg.name);
+					phone.outgoing = out_flag;
+					if ((ret = isdn_net_addphone(&phone))) {
+						restore_flags(flags);
+						return ret;
+					}
+				} else
+					out_flag++;
+				phone_len = 0;
 			}
-			if (c2)
-				c = c2;
-			else
-				c += strlen(c);
+			if (++phone_len >= sizeof(phone.phone))
+				printk(KERN_WARNING
+				       "%s: IIOCSETSET phone number too long, ignored\n",
+				       cfg.name);
 		}
 		i--;
 	}
@@ -1165,7 +1109,8 @@
 	return 0;
 }
 
-static int isdn_get_allcfg(char *dest)
+static int
+isdn_get_allcfg(char *dest)
 {
 	isdn_net_ioctl_cfg cfg;
 	isdn_net_ioctl_phone phone;
@@ -1178,7 +1123,7 @@
 	cli();
 	p = dev->netdev;
 	while (p) {
-		if ((ret = verify_area(VERIFY_WRITE, (void *) dest, sizeof(cfg) + 10))) {
+		if ((ret = verify_area(VERIFY_WRITE, (void *) dest, sizeof(cfg) + 200))) {
 			restore_flags(flags);
 			return ret;
 		}
@@ -1196,11 +1141,18 @@
 		cfg.p_encap = p->local.p_encap;
 		cfg.secure = (p->local.flags & ISDN_NET_SECURE) ? 1 : 0;
 		cfg.callback = (p->local.flags & ISDN_NET_CALLBACK) ? 1 : 0;
-		cfg.chargehup = (p->local.hupflags & 4) ? 1 : 0;
-		cfg.ihup = (p->local.hupflags & 8) ? 1 : 0;
-		copy_to_user(dest, p->local.name, 10);
+		cfg.chargehup = (p->local.hupflags & ISDN_CHARGEHUP) ? 1 : 0;
+		cfg.ihup = (p->local.hupflags & ISDN_INHUP) ? 1 : 0;
+		cfg.chargeint = p->local.chargeint;
+		if (copy_to_user(dest, p->local.name, 10)) {
+			restore_flags(flags);
+			return -EFAULT;
+		}
 		dest += 10;
-		copy_to_user(dest, (char *) &cfg, sizeof(cfg));
+		if (copy_to_user(dest, (char *) &cfg, sizeof(cfg))) {
+			restore_flags(flags);
+			return -EFAULT;
+		}
 		dest += sizeof(cfg);
 		strcpy(phone.name, p->local.name);
 		phone.outgoing = 0;
@@ -1216,51 +1168,63 @@
 			return ret;
 		} else
 			dest += ret;
+		put_user(0, dest);
 		p = p->next;
 	}
 	restore_flags(flags);
 	return 0;
 }
 
-static int isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
+static int
+isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
 {
 	uint minor = MINOR(inode->i_rdev);
 	isdn_ctrl c;
 	int drvidx;
 	int chidx;
 	int ret;
+	int i;
+	char *p;
 	char *s;
-	char name[10];
-	char bname[21];
-	isdn_ioctl_struct iocts;
-	isdn_net_ioctl_phone phone;
-	isdn_net_ioctl_cfg cfg;
+	union iocpar {
+		char name[10];
+		char bname[22];
+		isdn_ioctl_struct iocts;
+		isdn_net_ioctl_phone phone;
+		isdn_net_ioctl_cfg cfg;
+	} iocpar;
+
+#define name  iocpar.name
+#define bname iocpar.bname
+#define iocts iocpar.iocts
+#define phone iocpar.phone
+#define cfg   iocpar.cfg
 
 	if (minor == ISDN_MINOR_STATUS) {
-                switch (cmd) {
-                        case IIOCGETDVR:
-                                return(TTY_DV +
-                                       (NET_DV << 8) +
-                                       (INF_DV << 16));
-                        case IIOCGETCPS:
-                                if (arg) {
-                                        ulong *p = (ulong *)arg;
-                                        int i;
-                                        if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
-                                                               sizeof(ulong)*ISDN_MAX_CHANNELS*2)))
-                                                return ret;
-                                        for (i = 0;i<ISDN_MAX_CHANNELS;i++) {
-                                                put_user(dev->ibytes[i],p++);
-                                                put_user(dev->obytes[i],p++);
-                                        }
-                                        return 0;
-                                } else
-                                        return -EINVAL;
-                                break;
-                        default:
-		                return -EINVAL;
-                }
-        }
+		switch (cmd) {
+			case IIOCGETDVR:
+				return (TTY_DV +
+					(NET_DV << 8) +
+					(INF_DV << 16));
+			case IIOCGETCPS:
+				if (arg) {
+					ulong *p = (ulong *) arg;
+					int i;
+					if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
+							       sizeof(ulong) * ISDN_MAX_CHANNELS * 2)))
+						return ret;
+					for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+						put_user(dev->ibytes[i], p++);
+						put_user(dev->obytes[i], p++);
+					}
+					return 0;
+				} else
+					return -EINVAL;
+				break;
+			default:
+				return -EINVAL;
+		}
+	}
 	if (!dev->drivers)
 		return -ENODEV;
 	if (minor < ISDN_MINOR_CTRL) {
@@ -1275,336 +1239,329 @@
 	if (minor <= ISDN_MINOR_CTRLMAX) {
 		switch (cmd) {
 #ifdef CONFIG_NETDEVICES
-                        case IIOCNETAIF:
-                                /* Add a network-interface */
-                                if (arg) {
-                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(name))))
-                                                return ret;
-                                        copy_from_user(name, (char *) arg, sizeof(name));
-                                        s = name;
-                                } else
-                                        s = NULL;
-                                if ((s = isdn_net_new(s, NULL))) {
-                                        if ((ret = verify_area(VERIFY_WRITE, (void *) arg, strlen(s) + 1)))
-                                                return ret;
-                                        copy_to_user((char *) arg, s, strlen(s) + 1);
-                                        return 0;
-                                } else
-                                        return -ENODEV;
-                        case IIOCNETASL:
-                                /* Add a slave to a network-interface */
-                                if (arg) {
-                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(bname))))
-                                                return ret;
-                                        copy_from_user(bname, (char *) arg, sizeof(bname));
-                                } else
-                                        return -EINVAL;
-                                if ((s = isdn_net_newslave(bname))) {
-                                        if ((ret = verify_area(VERIFY_WRITE, (void *) arg, strlen(s) + 1)))
-                                                return ret;
-                                        copy_to_user((char *) arg, s, strlen(s) + 1);
-                                        return 0;
-                                } else
-                                        return -ENODEV;
-                        case IIOCNETDIF:
-                                /* Delete a network-interface */
-                                if (arg) {
-                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(name))))
-                                                return ret;
-                                        copy_from_user(name, (char *) arg, sizeof(name));
-                                        return isdn_net_rm(name);
-                                } else
-                                        return -EINVAL;
-                        case IIOCNETSCF:
-                                /* Set configurable parameters of a network-interface */
-                                if (arg) {
-                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(cfg))))
-                                                return ret;
-                                        copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg));
-                                        return isdn_net_setcfg(&cfg);
-                                } else
-                                        return -EINVAL;
-                        case IIOCNETGCF:
-                                /* Get configurable parameters of a network-interface */
-                                if (arg) {
-                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(cfg))))
-                                                return ret;
-                                        copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg));
-                                        if (!(ret = isdn_net_getcfg(&cfg))) {
-                                                if ((ret = verify_area(VERIFY_WRITE, (void *) arg, sizeof(cfg))))
-                                                        return ret;
-                                                copy_to_user((char *) arg, (char *) &cfg, sizeof(cfg));
-                                        }
-                                        return ret;
-                                } else
-                                        return -EINVAL;
-                        case IIOCNETANM:
-                                /* Add a phone-number to a network-interface */
-                                if (arg) {
-                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(phone))))
-                                                return ret;
-                                        copy_from_user((char *) &phone, (char *) arg, sizeof(phone));
-                                        return isdn_net_addphone(&phone);
-                                } else
-                                        return -EINVAL;
-                        case IIOCNETGNM:
-                                /* Get list of phone-numbers of a network-interface */
-                                if (arg) {
-                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(phone))))
-                                                return ret;
-                                        copy_from_user((char *) &phone, (char *) arg, sizeof(phone));
-                                        return isdn_net_getphones(&phone, (char *) arg);
-                                } else
-                                        return -EINVAL;
-                        case IIOCNETDNM:
-                                /* Delete a phone-number of a network-interface */
-                                if (arg) {
-                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(phone))))
-                                                return ret;
-                                        copy_from_user((char *) &phone, (char *) arg, sizeof(phone));
-                                        return isdn_net_delphone(&phone);
-                                } else
-                                        return -EINVAL;
-                        case IIOCNETDIL:
-                                /* Force dialing of a network-interface */
-                                if (arg) {
-                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(name))))
-                                                return ret;
-                                        copy_from_user(name, (char *) arg, sizeof(name));
-                                        return isdn_net_force_dial(name);
-                                } else
-                                        return -EINVAL;
+			case IIOCNETAIF:
+				/* Add a network-interface */
+				if (arg) {
+					if ((ret = copy_from_user(name, (char *) arg, sizeof(name))))
+						return ret;
+					s = name;
+				} else
+					s = NULL;
+				if ((s = isdn_net_new(s, NULL))) {
+					if ((ret = copy_to_user((char *) arg, s, strlen(s) + 1)))
+						return ret;
+					return 0;
+				} else
+					return -ENODEV;
+			case IIOCNETASL:
+				/* Add a slave to a network-interface */
+				if (arg) {
+					if ((ret = copy_from_user(bname, (char *) arg, sizeof(bname) - 1)))
+						return ret;
+				} else
+					return -EINVAL;
+				if ((s = isdn_net_newslave(bname))) {
+					if ((ret = copy_to_user((char *) arg, s, strlen(s) + 1)))
+						return ret;
+					return 0;
+				} else
+					return -ENODEV;
+			case IIOCNETDIF:
+				/* Delete a network-interface */
+				if (arg) {
+					if ((ret = copy_from_user(name, (char *) arg, sizeof(name))))
+						return ret;
+					return isdn_net_rm(name);
+				} else
+					return -EINVAL;
+			case IIOCNETSCF:
+				/* Set configurable parameters of a network-interface */
+				if (arg) {
+					if ((ret = copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg))))
+						return ret;
+					return isdn_net_setcfg(&cfg);
+				} else
+					return -EINVAL;
+			case IIOCNETGCF:
+				/* Get configurable parameters of a network-interface */
+				if (arg) {
+					if ((ret = copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg))))
+						return ret;
+					if (!(ret = isdn_net_getcfg(&cfg))) {
+						if ((ret = copy_to_user((char *) arg, (char *) &cfg, sizeof(cfg))))
+							return ret;
+					}
+					return ret;
+				} else
+					return -EINVAL;
+			case IIOCNETANM:
+				/* Add a phone-number to a network-interface */
+				if (arg) {
+					if ((ret = copy_from_user((char *) &phone, (char *) arg, sizeof(phone))))
+						return ret;
+					return isdn_net_addphone(&phone);
+				} else
+					return -EINVAL;
+			case IIOCNETGNM:
+				/* Get list of phone-numbers of a network-interface */
+				if (arg) {
+					if ((ret = copy_from_user((char *) &phone, (char *) arg, sizeof(phone))))
+						return ret;
+					return isdn_net_getphones(&phone, (char *) arg);
+				} else
+					return -EINVAL;
+			case IIOCNETDNM:
+				/* Delete a phone-number of a network-interface */
+				if (arg) {
+					if ((ret = copy_from_user((char *) &phone, (char *) arg, sizeof(phone))))
+						return ret;
+					return isdn_net_delphone(&phone);
+				} else
+					return -EINVAL;
+			case IIOCNETDIL:
+				/* Force dialing of a network-interface */
+				if (arg) {
+					if ((ret = copy_from_user(name, (char *) arg, sizeof(name))))
+						return ret;
+					return isdn_net_force_dial(name);
+				} else
+					return -EINVAL;
 #ifdef CONFIG_ISDN_PPP
-                        case IIOCNETALN:
-                                if(arg) {
-                                        if ((ret = verify_area(VERIFY_READ,
-                                                               (void*)arg,
-                                                               sizeof(name))))
-                                                return ret;
-                                } else
-                                        return -EINVAL;
-                                copy_from_user(name,(char*)arg,sizeof(name));
-                                return isdn_ppp_dial_slave(name);
-                        case IIOCNETDLN:
-                                if(arg) {
-                                        if ((ret = verify_area(VERIFY_READ,
-                                                               (void*)arg,
-                                                               sizeof(name))))
-                                                return ret;
-                                } else
-                                        return -EINVAL;
-                                copy_from_user(name,(char*)arg,sizeof(name));
-                                return isdn_ppp_hangup_slave(name);
-#endif
-                        case IIOCNETHUP:
-                                /* Force hangup of a network-interface */
-                                if (arg) {
-                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(name))))
-                                                return ret;
-                                        copy_from_user(name, (char *) arg, sizeof(name));
-                                        return isdn_net_force_hangup(name);
-                                } else
-                                        return -EINVAL;
-                                break;
-#endif				/* CONFIG_NETDEVICES */
-                        case IIOCSETVER:
-                                dev->net_verbose = arg;
-                                printk(KERN_INFO "isdn: Verbose-Level is %d\n", dev->net_verbose);
-                                return 0;
-                        case IIOCSETGST:
-                                if (arg)
-                                        dev->global_flags |= ISDN_GLOBAL_STOPPED;
-                                else
-                                        dev->global_flags &= ~ISDN_GLOBAL_STOPPED;
-                                printk(KERN_INFO "isdn: Global Mode %s\n",
-                                       (dev->global_flags & ISDN_GLOBAL_STOPPED) ? "stopped" : "running");
-                                return 0;
-                        case IIOCSETBRJ:
-                                drvidx = -1;
-                                if (arg) {
-                                        int i;
-                                        char *p;
-                                        if ((ret = verify_area(VERIFY_READ, (void *) arg,
-                                                               sizeof(isdn_ioctl_struct))))
-                                                return ret;
-                                        copy_from_user((char *) &iocts, (char *) arg,
-                                                      sizeof(isdn_ioctl_struct));
-                                        if (strlen(iocts.drvid)) {
-                                                if ((p = strchr(iocts.drvid, ',')))
-                                                        *p = 0;
-                                                drvidx = -1;
-                                                for (i = 0; i < ISDN_MAX_DRIVERS; i++)
-                                                        if (!(strcmp(dev->drvid[i], iocts.drvid))) {
-                                                                drvidx = i;
-                                                                break;
-                                                        }
-                                        }
-                                }
-                                if (drvidx == -1)
-                                        return -ENODEV;
-                                dev->drv[drvidx]->reject_bus = iocts.arg;
-                                return 0;
-                        case IIOCGETSET:
-                                /* Get complete setup (all network-interfaces and profile-
-                                   settings of all tty-devices */
-                                if (arg)
-                                        return (isdn_get_allcfg((char *) arg));
-                                else
-                                        return -EINVAL;
-                                break;
-                        case IIOCSETSET:
-                                /* Set complete setup (all network-interfaces and profile-
-                                   settings of all tty-devices */
-                                if (arg)
-                                        return (isdn_set_allcfg((char *) arg));
-                                else
-                                        return -EINVAL;
-                                break;
-                        case IIOCSIGPRF:
-                                dev->profd = current;
-                                return 0;
-                                break;
-                        case IIOCGETPRF:
-                                /* Get all Modem-Profiles */
-                                if (arg) {
-                                        char *p = (char *) arg;
-                                        int i;
-                                        
-                                        if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
-                                                               (ISDN_MODEM_ANZREG + ISDN_MSNLEN)
-                                                               * ISDN_MAX_CHANNELS)))
-                                                return ret;
-                                        
-                                        for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-                                                copy_to_user(p, dev->mdm.info[i].emu.profile,
-                                                            ISDN_MODEM_ANZREG);
-                                                p += ISDN_MODEM_ANZREG;
-                                                copy_to_user(p, dev->mdm.info[i].emu.pmsn, ISDN_MSNLEN);
-                                                p += ISDN_MSNLEN;
-                                        }
-                                        return (ISDN_MODEM_ANZREG + ISDN_MSNLEN) * ISDN_MAX_CHANNELS;
-                                } else
-                                        return -EINVAL;
-                                break;
-                        case IIOCSETPRF:
-                                /* Set all Modem-Profiles */
-                                if (arg) {
-                                        char *p = (char *) arg;
-                                        int i;
-                                        
-                                        if ((ret = verify_area(VERIFY_READ, (void *) arg,
-                                                               (ISDN_MODEM_ANZREG + ISDN_MSNLEN)
-                                                               * ISDN_MAX_CHANNELS)))
-                                                return ret;
-                                        
-                                        for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-                                                copy_from_user(dev->mdm.info[i].emu.profile, p,
-                                                              ISDN_MODEM_ANZREG);
-                                                p += ISDN_MODEM_ANZREG;
-                                                copy_from_user(dev->mdm.info[i].emu.pmsn, p, ISDN_MSNLEN);
-                                                p += ISDN_MSNLEN;
-                                        }
-                                        return 0;
-                                } else
-                                        return -EINVAL;
-                                break;
-                        case IIOCSETMAP:
-                        case IIOCGETMAP:
-                                /* Set/Get MSN->EAZ-Mapping for a driver */
-                                if (arg) {
-                                        int i;
-                                        char *p;
-                                        char nstring[255];
-                                        
-                                        if ((ret = verify_area(VERIFY_READ, (void *) arg,
-                                                               sizeof(isdn_ioctl_struct))))
-                                                return ret;
-                                        copy_from_user((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct));
-                                        if (strlen(iocts.drvid)) {
-                                                drvidx = -1;
-                                                for (i = 0; i < ISDN_MAX_DRIVERS; i++)
-                                                        if (!(strcmp(dev->drvid[i], iocts.drvid))) {
-                                                                drvidx = i;
-                                                                break;
-                                                        }
-                                        } else
-                                                drvidx = 0;
-                                        if (drvidx == -1)
-                                                return -ENODEV;
-                                        if (cmd == IIOCSETMAP) {
-                                                if ((ret = verify_area(VERIFY_READ, (void *) iocts.arg, 255)))
-                                                        return ret;
-                                                copy_from_user(nstring, (char *) iocts.arg, 255);
-                                                memset(dev->drv[drvidx]->msn2eaz, 0,
-                                                       sizeof(dev->drv[drvidx]->msn2eaz));
-                                                p = strtok(nstring, ",");
-                                                i = 0;
-                                                while ((p) && (i < 10)) {
-                                                        strcpy(dev->drv[drvidx]->msn2eaz[i++], p);
-                                                        p = strtok(NULL, ",");
-                                                }
-                                        } else {
-                                                p = nstring;
-                                                for (i = 0; i < 10; i++)
-                                                        p += sprintf(p, "%s%s",
-                                                                     strlen(dev->drv[drvidx]->msn2eaz[i]) ?
-                                                                     dev->drv[drvidx]->msn2eaz[i] : "-",
-                                                                     (i < 9) ? "," : "\0");
-                                                if ((ret = verify_area(VERIFY_WRITE, (void *) iocts.arg,
-                                                                       strlen(nstring) + 1)))
-                                                        return ret;
-                                                copy_to_user((char *) iocts.arg, nstring, strlen(nstring) + 1);
-                                        }
-                                        return 0;
-                                } else
-                                        return -EINVAL;
-                        case IIOCDBGVAR:
-                                if (arg) {
-                                        if ((ret = verify_area(VERIFY_WRITE, (void *) arg, sizeof(ulong))))
-                                                return ret;
-                                        copy_to_user((char *) arg, (char *) &dev, sizeof(ulong));
-                                        return 0;
-                                } else
-                                        return -EINVAL;
-                                break;
-                        default:
-                                if ((cmd&IIOCDRVCTL)==IIOCDRVCTL)
-                                        cmd = ((cmd>>_IOC_NRSHIFT)&_IOC_NRMASK)& ISDN_DRVIOCTL_MASK;
+			case IIOCNETALN:
+				if (!arg)
+					return -EINVAL;
+				if ((ret = copy_from_user(name, (char *) arg, sizeof(name))))
+					return ret;
+				return isdn_ppp_dial_slave(name);
+			case IIOCNETDLN:
+				if (!arg)
+					return -EINVAL;
+				if ((ret = copy_from_user(name, (char *) arg, sizeof(name))))
+					return ret;
+				return isdn_ppp_hangup_slave(name);
+#endif
+			case IIOCNETHUP:
+				/* Force hangup of a network-interface */
+				if (!arg)
+					return -EINVAL;
+			        if ((ret = copy_from_user(name, (char *) arg, sizeof(name))))
+			        	return ret;
+			        return isdn_net_force_hangup(name);
+				break;
+#endif                          /* CONFIG_NETDEVICES */
+			case IIOCSETVER:
+				dev->net_verbose = arg;
+				printk(KERN_INFO "isdn: Verbose-Level is %d\n", dev->net_verbose);
+				return 0;
+			case IIOCSETGST:
+				if (arg)
+					dev->global_flags |= ISDN_GLOBAL_STOPPED;
+				else
+					dev->global_flags &= ~ISDN_GLOBAL_STOPPED;
+				printk(KERN_INFO "isdn: Global Mode %s\n",
+				       (dev->global_flags & ISDN_GLOBAL_STOPPED) ? "stopped" : "running");
+				return 0;
+			case IIOCSETBRJ:
+				drvidx = -1;
+				if (arg) {
+					int i;
+					char *p;
+					if ((ret = copy_from_user((char *) &iocts, (char *) arg,
+					      sizeof(isdn_ioctl_struct))))
+						return ret;
+					if (strlen(iocts.drvid)) {
+						if ((p = strchr(iocts.drvid, ',')))
+							*p = 0;
+						drvidx = -1;
+						for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+							if (!(strcmp(dev->drvid[i], iocts.drvid))) {
+								drvidx = i;
+								break;
+							}
+					}
+				}
+				if (drvidx == -1)
+					return -ENODEV;
+				dev->drv[drvidx]->reject_bus = iocts.arg;
+				return 0;
+			case IIOCGETSET:
+				/* Get complete setup (all network-interfaces and profile-
+				   settings of all tty-devices */
+				if (arg)
+					return (isdn_get_allcfg((char *) arg));
 				else
 					return -EINVAL;
-                                if (arg) {
-                                        int i;
-                                        char *p;
-                                        if ((ret = verify_area(VERIFY_READ, (void *) arg,
-                                                               sizeof(isdn_ioctl_struct))))
-                                                return ret;
-                                        copy_from_user((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct));
-                                        if (strlen(iocts.drvid)) {
-                                                if ((p = strchr(iocts.drvid, ',')))
-                                                        *p = 0;
-                                                drvidx = -1;
-                                                for (i = 0; i < ISDN_MAX_DRIVERS; i++)
-                                                        if (!(strcmp(dev->drvid[i], iocts.drvid))) {
-                                                                drvidx = i;
-                                                                break;
-                                                        }
-                                        } else
-                                                drvidx = 0;
-                                        if (drvidx == -1)
-                                                return -ENODEV;
-                                        if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
-                                                               sizeof(isdn_ioctl_struct))))
-                                                return ret;
-                                        c.driver = drvidx;
-                                        c.command = ISDN_CMD_IOCTL;
-                                        c.arg = cmd;
-                                        memcpy(c.num, (char *) &iocts.arg, sizeof(ulong));
-                                        ret = dev->drv[drvidx]->interface->command(&c);
-                                        memcpy((char *) &iocts.arg, c.num, sizeof(ulong));
-                                        copy_to_user((char *) arg, &iocts, sizeof(isdn_ioctl_struct));
-                                        return ret;
-                                } else
-                                        return -EINVAL;
+				break;
+			case IIOCSETSET:
+				/* Set complete setup (all network-interfaces and profile-
+				   settings of all tty-devices */
+				if (arg)
+					return (isdn_set_allcfg((char *) arg));
+				else
+					return -EINVAL;
+				break;
+			case IIOCSIGPRF:
+				dev->profd = current;
+				return 0;
+				break;
+			case IIOCGETPRF:
+				/* Get all Modem-Profiles */
+				if (arg) {
+					char *p = (char *) arg;
+					int i;
+
+					if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
+					(ISDN_MODEM_ANZREG + ISDN_MSNLEN)
+						   * ISDN_MAX_CHANNELS)))
+						return ret;
+
+					for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+						if (copy_to_user(p, dev->mdm.info[i].emu.profile,
+						      ISDN_MODEM_ANZREG))
+							return -EFAULT;
+						p += ISDN_MODEM_ANZREG;
+						if (copy_to_user(p, dev->mdm.info[i].emu.pmsn, ISDN_MSNLEN))
+							return -EFAULT;
+						p += ISDN_MSNLEN;
+					}
+					return (ISDN_MODEM_ANZREG + ISDN_MSNLEN) * ISDN_MAX_CHANNELS;
+				} else
+					return -EINVAL;
+				break;
+			case IIOCSETPRF:
+				/* Set all Modem-Profiles */
+				if (arg) {
+					char *p = (char *) arg;
+					int i;
+
+					if ((ret = verify_area(VERIFY_READ, (void *) arg,
+					(ISDN_MODEM_ANZREG + ISDN_MSNLEN)
+						   * ISDN_MAX_CHANNELS)))
+						return ret;
+
+					for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+						if ((ret = copy_from_user(dev->mdm.info[i].emu.profile, p,
+						      ISDN_MODEM_ANZREG)))
+							return ret;
+						p += ISDN_MODEM_ANZREG;
+						if ((ret = copy_from_user(dev->mdm.info[i].emu.pmsn, p, ISDN_MSNLEN)))
+							return ret;
+						p += ISDN_MSNLEN;
+					}
+					return 0;
+				} else
+					return -EINVAL;
+				break;
+			case IIOCSETMAP:
+			case IIOCGETMAP:
+				/* Set/Get MSN->EAZ-Mapping for a driver */
+				if (arg) {
+
+					if ((ret = copy_from_user((char *) &iocts,
+							    (char *) arg,
+					     sizeof(isdn_ioctl_struct))))
+						return ret;
+					if (strlen(iocts.drvid)) {
+						drvidx = -1;
+						for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+							if (!(strcmp(dev->drvid[i], iocts.drvid))) {
+								drvidx = i;
+								break;
+							}
+					} else
+						drvidx = 0;
+					if (drvidx == -1)
+						return -ENODEV;
+					if (cmd == IIOCSETMAP) {
+						int loop = 1;
+
+						p = (char *) iocts.arg;
+						i = 0;
+						while (loop) {
+							int j = 0;
+
+							while (1) {
+								if ((ret = verify_area(VERIFY_READ, p, 1)))
+									return ret;
+								GET_USER(bname[j], p++);
+								switch (bname[j]) {
+									case '\0':
+										loop = 0;
+										/* Fall through */
+									case ',':
+										bname[j] = '\0';
+										strcpy(dev->drv[drvidx]->msn2eaz[i], bname);
+										j = ISDN_MSNLEN;
+										break;
+									default:
+										j++;
+								}
+								if (j >= ISDN_MSNLEN)
+									break;
+							}
+							if (++i > 9)
+								break;
+						}
+					} else {
+						p = (char *) iocts.arg;
+						for (i = 0; i < 10; i++) {
+							sprintf(bname, "%s%s",
+								strlen(dev->drv[drvidx]->msn2eaz[i]) ?
+								dev->drv[drvidx]->msn2eaz[i] : "-",
+								(i < 9) ? "," : "\0");
+							if ((ret = copy_to_user(p, bname, strlen(bname) + 1)))
+								return ret;
+							p += strlen(bname);
+						}
+					}
+					return 0;
+				} else
+					return -EINVAL;
+			case IIOCDBGVAR:
+				if (arg) {
+					if ((ret = copy_to_user((char *) arg, (char *) &dev, sizeof(ulong))))
+						return ret;
+					return 0;
+				} else
+					return -EINVAL;
+				break;
+			default:
+				if ((cmd & IIOCDRVCTL) == IIOCDRVCTL)
+					cmd = ((cmd >> _IOC_NRSHIFT) & _IOC_NRMASK) & ISDN_DRVIOCTL_MASK;
+				else
+					return -EINVAL;
+				if (arg) {
+					int i;
+					char *p;
+					if ((ret = copy_from_user((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct))))
+						return ret;
+					if (strlen(iocts.drvid)) {
+						if ((p = strchr(iocts.drvid, ',')))
+							*p = 0;
+						drvidx = -1;
+						for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+							if (!(strcmp(dev->drvid[i], iocts.drvid))) {
+								drvidx = i;
+								break;
+							}
+					} else
+						drvidx = 0;
+					if (drvidx == -1)
+						return -ENODEV;
+					if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
+					     sizeof(isdn_ioctl_struct))))
+						return ret;
+					c.driver = drvidx;
+					c.command = ISDN_CMD_IOCTL;
+					c.arg = cmd;
+					memcpy(c.parm.num, (char *) &iocts.arg, sizeof(ulong));
+					ret = dev->drv[drvidx]->interface->command(&c);
+					memcpy((char *) &iocts.arg, c.parm.num, sizeof(ulong));
+					if ((copy_to_user((char *) arg, &iocts, sizeof(isdn_ioctl_struct))))
+						return -EFAULT;
+					return ret;
+				} else
+					return -EINVAL;
 		}
 	}
 #ifdef CONFIG_ISDN_PPP
@@ -1612,6 +1569,12 @@
 		return (isdn_ppp_ioctl(minor - ISDN_MINOR_PPP, file, cmd, arg));
 #endif
 	return -ENODEV;
+
+#undef name
+#undef bname
+#undef iocts
+#undef phone
+#undef cfg
 }
 
 /*
@@ -1619,7 +1582,8 @@
  * MOD_INC_USE_COUNT make sure that the driver memory is not freed
  * while the device is in use.
  */
-static int isdn_open(struct inode *ino, struct file *filep)
+static int
+isdn_open(struct inode *ino, struct file *filep)
 {
 	uint minor = MINOR(ino->i_rdev);
 	int drvidx;
@@ -1671,20 +1635,21 @@
 	if (minor <= ISDN_MINOR_PPPMAX) {
 		int ret;
 		if (!(ret = isdn_ppp_open(minor - ISDN_MINOR_PPP, filep)))
-		        MOD_INC_USE_COUNT;
+			MOD_INC_USE_COUNT;
 		return ret;
 	}
 #endif
 	return -ENODEV;
 }
 
-static void isdn_close(struct inode *ino, struct file *filep)
+static CLOSETYPE
+isdn_close(struct inode *ino, struct file *filep)
 {
 	uint minor = MINOR(ino->i_rdev);
 	int drvidx;
 	isdn_ctrl c;
 
-        MOD_DEC_USE_COUNT;
+	MOD_DEC_USE_COUNT;
 	if (minor == ISDN_MINOR_STATUS) {
 		infostruct *p = dev->infochain;
 		infostruct *q = NULL;
@@ -1694,38 +1659,40 @@
 					q->next = p->next;
 				else
 					dev->infochain = (infostruct *) (p->next);
-				return;
+				kfree(p);
+				return CLOSEVAL;
 			}
 			q = p;
 			p = (infostruct *) (p->next);
 		}
 		printk(KERN_WARNING "isdn: No private data while closing isdnctrl\n");
-                return;
+		return CLOSEVAL;
 	}
 	if (minor < ISDN_MINOR_CTRL) {
 		drvidx = isdn_minor2drv(minor);
 		if (drvidx < 0)
-			return;
+			return CLOSEVAL;
 		c.command = ISDN_CMD_UNLOCK;
 		c.driver = drvidx;
 		(void) dev->drv[drvidx]->interface->command(&c);
-		return;
+		return CLOSEVAL;
 	}
 	if (minor <= ISDN_MINOR_CTRLMAX) {
 		drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
 		if (drvidx < 0)
-			return;
+			return CLOSEVAL;
 		if (dev->profd == current)
 			dev->profd = NULL;
 		c.command = ISDN_CMD_UNLOCK;
 		c.driver = drvidx;
 		(void) dev->drv[drvidx]->interface->command(&c);
-		return;
+		return CLOSEVAL;
 	}
 #ifdef CONFIG_ISDN_PPP
 	if (minor <= ISDN_MINOR_PPPMAX)
 		isdn_ppp_release(minor - ISDN_MINOR_PPP, filep);
 #endif
+	return CLOSEVAL;
 }
 
 static struct file_operations isdn_fops =
@@ -1733,17 +1700,21 @@
 	isdn_lseek,
 	isdn_read,
 	isdn_write,
-	NULL,			/* isdn_readdir */
-	isdn_select,		/* isdn_select */
-	isdn_ioctl,		/* isdn_ioctl */
-	NULL,			/* isdn_mmap */
+	NULL,                   /* isdn_readdir */
+#if (LINUX_VERSION_CODE < 0x020117)
+	isdn_select,            /* isdn_select */
+#else
+	isdn_poll,              /* isdn_poll */
+#endif
+	isdn_ioctl,             /* isdn_ioctl */
+	NULL,                   /* isdn_mmap */
 	isdn_open,
 	isdn_close,
-	NULL			/* fsync */
+	NULL                    /* fsync */
 };
 
 char *
- isdn_map_eaz2msn(char *msn, int di)
+isdn_map_eaz2msn(char *msn, int di)
 {
 	driver *this = dev->drv[di];
 	int i;
@@ -1761,13 +1732,14 @@
  * Find an unused ISDN-channel, whose feature-flags match the
  * given L2- and L3-protocols.
  */
-int isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev
-		     ,int pre_chan)
+int
+isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev
+		      ,int pre_chan)
 {
 	int i;
 	ulong flags;
 	ulong features;
-        isdn_ctrl cmd;
+	isdn_ctrl cmd;
 
 	save_flags(flags);
 	cli();
@@ -1785,10 +1757,10 @@
 						dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
 						dev->usage[i] |= usage;
 						isdn_info_update();
-                                                cmd.driver = d;
-                                                cmd.arg = 0;
-                                                cmd.command = ISDN_CMD_LOCK;
-                                                (void) dev->drv[d]->interface->command(&cmd);
+						cmd.driver = d;
+						cmd.arg = 0;
+						cmd.command = ISDN_CMD_LOCK;
+						(void) dev->drv[d]->interface->command(&cmd);
 						restore_flags(flags);
 						return i;
 					} else {
@@ -1796,10 +1768,10 @@
 							dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
 							dev->usage[i] |= usage;
 							isdn_info_update();
-                                                        cmd.driver = d;
-                                                        cmd.arg = 0;
-                                                        cmd.command = ISDN_CMD_LOCK;
-                                                        (void) dev->drv[d]->interface->command(&cmd);
+							cmd.driver = d;
+							cmd.arg = 0;
+							cmd.command = ISDN_CMD_LOCK;
+							(void) dev->drv[d]->interface->command(&cmd);
 							restore_flags(flags);
 							return i;
 						}
@@ -1814,7 +1786,8 @@
 /*
  * Set state of ISDN-channel to 'unused'
  */
-void isdn_free_channel(int di, int ch, int usage)
+void
+isdn_free_channel(int di, int ch, int usage)
 {
 	int i;
 	ulong flags;
@@ -1828,15 +1801,15 @@
 		    (dev->chanmap[i] == ch)) {
 			dev->usage[i] &= (ISDN_USAGE_NONE | ISDN_USAGE_EXCLUSIVE);
 			strcpy(dev->num[i], "???");
-                        dev->ibytes[i] = 0;
-                        dev->obytes[i] = 0;
+			dev->ibytes[i] = 0;
+			dev->obytes[i] = 0;
 			isdn_info_update();
-                        isdn_free_queue(&dev->drv[di]->rpqueue[ch]);
-                        cmd.driver = di;
-                        cmd.arg = ch;
-                        cmd.command = ISDN_CMD_UNLOCK;
-                        restore_flags(flags);
-                        (void) dev->drv[di]->interface->command(&cmd);
+			isdn_free_queue(&dev->drv[di]->rpqueue[ch]);
+			cmd.driver = di;
+			cmd.arg = ch;
+			cmd.command = ISDN_CMD_UNLOCK;
+			restore_flags(flags);
+			(void) dev->drv[di]->interface->command(&cmd);
 			return;
 		}
 	restore_flags(flags);
@@ -1845,7 +1818,8 @@
 /*
  * Cancel Exclusive-Flag for ISDN-channel
  */
-void isdn_unexclusive_channel(int di, int ch)
+void
+isdn_unexclusive_channel(int di, int ch)
 {
 	int i;
 	ulong flags;
@@ -1873,55 +1847,57 @@
  * len     = Length of packet-data
  *
  */
-void isdn_receive_callback(int drvidx, int chan, u_char *buf, int len) 
+static void
+isdn_receive_callback(int drvidx, int chan, u_char * buf, int len)
 {
-        struct sk_buff *skb;
+	struct sk_buff *skb;
 
 	if (dev->global_flags & ISDN_GLOBAL_STOPPED)
 		return;
-        skb = dev_alloc_skb(len);
-        if (skb) {
-                memcpy(skb_put(skb, len), buf, len);
-                isdn_receive_skb_callback(drvidx, chan, skb);
-        } else
-                printk(KERN_WARNING "isdn: rcv alloc_skb failed, packet dropped.\n");
+	skb = dev_alloc_skb(len);
+	if (skb) {
+		memcpy(skb_put(skb, len), buf, len);
+		isdn_receive_skb_callback(drvidx, chan, skb);
+	} else
+		printk(KERN_WARNING "isdn: rcv alloc_skb failed, packet dropped.\n");
 }
 
 /*
  *  writebuf replacement for SKB_ABLE drivers
  */
-int isdn_writebuf_stub(int drvidx, int chan, const u_char *buf, int len, 
-		       int user)
+static int
+isdn_writebuf_stub(int drvidx, int chan, const u_char * buf, int len,
+		   int user)
 {
 	int ret;
 
-        if (dev->drv[drvidx]->interface->writebuf)
-                ret = dev->drv[drvidx]->interface->writebuf(drvidx, chan, buf,
-                                                            len, user);
-        else {
-                struct sk_buff * skb;
-
-                skb = alloc_skb(dev->drv[drvidx]->interface->hl_hdrlen + len,
-                                GFP_ATOMIC);
-                if (skb == NULL)
-                        return 0;
-
-                skb_reserve(skb, dev->drv[drvidx]->interface->hl_hdrlen);
-                skb->free = 1;
-
-                if (user)
-                        copy_from_user(skb_put(skb, len), buf, len);
-                else
-                        memcpy(skb_put(skb, len), buf, len);
-
-                ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx,
-                                                                chan, skb);
-                if (ret <= 0)
-                        kfree_skb(skb, FREE_WRITE);
-        }
-        if (ret > 0)
-                dev->obytes[isdn_dc2minor(drvidx,chan)] += ret;
-        return ret;
+	if (dev->drv[drvidx]->interface->writebuf)
+		ret = dev->drv[drvidx]->interface->writebuf(drvidx, chan, buf,
+							    len, user);
+	else {
+		struct sk_buff *skb;
+
+		skb = alloc_skb(dev->drv[drvidx]->interface->hl_hdrlen + len,
+				GFP_ATOMIC);
+		if (skb == NULL)
+			return 0;
+
+		skb_reserve(skb, dev->drv[drvidx]->interface->hl_hdrlen);
+		SET_SKB_FREE(skb);
+
+		if (user)
+			copy_from_user(skb_put(skb, len), buf, len);
+		else
+			memcpy(skb_put(skb, len), buf, len);
+
+		ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx,
+							      chan, skb);
+		if (ret <= 0)
+			kfree_skb(skb, FREE_WRITE);
+	}
+	if (ret > 0)
+		dev->obytes[isdn_dc2minor(drvidx, chan)] += ret;
+	return ret;
 }
 
 /*
@@ -1932,32 +1908,36 @@
  * Return: length of data on success, -ERRcode on failure.
  */
 
-int isdn_writebuf_skb_stub(int drvidx, int chan, struct sk_buff * skb)
+int
+isdn_writebuf_skb_stub(int drvidx, int chan, struct sk_buff *skb)
 {
-        int ret;
-	int len = skb->len;	/* skb pointer no longer valid after free */
+	int ret;
+	int len = skb->len;     /* skb pointer no longer valid after free */
 
-        if (dev->drv[drvidx]->interface->writebuf_skb) 
+	if (dev->drv[drvidx]->interface->writebuf_skb)
 		ret = dev->drv[drvidx]->interface->
-			writebuf_skb(drvidx, chan, skb);
+		    writebuf_skb(drvidx, chan, skb);
 	else {
 		if ((ret = dev->drv[drvidx]->interface->
-                     writebuf(drvidx,chan,skb->data,skb->len,0)) == len)
-		        dev_kfree_skb(skb, FREE_WRITE);
-        }
-        if (ret > 0)
-                dev->obytes[isdn_dc2minor(drvidx,chan)] += len;
-        return ret;
+		  writebuf(drvidx, chan, skb->data, skb->len, 0)) == len)
+			dev_kfree_skb(skb, FREE_WRITE);
+	}
+	if (ret > 0)
+		dev->obytes[isdn_dc2minor(drvidx, chan)] += len;
+	return ret;
 }
 
 /*
  * Low-level-driver registration
  */
 
-int register_isdn(isdn_if * i)
+int
+register_isdn(isdn_if * i)
 {
 	driver *d;
-	int n, j, k;
+	int n,
+	 j,
+	 k;
 	ulong flags;
 	int drvidx;
 
@@ -1973,9 +1953,9 @@
 		return 0;
 	}
 	if ((!i->writebuf_skb) && (!i->writebuf)) {
-                printk(KERN_WARNING "register_isdn: No write routine given.\n");
-                return 0;
-        }
+		printk(KERN_WARNING "register_isdn: No write routine given.\n");
+		return 0;
+	}
 	if (!(d = (driver *) kmalloc(sizeof(driver), GFP_KERNEL))) {
 		printk(KERN_WARNING "register_isdn: Could not alloc driver-struct\n");
 		return 0;
@@ -1994,17 +1974,17 @@
 		return 0;
 	}
 	memset((char *) d->rcvcount, 0, sizeof(int) * n);
-        if (!(d->rpqueue =
-              (struct sk_buff_head *) kmalloc(sizeof(struct sk_buff_head) * n, GFP_KERNEL))) {
-                printk(KERN_WARNING "register_isdn: Could not alloc rpqueue\n");
-                kfree(d->rcvcount);
-                kfree(d->rcverr);
-                kfree(d);
-                return 0;
-        }
-        for (j = 0; j < n; j++) {
-                skb_queue_head_init(&d->rpqueue[j]);
-        }
+	if (!(d->rpqueue =
+	      (struct sk_buff_head *) kmalloc(sizeof(struct sk_buff_head) * n, GFP_KERNEL))) {
+		printk(KERN_WARNING "register_isdn: Could not alloc rpqueue\n");
+		kfree(d->rcvcount);
+		kfree(d->rcverr);
+		kfree(d);
+		return 0;
+	}
+	for (j = 0; j < n; j++) {
+		skb_queue_head_init(&d->rpqueue[j]);
+	}
 	if (!(d->rcv_waitq = (struct wait_queue **)
 	      kmalloc(sizeof(struct wait_queue *) * n, GFP_KERNEL))) {
 		printk(KERN_WARNING "register_isdn: Could not alloc rcv_waitq\n");
@@ -2038,17 +2018,17 @@
 		if (!dev->drv[drvidx])
 			break;
 	i->channels = drvidx;
- 
+
 	i->rcvcallb_skb = isdn_receive_skb_callback;
-	i->rcvcallb     = isdn_receive_callback;
-	i->statcallb    = isdn_status_callback;
+	i->rcvcallb = isdn_receive_callback;
+	i->statcallb = isdn_status_callback;
 	if (!strlen(i->id))
 		sprintf(i->id, "line%d", drvidx);
 	save_flags(flags);
 	cli();
-        for (j = 0; j < drvidx; j++)
-                if (!strcmp(i->id,dev->drvid[j]))
-                    sprintf(i->id, "line%d", drvidx);                    
+	for (j = 0; j < drvidx; j++)
+		if (!strcmp(i->id, dev->drvid[j]))
+			sprintf(i->id, "line%d", drvidx);
 	for (j = 0; j < n; j++)
 		for (k = 0; k < ISDN_MAX_CHANNELS; k++)
 			if (dev->chanmap[k] < 0) {
@@ -2077,7 +2057,8 @@
 #define isdn_init init_module
 #endif
 
-static char *isdn_getrev(const char *revision)
+static char *
+isdn_getrev(const char *revision)
 {
 	char *rev;
 	char *p;
@@ -2091,29 +2072,18 @@
 	return rev;
 }
 
-static struct symbol_table isdn_syms = {
-#include <linux/symtab_begin.h>
-        X(register_isdn),
-#include <linux/symtab_end.h>
-};
-
-static void isdn_export_syms(void)
-{
-        register_symtab(&isdn_syms);
-        has_exported = 1;
-}
-
 /*
  * Allocate and initialize all data, register modem-devices
  */
-int isdn_init(void)
+int
+isdn_init(void)
 {
 	int i;
-        char irev[50];
-        char trev[50];
-        char nrev[50];
-        char prev[50];
-        char arev[50];
+	char irev[50];
+	char trev[50];
+	char nrev[50];
+	char prev[50];
+	char arev[50];
 
 	sti();
 	if (!(dev = (isdn_dev *) kmalloc(sizeof(isdn_dev), GFP_KERNEL))) {
@@ -2121,10 +2091,8 @@
 		return -EIO;
 	}
 	memset((char *) dev, 0, sizeof(isdn_dev));
-#ifdef NEW_ISDN_TIMER_CTRL
-        init_timer(&dev->timer);
-        dev->timer.function = isdn_timer_funct;
-#endif
+	init_timer(&dev->timer);
+	dev->timer.function = isdn_timer_funct;
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
 		dev->drvmap[i] = -1;
 		dev->chanmap[i] = -1;
@@ -2157,21 +2125,20 @@
 		kfree(dev);
 		return -EIO;
 	}
-#endif				/* CONFIG_ISDN_PPP */
+#endif                          /* CONFIG_ISDN_PPP */
 
-        if (!has_exported)
-                isdn_export_syms();
+	isdn_export_syms();
 
-        strcpy(irev,isdn_revision);
-        strcpy(trev,isdn_tty_revision);
-        strcpy(nrev,isdn_net_revision);
-        strcpy(prev,isdn_ppp_revision);
-        strcpy(arev,isdn_audio_revision);
+	strcpy(irev, isdn_revision);
+	strcpy(trev, isdn_tty_revision);
+	strcpy(nrev, isdn_net_revision);
+	strcpy(prev, isdn_ppp_revision);
+	strcpy(arev, isdn_audio_revision);
 	printk(KERN_NOTICE "ISDN subsystem Rev: %s/", isdn_getrev(irev));
-        printk("%s/", isdn_getrev(trev));
-        printk("%s/", isdn_getrev(nrev));
-        printk("%s/", isdn_getrev(prev));
-        printk("%s", isdn_getrev(arev));
+	printk("%s/", isdn_getrev(trev));
+	printk("%s/", isdn_getrev(nrev));
+	printk("%s/", isdn_getrev(prev));
+	printk("%s", isdn_getrev(arev));
 
 #ifdef MODULE
 	printk(" loaded\n");
@@ -2187,7 +2154,8 @@
 /*
  * Unload module
  */
-void cleanup_module(void)
+void
+cleanup_module(void)
 {
 	int flags;
 	int i;
@@ -2213,9 +2181,9 @@
 		return;
 	}
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-                isdn_tty_cleanup_xmit(&dev->mdm.info[i]);
-                kfree(dev->mdm.info[i].xmit_buf - 4);
-        }
+		isdn_tty_cleanup_xmit(&dev->mdm.info[i]);
+		kfree(dev->mdm.info[i].xmit_buf - 4);
+	}
 	if (unregister_chrdev(ISDN_MAJOR, "isdn") != 0) {
 		printk(KERN_WARNING "isdn: controldevice busy, remove cancelled\n");
 	} else {
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_common.h linux/drivers/isdn/isdn_common.h
--- v2.0.30/linux/drivers/isdn/isdn_common.h	Sat Jun  1 01:56:51 1996
+++ linux/drivers/isdn/isdn_common.h	Mon Aug  4 17:34:00 1997
@@ -1,11 +1,11 @@
-/* $Id: isdn_common.h,v 1.3 1996/05/19 00:13:05 fritz Exp $
- *
+/* $Id: isdn_common.h,v 1.6 1997/02/28 02:32:44 fritz Exp $
+
  * header for Linux ISDN subsystem, common used functions and debugging-switches (linklevel).
  *
  * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
  * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
  * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
- * 
+ *
  * 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)
@@ -18,9 +18,21 @@
  *
  * 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. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_common.h,v $
+ * Revision 1.6  1997/02/28 02:32:44  fritz
+ * Cleanup: Moved some tty related stuff from isdn_common.c
+ *          to isdn_tty.c
+ * Bugfix:  Bisync protocol did not behave like documented.
+ *
+ * Revision 1.5  1997/02/10 10:05:45  fritz
+ * More changes for Kernel 2.1.X
+ * Symbol information moved to isdn_syms.c
+ *
+ * Revision 1.4  1997/02/03 22:56:50  fritz
+ * Removed isdn_writebuf_stub prototype.
+ *
  * Revision 1.3  1996/05/19 00:13:05  fritz
  * Removed debug flag.
  *
@@ -38,26 +50,32 @@
 #undef  ISDN_DEBUG_MODEM_HUP
 #undef  ISDN_DEBUG_MODEM_ICALL
 #undef  ISDN_DEBUG_MODEM_DUMP
+#undef  ISDN_DEBUG_MODEM_VOICE
 #undef  ISDN_DEBUG_AT
 #undef  ISDN_DEBUG_NET_DUMP
 #undef  ISDN_DEBUG_NET_DIAL
 #undef  ISDN_DEBUG_NET_ICALL
 
 /* Prototypes */
-extern void  isdn_MOD_INC_USE_COUNT(void);
-extern void  isdn_MOD_DEC_USE_COUNT(void);
-extern void  isdn_free_channel(int di, int ch, int usage);
-extern void  isdn_all_eaz(int di, int ch);
-extern int   isdn_dc2minor(int di, int ch);
-extern void  isdn_info_update(void);
-extern char* isdn_map_eaz2msn(char *msn, int di);
-extern void  isdn_timer_ctrl(int tf, int onoff);
-extern void  isdn_unexclusive_channel(int di, int ch);
-extern int   isdn_getnum(char **);
-extern int   isdn_readbchan (int, int, u_char *, u_char *, int, int);
-extern int   isdn_get_free_channel(int, int, int, int, int);
-extern int   isdn_writebuf_stub(int, int, const u_char *, int, int);
-extern int   isdn_writebuf_skb_stub(int, int, struct sk_buff *);
+extern void isdn_MOD_INC_USE_COUNT(void);
+extern void isdn_MOD_DEC_USE_COUNT(void);
+extern void isdn_free_channel(int di, int ch, int usage);
+extern void isdn_all_eaz(int di, int ch);
+extern int isdn_dc2minor(int di, int ch);
+extern void isdn_info_update(void);
+extern char *isdn_map_eaz2msn(char *msn, int di);
+extern void isdn_timer_ctrl(int tf, int onoff);
+extern void isdn_unexclusive_channel(int di, int ch);
+extern int isdn_getnum(char **);
+extern int isdn_readbchan(int, int, u_char *, u_char *, int, int);
+extern int isdn_get_free_channel(int, int, int, int, int);
+extern int isdn_writebuf_skb_stub(int, int, struct sk_buff *);
+extern int register_isdn(isdn_if * i);
+#if (LINUX_VERSION_CODE < 0x020111)
+extern void isdn_export_syms(void);
+#else
+#define isdn_export_syms()
+#endif
 #if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
-extern void  isdn_dumppkt(char *, u_char *, int, int);
+extern void isdn_dumppkt(char *, u_char *, int, int);
 #endif
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_net.c linux/drivers/isdn/isdn_net.c
--- v2.0.30/linux/drivers/isdn/isdn_net.c	Tue Nov 12 22:36:19 1996
+++ linux/drivers/isdn/isdn_net.c	Mon Aug  4 17:34:00 1997
@@ -1,11 +1,11 @@
-/* $Id: isdn_net.c,v 1.29 1996/11/13 02:31:38 fritz Exp $
- *
+/* $Id: isdn_net.c,v 1.47 1997/06/21 10:52:05 fritz Exp $
+
  * Linux ISDN subsystem, network interfaces and related functions (linklevel).
  *
  * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
  * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
  * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
- * 
+ *
  * 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)
@@ -18,9 +18,73 @@
  *
  * 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. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_net.c,v $
+ * Revision 1.47  1997/06/21 10:52:05  fritz
+ * Removed wrong SET_SKB_FREE in isdn_net_send_skb()
+ *
+ * Revision 1.46  1997/06/17 13:05:24  hipp
+ * Applied Eric's underflow-patches (slightly modified)
+ *
+ * Revision 1.45  1997/06/10 16:24:22  hipp
+ * hard_header changes for syncPPP (now behaves like RAWIP)
+ *
+ * Revision 1.44  1997/05/27 15:17:26  fritz
+ * Added changes for recent 2.1.x kernels:
+ *   changed return type of isdn_close
+ *   queue_task_* -> queue_task
+ *   clear/set_bit -> test_and_... where apropriate.
+ *   changed type of hard_header_cache parameter.
+ *
+ * Revision 1.43  1997/03/30 16:51:13  calle
+ * changed calls to copy_from_user/copy_to_user and removed verify_area
+ * were possible.
+ *
+ * Revision 1.42  1997/03/11 08:43:51  fritz
+ * Perform a hangup if number is deleted while dialing.
+ *
+ * Revision 1.41  1997/03/08 08:16:31  fritz
+ * Bugfix: Deleting a phone number during dial gave unpredictable results.
+ *
+ * Revision 1.40  1997/03/05 21:16:08  fritz
+ * Fix: did not compile with 2.1.27
+ *
+ * Revision 1.39  1997/03/04 21:36:52  fritz
+ * Added sending ICMP messages when no connetion is possible.
+ *
+ * Revision 1.38  1997/02/23 23:41:14  fritz
+ * Bugfix: Slave interfaces have to be hung up before master.
+ *
+ * Revision 1.37  1997/02/11 18:32:51  fritz
+ * Bugfix in isdn_ppp_free_mpqueue().
+ *
+ * Revision 1.36  1997/02/10 21:31:11  fritz
+ * Changed setup-interface (incoming and outgoing).
+ *
+ * Revision 1.35  1997/02/10 20:12:45  fritz
+ * Changed interface for reporting incoming calls.
+ *
+ * Revision 1.34  1997/02/03 23:15:07  fritz
+ * Reformatted according CodingStyle.
+ * replaced arp_find prototype by proper include.
+ * made dev_purge_queues static.
+ * Bugfix in bogocps calculation.
+ * removed isdn_net_receive_callback - was never used ;-)
+ * Misc. fixes for Kernel 2.1.X comaptibility.
+ *
+ * Revision 1.33  1997/01/17 01:19:25  fritz
+ * Applied chargeint patch.
+ *
+ * Revision 1.32  1997/01/14 01:29:31  fritz
+ * Bugfix: isdn_net_hangup() did not reset ISDN_NET_CONNECTED.
+ *
+ * Revision 1.31  1997/01/11 23:30:42  fritz
+ * Speed up dial statemachine.
+ *
+ * Revision 1.30  1996/11/25 17:20:50  hipp
+ * fixed pppbind bug in isdn_net_find_icall()
+ *
  * Revision 1.29  1996/11/13 02:31:38  fritz
  * Minor cleanup.
  *
@@ -140,38 +204,47 @@
 #include <linux/module.h>
 #include <linux/isdn.h>
 #include <linux/if_arp.h>
+#include <net/arp.h>
+#include <net/icmp.h>
 #include "isdn_common.h"
 #include "isdn_net.h"
 #ifdef CONFIG_ISDN_PPP
 #include "isdn_ppp.h"
 #endif
 
-/* In ksyms.c, but why not in some .h ??? */
-extern int arp_find(unsigned char *, u32, struct device *, u32,
-                    struct sk_buff *);
-
 /* Prototypes */
 
 int isdn_net_force_dial_lp(isdn_net_local *);
 static int isdn_net_wildmat(char *s, char *p);
 static int isdn_net_start_xmit(struct sk_buff *, struct device *);
 static int isdn_net_xmit(struct device *, isdn_net_local *, struct sk_buff *);
- 
-extern void dev_purge_queues(struct device *dev);	/* move this to net/core/dev.c */
+static void dev_purge_queues(struct device *dev);	/* move this to net/core/dev.c */
 
-char *isdn_net_revision = "$Revision: 1.29 $";
+char *isdn_net_revision = "$Revision: 1.47 $";
 
  /*
   * Code for raw-networking over ISDN
   */
 
 static void
+isdn_net_unreachable(struct device *dev, struct sk_buff *skb, char *reason)
+{
+	printk(KERN_DEBUG "isdn_net: %s: %s, send ICMP\n",
+	       dev->name, reason);
+	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0
+#if (LINUX_VERSION_CODE < 0x02010f)	/* 2.1.15 */
+		  ,dev
+#endif
+	    );
+}
+
+static void
 isdn_net_reset(struct device *dev)
 {
 	ulong flags;
 
 	save_flags(flags);
-	cli();			/* Avoid glitch on writes to CMD regs */
+	cli();                  /* Avoid glitch on writes to CMD regs */
 	dev->interrupt = 0;
 	dev->tbusy = 0;
 	restore_flags(flags);
@@ -191,7 +264,7 @@
 		dev->dev_addr[i] = 0xfc;
 	memcpy(&(dev->dev_addr[i]), &dev->pa_addr, sizeof(u32));
 
-        /* If this interface has slaves, start them also */
+	/* If this interface has slaves, start them also */
 
 	if ((p = (((isdn_net_local *) dev->priv)->slave))) {
 		while (p) {
@@ -200,7 +273,6 @@
 			p = (((isdn_net_local *) p->priv)->slave);
 		}
 	}
-
 	isdn_MOD_INC_USE_COUNT();
 	return 0;
 }
@@ -211,15 +283,15 @@
 static void
 isdn_net_bind_channel(isdn_net_local * lp, int idx)
 {
-        ulong flags;
+	ulong flags;
 
-        save_flags(flags);
-        cli();
+	save_flags(flags);
+	cli();
 	lp->isdn_device = dev->drvmap[idx];
 	lp->isdn_channel = dev->chanmap[idx];
-        dev->rx_netdev[idx] = lp->netdev;
-        dev->st_netdev[idx] = lp->netdev;
-        restore_flags(flags);
+	dev->rx_netdev[idx] = lp->netdev;
+	dev->st_netdev[idx] = lp->netdev;
+	restore_flags(flags);
 }
 
 /*
@@ -232,25 +304,25 @@
 
 	save_flags(flags);
 	cli();
-        if (lp->first_skb) {
-                dev_kfree_skb(lp->first_skb,FREE_WRITE);
-                lp->first_skb = NULL;
-        }
-	if(lp->sav_skb) {
-                dev_kfree_skb(lp->sav_skb,FREE_WRITE);
+	if (lp->first_skb) {
+		dev_kfree_skb(lp->first_skb, FREE_WRITE);
+		lp->first_skb = NULL;
+	}
+	if (lp->sav_skb) {
+		dev_kfree_skb(lp->sav_skb, FREE_WRITE);
 		lp->sav_skb = NULL;
 	}
-	if(!lp->master) /* purge only for master device */
+	if (!lp->master)        /* purge only for master device */
 		dev_purge_queues(&lp->netdev->dev);
 	lp->dialstate = 0;
-	dev->rx_netdev[isdn_dc2minor(lp->isdn_device,lp->isdn_channel)] = NULL;
-	dev->st_netdev[isdn_dc2minor(lp->isdn_device,lp->isdn_channel)] = NULL;
+	dev->rx_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL;
+	dev->st_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL;
 	isdn_free_channel(lp->isdn_device, lp->isdn_channel, ISDN_USAGE_NET);
 	lp->flags &= ~ISDN_NET_CONNECTED;
 	lp->isdn_device = -1;
 	lp->isdn_channel = -1;
 
-        restore_flags(flags);
+	restore_flags(flags);
 }
 
 /*
@@ -272,37 +344,50 @@
 isdn_net_autohup()
 {
 	isdn_net_dev *p = dev->netdev;
-        int anymore;
+	int anymore;
 
-        anymore = 0;
+	anymore = 0;
 	while (p) {
 		isdn_net_local *l = (isdn_net_local *) & (p->local);
 		if ((jiffies - last_jiffies) == 0)
-			l->cps = 0;
+			l->cps = l->transcount;
 		else
-			l->cps = l->transcount / (jiffies - last_jiffies);
+			l->cps = (l->transcount * HZ) / (jiffies - last_jiffies);
 		l->transcount = 0;
 		if (dev->net_verbose > 3)
 			printk(KERN_DEBUG "%s: %d bogocps\n", l->name, l->cps);
 		if ((l->flags & ISDN_NET_CONNECTED) && (!l->dialstate)) {
-                        anymore = 1;
+			anymore = 1;
 			l->huptimer++;
 			if ((l->onhtime) && (l->huptimer > l->onhtime))
-				if (l->outgoing) {
-					if (l->hupflags & 4) {
-						if (l->hupflags & 1)
+				if (l->hupflags & ISDN_MANCHARGE &&
+				    l->hupflags & ISDN_CHARGEHUP) {
+					while (jiffies - l->chargetime > l->chargeint)
+						l->chargetime += l->chargeint;
+					if (jiffies - l->chargetime >= l->chargeint - 2 * HZ)
+						if (l->outgoing || l->hupflags & ISDN_INHUP)
 							isdn_net_hangup(&p->dev);
-						else if (jiffies - l->chargetime > l->chargeint)
+				} else if (l->outgoing) {
+					if (l->hupflags & ISDN_CHARGEHUP) {
+						if (l->hupflags & ISDN_WAITCHARGE) {
+							printk(KERN_DEBUG "isdn_net: Hupflags of %s are %X\n",
+							       l->name, l->hupflags);
 							isdn_net_hangup(&p->dev);
+						} else if (jiffies - l->chargetime > l->chargeint) {
+							printk(KERN_DEBUG
+							       "isdn_net: %s: chtime = %d, chint = %d\n",
+							       l->name, l->chargetime, l->chargeint);
+							isdn_net_hangup(&p->dev);
+						}
 					} else
 						isdn_net_hangup(&p->dev);
-				} else if (l->hupflags & 8)
+				} else if (l->hupflags & ISDN_INHUP)
 					isdn_net_hangup(&p->dev);
 		}
 		p = (isdn_net_dev *) p->next;
 	}
 	last_jiffies = jiffies;
-        isdn_timer_ctrl(ISDN_TIMER_NETHANGUP,anymore);
+	isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, anymore);
 }
 
 /*
@@ -318,57 +403,56 @@
 
 	if (p) {
 		isdn_net_local *lp = &(p->local);
-                switch (cmd) {
+		switch (cmd) {
 			case ISDN_STAT_BSENT:
 				/* A packet has successfully been sent out */
 				if ((lp->flags & ISDN_NET_CONNECTED) &&
 				    (!lp->dialstate)) {
 					lp->stats.tx_packets++;
-					if(lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && lp->sav_skb) {
+					if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && lp->sav_skb) {
 						struct device *mdev;
-						if(lp->master)
+						if (lp->master)
 							mdev = lp->master;
 						else
 							mdev = &lp->netdev->dev;
-						if(!isdn_net_send_skb(mdev,lp,lp->sav_skb)) {
+						if (!isdn_net_send_skb(mdev, lp, lp->sav_skb)) {
 							lp->sav_skb = NULL;
 							mark_bh(NET_BH);
-						}
-						else {
+						} else {
 							return 1;
 						}
 					}
-                                        if (clear_bit(0,(void*)&(p->dev.tbusy)))
-                                                mark_bh(NET_BH);
+					if (test_and_clear_bit(0, (void *) &(p->dev.tbusy)))
+						mark_bh(NET_BH);
 				}
 				return 1;
 			case ISDN_STAT_DCONN:
 				/* D-Channel is up */
-                                switch (lp->dialstate) {
-                                        case 4:
-                                        case 7:
-                                        case 8:
-                                                lp->dialstate++;
-                                                return 1;
-                                        case 12:
-                                                lp->dialstate = 5;
-                                                return 1;
-                                }
+				switch (lp->dialstate) {
+					case 4:
+					case 7:
+					case 8:
+						lp->dialstate++;
+						return 1;
+					case 12:
+						lp->dialstate = 5;
+						return 1;
+				}
 				break;
 			case ISDN_STAT_DHUP:
 				/* Either D-Channel-hangup or error during dialout */
 				if ((!lp->dialstate) && (lp->flags & ISDN_NET_CONNECTED)) {
 					lp->flags &= ~ISDN_NET_CONNECTED;
-					if(lp->first_skb) {
-						dev_kfree_skb(lp->first_skb,FREE_WRITE);
+					if (lp->first_skb) {
+						dev_kfree_skb(lp->first_skb, FREE_WRITE);
 						lp->first_skb = NULL;
 					}
-					if(lp->sav_skb) {
-						dev_kfree_skb(lp->sav_skb,FREE_WRITE);
+					if (lp->sav_skb) {
+						dev_kfree_skb(lp->sav_skb, FREE_WRITE);
 						lp->sav_skb = NULL;
 					}
 					isdn_free_channel(lp->isdn_device, lp->isdn_channel,
-                                                          ISDN_USAGE_NET);
+							  ISDN_USAGE_NET);
 #ifdef CONFIG_ISDN_PPP
 					isdn_ppp_free(lp);
 #endif
@@ -380,49 +464,51 @@
 					lp->isdn_channel = -1;
 					dev->st_netdev[idx] = NULL;
 					dev->rx_netdev[idx] = NULL;
-                                        return 1;
+					return 1;
 				}
-                                break;
+				break;
 			case ISDN_STAT_BCONN:
 				/* B-Channel is up */
-                                switch (lp->dialstate) {
-                                        case 5:
-                                        case 6:
-                                        case 7:
-                                        case 8:
-                                        case 9:
-                                        case 10:
-                                        case 12:
-                                                if (lp->dialstate <= 6) {
-                                                        dev->usage[idx] |= ISDN_USAGE_OUTGOING;
-                                                        isdn_info_update();
-                                                } else
-                                                        dev->rx_netdev[idx] = p;
-                                                lp->dialstate = 0;
-                                                isdn_timer_ctrl(ISDN_TIMER_NETHANGUP,1);
-                                                printk(KERN_INFO "isdn_net: %s connected\n", lp->name);
-                                                /* If first Chargeinfo comes before B-Channel connect,
-                                                 * we correct the timestamp here.
-                                                 */
-                                                lp->chargetime = jiffies;
-                                                /* Immediately send first skb to speed up arp */
+				switch (lp->dialstate) {
+					case 5:
+					case 6:
+					case 7:
+					case 8:
+					case 9:
+					case 10:
+					case 12:
+						if (lp->dialstate <= 6) {
+							dev->usage[idx] |= ISDN_USAGE_OUTGOING;
+							isdn_info_update();
+						} else
+							dev->rx_netdev[idx] = p;
+						lp->dialstate = 0;
+						isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 1);
+						printk(KERN_INFO "isdn_net: %s connected\n", lp->name);
+						/* If first Chargeinfo comes before B-Channel connect,
+						 * we correct the timestamp here.
+						 */
+						lp->chargetime = jiffies;
+						printk(KERN_DEBUG "isdn_net: chargetime of %s now %d\n",
+						lp->name, lp->chargetime);
+						/* Immediately send first skb to speed up arp */
 #ifdef CONFIG_ISDN_PPP
-						if(lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+						if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
 							isdn_ppp_wakeup_daemon(lp);
 #endif
-                                                if (lp->first_skb) {
-                                                        if (!(isdn_net_xmit(&p->dev,lp,lp->first_skb)))
-                                                                lp->first_skb = NULL;
-                                                }
-                                                return 1;
+						if (lp->first_skb) {
+							if (!(isdn_net_xmit(&p->dev, lp, lp->first_skb)))
+								lp->first_skb = NULL;
+						}
+						return 1;
 				}
 				break;
 			case ISDN_STAT_NODCH:
 				/* No D-Channel avail. */
 				if (lp->dialstate == 4) {
 					lp->dialstate--;
-                                        return 1;
-                                }
+					return 1;
+				}
 				break;
 			case ISDN_STAT_CINF:
 				/* Charge-info from TelCo. Calculate interval between
@@ -430,17 +516,19 @@
 				 * usage by isdn_net_autohup()
 				 */
 				lp->charge++;
-				if (lp->hupflags & 2) {
-					lp->hupflags &= ~1;
+				if (lp->hupflags & ISDN_HAVECHARGE) {
+					lp->hupflags &= ~ISDN_WAITCHARGE;
 					lp->chargeint = jiffies - lp->chargetime - (2 * HZ);
 				}
-				if (lp->hupflags & 1)
-					lp->hupflags |= 2;
+				if (lp->hupflags & ISDN_WAITCHARGE)
+					lp->hupflags |= ISDN_HAVECHARGE;
 				lp->chargetime = jiffies;
+				printk(KERN_DEBUG "isdn_net: Got CINF chargetime of %s now %d\n",
+				       lp->name, lp->chargetime);
 				return 1;
-                }
+		}
 	}
-        return 0;
+	return 0;
 }
 
 /*
@@ -473,191 +561,220 @@
 	isdn_net_dev *p = dev->netdev;
 	int anymore = 0;
 	int i;
+	int flags;
 	isdn_ctrl cmd;
 
 	while (p) {
 #ifdef ISDN_DEBUG_NET_DIAL
-                if (p->local.dialstate)
-                        printk(KERN_DEBUG "%s: dialstate=%d\n", p->local.name,p->local.dialstate);
+		if (p->local.dialstate)
+			printk(KERN_DEBUG "%s: dialstate=%d\n", p->local.name, p->local.dialstate);
 #endif
 		switch (p->local.dialstate) {
-		case 0:
-			/* Nothing to do for this interface */
-			break;
-		case 1:
-			/* Initiate dialout. Set phone-number-pointer to first number
-			 * of interface.
-			 */
-			p->local.dial = p->local.phone[1];
-			anymore = 1;
-			p->local.dialstate++;
-			break;
-			/* Prepare dialing. Clear EAZ, then set EAZ. */
-		case 2:
-			cmd.driver = p->local.isdn_device;
-			cmd.arg = p->local.isdn_channel;
-			cmd.command = ISDN_CMD_CLREAZ;
-			dev->drv[p->local.isdn_device]->interface->command(&cmd);
-			sprintf(cmd.num, "%s", isdn_map_eaz2msn(p->local.msn, cmd.driver));
-			cmd.command = ISDN_CMD_SETEAZ;
-			dev->drv[p->local.isdn_device]->interface->command(&cmd);
-			p->local.dialretry = 0;
-			anymore = 1;
-			p->local.dialstate++;
-			break;
-		case 3:
-			/* Setup interface, dial current phone-number, switch to next number.
-			 * If list of phone-numbers is exhausted, increment
-			 * retry-counter.
-			 */
-			cmd.driver = p->local.isdn_device;
-			cmd.command = ISDN_CMD_SETL2;
-			cmd.arg = p->local.isdn_channel + (p->local.l2_proto << 8);
-			dev->drv[p->local.isdn_device]->interface->command(&cmd);
-			cmd.driver = p->local.isdn_device;
-			cmd.command = ISDN_CMD_SETL3;
-			cmd.arg = p->local.isdn_channel + (p->local.l3_proto << 8);
-			dev->drv[p->local.isdn_device]->interface->command(&cmd);
-			cmd.driver = p->local.isdn_device;
-			cmd.arg = p->local.isdn_channel;
-			p->local.huptimer = 0;
-			p->local.outgoing = 1;
-			p->local.hupflags |= 1;
-                        p->local.hupflags &= ~2;
-			if (!strcmp(p->local.dial->num, "LEASED")) {
-				p->local.dialstate = 4;
-				printk(KERN_INFO "%s: Open leased line ...\n", p->local.name);
-			} else {
-				cmd.command = ISDN_CMD_DIAL;
-				sprintf(cmd.num, "%s,%s,7,0", p->local.dial->num,
-				  isdn_map_eaz2msn(p->local.msn, cmd.driver));
-				i = isdn_dc2minor(p->local.isdn_device, p->local.isdn_channel);
-				if (i >= 0) {
-					strcpy(dev->num[i], p->local.dial->num);
-					isdn_info_update();
+			case 0:
+				/* Nothing to do for this interface */
+				break;
+			case 1:
+				/* Initiate dialout. Set phone-number-pointer to first number
+				 * of interface.
+				 */
+				save_flags(flags);
+				cli();
+				p->local.dial = p->local.phone[1];
+				restore_flags(flags);
+				if (!p->local.dial) {
+					printk(KERN_WARNING "%s: phone number deleted?\n",
+					       p->local.name);
+					isdn_net_hangup(&p->dev);
+					break;
 				}
-				printk(KERN_INFO "%s: dialing %d %s...\n", p->local.name,
-				 p->local.dialretry, p->local.dial->num);
-				/*
-				 * Switch to next number or back to start if at end of list.
+				anymore = 1;
+				p->local.dialstate++;
+				/* Fall through */
+			case 2:
+				/* Prepare dialing. Clear EAZ, then set EAZ. */
+				cmd.driver = p->local.isdn_device;
+				cmd.arg = p->local.isdn_channel;
+				cmd.command = ISDN_CMD_CLREAZ;
+				dev->drv[p->local.isdn_device]->interface->command(&cmd);
+				sprintf(cmd.parm.num, "%s", isdn_map_eaz2msn(p->local.msn, cmd.driver));
+				cmd.command = ISDN_CMD_SETEAZ;
+				dev->drv[p->local.isdn_device]->interface->command(&cmd);
+				p->local.dialretry = 0;
+				anymore = 1;
+				p->local.dialstate++;
+				/* Falls through */
+			case 3:
+				/* Setup interface, dial current phone-number, switch to next number.
+				 * If list of phone-numbers is exhausted, increment
+				 * retry-counter.
 				 */
-				if (!(p->local.dial = (isdn_net_phone *) p->local.dial->next)) {
-					p->local.dial = p->local.phone[1];
-					p->local.dialretry++;
+				cmd.driver = p->local.isdn_device;
+				cmd.command = ISDN_CMD_SETL2;
+				cmd.arg = p->local.isdn_channel + (p->local.l2_proto << 8);
+				dev->drv[p->local.isdn_device]->interface->command(&cmd);
+				cmd.driver = p->local.isdn_device;
+				cmd.command = ISDN_CMD_SETL3;
+				cmd.arg = p->local.isdn_channel + (p->local.l3_proto << 8);
+				dev->drv[p->local.isdn_device]->interface->command(&cmd);
+				cmd.driver = p->local.isdn_device;
+				cmd.arg = p->local.isdn_channel;
+				save_flags(flags);
+				cli();
+				if (!p->local.dial) {
+					restore_flags(flags);
+					printk(KERN_WARNING "%s: phone number deleted?\n",
+					       p->local.name);
+					isdn_net_hangup(&p->dev);
+					break;
 				}
-				p->local.dtimer = 0;
+				if (!strcmp(p->local.dial->num, "LEASED")) {
+					restore_flags(flags);
+					p->local.dialstate = 4;
+					printk(KERN_INFO "%s: Open leased line ...\n", p->local.name);
+				} else {
+					sprintf(cmd.parm.setup.phone, "%s", p->local.dial->num);
+					/*
+					 * Switch to next number or back to start if at end of list.
+					 */
+					if (!(p->local.dial = (isdn_net_phone *) p->local.dial->next)) {
+						p->local.dial = p->local.phone[1];
+						p->local.dialretry++;
+					}
+					restore_flags(flags);
+					cmd.command = ISDN_CMD_DIAL;
+					cmd.parm.setup.si1 = 7;
+					cmd.parm.setup.si2 = 0;
+					sprintf(cmd.parm.setup.eazmsn, "%s",
+						isdn_map_eaz2msn(p->local.msn, cmd.driver));
+					i = isdn_dc2minor(p->local.isdn_device, p->local.isdn_channel);
+					if (i >= 0) {
+						strcpy(dev->num[i], cmd.parm.setup.phone);
+						isdn_info_update();
+					}
+					printk(KERN_INFO "%s: dialing %d %s...\n", p->local.name,
+					       p->local.dialretry - 1, cmd.parm.setup.phone);
+					p->local.dtimer = 0;
 #ifdef ISDN_DEBUG_NET_DIAL
-				printk(KERN_DEBUG "dial: d=%d c=%d\n", p->local.isdn_device,
-				       p->local.isdn_channel);
+					printk(KERN_DEBUG "dial: d=%d c=%d\n", p->local.isdn_device,
+					       p->local.isdn_channel);
 #endif
+					dev->drv[p->local.isdn_device]->interface->command(&cmd);
+				}
+				p->local.huptimer = 0;
+				p->local.outgoing = 1;
+				if (p->local.chargeint) {
+					p->local.hupflags |= ISDN_HAVECHARGE;
+					p->local.hupflags &= ~ISDN_WAITCHARGE;
+				} else {
+					p->local.hupflags |= ISDN_WAITCHARGE;
+					p->local.hupflags &= ~ISDN_HAVECHARGE;
+				}
+				anymore = 1;
+				p->local.dialstate =
+				    (p->local.cbdelay &&
+				     (p->local.flags & ISDN_NET_CBOUT)) ? 12 : 4;
+				break;
+			case 4:
+				/* Wait for D-Channel-connect.
+				 * If timeout and max retries not
+				 * reached, switch back to state 3.
+				 */
+				if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
+					if (p->local.dialretry < p->local.dialmax) {
+						p->local.dialstate = 3;
+					} else
+						isdn_net_hangup(&p->dev);
+				anymore = 1;
+				break;
+			case 5:
+				/* Got D-Channel-Connect, send B-Channel-request */
+				cmd.driver = p->local.isdn_device;
+				cmd.arg = p->local.isdn_channel;
+				cmd.command = ISDN_CMD_ACCEPTB;
+				anymore = 1;
+				p->local.dtimer = 0;
+				p->local.dialstate++;
 				dev->drv[p->local.isdn_device]->interface->command(&cmd);
-			}
-			anymore = 1;
-			p->local.dialstate =
-                                (p->local.cbdelay &&
-                                 (p->local.flags & ISDN_NET_CBOUT))?12:4;
-			break;
-		case 4:
-			/* Wait for D-Channel-connect.
-                         * If timeout and max retries not
-			 * reached, switch back to state 3.
-			 */
-			if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
-				if (p->local.dialretry < p->local.dialmax) {
-					p->local.dialstate = 3;
-				} else
-                                        isdn_net_hangup(&p->dev);
-			anymore = 1;
-			break;
-		case 5:
-			/* Got D-Channel-Connect, send B-Channel-request */
-			cmd.driver = p->local.isdn_device;
-			cmd.arg = p->local.isdn_channel;
-			cmd.command = ISDN_CMD_ACCEPTB;
-			anymore = 1;
-			p->local.dtimer = 0;
-			p->local.dialstate++;
-			dev->drv[p->local.isdn_device]->interface->command(&cmd);
-			break;
-		case 6:
-			/* Wait for B- or D-Channel-connect. If timeout,
-                         * switch back to state 3.
-			 */
+				break;
+			case 6:
+				/* Wait for B- or D-Channel-connect. If timeout,
+				 * switch back to state 3.
+				 */
 #ifdef ISDN_DEBUG_NET_DIAL
-			printk(KERN_DEBUG "dialtimer2: %d\n", p->local.dtimer);
+				printk(KERN_DEBUG "dialtimer2: %d\n", p->local.dtimer);
 #endif
-			if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
-				p->local.dialstate = 3;
-			anymore = 1;
-			break;
-		case 7:
-			/* Got incoming Call, setup L2 and L3 protocols,
-                         * then wait for D-Channel-connect
-                         */
+				if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
+					p->local.dialstate = 3;
+				anymore = 1;
+				break;
+			case 7:
+				/* Got incoming Call, setup L2 and L3 protocols,
+				 * then wait for D-Channel-connect
+				 */
 #ifdef ISDN_DEBUG_NET_DIAL
-			printk(KERN_DEBUG "dialtimer4: %d\n", p->local.dtimer);
+				printk(KERN_DEBUG "dialtimer4: %d\n", p->local.dtimer);
 #endif
-			cmd.driver = p->local.isdn_device;
-			cmd.command = ISDN_CMD_SETL2;
-			cmd.arg = p->local.isdn_channel + (p->local.l2_proto << 8);
-			dev->drv[p->local.isdn_device]->interface->command(&cmd);
-			cmd.driver = p->local.isdn_device;
-			cmd.command = ISDN_CMD_SETL3;
-			cmd.arg = p->local.isdn_channel + (p->local.l3_proto << 8);
-			dev->drv[p->local.isdn_device]->interface->command(&cmd);
-			if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT15)
-				isdn_net_hangup(&p->dev);
-			else {
+				cmd.driver = p->local.isdn_device;
+				cmd.command = ISDN_CMD_SETL2;
+				cmd.arg = p->local.isdn_channel + (p->local.l2_proto << 8);
+				dev->drv[p->local.isdn_device]->interface->command(&cmd);
+				cmd.driver = p->local.isdn_device;
+				cmd.command = ISDN_CMD_SETL3;
+				cmd.arg = p->local.isdn_channel + (p->local.l3_proto << 8);
+				dev->drv[p->local.isdn_device]->interface->command(&cmd);
+				if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT15)
+					isdn_net_hangup(&p->dev);
+				else {
+					anymore = 1;
+					p->local.dialstate++;
+				}
+				break;
+			case 9:
+				/* Got incoming D-Channel-Connect, send B-Channel-request */
+				cmd.driver = p->local.isdn_device;
+				cmd.arg = p->local.isdn_channel;
+				cmd.command = ISDN_CMD_ACCEPTB;
+				dev->drv[p->local.isdn_device]->interface->command(&cmd);
 				anymore = 1;
-                                p->local.dialstate++;
-                        }
-			break;
-		case 9:
-			/* Got incoming D-Channel-Connect, send B-Channel-request */
-			cmd.driver = p->local.isdn_device;
-			cmd.arg = p->local.isdn_channel;
-			cmd.command = ISDN_CMD_ACCEPTB;
-			dev->drv[p->local.isdn_device]->interface->command(&cmd);
-			anymore = 1;
-			p->local.dtimer = 0;
-			p->local.dialstate++;
-			break;
-		case 8:
-                case 10:
-			/*  Wait for B- or D-channel-connect */
+				p->local.dtimer = 0;
+				p->local.dialstate++;
+				break;
+			case 8:
+			case 10:
+				/*  Wait for B- or D-channel-connect */
 #ifdef ISDN_DEBUG_NET_DIAL
-			printk(KERN_DEBUG "dialtimer4: %d\n", p->local.dtimer);
+				printk(KERN_DEBUG "dialtimer4: %d\n", p->local.dtimer);
 #endif
-			if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
-				isdn_net_hangup(&p->dev);
-			else
+				if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
+					isdn_net_hangup(&p->dev);
+				else
+					anymore = 1;
+				break;
+			case 11:
+				/* Callback Delay */
+				if (p->local.dtimer++ > p->local.cbdelay)
+					p->local.dialstate = 1;
 				anymore = 1;
-			break;
-                case 11:
-                        /* Callback Delay */
-                        if (p->local.dtimer++ > p->local.cbdelay)
-                                p->local.dialstate = 1;
-                        anymore = 1;
-                        break;
-		case 12:
-			/* Remote does callback. Hangup after cbdelay, then wait for incoming
-                         * call (in state 4).
-			 */
-                        if (p->local.dtimer++ > p->local.cbdelay) {
-                                printk(KERN_INFO "%s: hangup waiting for callback ...\n", p->local.name);
-                                p->local.dtimer = 0;
-                                p->local.dialstate = 4;
-                                cmd.driver = p->local.isdn_device;
-                                cmd.command = ISDN_CMD_HANGUP;
-                                cmd.arg = p->local.isdn_channel;
-                                (void) dev->drv[cmd.driver]->interface->command(&cmd);
-                                isdn_all_eaz(p->local.isdn_device, p->local.isdn_channel);
-                        }
-                        anymore = 1;
-			break;
-		default:
-			printk(KERN_WARNING "isdn_net: Illegal dialstate %d for device %s\n",
-			       p->local.dialstate, p->local.name);
+				break;
+			case 12:
+				/* Remote does callback. Hangup after cbdelay, then wait for incoming
+				 * call (in state 4).
+				 */
+				if (p->local.dtimer++ > p->local.cbdelay) {
+					printk(KERN_INFO "%s: hangup waiting for callback ...\n", p->local.name);
+					p->local.dtimer = 0;
+					p->local.dialstate = 4;
+					cmd.driver = p->local.isdn_device;
+					cmd.command = ISDN_CMD_HANGUP;
+					cmd.arg = p->local.isdn_channel;
+					(void) dev->drv[cmd.driver]->interface->command(&cmd);
+					isdn_all_eaz(p->local.isdn_device, p->local.isdn_channel);
+				}
+				anymore = 1;
+				break;
+			default:
+				printk(KERN_WARNING "isdn_net: Illegal dialstate %d for device %s\n",
+				       p->local.dialstate, p->local.name);
 		}
 		p = (isdn_net_dev *) p->next;
 	}
@@ -674,6 +791,7 @@
 	isdn_ctrl cmd;
 
 	if (lp->flags & ISDN_NET_CONNECTED) {
+		lp->flags &= ~ISDN_NET_CONNECTED;
 		printk(KERN_INFO "isdn_net: local hangup %s\n", lp->name);
 #ifdef CONFIG_ISDN_PPP
 		isdn_ppp_free(lp);
@@ -685,7 +803,7 @@
 		printk(KERN_INFO "%s: Chargesum is %d\n", lp->name, lp->charge);
 		isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
 	}
-        isdn_net_unbind_channel(lp);
+	isdn_net_unbind_channel(lp);
 }
 
 typedef struct {
@@ -696,59 +814,59 @@
 static void
 isdn_net_log_packet(u_char * buf, isdn_net_local * lp)
 {
-        u_char *p = buf;
+	u_char *p = buf;
 	unsigned short proto = ETH_P_IP;
-        int data_ofs;
+	int data_ofs;
 	ip_ports *ipp;
 	char addinfo[100];
 
-        addinfo[0] = '\0';
-        switch (lp->p_encap) {
-                case ISDN_NET_ENCAP_IPTYP:
-                        proto = ntohs(*(unsigned short *)&buf[0]);
-                        p = &buf[2];
-                        break;
-                case ISDN_NET_ENCAP_ETHER:
-                        proto = ntohs(*(unsigned short *)&buf[12]);
-                        p = &buf[14];
-                        break;
-                case ISDN_NET_ENCAP_CISCOHDLC:
-                        proto = ntohs(*(unsigned short *)&buf[2]);
-                        p = &buf[4];
-                        break;
-        }
+	addinfo[0] = '\0';
+	switch (lp->p_encap) {
+		case ISDN_NET_ENCAP_IPTYP:
+			proto = ntohs(*(unsigned short *) &buf[0]);
+			p = &buf[2];
+			break;
+		case ISDN_NET_ENCAP_ETHER:
+			proto = ntohs(*(unsigned short *) &buf[12]);
+			p = &buf[14];
+			break;
+		case ISDN_NET_ENCAP_CISCOHDLC:
+			proto = ntohs(*(unsigned short *) &buf[2]);
+			p = &buf[4];
+			break;
+	}
 	data_ofs = ((p[0] & 15) * 4);
-        switch (proto) {
+	switch (proto) {
 		case ETH_P_IP:
 			switch (p[9]) {
-                                case 1:
-                                        strcpy(addinfo, " ICMP");
-                                        break;
-                                case 2:
-                                        strcpy(addinfo, " IGMP");
-                                        break;
-                                case 4:
-                                        strcpy(addinfo, " IPIP");
-                                        break;
-                                case 6:
-                                        ipp = (ip_ports *) (&p[data_ofs]);
-                                        sprintf(addinfo, " TCP, port: %d -> %d", ntohs(ipp->source),
-                                                ntohs(ipp->dest));
-                                        break;
-                                case 8:
-                                        strcpy(addinfo, " EGP");
-                                        break;
-                                case 12:
-                                        strcpy(addinfo, " PUP");
-                                        break;
-                                case 17:
-                                        ipp = (ip_ports *) (&p[data_ofs]);
-                                        sprintf(addinfo, " UDP, port: %d -> %d", ntohs(ipp->source),
-                                                ntohs(ipp->dest));
-                                        break;
-                                case 22:
-                                        strcpy(addinfo, " IDP");
-                                        break;
+				case 1:
+					strcpy(addinfo, " ICMP");
+					break;
+				case 2:
+					strcpy(addinfo, " IGMP");
+					break;
+				case 4:
+					strcpy(addinfo, " IPIP");
+					break;
+				case 6:
+					ipp = (ip_ports *) (&p[data_ofs]);
+					sprintf(addinfo, " TCP, port: %d -> %d", ntohs(ipp->source),
+						ntohs(ipp->dest));
+					break;
+				case 8:
+					strcpy(addinfo, " EGP");
+					break;
+				case 12:
+					strcpy(addinfo, " PUP");
+					break;
+				case 17:
+					ipp = (ip_ports *) (&p[data_ofs]);
+					sprintf(addinfo, " UDP, port: %d -> %d", ntohs(ipp->source),
+						ntohs(ipp->dest));
+					break;
+				case 22:
+					strcpy(addinfo, " IDP");
+					break;
 			}
 			printk(KERN_INFO "OPEN: %d.%d.%d.%d -> %d.%d.%d.%d%s\n",
 			       p[12], p[13], p[14], p[15],
@@ -760,7 +878,7 @@
 			       p[14], p[15], p[16], p[17],
 			       p[24], p[25], p[26], p[27]);
 			break;
-        }
+	}
 }
 
 /*
@@ -772,28 +890,27 @@
  * Side-effects: ndev->tbusy is cleared on success.
  */
 int
-isdn_net_send_skb(struct device *ndev, isdn_net_local *lp,
-                  struct sk_buff *skb)
+isdn_net_send_skb(struct device *ndev, isdn_net_local * lp,
+		  struct sk_buff *skb)
 {
 	int ret;
-	int len = skb->len;	/* save len */
-	
-        ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, skb);
+	int len = skb->len;     /* save len */
+
+	ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, skb);
 	if (ret == len) {
 		lp->transcount += len;
-		clear_bit(0, (void *)&(ndev->tbusy));
+		clear_bit(0, (void *) &(ndev->tbusy));
+		return 0;
+	}
+	if (ret < 0) {
+		dev_kfree_skb(skb, FREE_WRITE);
+		lp->stats.tx_errors++;
+		clear_bit(0, (void *) &(ndev->tbusy));
 		return 0;
 	}
-        if (ret < 0) {
-                skb->free = 1;
-                dev_kfree_skb(skb, FREE_WRITE);
-                lp->stats.tx_errors++;
-                clear_bit(0, (void *)&(ndev->tbusy));
-                return 0;
-        }
 	return 1;
-}                                      
-	
+}
+
 
 /*
  *  Helper function for isdn_net_start_xmit.
@@ -807,23 +924,23 @@
  */
 
 static int
-isdn_net_xmit(struct device *ndev, isdn_net_local *lp, struct sk_buff *skb) 
+isdn_net_xmit(struct device *ndev, isdn_net_local * lp, struct sk_buff *skb)
 {
-        int ret;
+	int ret;
 
 	/* For the other encaps the header has already been built */
 #ifdef CONFIG_ISDN_PPP
 	if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
 		return isdn_ppp_xmit(skb, ndev);
 	}
-#endif		
+#endif
 	/* Reset hangup-timeout */
 	lp->huptimer = 0;
-	if (lp->cps > 7000) {
+	if (lp->cps > lp->triggercps) {
 		/* Device overloaded */
 
-		/* 
-		 * Packet-delivery via round-robin over master 
+		/*
+		 * Packet-delivery via round-robin over master
 		 * and all connected slaves.
 		 */
 		if (lp->master)
@@ -847,18 +964,16 @@
 				/* First time overload: set timestamp only */
 				lp->sqfull = 1;
 				lp->sqfull_stamp = jiffies;
-			} 
-			else {
+			} else {
 				/* subsequent overload: if slavedelay exceeded, start dialing */
 				if ((jiffies - lp->sqfull_stamp) > lp->slavedelay)
 					isdn_net_force_dial_lp((isdn_net_local *) lp->slave->priv);
 			}
 		}
-	} 
-	else {
+	} else {
 		/* Not overloaded, deliver locally */
 		ret = isdn_net_send_skb(ndev, lp, skb);
-		if (lp->sqfull && ((jiffies - lp->sqfull_stamp) > (lp->slavedelay + (10*HZ) )))
+		if (lp->sqfull && ((jiffies - lp->sqfull_stamp) > (lp->slavedelay + (10 * HZ))))
 			lp->sqfull = 0;
 	}
 	return ret;
@@ -877,8 +992,8 @@
 	if (ndev->tbusy) {
 		if (jiffies - ndev->trans_start < (2 * HZ))
 			return 1;
-                if (!lp->dialstate)
-                        lp->stats.tx_errors++;
+		if (!lp->dialstate)
+			lp->stats.tx_errors++;
 		ndev->tbusy = 0;
 		ndev->trans_start = jiffies;
 	}
@@ -887,10 +1002,10 @@
 		return 0;
 	}
 	/* Avoid timer-based retransmission conflicts. */
-	if (set_bit(0, (void *) &ndev->tbusy) != 0)
+	if (test_and_set_bit(0, (void *) &ndev->tbusy) != 0)
 		printk(KERN_WARNING
-                       "%s: Transmitter access conflict.\n",
-                       ndev->name);
+		       "%s: Transmitter access conflict.\n",
+		       ndev->name);
 	else {
 		u_char *buf = skb->data;
 #ifdef ISDN_DEBUG_NET_DUMP
@@ -903,23 +1018,31 @@
 				save_flags(flags);
 				cli();
 				/* Grab a free ISDN-Channel */
-				if ((chi = 
-                                     isdn_get_free_channel(ISDN_USAGE_NET,
-                                                           lp->l2_proto,
-                                                           lp->l3_proto,
-                                                           lp->pre_device,
-                                                           lp->pre_channel)) < 0) {
-                                        printk(KERN_WARNING
-                                               "isdn_net_start_xmit: No channel for %s\n",
-                                               ndev->name);
+				if ((chi =
+				     isdn_get_free_channel(ISDN_USAGE_NET,
+							   lp->l2_proto,
+							   lp->l3_proto,
+							   lp->pre_device,
+						 lp->pre_channel)) < 0) {
 					restore_flags(flags);
-		/* we probably should drop the skb here and return 0 to omit
-		   'socket destroy delayed' messages */
+#if 0
+					printk(KERN_WARNING
+					       "isdn_net_start_xmit: No channel for %s\n",
+					       ndev->name);
+					/* we probably should drop the skb here and return 0 to omit
+					   'socket destroy delayed' messages */
 					return 1;
+#else
+					isdn_net_unreachable(ndev, skb,
+							   "No channel");
+					dev_kfree_skb(skb, FREE_WRITE);
+					ndev->tbusy = 0;
+					return 0;
+#endif
 				}
-                                /* Log packet, which triggered dialing */
+				/* Log packet, which triggered dialing */
 				if (dev->net_verbose)
-                                        isdn_net_log_packet(buf, lp);
+					isdn_net_log_packet(buf, lp);
 				lp->dialstate = 1;
 				lp->flags |= ISDN_NET_CONNECTED;
 				/* Connect interface with channel */
@@ -928,59 +1051,47 @@
 				if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
 					/* no 'first_skb' handling for syncPPP */
 					if (isdn_ppp_bind(lp) < 0) {
-                                        	dev_kfree_skb(skb,FREE_WRITE);
+						dev_kfree_skb(skb, FREE_WRITE);
 						isdn_net_unbind_channel(lp);
-                                                restore_flags(flags);
+						restore_flags(flags);
 						return 0;	/* STN (skb to nirvana) ;) */
 					}
 					restore_flags(flags);
-	                                isdn_net_dial();	/* Initiate dialing */
+					isdn_net_dial();	/* Initiate dialing */
 					return 1;	/* let upper layer requeue skb packet */
 				}
 #endif
-                                /* remember first skb to speed up arp
-                                 * when using encap ETHER
-                                 */
-                                if (lp->first_skb) {
-                                        printk(KERN_WARNING "isdn_net_start_xmit: First skb already set!\n");
-                                        dev_kfree_skb(lp->first_skb,FREE_WRITE);
-                                        lp->first_skb = NULL;
-                                }
-                                lp->first_skb = skb;
+				/* remember first skb to speed up arp
+				 * when using encap ETHER
+				 */
+				if (lp->first_skb) {
+					printk(KERN_WARNING "isdn_net_start_xmit: First skb already set!\n");
+					dev_kfree_skb(lp->first_skb, FREE_WRITE);
+					lp->first_skb = NULL;
+				}
+				lp->first_skb = skb;
 				/* Initiate dialing */
-                                ndev->tbusy = 0;
+				ndev->tbusy = 0;
 				restore_flags(flags);
 				isdn_net_dial();
-                                return 0;
+				return 0;
 			} else {
-                                /*
-                                 * Having no phone-number is a permanent
-                                 * failure or misconfiguration.
-                                 * Instead of just dropping, we should also
-                                 * have the upper layers to respond
-                                 * with an ICMP No route to host in the
-                                 * future, however at the moment, i don't
-                                 * know a simple way to do that.
-                                 * The same applies, when the telecom replies
-                                 * "no destination" to our dialing-attempt.
-                                 */
-                                printk(KERN_WARNING
-                                       "isdn_net: No phone number for %s, packet dropped\n",
-                                       ndev->name);
+				isdn_net_unreachable(ndev, skb,
+						     "No phone number");
 				dev_kfree_skb(skb, FREE_WRITE);
 				ndev->tbusy = 0;
-                                return 0;
+				return 0;
 			}
 		} else {
-                        /* Connection is established, try sending */
+			/* Connection is established, try sending */
 			ndev->trans_start = jiffies;
 			if (!lp->dialstate) {
-                                if (lp->first_skb) {
-                                        if (isdn_net_xmit(ndev,lp,lp->first_skb))
-                                                return 1;
-                                        lp->first_skb = NULL;
-                                }
-				return(isdn_net_xmit(ndev, lp, skb));
+				if (lp->first_skb) {
+					if (isdn_net_xmit(ndev, lp, lp->first_skb))
+						return 1;
+					lp->first_skb = NULL;
+				}
+				return (isdn_net_xmit(ndev, lp, skb));
 			} else
 				ndev->tbusy = 1;
 		}
@@ -998,7 +1109,6 @@
 
 	dev->tbusy = 1;
 	dev->start = 0;
-	isdn_net_hangup(dev);
 	if ((p = (((isdn_net_local *) dev->priv)->slave))) {
 		/* If this interface has slaves, stop them also */
 		while (p) {
@@ -1008,6 +1118,7 @@
 			p = (((isdn_net_local *) p->priv)->slave);
 		}
 	}
+	isdn_net_hangup(dev);
 	isdn_MOD_DEC_USE_COUNT();
 	return 0;
 }
@@ -1016,7 +1127,7 @@
  * Get statistics
  */
 static struct enet_statistics *
- isdn_net_get_stats(struct device *dev)
+isdn_net_get_stats(struct device *dev)
 {
 	isdn_net_local *lp = (isdn_net_local *) dev->priv;
 	return &lp->stats;
@@ -1031,51 +1142,50 @@
  *      This is normal practice and works for any 'now in use' protocol.
  */
 
-unsigned short isdn_net_type_trans(struct sk_buff *skb, struct device *dev)
+static unsigned short
+isdn_net_type_trans(struct sk_buff *skb, struct device *dev)
 {
-        struct ethhdr *eth;
-        unsigned char *rawp;
-        
-        skb_pull(skb,ETH_HLEN);
-        eth= skb->mac.ethernet;
-        
-        if(*eth->h_dest&1) {
-                if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0)
-                        skb->pkt_type=PACKET_BROADCAST;
-                else
-                        skb->pkt_type=PACKET_MULTICAST;
-        }
-        
-        /*
-         *      This ALLMULTI check should be redundant by 1.4
-         *      so don't forget to remove it.
-         */
-        
-        else if (dev->flags&(IFF_PROMISC|IFF_ALLMULTI)) {
-                if (memcmp(eth->h_dest,dev->dev_addr, ETH_ALEN))
-                        skb->pkt_type=PACKET_OTHERHOST;
-        }
-
-        if (ntohs(eth->h_proto) >= 1536)
-                return eth->h_proto;
-
-        rawp = skb->data;
-
-        /*
-         *      This is a magic hack to spot IPX packets. Older Novell breaks
-         *      the protocol design and runs IPX over 802.3 without an 802.2 LLC
-         *      layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
-         *      won't work for fault tolerant netware but does for the rest.
-         */
-        if (*(unsigned short *)rawp == 0xFFFF)
-                return htons(ETH_P_802_3);
-        /*
-         *      Real 802.2 LLC
-         */
-        return htons(ETH_P_802_2);
+	struct ethhdr *eth;
+	unsigned char *rawp;
+
+	skb_pull(skb, ETH_HLEN);
+	eth = skb->mac.ethernet;
+
+	if (*eth->h_dest & 1) {
+		if (memcmp(eth->h_dest, dev->broadcast, ETH_ALEN) == 0)
+			skb->pkt_type = PACKET_BROADCAST;
+		else
+			skb->pkt_type = PACKET_MULTICAST;
+	}
+	/*
+	 *      This ALLMULTI check should be redundant by 1.4
+	 *      so don't forget to remove it.
+	 */
+
+	else if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
+		if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN))
+			skb->pkt_type = PACKET_OTHERHOST;
+	}
+	if (ntohs(eth->h_proto) >= 1536)
+		return eth->h_proto;
+
+	rawp = skb->data;
+
+	/*
+	 *      This is a magic hack to spot IPX packets. Older Novell breaks
+	 *      the protocol design and runs IPX over 802.3 without an 802.2 LLC
+	 *      layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
+	 *      won't work for fault tolerant netware but does for the rest.
+	 */
+	if (*(unsigned short *) rawp == 0xFFFF)
+		return htons(ETH_P_802_3);
+	/*
+	 *      Real 802.2 LLC
+	 */
+	return htons(ETH_P_802_2);
 }
 
-/* 
+/*
  * Got a packet from ISDN-Channel.
  */
 static void
@@ -1083,21 +1193,21 @@
 {
 	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
 #ifdef CONFIG_ISDN_PPP
-        isdn_net_local *olp = lp;  /* original 'lp' */
-        int proto = PPP_PROTOCOL(skb->data);
+	isdn_net_local *olp = lp;	/* original 'lp' */
+	int proto = PPP_PROTOCOL(skb->data);
 #endif
 
 	lp->transcount += skb->len;
 	lp->stats.rx_packets++;
 #ifdef CONFIG_ISDN_PPP
-        /*
-         * If encapsulation is syncppp, don't reset
-         * huptimer on LCP packets.
-         */
-        if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP ||
-            (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && proto != PPP_LCP))
+	/*
+	 * If encapsulation is syncppp, don't reset
+	 * huptimer on LCP packets.
+	 */
+	if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP ||
+	    (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && proto != PPP_LCP))
 #endif
-	        lp->huptimer = 0;
+		lp->huptimer = 0;
 
 	if (lp->master) {
 		/* Bundling: If device is a slave-device, deliver to master, also
@@ -1107,56 +1217,55 @@
 		lp = (isdn_net_local *) ndev->priv;
 		lp->stats.rx_packets++;
 #ifdef CONFIG_ISDN_PPP
-                /*
-                 * If encapsulation is syncppp, don't reset
-                 * huptimer on LCP packets.
-                 */
-                if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP ||
-                    (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && proto != PPP_LCP))
+		/*
+		 * If encapsulation is syncppp, don't reset
+		 * huptimer on LCP packets.
+		 */
+		if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP ||
+		    (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && proto != PPP_LCP))
 #endif
-                        lp->huptimer = 0;
+			lp->huptimer = 0;
 	}
-
 	skb->dev = ndev;
 	skb->pkt_type = PACKET_HOST;
-        skb->mac.raw = skb->data;
+	skb->mac.raw = skb->data;
 #ifdef ISDN_DEBUG_NET_DUMP
-        isdn_dumppkt("R:", skb->data, skb->len, 40);
+	isdn_dumppkt("R:", skb->data, skb->len, 40);
 #endif
 	switch (lp->p_encap) {
-                case ISDN_NET_ENCAP_ETHER:
-                        /* Ethernet over ISDN */
-                        skb->protocol = isdn_net_type_trans(skb,ndev);
-                        break;
-                case ISDN_NET_ENCAP_UIHDLC:
-                        /* HDLC with UI-frame (for ispa with -h1 option) */
-                        skb_pull(skb,2);
-                        /* Fall through */
-                case ISDN_NET_ENCAP_RAWIP:
-                        /* RAW-IP without MAC-Header */
-                        skb->protocol = htons(ETH_P_IP);
-                        break;
-                case ISDN_NET_ENCAP_CISCOHDLC:
-                        /* CISCO-HDLC IP with type field and  fake I-frame-header */
-                        skb_pull(skb, 2);
-                        /* Fall through */
-                case ISDN_NET_ENCAP_IPTYP:
-                        /* IP with type field */
-                        skb->protocol = *(unsigned short *)&(skb->data[0]);
-                        skb_pull(skb, 2);
-                        if (*(unsigned short *)skb->data == 0xFFFF)
-                                skb->protocol = htons(ETH_P_802_3);
-                        break;
+		case ISDN_NET_ENCAP_ETHER:
+			/* Ethernet over ISDN */
+			skb->protocol = isdn_net_type_trans(skb, ndev);
+			break;
+		case ISDN_NET_ENCAP_UIHDLC:
+			/* HDLC with UI-frame (for ispa with -h1 option) */
+			skb_pull(skb, 2);
+			/* Fall through */
+		case ISDN_NET_ENCAP_RAWIP:
+			/* RAW-IP without MAC-Header */
+			skb->protocol = htons(ETH_P_IP);
+			break;
+		case ISDN_NET_ENCAP_CISCOHDLC:
+			/* CISCO-HDLC IP with type field and  fake I-frame-header */
+			skb_pull(skb, 2);
+			/* Fall through */
+		case ISDN_NET_ENCAP_IPTYP:
+			/* IP with type field */
+			skb->protocol = *(unsigned short *) &(skb->data[0]);
+			skb_pull(skb, 2);
+			if (*(unsigned short *) skb->data == 0xFFFF)
+				skb->protocol = htons(ETH_P_802_3);
+			break;
 #ifdef CONFIG_ISDN_PPP
-                case ISDN_NET_ENCAP_SYNCPPP:
-                        isdn_ppp_receive(lp->netdev, olp, skb);
-                        return;
-#endif
-                default:
-                        printk(KERN_WARNING "%s: unknown encapsulation, dropping\n",
-                               lp->name);
-                        kfree_skb(skb,FREE_READ);
-                        return;
+		case ISDN_NET_ENCAP_SYNCPPP:
+			isdn_ppp_receive(lp->netdev, olp, skb);
+			return;
+#endif
+		default:
+			printk(KERN_WARNING "%s: unknown encapsulation, dropping\n",
+			       lp->name);
+			kfree_skb(skb, FREE_READ);
+			return;
 	}
 	netif_rx(skb);
 	return;
@@ -1168,34 +1277,7 @@
  * else return 0.
  */
 int
-isdn_net_receive_callback(int idx, u_char * buf, int len)
-{
-	isdn_net_dev *p = dev->rx_netdev[idx];
-	struct sk_buff *skb;
-
-	if (p) {
-		isdn_net_local *lp = &p->local;
-		if ((lp->flags & ISDN_NET_CONNECTED) &&
-		    (!lp->dialstate)) {
-			skb = dev_alloc_skb(len);
-			if (skb == NULL) {
-				printk(KERN_WARNING "out of memory\n");
-				return 0;
-			}
-			memcpy(skb_put(skb, len), buf, len);
-			isdn_net_receive(&p->dev, skb);
-			return 1;
-		}
-	}
-	return 0;
-}
-
-/*
- *  receive callback for lowlevel drivers, which support skb's
- */
-
-int
-isdn_net_rcv_skb(int idx, struct sk_buff *skb) 
+isdn_net_rcv_skb(int idx, struct sk_buff *skb)
 {
 	isdn_net_dev *p = dev->rx_netdev[idx];
 
@@ -1212,43 +1294,41 @@
 
 static int
 my_eth_header(struct sk_buff *skb, struct device *dev, unsigned short type,
-              void *daddr, void *saddr, unsigned len)
+	      void *daddr, void *saddr, unsigned len)
 {
-	struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN);
+	struct ethhdr *eth = (struct ethhdr *) skb_push(skb, ETH_HLEN);
 
-	/* 
+	/*
 	 * Set the protocol type. For a packet of type ETH_P_802_3 we
-         * put the length here instead. It is up to the 802.2 layer to
-         * carry protocol information.
+	 * put the length here instead. It is up to the 802.2 layer to
+	 * carry protocol information.
 	 */
-	
-	if(type!=ETH_P_802_3) 
+
+	if (type != ETH_P_802_3)
 		eth->h_proto = htons(type);
 	else
 		eth->h_proto = htons(len);
 
 	/*
-	 *	Set the source hardware address. 
+	 * Set the source hardware address.
 	 */
-	if(saddr)
-		memcpy(eth->h_source,saddr,dev->addr_len);
+	if (saddr)
+		memcpy(eth->h_source, saddr, dev->addr_len);
 	else
-		memcpy(eth->h_source,dev->dev_addr,dev->addr_len);
+		memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
 
 	/*
-	 *	Anyway, the loopback-device should never use this function... 
+	 * Anyway, the loopback-device should never use this function...
 	 */
 
 	if (dev->flags & IFF_LOOPBACK) {
 		memset(eth->h_dest, 0, dev->addr_len);
-		return(dev->hard_header_len);
+		return (dev->hard_header_len);
 	}
-	
-	if(daddr) {
-		memcpy(eth->h_dest,daddr,dev->addr_len);
+	if (daddr) {
+		memcpy(eth->h_dest, daddr, dev->addr_len);
 		return dev->hard_header_len;
 	}
-	
 	return -dev->hard_header_len;
 }
 
@@ -1256,76 +1336,114 @@
  *  build an header
  *  depends on encaps that is being used.
  */
- 
+
 static int
 isdn_net_header(struct sk_buff *skb, struct device *dev, unsigned short type,
-                void *daddr, void *saddr, unsigned plen)
+		void *daddr, void *saddr, unsigned plen)
 {
 	isdn_net_local *lp = dev->priv;
 	ushort len = 0;
-	
+
 	switch (lp->p_encap) {
-                case ISDN_NET_ENCAP_ETHER:
-                        len = my_eth_header(skb, dev, type, daddr, saddr, plen);
-                        break;
-                case ISDN_NET_ENCAP_RAWIP:
-                        printk(KERN_WARNING "isdn_net_header called with RAW_IP!\n");
+		case ISDN_NET_ENCAP_ETHER:
+			len = my_eth_header(skb, dev, type, daddr, saddr, plen);
+			break;
+#ifdef CONFIG_ISDN_PPP
+		case ISDN_NET_ENCAP_SYNCPPP:
+			/* stick on a fake header to keep fragmentation code happy. */
+			len = IPPP_MAX_HEADER;
+			skb_push(skb,len);
+			break;
+#endif
+		case ISDN_NET_ENCAP_RAWIP:
+			printk(KERN_WARNING "isdn_net_header called with RAW_IP!\n");
 			len = 0;
-                        break;
-                case ISDN_NET_ENCAP_IPTYP:
-                        /* ethernet type field */
-                        *((ushort*) skb_push(skb, 2)) = htons(type);
-                        len = 2;
-                        break;
-                case ISDN_NET_ENCAP_UIHDLC:
-                        /* HDLC with UI-Frames (for ispa with -h1 option) */
-                        *((ushort*) skb_push(skb, 2)) = htons(0x0103);
-                        len = 2;
-                        break;
-                case ISDN_NET_ENCAP_CISCOHDLC:
+			break;
+		case ISDN_NET_ENCAP_IPTYP:
+			/* ethernet type field */
+			*((ushort *) skb_push(skb, 2)) = htons(type);
+			len = 2;
+			break;
+		case ISDN_NET_ENCAP_UIHDLC:
+			/* HDLC with UI-Frames (for ispa with -h1 option) */
+			*((ushort *) skb_push(skb, 2)) = htons(0x0103);
+			len = 2;
+			break;
+		case ISDN_NET_ENCAP_CISCOHDLC:
 			skb_push(skb, 4);
-                        skb->data[0] = 0x0f;
-                        skb->data[1] = 0x00;
-                        *((ushort*)&skb->data[2]) = htons(type);
-                        len = 4;
-                        break;
+			skb->data[0] = 0x0f;
+			skb->data[1] = 0x00;
+			*((ushort *) & skb->data[2]) = htons(type);
+			len = 4;
+			break;
 	}
 	return len;
 }
 
 /* We don't need to send arp, because we have point-to-point connections. */
-
+#if (LINUX_VERSION_CODE < 0x02010F)
 static int
 isdn_net_rebuild_header(void *buff, struct device *dev, unsigned long dst,
-                        struct sk_buff *skb)
+			struct sk_buff *skb)
 {
 	isdn_net_local *lp = dev->priv;
-        int ret = 0;
+	int ret = 0;
+
+	if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
+		struct ethhdr *eth = (struct ethhdr *) buff;
+
+		/*
+		 *      Only ARP/IP is currently supported
+		 */
 
-        if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
-                struct ethhdr *eth = (struct ethhdr *)buff;
-                
-                /*
-                 *      Only ARP/IP is currently supported
-                 */
-                
-                if(eth->h_proto != htons(ETH_P_IP)) {
-                        printk(KERN_WARNING
-                               "isdn_net: %s don't know how to resolve type %d addresses?\n",
-                               dev->name, (int)eth->h_proto);
-                        memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
-                        return 0;
-                }
-                /*
-                 *      Try to get ARP to resolve the header.
-                 */
-#ifdef CONFIG_INET       
-                ret = arp_find(eth->h_dest, dst, dev, dev->pa_addr, skb)? 1 : 0;
-#endif  
-        }
+		if (eth->h_proto != htons(ETH_P_IP)) {
+			printk(KERN_WARNING
+			       "isdn_net: %s don't know how to resolve type %d addresses?\n",
+			       dev->name, (int) eth->h_proto);
+			memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+			return 0;
+		}
+		/*
+		 *      Try to get ARP to resolve the header.
+		 */
+#ifdef CONFIG_INET
+		ret = arp_find(eth->h_dest, dst, dev, dev->pa_addr, skb) ? 1 : 0;
+#endif
+	}
 	return ret;
 }
+#else
+static int
+isdn_net_rebuild_header(struct sk_buff *skb)
+{
+	struct device *dev = skb->dev;
+	isdn_net_local *lp = dev->priv;
+	int ret = 0;
+
+	if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
+		struct ethhdr *eth = (struct ethhdr *) skb->data;
 
+		/*
+		 *      Only ARP/IP is currently supported
+		 */
+
+		if (eth->h_proto != htons(ETH_P_IP)) {
+			printk(KERN_WARNING
+			       "isdn_net: %s don't know how to resolve type %d addresses?\n",
+			       dev->name, (int) eth->h_proto);
+			memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+			return 0;
+		}
+		/*
+		 *      Try to get ARP to resolve the header.
+		 */
+#ifdef CONFIG_INET
+		ret = arp_find(eth->h_dest, skb) ? 1 : 0;
+#endif
+	}
+	return ret;
+}
+#endif
 /*
  * Interface-setup. (called just after registering a new interface)
  */
@@ -1333,8 +1451,9 @@
 isdn_net_init(struct device *ndev)
 {
 	ushort max_hlhdr_len = 0;
-        isdn_net_local *lp = (isdn_net_local *)ndev->priv;
-	int drvidx, i;
+	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+	int drvidx,
+	 i;
 
 	if (ndev == NULL) {
 		printk(KERN_WARNING "isdn_net_init: dev = NULL!\n");
@@ -1344,54 +1463,61 @@
 		printk(KERN_WARNING "isdn_net_init: dev->priv = NULL!\n");
 		return -ENODEV;
 	}
-
-        ether_setup(ndev);
-        lp->org_hcb               = ndev->header_cache_bind;
-        lp->org_hcu               = ndev->header_cache_update;
+	ether_setup(ndev);
+#if (LINUX_VERSION_CODE < 0x02010F)
+	lp->org_hcb = ndev->header_cache_bind;
+#else
+	lp->org_hhc = ndev->hard_header_cache;
+#endif
+	lp->org_hcu = ndev->header_cache_update;
 
 	/* Setup the generic properties */
 
-        ndev->hard_header         = NULL;
-        ndev->header_cache_bind   = NULL;
-        ndev->header_cache_update = NULL;
-        ndev->mtu                 = 1500;
-        ndev->flags               = IFF_NOARP;
-        ndev->family              = AF_INET;
-        ndev->type                = ARPHRD_ETHER;  
-        ndev->addr_len            = ETH_ALEN;
-        ndev->pa_addr             = 0;
-        ndev->pa_brdaddr          = 0;
-        ndev->pa_mask             = 0;
-        ndev->pa_alen             = 4;
+	ndev->hard_header = NULL;
+#if (LINUX_VERSION_CODE < 0x02010F)
+	ndev->header_cache_bind = NULL;
+#else
+	ndev->hard_header_cache = NULL;
+#endif
+	ndev->header_cache_update = NULL;
+	ndev->mtu = 1500;
+	ndev->flags = IFF_NOARP;
+	ndev->family = AF_INET;
+	ndev->type = ARPHRD_ETHER;
+	ndev->addr_len = ETH_ALEN;
+	ndev->pa_addr = 0;
+	ndev->pa_brdaddr = 0;
+	ndev->pa_mask = 0;
+	ndev->pa_alen = 4;
 
-        for (i = 0; i < ETH_ALEN; i++)
-                ndev->broadcast[i]=0xff;
+	for (i = 0; i < ETH_ALEN; i++)
+		ndev->broadcast[i] = 0xff;
 
 	for (i = 0; i < DEV_NUMBUFFS; i++)
-                skb_queue_head_init(&ndev->buffs[i]);
-	
+		skb_queue_head_init(&ndev->buffs[i]);
+
 	/* The ISDN-specific entries in the device structure. */
-	ndev->open                = &isdn_net_open;
-	ndev->hard_start_xmit     = &isdn_net_start_xmit;
+	ndev->open = &isdn_net_open;
+	ndev->hard_start_xmit = &isdn_net_start_xmit;
 
-	/* 
+	/*
 	 *  up till binding we ask the protocol layer to reserve as much
 	 *  as we might need for HL layer
-         */
-	
+	 */
+
 	for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++)
 		if (dev->drv[drvidx])
 			if (max_hlhdr_len < dev->drv[drvidx]->interface->hl_hdrlen)
 				max_hlhdr_len = dev->drv[drvidx]->interface->hl_hdrlen;
 
-	ndev->hard_header_len     = ETH_HLEN + max_hlhdr_len;
+	ndev->hard_header_len = ETH_HLEN + max_hlhdr_len;
 
-	ndev->stop                = &isdn_net_close;
-	ndev->get_stats           = &isdn_net_get_stats;
-	ndev->rebuild_header      = &isdn_net_rebuild_header;
+	ndev->stop = &isdn_net_close;
+	ndev->get_stats = &isdn_net_get_stats;
+	ndev->rebuild_header = &isdn_net_rebuild_header;
 
 #ifdef CONFIG_ISDN_PPP
-	ndev->do_ioctl            = isdn_ppp_dev_ioctl;
+	ndev->do_ioctl = isdn_ppp_dev_ioctl;
 #endif
 	return 0;
 }
@@ -1432,35 +1558,35 @@
 
 	for (; *p; s++, p++)
 		switch (*p) {
-                        case '\\':
-                                /*
-                                 * Literal match with following character,
-                                 * fall through.
-                                 */
-                                p++;
-                        default:
-                                if (*s != *p)
-                                        return (0);
-                                continue;
-                        case '?':
-                                /* Match anything. */
-                                if (*s == '\0')
-                                        return (0);
-                                continue;
-                        case '*':
-                                /* Trailing star matches everything. */
-                                return (*++p ? isdn_net_Star(s, p) : 1);
-                        case '[':
-                                /* [^....] means inverse character class. */
-                                if ((reverse = (p[1] == '^')))
-                                        p++;
-                                for (last = 0, matched = 0; *++p && (*p != ']'); last = *p)
-                                        /* This next line requires a good C compiler. */
-                                        if (*p == '-' ? *s <= *++p && *s >= last : *s == *p)
-                                                matched = 1;
-                                if (matched == reverse)
-                                        return (0);
-                                continue;
+			case '\\':
+				/*
+				 * Literal match with following character,
+				 * fall through.
+				 */
+				p++;
+			default:
+				if (*s != *p)
+					return (0);
+				continue;
+			case '?':
+				/* Match anything. */
+				if (*s == '\0')
+					return (0);
+				continue;
+			case '*':
+				/* Trailing star matches everything. */
+				return (*++p ? isdn_net_Star(s, p) : 1);
+			case '[':
+				/* [^....] means inverse character class. */
+				if ((reverse = (p[1] == '^')))
+					p++;
+				for (last = 0, matched = 0; *++p && (*p != ']'); last = *p)
+					/* This next line requires a good C compiler. */
+					if (*p == '-' ? *s <= *++p && *s >= last : *s == *p)
+						matched = 1;
+				if (matched == reverse)
+					return (0);
+				continue;
 		}
 	return (*s == '\0');
 }
@@ -1477,12 +1603,12 @@
 	while (p) {
 		if (p->local.pre_device == drvidx)
 			switch (p->local.pre_channel) {
-			case 0:
-				p->local.pre_channel = 1;
-				break;
-			case 1:
-				p->local.pre_channel = 0;
-				break;
+				case 0:
+					p->local.pre_channel = 1;
+					break;
+				case 1:
+					p->local.pre_channel = 0;
+					break;
 			}
 		p = (isdn_net_dev *) p->next;
 	}
@@ -1519,7 +1645,7 @@
  *               4 = Wait cbdelay, then call back
  */
 int
-isdn_net_find_icall(int di, int ch, int idx, char *num)
+isdn_net_find_icall(int di, int ch, int idx, setup_parm setup)
 {
 	char *eaz;
 	int si1;
@@ -1530,40 +1656,24 @@
 	isdn_net_dev *p;
 	isdn_net_phone *n;
 	ulong flags;
-	char nr[31];
-	char *s;
+	char nr[32];
 
 	/* Search name in netdev-chain */
 	save_flags(flags);
 	cli();
-	if (num[0] == ',') {
+	if (!setup.phone[0]) {
 		nr[0] = '0';
-		strncpy(&nr[1], num, 30);
+		nr[1] = '\0';
 		printk(KERN_INFO "isdn_net: Incoming call without OAD, assuming '0'\n");
 	} else
-		strncpy(nr, num, 30);
-	s = strtok(nr, ",");
-	s = strtok(NULL, ",");
-	if (!s) {
-		printk(KERN_WARNING "isdn_net: Incoming callinfo garbled, ignored: %s\n",
-		       num);
-		restore_flags(flags);
-		return 0;
-	}
-	si1 = (int)simple_strtoul(s,NULL,10);
-	s = strtok(NULL, ",");
-	if (!s) {
-		printk(KERN_WARNING "isdn_net: Incoming callinfo garbled, ignored: %s\n",
-		       num);
-		restore_flags(flags);
-		return 0;
-	}
-	si2 = (int)simple_strtoul(s,NULL,10);
-	eaz = strtok(NULL, ",");
-	if (!eaz) {
+		strcpy(nr, setup.phone);
+	si1 = (int) setup.si1;
+	si2 = (int) setup.si2;
+	if (!setup.eazmsn[0]) {
 		printk(KERN_WARNING "isdn_net: Incoming call without CPN, assuming '0'\n");
 		eaz = "0";
-	}
+	} else
+		eaz = setup.eazmsn;
 	if (dev->net_verbose > 1)
 		printk(KERN_INFO "isdn_net: call from %s,%d,%d -> %s\n", nr, si1, si2, eaz);
 	/* Accept only calls with Si1 = 7 (Data-Transmission) */
@@ -1583,12 +1693,12 @@
 	while (p) {
 		/* If last check has triggered as binding-swap, revert it */
 		switch (swapped) {
-		case 2:
-			isdn_net_swap_usage(idx, sidx);
-			/* fall through */
-		case 1:
-			isdn_net_swapbind(di);
-			break;
+			case 2:
+				isdn_net_swap_usage(idx, sidx);
+				/* fall through */
+			case 1:
+				isdn_net_swapbind(di);
+				break;
 		}
 		swapped = 0;
 		if (!strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz))
@@ -1597,11 +1707,11 @@
 		printk(KERN_DEBUG "n_fi: if='%s', l.msn=%s, l.flags=%d, l.dstate=%d\n",
 		       p->local.name, p->local.msn, p->local.flags, p->local.dialstate);
 #endif
-		if ((!strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz)) &&	    /* EAZ is matching   */
-		    (((!(p->local.flags & ISDN_NET_CONNECTED)) &&	            /* but not connected */
-		      (USG_NONE(dev->usage[idx]))) ||	                            /* and ch. unused or */
-		     ((((p->local.dialstate == 4) || (p->local.dialstate == 12)) && /* if dialing        */
-		       (!(p->local.flags & ISDN_NET_CALLBACK)))		            /* but no callback   */
+		if ((!strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz)) &&	/* EAZ is matching   */
+		    (((!(p->local.flags & ISDN_NET_CONNECTED)) &&	/* but not connected */
+		      (USG_NONE(dev->usage[idx]))) ||	/* and ch. unused or */
+		     ((((p->local.dialstate == 4) || (p->local.dialstate == 12)) &&	/* if dialing        */
+		       (!(p->local.flags & ISDN_NET_CALLBACK)))	/* but no callback   */
 		     ))) {
 #ifdef ISDN_DEBUG_NET_ICALL
 			printk(KERN_DEBUG "n_fi: match1, pdev=%d pch=%d\n",
@@ -1611,13 +1721,13 @@
 				if ((p->local.pre_channel != ch) ||
 				    (p->local.pre_device != di)) {
 					/* Here we got a problem:
-					   If using an ICN-Card, an incoming call is always signaled on
-					   on the first channel of the card, if both channels are
-					   down. However this channel may be bound exclusive. If the
-					   second channel is free, this call should be accepted.
-					   The solution is horribly but it runs, so what:
-					   We exchange the exclusive bindings of the two channels, the
-					   corresponding variables in the interface-structs.
+					 * If using an ICN-Card, an incoming call is always signaled on
+					 * on the first channel of the card, if both channels are
+					 * down. However this channel may be bound exclusive. If the
+					 * second channel is free, this call should be accepted.
+					 * The solution is horribly but it runs, so what:
+					 * We exchange the exclusive bindings of the two channels, the
+					 * corresponding variables in the interface-structs.
 					 */
 					if (ch == 0) {
 						sidx = isdn_dc2minor(di, 1);
@@ -1626,13 +1736,13 @@
 #endif
 						if (USG_NONE(dev->usage[sidx])) {
 							/* Second Channel is free, now see if it is bound
-							   exclusive too. */
+							 * exclusive too. */
 							if (dev->usage[sidx] & ISDN_USAGE_EXCLUSIVE) {
 #ifdef ISDN_DEBUG_NET_ICALL
 								printk(KERN_DEBUG "n_fi: 2nd channel is down and bound\n");
 #endif
 								/* Yes, swap bindings only, if the original
-								   binding is bound to channel 1 of this driver */
+								 * binding is bound to channel 1 of this driver */
 								if ((p->local.pre_device == di) &&
 								    (p->local.pre_channel == 1)) {
 									isdn_net_swapbind(di);
@@ -1674,7 +1784,7 @@
 						continue;
 					}
 				}
-			} /* if (dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) */
+			}
 #ifdef ISDN_DEBUG_NET_ICALL
 			printk(KERN_DEBUG "n_fi: match2\n");
 #endif
@@ -1739,7 +1849,7 @@
 							return 0;
 						}
 						/* Setup dialstate. */
-                                                lp->dtimer = 0;
+						lp->dtimer = 0;
 						lp->dialstate = 11;
 						lp->flags |= ISDN_NET_CONNECTED;
 						/* Connect interface with channel */
@@ -1754,7 +1864,7 @@
 #endif
 						/* Initiate dialing by returning 2 or 4 */
 						restore_flags(flags);
-						return (lp->flags & ISDN_NET_CBHUP)?2:4;
+						return (lp->flags & ISDN_NET_CBHUP) ? 2 : 4;
 					} else
 						printk(KERN_WARNING "isdn_net: %s: No phone number\n", lp->name);
 					restore_flags(flags);
@@ -1766,28 +1876,27 @@
 					   device, so free this device */
 					if ((p->local.dialstate == 4) || (p->local.dialstate == 12)) {
 #ifdef CONFIG_ISDN_PPP
-                                                if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
-                                                        isdn_ppp_free(lp);
+						if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+							isdn_ppp_free(lp);
 #endif
 						isdn_free_channel(p->local.isdn_device, p->local.isdn_channel,
 							 ISDN_USAGE_NET);
-                                        }
+					}
 					dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
 					dev->usage[idx] |= ISDN_USAGE_NET;
 					strcpy(dev->num[idx], nr);
 					isdn_info_update();
-                                        dev->st_netdev[idx] = lp->netdev;
+					dev->st_netdev[idx] = lp->netdev;
 					p->local.isdn_device = di;
 					p->local.isdn_channel = ch;
 					p->local.ppp_slot = -1;
-					p->local.pppbind = -1;
 					p->local.flags |= ISDN_NET_CONNECTED;
 					p->local.dialstate = 7;
 					p->local.dtimer = 0;
 					p->local.outgoing = 0;
 					p->local.huptimer = 0;
-					p->local.hupflags |= 1;
-                                        p->local.hupflags &= ~2;
+					p->local.hupflags |= ISDN_WAITCHARGE;
+					p->local.hupflags &= ~ISDN_HAVECHARGE;
 #ifdef CONFIG_ISDN_PPP
 					if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
 						if (isdn_ppp_bind(lp) < 0) {
@@ -1814,7 +1923,7 @@
  * Search list of net-interfaces for an interface with given name.
  */
 isdn_net_dev *
- isdn_net_findif(char *name)
+isdn_net_findif(char *name)
 {
 	isdn_net_dev *p = dev->netdev;
 
@@ -1831,7 +1940,8 @@
  * This is called from the userlevel-routine below or
  * from isdn_net_start_xmit().
  */
-int isdn_net_force_dial_lp(isdn_net_local * lp)
+int
+isdn_net_force_dial_lp(isdn_net_local * lp)
 {
 	if ((!(lp->flags & ISDN_NET_CONNECTED)) && !lp->dialstate) {
 		int chi;
@@ -1841,8 +1951,8 @@
 			cli();
 			/* Grab a free ISDN-Channel */
 			if ((chi = isdn_get_free_channel(ISDN_USAGE_NET, lp->l2_proto,
-						    lp->l3_proto,
-						    lp->pre_device,
+							 lp->l3_proto,
+							 lp->pre_device,
 						 lp->pre_channel)) < 0) {
 				printk(KERN_WARNING "isdn_net_force_dial: No channel for %s\n", lp->name);
 				restore_flags(flags);
@@ -1874,7 +1984,7 @@
  * Force a net-interface to dial out.
  * This is always called from within userspace (ISDN_IOCTL_NET_DIAL).
  */
-int 
+int
 isdn_net_force_dial(char *name)
 {
 	isdn_net_dev *p = isdn_net_findif(name);
@@ -1906,9 +2016,9 @@
 		strcpy(netdev->local.name, "         ");
 	else
 		strcpy(netdev->local.name, name);
-	netdev->dev.name      = netdev->local.name;
-	netdev->dev.priv      = &netdev->local;
-	netdev->dev.init      = isdn_net_init;
+	netdev->dev.name = netdev->local.name;
+	netdev->dev.priv = &netdev->local;
+	netdev->dev.init = isdn_net_init;
 	netdev->local.p_encap = ISDN_NET_ENCAP_RAWIP;
 	if (master) {
 		/* Device shall be a slave */
@@ -1936,7 +2046,7 @@
 	netdev->local.magic = ISDN_NET_MAGIC;
 
 #ifdef CONFIG_ISDN_PPP
-	netdev->mp_last = NULL;	/* mpqueue is empty */
+	netdev->mp_last = NULL; /* mpqueue is empty */
 	netdev->ib.next_num = 0;
 	netdev->ib.last = NULL;
 #endif
@@ -1952,18 +2062,19 @@
 	netdev->local.exclusive = -1;
 	netdev->local.ppp_slot = -1;
 	netdev->local.pppbind = -1;
-        netdev->local.sav_skb = NULL;
-        netdev->local.first_skb = NULL;
+	netdev->local.sav_skb = NULL;
+	netdev->local.first_skb = NULL;
 	netdev->local.l2_proto = ISDN_PROTO_L2_X75I;
 	netdev->local.l3_proto = ISDN_PROTO_L3_TRANS;
+	netdev->local.triggercps = 6000;
 	netdev->local.slavedelay = 10 * HZ;
 	netdev->local.srobin = &netdev->dev;
-	netdev->local.hupflags = 8;	/* Do hangup even on incoming calls */
+	netdev->local.hupflags = ISDN_INHUP;	/* Do hangup even on incoming calls */
 	netdev->local.onhtime = 10;	/* Default hangup-time for saving costs
-					   of those who forget configuring this */
+	   of those who forget configuring this */
 	netdev->local.dialmax = 1;
-        netdev->local.flags = ISDN_NET_CBHUP; /* Hangup before Callback */
-        netdev->local.cbdelay = 25;      /* Wait 5 secs before Callback */
+	netdev->local.flags = ISDN_NET_CBHUP;	/* Hangup before Callback */
+	netdev->local.cbdelay = 25;	/* Wait 5 secs before Callback */
 	/* Put into to netdev-chain */
 	netdev->next = (void *) dev->netdev;
 	dev->netdev = netdev;
@@ -1989,6 +2100,9 @@
 		/* Master must be a real interface, not a slave */
 		if (n->local.master)
 			return NULL;
+		/* Master must not be started yet */
+		if (n->dev.start)
+			return NULL;
 		return (isdn_net_new(newname, &(n->dev)));
 	}
 	return NULL;
@@ -2000,7 +2114,8 @@
  * for not overwriting existing setups. It has to get the current
  * setup first, if only selected parameters are to be changed.
  */
-int isdn_net_setcfg(isdn_net_ioctl_cfg * cfg)
+int
+isdn_net_setcfg(isdn_net_ioctl_cfg * cfg)
 {
 	isdn_net_dev *p = isdn_net_findif(cfg->name);
 	ulong features;
@@ -2020,33 +2135,34 @@
 			printk(KERN_WARNING "isdn_net: No driver with selected features\n");
 			return -ENODEV;
 		}
-                if (p->local.p_encap != cfg->p_encap)
-                        if (p->dev.start) {
-                                printk(KERN_WARNING
-                                       "%s: cannot change encap when if is up\n",
-                                       p->local.name);
-                                return -EBUSY;
-                        }
-                if (cfg->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
+		if (p->local.p_encap != cfg->p_encap)
+			if (p->dev.start) {
+				printk(KERN_WARNING
+				"%s: cannot change encap when if is up\n",
+				       p->local.name);
+				return -EBUSY;
+			}
+		if (cfg->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
 #ifndef CONFIG_ISDN_PPP
-                        printk(KERN_WARNING "%s: SyncPPP support not configured\n",
-                               p->local.name);
-                        return -EINVAL;
+			printk(KERN_WARNING "%s: SyncPPP support not configured\n",
+			       p->local.name);
+			return -EINVAL;
 #else
-                        p->dev.type = ARPHRD_PPP; /* change ARP type */
-                        p->dev.addr_len = 0;
+			p->dev.type = ARPHRD_PPP;	/* change ARP type */
+			p->dev.addr_len = 0;
 #endif
-                }
+		}
 		if (strlen(cfg->drvid)) {
 			/* A bind has been requested ... */
-			char *c,*e;
+			char *c,
+			*e;
 
 			drvidx = -1;
 			chidx = -1;
 			strcpy(drvid, cfg->drvid);
 			if ((c = strchr(drvid, ','))) {
 				/* The channel-number is appended to the driver-Id with a comma */
-				chidx = (int)simple_strtoul(c + 1,&e,10);
+				chidx = (int) simple_strtoul(c + 1, &e, 10);
 				if (e == c)
 					chidx = -1;
 				*c = '\0';
@@ -2071,9 +2187,9 @@
 			/* If binding is exclusive, try to grab the channel */
 			save_flags(flags);
 			if ((i = isdn_get_free_channel(ISDN_USAGE_NET, p->local.l2_proto,
-						  p->local.l3_proto,
-						  drvidx,
-						  chidx)) < 0) {
+						       p->local.l3_proto,
+						       drvidx,
+						       chidx)) < 0) {
 				/* Grab failed, because desired channel is in use */
 				p->local.exclusive = -1;
 				restore_flags(flags);
@@ -2089,22 +2205,23 @@
 			p->local.exclusive = -1;
 			if ((p->local.pre_device != -1) && (cfg->exclusive == -1)) {
 				isdn_unexclusive_channel(p->local.pre_device, p->local.pre_channel);
-                                isdn_free_channel(p->local.pre_device, p->local.pre_channel,ISDN_USAGE_NET);
+				isdn_free_channel(p->local.pre_device, p->local.pre_channel, ISDN_USAGE_NET);
 				drvidx = -1;
 				chidx = -1;
 			}
 		}
 		strcpy(p->local.msn, cfg->eaz);
-		p->local.pre_device  = drvidx;
+		p->local.pre_device = drvidx;
 		p->local.pre_channel = chidx;
-		p->local.onhtime     = cfg->onhtime;
-		p->local.charge      = cfg->charge;
-		p->local.l2_proto    = cfg->l2_proto;
-		p->local.l3_proto    = cfg->l3_proto;
-                p->local.cbdelay     = cfg->cbdelay;
-                p->local.dialmax     = cfg->dialmax;
-		p->local.slavedelay  = cfg->slavedelay * HZ;
-		p->local.pppbind     = cfg->pppbind;
+		p->local.onhtime = cfg->onhtime;
+		p->local.charge = cfg->charge;
+		p->local.l2_proto = cfg->l2_proto;
+		p->local.l3_proto = cfg->l3_proto;
+		p->local.cbdelay = cfg->cbdelay;
+		p->local.dialmax = cfg->dialmax;
+		p->local.triggercps = cfg->triggercps;
+		p->local.slavedelay = cfg->slavedelay * HZ;
+		p->local.pppbind = cfg->pppbind;
 		if (cfg->secure)
 			p->local.flags |= ISDN_NET_SECURE;
 		else
@@ -2114,47 +2231,63 @@
 		else
 			p->local.flags &= ~ISDN_NET_CBHUP;
 		switch (cfg->callback) {
-                        case 0:
-                                p->local.flags &= ~(ISDN_NET_CALLBACK|ISDN_NET_CBOUT);
-                                break;
-                        case 1:
-                                p->local.flags |= ISDN_NET_CALLBACK;
-                                p->local.flags &= ~ISDN_NET_CBOUT;
-                                break;
-                        case 2:
-                                p->local.flags |= ISDN_NET_CBOUT;
-                                p->local.flags &= ~ISDN_NET_CALLBACK;
-                                break;
-                }
+			case 0:
+				p->local.flags &= ~(ISDN_NET_CALLBACK | ISDN_NET_CBOUT);
+				break;
+			case 1:
+				p->local.flags |= ISDN_NET_CALLBACK;
+				p->local.flags &= ~ISDN_NET_CBOUT;
+				break;
+			case 2:
+				p->local.flags |= ISDN_NET_CBOUT;
+				p->local.flags &= ~ISDN_NET_CALLBACK;
+				break;
+		}
 		if (cfg->chargehup)
-			p->local.hupflags |= 4;
+			p->local.hupflags |= ISDN_CHARGEHUP;
 		else
-			p->local.hupflags &= ~4;
+			p->local.hupflags &= ~ISDN_CHARGEHUP;
 		if (cfg->ihup)
-			p->local.hupflags |= 8;
+			p->local.hupflags |= ISDN_INHUP;
 		else
-			p->local.hupflags &= ~8;
+			p->local.hupflags &= ~ISDN_INHUP;
+		if (cfg->chargeint > 10) {
+			p->local.hupflags |= ISDN_CHARGEHUP | ISDN_HAVECHARGE | ISDN_MANCHARGE;
+			p->local.chargeint = cfg->chargeint * HZ;
+		}
 		if (cfg->p_encap != p->local.p_encap) {
-                        if (cfg->p_encap == ISDN_NET_ENCAP_RAWIP) {
-                                p->dev.hard_header         = NULL;
-                                p->dev.header_cache_bind   = NULL;
-                                p->dev.header_cache_update = NULL;
-                                p->dev.flags               = IFF_NOARP;
-                        } else {
-                                p->dev.hard_header = isdn_net_header;
-                                if (cfg->p_encap == ISDN_NET_ENCAP_ETHER) {
-                                        p->dev.header_cache_bind   = p->local.org_hcb;
-                                        p->dev.header_cache_update = p->local.org_hcu;
-                                        p->dev.flags = IFF_BROADCAST | IFF_MULTICAST;
-                                } else {
-                                        p->dev.header_cache_bind   = NULL;
-                                        p->dev.header_cache_update = NULL;
-                                        p->dev.flags               = IFF_NOARP;
-                                }
-                        }
-                }
-		p->local.p_encap     = cfg->p_encap;
-                return 0;
+			if (cfg->p_encap == ISDN_NET_ENCAP_RAWIP) {
+				p->dev.hard_header = NULL;
+#if (LINUX_VERSION_CODE < 0x02010F)
+				p->dev.header_cache_bind = NULL;
+#else
+				p->dev.hard_header_cache = NULL;
+#endif
+				p->dev.header_cache_update = NULL;
+				p->dev.flags = IFF_NOARP;
+			} else {
+				p->dev.hard_header = isdn_net_header;
+				if (cfg->p_encap == ISDN_NET_ENCAP_ETHER) {
+#if (LINUX_VERSION_CODE < 0x02010F)
+					p->dev.header_cache_bind = p->local.org_hcb;
+#else
+					p->dev.hard_header_cache = p->local.org_hhc;
+#endif
+					p->dev.header_cache_update = p->local.org_hcu;
+					p->dev.flags = IFF_BROADCAST | IFF_MULTICAST;
+				} else {
+#if (LINUX_VERSION_CODE < 0x02010F)
+					p->dev.header_cache_bind = NULL;
+#else
+					p->dev.hard_header_cache = NULL;
+#endif
+					p->dev.header_cache_update = NULL;
+					p->dev.flags = IFF_NOARP;
+				}
+			}
+		}
+		p->local.p_encap = cfg->p_encap;
+		return 0;
 	}
 	return -ENODEV;
 }
@@ -2162,7 +2295,8 @@
 /*
  * Perform get-interface-parameters.ioctl
  */
-int isdn_net_getcfg(isdn_net_ioctl_cfg * cfg)
+int
+isdn_net_getcfg(isdn_net_ioctl_cfg * cfg)
 {
 	isdn_net_dev *p = isdn_net_findif(cfg->name);
 
@@ -2181,16 +2315,19 @@
 		cfg->p_encap = p->local.p_encap;
 		cfg->secure = (p->local.flags & ISDN_NET_SECURE) ? 1 : 0;
 		cfg->callback = 0;
-                if (p->local.flags & ISDN_NET_CALLBACK)
-                        cfg->callback = 1;
-                if (p->local.flags & ISDN_NET_CBOUT)
-                        cfg->callback = 2;
+		if (p->local.flags & ISDN_NET_CALLBACK)
+			cfg->callback = 1;
+		if (p->local.flags & ISDN_NET_CBOUT)
+			cfg->callback = 2;
 		cfg->cbhup = (p->local.flags & ISDN_NET_CBHUP) ? 1 : 0;
 		cfg->chargehup = (p->local.hupflags & 4) ? 1 : 0;
 		cfg->ihup = (p->local.hupflags & 8) ? 1 : 0;
-                cfg->cbdelay = p->local.cbdelay;
-                cfg->dialmax = p->local.dialmax;
+		cfg->cbdelay = p->local.cbdelay;
+		cfg->dialmax = p->local.dialmax;
+		cfg->triggercps = p->local.triggercps;
 		cfg->slavedelay = p->local.slavedelay / HZ;
+		cfg->chargeint = (p->local.hupflags & ISDN_CHARGEHUP) ?
+		    (p->local.chargeint / HZ) : 0;
 		cfg->pppbind = p->local.pppbind;
 		if (p->local.slave)
 			strcpy(cfg->slave, ((isdn_net_local *) p->local.slave->priv)->name);
@@ -2208,7 +2345,8 @@
 /*
  * Add a phone-number to an interface.
  */
-int isdn_net_addphone(isdn_net_ioctl_phone * phone)
+int
+isdn_net_addphone(isdn_net_ioctl_phone * phone)
 {
 	isdn_net_dev *p = isdn_net_findif(phone->name);
 	isdn_net_phone *n;
@@ -2229,7 +2367,8 @@
 /*
  * Return a string of all phone-numbers of an interface.
  */
-int isdn_net_getphones(isdn_net_ioctl_phone * phone, char *phones)
+int
+isdn_net_getphones(isdn_net_ioctl_phone * phone, char *phones)
 {
 	isdn_net_dev *p = isdn_net_findif(phone->name);
 	int inout = phone->outgoing & 1;
@@ -2244,22 +2383,21 @@
 	save_flags(flags);
 	cli();
 	inout &= 1;
-        for (n = p->local.phone[inout]; n; n = n->next) {
+	for (n = p->local.phone[inout]; n; n = n->next) {
 		if (more) {
 			put_user(' ', phones++);
 			count++;
 		}
-		if ((ret = verify_area(VERIFY_WRITE, (void *) phones, strlen(n->num) + 1))) {
+		if ((ret = copy_to_user(phones, n->num, strlen(n->num) + 1))) {
 			restore_flags(flags);
 			return ret;
 		}
-		copy_to_user(phones, n->num, strlen(n->num) + 1);
 		phones += strlen(n->num);
 		count += strlen(n->num);
 		more = 1;
 	}
-        put_user(0,phones);
-        count++;
+	put_user(0, phones);
+	count++;
 	restore_flags(flags);
 	return count;
 }
@@ -2268,18 +2406,24 @@
  * Delete a phone-number from an interface.
  */
 
-int isdn_net_delphone(isdn_net_ioctl_phone * phone)
+int
+isdn_net_delphone(isdn_net_ioctl_phone * phone)
 {
 	isdn_net_dev *p = isdn_net_findif(phone->name);
 	int inout = phone->outgoing & 1;
 	isdn_net_phone *n;
 	isdn_net_phone *m;
+	int flags;
 
 	if (p) {
+		save_flags(flags);
+		cli();
 		n = p->local.phone[inout];
 		m = NULL;
 		while (n) {
 			if (!strcmp(n->num, phone->phone)) {
+				if (p->local.dial == n)
+					p->local.dial = n->next;
 				if (m)
 					m->next = n->next;
 				else
@@ -2290,6 +2434,7 @@
 			m = n;
 			n = (isdn_net_phone *) n->next;
 		}
+		restore_flags(flags);
 		return -EINVAL;
 	}
 	return -ENODEV;
@@ -2298,7 +2443,8 @@
 /*
  * Delete all phone-numbers of an interface.
  */
-static int isdn_net_rmallphone(isdn_net_dev * p)
+static int
+isdn_net_rmallphone(isdn_net_dev * p)
 {
 	isdn_net_phone *n;
 	isdn_net_phone *m;
@@ -2316,6 +2462,7 @@
 		}
 		p->local.phone[i] = NULL;
 	}
+	p->local.dial = NULL;
 	restore_flags(flags);
 	return 0;
 }
@@ -2323,7 +2470,8 @@
 /*
  * Force a hangup of a network-interface.
  */
-int isdn_net_force_hangup(char *name)
+int
+isdn_net_force_hangup(char *name)
 {
 	isdn_net_dev *p = isdn_net_findif(name);
 	struct device *q;
@@ -2331,13 +2479,13 @@
 	if (p) {
 		if (p->local.isdn_device < 0)
 			return 1;
-		isdn_net_hangup(&p->dev);
 		q = p->local.slave;
 		/* If this interface has slaves, do a hangup for them also. */
 		while (q) {
 			isdn_net_hangup(q);
 			q = (((isdn_net_local *) q->priv)->slave);
 		}
+		isdn_net_hangup(&p->dev);
 		return 0;
 	}
 	return -ENODEV;
@@ -2346,7 +2494,8 @@
 /*
  * Helper-function for isdn_net_rm: Do the real work.
  */
-static int isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q)
+static int
+isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q)
 {
 	int flags;
 
@@ -2399,9 +2548,6 @@
 		isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 0);
 	restore_flags(flags);
 
-#ifdef CONFIG_ISDN_MPP
-	isdn_ppp_free_mpqueue(p);	/* still necessary? */
-#endif
 	kfree(p);
 
 	return 0;
@@ -2410,7 +2556,8 @@
 /*
  * Remove a single network-interface.
  */
-int isdn_net_rm(char *name)
+int
+isdn_net_rm(char *name)
 {
 	isdn_net_dev *p;
 	isdn_net_dev *q;
@@ -2433,7 +2580,8 @@
 /*
  * Remove all network-interfaces
  */
-int isdn_net_rmall(void)
+int
+isdn_net_rmall(void)
 {
 	int flags;
 	int ret;
@@ -2446,7 +2594,7 @@
 			/* Remove master-devices only, slaves get removed with their master */
 			if ((ret = isdn_net_realrm(dev->netdev, NULL))) {
 				restore_flags(flags);
-			        return ret;
+				return ret;
 			}
 		}
 	}
@@ -2455,17 +2603,18 @@
 	return 0;
 }
 
-/* 
+/*
  * helper function to flush device queues
  * the better place would be net/core/dev.c
  */
-void dev_purge_queues(struct device *dev)
+static void
+dev_purge_queues(struct device *dev)
 {
 	int i;
-	for(i=0;i<DEV_NUMBUFFS;i++) {
+	for (i = 0; i < DEV_NUMBUFFS; i++) {
 		struct sk_buff *skb;
-		while((skb=skb_dequeue(&dev->buffs[i])))
-				dev_kfree_skb(skb,FREE_WRITE);
-        }
-	
+		while ((skb = skb_dequeue(&dev->buffs[i])))
+			dev_kfree_skb(skb, FREE_WRITE);
+	}
+
 }
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_net.h linux/drivers/isdn/isdn_net.h
--- v2.0.30/linux/drivers/isdn/isdn_net.h	Sun Apr 21 01:56:14 1996
+++ linux/drivers/isdn/isdn_net.h	Mon Aug  4 17:34:00 1997
@@ -1,11 +1,11 @@
-/* $Id: isdn_net.h,v 1.2 1996/04/20 16:29:43 fritz Exp $
- *
+/* $Id: isdn_net.h,v 1.5 1997/02/10 20:12:47 fritz Exp $
+
  * header for Linux ISDN subsystem, network related functions (linklevel).
  *
  * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
  * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
  * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
- * 
+ *
  * 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)
@@ -18,9 +18,18 @@
  *
  * 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. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_net.h,v $
+ * Revision 1.5  1997/02/10 20:12:47  fritz
+ * Changed interface for reporting incoming calls.
+ *
+ * Revision 1.4  1997/02/03 23:16:48  fritz
+ * Removed isdn_net_receive_callback prototype.
+ *
+ * Revision 1.3  1997/01/17 01:19:30  fritz
+ * Applied chargeint patch.
+ *
  * Revision 1.2  1996/04/20 16:29:43  fritz
  * Misc. typos
  *
@@ -29,24 +38,30 @@
  *
  */
 
-extern char*         isdn_net_new(char *, struct device *);
-extern char*         isdn_net_newslave(char *);
-extern int           isdn_net_rm(char *);
-extern int           isdn_net_rmall(void);
-extern int           isdn_net_stat_callback(int, int);
-extern int           isdn_net_receive_callback(int, u_char *, int);
-extern int           isdn_net_setcfg(isdn_net_ioctl_cfg *);
-extern int           isdn_net_getcfg(isdn_net_ioctl_cfg *);
-extern int           isdn_net_addphone(isdn_net_ioctl_phone *);
-extern int           isdn_net_getphones(isdn_net_ioctl_phone *, char *);
-extern int           isdn_net_delphone(isdn_net_ioctl_phone *);
-extern int           isdn_net_find_icall(int, int, int, char *);
-extern void          isdn_net_hangup(struct device *);
-extern void          isdn_net_dial(void);
-extern void          isdn_net_autohup(void);
-extern int           isdn_net_force_hangup(char *);
-extern int           isdn_net_force_dial(char *);
-extern isdn_net_dev* isdn_net_findif(char *);
-extern int           isdn_net_send_skb(struct device *, isdn_net_local *, 
-				      struct sk_buff *);
-extern int           isdn_net_rcv_skb(int, struct sk_buff *); 
+			      /* Definitions for hupflags:                */
+#define ISDN_WAITCHARGE  1      /* did not get a charge info yet            */
+#define ISDN_HAVECHARGE  2      /* We know a charge info                    */
+#define ISDN_CHARGEHUP   4      /* We want to use the charge mechanism      */
+#define ISDN_INHUP       8      /* Even if incoming, close after huptimeout */
+#define ISDN_MANCHARGE  16      /* Charge Interval manually set             */
+
+extern char *isdn_net_new(char *, struct device *);
+extern char *isdn_net_newslave(char *);
+extern int isdn_net_rm(char *);
+extern int isdn_net_rmall(void);
+extern int isdn_net_stat_callback(int, int);
+extern int isdn_net_setcfg(isdn_net_ioctl_cfg *);
+extern int isdn_net_getcfg(isdn_net_ioctl_cfg *);
+extern int isdn_net_addphone(isdn_net_ioctl_phone *);
+extern int isdn_net_getphones(isdn_net_ioctl_phone *, char *);
+extern int isdn_net_delphone(isdn_net_ioctl_phone *);
+extern int isdn_net_find_icall(int, int, int, setup_parm);
+extern void isdn_net_hangup(struct device *);
+extern void isdn_net_dial(void);
+extern void isdn_net_autohup(void);
+extern int isdn_net_force_hangup(char *);
+extern int isdn_net_force_dial(char *);
+extern isdn_net_dev *isdn_net_findif(char *);
+extern int isdn_net_send_skb(struct device *, isdn_net_local *,
+			     struct sk_buff *);
+extern int isdn_net_rcv_skb(int, struct sk_buff *);
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_ppp.c linux/drivers/isdn/isdn_ppp.c
--- v2.0.30/linux/drivers/isdn/isdn_ppp.c	Tue Nov 12 22:36:19 1996
+++ linux/drivers/isdn/isdn_ppp.c	Mon Aug  4 17:34:00 1997
@@ -1,9 +1,9 @@
-/* $Id: isdn_ppp.c,v 1.20 1996/10/30 12:21:58 fritz Exp $
+/* $Id: isdn_ppp.c,v 1.28 1997/06/17 13:05:57 hipp Exp $
  *
  * Linux ISDN subsystem, functions for synchronous PPP (linklevel).
  *
  * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
- * 
+ *
  * 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)
@@ -16,9 +16,42 @@
  *
  * 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. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_ppp.c,v $
+ * Revision 1.28  1997/06/17 13:05:57  hipp
+ * Applied Eric's underflow-patches (slightly modified)
+ * more compression changes (but disabled at the moment)
+ * changed one copy_to_user() to run with enabled IRQs
+ * a few MP changes
+ * changed 'proto' handling in the isdn_ppp receive code
+ *
+ * Revision 1.27  1997/03/30 16:51:17  calle
+ * changed calls to copy_from_user/copy_to_user and removed verify_area
+ * were possible.
+ *
+ * Revision 1.26  1997/02/23 16:53:44  hipp
+ * minor cleanup
+ * some initial changes for future PPP compresion
+ * added AC,PC compression for outgoing frames
+ *
+ * Revision 1.25  1997/02/12 20:37:35  hipp
+ * New ioctl() PPPIOCGCALLINFO, minor cleanup
+ *
+ * Revision 1.24  1997/02/11 18:32:56  fritz
+ * Bugfix in isdn_ppp_free_mpqueue().
+ *
+ * Revision 1.23  1997/02/10 11:12:19  fritz
+ * More changes for Kernel 2.1.X compatibility.
+ *
+ * Revision 1.22  1997/02/06 15:03:51  hipp
+ * changed GFP_KERNEL kmalloc to GFP_ATOMIC in isdn_ppp_fill_mpqueue()
+ *
+ * Revision 1.21  1997/02/03 23:29:38  fritz
+ * Reformatted according CodingStyle
+ * Bugfix: removed isdn_ppp_skb_destructor, used by upper layers.
+ * Misc changes for Kernel 2.1.X compatibility.
+ *
  * Revision 1.20  1996/10/30 12:21:58  fritz
  * Cosmetic fix: Compiler warning when compiling without MPP.
  *
@@ -92,70 +125,91 @@
 
 /* TODO: right tbusy handling when using MP */
 
+/*
+ * experimental for dynamic addressing: readdress IP frames
+ */
 #undef ISDN_SYNCPPP_READDRESS
 
 #include <linux/config.h>
 #define __NO_VERSION__
 #include <linux/module.h>
+#include <linux/version.h>
 #include <linux/isdn.h>
+#if (LINUX_VERSION_CODE >= 0x020117)
+#include <asm/poll.h>
+#endif
 #include "isdn_common.h"
 #include "isdn_ppp.h"
 #include "isdn_net.h"
 
 #ifndef PPP_IPX
-#define PPP_IPX 0x002b 
+#define PPP_IPX 0x002b
 #endif
 
 /* set this if you use dynamic addressing */
- 
+
 /* Prototypes */
-static int isdn_ppp_fill_rq(unsigned char *buf, int len,int proto, int slot);
+static int isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot);
 static int isdn_ppp_closewait(int slot);
 static void isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp,
-			struct sk_buff *skb, int proto);
+				 struct sk_buff *skb, int proto);
 static int isdn_ppp_if_get_unit(char *namebuf);
+static int isdn_ppp_set_compressor(struct ippp_struct *is,int num);
+static struct sk_buff *isdn_ppp_decompress(struct sk_buff *,
+				struct ippp_struct *,struct ippp_struct *);
+static void isdn_ppp_receive_ccp(isdn_net_dev * net_dev, isdn_net_local * lp,
+				struct sk_buff *skb);
+static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto,
+	struct ippp_struct *is,struct ippp_struct *master,int type);
 
 #ifdef CONFIG_ISDN_MPP
 static int isdn_ppp_bundle(struct ippp_struct *, int unit);
 static void isdn_ppp_mask_queue(isdn_net_dev * dev, long mask);
 static void isdn_ppp_cleanup_mpqueue(isdn_net_dev * dev, long min);
-static void isdn_ppp_cleanup_sqqueue(isdn_net_dev * dev,isdn_net_local *, long min);
+static void isdn_ppp_cleanup_sqqueue(isdn_net_dev * dev, isdn_net_local *, long min);
+static void isdn_ppp_free_sqqueue(isdn_net_dev *);
 static int isdn_ppp_fill_mpqueue(isdn_net_dev *, struct sk_buff **skb,
-		int BEbyte, long *sqno, int min_sqno);
+				 int BEbyte, long *sqno, int min_sqno);
+static void isdn_ppp_free_mpqueue(isdn_net_dev *);
 #endif
 
-char *isdn_ppp_revision              = "$Revision: 1.20 $";
+char *isdn_ppp_revision = "$Revision: 1.28 $";
 
-struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
+static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
+static struct isdn_ppp_compressor *ipc_head = NULL;
 
 extern int isdn_net_force_dial_lp(isdn_net_local *);
 
 /*
  * frame log (debug)
  */
-static void isdn_ppp_frame_log(char *info,char *data,int len,int maxlen)
+static void
+isdn_ppp_frame_log(char *info, char *data, int len, int maxlen)
 {
-	int cnt,j,i;
+	int cnt,
+	 j,
+	 i;
 	char buf[80];
 
-	if(len < maxlen)
+	if (len < maxlen)
 		maxlen = len;
-	
-	for(i=0,cnt=0;cnt<maxlen;i++) {
-		for(j=0;j<16 && cnt<maxlen;j++,cnt++)
-			sprintf(buf+j*3,"%02x ",(unsigned char) data[cnt]);
-		printk(KERN_DEBUG "%s[%d]: %s\n",info,i,buf);
+
+	for (i = 0, cnt = 0; cnt < maxlen; i++) {
+		for (j = 0; j < 16 && cnt < maxlen; j++, cnt++)
+			sprintf(buf + j * 3, "%02x ", (unsigned char) data[cnt]);
+		printk(KERN_DEBUG "%s[%d]: %s\n", info, i, buf);
 	}
 }
 
 /*
- * unbind isdn_net_local <=> ippp-device 
+ * unbind isdn_net_local <=> ippp-device
  * note: it can happen, that we hangup/free the master before the slaves
  */
-int isdn_ppp_free(isdn_net_local *lp)
+int
+isdn_ppp_free(isdn_net_local * lp)
 {
 #ifdef CONFIG_ISDN_MPP
-	isdn_net_local *master_lp=lp;
+	isdn_net_local *master_lp = lp;
 #endif
 	unsigned long flags;
 	struct ippp_struct *is;
@@ -168,14 +222,14 @@
 	save_flags(flags);
 	cli();
 #ifdef CONFIG_ISDN_MPP
-	if(lp->master)
+	if (lp->master)
 		master_lp = (isdn_net_local *) lp->master->priv;
 
 	lp->last->next = lp->next;
 	lp->next->last = lp->last;
-	if(master_lp->netdev->queue == lp) {
+	if (master_lp->netdev->queue == lp) {
 		master_lp->netdev->queue = lp->next;
-		if(lp->next == lp) {	/* last link in queue? */
+		if (lp->next == lp) {	/* last link in queue? */
 			master_lp->netdev->ib.bundled = 0;
 			isdn_ppp_free_mpqueue(master_lp->netdev);
 			isdn_ppp_free_sqqueue(master_lp->netdev);
@@ -184,21 +238,21 @@
 	lp->next = lp->last = lp;	/* (re)set own pointers */
 #endif
 
-	if( (is->state & IPPP_CONNECT) )
+	if ((is->state & IPPP_CONNECT))
 		isdn_ppp_closewait(lp->ppp_slot);	/* force wakeup on ippp device */
-	else if(is->state & IPPP_ASSIGNED)
+	else if (is->state & IPPP_ASSIGNED)
 		is->state = IPPP_OPEN;	/* fallback to 'OPEN but not ASSIGEND' staet */
-		
 
-	if(is->debug & 0x1)
-		printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_slot, (long) lp,(long) is->lp);
 
-	is->lp = NULL;	/* link is down .. set lp to NULL */
+	if (is->debug & 0x1)
+		printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_slot, (long) lp, (long) is->lp);
+
+	is->lp = NULL;          /* link is down .. set lp to NULL */
 #ifdef ISDN_SYNCPPP_READDRESS
 	is->old_pa_addr = 0x0;
 	is->old_pa_dstaddr = 0x0;
 #endif
-	lp->ppp_slot = -1;			/* is this OK ?? */
+	lp->ppp_slot = -1;      /* is this OK ?? */
 	restore_flags(flags);
 
 	return 0;
@@ -207,7 +261,8 @@
 /*
  * bind isdn_net_local <=> ippp-device
  */
-int isdn_ppp_bind(isdn_net_local * lp)
+int
+isdn_ppp_bind(isdn_net_local * lp)
 {
 	int i;
 	int unit = 0;
@@ -220,14 +275,13 @@
 	save_flags(flags);
 	cli();
 
-	if(lp->pppbind < 0)	/* device bounded to ippp device ? */
-	{
- 		isdn_net_dev *net_dev = dev->netdev;
+	if (lp->pppbind < 0) {  /* device bounded to ippp device ? */
+		isdn_net_dev *net_dev = dev->netdev;
 		char exclusive[ISDN_MAX_CHANNELS];	/* exclusive flags */
-		memset(exclusive,0,ISDN_MAX_CHANNELS);
+		memset(exclusive, 0, ISDN_MAX_CHANNELS);
 		while (net_dev) {	/* step through net devices to find exclusive minors */
 			isdn_net_local *lp = &net_dev->local;
-			if(lp->pppbind >= 0)
+			if (lp->pppbind >= 0)
 				exclusive[lp->pppbind] = 1;
 			net_dev = net_dev->next;
 		}
@@ -235,14 +289,13 @@
 		 * search a free device / slot
 		 */
 		for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-			if (ippp_table[i]->state == IPPP_OPEN && !exclusive[ippp_table[i]->minor]) { /* OPEN, but not connected! */
+			if (ippp_table[i]->state == IPPP_OPEN && !exclusive[ippp_table[i]->minor]) {	/* OPEN, but not connected! */
 				break;
 			}
 		}
-	}
-	else {
-		for(i=0;i<ISDN_MAX_CHANNELS;i++)
-			if(ippp_table[i]->minor == lp->pppbind && ippp_table[i]->state == IPPP_OPEN)
+	} else {
+		for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+			if (ippp_table[i]->minor == lp->pppbind && ippp_table[i]->state == IPPP_OPEN)
 				break;
 	}
 
@@ -251,13 +304,11 @@
 		printk(KERN_WARNING "isdn_ppp_bind: Can't find usable ippp device.\n");
 		return -1;
 	}
-
-	unit = isdn_ppp_if_get_unit(lp->name); /* get unit number from interface name .. ugly! */
-	if(unit < 0) {
-		printk(KERN_ERR "isdn_ppp_bind: illegal interface name %s.\n",lp->name);
+	unit = isdn_ppp_if_get_unit(lp->name);	/* get unit number from interface name .. ugly! */
+	if (unit < 0) {
+		printk(KERN_ERR "isdn_ppp_bind: illegal interface name %s.\n", lp->name);
 		return -1;
 	}
-
 	lp->ppp_slot = i;
 	is = ippp_table[i];
 	is->lp = lp;
@@ -274,9 +325,10 @@
  * (wakes up daemon after B-channel connect)
  */
 
-void isdn_ppp_wakeup_daemon(isdn_net_local *lp)
+void
+isdn_ppp_wakeup_daemon(isdn_net_local * lp)
 {
-	if(lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS)
+	if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS)
 		return;
 
 	ippp_table[lp->ppp_slot]->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK;
@@ -287,10 +339,11 @@
 
 /*
  * there was a hangup on the netdevice
- * force wakeup of the ippp device 
+ * force wakeup of the ippp device
  * go into 'device waits for release' state
  */
-static int isdn_ppp_closewait(int slot)
+static int
+isdn_ppp_closewait(int slot)
 {
 	struct ippp_struct *is;
 
@@ -309,56 +362,64 @@
  * isdn_ppp_find_slot / isdn_ppp_free_slot
  */
 
-static int isdn_ppp_get_slot(void)
+static int
+isdn_ppp_get_slot(void)
 {
 	int i;
-	for(i=0;i<ISDN_MAX_CHANNELS;i++) {
-		if(!ippp_table[i]->state)
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		if (!ippp_table[i]->state)
 			return i;
 	}
 	return -1;
 }
 
 /*
- * isdn_ppp_open 
+ * isdn_ppp_open
  */
 
-int isdn_ppp_open(int min, struct file *file)
+int
+isdn_ppp_open(int min, struct file *file)
 {
 	int slot;
 	struct ippp_struct *is;
 
-	if(min < 0 || min > ISDN_MAX_CHANNELS)
+	if (min < 0 || min > ISDN_MAX_CHANNELS)
 		return -ENODEV;
 
 	slot = isdn_ppp_get_slot();
-	if(slot < 0) {
+	if (slot < 0) {
 		return -EBUSY;
 	}
 	is = file->private_data = ippp_table[slot];
 
-	if(is->debug & 0x1)
-		printk(KERN_DEBUG "ippp, open, slot: %d, minor: %d, state: %04x\n",slot, min,is->state);
+	if (is->debug & 0x1)
+		printk(KERN_DEBUG "ippp, open, slot: %d, minor: %d, state: %04x\n", slot, min, is->state);
 
-	is->lp = 0;
-	is->mp_seqno = 0;  /* MP sequence number */
-	is->pppcfg = 0;    /* ppp configuration */
-	is->mpppcfg = 0;   /* mppp configuration */
-	is->range = 0x1000000;	/* MP: 24 bit range */
+	/* compression stuff */
+	is->compressor = NULL;
+	is->decomp_stat = is->comp_stat = NULL;
+	is->link_compressor = NULL;
+	is->link_decomp_stat = is->link_comp_stat = NULL;
+
+	is->lp = NULL;
+	is->mp_seqno = 0;       /* MP sequence number */
+	is->pppcfg = 0;         /* ppp configuration */
+	is->mpppcfg = 0;        /* mppp configuration */
+	is->range = 0x1000000;  /* MP: 24 bit range */
 	is->last_link_seqno = -1;	/* MP: maybe set to Bundle-MIN, when joining a bundle ?? */
-	is->unit = -1;	/* set, when we have our interface */
-	is->mru = 1524;	/* MRU, default 1524 */
-	is->maxcid = 16;	/* VJ: maxcid */
+	is->unit = -1;          /* set, when we have our interface */
+	is->mru = 1524;         /* MRU, default 1524 */
+	is->maxcid = 16;        /* VJ: maxcid */
 	is->tk = current;
-	is->wq = NULL;    /* read() wait queue */
-	is->wq1 = NULL;   /* select() wait queue */
-	is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */
+	is->wq = NULL;          /* read() wait queue */
+	is->wq1 = NULL;         /* select() wait queue */
+	is->first = is->rq + NUM_RCV_BUFFS - 1;	/* receive queue */
 	is->last = is->rq;
 	is->minor = min;
 #ifdef CONFIG_ISDN_PPP_VJ
-        /*
-         * VJ header compression init
-         */
+	/*
+	 * VJ header compression init
+	 */
 	is->slcomp = slhc_init(16, 16);	/* not necessary for 2. link in bundle */
 #endif
 
@@ -370,7 +431,8 @@
 /*
  * release ippp device
  */
-void isdn_ppp_release(int min, struct file *file)
+void
+isdn_ppp_release(int min, struct file *file)
 {
 	int i;
 	struct ippp_struct *is;
@@ -379,14 +441,14 @@
 		return;
 	is = file->private_data;
 
-	if(is->debug & 0x1)
+	if (is->debug & 0x1)
 		printk(KERN_DEBUG "ippp: release, minor: %d %lx\n", min, (long) is->lp);
 
-	if (is->lp) {	/* a lp address says: this link is still up */
+	if (is->lp) {           /* a lp address says: this link is still up */
 		isdn_net_dev *p = is->lp->netdev;
-		
-		is->state &= ~IPPP_CONNECT; /* -> effect: no call of wakeup */
-		/* 
+
+		is->state &= ~IPPP_CONNECT;	/* -> effect: no call of wakeup */
+		/*
 		 * isdn_net_hangup() calls isdn_ppp_free()
 		 * isdn_ppp_free() sets is->lp to NULL and lp->ppp_slot to -1
 		 * removing the IPPP_CONNECT flag omits calling of isdn_ppp_wakeup_daemon()
@@ -399,8 +461,8 @@
 			is->rq[i].buf = NULL;
 		}
 	}
-        is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */
-        is->last = is->rq;
+	is->first = is->rq + NUM_RCV_BUFFS - 1;	/* receive queue */
+	is->last = is->rq;
 
 #ifdef CONFIG_ISDN_PPP_VJ
 	slhc_free(is->slcomp);
@@ -413,216 +475,286 @@
 /*
  * get_arg .. ioctl helper
  */
-static int get_arg(void *b,void *val,int len)
+static int
+get_arg(void *b, void *val, int len)
 {
 	int r;
-	if(len <= 0)
-		len = sizeof(unsigned long); 
-	if ((r = verify_area(VERIFY_READ, (void *) b, len )))
-		 return r;
-	copy_from_user((void *) val, b, len );
+	if (len <= 0)
+		len = sizeof(unsigned long);
+	if ((r = copy_from_user((void *) val, b, len)))
+		return r;
 	return 0;
 }
 
 /*
  * set arg .. ioctl helper
  */
-static int set_arg(void *b, unsigned long val,void *str)
+static int
+set_arg(void *b, unsigned long val, void *str)
 {
 	int r;
-	if(!str) {
-		if ((r = verify_area(VERIFY_WRITE, b, 4 )))
-			 return r;
-		copy_to_user(b, (void *) &val, 4 );
-	}
-	else {
-		if ((r = verify_area(VERIFY_WRITE, b,val)))
+	if (!str) {
+		if ((r = copy_to_user(b, (void *) &val, 4)))
+			return r;
+	} else {
+		if ((r = copy_to_user(b, str, val)))
 			return r;
-		copy_to_user(b,str,val);
 	}
 	return 0;
 }
 
 /*
- * ippp device ioctl 
+ * ippp device ioctl
  */
-int isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg)
+int
+isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg)
 {
 	unsigned long val;
-	int r;
+	int num,r;
 	struct ippp_struct *is;
+	isdn_net_local *lp;
 
-	is = file->private_data;
+	is = (struct ippp_struct *) file->private_data;
+	lp = is->lp;
 
-	if(is->debug & 0x1)
-		printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x state: %x\n",min,cmd,is->state);
+	if (is->debug & 0x1)
+		printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x state: %x\n", min, cmd, is->state);
 
 	if (!(is->state & IPPP_OPEN))
 		return -EINVAL;
 
 	switch (cmd) {
-	case PPPIOCBUNDLE:
+		case PPPIOCBUNDLE:
 #ifdef CONFIG_ISDN_MPP
-		if( !(is->state & IPPP_CONNECT) )
-			return -EINVAL;
-		if ((r = get_arg((void *) arg, &val,0)))
-			return r;
-		printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n",
-                        (int) min, (int) is->unit, (int) val);
-		return isdn_ppp_bundle(is, val);
+			if (!(is->state & IPPP_CONNECT))
+				return -EINVAL;
+			if ((r = get_arg((void *) arg, &val, 0)))
+				return r;
+			printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n",
+			       (int) min, (int) is->unit, (int) val);
+			return isdn_ppp_bundle(is, val);
 #else
-		return -1;
+			return -1;
 #endif
-		break;
-	case PPPIOCGUNIT:	/* get ppp/isdn unit number */
-		if ((r = set_arg((void *) arg, is->unit,NULL)))
-			return r;
-		break;
-	case PPPIOCGMPFLAGS:	/* get configuration flags */
-		if ((r = set_arg((void *) arg, is->mpppcfg,NULL)))
-			return r;
-		break;
-	case PPPIOCSMPFLAGS:	/* set configuration flags */
-		if ((r = get_arg((void *) arg, &val,0)))
-			return r;
-		is->mpppcfg = val;
-		break;
-	case PPPIOCGFLAGS:	/* get configuration flags */
-		if ((r = set_arg((void *) arg, is->pppcfg,NULL)))
-			return r;
-		break;
-	case PPPIOCSFLAGS:	/* set configuration flags */
-		if ((r = get_arg((void *) arg, &val,0))) {
-			return r;
-		}
-		if (val & SC_ENABLE_IP && !(is->pppcfg & SC_ENABLE_IP) && (is->state & IPPP_CONNECT) ) {
-			isdn_net_local *lp = is->lp;
-			if(lp) {
-				lp->netdev->dev.tbusy = 0;
-				mark_bh(NET_BH); /* OK .. we are ready to send buffers */
+			break;
+		case PPPIOCGUNIT:	/* get ppp/isdn unit number */
+			if ((r = set_arg((void *) arg, is->unit, NULL)))
+				return r;
+			break;
+		case PPPIOCGMPFLAGS:	/* get configuration flags */
+			if ((r = set_arg((void *) arg, is->mpppcfg, NULL)))
+				return r;
+			break;
+		case PPPIOCSMPFLAGS:	/* set configuration flags */
+			if ((r = get_arg((void *) arg, &val, 0)))
+				return r;
+			is->mpppcfg = val;
+			break;
+		case PPPIOCGFLAGS:	/* get configuration flags */
+			if ((r = set_arg((void *) arg, is->pppcfg, NULL)))
+				return r;
+			break;
+		case PPPIOCSFLAGS:	/* set configuration flags */
+			if ((r = get_arg((void *) arg, &val, 0))) {
+				return r;
 			}
-		}
-		is->pppcfg = val;
-		break;
+			if (val & SC_ENABLE_IP && !(is->pppcfg & SC_ENABLE_IP) && (is->state & IPPP_CONNECT)) {
+				if (lp) {
+					lp->netdev->dev.tbusy = 0;
+					mark_bh(NET_BH);	/* OK .. we are ready to send buffers */
+				}
+			}
+			is->pppcfg = val;
+			break;
 #if 0
-	case PPPIOCGSTAT:	/* read PPP statistic information */
-		break;
+		case PPPIOCGSTAT:	/* read PPP statistic information */
+			break;
 #endif
-	case PPPIOCGIDLE:	/* get idle time information */
-		if(is->lp)
-		{
-			struct ppp_idle pidle;
-			pidle.xmit_idle = pidle.recv_idle = is->lp->huptimer;
-			if((r = set_arg((void *) arg,sizeof(struct ppp_idle),&pidle)))
-				return r;	
-		}
-		break;
-	case PPPIOCSMRU:	/* set receive unit size for PPP */
-		if ((r = get_arg((void *) arg, &val,0)))
-			return r;
-		is->mru = val;
-		break;
-	case PPPIOCSMPMRU:
-		break;
-	case PPPIOCSMPMTU:
-		break;
-	case PPPIOCSMAXCID:	/* set the maximum compression slot id */
-		if ((r = get_arg((void *) arg, &val,0)))
-			return r;
-		val++;
-		if(is->maxcid != val) {
+		case PPPIOCGIDLE:	/* get idle time information */
+			if (lp) {
+				struct ppp_idle pidle;
+				pidle.xmit_idle = pidle.recv_idle = lp->huptimer;
+				if ((r = set_arg((void *) arg, sizeof(struct ppp_idle), &pidle)))
+					 return r;
+			}
+			break;
+		case PPPIOCSMRU:	/* set receive unit size for PPP */
+			if ((r = get_arg((void *) arg, &val, 0)))
+				return r;
+			is->mru = val;
+			break;
+		case PPPIOCSMPMRU:
+			break;
+		case PPPIOCSMPMTU:
+			break;
+		case PPPIOCSMAXCID:	/* set the maximum compression slot id */
+			if ((r = get_arg((void *) arg, &val, 0)))
+				return r;
+			val++;
+			if (is->maxcid != val) {
 #ifdef CONFIG_ISDN_PPP_VJ
-			struct slcompress *sltmp;
+				struct slcompress *sltmp;
 #endif
-			if(is->debug & 0x1)
-				printk(KERN_DEBUG "ippp, ioctl: changed MAXCID to %ld\n",val);
-			is->maxcid = val;
+				if (is->debug & 0x1)
+					printk(KERN_DEBUG "ippp, ioctl: changed MAXCID to %ld\n", val);
+				is->maxcid = val;
 #ifdef CONFIG_ISDN_PPP_VJ
-			sltmp = slhc_init(16,val);
-			if(!sltmp) {
-				printk(KERN_ERR "ippp, can't realloc slhc struct\n");
-				return -ENOMEM;
-			}	
-			if(is->slcomp)
-				slhc_free(is->slcomp);
-			is->slcomp = sltmp;
+				sltmp = slhc_init(16, val);
+				if (!sltmp) {
+					printk(KERN_ERR "ippp, can't realloc slhc struct\n");
+					return -ENOMEM;
+				}
+				if (is->slcomp)
+					slhc_free(is->slcomp);
+				is->slcomp = sltmp;
 #endif
-		}
-		break;
-	case PPPIOCGDEBUG:
-		if ((r = set_arg((void *) arg, is->debug,0)))
-			return r;
-		break;
-	case PPPIOCSDEBUG:
-		if ((r = get_arg((void *) arg, &val,0)))
-			return r;
-		is->debug = val;
-		break;
-	case PPPIOCSCOMPRESS:
-#if 0
-		{
-			struct ppp_option_data pod;
-			r = get_arg((void *) arg,&pod,sizeof(struct ppp_option_data));
-			if(r)
+			}
+			break;
+		case PPPIOCGDEBUG:
+			if ((r = set_arg((void *) arg, is->debug, 0)))
 				return r;
-			ippp_set_compression(is,&pod);
-		}
-#endif
-		break;
-	default:
-		break;
+			break;
+		case PPPIOCSDEBUG:
+			if ((r = get_arg((void *) arg, &val, 0)))
+				return r;
+			is->debug = val;
+			break;
+		case PPPIOCGCOMPRESSORS:
+			{
+				unsigned long protos = 0;
+				struct isdn_ppp_compressor *ipc = ipc_head;
+				while(ipc) {
+					protos |= (0x1<<ipc->num);
+					ipc = ipc->next;
+				}
+				if ((r = set_arg((void *) arg, protos, 0)))
+					return r;
+			}
+			break;
+		case PPPIOCSCOMPRESSOR:
+			if ((r = get_arg((void *) arg, &num, sizeof(int))))
+				return r;
+			return isdn_ppp_set_compressor(is, num);
+			break;
+		case PPPIOCGCALLINFO:
+			{
+				struct pppcallinfo pci;
+				memset((char *) &pci,0,sizeof(struct pppcallinfo));
+				if(lp)
+				{
+					strncpy(pci.local_num,lp->msn,63);
+					if(lp->dial) {
+						strncpy(pci.remote_num,lp->dial->num,63);
+					}
+					pci.charge_units = lp->charge;
+					if(lp->outgoing)
+						pci.calltype = CALLTYPE_OUTGOING;
+					else
+						pci.calltype = CALLTYPE_INCOMING;
+					if(lp->flags & ISDN_NET_CALLBACK)
+						pci.calltype |= CALLTYPE_CALLBACK;
+				}
+				return set_arg((void *)arg,sizeof(struct pppcallinfo),&pci);
+			}
+		default:
+			break;
 	}
 	return 0;
 }
 
-int isdn_ppp_select(int min, struct file *file, int type, select_table * st)
+#if (LINUX_VERSION_CODE < 0x020117)
+int
+isdn_ppp_select(int min, struct file *file, int type, select_table * st)
 {
-	struct ippp_buf_queue *bf, *bl;
+	struct ippp_buf_queue *bf,
+	*bl;
 	unsigned long flags;
 	struct ippp_struct *is;
 
 	is = file->private_data;
 
-	if(is->debug & 0x2)
-		printk(KERN_DEBUG "isdn_ppp_select: minor: %d, type: %d \n",min,type);
+	if (is->debug & 0x2)
+		printk(KERN_DEBUG "isdn_ppp_select: minor: %d, type: %d \n", min, type);
 
 	if (!(is->state & IPPP_OPEN))
 		return -EINVAL;
 
 	switch (type) {
-	case SEL_IN:
-		save_flags(flags);
-		cli();
-		bl = is->last;
-		bf = is->first;
-		/* 
-		 * if IPPP_NOBLOCK is set we return even if we have nothing to read 
-		 */
-		if (bf->next == bl && !(is->state & IPPP_NOBLOCK)) {
-			select_wait(&is->wq, st);
+		case SEL_IN:
+			save_flags(flags);
+			cli();
+			bl = is->last;
+			bf = is->first;
+			/*
+			 * if IPPP_NOBLOCK is set we return even if we have nothing to read
+			 */
+			if (bf->next == bl && !(is->state & IPPP_NOBLOCK)) {
+				select_wait(&is->wq, st);
+				restore_flags(flags);
+				return 0;
+			}
+			is->state &= ~IPPP_NOBLOCK;
 			restore_flags(flags);
+			return 1;
+		case SEL_OUT:
+			/* we're always ready to send .. */
+			return 1;
+		case SEL_EX:
+			select_wait(&is->wq1, st);
 			return 0;
-		}
-		is->state &= ~IPPP_NOBLOCK;
-		restore_flags(flags);
-		return 1;
-	case SEL_OUT:
-                /* we're always ready to send .. */
-		return 1;
-	case SEL_EX:
-		select_wait(&is->wq1, st);
-		return 0;
 	}
 	return 1;
 }
+#else
+unsigned int
+isdn_ppp_poll(struct file *file, poll_table * wait)
+{
+	unsigned int mask;
+	struct ippp_buf_queue *bf;
+	struct ippp_buf_queue *bl;
+	unsigned long flags;
+	struct ippp_struct *is;
+
+	is = file->private_data;
+
+	if (is->debug & 0x2)
+		printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n", MINOR(file->f_inode->i_rdev));
+
+	poll_wait(&is->wq, wait);
+
+	if (!(is->state & IPPP_OPEN)) {
+		printk(KERN_DEBUG "isdn_ppp: device not open\n");
+		return POLLERR;
+	}
+	/* we're always ready to send .. */
+	mask = POLLOUT | POLLWRNORM;
+
+	save_flags(flags);
+	cli();
+	bl = is->last;
+	bf = is->first;
+	/*
+	 * if IPPP_NOBLOCK is set we return even if we have nothing to read
+	 */
+	if (bf->next != bl || (is->state & IPPP_NOBLOCK)) {
+		is->state &= ~IPPP_NOBLOCK;
+		mask |= POLLIN | POLLRDNORM;
+	}
+	restore_flags(flags);
+	return mask;
+}
+#endif
+
 
 /*
  *  fill up isdn_ppp_read() queue ..
  */
 
-static int isdn_ppp_fill_rq(unsigned char *buf, int len,int proto, int slot)
+static int
+isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot)
 {
-	struct ippp_buf_queue *bf, *bl;
+	struct ippp_buf_queue *bf,
+	*bl;
 	unsigned long flags;
 	unsigned char *nbuf;
 	struct ippp_struct *is;
@@ -637,9 +769,8 @@
 		printk(KERN_DEBUG "ippp: device not activated.\n");
 		return 0;
 	}
-
-	nbuf = (unsigned char *) kmalloc(len+4, GFP_ATOMIC);
-	if(!nbuf) {
+	nbuf = (unsigned char *) kmalloc(len + 4, GFP_ATOMIC);
+	if (!nbuf) {
 		printk(KERN_WARNING "ippp: Can't alloc buf\n");
 		return 0;
 	}
@@ -647,7 +778,7 @@
 	nbuf[1] = PPP_UI;
 	nbuf[2] = proto >> 8;
 	nbuf[3] = proto & 0xff;
-	memcpy(nbuf+4, buf, len);
+	memcpy(nbuf + 4, buf, len);
 
 	save_flags(flags);
 	cli();
@@ -662,7 +793,7 @@
 		is->first = bf;
 	}
 	bl->buf = (char *) nbuf;
-	bl->len = len+4;
+	bl->len = len + 4;
 
 	is->last = bl->next;
 	restore_flags(flags);
@@ -678,12 +809,14 @@
  *           reports, that there is data
  */
 
-int isdn_ppp_read(int min, struct file *file, char *buf, int count)
+int
+isdn_ppp_read(int min, struct file *file, char *buf, int count)
 {
 	struct ippp_struct *is;
 	struct ippp_buf_queue *b;
 	int r;
 	unsigned long flags;
+	unsigned char *save_buf;
 
 	is = file->private_data;
 
@@ -697,26 +830,30 @@
 	cli();
 
 	b = is->first->next;
-	if (!b->buf) {
+	save_buf = b->buf;
+	if (!save_buf) {
 		restore_flags(flags);
 		return -EAGAIN;
 	}
 	if (b->len < count)
 		count = b->len;
-	copy_to_user(buf, b->buf, count);
-	kfree(b->buf);
 	b->buf = NULL;
 	is->first = b;
+
 	restore_flags(flags);
 
+	copy_to_user(buf, save_buf, count);
+	kfree(save_buf);
+
 	return count;
 }
 
 /*
  * ipppd wanna write a packet to the card .. non-blocking
  */
- 
-int isdn_ppp_write(int min, struct file *file,  const char *buf, int count)
+
+int
+isdn_ppp_write(int min, struct file *file, const char *buf, int count)
 {
 	isdn_net_local *lp;
 	struct ippp_struct *is;
@@ -735,15 +872,16 @@
 	if (!lp)
 		printk(KERN_DEBUG "isdn_ppp_write: lp == NULL\n");
 	else {
-                /*
-                 * Don't reset huptimer for
-                 * LCP packets. (Echo requests).
-                 */
-                copy_from_user(protobuf, buf, 4);
-                proto = PPP_PROTOCOL(protobuf);
-                if (proto != PPP_LCP)
+		/*
+		 * Don't reset huptimer for
+		 * LCP packets. (Echo requests).
+		 */
+		if (copy_from_user(protobuf, buf, 4))
+			return -EFAULT;
+		proto = PPP_PROTOCOL(protobuf);
+		if (proto != PPP_LCP)
 			lp->huptimer = 0;
-		
+
 		if (lp->isdn_device < 0 || lp->isdn_channel < 0)
 			return 0;
 
@@ -752,23 +890,23 @@
 			int cnt;
 			struct sk_buff *skb;
 			skb = dev_alloc_skb(count);
-			if(!skb) {
+			if (!skb) {
 				printk(KERN_WARNING "isdn_ppp_write: out of memory!\n");
 				return count;
 			}
-			skb->free = 1;
-			copy_from_user(skb_put(skb, count), buf, count);
-			if(is->debug & 0x40) {
-				printk(KERN_DEBUG "ppp xmit: len %ld\n",skb->len);
-				isdn_ppp_frame_log("xmit",skb->data,skb->len,32);
-			}
-			if( (cnt=isdn_writebuf_skb_stub(lp->isdn_device,lp->isdn_channel,skb)) != count) {
-				if(lp->sav_skb) {
-					dev_kfree_skb(lp->sav_skb,FREE_WRITE);
-					printk(KERN_INFO "isdn_ppp_write: freeing sav_skb (%d,%d)!\n",cnt,count);
-				}
-				else
-					printk(KERN_INFO "isdn_ppp_write: Can't write PPP frame to LL (%d,%d)!\n",cnt,count);
+			SET_SKB_FREE(skb);
+			if (copy_from_user(skb_put(skb, count), buf, count))
+				return -EFAULT;
+			if (is->debug & 0x40) {
+				printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len);
+				isdn_ppp_frame_log("xmit", skb->data, skb->len, 32);
+			}
+			if ((cnt = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, skb)) != count) {
+				if (lp->sav_skb) {
+					dev_kfree_skb(lp->sav_skb, FREE_WRITE);
+					printk(KERN_INFO "isdn_ppp_write: freeing sav_skb (%d,%d)!\n", cnt, count);
+				} else
+					printk(KERN_INFO "isdn_ppp_write: Can't write PPP frame to LL (%d,%d)!\n", cnt, count);
 				lp->sav_skb = skb;
 			}
 		}
@@ -777,21 +915,23 @@
 }
 
 /*
- * init memory, structures etc. 
+ * init memory, structures etc.
  */
 
-int isdn_ppp_init(void)
+int
+isdn_ppp_init(void)
 {
-	int i, j;
+	int i,
+	 j;
 
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-	        if (!(ippp_table[i] = (struct ippp_struct *)
-	                kmalloc(sizeof(struct ippp_struct), GFP_KERNEL))) {
-		        printk(KERN_WARNING "isdn_ppp_init: Could not alloc ippp_table\n");
+		if (!(ippp_table[i] = (struct ippp_struct *)
+		      kmalloc(sizeof(struct ippp_struct), GFP_KERNEL))) {
+			printk(KERN_WARNING "isdn_ppp_init: Could not alloc ippp_table\n");
 			for (j = 0; j < i; j++)
 				kfree(ippp_table[i]);
-		        return -1;
-	        }
+			return -1;
+		}
 		memset((char *) ippp_table[i], 0, sizeof(struct ippp_struct));
 		ippp_table[i]->state = 0;
 		ippp_table[i]->first = ippp_table[i]->rq + NUM_RCV_BUFFS - 1;
@@ -807,78 +947,109 @@
 	return 0;
 }
 
-void isdn_ppp_cleanup(void)
+void
+isdn_ppp_cleanup(void)
 {
-        int i;
+	int i;
 
-        for (i = 0; i < ISDN_MAX_CHANNELS; i++)
-                kfree(ippp_table[i]);
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+		kfree(ippp_table[i]);
 }
 
 /*
+ * get the PPP protocol header and pull skb
+ */
+static int isdn_ppp_strip_proto(struct sk_buff *skb) 
+{
+	int proto;
+	if (skb->data[0] & 0x1) {
+		proto = skb->data[0];
+		skb_pull(skb, 1);   /* protocol ID is only 8 bit */
+	} else {
+		proto = ((int) skb->data[0] << 8) + skb->data[1];
+		skb_pull(skb, 2);
+	}
+	return proto;
+}
+
+
+/*
  * handler for incoming packets on a syncPPP interface
  */
 void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb)
 {
 	struct ippp_struct *is;
+	int proto;
+
 	is = ippp_table[lp->ppp_slot];
 
-	if(is->debug & 0x4) {
-		printk(KERN_DEBUG "ippp_receive: len: %ld\n",skb->len);
-		isdn_ppp_frame_log("receive",skb->data,skb->len,32);
+	if (is->debug & 0x4) {
+		printk(KERN_DEBUG "ippp_receive: len: %d\n", (int) skb->len);
+		isdn_ppp_frame_log("receive", skb->data, skb->len, 32);
 	}
-
-	if(net_dev->local.master) {
+	if (net_dev->local.master) {
 		printk(KERN_WARNING "isdn_ppp_receice: net_dev != master\n");
-		net_dev = ((isdn_net_local*) net_dev->local.master->priv)->netdev;
+		net_dev = ((isdn_net_local *) net_dev->local.master->priv)->netdev;
 	}
-
-	if(skb->data[0] == 0xff && skb->data[1] == 0x03)
-		skb_pull(skb,2);
+	if (skb->data[0] == 0xff && skb->data[1] == 0x03)
+		skb_pull(skb, 2);
 	else if (is->pppcfg & SC_REJ_COMP_AC) {
-		skb->free = 1;
-		dev_kfree_skb(skb,0 /* FREE_READ */ );
-		return;		/* discard it silently */
+		SET_SKB_FREE(skb);
+		dev_kfree_skb(skb, 0 /* FREE_READ */ );
+		return;         /* discard it silently */
 	}
 
+	proto = isdn_ppp_strip_proto(skb);
+
 #ifdef CONFIG_ISDN_MPP
 	if (!(is->mpppcfg & SC_REJ_MP_PROT)) {
-		int proto;
 		int sqno_end;
-		if (skb->data[0] & 0x1) {
-			proto = skb->data[0];
-			skb_pull(skb,1);	/* protocol ID is only 8 bit */
-		} else {
-			proto = ((int) skb->data[0] << 8) + skb->data[1];
-			skb_pull(skb,2);
+		
+		if(proto == PPP_LINK_COMP) {
+			printk(KERN_DEBUG "received single link compressed frame\n");
+			skb = isdn_ppp_decompress(skb,is,NULL);
+			if(!skb)
+				return;
+			proto = isdn_ppp_strip_proto(skb);
 		}
+
 		if (proto == PPP_MP) {
 			isdn_net_local *lpq;
 			long sqno, min_sqno, tseq;
+
 			u_char BEbyte = skb->data[0];
-			if(is->debug & 0x8)
-	 			printk(KERN_DEBUG "recv: %d/%04x/%d -> %02x %02x %02x %02x %02x %02x\n", lp->ppp_slot, proto ,
-					(int) skb->len, (int) skb->data[0], (int) skb->data[1], (int) skb->data[2], 
-					(int) skb->data[3], (int) skb->data[4], (int) skb->data[5]);
+			if (is->debug & 0x8)
+				printk(KERN_DEBUG "recv: %d/%04x/%d -> %02x %02x %02x %02x %02x %02x\n", lp->ppp_slot, proto,
+				       (int) skb->len, (int) skb->data[0], (int) skb->data[1], (int) skb->data[2],
+				       (int) skb->data[3], (int) skb->data[4], (int) skb->data[5]);
 			if (!(is->mpppcfg & SC_IN_SHORT_SEQ)) {
 				sqno = ((int) skb->data[1] << 16) + ((int) skb->data[2] << 8) + (int) skb->data[3];
-				skb_pull(skb,4);
+				skb_pull(skb, 4);
 			} else {
 				sqno = (((int) skb->data[0] & 0xf) << 8) + (int) skb->data[1];
-				skb_pull(skb,2);
+				skb_pull(skb, 2);
 			}
 
+			/*
+			 * new sequence number lower than last number? (this is only allowed
+			 * for overflow case)
+			 */
 			if ((tseq = is->last_link_seqno) >= sqno) {
 				int range = is->range;
-				if (tseq + 1024 < range + sqno) /* redundancy check .. not MP conform */
+				if (tseq + 1024 < range + sqno)	/* redundancy check .. not MP conform */
 					printk(KERN_WARNING "isdn_ppp_receive, MP, detected overflow with sqno: %ld, last: %ld !!!\n", sqno, tseq);
 				else {
 					sqno += range;
 					is->last_link_seqno = sqno;
 				}
-			} else
+			} else {
+				/* here, we should also add an redundancy check */
 				is->last_link_seqno = sqno;
+			}
 
+			/* 
+			 * step over all links to find lowest link number
+			 */
 			for (min_sqno = LONG_MAX, lpq = net_dev->queue;;) {
 				long lls = ippp_table[lpq->ppp_slot]->last_link_seqno;
 				if (lls >= 0 && lls < min_sqno)
@@ -887,11 +1058,14 @@
 				if (lpq == net_dev->queue)
 					break;
 			}
-			if (min_sqno >= ippp_table[lpq->ppp_slot]->range) {	/* OK, every link overflowed */
-				int mask = ippp_table[lpq->ppp_slot]->range - 1;	/* range is a power of 2 */
-#if 0
-				isdn_ppp_cleanup_queue(net_dev, min_sqno);
-#endif
+
+			/*
+			 * for the case, that the last frame numbers of all 
+			 * links are overflowed: mask/reduce the sequenece number to
+			 * 'normal' numbering.
+			 */
+			if (min_sqno >= ippp_table[lpq->ppp_slot]->range) {
+				int mask = ippp_table[lpq->ppp_slot]->range-1;	/* range is power of two, so a mask will do the job */
 				isdn_ppp_mask_queue(net_dev, mask);
 				net_dev->ib.next_num &= mask;
 				{
@@ -910,18 +1084,22 @@
 				}
 			}
 			if ((BEbyte & (MP_BEGIN_FRAG | MP_END_FRAG)) != (MP_BEGIN_FRAG | MP_END_FRAG)) {
-				printk(KERN_DEBUG "ippp: trying ;) to fill mp_queue %d .. UNTESTED!!\n", lp->ppp_slot);
-				if ((sqno_end = isdn_ppp_fill_mpqueue(net_dev, &skb , BEbyte, &sqno, min_sqno)) < 0) {
+				static int dmes = 0;
+				if( !dmes ) {
+					printk(KERN_DEBUG "ippp: trying ;) to fill mp_queue %d .. UNTESTED!!\n", lp->ppp_slot);
+					dmes = 1;
+				}
+				if ((sqno_end = isdn_ppp_fill_mpqueue(net_dev, &skb, BEbyte, &sqno, min_sqno)) < 0) {
 					net_dev->ib.modify = 1;	/* block timeout-timer */
-					isdn_ppp_cleanup_sqqueue(net_dev,lp,min_sqno); 
+					isdn_ppp_cleanup_sqqueue(net_dev, lp, min_sqno);
 					net_dev->ib.modify = 0;
-					return;		/* no packet complete */
+					return;	/* no packet complete */
 				}
 			} else
 				sqno_end = sqno;
 
-			if(is->debug & 0x40)
-				printk(KERN_DEBUG "min_sqno: %ld sqno_end %d next: %ld\n",min_sqno,sqno_end,net_dev->ib.next_num );
+			if (is->debug & 0x40)
+				printk(KERN_DEBUG "min_sqno: %ld sqno_end %d next: %ld\n", min_sqno, sqno_end, net_dev->ib.next_num);
 
 			/*
 			 * MP buffer management .. reorders incoming packets ..
@@ -930,7 +1108,7 @@
 			 * first check whether there is more than one link in the bundle
 			 * then check whether the number is in order
 			 */
-			net_dev->ib.modify = 1;		/* block timeout-timer */
+			net_dev->ib.modify = 1;	/* block timeout-timer */
 			if (net_dev->ib.bundled && net_dev->ib.next_num != sqno) {
 				/*
 				 * packet is not 'in order'
@@ -941,9 +1119,9 @@
 				if (!q) {
 					net_dev->ib.modify = 0;
 					printk(KERN_WARNING "ippp/MPPP: Bad! Can't alloc sq node!\n");
-					skb->free = 1;
+					SET_SKB_FREE(skb);
 					dev_kfree_skb(skb, 0 /* FREE_READ */ );
-					return;		/* discard */
+					return;	/* discard */
 				}
 				q->skb = skb;
 				q->sqno_end = sqno_end;
@@ -966,123 +1144,128 @@
 					}
 				}
 			} else {
-				/* 
-			 	 * packet was 'in order' .. push it higher
+				/*
+				 * packet was 'in order' .. push it higher
 				 */
 				net_dev->ib.next_num = sqno_end + 1;
-				isdn_ppp_push_higher(net_dev, lp, skb, -1);
+				proto = isdn_ppp_strip_proto(skb);
+				isdn_ppp_push_higher(net_dev, lp, skb, proto);
 			}
-			isdn_ppp_cleanup_sqqueue(net_dev,lp,min_sqno);
+			isdn_ppp_cleanup_sqqueue(net_dev, lp, min_sqno);
 			net_dev->ib.modify = 0;
 
 		} else
-			isdn_ppp_push_higher(net_dev, lp, skb , proto);
+			isdn_ppp_push_higher(net_dev, lp, skb, proto);
 	} else
 #endif
-		isdn_ppp_push_higher(net_dev, lp, skb , -1);
+		isdn_ppp_push_higher(net_dev, lp, skb, proto);
 }
 
 /*
  * push frame to higher layers
  * note: net_dev has to be master net_dev
  */
-static void isdn_ppp_push_higher(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb,int proto)
+static void
+isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb, int proto)
 {
 	struct device *dev = &net_dev->dev;
 	struct ippp_struct *is = ippp_table[lp->ppp_slot];
 
-	if (proto < 0) {	/* MP, oder normales Paket bei REJ_MP, MP Pakete gehen bei REJ zum pppd */
-		if (skb->data[0] & 0x01) {	/* is it odd? */
-			proto = (unsigned char) skb->data[0];
-			skb_pull(skb,1);	/* protocol ID is only 8 bit */
-		} else {
-			proto = ((int) (unsigned char) skb->data[0] << 8) + (unsigned char) skb->data[1];
-			skb_pull(skb,2);
-		}
+	if (is->debug & 0x10) {
+		printk(KERN_DEBUG "push, skb %d %04x\n", (int) skb->len, proto);
+		isdn_ppp_frame_log("rpush", skb->data, skb->len, 32);
 	}
 
-	if(is->debug & 0x10) {
-		printk(KERN_DEBUG "push, skb %ld %04x\n",skb->len,proto);
-		isdn_ppp_frame_log("rpush",skb->data,skb->len,32);
+	if(proto == PPP_COMP) {
+		if(!lp->master)
+			skb = isdn_ppp_decompress(skb,is,is);
+		else
+			skb = isdn_ppp_decompress(skb,is,ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot]);
+		if(!skb)
+			return;
+		proto = isdn_ppp_strip_proto(skb);
 	}
 
 	switch (proto) {
-	case PPP_IPX: /* untested */
-		if(is->debug & 0x20)
-			printk(KERN_DEBUG "isdn_ppp: IPX\n");
-		skb->dev = dev;
-		skb->mac.raw = skb->data;
-		skb->protocol = htons(ETH_P_IPX);
-		break;
-#ifdef CONFIG_ISDN_PPP_VJ
-	case PPP_VJC_UNCOMP:
-		if(is->debug & 0x20)
-			printk(KERN_DEBUG "isdn_ppp: VJC_UNCOMP\n");
-		if(slhc_remember(ippp_table[net_dev->local.ppp_slot]->slcomp, skb->data, skb->len) <= 0) {
-			printk(KERN_WARNING "isdn_ppp: received illegal VJC_UNCOMP frame!\n");
-			net_dev->local.stats.rx_dropped++;
-			skb->free = 1;
-			dev_kfree_skb(skb,0 /* FREE_READ */ );
-			return;
-		}
-#endif
-	case PPP_IP:
-		if(is->debug & 0x20)
-			printk(KERN_DEBUG "isdn_ppp: IP\n");
-		skb->dev = dev;
-		skb->mac.raw = skb->data;
-		skb->protocol = htons(ETH_P_IP);
-		break;
-	case PPP_VJC_COMP:
-                if(is->debug & 0x20)
-                        printk(KERN_DEBUG "isdn_ppp: VJC_COMP\n");
+		case PPP_IPX:  /* untested */
+			if (is->debug & 0x20)
+				printk(KERN_DEBUG "isdn_ppp: IPX\n");
+			skb->dev = dev;
+			skb->mac.raw = skb->data;
+			skb->protocol = htons(ETH_P_IPX);
+			break;
 #ifdef CONFIG_ISDN_PPP_VJ
-		{
-			struct sk_buff *skb_old = skb;
-			int pkt_len;
-			skb = dev_alloc_skb(skb_old->len + 40);
-
-			skb_old->free = 1;
-
-			if (!skb) {
-				printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
+		case PPP_VJC_UNCOMP:
+			if (is->debug & 0x20)
+				printk(KERN_DEBUG "isdn_ppp: VJC_UNCOMP\n");
+			if (slhc_remember(ippp_table[net_dev->local.ppp_slot]->slcomp, skb->data, skb->len) <= 0) {
+				printk(KERN_WARNING "isdn_ppp: received illegal VJC_UNCOMP frame!\n");
 				net_dev->local.stats.rx_dropped++;
-				dev_kfree_skb(skb_old,0 /* FREE_READ */ );
+				SET_SKB_FREE(skb);
+				dev_kfree_skb(skb, 0 /* FREE_READ */ );
 				return;
 			}
+#endif
+		case PPP_IP:
+			if (is->debug & 0x20)
+				printk(KERN_DEBUG "isdn_ppp: IP\n");
 			skb->dev = dev;
-			skb_put(skb,skb_old->len + 40);
-			memcpy(skb->data, skb_old->data, skb_old->len);
 			skb->mac.raw = skb->data;
-			pkt_len = slhc_uncompress(ippp_table[net_dev->local.ppp_slot]->slcomp,
-						  skb->data, skb_old->len);
-			dev_kfree_skb(skb_old,0 /* FREE_READ */ );
-			if(pkt_len < 0) {
-				skb->free = 1;
-				dev_kfree_skb(skb, 0 /* FREE_READ */ );
-				lp->stats.rx_dropped++;
-				return;
-			}
-			skb_trim(skb, pkt_len);
 			skb->protocol = htons(ETH_P_IP);
-		}
+			break;
+		case PPP_VJC_COMP:
+			if (is->debug & 0x20)
+				printk(KERN_DEBUG "isdn_ppp: VJC_COMP\n");
+#ifdef CONFIG_ISDN_PPP_VJ
+			{
+				struct sk_buff *skb_old = skb;
+				int pkt_len;
+				skb = dev_alloc_skb(skb_old->len + 40);
+
+				SET_SKB_FREE(skb_old);
+
+				if (!skb) {
+					printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
+					net_dev->local.stats.rx_dropped++;
+					dev_kfree_skb(skb_old, 0 /* FREE_READ */ );
+					return;
+				}
+				skb->dev = dev;
+				skb_put(skb, skb_old->len + 40);
+				memcpy(skb->data, skb_old->data, skb_old->len);
+				skb->mac.raw = skb->data;
+				pkt_len = slhc_uncompress(ippp_table[net_dev->local.ppp_slot]->slcomp,
+						skb->data, skb_old->len);
+				dev_kfree_skb(skb_old, 0 /* FREE_READ */ );
+				if (pkt_len < 0) {
+					SET_SKB_FREE(skb);
+					dev_kfree_skb(skb, 0 /* FREE_READ */ );
+					lp->stats.rx_dropped++;
+					return;
+				}
+				skb_trim(skb, pkt_len);
+				skb->protocol = htons(ETH_P_IP);
+			}
 #else
-		printk(KERN_INFO "isdn: Ooopsa .. VJ-Compression support not compiled into isdn driver.\n");
-		lp->stats.rx_dropped++;
-		skb->free = 1;
-		dev_kfree_skb(skb,0 /* FREE_READ */ );
-		return;
+			printk(KERN_INFO "isdn: Ooopsa .. VJ-Compression support not compiled into isdn driver.\n");
+			lp->stats.rx_dropped++;
+			SET_SKB_FREE(skb);
+			dev_kfree_skb(skb, 0 /* FREE_READ */ );
+			return;
 #endif
-		break;
-	default:
-		isdn_ppp_fill_rq(skb->data, skb->len,proto, lp->ppp_slot);	/* push data to pppd device */
-		skb->free = 1;
-		dev_kfree_skb(skb,0 /* FREE_READ */ );
-		return;
+			break;
+		case PPP_CCP:
+			isdn_ppp_receive_ccp(net_dev,lp,skb);
+			/* fall through */
+		default:
+			isdn_ppp_fill_rq(skb->data, skb->len, proto, lp->ppp_slot);	/* push data to pppd device */
+			SET_SKB_FREE(skb);
+			dev_kfree_skb(skb, 0 /* FREE_READ */ );
+			return;
 	}
 
 	netif_rx(skb);
-/* net_dev->local.stats.rx_packets++; */ /* done in isdn_net.c */
+	/* net_dev->local.stats.rx_packets++; *//* done in isdn_net.c */
 	/* Reset hangup-timer */
 	lp->huptimer = 0;
 
@@ -1090,89 +1273,95 @@
 }
 
 /*
- * send ppp frame .. we expect a PIDCOMPressable proto -- 
- *  (here: currently always PPP_IP,PPP_VJC_COMP,PPP_VJC_UNCOMP)
- *
- * VJ compression may change skb pointer!!! .. requeue with old
- * skb isn't allowed!! 
+ * isdn_ppp_skb_push ..
+ * checks whether we have enough space at the beginning of the SKB
+ * and allocs a new SKB if necessary
  */
-
-static void isdn_ppp_skb_destructor(struct sk_buff *skb)	/* debug function */
+static unsigned char *isdn_ppp_skb_push(struct sk_buff **skb_p,int len)
 {
-	char outstr[80],*outpnt=outstr;
-	int i;
+	struct sk_buff *skb = *skb_p;
 
-	*outpnt = 0;
-	for(i=0;i<24 && i<skb->len;i++) {
-		sprintf(outpnt,"%02x ",skb->data[i]);
-		outpnt += 3;
+	if(skb_headroom(skb) < len) {
+		printk(KERN_ERR "isdn_ppp_skb_push:under %d %d\n",skb_headroom(skb),len);
+		dev_kfree_skb(skb,FREE_WRITE);
+		return NULL;
 	}
-	printk(KERN_DEBUG "ippp_dstrct: %s\n",outstr);
+	return skb_push(skb,len);
 }
 
-int isdn_ppp_xmit(struct sk_buff *skb, struct device *dev)
+
+/*
+ * send ppp frame .. we expect a PIDCOMPressable proto --
+ *  (here: currently always PPP_IP,PPP_VJC_COMP,PPP_VJC_UNCOMP)
+ *
+ * VJ compression may change skb pointer!!! .. requeue with old
+ * skb isn't allowed!!
+ */
+
+int
+isdn_ppp_xmit(struct sk_buff *skb, struct device *dev)
 {
-	struct device *mdev = ((isdn_net_local *) (dev->priv) )->master;	/* get master (for redundancy) */
+	struct device *mdev = ((isdn_net_local *) (dev->priv))->master;	/* get master (for redundancy) */
 	isdn_net_local *lp,*mlp;
 	isdn_net_dev *nd;
-	int proto = PPP_IP;	/* 0x21 */
+	unsigned int proto = PPP_IP;     /* 0x21 */
 	struct ippp_struct *ipt,*ipts;
 
-	if(mdev)
-		mlp = (isdn_net_local *) (mdev->priv); 
+	if (mdev)
+		mlp = (isdn_net_local *) (mdev->priv);
 	else {
 		mdev = dev;
 		mlp = (isdn_net_local *) (dev->priv);
 	}
-	nd = mlp->netdev;	/* get master lp */
+	nd = mlp->netdev;       /* get master lp */
 	ipts = ippp_table[mlp->ppp_slot];
 
-	if (!(ipts->pppcfg & SC_ENABLE_IP)) {    /* PPP connected ? */
+	if (!(ipts->pppcfg & SC_ENABLE_IP)) {	/* PPP connected ? */
 #ifdef ISDN_SYNCPPP_READDRESS
-		if(!ipts->old_pa_addr)
+		if (!ipts->old_pa_addr)
 			ipts->old_pa_addr = mdev->pa_addr;
-		if(!ipts->old_pa_dstaddr)
+		if (!ipts->old_pa_dstaddr)
 			ipts->old_pa_dstaddr = mdev->pa_dstaddr;
 #endif
-		if(ipts->debug & 0x1) {
-			printk(KERN_INFO "%s: IP frame delayed.\n",dev->name);
-			skb->destructor = isdn_ppp_skb_destructor;
-		}
- 		return 1;
+		if (ipts->debug & 0x1)
+			printk(KERN_INFO "%s: IP frame delayed.\n", dev->name);
+		return 1;
 	}
 
-	skb->destructor = NULL;
-
-	switch(ntohs(skb->protocol)) {
+	switch (ntohs(skb->protocol)) {
 		case ETH_P_IP:
 			proto = PPP_IP;
 #ifdef ISDN_SYNCPPP_READDRESS
-			if(ipts->old_pa_addr != mdev->pa_addr)
-			{
+			if (ipts->old_pa_addr != mdev->pa_addr) {
 				struct iphdr *ipfr;
 				ipfr = (struct iphdr *) skb->data;
-printk(KERN_DEBUG "IF-address changed from %lx to %lx\n",ipts->old_pa_addr,mdev->pa_addr);
-				if(ipfr->version == 4) {
-					if(ipfr->saddr == ipts->old_pa_addr) {
-printk(KERN_DEBUG "readdressing %lx to %lx\n",ipfr->saddr,mdev->pa_addr);
+				if(ipts->debug & 0x4)
+					printk(KERN_DEBUG "IF-address changed from %lx to %lx\n", ipts->old_pa_addr, mdev->pa_addr);
+				if (ipfr->version == 4) {
+					if (ipfr->saddr == ipts->old_pa_addr) {
+						printk(KERN_DEBUG "readdressing %lx to %lx\n", ipfr->saddr, mdev->pa_addr);
 						ipfr->saddr = mdev->pa_addr;
 					}
 				}
 			}
-			/* dstaddr change not so improtant */
+			/* dstaddr change not so important */
 #endif
 			break;
 		case ETH_P_IPX:
 			proto = PPP_IPX;	/* untested */
 			break;
+		default:
+			dev_kfree_skb(skb, FREE_WRITE);
+			printk(KERN_ERR "isdn_ppp: skipped frame with unsupported protocoll: %#x.\n", skb->protocol);
+			return 0;
 	}
 
-	lp = nd->queue;		/* get lp on top of queue */
+	lp = nd->queue;         /* get lp on top of queue */
 
-	if(lp->sav_skb) {	/* find a non-busy device */
+	if (lp->sav_skb) {      /* find a non-busy device */
 		isdn_net_local *nlp = lp->next;
-		while(lp->sav_skb) {
-			if(lp == nlp)
+		while (lp->sav_skb) {
+			if (lp == nlp)
 				return 1;
 			nlp = nd->queue = nd->queue->next;
 		}
@@ -1180,43 +1369,49 @@
 	}
 	ipt = ippp_table[lp->ppp_slot];
 
-        lp->huptimer = 0;
+	lp->huptimer = 0;
 
 	/*
-	 * after this line .. requeueing in the device queue is no longer allowed!!! 
+	 * after this line .. requeueing in the device queue is no longer allowed!!!
 	 */
 
-	if(ipt->debug & 0x4)
-		printk(KERN_DEBUG  "xmit skb, len %ld\n",skb->len);
+	/* Pull off the fake header we stuck on earlier to keep
+     * the fragemntation code happy.
+     * this will break the ISDN_SYNCPPP_READDRESS hack a few lines
+     * above. So, enabling this is no longer allowed
+     */
+	skb_pull(skb,IPPP_MAX_HEADER);
+
+	if (ipt->debug & 0x4)
+		printk(KERN_DEBUG "xmit skb, len %d\n", (int) skb->len);
 
 #ifdef CONFIG_ISDN_PPP_VJ
-	if (proto == PPP_IP && ipts->pppcfg & SC_COMP_TCP) {	/* ipts here? probably yes .. but this check again */
+	if (proto == PPP_IP && ipts->pppcfg & SC_COMP_TCP) {	/* ipts here? probably yes, but check this again */
 		struct sk_buff *new_skb;
 
 		new_skb = dev_alloc_skb(skb->len);
-		if(new_skb) {
+		if (new_skb) {
 			u_char *buf;
 			int pktlen;
 
 			new_skb->dev = skb->dev;
-			new_skb->free = 1;
-			skb_put(new_skb,skb->len);
+			SET_SKB_FREE(new_skb);
+			skb_put(new_skb, skb->len);
 			buf = skb->data;
 
 			pktlen = slhc_compress(ipts->slcomp, skb->data, skb->len, new_skb->data,
-					&buf, !(ipts->pppcfg & SC_NO_TCP_CCID));
+				 &buf, !(ipts->pppcfg & SC_NO_TCP_CCID));
 
-			if(buf != skb->data) { /* copied to new buffer ??? (btw: WHY must slhc copy it?? *sigh*)  */
-				if(new_skb->data != buf)
+			if (buf != skb->data) {	
+				if (new_skb->data != buf)
 					printk(KERN_ERR "isdn_ppp: FATAL error after slhc_compress!!\n");
-				dev_kfree_skb(skb,FREE_WRITE);
+				dev_kfree_skb(skb, FREE_WRITE);
 				skb = new_skb;
-			}
-			else {
-				dev_kfree_skb(new_skb,0 /* FREE_WRITE */ );
+			} else {
+				dev_kfree_skb(new_skb, 0 /* FREE_WRITE */ );
 			}
 
-			skb_trim(skb,pktlen);
+			skb_trim(skb, pktlen);
 			if (skb->data[0] & SL_TYPE_COMPRESSED_TCP) {	/* cslip? style -> PPP */
 				proto = PPP_VJC_COMP;
 				skb->data[0] ^= SL_TYPE_COMPRESSED_TCP;
@@ -1229,8 +1424,13 @@
 	}
 #endif
 
-        if(ipt->debug & 0x24)
- 		printk(KERN_DEBUG  "xmit2 skb, len %ld, proto %04x\n",skb->len,proto);
+    /*
+     * normal or bundle compression
+     */
+	skb = isdn_ppp_compress(skb,&proto,ipt,ipts,0);
+
+	if (ipt->debug & 0x24)
+		printk(KERN_DEBUG "xmit2 skb, len %d, proto %04x\n", (int) skb->len, proto);
 
 #ifdef CONFIG_ISDN_MPP
 	if (ipt->mpppcfg & SC_MP_PROT) {
@@ -1239,41 +1439,64 @@
 		ipts->mp_seqno++;
 		nd->queue = nd->queue->next;
 		if (ipt->mpppcfg & SC_OUT_SHORT_SEQ) {
-			skb_push(skb, 3);
+			unsigned char *data = isdn_ppp_skb_push(&skb, 3);
+			if(!data)
+				return 0;
 			mp_seqno &= 0xfff;
-			skb->data[0] = MP_BEGIN_FRAG | MP_END_FRAG | (mp_seqno >> 8);	/* (B)egin & (E)ndbit .. */
-			skb->data[1] = mp_seqno & 0xff;
-			skb->data[2] = proto;	/* PID compression */
+			data[0] = MP_BEGIN_FRAG | MP_END_FRAG | ((mp_seqno >> 8) & 0xf);	/* (B)egin & (E)ndbit .. */
+			data[1] = mp_seqno & 0xff;
+			data[2] = proto;	/* PID compression */
 		} else {
-			skb_push(skb, 5);
-			skb->data[0] = MP_BEGIN_FRAG | MP_END_FRAG;	/* (B)egin & (E)ndbit .. */
-			skb->data[1] = (mp_seqno >> 16) & 0xff;	/* sequence number: 24bit */
-			skb->data[2] = (mp_seqno >> 8) & 0xff;
-			skb->data[3] = (mp_seqno >> 0) & 0xff;
-			skb->data[4] = proto;	/* PID compression */
+			unsigned char *data = isdn_ppp_skb_push(&skb, 5);
+			if(!data)
+				return 0;
+			data[0] = MP_BEGIN_FRAG | MP_END_FRAG;	/* (B)egin & (E)ndbit .. */
+			data[1] = (mp_seqno >> 16) & 0xff;	/* sequence number: 24bit */
+			data[2] = (mp_seqno >> 8) & 0xff;
+			data[3] = (mp_seqno >> 0) & 0xff;
+			data[4] = proto;	/* PID compression */
 		}
 		proto = PPP_MP; /* MP Protocol, 0x003d */
 	}
 #endif
-	skb_push(skb,4);
-	skb->data[0] = 0xff;        /* All Stations */
-	skb->data[1] = 0x03;        /* Unnumbered information */
-	skb->data[2] = proto >> 8;
-	skb->data[3] = proto & 0xff;
-
-        /* tx-stats are now updated via BSENT-callback */
-
-	if(ipts->debug & 0x40) {
-		printk(KERN_DEBUG "skb xmit: len: %ld\n",skb->len);
-		isdn_ppp_frame_log("xmit",skb->data,skb->len,32);
-	}
-
-	if(isdn_net_send_skb(dev , lp , skb)) {	
-		if(lp->sav_skb) {	/* whole sav_skb processing with disabled IRQs ?? */
-			printk(KERN_ERR "%s: whoops .. there is another stored skb!\n",dev->name);
-			dev_kfree_skb(skb,FREE_WRITE);
-		}
-		else
+
+	/*
+	 * 'link' compression 
+	 */
+	skb = isdn_ppp_compress(skb,&proto,ipt,ipts,1);
+
+	if( (ipt->pppcfg & SC_COMP_PROT) && (proto <= 0xff) ) {
+		unsigned char *data = isdn_ppp_skb_push(&skb,1);
+		if(!data)
+			return 0;
+		data[0] = proto & 0xff;
+	}
+	else {
+		unsigned char *data = isdn_ppp_skb_push(&skb,2);
+		if(!data)
+			return 0;
+		data[0] = (proto >> 8) & 0xff;
+		data[1] = proto & 0xff;
+	}
+	if(!(ipt->pppcfg & SC_COMP_AC)) {
+		unsigned char *data = isdn_ppp_skb_push(&skb,2);
+		if(!data)
+			return 0;
+		data[0] = 0xff;    /* All Stations */
+		data[1] = 0x03;    /* Unnumbered information */
+	}
+
+	/* tx-stats are now updated via BSENT-callback */
+
+	if (ipts->debug & 0x40) {
+		printk(KERN_DEBUG "skb xmit: len: %d\n", (int) skb->len);
+		isdn_ppp_frame_log("xmit", skb->data, skb->len, 32);
+	}
+	if (isdn_net_send_skb(dev, lp, skb)) {
+		if (lp->sav_skb) {	/* whole sav_skb processing with disabled IRQs ?? */
+			printk(KERN_ERR "%s: whoops .. there is another stored skb!\n", dev->name);
+			dev_kfree_skb(skb, FREE_WRITE);
+		} else
 			lp->sav_skb = skb;
 	}
 	return 0;
@@ -1281,16 +1504,17 @@
 
 #ifdef CONFIG_ISDN_MPP
 
-void isdn_ppp_free_sqqueue(isdn_net_dev * p) 
+static void
+isdn_ppp_free_sqqueue(isdn_net_dev * p)
 {
 	struct sqqueue *q = p->ib.sq;
 
 	p->ib.sq = NULL;
-	while(q) {
+	while (q) {
 		struct sqqueue *qn = q->next;
-		if(q->skb) {
-			q->skb->free = 1;
-			dev_kfree_skb(q->skb,0 /* FREE_READ */ );
+		if (q->skb) {
+			SET_SKB_FREE(q->skb);
+			dev_kfree_skb(q->skb, 0 /* FREE_READ */ );
 		}
 		kfree(q);
 		q = qn;
@@ -1298,24 +1522,29 @@
 
 }
 
-void isdn_ppp_free_mpqueue(isdn_net_dev * p)
+static void 
+isdn_ppp_free_mpqueue(isdn_net_dev * p)
 {
-	struct mpqueue *ql, *q = p->mp_last;
+	struct mpqueue *q = p->mp_last;
+	p->mp_last = NULL;
+
 	while (q) {
-		ql = q->next;
-		q->skb->free = 1;
- 		dev_kfree_skb(q->skb,0 /* FREE_READ */ );
+		struct mpqueue *ql = q->next;
+		SET_SKB_FREE(q->skb);
+		dev_kfree_skb(q->skb, 0 /* FREE_READ */ );
 		kfree(q);
 		q = ql;
 	}
 }
 
-static int isdn_ppp_bundle(struct ippp_struct *is, int unit)
+static int
+isdn_ppp_bundle(struct ippp_struct *is, int unit)
 {
 	char ifn[IFNAMSIZ + 1];
 	long flags;
 	isdn_net_dev *p;
-	isdn_net_local *lp,*nlp;
+	isdn_net_local *lp,
+	*nlp;
 
 	sprintf(ifn, "ippp%d", unit);
 	p = isdn_net_findif(ifn);
@@ -1356,7 +1585,8 @@
 }
 
 
-static void isdn_ppp_mask_queue(isdn_net_dev * dev, long mask)
+static void
+isdn_ppp_mask_queue(isdn_net_dev * dev, long mask)
 {
 	struct mpqueue *q = dev->mp_last;
 	while (q) {
@@ -1365,14 +1595,19 @@
 	}
 }
 
-static int isdn_ppp_fill_mpqueue(isdn_net_dev * dev, struct sk_buff ** skb, int BEbyte, long *sqnop, int min_sqno)
+static int
+isdn_ppp_fill_mpqueue(isdn_net_dev * dev, struct sk_buff **skb, int BEbyte, long *sqnop, int min_sqno)
 {
-	struct mpqueue *qe, *q1, *q;
-	long cnt, flags;
-	int pktlen, sqno_end;
+	struct mpqueue *qe,
+	*q1,
+	*q;
+	long cnt,
+	 flags;
+	int pktlen,
+	 sqno_end;
 	int sqno = *sqnop;
 
-	q1 = (struct mpqueue *) kmalloc(sizeof(struct mpqueue), GFP_KERNEL);
+	q1 = (struct mpqueue *) kmalloc(sizeof(struct mpqueue), GFP_ATOMIC);
 	if (!q1) {
 		printk(KERN_WARNING "isdn_ppp_fill_mpqueue: Can't alloc struct memory.\n");
 		save_flags(flags);
@@ -1395,9 +1630,9 @@
 		q1->last = NULL;
 		isdn_ppp_cleanup_mpqueue(dev, min_sqno);	/* not necessary */
 		restore_flags(flags);
-		return -1;
+		return -1;	/* -1 is not an error. Just says, that this fragment hasn't complete a full frame */
 	}
-	for (;;) {		/* the faster way would be to step from the queue-end to the start */
+	for (;;) {              /* the faster way would be to step from the queue-end to the start */
 		if (sqno > q->sqno) {
 			if (q->next) {
 				q = q->next;
@@ -1465,26 +1700,26 @@
 	isdn_ppp_cleanup_mpqueue(dev, min_sqno);
 	restore_flags(flags);
 
-	*skb = dev_alloc_skb(pktlen + 40); /* not needed: +40 for VJ compression .. */
+	*skb = dev_alloc_skb(pktlen + 40);	/* not needed: +40 for VJ compression .. */
 
 	if (!(*skb)) {
 		while (q) {
 			struct mpqueue *ql = q->next;
-			q->skb->free = 1;
-			dev_kfree_skb(q->skb,0 /* FREE_READ */ );
+			SET_SKB_FREE(q->skb);
+			dev_kfree_skb(q->skb, 0 /* FREE_READ */ );
 			kfree(q);
 			q = ql;
 		}
 		return -2;
 	}
 	cnt = 0;
-	skb_put(*skb,pktlen);
+	skb_put(*skb, pktlen);
 	while (q) {
 		struct mpqueue *ql = q->next;
 		memcpy((*skb)->data + cnt, q->skb->data, q->skb->len);
 		cnt += q->skb->len;
-		q->skb->free = 1;
-		dev_kfree_skb(q->skb,0 /* FREE_READ */ );
+		SET_SKB_FREE(q->skb);
+		dev_kfree_skb(q->skb, 0 /* FREE_READ */ );
 		kfree(q);
 		q = ql;
 	}
@@ -1497,28 +1732,32 @@
  * or packets with a sqno less or equal to min_sqno
  * net_dev: master netdevice , lp: 'real' local connection
  */
-static void isdn_ppp_cleanup_sqqueue(isdn_net_dev *net_dev, isdn_net_local *lp,long min_sqno)
+static void
+isdn_ppp_cleanup_sqqueue(isdn_net_dev * net_dev, isdn_net_local * lp, long min_sqno)
 {
 	struct sqqueue *q;
 
-	while ((q = net_dev->ib.sq) && (q->sqno_start == net_dev->ib.next_num || q->sqno_end <= min_sqno) ) {
-		if(q->sqno_start != net_dev->ib.next_num) {
-			printk(KERN_DEBUG "ippp: MP, stepping over missing frame: %ld\n",net_dev->ib.next_num);
+	while ((q = net_dev->ib.sq) && (q->sqno_start == net_dev->ib.next_num || q->sqno_end <= min_sqno)) {
+		int proto;
+		if (q->sqno_start != net_dev->ib.next_num) {
+			printk(KERN_DEBUG "ippp: MP, stepping over missing frame: %ld\n", net_dev->ib.next_num);
 #ifdef CONFIG_ISDN_PPP_VJ
 			slhc_toss(ippp_table[net_dev->local.ppp_slot]->slcomp);
 #endif
 		}
-		isdn_ppp_push_higher(net_dev, lp, q->skb, -1);
+		proto = isdn_ppp_strip_proto(q->skb);
+		isdn_ppp_push_higher(net_dev, lp, q->skb, proto);
 		net_dev->ib.sq = q->next;
 		net_dev->ib.next_num = q->sqno_end + 1;
 		kfree(q);
-    }
+	}
 }
 
 /*
  * remove stale packets from list
  */
-static void isdn_ppp_cleanup_mpqueue(isdn_net_dev * dev, long min_sqno)
+static void
+isdn_ppp_cleanup_mpqueue(isdn_net_dev * dev, long min_sqno)
 {
 #ifdef CONFIG_ISDN_PPP_VJ
 	int toss = 0;
@@ -1526,36 +1765,36 @@
 /* z.z einfaches aussortieren gammeliger pakete. Fuer die Zukunft:
    eventuell, solange vorne kein B-paket ist und sqno<=min_sqno: auch rauswerfen
    wenn sqno<min_sqno und Luecken vorhanden sind: auch weg (die koennen nicht mehr gefuellt werden)
-   bei paketen groesser min_sqno: ueber mp_mrru: wenn summe ueber pktlen der rumhaengenden Pakete 
+   bei paketen groesser min_sqno: ueber mp_mrru: wenn summe ueber pktlen der rumhaengenden Pakete
    groesser als mrru ist: raus damit , Pakete muessen allerdings zusammenhaengen sonst koennte
    ja ein Paket mit B und eins mit E dazwischenpassen */
 
-	struct mpqueue *ql, *q = dev->mp_last;
-	while (q) {
-		if (q->sqno < min_sqno) {
-			if (q->BEbyte & MP_END_FRAG) {
-				printk(KERN_DEBUG "ippp: freeing stale packet!\n");
-				if ((dev->mp_last = q->next))
-					q->next->last = NULL;
-				while (q) {
-					ql = q->last;
-					q->skb->free = 1;
-					dev_kfree_skb(q->skb,0 /* FREE_READ */ );
-					kfree(q);
+	struct mpqueue *ql,
+	*q = dev->mp_last;
+	while(q && (q->sqno < min_sqno) ) {
+		if ( (q->BEbyte & MP_END_FRAG) || 
+			 (q->next && (q->next->sqno <= min_sqno) && (q->next->BEbyte & MP_BEGIN_FRAG)) ) {
+			printk(KERN_DEBUG "ippp: freeing stale packet(s), min_sq: %ld!\n",min_sqno);
+			if ((dev->mp_last = q->next))
+				q->next->last = NULL;
+			while (q) {
+				ql = q->last;
+				SET_SKB_FREE(q->skb);
+				printk(KERN_DEBUG "ippp, freeing packet with sqno: %ld\n",q->sqno);
+				dev_kfree_skb(q->skb, 0 /* FREE_READ */ );
+				kfree(q);
 #ifdef CONFIG_ISDN_PPP_VJ
-					toss = 1;
+				toss = 1;
 #endif
-					q = ql;
-				}
-				q = dev->mp_last;
-			} else
-				q = q->next;
+				q = ql;
+			}
+			q = dev->mp_last;
 		} else
-			break;
+			q = q->next;
 	}
 #ifdef CONFIG_ISDN_PPP_VJ
 	/* did we free a stale frame ? */
-	if(toss)
+	if (toss)
 		slhc_toss(ippp_table[dev->local.ppp_slot]->slcomp);
 #endif
 }
@@ -1566,26 +1805,28 @@
 
 #endif
 
-void isdn_ppp_timer_timeout(void)
+void
+isdn_ppp_timer_timeout(void)
 {
 #ifdef CONFIG_ISDN_MPP
 	isdn_net_dev *net_dev = dev->netdev;
-	struct sqqueue *q, *ql = NULL, *qn;
+	struct sqqueue *q,
+	*ql = NULL,
+	*qn;
 
 	while (net_dev) {
 		isdn_net_local *lp = &net_dev->local;
-		if (net_dev->ib.modify || lp->master)	{	/* interface locked or slave?*/
+		if (net_dev->ib.modify || lp->master) {	/* interface locked or slave? */
 			net_dev = net_dev->next;
 			continue;
 		}
-
 		q = net_dev->ib.sq;
 		while (q) {
 			if (q->sqno_start == net_dev->ib.next_num || q->timer < jiffies) {
 
 #ifdef CONFIG_ISDN_PPP_VJ
 				/* did we step over a missing frame ? */
-				if(q->sqno_start != net_dev->ib.next_num)
+				if (q->sqno_start != net_dev->ib.next_num)
 					slhc_toss(ippp_table[lp->ppp_slot]->slcomp);
 #endif
 
@@ -1594,7 +1835,8 @@
 				net_dev->ib.next_num = q->sqno_end + 1;
 				q->next = NULL;
 				for (; ql;) {
-					isdn_ppp_push_higher(net_dev, lp, ql->skb, -1);
+					int proto = isdn_ppp_strip_proto(ql->skb);
+					isdn_ppp_push_higher(net_dev, lp, ql->skb, proto);
 					qn = ql->next;
 					kfree(ql);
 					ql = qn;
@@ -1612,46 +1854,47 @@
  * network device ioctl handlers
  */
 
-static int isdn_ppp_dev_ioctl_stats(int slot,struct ifreq *ifr,struct device *dev)
+static int
+isdn_ppp_dev_ioctl_stats(int slot, struct ifreq *ifr, struct device *dev)
 {
-	struct ppp_stats *res, t;
+	struct ppp_stats *res,
+	 t;
 	isdn_net_local *lp = (isdn_net_local *) dev->priv;
 	int err;
 
-        res = (struct ppp_stats *) ifr->ifr_ifru.ifru_data;
-        err = verify_area (VERIFY_WRITE, res,sizeof(struct ppp_stats));
+	res = (struct ppp_stats *) ifr->ifr_ifru.ifru_data;
+	err = verify_area(VERIFY_WRITE, res, sizeof(struct ppp_stats));
 
-	if(err)
+	if (err)
 		return err;
 
 	/* build a temporary stat struct and copy it to user space */
 
-	memset (&t, 0, sizeof(struct ppp_stats));
-	if(dev->flags & IFF_UP) {
+	memset(&t, 0, sizeof(struct ppp_stats));
+	if (dev->flags & IFF_UP) {
 		t.p.ppp_ipackets = lp->stats.rx_packets;
 		t.p.ppp_ierrors = lp->stats.rx_errors;
 		t.p.ppp_opackets = lp->stats.tx_packets;
 		t.p.ppp_oerrors = lp->stats.tx_errors;
 #ifdef CONFIG_ISDN_PPP_VJ
-		if(slot >= 0 && ippp_table[slot]->slcomp) {
+		if (slot >= 0 && ippp_table[slot]->slcomp) {
 			struct slcompress *slcomp = ippp_table[slot]->slcomp;
-			t.vj.vjs_packets  = slcomp->sls_o_compressed+slcomp->sls_o_uncompressed;
+			t.vj.vjs_packets = slcomp->sls_o_compressed + slcomp->sls_o_uncompressed;
 			t.vj.vjs_compressed = slcomp->sls_o_compressed;
 			t.vj.vjs_searches = slcomp->sls_o_searches;
-			t.vj.vjs_misses   = slcomp->sls_o_misses;
-			t.vj.vjs_errorin  = slcomp->sls_i_error;
-			t.vj.vjs_tossed   = slcomp->sls_i_tossed;
+			t.vj.vjs_misses = slcomp->sls_o_misses;
+			t.vj.vjs_errorin = slcomp->sls_i_error;
+			t.vj.vjs_tossed = slcomp->sls_i_tossed;
 			t.vj.vjs_uncompressedin = slcomp->sls_i_uncompressed;
 			t.vj.vjs_compressedin = slcomp->sls_i_compressed;
 		}
 #endif
 	}
-	copy_to_user (res, &t, sizeof (struct ppp_stats));
-	return 0;
-
+	return copy_to_user(res, &t, sizeof(struct ppp_stats));
 }
 
-int isdn_ppp_dev_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+int
+isdn_ppp_dev_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
 {
 	int error;
 	char *r;
@@ -1659,7 +1902,7 @@
 	isdn_net_local *lp = (isdn_net_local *) dev->priv;
 
 #if 0
-	printk(KERN_DEBUG "ippp, dev_ioctl: cmd %#08x , %d \n",cmd,lp->ppp_slot);
+	printk(KERN_DEBUG "ippp, dev_ioctl: cmd %#08x , %d \n", cmd, lp->ppp_slot);
 #endif
 
 	if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
@@ -1669,12 +1912,10 @@
 		case SIOCGPPPVER:
 			r = (char *) ifr->ifr_ifru.ifru_data;
 			len = strlen(PPP_VERSION) + 1;
-			error = verify_area(VERIFY_WRITE, r, len);
-			if (!error)
-				copy_to_user(r, PPP_VERSION, len);
+			error = copy_to_user(r, PPP_VERSION, len);
 			break;
 		case SIOCGPPPSTATS:
-			error = isdn_ppp_dev_ioctl_stats (lp->ppp_slot, ifr, dev);
+			error = isdn_ppp_dev_ioctl_stats(lp->ppp_slot, ifr, dev);
 			break;
 		default:
 			error = -EINVAL;
@@ -1683,51 +1924,55 @@
 	return error;
 }
 
-static int isdn_ppp_if_get_unit(char *name)
+static int
+isdn_ppp_if_get_unit(char *name)
 {
-	int len, i, unit = 0, deci;
+	int len,
+	 i,
+	 unit = 0,
+	 deci;
 
 	len = strlen(name);
 
-	if(strncmp("ippp",name,4) || len > 8)
+	if (strncmp("ippp", name, 4) || len > 8)
 		return -1;
 
 	for (i = 0, deci = 1; i < len; i++, deci *= 10) {
-		char a = name[len-i-1];
+		char a = name[len - i - 1];
 		if (a >= '0' && a <= '9')
 			unit += (a - '0') * deci;
 		else
 			break;
 	}
-	if (!i || len-i != 4)
+	if (!i || len - i != 4)
 		unit = -1;
 
 	return unit;
 }
 
 
-int isdn_ppp_dial_slave(char *name)
+int
+isdn_ppp_dial_slave(char *name)
 {
 #ifdef CONFIG_ISDN_MPP
 	isdn_net_dev *ndev;
 	isdn_net_local *lp;
 	struct device *sdev;
 
-	if(!(ndev = isdn_net_findif(name)))
+	if (!(ndev = isdn_net_findif(name)))
 		return 1;
 	lp = &ndev->local;
-	if(!(lp->flags & ISDN_NET_CONNECTED))
+	if (!(lp->flags & ISDN_NET_CONNECTED))
 		return 5;
 
 	sdev = lp->slave;
-	while(sdev)
-	{
+	while (sdev) {
 		isdn_net_local *mlp = (isdn_net_local *) sdev->priv;
-		if(!(mlp->flags & ISDN_NET_CONNECTED))
+		if (!(mlp->flags & ISDN_NET_CONNECTED))
 			break;
 		sdev = mlp->slave;
 	}
-	if(!sdev)
+	if (!sdev)
 		return 2;
 
 	isdn_net_force_dial_lp((isdn_net_local *) sdev->priv);
@@ -1737,28 +1982,28 @@
 #endif
 }
 
-int isdn_ppp_hangup_slave(char *name)
+int
+isdn_ppp_hangup_slave(char *name)
 {
 #ifdef CONFIG_ISDN_MPP
-        isdn_net_dev *ndev;
-        isdn_net_local *lp;
-        struct device *sdev;
+	isdn_net_dev *ndev;
+	isdn_net_local *lp;
+	struct device *sdev;
 
-	if(!(ndev = isdn_net_findif(name)))
+	if (!(ndev = isdn_net_findif(name)))
 		return 1;
 	lp = &ndev->local;
-	if(!(lp->flags & ISDN_NET_CONNECTED))
+	if (!(lp->flags & ISDN_NET_CONNECTED))
 		return 5;
 
 	sdev = lp->slave;
-	while(sdev)
-	{
+	while (sdev) {
 		isdn_net_local *mlp = (isdn_net_local *) sdev->priv;
-		if((mlp->flags & ISDN_NET_CONNECTED))
+		if ((mlp->flags & ISDN_NET_CONNECTED))
 			break;
 		sdev = mlp->slave;
 	}
-	if(!sdev)
+	if (!sdev)
 		return 2;
 
 	isdn_net_hangup(sdev);
@@ -1768,13 +2013,182 @@
 #endif
 }
 
+/*
+ * PPP compression stuff
+ */
+static struct sk_buff *isdn_ppp_decompress(struct sk_buff *skb,struct ippp_struct *is,struct ippp_struct *master)
+{
+#if 1
+	printk(KERN_ERR "compression not included!\n");
+	SET_SKB_FREE(skb);
+	dev_kfree_skb(skb,FREE_WRITE);
+	return NULL;
+#else
+	if(!master) {
+		/* 
+		 * single link compression 
+		 */
+		if(!is->link_compressor) {
+			printk(KERN_ERR "ippp: no (link) compressor defined!\n");
+			SET_SKB_FREE(skb);
+			dev_kfree_skb(skb,FREE_WRITE);
+			return NULL;
+		}
+		if(!is->link_decomp_stat) {
+			printk(KERN_DEBUG "ippp: initialize link compressor\n");
+		}
+/*
+		-> decompress link
+*/
+    }
+	else {
+		/*
+		 * 'normal' or bundle-compression 
+		 */
+		if(!master->compressor) {
+			printk(KERN_ERR "ippp: no (link) compressor defined!\n");
+			SET_SKB_FREE(skb);
+			dev_kfree_skb(skb,FREE_WRITE);
+			return NULL;
+		}
+		if(!master->decomp_stat) {
+#if 0
+			master->decomp_stat = (master->compressor->decomp_alloc)( .. );
+#endif
+			printk(KERN_DEBUG "ippp: initialize compressor\n");
+		}
+	}
+	
+	return skb;
+#endif
+}
+
+/*
+ * compress a frame 
+ *   type=0: normal/bundle compression
+ *       =1: link compression
+ * returns original skb if we haven't compressed the frame
+ * and a new skb pointer if we've done it
+ */
+static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto,
+	struct ippp_struct *is,struct ippp_struct *master,int type)
+{
+#if 1	
+	return skb_in;
+#else
+    int ret;
+    int new_proto;
+    struct isdn_ppp_compressor *compressor;
+    void *stat;
+    struct sk_buff *skb_out;
+
+	if(type) { /* type=1 => Link compression */
+		compressor = is->link_compressor;
+		stat = is->link_comp_stat;
+		new_proto = PPP_LINK_COMP;
+	}
+	else {
+		if(!master) {
+			compressor = is->compressor;
+			stat = is->comp_stat;
+		}
+		else {
+			compressor = master->compressor;
+			stat = master->comp_stat;
+		}
+		new_proto = PPP_COMP;
+	}
+
+	if(!compressor) {
+		printk(KERN_ERR "No compressor set!\n");
+		return skb_in;
+	}
+	if(!stat) {
+		/* init here ? */
+		return skb_in;
+	}
+
+	skb_out = dev_alloc_skb(skb_in->len);
+	if(!skb_out)
+		return skb_in;
+
+	ret = (compressor->compress)(stat,skb_in,skb_out,*proto);
+	if(!ret) {
+		SET_SKB_FREE(skb_out);
+		dev_kfree_skb(skb_out,0);
+		return skb_in;
+	}
+	
+	dev_kfree_skb(skb_in,FREE_WRITE);
+	*proto = new_proto;
+	return skb_out;
+#endif
+
+}
+
+/*
+ * we received a CCP frame .. 
+ * not a clean solution, but we SHOULD handle a few cased in the kernel
+ */
+static void isdn_ppp_receive_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
+	 struct sk_buff *skb)
+{
+#if 0
+	printk(KERN_DEBUG "isdn_ppp_receive_cpp: %02x %02x %02x %02x %02x %02x %02x %02x\n",
+		skb->data[0],skb->data[1],skb->data[2],skb->data[3],
+		skb->data[4],skb->data[5],skb->data[6],skb->data[7] );
+#endif
+}
+
+int isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc)
+{
+	ipc->next = ipc_head;
+	ipc->prev = NULL;
+	if(ipc_head) {
+		ipc_head->prev = ipc;
+	}
+	ipc_head = ipc;
+	return 0;
+}
+
+int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *ipc)
+{
+	if(ipc->prev)
+		ipc->prev->next = ipc->next;
+	else
+		ipc_head = ipc->next;
+	if(ipc->next)
+		ipc->next->prev = ipc->prev;
+	ipc->prev = ipc->next = NULL;
+	return 0;
+}
+
+static int isdn_ppp_set_compressor(struct ippp_struct *is,int num)
+{
+	struct isdn_ppp_compressor *ipc = ipc_head;
+
+	while(ipc) {
+		if(ipc->num == num) {
+			return 0;	
+			is->compressor = ipc;
+			is->link_compressor = ipc;
+		}
+		ipc = ipc->next;
+	}
+	return -EINVAL;
+}
+
+
 #if 0
-static struct symbol_table isdn_ppp_syms = {
+static struct symbol_table isdn_ppp_syms =
+{
 #include <linux/symtab_begin.h>
-    X(isdn_ppp_register_compressor),
-    X(isdn_ppp_unregister_compressor),
+	X(isdn_ppp_register_compressor),
+	X(isdn_ppp_unregister_compressor),
 #include <linux/symtab_end.h>
 };
 #endif
+
+
 
 
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_ppp.h linux/drivers/isdn/isdn_ppp.h
--- v2.0.30/linux/drivers/isdn/isdn_ppp.h	Tue Nov 12 22:36:20 1996
+++ linux/drivers/isdn/isdn_ppp.h	Mon Aug  4 17:34:00 1997
@@ -1,9 +1,9 @@
-/* $Id: isdn_ppp.h,v 1.6 1996/09/23 01:58:11 fritz Exp $
- *
+/* $Id: isdn_ppp.h,v 1.10 1997/06/17 13:06:00 hipp Exp $
+
  * header for Linux ISDN subsystem, functions for synchronous PPP (linklevel).
  *
  * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
- * 
+ *
  * 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)
@@ -16,9 +16,26 @@
  *
  * 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. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_ppp.h,v $
+ * Revision 1.10  1997/06/17 13:06:00  hipp
+ * Applied Eric's underflow-patches (slightly modified)
+ * more compression changes (but disabled at the moment)
+ * changed one copy_to_user() to run with enabled IRQs
+ * a few MP changes
+ * changed 'proto' handling in the isdn_ppp receive code
+ *
+ * Revision 1.9  1997/02/11 18:32:59  fritz
+ * Bugfix in isdn_ppp_free_mpqueue().
+ *
+ * Revision 1.8  1997/02/10 10:11:33  fritz
+ * More changes for Kernel 2.1.X compatibility.
+ *
+ * Revision 1.7  1997/02/03 23:18:57  fritz
+ * Removed isdn_ppp_free_sqqueue prototype
+ *         and ippp_table (both static in isdn_ppp.c).
+ *
  * Revision 1.6  1996/09/23 01:58:11  fritz
  * Fix: With syncPPP encapsulation, discard LCP packets
  *      when calculating hangup timeout.
@@ -41,31 +58,34 @@
  *
  */
 
-#include <linux/ppp_defs.h> /* for PPP_PROTOCOL */
+#include <linux/ppp_defs.h>     /* for PPP_PROTOCOL */
 extern void isdn_ppp_timer_timeout(void);
-extern int  isdn_ppp_read(int , struct file *, char *, int);
-extern int  isdn_ppp_write(int , struct file *, const char *, int);
-extern int  isdn_ppp_open(int , struct file *);
-extern int  isdn_ppp_init(void);
+extern int isdn_ppp_read(int, struct file *, char *, int);
+extern int isdn_ppp_write(int, struct file *, const char *, int);
+extern int isdn_ppp_open(int, struct file *);
+extern int isdn_ppp_init(void);
 extern void isdn_ppp_cleanup(void);
-extern int  isdn_ppp_free(isdn_net_local *);
-extern int  isdn_ppp_bind(isdn_net_local *);
-extern int  isdn_ppp_xmit(struct sk_buff *, struct device *);
+extern int isdn_ppp_free(isdn_net_local *);
+extern int isdn_ppp_bind(isdn_net_local *);
+extern int isdn_ppp_xmit(struct sk_buff *, struct device *);
 extern void isdn_ppp_receive(isdn_net_dev *, isdn_net_local *, struct sk_buff *);
-extern int  isdn_ppp_dev_ioctl(struct device *, struct ifreq *, int);
-extern void isdn_ppp_free_mpqueue(isdn_net_dev *);
-extern void isdn_ppp_free_sqqueue(isdn_net_dev *);
-extern int  isdn_ppp_select(int, struct file *, int, select_table *);
-extern int  isdn_ppp_ioctl(int, struct file *, unsigned int, unsigned long);
+extern int isdn_ppp_dev_ioctl(struct device *, struct ifreq *, int);
+#if (LINUX_VERSION_CODE < 0x020117)
+extern int isdn_ppp_select(int, struct file *, int, select_table *);
+#else
+extern unsigned int isdn_ppp_poll(struct file *, poll_table *);
+#endif
+extern int isdn_ppp_ioctl(int, struct file *, unsigned int, unsigned long);
 extern void isdn_ppp_release(int, struct file *);
-extern int  isdn_ppp_dial_slave(char *);
+extern int isdn_ppp_dial_slave(char *);
 extern void isdn_ppp_wakeup_daemon(isdn_net_local *);
 
-extern struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
-
 #define IPPP_OPEN	0x01
 #define IPPP_CONNECT	0x02
 #define IPPP_CLOSEWAIT	0x04
 #define IPPP_NOBLOCK	0x08
 #define IPPP_ASSIGNED	0x10
+
+#define IPPP_MAX_HEADER 10
+
 
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_syms.c linux/drivers/isdn/isdn_syms.c
--- v2.0.30/linux/drivers/isdn/isdn_syms.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/isdn_syms.c	Mon Aug  4 17:34:00 1997
@@ -0,0 +1,55 @@
+/* $Id: isdn_syms.c,v 1.3 1997/02/16 01:02:47 fritz Exp $
+
+ * Linux ISDN subsystem, exported symbols (linklevel).
+ *
+ * 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.
+ *
+ * $Log: isdn_syms.c,v $
+ * Revision 1.3  1997/02/16 01:02:47  fritz
+ * Added GPL-Header, Id and Log
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+
+#ifndef __GENKSYMS__      /* Don't want genksyms report unneeded structs */
+#include <linux/isdn.h>
+#endif
+#include "isdn_common.h"
+
+#if (LINUX_VERSION_CODE < 0x020111)
+static int has_exported;
+
+static struct symbol_table isdn_syms = {
+#include <linux/symtab_begin.h>
+        X(register_isdn),
+#include <linux/symtab_end.h>
+};
+
+void
+isdn_export_syms(void)
+{
+	if (has_exported)
+		return;
+        register_symtab(&isdn_syms);
+        has_exported = 1;
+}
+
+#else
+
+EXPORT_SYMBOL(register_isdn);
+
+#endif
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_tty.c linux/drivers/isdn/isdn_tty.c
--- v2.0.30/linux/drivers/isdn/isdn_tty.c	Tue Nov 12 22:36:20 1996
+++ linux/drivers/isdn/isdn_tty.c	Thu Aug 14 11:19:48 1997
@@ -1,10 +1,10 @@
-/* $Id: isdn_tty.c,v 1.23 1996/10/22 23:14:02 fritz Exp $
- *
+/* $Id: isdn_tty.c,v 1.41 1997/05/27 15:17:31 fritz Exp $
+
  * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel).
  *
  * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
  * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
- * 
+ *
  * 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)
@@ -17,9 +17,80 @@
  *
  * 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. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_tty.c,v $
+ * Revision 1.41  1997/05/27 15:17:31  fritz
+ * Added changes for recent 2.1.x kernels:
+ *   changed return type of isdn_close
+ *   queue_task_* -> queue_task
+ *   clear/set_bit -> test_and_... where apropriate.
+ *   changed type of hard_header_cache parameter.
+ *
+ * Revision 1.40  1997/03/24 22:55:27  fritz
+ * Added debug code for status callbacks.
+ *
+ * Revision 1.39  1997/03/21 18:25:56  fritz
+ * Corrected CTS handling.
+ *
+ * Revision 1.38  1997/03/07 12:13:35  fritz
+ * Bugfix: Send audio in adpcm format was broken.
+ * Bugfix: CTS handling was wrong.
+ *
+ * Revision 1.37  1997/03/07 01:37:34  fritz
+ * Bugfix: Did not compile with CONFIG_ISDN_AUDIO disabled.
+ * Bugfix: isdn_tty_tint() did not handle lowlevel errors correctly.
+ * Bugfix: conversion was wrong when sending ulaw audio.
+ * Added proper ifdef's for CONFIG_ISDN_AUDIO
+ *
+ * Revision 1.36  1997/03/04 21:41:55  fritz
+ * Fix: Excessive stack usage of isdn_tty_senddown()
+ *      and isdn_tty_end_vrx() could lead to problems.
+ *
+ * Revision 1.35  1997/03/02 19:05:52  fritz
+ * Bugfix: Avoid recursion.
+ *
+ * Revision 1.34  1997/03/02 14:29:22  fritz
+ * More ttyI related cleanup.
+ *
+ * Revision 1.33  1997/02/28 02:32:45  fritz
+ * Cleanup: Moved some tty related stuff from isdn_common.c
+ *          to isdn_tty.c
+ * Bugfix:  Bisync protocol did not behave like documented.
+ *
+ * Revision 1.32  1997/02/23 15:43:03  fritz
+ * Small change in handling of incoming calls
+ * documented in newest version of ttyI.4
+ *
+ * Revision 1.31  1997/02/21 13:05:57  fritz
+ * Bugfix: Remote hangup did not set location-info on ttyI's
+ *
+ * Revision 1.30  1997/02/18 09:41:05  fritz
+ * Added support for bitwise access to modem registers (ATSx.y=n, ATSx.y?).
+ * Beautified output of AT&V.
+ *
+ * Revision 1.29  1997/02/16 12:11:51  fritz
+ * Added S13,Bit4 option.
+ *
+ * Revision 1.28  1997/02/10 22:07:08  fritz
+ * Added 2 modem registers for numbering plan and screening info.
+ *
+ * Revision 1.27  1997/02/10 21:31:14  fritz
+ * Changed setup-interface (incoming and outgoing).
+ *
+ * Revision 1.26  1997/02/10 20:12:48  fritz
+ * Changed interface for reporting incoming calls.
+ *
+ * Revision 1.25  1997/02/03 23:04:30  fritz
+ * Reformatted according CodingStyle.
+ * skb->free stuff replaced by macro.
+ * Finished full-duplex audio.
+ *
+ * Revision 1.24  1997/01/14 01:32:42  fritz
+ * Changed audio receive not to rely on skb->users and skb->lock.
+ * Added ATI2 and related variables.
+ * Started adding full-duplex audio capability.
+ *
  * Revision 1.23  1996/10/22 23:14:02  fritz
  * Changes for compatibility to 2.0.X and 2.1.X kernels.
  *
@@ -114,6 +185,7 @@
  * Initial revision
  *
  */
+#undef ISDN_TTY_STAT_DEBUG
 
 #define __NO_VERSION__
 #include <linux/config.h>
@@ -129,29 +201,35 @@
 
 /* Prototypes */
 
-static int  isdn_tty_edit_at(const char *, int, modem_info *, int);
+static int isdn_tty_edit_at(const char *, int, modem_info *, int);
 static void isdn_tty_check_esc(const u_char *, u_char, int, int *, int *, int);
 static void isdn_tty_modem_reset_regs(modem_info *, int);
 static void isdn_tty_cmd_ATA(modem_info *);
 static void isdn_tty_at_cout(char *, modem_info *);
 static void isdn_tty_flush_buffer(struct tty_struct *);
+static void isdn_tty_modem_result(int, modem_info *);
+#ifdef CONFIG_ISDN_AUDIO
+static int isdn_tty_countDLE(unsigned char *, int);
+#endif
 
 /* Leave this unchanged unless you know what you do! */
 #define MODEM_PARANOIA_CHECK
 #define MODEM_DO_RESTART
 
 static char *isdn_ttyname_ttyI = "ttyI";
-static char *isdn_ttyname_cui  = "cui";
-static int bit2si[8] = {1,5,7,7,7,7,7,7};
-static int si2bit[8] = {4,1,4,4,4,4,4,4};
-                                
-char *isdn_tty_revision        = "$Revision: 1.23 $";
+static char *isdn_ttyname_cui = "cui";
+static int bit2si[8] =
+{1, 5, 7, 7, 7, 7, 7, 7};
+static int si2bit[8] =
+{4, 1, 4, 4, 4, 4, 4, 4};
+
+char *isdn_tty_revision = "$Revision: 1.41 $";
 
 #define DLE 0x10
 #define ETX 0x03
 #define DC4 0x14
 
-/* isdn_tty_try_read() is called from within isdn_receive_callback()
+/* isdn_tty_try_read() is called from within isdn_tty_rcv_skb()
  * to stuff incoming data directly into a tty's flip-buffer. This
  * is done to speed up tty-receiving if the receive-queue is empty.
  * This routine MUST be called with interrupts off.
@@ -160,37 +238,45 @@
  *  0 = Failure, data has to be buffered and later processed by
  *      isdn_tty_readmodem().
  */
-int isdn_tty_try_read(modem_info *info, struct sk_buff *skb)
+static int
+isdn_tty_try_read(modem_info * info, struct sk_buff *skb)
 {
-        int c;
-        int len;
-        struct tty_struct *tty;
+	int c;
+	int len;
+	struct tty_struct *tty;
 
 	if (info->online) {
 		if ((tty = info->tty)) {
 			if (info->mcr & UART_MCR_RTS) {
 				c = TTY_FLIPBUF_SIZE - tty->flip.count;
-                                len = skb->len + skb->users;
+				len = skb->len
+#ifdef CONFIG_ISDN_AUDIO
+					+ ISDN_AUDIO_SKB_DLECOUNT(skb)
+#endif
+					;
 				if (c >= len) {
-                                        if (skb->users)
-                                                while (skb->len--) {
-                                                        if (*skb->data == DLE)
-                                                                tty_insert_flip_char(tty, DLE, 0);
-                                                        tty_insert_flip_char(tty, *skb->data++, 0);
-                                                }
-                                        else {
-                                                memcpy(tty->flip.char_buf_ptr,
-                                                       skb->data, len);
-                                                tty->flip.count += len;
-                                                tty->flip.char_buf_ptr += len;
-                                                memset(tty->flip.flag_buf_ptr, 0, len);
-                                                tty->flip.flag_buf_ptr += len;
-                                        }
-                                        if (info->emu.mdmreg[12] & 128)
-                                                tty->flip.flag_buf_ptr[len - 1] = 0xff;
-					queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
-                                        skb->free = 1;
-                                        kfree_skb(skb, FREE_READ);
+#ifdef CONFIG_ISDN_AUDIO
+					if (ISDN_AUDIO_SKB_DLECOUNT(skb))
+						while (skb->len--) {
+							if (*skb->data == DLE)
+								tty_insert_flip_char(tty, DLE, 0);
+							tty_insert_flip_char(tty, *skb->data++, 0);
+					} else {
+#endif
+						memcpy(tty->flip.char_buf_ptr,
+						       skb->data, len);
+						tty->flip.count += len;
+						tty->flip.char_buf_ptr += len;
+						memset(tty->flip.flag_buf_ptr, 0, len);
+						tty->flip.flag_buf_ptr += len;
+#ifdef CONFIG_ISDN_AUDIO
+					}
+#endif
+					if (info->emu.mdmreg[12] & 128)
+						tty->flip.flag_buf_ptr[len - 1] = 0xff;
+					queue_task(&tty->flip.tqueue, &tq_timer);
+					SET_SKB_FREE(skb);
+					kfree_skb(skb, FREE_READ);
 					return 1;
 				}
 			}
@@ -203,7 +289,8 @@
  * It tries getting received data from the receive queue an stuff it into
  * the tty's flip-buffer.
  */
-void isdn_tty_readmodem(void)
+void
+isdn_tty_readmodem(void)
 {
 	int resched = 0;
 	int midx;
@@ -216,7 +303,7 @@
 
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
 		if ((midx = dev->m_idx[i]) >= 0) {
-                        info = &dev->mdm.info[midx];
+			info = &dev->mdm.info[midx];
 			if (info->online) {
 				r = 0;
 #ifdef CONFIG_ISDN_AUDIO
@@ -226,20 +313,20 @@
 					if (info->mcr & UART_MCR_RTS) {
 						c = TTY_FLIPBUF_SIZE - tty->flip.count;
 						if (c > 0) {
-                                                        save_flags(flags);
-                                                        cli();
+							save_flags(flags);
+							cli();
 							r = isdn_readbchan(info->isdn_driver, info->isdn_channel,
-								      tty->flip.char_buf_ptr,
-								      tty->flip.flag_buf_ptr, c, 0);
-                                                        /* CISCO AsyncPPP Hack */
+									   tty->flip.char_buf_ptr,
+									   tty->flip.flag_buf_ptr, c, 0);
+							/* CISCO AsyncPPP Hack */
 							if (!(info->emu.mdmreg[12] & 128))
 								memset(tty->flip.flag_buf_ptr, 0, r);
 							tty->flip.count += r;
 							tty->flip.flag_buf_ptr += r;
 							tty->flip.char_buf_ptr += r;
 							if (r)
-								queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
-                                                        restore_flags(flags);
+								queue_task(&tty->flip.tqueue, &tq_timer);
+							restore_flags(flags);
 						}
 					} else
 						r = 1;
@@ -251,257 +338,383 @@
 				} else
 					info->rcvsched = 1;
 			}
-                }
+		}
 	}
 	if (!resched)
 		isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 0);
 }
 
-void isdn_tty_cleanup_xmit(modem_info *info)
+int
+isdn_tty_rcv_skb(int i, int di, int channel, struct sk_buff *skb)
 {
-        struct sk_buff *skb;
-        unsigned long flags;
+	ulong flags;
+	int midx;
+#ifdef CONFIG_ISDN_AUDIO
+	int ifmt;
+#endif
+	modem_info *info;
 
-        save_flags(flags);
-        cli();
-        if (skb_queue_len(&info->xmit_queue))
-                while ((skb = skb_dequeue(&info->xmit_queue))) {
-                        skb->free = 1;
-                        kfree_skb(skb, FREE_WRITE);
-                }
-        if (skb_queue_len(&info->dtmf_queue))
-                while ((skb = skb_dequeue(&info->dtmf_queue))) {
-                        skb->free = 1;
-                        kfree_skb(skb, FREE_WRITE);
-                }
-        restore_flags(flags);
-}
-
-static void isdn_tty_tint(modem_info *info)
-{
-        struct sk_buff *skb = skb_dequeue(&info->xmit_queue);
-        int len, slen;
-
-        if (!skb)
-                return;
-        len = skb->len;
-        if ((slen = isdn_writebuf_skb_stub(info->isdn_driver,
-                                         info->isdn_channel, skb)) == len) {
-                struct tty_struct *tty = info->tty;
-                info->send_outstanding++;
-                info->msr |= UART_MSR_CTS;
-                info->lsr |= UART_LSR_TEMT;
-                if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-                    tty->ldisc.write_wakeup)
-                        (tty->ldisc.write_wakeup) (tty);
-                wake_up_interruptible(&tty->write_wait);
-                return;
-        }
-        if (slen > 0)
-                skb_pull(skb,slen);
-        skb_queue_head(&info->xmit_queue, skb);
+	if ((midx = dev->m_idx[i]) < 0) {
+		/* if midx is invalid, packet is not for tty */
+		return 0;
+	}
+	info = &dev->mdm.info[midx];
+#ifdef CONFIG_ISDN_AUDIO
+	ifmt = 1;
+	
+	if (info->vonline)
+		isdn_audio_calc_dtmf(info, skb->data, skb->len, ifmt);
+#endif
+	if ((info->online < 2)
+#ifdef CONFIG_ISDN_AUDIO
+	    && (!(info->vonline & 1))
+#endif
+		) {
+		/* If Modem not listening, drop data */
+		SET_SKB_FREE(skb);
+		kfree_skb(skb, FREE_READ);
+		return 1;
+	}
+	if (info->emu.mdmreg[13] & 2)
+		/* T.70 decoding: Simply throw away the T.70 header (4 bytes) */
+		if ((skb->data[0] == 1) && ((skb->data[1] == 0) || (skb->data[1] == 1)))
+			skb_pull(skb, 4);
+#ifdef CONFIG_ISDN_AUDIO
+	if (skb_headroom(skb) < sizeof(isdn_audio_skb)) {
+		printk(KERN_WARNING
+		       "isdn_audio: insufficient skb_headroom, dropping\n");
+		SET_SKB_FREE(skb);
+		kfree_skb(skb, FREE_READ);
+		return 1;
+	}
+	ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
+	ISDN_AUDIO_SKB_LOCK(skb) = 0;
+	if (info->vonline & 1) {
+		/* voice conversion/compression */
+		switch (info->emu.vpar[3]) {
+			case 2:
+			case 3:
+			case 4:
+				/* adpcm
+				 * Since compressed data takes less
+				 * space, we can overwrite the buffer.
+				 */
+				skb_trim(skb, isdn_audio_xlaw2adpcm(info->adpcmr,
+								    ifmt,
+								    skb->data,
+								    skb->data,
+								    skb->len));
+				break;
+			case 5:
+				/* a-law */
+				if (!ifmt)
+					isdn_audio_ulaw2alaw(skb->data, skb->len);
+				break;
+			case 6:
+				/* u-law */
+				if (ifmt)
+					isdn_audio_alaw2ulaw(skb->data, skb->len);
+				break;
+		}
+		ISDN_AUDIO_SKB_DLECOUNT(skb) =
+			isdn_tty_countDLE(skb->data, skb->len);
+	}
+#endif
+	/* Try to deliver directly via tty-flip-buf if queue is empty */
+	save_flags(flags);
+	cli();
+	if (skb_queue_empty(&dev->drv[di]->rpqueue[channel]))
+		if (isdn_tty_try_read(info, skb)) {
+			restore_flags(flags);
+			return 1;
+		}
+	/* Direct deliver failed or queue wasn't empty.
+	 * Queue up for later dequeueing via timer-irq.
+	 */
+	__skb_queue_tail(&dev->drv[di]->rpqueue[channel], skb);
+	dev->drv[di]->rcvcount[channel] +=
+		(skb->len
+#ifdef CONFIG_ISDN_AUDIO
+		 + ISDN_AUDIO_SKB_DLECOUNT(skb)
+#endif
+			);
+	restore_flags(flags);
+	/* Schedule dequeuing */
+	if ((dev->modempoll) && (info->rcvsched))
+		isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+	return 1;
 }
 
+void
+isdn_tty_cleanup_xmit(modem_info * info)
+{
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	if (skb_queue_len(&info->xmit_queue))
+		while ((skb = skb_dequeue(&info->xmit_queue))) {
+			SET_SKB_FREE(skb);
+			kfree_skb(skb, FREE_WRITE);
+		}
 #ifdef CONFIG_ISDN_AUDIO
-int isdn_tty_countDLE(unsigned char *buf, int len)
-{
-        int count = 0;
-
-        while (len--)
-                if (*buf++ == DLE)
-                        count++;
-        return count;
+	if (skb_queue_len(&info->dtmf_queue))
+		while ((skb = skb_dequeue(&info->dtmf_queue))) {
+			SET_SKB_FREE(skb);
+			kfree_skb(skb, FREE_WRITE);
+		}
+#endif
+	restore_flags(flags);
+}
+
+static void
+isdn_tty_tint(modem_info * info)
+{
+	struct sk_buff *skb = skb_dequeue(&info->xmit_queue);
+	int len,
+	 slen;
+
+	if (!skb)
+		return;
+	len = skb->len;
+	if ((slen = isdn_writebuf_skb_stub(info->isdn_driver,
+					   info->isdn_channel, skb)) == len) {
+		struct tty_struct *tty = info->tty;
+		info->send_outstanding++;
+		info->msr |= UART_MSR_CTS;
+		info->lsr |= UART_LSR_TEMT;
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup)
+			(tty->ldisc.write_wakeup) (tty);
+		wake_up_interruptible(&tty->write_wait);
+		return;
+	}
+	if (slen < 0) {
+		/* Error: no channel, already shutdown, or wrong parameter */
+		SET_SKB_FREE(skb);
+		dev_kfree_skb(skb, FREE_WRITE);
+		return;
+	}
+	if (slen)
+		skb_pull(skb, slen);
+	skb_queue_head(&info->xmit_queue, skb);
+}
+
+#ifdef CONFIG_ISDN_AUDIO
+static int
+isdn_tty_countDLE(unsigned char *buf, int len)
+{
+	int count = 0;
+
+	while (len--)
+		if (*buf++ == DLE)
+			count++;
+	return count;
 }
 
 /* This routine is called from within isdn_tty_write() to perform
  * DLE-decoding when sending audio-data.
  */
-static int isdn_tty_handleDLEdown(modem_info *info, atemu *m, int len)
+static int
+isdn_tty_handleDLEdown(modem_info * info, atemu * m, int len)
 {
-        unsigned char *p = &info->xmit_buf[info->xmit_count];
-        int count = 0;
+	unsigned char *p = &info->xmit_buf[info->xmit_count];
+	int count = 0;
 
-        while (len>0) {
-                if (m->lastDLE) {
-                        m->lastDLE = 0;
-                        switch (*p) {
-                                case DLE:
-                                        /* Escape code */
-                                        if (len>1)
-                                                memmove(p,p+1,len-1);
-                                        p--;
-                                        count++;
-                                        break;
-                                case ETX:
-                                        /* End of data */
-                                        info->vonline |= 4;
-                                        return count;
-                                case DC4:
-                                        /* Abort RX */
-                                        info->vonline &= ~1;
-                                        isdn_tty_at_cout("\020\003", info);
-                                        if (!info->vonline)
-                                                isdn_tty_at_cout("\r\nVCON\r\n", info);
-                                        /* Fall through */
-                                case 'q':
-                                case 's':
-                                        /* Silence */
-                                        if (len>1)
-                                                memmove(p,p+1,len-1);
-                                        p--;
-                                        break;
-                        }
-                } else {
-                        if (*p == DLE)
-                                m->lastDLE = 1;
-                        else
-                                count++;
-                }
-                p++;
-                len--;
-        }
-        if (len<0) {
-                printk(KERN_WARNING "isdn_tty: len<0 in DLEdown\n");
-                return 0;
-        }
-        return count;
+	while (len > 0) {
+		if (m->lastDLE) {
+			m->lastDLE = 0;
+			switch (*p) {
+				case DLE:
+					/* Escape code */
+					if (len > 1)
+						memmove(p, p + 1, len - 1);
+					p--;
+					count++;
+					break;
+				case ETX:
+					/* End of data */
+					info->vonline |= 4;
+					return count;
+				case DC4:
+					/* Abort RX */
+					info->vonline &= ~1;
+#ifdef ISDN_DEBUG_MODEM_VOICE
+					printk(KERN_DEBUG
+					       "DLEdown: got DLE-DC4, send DLE-ETX on ttyI%d\n",
+					       info->line);
+#endif
+					isdn_tty_at_cout("\020\003", info);
+					if (!info->vonline) {
+#ifdef ISDN_DEBUG_MODEM_VOICE
+						printk(KERN_DEBUG
+						       "DLEdown: send VCON on ttyI%d\n",
+						       info->line);
+#endif
+						isdn_tty_at_cout("\r\nVCON\r\n", info);
+					}
+					/* Fall through */
+				case 'q':
+				case 's':
+					/* Silence */
+					if (len > 1)
+						memmove(p, p + 1, len - 1);
+					p--;
+					break;
+			}
+		} else {
+			if (*p == DLE)
+				m->lastDLE = 1;
+			else
+				count++;
+		}
+		p++;
+		len--;
+	}
+	if (len < 0) {
+		printk(KERN_WARNING "isdn_tty: len<0 in DLEdown\n");
+		return 0;
+	}
+	return count;
 }
 
 /* This routine is called from within isdn_tty_write() when receiving
  * audio-data. It interrupts receiving, if an character other than
  * ^S or ^Q is sent.
  */
-static int isdn_tty_end_vrx(const char *buf, int c, int from_user)
+static int
+isdn_tty_end_vrx(const char *buf, int c, int from_user)
 {
-	char tmpbuf[VBUF];
-        char *p;
+	char ch;
 
-        if (c > VBUF) {
-                printk(KERN_ERR "isdn_tty: (end_vrx) BUFFER OVERFLOW!!!\n");
-                return 1;
-        }
-	if (from_user) {
-		copy_from_user(tmpbuf, buf, c);
-                p = tmpbuf;
-        } else
-                p = (char *)buf;
-        while (c--) {
-                if ((*p != 0x11) && (*p != 0x13))
-                        return 1;
-                p++;
-        }
-        return 0;
+	while (c--) {
+		if (from_user)
+			GET_USER(ch, buf);
+		else
+			ch = *buf;
+		if ((ch != 0x11) && (ch != 0x13))
+			return 1;
+		buf++;
+	}
+	return 0;
 }
 
-static int voice_cf[7] = { 1, 1, 4, 3, 2, 1, 1 };
+static int voice_cf[7] =
+{0, 0, 4, 3, 2, 0, 0};
 
-#endif        /* CONFIG_ISDN_AUDIO */
+#endif                          /* CONFIG_ISDN_AUDIO */
 
 /* isdn_tty_senddown() is called either directly from within isdn_tty_write()
  * or via timer-interrupt from within isdn_tty_modem_xmit(). It pulls
  * outgoing data from the tty's xmit-buffer, handles voice-decompression or
  * T.70 if necessary, and finally queues it up for sending via isdn_tty_tint.
  */
-static void isdn_tty_senddown(modem_info * info)
+static void
+isdn_tty_senddown(modem_info * info)
 {
-        unsigned char *buf = info->xmit_buf;
-        int buflen;
-        int skb_res;
-        struct sk_buff *skb;
-        unsigned long flags;
-
-        save_flags(flags);
-        cli();
-        if (!(buflen = info->xmit_count)) {
-                restore_flags(flags);
-                return;
-        }
-        if (info->isdn_driver < 0) {
-                info->xmit_count = 0;
-                restore_flags(flags);
-                return;
-        }
-        skb_res = dev->drv[info->isdn_driver]->interface->hl_hdrlen + 4;
-#ifdef CONFIG_ISDN_AUDIO
-        if (info->vonline & 2) {
-                /* For now, ifmt is fixed to 1 (alaw), since this
-                 * is used with ISDN everywhere in the world, except
-                 * US, Canada and Japan.
-                 * Later, when US-ISDN protocols are implemented,
-                 * this setting will depend on the D-channel protocol.
-                 */
-                int ifmt = 1;
-                int skb_len;
-                unsigned char hbuf[VBUF];
-
-                memcpy(hbuf,info->xmit_buf,buflen);
-                info->xmit_count = 0;
-                restore_flags(flags);
-                /* voice conversion/decompression */
-                skb_len = buflen * voice_cf[info->emu.vpar[3]];
-                skb = dev_alloc_skb(skb_len + skb_res);
-                if (!skb) {
-                        printk(KERN_WARNING
-                               "isdn_tty: Out of memory in ttyI%d senddown\n", info->line);
-                        return;
-                }
-                skb_reserve(skb, skb_res);
-                switch (info->emu.vpar[3]) {
-                        case 2:
-                        case 3:
-                        case 4:
-                                /* adpcm, compatible to ZyXel 1496 modem
-                                 * with ROM revision 6.01
-                                 */
-                                buflen = isdn_audio_adpcm2xlaw(info->adpcms,
-                                                               ifmt,
-                                                               hbuf,
-                                                               skb_put(skb,skb_len),
-                                                               buflen);
-                                skb_trim(skb, buflen);
-                                break;
-                        case 5:
-                                /* a-law */
-                                if (!ifmt)
-                                        isdn_audio_alaw2ulaw(hbuf,buflen);
-                                memcpy(skb_put(skb,buflen),hbuf,buflen);
-                                break;
-                        case 6:
-                                /* u-law */
-                                if (ifmt)
-                                        isdn_audio_ulaw2alaw(hbuf,buflen);
-                                memcpy(skb_put(skb,buflen),hbuf,buflen);
-                                break;
-                }
-                if (info->vonline & 4) {
-                        info->vonline &= ~6;
-                        if (!info->vonline)
-                                isdn_tty_at_cout("\r\nVCON\r\n",info);
-                }
-        } else {
-#endif        /* CONFIG_ISDN_AUDIO */
-                skb = dev_alloc_skb(buflen + skb_res);
-                if (!skb) {
-                        printk(KERN_WARNING
-                               "isdn_tty: Out of memory in ttyI%d senddown\n", info->line);
-                        restore_flags(flags);
-                        return;
-                }
-                skb_reserve(skb, skb_res);
-                memcpy(skb_put(skb,buflen),buf,buflen);
-                info->xmit_count = 0;
-                restore_flags(flags);
-#ifdef CONFIG_ISDN_AUDIO
-        }
-#endif
-        skb->free = 1;
-        if (info->emu.mdmreg[13] & 2)
-                /* Add T.70 simplified header */
-                memcpy(skb_push(skb, 4), "\1\0\1\0", 4);
-        skb_queue_tail(&info->xmit_queue, skb);
-        if ((info->emu.mdmreg[12] & 0x10) != 0)
-                info->msr &= UART_MSR_CTS;
-        info->lsr &= UART_LSR_TEMT;
+	int buflen;
+	int skb_res;
+#ifdef CONFIG_ISDN_AUDIO
+	int audio_len;
+#endif
+	struct sk_buff *skb;
+	unsigned long flags;
+
+#ifdef CONFIG_ISDN_AUDIO
+	if (info->vonline & 4) {
+		info->vonline &= ~6;
+		if (!info->vonline) {
+#ifdef ISDN_DEBUG_MODEM_VOICE
+			printk(KERN_DEBUG
+			       "senddown: send VCON on ttyI%d\n",
+			       info->line);
+#endif
+			isdn_tty_at_cout("\r\nVCON\r\n", info);
+		}
+	}
+#endif
+	save_flags(flags);
+	cli();
+	if (!(buflen = info->xmit_count)) {
+		restore_flags(flags);
+		return;
+	}
+	if ((info->emu.mdmreg[12] & 0x10) != 0)
+		info->msr &= ~UART_MSR_CTS;
+	info->lsr &= ~UART_LSR_TEMT;
+	if (info->isdn_driver < 0) {
+		info->xmit_count = 0;
+		restore_flags(flags);
+		return;
+	}
+	skb_res = dev->drv[info->isdn_driver]->interface->hl_hdrlen + 4;
+#ifdef CONFIG_ISDN_AUDIO
+	if (info->vonline & 2)
+		audio_len = buflen * voice_cf[info->emu.vpar[3]];
+	else
+		audio_len = 0;
+	skb = dev_alloc_skb(skb_res + buflen + audio_len);
+#else
+	skb = dev_alloc_skb(skb_res + buflen);
+#endif
+	if (!skb) {
+		restore_flags(flags);
+		printk(KERN_WARNING
+		       "isdn_tty: Out of memory in ttyI%d senddown\n",
+		       info->line);
+		return;
+	}
+	skb_reserve(skb, skb_res);
+	memcpy(skb_put(skb, buflen), info->xmit_buf, buflen);
+	info->xmit_count = 0;
+	restore_flags(flags);
+#ifdef CONFIG_ISDN_AUDIO
+	if (info->vonline & 2) {
+		/* For now, ifmt is fixed to 1 (alaw), since this
+		 * is used with ISDN everywhere in the world, except
+		 * US, Canada and Japan.
+		 * Later, when US-ISDN protocols are implemented,
+		 * this setting will depend on the D-channel protocol.
+		 */
+		int ifmt = 1;
+
+		/* voice conversion/decompression */
+		switch (info->emu.vpar[3]) {
+			case 2:
+			case 3:
+			case 4:
+				/* adpcm, compatible to ZyXel 1496 modem
+				 * with ROM revision 6.01
+				 */
+				audio_len = isdn_audio_adpcm2xlaw(info->adpcms,
+								  ifmt,
+								  skb->data,
+						    skb_put(skb, audio_len),
+								  buflen);
+				skb_pull(skb, buflen);
+				skb_trim(skb, audio_len);
+				break;
+			case 5:
+				/* a-law */
+				if (!ifmt)
+					isdn_audio_alaw2ulaw(skb->data,
+							     buflen);
+				break;
+			case 6:
+				/* u-law */
+				if (ifmt)
+					isdn_audio_ulaw2alaw(skb->data,
+							     buflen);
+				break;
+		}
+	}
+#endif                          /* CONFIG_ISDN_AUDIO */
+	SET_SKB_FREE(skb);
+	if (info->emu.mdmreg[13] & 2)
+		/* Add T.70 simplified header */
+		memcpy(skb_push(skb, 4), "\1\0\1\0", 4);
+	skb_queue_tail(&info->xmit_queue, skb);
 }
 
 /************************************************************
@@ -517,53 +730,55 @@
  * isdn_tty_modem_result() to stuff a "NO CARRIER" Message
  * into the tty's flip-buffer.
  */
-static void isdn_tty_modem_do_ncarrier(unsigned long data)
+static void
+isdn_tty_modem_do_ncarrier(unsigned long data)
 {
-        modem_info * info = (modem_info *)data;
-        isdn_tty_modem_result(3, info);
+	modem_info *info = (modem_info *) data;
+	isdn_tty_modem_result(3, info);
 }
 
 /* Next routine is called, whenever the DTR-signal is raised.
  * It checks the ncarrier-flag, and triggers the above routine
  * when necessary. The ncarrier-flag is set, whenever DTR goes
  * low.
- */      
-static void isdn_tty_modem_ncarrier(modem_info * info)
+ */
+static void
+isdn_tty_modem_ncarrier(modem_info * info)
 {
-        if (info->ncarrier) {
-                info->ncarrier = 0;
-                info->nc_timer.expires = jiffies + HZ;
-                info->nc_timer.function = isdn_tty_modem_do_ncarrier;
-                info->nc_timer.data = (unsigned long)info;
-                add_timer(&info->nc_timer);
-        }
+	if (info->ncarrier) {
+		info->nc_timer.expires = jiffies + HZ;
+		info->nc_timer.function = isdn_tty_modem_do_ncarrier;
+		info->nc_timer.data = (unsigned long) info;
+		add_timer(&info->nc_timer);
+	}
 }
 
 /* isdn_tty_dial() performs dialing of a tty an the necessary
  * setup of the lower levels before that.
  */
-static void isdn_tty_dial(char *n, modem_info * info, atemu * m)
+static void
+isdn_tty_dial(char *n, modem_info * info, atemu * m)
 {
-        int usg = ISDN_USAGE_MODEM;
-        int si = 7;
-        int l2 = m->mdmreg[14];
+	int usg = ISDN_USAGE_MODEM;
+	int si = 7;
+	int l2 = m->mdmreg[14];
 	isdn_ctrl cmd;
 	ulong flags;
 	int i;
-        int j;
+	int j;
 
-        for (j=7;j>=0;j--)
-                if (m->mdmreg[18] & (1<<j)) {
-                        si = bit2si[j];
-                        break;
-                }
-#ifdef CONFIG_ISDN_AUDIO
-                if (si == 1) {
-                        l2 = 4;
-                        usg = ISDN_USAGE_VOICE;
-                }
+	for (j = 7; j >= 0; j--)
+		if (m->mdmreg[18] & (1 << j)) {
+			si = bit2si[j];
+			break;
+		}
+#ifdef CONFIG_ISDN_AUDIO
+	if (si == 1) {
+		l2 = 4;
+		usg = ISDN_USAGE_VOICE;
+	}
 #endif
-        m->mdmreg[20] = si2bit[si];
+	m->mdmreg[20] = si2bit[si];
 	save_flags(flags);
 	cli();
 	i = isdn_get_free_channel(usg, l2, m->mdmreg[15], -1, -1);
@@ -571,38 +786,44 @@
 		restore_flags(flags);
 		isdn_tty_modem_result(6, info);
 	} else {
-                info->isdn_driver = dev->drvmap[i];
-                info->isdn_channel = dev->chanmap[i];
-                info->drv_index = i;
-                dev->m_idx[i] = info->line;
-                dev->usage[i] |= ISDN_USAGE_OUTGOING;
-                isdn_info_update();
-                restore_flags(flags);
-                cmd.driver = info->isdn_driver;
-                cmd.arg = info->isdn_channel;
-                cmd.command = ISDN_CMD_CLREAZ;
-                dev->drv[info->isdn_driver]->interface->command(&cmd);
-                strcpy(cmd.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
-                cmd.driver = info->isdn_driver;
-                cmd.command = ISDN_CMD_SETEAZ;
-                dev->drv[info->isdn_driver]->interface->command(&cmd);
-                cmd.driver = info->isdn_driver;
-                cmd.command = ISDN_CMD_SETL2;
-                cmd.arg = info->isdn_channel + (l2 << 8);
-                dev->drv[info->isdn_driver]->interface->command(&cmd);
-                cmd.driver = info->isdn_driver;
-                cmd.command = ISDN_CMD_SETL3;
-                cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8);
-                dev->drv[info->isdn_driver]->interface->command(&cmd);
-                cmd.driver = info->isdn_driver;
-                cmd.arg = info->isdn_channel;
-                sprintf(cmd.num, "%s,%s,%d,%d", n, isdn_map_eaz2msn(m->msn, info->isdn_driver),
-                        si, m->mdmreg[19]);
-                cmd.command = ISDN_CMD_DIAL;
-                info->dialing = 1;
-                strcpy(dev->num[i], n);
-                isdn_info_update();
-                dev->drv[info->isdn_driver]->interface->command(&cmd);
+		info->isdn_driver = dev->drvmap[i];
+		info->isdn_channel = dev->chanmap[i];
+		info->drv_index = i;
+		dev->m_idx[i] = info->line;
+		dev->usage[i] |= ISDN_USAGE_OUTGOING;
+		info->last_dir = 1;
+		strcpy(info->last_num, n);
+		isdn_info_update();
+		restore_flags(flags);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		cmd.command = ISDN_CMD_CLREAZ;
+		dev->drv[info->isdn_driver]->interface->command(&cmd);
+		strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETEAZ;
+		dev->drv[info->isdn_driver]->interface->command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL2;
+		info->last_l2 = l2;
+		cmd.arg = info->isdn_channel + (l2 << 8);
+		dev->drv[info->isdn_driver]->interface->command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL3;
+		cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8);
+		dev->drv[info->isdn_driver]->interface->command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		sprintf(cmd.parm.setup.phone, "%s", n);
+		sprintf(cmd.parm.setup.eazmsn, "%s",
+			isdn_map_eaz2msn(m->msn, info->isdn_driver));
+		cmd.parm.setup.si1 = si;
+		cmd.parm.setup.si2 = m->mdmreg[19];
+		cmd.command = ISDN_CMD_DIAL;
+		info->dialing = 1;
+		strcpy(dev->num[i], n);
+		isdn_info_update();
+		dev->drv[info->isdn_driver]->interface->command(&cmd);
 	}
 }
 
@@ -610,64 +831,66 @@
  * ISDN-line (hangup). The usage-status is cleared
  * and some cleanup is done also.
  */
-void isdn_tty_modem_hup(modem_info * info)
+static void
+isdn_tty_modem_hup(modem_info * info, int local)
 {
 	isdn_ctrl cmd;
-        int usage;
+	int usage;
 
-        if (!info)
-                return;
+	if (!info)
+		return;
 #ifdef ISDN_DEBUG_MODEM_HUP
-        printk(KERN_DEBUG "Mhup ttyI%d\n", info->line);
+	printk(KERN_DEBUG "Mhup ttyI%d\n", info->line);
 #endif
-        info->rcvsched = 0;
-        info->online = 0;
-        isdn_tty_flush_buffer(info->tty);
-        if (info->vonline & 1) {
-                /* voice-recording, add DLE-ETX */
-                isdn_tty_at_cout("\020\003", info);
-        }
-        if (info->vonline & 2) {
-                /* voice-playing, add DLE-DC4 */
-                isdn_tty_at_cout("\020\024", info);
-        }
-        info->vonline = 0;
-#ifdef CONFIG_ISDN_AUDIO
-        if (info->dtmf_state) {
-                kfree(info->dtmf_state);
-                info->dtmf_state = NULL;
-        }
-        if (info->adpcms) {
-                kfree(info->adpcms);
-                info->adpcms = NULL;
-        }
-        if (info->adpcmr) {
-                kfree(info->adpcmr);
-                info->adpcmr = NULL;
-        }
+	info->rcvsched = 0;
+	isdn_tty_flush_buffer(info->tty);
+	if (info->online) {
+		info->last_lhup = local;
+		info->online = 0;
+		/* NO CARRIER message */
+		isdn_tty_modem_result(3, info);
+	}
+#ifdef CONFIG_ISDN_AUDIO
+	info->vonline = 0;
+	if (info->dtmf_state) {
+		kfree(info->dtmf_state);
+		info->dtmf_state = NULL;
+	}
+	if (info->adpcms) {
+		kfree(info->adpcms);
+		info->adpcms = NULL;
+	}
+	if (info->adpcmr) {
+		kfree(info->adpcmr);
+		info->adpcmr = NULL;
+	}
 #endif
-        info->msr &= ~(UART_MSR_DCD | UART_MSR_RI);
-        info->lsr |= UART_LSR_TEMT;
+	info->msr &= ~(UART_MSR_DCD | UART_MSR_RI);
+	info->lsr |= UART_LSR_TEMT;
 	if (info->isdn_driver >= 0) {
-		cmd.driver = info->isdn_driver;
-		cmd.command = ISDN_CMD_HANGUP;
-		cmd.arg = info->isdn_channel;
-		dev->drv[info->isdn_driver]->interface->command(&cmd);
+		if (local) {
+			cmd.driver = info->isdn_driver;
+			cmd.command = ISDN_CMD_HANGUP;
+			cmd.arg = info->isdn_channel;
+			dev->drv[info->isdn_driver]->interface->command(&cmd);
+		}
 		isdn_all_eaz(info->isdn_driver, info->isdn_channel);
-                usage = (info->emu.mdmreg[20] == 1)?
-                        ISDN_USAGE_VOICE:ISDN_USAGE_MODEM;
+		info->emu.mdmreg[1] = 0;
+		usage = (info->emu.mdmreg[20] == 1) ?
+		    ISDN_USAGE_VOICE : ISDN_USAGE_MODEM;
 		isdn_free_channel(info->isdn_driver, info->isdn_channel,
-                                  usage);
+				  usage);
 	}
 	info->isdn_driver = -1;
 	info->isdn_channel = -1;
-        if (info->drv_index >= 0) {
-                dev->m_idx[info->drv_index] = -1;
-                info->drv_index = -1;
-        }
+	if (info->drv_index >= 0) {
+		dev->m_idx[info->drv_index] = -1;
+		info->drv_index = -1;
+	}
 }
 
-static inline int isdn_tty_paranoia_check(modem_info * info, kdev_t device, const char *routine)
+static inline int
+isdn_tty_paranoia_check(modem_info * info, kdev_t device, const char *routine)
 {
 #ifdef MODEM_PARANOIA_CHECK
 	if (!info) {
@@ -688,9 +911,13 @@
  * This routine is called to set the UART divisor registers to match
  * the specified baud rate for a serial port.
  */
-static void isdn_tty_change_speed(modem_info * info)
+static void
+isdn_tty_change_speed(modem_info * info)
 {
-	uint cflag, cval, fcr, quot;
+	uint cflag,
+	 cval,
+	 fcr,
+	 quot;
 	int i;
 
 	if (!info->tty || !info->tty->termios)
@@ -707,19 +934,19 @@
 	}
 	if (quot) {
 		info->mcr |= UART_MCR_DTR;
-                isdn_tty_modem_ncarrier(info);                
+		isdn_tty_modem_ncarrier(info);
 	} else {
 		info->mcr &= ~UART_MCR_DTR;
-                if (info->emu.mdmreg[13] & 4) {
+		if (info->emu.mdmreg[13] & 4) {
 #ifdef ISDN_DEBUG_MODEM_HUP
-                        printk(KERN_DEBUG "Mhup in changespeed\n");
+			printk(KERN_DEBUG "Mhup in changespeed\n");
 #endif
-                        if (info->online)
-                                info->ncarrier = 1;
-                        isdn_tty_modem_reset_regs(info, 0);
-                        isdn_tty_modem_hup(info);
-                }
-                return;
+			if (info->online)
+				info->ncarrier = 1;
+			isdn_tty_modem_reset_regs(info, 0);
+			isdn_tty_modem_hup(info, 1);
+		}
+		return;
 	}
 	/* byte size and parity */
 	cval = cflag & (CSIZE | CSTOPB);
@@ -742,7 +969,8 @@
 	}
 }
 
-static int isdn_tty_startup(modem_info * info)
+static int
+isdn_tty_startup(modem_info * info)
 {
 	ulong flags;
 
@@ -750,12 +978,12 @@
 		return 0;
 	save_flags(flags);
 	cli();
-        isdn_MOD_INC_USE_COUNT();
+	isdn_MOD_INC_USE_COUNT();
 #ifdef ISDN_DEBUG_MODEM_OPEN
 	printk(KERN_DEBUG "starting up ttyi%d ...\n", info->line);
 #endif
 	/*
-	 * Now, initialize the UART 
+	 * Now, initialize the UART
 	 */
 	info->mcr = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
 	if (info->tty)
@@ -776,7 +1004,8 @@
  * This routine will shutdown a serial port; interrupts are disabled, and
  * DTR is dropped if the hangup on close termio flag is on.
  */
-static void isdn_tty_shutdown(modem_info * info)
+static void
+isdn_tty_shutdown(modem_info * info)
 {
 	ulong flags;
 
@@ -786,17 +1015,18 @@
 	printk(KERN_DEBUG "Shutting down isdnmodem port %d ....\n", info->line);
 #endif
 	save_flags(flags);
-	cli();			/* Disable interrupts */
-        isdn_MOD_DEC_USE_COUNT();
+	cli();                  /* Disable interrupts */
+	isdn_MOD_DEC_USE_COUNT();
+	info->msr &= ~UART_MSR_RI;
 	if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
 		info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS);
-                if (info->emu.mdmreg[13] & 4) {
-                        isdn_tty_modem_reset_regs(info, 0);
+		if (info->emu.mdmreg[13] & 4) {
+			isdn_tty_modem_reset_regs(info, 0);
 #ifdef ISDN_DEBUG_MODEM_HUP
-                        printk(KERN_DEBUG "Mhup in isdn_tty_shutdown\n");
+			printk(KERN_DEBUG "Mhup in isdn_tty_shutdown\n");
 #endif
-                        isdn_tty_modem_hup(info);
-                }
+			isdn_tty_modem_hup(info, 1);
+		}
 	}
 	if (info->tty)
 		set_bit(TTY_IO_ERROR, &info->tty->flags);
@@ -814,9 +1044,11 @@
  *  - If receiving audio-data, call isdn_tty_end_vrx() to abort if needed.
  *  - If dialing, abort dial.
  */
-static int isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int count)
+static int
+isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int count)
 {
-	int c, total = 0;
+	int c,
+	 total = 0;
 	ulong flags;
 	modem_info *info = (modem_info *) tty->driver_data;
 
@@ -824,70 +1056,82 @@
 		return 0;
 	if (!tty)
 		return 0;
-        save_flags(flags);
-        cli();
+	save_flags(flags);
+	cli();
 	while (1) {
 		c = MIN(count, info->xmit_size - info->xmit_count);
 		if (info->isdn_driver >= 0)
 			c = MIN(c, dev->drv[info->isdn_driver]->maxbufsize);
 		if (c <= 0)
 			break;
-                if ((info->online > 1) ||
-                    (info->vonline & 2)) {
-                        atemu *m = &info->emu;
-
-                        if (!(info->vonline & 2))
-                                isdn_tty_check_esc(buf, m->mdmreg[2], c,
-                                                   &(m->pluscount),
-                                                   &(m->lastplus),
-                                                   from_user);
-                        if (from_user)
-                                copy_from_user(&(info->xmit_buf[info->xmit_count]), buf, c);
-                        else
-                                memcpy(&(info->xmit_buf[info->xmit_count]), buf, c);
-#ifdef CONFIG_ISDN_AUDIO
-                        if (info->vonline & 2) {
-                                int cc;
-
-                                if (!(cc = isdn_tty_handleDLEdown(info,m,c))) {
-                                        /* If DLE decoding results in zero-transmit, but
-                                         * c originally was non-zero, do a wakeup.
-                                         */
-                                        if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-                                            tty->ldisc.write_wakeup)
-                                                (tty->ldisc.write_wakeup) (tty);
-                                        wake_up_interruptible(&tty->write_wait);
-                                        info->msr |= UART_MSR_CTS;
-                                        info->lsr |= UART_LSR_TEMT;
-                                }
-                                info->xmit_count += cc;
-                        } else
+		if ((info->online > 1)
+#ifdef CONFIG_ISDN_AUDIO
+		    || (info->vonline & 3)
 #endif
-                                info->xmit_count += c;
-			if (m->mdmreg[13] & 1) {
-                                isdn_tty_senddown(info);
-                                isdn_tty_tint(info);
-                        }
-		} else {
-                        info->msr |= UART_MSR_CTS;
-                        info->lsr |= UART_LSR_TEMT;
+			) {
+			atemu *m = &info->emu;
+
 #ifdef CONFIG_ISDN_AUDIO
-                        if (info->vonline & 1) {
-                                if (isdn_tty_end_vrx(buf, c, from_user)) {
-                                        info->vonline &= ~1;
-                                        isdn_tty_at_cout("\020\003\r\nVCON\r\n", info);
-                                }
-                        } else
+			if (!info->vonline)
+#endif
+				isdn_tty_check_esc(buf, m->mdmreg[2], c,
+						   &(m->pluscount),
+						   &(m->lastplus),
+						   from_user);
+			if (from_user)
+				copy_from_user(&(info->xmit_buf[info->xmit_count]), buf, c);
+			else
+				memcpy(&(info->xmit_buf[info->xmit_count]), buf, c);
+#ifdef CONFIG_ISDN_AUDIO
+			if (info->vonline) {
+				int cc = isdn_tty_handleDLEdown(info, m, c);
+				if (info->vonline & 2) {
+					if (!cc) {
+						/* If DLE decoding results in zero-transmit, but
+						 * c originally was non-zero, do a wakeup.
+						 */
+						if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+						 tty->ldisc.write_wakeup)
+							(tty->ldisc.write_wakeup) (tty);
+						wake_up_interruptible(&tty->write_wait);
+						info->msr |= UART_MSR_CTS;
+						info->lsr |= UART_LSR_TEMT;
+					}
+					info->xmit_count += cc;
+				}
+				if ((info->vonline & 3) == 1) {
+					/* Do NOT handle Ctrl-Q or Ctrl-S
+					 * when in full-duplex audio mode.
+					 */
+					if (isdn_tty_end_vrx(buf, c, from_user)) {
+						info->vonline &= ~1;
+#ifdef ISDN_DEBUG_MODEM_VOICE
+						printk(KERN_DEBUG
+						       "got !^Q/^S, send DLE-ETX,VCON on ttyI%d\n",
+						       info->line);
+#endif
+						isdn_tty_at_cout("\020\003\r\nVCON\r\n", info);
+					}
+				}
+			} else
 #endif
-                                if (info->dialing) {
-                                        info->dialing = 0;
+				info->xmit_count += c;
+			if (m->mdmreg[13] & 1) {
+				isdn_tty_senddown(info);
+				isdn_tty_tint(info);
+			}
+		} else {
+			info->msr |= UART_MSR_CTS;
+			info->lsr |= UART_LSR_TEMT;
+			if (info->dialing) {
+				info->dialing = 0;
 #ifdef ISDN_DEBUG_MODEM_HUP
-                                        printk(KERN_DEBUG "Mhup in isdn_tty_write\n");
+				printk(KERN_DEBUG "Mhup in isdn_tty_write\n");
 #endif
-                                        isdn_tty_modem_result(3, info);
-                                        isdn_tty_modem_hup(info);
-                                } else
-                                        c = isdn_tty_edit_at(buf, c, info, from_user);
+				isdn_tty_modem_result(3, info);
+				isdn_tty_modem_hup(info, 1);
+			} else
+				c = isdn_tty_edit_at(buf, c, info, from_user);
 		}
 		buf += c;
 		count -= c;
@@ -895,11 +1139,12 @@
 	}
 	if ((info->xmit_count) || (skb_queue_len(&info->xmit_queue)))
 		isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
-        restore_flags(flags);
+	restore_flags(flags);
 	return total;
 }
 
-static int isdn_tty_write_room(struct tty_struct *tty)
+static int
+isdn_tty_write_room(struct tty_struct *tty)
 {
 	modem_info *info = (modem_info *) tty->driver_data;
 	int ret;
@@ -912,7 +1157,8 @@
 	return (ret < 0) ? 0 : ret;
 }
 
-static int isdn_tty_chars_in_buffer(struct tty_struct *tty)
+static int
+isdn_tty_chars_in_buffer(struct tty_struct *tty)
 {
 	modem_info *info = (modem_info *) tty->driver_data;
 
@@ -923,32 +1169,34 @@
 	return (info->xmit_count);
 }
 
-static void isdn_tty_flush_buffer(struct tty_struct *tty)
+static void
+isdn_tty_flush_buffer(struct tty_struct *tty)
 {
 	modem_info *info;
-        unsigned long flags;
+	unsigned long flags;
 
-        save_flags(flags);
-        cli();
-        if (!tty) {
-                restore_flags(flags);
-                return;
-        }
-        info =  (modem_info *) tty->driver_data;
+	save_flags(flags);
+	cli();
+	if (!tty) {
+		restore_flags(flags);
+		return;
+	}
+	info = (modem_info *) tty->driver_data;
 	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_flush_buffer")) {
-                restore_flags(flags);
+		restore_flags(flags);
 		return;
-        }
-        isdn_tty_cleanup_xmit(info);
-        info->xmit_count = 0;
-        restore_flags(flags);
+	}
+	isdn_tty_cleanup_xmit(info);
+	info->xmit_count = 0;
+	restore_flags(flags);
 	wake_up_interruptible(&tty->write_wait);
 	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
 	    tty->ldisc.write_wakeup)
 		(tty->ldisc.write_wakeup) (tty);
 }
 
-static void isdn_tty_flush_chars(struct tty_struct *tty)
+static void
+isdn_tty_flush_chars(struct tty_struct *tty)
 {
 	modem_info *info = (modem_info *) tty->driver_data;
 
@@ -961,12 +1209,13 @@
 /*
  * ------------------------------------------------------------
  * isdn_tty_throttle()
- * 
+ *
  * This routine is called by the upper-layer tty layer to signal that
  * incoming characters should be throttled.
  * ------------------------------------------------------------
  */
-static void isdn_tty_throttle(struct tty_struct *tty)
+static void
+isdn_tty_throttle(struct tty_struct *tty)
 {
 	modem_info *info = (modem_info *) tty->driver_data;
 
@@ -977,7 +1226,8 @@
 	info->mcr &= ~UART_MCR_RTS;
 }
 
-static void isdn_tty_unthrottle(struct tty_struct *tty)
+static void
+isdn_tty_unthrottle(struct tty_struct *tty)
 {
 	modem_info *info = (modem_info *) tty->driver_data;
 
@@ -1006,9 +1256,10 @@
  *          release the bus after transmitting. This must be done when
  *          the transmit shift register is empty, not be done when the
  *          transmit holding register is empty.  This functionality
- *          allows RS485 driver to be written in user space. 
+ *          allows RS485 driver to be written in user space.
  */
-static int isdn_tty_get_lsr_info(modem_info * info, uint * value)
+static int
+isdn_tty_get_lsr_info(modem_info * info, uint * value)
 {
 	u_char status;
 	uint result;
@@ -1024,9 +1275,11 @@
 }
 
 
-static int isdn_tty_get_modem_info(modem_info * info, uint * value)
+static int
+isdn_tty_get_modem_info(modem_info * info, uint * value)
 {
-	u_char control, status;
+	u_char control,
+	 status;
 	uint result;
 	ulong flags;
 
@@ -1045,76 +1298,78 @@
 	return 0;
 }
 
-static int isdn_tty_set_modem_info(modem_info * info, uint cmd, uint * value)
+static int
+isdn_tty_set_modem_info(modem_info * info, uint cmd, uint * value)
 {
 	uint arg;
-        int pre_dtr;
+	int pre_dtr;
 
-    GET_USER(arg, (uint *)value);
+	GET_USER(arg, (uint *) value);
 	switch (cmd) {
-                case TIOCMBIS:
+		case TIOCMBIS:
 #ifdef ISDN_DEBUG_MODEM_IOCTL
-                        printk(KERN_DEBUG "ttyI%d ioctl TIOCMBIS\n", info->line);
+			printk(KERN_DEBUG "ttyI%d ioctl TIOCMBIS\n", info->line);
 #endif
-                        if (arg & TIOCM_RTS) {
-                                info->mcr |= UART_MCR_RTS;
-                        }
-                        if (arg & TIOCM_DTR) {
-                                info->mcr |= UART_MCR_DTR;
-                                isdn_tty_modem_ncarrier(info);
-                        }
-                        break;
-                case TIOCMBIC:
+			if (arg & TIOCM_RTS) {
+				info->mcr |= UART_MCR_RTS;
+			}
+			if (arg & TIOCM_DTR) {
+				info->mcr |= UART_MCR_DTR;
+				isdn_tty_modem_ncarrier(info);
+			}
+			break;
+		case TIOCMBIC:
 #ifdef ISDN_DEBUG_MODEM_IOCTL
-                        printk(KERN_DEBUG "ttyI%d ioctl TIOCMBIC\n", info->line);
+			printk(KERN_DEBUG "ttyI%d ioctl TIOCMBIC\n", info->line);
 #endif
-                        if (arg & TIOCM_RTS) {
-                                info->mcr &= ~UART_MCR_RTS;
-                        }
-                        if (arg & TIOCM_DTR) {
-                                info->mcr &= ~UART_MCR_DTR;
-                                if (info->emu.mdmreg[13] & 4) {
-                                        isdn_tty_modem_reset_regs(info, 0);
+			if (arg & TIOCM_RTS) {
+				info->mcr &= ~UART_MCR_RTS;
+			}
+			if (arg & TIOCM_DTR) {
+				info->mcr &= ~UART_MCR_DTR;
+				if (info->emu.mdmreg[13] & 4) {
+					isdn_tty_modem_reset_regs(info, 0);
 #ifdef ISDN_DEBUG_MODEM_HUP
-                                        printk(KERN_DEBUG "Mhup in TIOCMBIC\n");
+					printk(KERN_DEBUG "Mhup in TIOCMBIC\n");
 #endif
-                                        if (info->online)
-                                                info->ncarrier = 1;
-                                        isdn_tty_modem_hup(info);
-                                }
-                        }
-                        break;
-                case TIOCMSET:
+					if (info->online)
+						info->ncarrier = 1;
+					isdn_tty_modem_hup(info, 1);
+				}
+			}
+			break;
+		case TIOCMSET:
 #ifdef ISDN_DEBUG_MODEM_IOCTL
-                        printk(KERN_DEBUG "ttyI%d ioctl TIOCMSET\n", info->line);
+			printk(KERN_DEBUG "ttyI%d ioctl TIOCMSET\n", info->line);
 #endif
-                        pre_dtr = (info->mcr & UART_MCR_DTR);
-                        info->mcr = ((info->mcr & ~(UART_MCR_RTS | UART_MCR_DTR))
-                                     | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
-                                     | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
-                        if (pre_dtr |= (info->mcr & UART_MCR_DTR)) {
-                                if (!(info->mcr & UART_MCR_DTR)) {
-                                        if (info->emu.mdmreg[13] & 4) {
-                                                isdn_tty_modem_reset_regs(info, 0);
+			pre_dtr = (info->mcr & UART_MCR_DTR);
+			info->mcr = ((info->mcr & ~(UART_MCR_RTS | UART_MCR_DTR))
+				 | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
+			       | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
+			if (pre_dtr |= (info->mcr & UART_MCR_DTR)) {
+				if (!(info->mcr & UART_MCR_DTR)) {
+					if (info->emu.mdmreg[13] & 4) {
+						isdn_tty_modem_reset_regs(info, 0);
 #ifdef ISDN_DEBUG_MODEM_HUP
-                                                printk(KERN_DEBUG "Mhup in TIOCMSET\n");
+						printk(KERN_DEBUG "Mhup in TIOCMSET\n");
 #endif
-                                                if (info->online)
-                                                        info->ncarrier = 1;
-                                                isdn_tty_modem_hup(info);
-                                        }
-                                } else
-                                        isdn_tty_modem_ncarrier(info);
-                        }
-                        break;
-                default:
-                        return -EINVAL;
+						if (info->online)
+							info->ncarrier = 1;
+						isdn_tty_modem_hup(info, 1);
+					}
+				} else
+					isdn_tty_modem_ncarrier(info);
+			}
+			break;
+		default:
+			return -EINVAL;
 	}
 	return 0;
 }
 
-static int isdn_tty_ioctl(struct tty_struct *tty, struct file *file,
-		       uint cmd, ulong arg)
+static int
+isdn_tty_ioctl(struct tty_struct *tty, struct file *file,
+	       uint cmd, ulong arg)
 {
 	modem_info *info = (modem_info *) tty->driver_data;
 	int error;
@@ -1122,97 +1377,98 @@
 
 	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_ioctl"))
 		return -ENODEV;
-        if (tty->flags & (1 << TTY_IO_ERROR))
-                return -EIO;
+	if (tty->flags & (1 << TTY_IO_ERROR))
+		return -EIO;
 	switch (cmd) {
-                case TCSBRK:		/* SVID version: non-zero arg --> no break */
+		case TCSBRK:   /* SVID version: non-zero arg --> no break */
 #ifdef ISDN_DEBUG_MODEM_IOCTL
-                        printk(KERN_DEBUG "ttyI%d ioctl TCSBRK\n", info->line);
+			printk(KERN_DEBUG "ttyI%d ioctl TCSBRK\n", info->line);
 #endif
-                        retval = tty_check_change(tty);
-                        if (retval)
-                                return retval;
-                        tty_wait_until_sent(tty, 0);
-                        return 0;
-                case TCSBRKP:		/* support for POSIX tcsendbreak() */
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			return 0;
+		case TCSBRKP:  /* support for POSIX tcsendbreak() */
 #ifdef ISDN_DEBUG_MODEM_IOCTL
-                        printk(KERN_DEBUG "ttyI%d ioctl TCSBRKP\n", info->line);
+			printk(KERN_DEBUG "ttyI%d ioctl TCSBRKP\n", info->line);
 #endif
-                        retval = tty_check_change(tty);
-                        if (retval)
-                                return retval;
-                        tty_wait_until_sent(tty, 0);
-                        return 0;
-                case TIOCGSOFTCAR:
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			return 0;
+		case TIOCGSOFTCAR:
 #ifdef ISDN_DEBUG_MODEM_IOCTL
-                        printk(KERN_DEBUG "ttyI%d ioctl TIOCGSOFTCAR\n", info->line);
+			printk(KERN_DEBUG "ttyI%d ioctl TIOCGSOFTCAR\n", info->line);
 #endif
-                        error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
-                        if (error)
-                                return error;
-                        put_user(C_CLOCAL(tty) ? 1 : 0, (ulong *) arg);
-                        return 0;
-                case TIOCSSOFTCAR:
+			error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
+			if (error)
+				return error;
+			put_user(C_CLOCAL(tty) ? 1 : 0, (ulong *) arg);
+			return 0;
+		case TIOCSSOFTCAR:
 #ifdef ISDN_DEBUG_MODEM_IOCTL
-                        printk(KERN_DEBUG "ttyI%d ioctl TIOCSSOFTCAR\n", info->line);
+			printk(KERN_DEBUG "ttyI%d ioctl TIOCSSOFTCAR\n", info->line);
 #endif
-                        error = verify_area(VERIFY_READ, (void *) arg, sizeof(long));
-                        if (error)
-                                return error;
-                        GET_USER(arg, (ulong *) arg);
-                        tty->termios->c_cflag =
-                                ((tty->termios->c_cflag & ~CLOCAL) |
-                                 (arg ? CLOCAL : 0));
-                        return 0;
-                case TIOCMGET:
+			error = verify_area(VERIFY_READ, (void *) arg, sizeof(long));
+			if (error)
+				return error;
+			GET_USER(arg, (ulong *) arg);
+			tty->termios->c_cflag =
+			    ((tty->termios->c_cflag & ~CLOCAL) |
+			     (arg ? CLOCAL : 0));
+			return 0;
+		case TIOCMGET:
 #ifdef ISDN_DEBUG_MODEM_IOCTL
-                        printk(KERN_DEBUG "ttyI%d ioctl TIOCMGET\n", info->line);
+			printk(KERN_DEBUG "ttyI%d ioctl TIOCMGET\n", info->line);
 #endif
-                        error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint));
-                        if (error)
-                                return error;
-                        return isdn_tty_get_modem_info(info, (uint *) arg);
-                case TIOCMBIS:
-                case TIOCMBIC:
-                case TIOCMSET:
-                        error = verify_area(VERIFY_READ, (void *) arg, sizeof(uint));
-                        if (error)
-                                return error;
-                        return isdn_tty_set_modem_info(info, cmd, (uint *) arg);
-                case TIOCSERGETLSR:	/* Get line status register */
+			error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint));
+			if (error)
+				return error;
+			return isdn_tty_get_modem_info(info, (uint *) arg);
+		case TIOCMBIS:
+		case TIOCMBIC:
+		case TIOCMSET:
+			error = verify_area(VERIFY_READ, (void *) arg, sizeof(uint));
+			if (error)
+				return error;
+			return isdn_tty_set_modem_info(info, cmd, (uint *) arg);
+		case TIOCSERGETLSR:	/* Get line status register */
 #ifdef ISDN_DEBUG_MODEM_IOCTL
-                        printk(KERN_DEBUG "ttyI%d ioctl TIOCSERGETLSR\n", info->line);
+			printk(KERN_DEBUG "ttyI%d ioctl TIOCSERGETLSR\n", info->line);
 #endif
-                        error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint));
-                        if (error)
-                                return error;
-                        else
-                                return isdn_tty_get_lsr_info(info, (uint *) arg);
-                        
-                default:
+			error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint));
+			if (error)
+				return error;
+			else
+				return isdn_tty_get_lsr_info(info, (uint *) arg);
+
+		default:
 #ifdef ISDN_DEBUG_MODEM_IOCTL
-                        printk(KERN_DEBUG "UNKNOWN ioctl 0x%08x on ttyi%d\n", cmd, info->line);
+			printk(KERN_DEBUG "UNKNOWN ioctl 0x%08x on ttyi%d\n", cmd, info->line);
 #endif
-                        return -ENOIOCTLCMD;
+			return -ENOIOCTLCMD;
 	}
 	return 0;
 }
 
-static void isdn_tty_set_termios(struct tty_struct *tty, struct termios *old_termios)
+static void
+isdn_tty_set_termios(struct tty_struct *tty, struct termios *old_termios)
 {
 	modem_info *info = (modem_info *) tty->driver_data;
 
-        if (!old_termios)
-                isdn_tty_change_speed(info);
-        else {
-                if (tty->termios->c_cflag == old_termios->c_cflag)
-                        return;
-                isdn_tty_change_speed(info);
-                if ((old_termios->c_cflag & CRTSCTS) &&
-                    !(tty->termios->c_cflag & CRTSCTS)) {
-                        tty->hw_stopped = 0;
-                }
-        }
+	if (!old_termios)
+		isdn_tty_change_speed(info);
+	else {
+		if (tty->termios->c_cflag == old_termios->c_cflag)
+			return;
+		isdn_tty_change_speed(info);
+		if ((old_termios->c_cflag & CRTSCTS) &&
+		    !(tty->termios->c_cflag & CRTSCTS)) {
+			tty->hw_stopped = 0;
+		}
+	}
 }
 
 /*
@@ -1220,9 +1476,11 @@
  * isdn_tty_open() and friends
  * ------------------------------------------------------------
  */
-static int isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * info)
+static int
+isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * info)
 {
-	struct wait_queue wait = {current, NULL};
+	struct wait_queue wait =
+	{current, NULL};
 	int do_clocal = 0;
 	unsigned long flags;
 	int retval;
@@ -1233,8 +1491,8 @@
 	 */
 	if (tty_hung_up_p(filp) ||
 	    (info->flags & ISDN_ASYNC_CLOSING)) {
-                if (info->flags & ISDN_ASYNC_CLOSING)
-                        interruptible_sleep_on(&info->close_wait);
+		if (info->flags & ISDN_ASYNC_CLOSING)
+			interruptible_sleep_on(&info->close_wait);
 #ifdef MODEM_DO_RESTART
 		if (info->flags & ISDN_ASYNC_HUP_NOTIFY)
 			return -EAGAIN;
@@ -1267,7 +1525,7 @@
 	 * and then exit.
 	 */
 	if ((filp->f_flags & O_NONBLOCK) ||
-            (tty->flags & (1 << TTY_IO_ERROR))) {
+	    (tty->flags & (1 << TTY_IO_ERROR))) {
 		if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE)
 			return -EBUSY;
 		info->flags |= ISDN_ASYNC_NORMAL_ACTIVE;
@@ -1293,11 +1551,11 @@
 	printk(KERN_DEBUG "isdn_tty_block_til_ready before block: ttyi%d, count = %d\n",
 	       info->line, info->count);
 #endif
-        save_flags(flags);
-        cli();
-        if (!(tty_hung_up_p(filp)))
-                info->count--;
-        restore_flags(flags);
+	save_flags(flags);
+	cli();
+	if (!(tty_hung_up_p(filp)))
+		info->count--;
+	restore_flags(flags);
 	info->blocked_open++;
 	while (1) {
 		current->state = TASK_INTERRUPTIBLE;
@@ -1349,10 +1607,12 @@
  * the IRQ chain.   It also performs the serial-specific
  * initialization for the tty structure.
  */
-static int isdn_tty_open(struct tty_struct *tty, struct file *filp)
+static int
+isdn_tty_open(struct tty_struct *tty, struct file *filp)
 {
 	modem_info *info;
-	int retval, line;
+	int retval,
+	 line;
 
 	line = MINOR(tty->device) - tty->driver.minor_start;
 	if (line < 0 || line > ISDN_MAX_CHANNELS)
@@ -1403,7 +1663,8 @@
 	return 0;
 }
 
-static void isdn_tty_close(struct tty_struct *tty, struct file *filp)
+static void
+isdn_tty_close(struct tty_struct *tty, struct file *filp)
 {
 	modem_info *info = (modem_info *) tty->driver_data;
 	ulong flags;
@@ -1454,7 +1715,7 @@
 	if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE)
 		info->callout_termios = *tty->termios;
 
-        tty->closing = 1;
+	tty->closing = 1;
 	/*
 	 * At this point we stop accepting input.  To do this, we
 	 * disable the receive line status interrupts, and tell the
@@ -1462,7 +1723,7 @@
 	 * line status register.
 	 */
 	if (info->flags & ISDN_ASYNC_INITIALIZED) {
-		tty_wait_until_sent(tty, 3000);		/* 30 seconds timeout */
+		tty_wait_until_sent(tty, 3000);	/* 30 seconds timeout */
 		/*
 		 * Before we drop DTR, make sure the UART transmitter
 		 * has completely drained; this is especially
@@ -1484,12 +1745,12 @@
 	if (tty->ldisc.flush_buffer)
 		tty->ldisc.flush_buffer(tty);
 	info->tty = 0;
-        info->ncarrier = 0;
+	info->ncarrier = 0;
 	tty->closing = 0;
 	if (info->blocked_open) {
-                current->state = TASK_INTERRUPTIBLE;
-                current->timeout = jiffies + 50;
-                schedule();
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = jiffies + 50;
+		schedule();
 		wake_up_interruptible(&info->open_wait);
 	}
 	info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE |
@@ -1504,7 +1765,8 @@
 /*
  * isdn_tty_hangup() --- called by tty_hangup() when a hangup is signaled.
  */
-static void isdn_tty_hangup(struct tty_struct *tty)
+static void
+isdn_tty_hangup(struct tty_struct *tty)
 {
 	modem_info *info = (modem_info *) tty->driver_data;
 
@@ -1519,7 +1781,8 @@
 
 /* This routine initializes all emulator-data.
  */
-static void isdn_tty_reset_profile(atemu * m)
+static void
+isdn_tty_reset_profile(atemu * m)
 {
 	m->profile[0] = 0;
 	m->profile[1] = 0;
@@ -1545,27 +1808,34 @@
 	m->pmsn[0] = '\0';
 }
 
-static void isdn_tty_modem_reset_vpar(atemu *m)
+#ifdef CONFIG_ISDN_AUDIO
+static void
+isdn_tty_modem_reset_vpar(atemu * m)
 {
-        m->vpar[0] = 2;  /* Voice-device            (2 = phone line) */
-        m->vpar[1] = 0;  /* Silence detection level (0 = none      ) */
-        m->vpar[2] = 70; /* Silence interval        (7 sec.        ) */
-        m->vpar[3] = 2;  /* Compression type        (1 = ADPCM-2   ) */
+	m->vpar[0] = 2;         /* Voice-device            (2 = phone line) */
+	m->vpar[1] = 0;         /* Silence detection level (0 = none      ) */
+	m->vpar[2] = 70;        /* Silence interval        (7 sec.        ) */
+	m->vpar[3] = 2;         /* Compression type        (1 = ADPCM-2   ) */
 }
+#endif
 
-static void isdn_tty_modem_reset_regs(modem_info *info, int force)
+static void
+isdn_tty_modem_reset_regs(modem_info * info, int force)
 {
-        atemu *m = &info->emu;
+	atemu *m = &info->emu;
 	if ((m->mdmreg[12] & 32) || force) {
 		memcpy(m->mdmreg, m->profile, ISDN_MODEM_ANZREG);
 		memcpy(m->msn, m->pmsn, ISDN_MSNLEN);
-                info->xmit_size = m->mdmreg[16] * 16;
+		info->xmit_size = m->mdmreg[16] * 16;
 	}
-        isdn_tty_modem_reset_vpar(m);
+#ifdef CONFIG_ISDN_AUDIO
+	isdn_tty_modem_reset_vpar(m);
+#endif
 	m->mdmcmdl = 0;
 }
 
-static void modem_write_profile(atemu * m)
+static void
+modem_write_profile(atemu * m)
 {
 	memcpy(m->profile, m->mdmreg, ISDN_MODEM_ANZREG);
 	memcpy(m->pmsn, m->msn, ISDN_MSNLEN);
@@ -1573,7 +1843,8 @@
 		send_sig(SIGIO, dev->profd, 1);
 }
 
-int isdn_tty_modem_init(void)
+int
+isdn_tty_modem_init(void)
 {
 	modem *m;
 	int i;
@@ -1630,6 +1901,12 @@
 	}
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
 		info = &m->info[i];
+		sprintf(info->last_cause, "0000");
+		sprintf(info->last_num, "none");
+		info->last_dir = 0;
+		info->last_lhup = 1;
+		info->last_l2 = 0;
+		info->last_si = 0;
 		isdn_tty_reset_profile(&info->emu);
 		isdn_tty_modem_reset_regs(info, 1);
 		info->magic = ISDN_ASYNC_MAGIC;
@@ -1646,14 +1923,16 @@
 		info->isdn_channel = -1;
 		info->drv_index = -1;
 		info->xmit_size = ISDN_SERIAL_XMIT_SIZE;
-                skb_queue_head_init(&info->xmit_queue);
-                skb_queue_head_init(&info->dtmf_queue);
-                if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_SIZE + 5, GFP_KERNEL))) {
-                        printk(KERN_ERR "Could not allocate modem xmit-buffer\n");
-                        return -3;
-                }
-                /* Make room for T.70 header */
-                info->xmit_buf += 4;
+		skb_queue_head_init(&info->xmit_queue);
+#ifdef CONFIG_ISDN_AUDIO
+		skb_queue_head_init(&info->dtmf_queue);
+#endif
+		if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_SIZE + 5, GFP_KERNEL))) {
+			printk(KERN_ERR "Could not allocate modem xmit-buffer\n");
+			return -3;
+		}
+		/* Make room for T.70 header */
+		info->xmit_buf += 4;
 	}
 	return 0;
 }
@@ -1664,61 +1943,46 @@
  * it to the ISDN-Channel.
  * Return Index to dev->mdm or -1 if none found.
  */
-int isdn_tty_find_icall(int di, int ch, char *num)
+int
+isdn_tty_find_icall(int di, int ch, setup_parm setup)
 {
 	char *eaz;
 	int i;
 	int idx;
 	int si1;
 	int si2;
-	char *s;
-	char nr[31];
+	char nr[32];
 	ulong flags;
 
 	save_flags(flags);
 	cli();
-	if (num[0] == ',') {
+	if (!setup.phone[0]) {
 		nr[0] = '0';
-		strncpy(&nr[1], num, 29);
+		nr[1] = '\0';
 		printk(KERN_INFO "isdn_tty: Incoming call without OAD, assuming '0'\n");
 	} else
-		strncpy(nr, num, 30);
-	s = strtok(nr, ",");
-	s = strtok(NULL, ",");
-	if (!s) {
-		printk(KERN_WARNING "isdn_tty: Incoming callinfo garbled, ignored: %s\n",
-		       num);
-		restore_flags(flags);
-		return -1;
-	}
-	si1 = (int)simple_strtoul(s,NULL,10);
-	s = strtok(NULL, ",");
-	if (!s) {
-		printk(KERN_WARNING "isdn_tty: Incoming callinfo garbled, ignored: %s\n",
-		       num);
-		restore_flags(flags);
-		return -1;
-	}
-	si2 = (int)simple_strtoul(s,NULL,10);
-	eaz = strtok(NULL, ",");
-	if (!eaz) {
+		strcpy(nr, setup.phone);
+	si1 = (int) setup.si1;
+	si2 = (int) setup.si2;
+	if (!setup.eazmsn[0]) {
 		printk(KERN_WARNING "isdn_tty: Incoming call without CPN, assuming '0'\n");
 		eaz = "0";
-	}
+	} else
+		eaz = setup.eazmsn;
 #ifdef ISDN_DEBUG_MODEM_ICALL
 	printk(KERN_DEBUG "m_fi: eaz=%s si1=%d si2=%d\n", eaz, si1, si2);
 #endif
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-                modem_info *info = &dev->mdm.info[i];
+		modem_info *info = &dev->mdm.info[i];
 #ifdef ISDN_DEBUG_MODEM_ICALL
 		printk(KERN_DEBUG "m_fi: i=%d msn=%s mmsn=%s mreg18=%d mreg19=%d\n", i,
 		       info->emu.msn, isdn_map_eaz2msn(info->emu.msn, di),
 		       info->emu.mdmreg[18], info->emu.mdmreg[19]);
 #endif
 		if ((!strcmp(isdn_map_eaz2msn(info->emu.msn, di),
-                             eaz)) &&                             /* EAZ is matching      */
-		    (info->emu.mdmreg[18] & si2bit[si1]) &&       /* SI1 is matching      */
-		    ((info->emu.mdmreg[19] == si2) || !si2)) {    /* SI2 is matching or 0 */
+			     eaz)) &&	/* EAZ is matching      */
+		    (info->emu.mdmreg[18] & si2bit[si1]) &&	/* SI1 is matching      */
+		    ((info->emu.mdmreg[19] == si2) || !si2)) {	/* SI2 is matching or 0 */
 			idx = isdn_dc2minor(di, ch);
 #ifdef ISDN_DEBUG_MODEM_ICALL
 			printk(KERN_DEBUG "m_fi: match1\n");
@@ -1735,13 +1999,18 @@
 				info->drv_index = idx;
 				dev->m_idx[idx] = info->line;
 				dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
-				dev->usage[idx] |= (si1==1)?ISDN_USAGE_VOICE:ISDN_USAGE_MODEM;
+				dev->usage[idx] |= (si1 == 1) ? ISDN_USAGE_VOICE : ISDN_USAGE_MODEM;
 				strcpy(dev->num[idx], nr);
-                                info->emu.mdmreg[20] = si2bit[si1];
+				info->emu.mdmreg[20] = si2bit[si1];
+				info->emu.mdmreg[21] = setup.plan;
+				info->emu.mdmreg[22] = setup.screen;
 				isdn_info_update();
 				restore_flags(flags);
 				printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr,
 				       info->line);
+				info->msr |= UART_MSR_RI;
+				isdn_tty_modem_result(2, info);
+				isdn_timer_ctrl(ISDN_TIMER_MODEMRING, 1);
 				return info->line;
 			}
 		}
@@ -1752,6 +2021,140 @@
 	return -1;
 }
 
+#define TTY_IS_ACTIVE(info) \
+	(info->flags & (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE))
+
+int
+isdn_tty_stat_callback(int i, isdn_ctrl * c)
+{
+	int mi;
+	modem_info *info;
+
+	if (i < 0)
+		return 0;
+	if ((mi = dev->m_idx[i]) >= 0) {
+		info = &dev->mdm.info[mi];
+		switch (c->command) {
+			case ISDN_STAT_BSENT:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_BSENT ttyI%d\n", info->line);
+#endif
+				if ((info->isdn_driver == c->driver) &&
+				    (info->isdn_channel == c->arg)) {
+					info->msr |= UART_MSR_CTS;
+					if (info->send_outstanding)
+						if (!(--info->send_outstanding))
+							info->lsr |= UART_LSR_TEMT;
+					isdn_tty_tint(info);
+					return 1;
+				}
+				break;
+			case ISDN_STAT_CAUSE:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_CAUSE ttyI%d\n", info->line);
+#endif
+				/* Signal cause to tty-device */
+				strncpy(info->last_cause, c->parm.num, 5);
+				return 1;
+			case ISDN_STAT_DCONN:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_DCONN ttyI%d\n", info->line);
+#endif
+				if (TTY_IS_ACTIVE(info)) {
+					if (info->dialing == 1) {
+						info->dialing = 2;
+						return 1;
+					}
+				}
+				break;
+			case ISDN_STAT_DHUP:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_DHUP ttyI%d\n", info->line);
+#endif
+				if (TTY_IS_ACTIVE(info)) {
+					if (info->dialing == 1) {
+						info->dialing = 0;
+						isdn_tty_modem_result(7, info);
+					}
+#ifdef ISDN_DEBUG_MODEM_HUP
+					printk(KERN_DEBUG "Mhup in ISDN_STAT_DHUP\n");
+#endif
+					isdn_tty_modem_hup(info, 0);
+					return 1;
+				}
+				break;
+			case ISDN_STAT_BCONN:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_BCONN ttyI%d\n", info->line);
+#endif
+				/* Schedule CONNECT-Message to any tty
+				 * waiting for it and
+				 * set DCD-bit of its modem-status.
+				 */
+				if (TTY_IS_ACTIVE(info)) {
+					info->msr |= UART_MSR_DCD;
+					if (info->dialing) {
+						info->dialing = 0;
+						info->last_dir = 1;
+					} else
+						info->last_dir = 0;
+					info->rcvsched = 1;
+					if (USG_MODEM(dev->usage[i]))
+						isdn_tty_modem_result(5, info);
+					if (USG_VOICE(dev->usage[i]))
+						isdn_tty_modem_result(11, info);
+					return 1;
+				}
+				break;
+			case ISDN_STAT_BHUP:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_BHUP ttyI%d\n", info->line);
+#endif
+				if (TTY_IS_ACTIVE(info)) {
+#ifdef ISDN_DEBUG_MODEM_HUP
+					printk(KERN_DEBUG "Mhup in ISDN_STAT_BHUP\n");
+#endif
+					isdn_tty_modem_hup(info, 0);
+					return 1;
+				}
+				break;
+			case ISDN_STAT_NODCH:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_NODCH ttyI%d\n", info->line);
+#endif
+				if (TTY_IS_ACTIVE(info)) {
+					if (info->dialing) {
+						info->dialing = 0;
+						info->last_l2 = -1;
+						info->last_si = 0;
+						sprintf(info->last_cause, "0000");
+						isdn_tty_modem_result(6, info);
+					}
+					info->msr &= ~UART_MSR_DCD;
+					if (info->online) {
+						isdn_tty_modem_result(3, info);
+						info->online = 0;
+					}
+					return 1;
+				}
+				break;
+			case ISDN_STAT_UNLOAD:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_UNLOAD ttyI%d\n", info->line);
+#endif
+				for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+					info = &dev->mdm.info[i];
+					if (info->isdn_driver == c->driver) {
+						if (info->online)
+							isdn_tty_modem_hup(info, 1);
+					}
+				}
+				return 1;
+		}
+	}
+	return 0;
+}
+
 /*********************************************************************
  Modem-Emulator-Routines
  *********************************************************************/
@@ -1762,7 +2165,8 @@
  * Put a message from the AT-emulator into receive-buffer of tty,
  * convert CR, LF, and BS to values in modem-registers 3, 4 and 5.
  */
-static void isdn_tty_at_cout(char *msg, modem_info * info)
+static void
+isdn_tty_at_cout(char *msg, modem_info * info)
 {
 	struct tty_struct *tty;
 	atemu *m = &info->emu;
@@ -1779,17 +2183,17 @@
 	tty = info->tty;
 	for (p = msg; *p; p++) {
 		switch (*p) {
-                        case '\r':
-                                c = m->mdmreg[3];
-                                break;
-                        case '\n':
-                                c = m->mdmreg[4];
-                                break;
-                        case '\b':
-                                c = m->mdmreg[5];
-                                break;
-                        default:
-                                c = *p;
+			case '\r':
+				c = m->mdmreg[3];
+				break;
+			case '\n':
+				c = m->mdmreg[4];
+				break;
+			case '\b':
+				c = m->mdmreg[5];
+				break;
+			default:
+				c = *p;
 		}
 		if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) {
 			restore_flags(flags);
@@ -1806,24 +2210,25 @@
 /*
  * Perform ATH Hangup
  */
-static void isdn_tty_on_hook(modem_info * info)
+static void
+isdn_tty_on_hook(modem_info * info)
 {
 	if (info->isdn_channel >= 0) {
 #ifdef ISDN_DEBUG_MODEM_HUP
 		printk(KERN_DEBUG "Mhup in isdn_tty_on_hook\n");
 #endif
-		isdn_tty_modem_result(3, info);
-		isdn_tty_modem_hup(info);
+		isdn_tty_modem_hup(info, 1);
 	}
 }
 
-static void isdn_tty_off_hook(void)
+static void
+isdn_tty_off_hook(void)
 {
 	printk(KERN_DEBUG "isdn_tty_off_hook\n");
 }
 
-#define PLUSWAIT1 (HZ/2)	/* 0.5 sec. */
-#define PLUSWAIT2 (HZ*3/2)	/* 1.5 sec */
+#define PLUSWAIT1 (HZ/2)        /* 0.5 sec. */
+#define PLUSWAIT2 (HZ*3/2)      /* 1.5 sec */
 
 /*
  * Check Buffer for Modem-escape-sequence, activate timer-callback to
@@ -1836,8 +2241,9 @@
  *   pluscount  count of valid escape-characters so far
  *   lastplus   timestamp of last character
  */
-static void isdn_tty_check_esc(const u_char * p, u_char plus, int count, int *pluscount,
-			   int *lastplus, int from_user)
+static void
+isdn_tty_check_esc(const u_char * p, u_char plus, int count, int *pluscount,
+		   int *lastplus, int from_user)
 {
 	char cbuf[3];
 
@@ -1880,7 +2286,8 @@
  * For CONNECT-messages also switch to online-mode.
  * For RING-message handle auto-ATA if register 0 != 0
  */
-void isdn_tty_modem_result(int code, modem_info * info)
+static void
+isdn_tty_modem_result(int code, modem_info * info)
 {
 	atemu *m = &info->emu;
 	static char *msg[] =
@@ -1888,47 +2295,66 @@
 	 "CONNECT 64000", "NO DIALTONE", "BUSY", "NO ANSWER",
 	 "RINGING", "NO MSN/EAZ", "VCON"};
 	ulong flags;
-	char s[4];
+	char s[10];
 
 	switch (code) {
-                case 2:
-                        m->mdmreg[1]++;	/* RING */
-                        if (m->mdmreg[1] == m->mdmreg[0])
-                                /* Automatically accept incoming call */
-                                isdn_tty_cmd_ATA(info);
-                        break;
-                case 3:
-                        /* NO CARRIER */
-                        save_flags(flags);
-                        cli();
+		case 2:
+			m->mdmreg[1]++;	/* RING */
+			if (m->mdmreg[1] == m->mdmreg[0])
+				/* Automatically accept incoming call */
+				isdn_tty_cmd_ATA(info);
+			break;
+		case 3:
+			/* NO CARRIER */
 #ifdef ISDN_DEBUG_MODEM_HUP
-                        printk(KERN_DEBUG "modem_result: NO CARRIER %d %d\n",
-                               (info->flags & ISDN_ASYNC_CLOSING),
-                               (!info->tty));
-#endif
-                        if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) {
-                                restore_flags(flags);
-                                return;
-                        }
-                        restore_flags(flags);
-                        if (info->vonline & 1) {
-                                /* voice-recording, add DLE-ETX */
-                                isdn_tty_at_cout("\020\003", info);
-                        }
-                        if (info->vonline & 2) {
-                                /* voice-playing, add DLE-DC4 */
-                                isdn_tty_at_cout("\020\024", info);
-                        }
-                        break;
-                case 1:
-                case 5:
-                        if (!info->online)
-                                info->online = 2;
-                        break;
-                case 11:
-                        if (!info->online)
-                                info->online = 1;
-                        break;
+			printk(KERN_DEBUG "modem_result: NO CARRIER %d %d\n",
+			       (info->flags & ISDN_ASYNC_CLOSING),
+			       (!info->tty));
+#endif
+			save_flags(flags);
+			cli();
+			m->mdmreg[1] = 0;
+			del_timer(&info->nc_timer);
+			info->ncarrier = 0;
+			if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) {
+				restore_flags(flags);
+				return;
+			}
+			restore_flags(flags);
+#ifdef CONFIG_ISDN_AUDIO
+			if (info->vonline & 1) {
+#ifdef ISDN_DEBUG_MODEM_VOICE
+				printk(KERN_DEBUG "res3: send DLE-ETX on ttyI%d\n",
+				       info->line);
+#endif
+				/* voice-recording, add DLE-ETX */
+				isdn_tty_at_cout("\020\003", info);
+			}
+			if (info->vonline & 2) {
+#ifdef ISDN_DEBUG_MODEM_VOICE
+				printk(KERN_DEBUG "res3: send DLE-DC4 on ttyI%d\n",
+				       info->line);
+#endif
+				/* voice-playing, add DLE-DC4 */
+				isdn_tty_at_cout("\020\024", info);
+			}
+#endif
+			break;
+		case 1:
+		case 5:
+			sprintf(info->last_cause, "0000");
+			if (!info->online)
+				info->online = 2;
+			break;
+		case 11:
+#ifdef ISDN_DEBUG_MODEM_VOICE
+			printk(KERN_DEBUG "res3: send VCON on ttyI%d\n",
+			       info->line);
+#endif
+			sprintf(info->last_cause, "0000");
+			if (!info->online)
+				info->online = 1;
+			break;
 	}
 	if (m->mdmreg[12] & 1) {
 		/* Show results */
@@ -1938,17 +2364,38 @@
 			sprintf(s, "%d", code);
 			isdn_tty_at_cout(s, info);
 		} else {
-			if (code == 2) {
+			if ((code == 2) && (!(m->mdmreg[13] & 16))) {
 				isdn_tty_at_cout("CALLER NUMBER: ", info);
 				isdn_tty_at_cout(dev->num[info->drv_index], info);
 				isdn_tty_at_cout("\r\n", info);
 			}
 			isdn_tty_at_cout(msg[code], info);
-			if (code == 5) {
-				/* Append Protocol to CONNECT message */
-				isdn_tty_at_cout((m->mdmreg[14] != 3) ? "/X.75" : "/HDLC", info);
-				if (m->mdmreg[13] & 2)
-					isdn_tty_at_cout("/T.70", info);
+			switch (code) {
+				case 2:
+					/* Print CID only once, _after_ 1.st RING */
+					if ((m->mdmreg[13] & 16) && (m->mdmreg[1] == 1)) {
+						isdn_tty_at_cout("\r\n", info);
+						isdn_tty_at_cout("CALLER NUMBER: ", info);
+						isdn_tty_at_cout(dev->num[info->drv_index], info);
+					}
+					break;
+				case 3:
+				case 6:
+				case 7:
+				case 8:
+					m->mdmreg[1] = 0;
+					/* Append Cause-Message if enabled */
+					if (m->mdmreg[13] & 8) {
+						sprintf(s, "/%s", info->last_cause);
+						isdn_tty_at_cout(s, info);
+					}
+					break;
+				case 5:
+					/* Append Protocol to CONNECT message */
+					isdn_tty_at_cout((m->mdmreg[14] != 3) ? "/X.75" : "/HDLC", info);
+					if (m->mdmreg[13] & 2)
+						isdn_tty_at_cout("/T.70", info);
+					break;
 			}
 		}
 		isdn_tty_at_cout("\r\n", info);
@@ -1960,13 +2407,13 @@
 			restore_flags(flags);
 			return;
 		}
-                if (info->tty->ldisc.flush_buffer)
-                        info->tty->ldisc.flush_buffer(info->tty);
+		if (info->tty->ldisc.flush_buffer)
+			info->tty->ldisc.flush_buffer(info->tty);
 		if ((info->flags & ISDN_ASYNC_CHECK_CD) &&
 		    (!((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
 		       (info->flags & ISDN_ASYNC_CALLOUT_NOHUP)))) {
 			tty_hangup(info->tty);
-                }
+		}
 		restore_flags(flags);
 	}
 }
@@ -1974,7 +2421,8 @@
 /*
  * Display a modem-register-value.
  */
-static void isdn_tty_show_profile(int ridx, modem_info * info)
+static void
+isdn_tty_show_profile(int ridx, modem_info * info)
 {
 	char v[6];
 
@@ -1985,7 +2433,8 @@
 /*
  * Get MSN-string from char-pointer, set pointer to end of number
  */
-static void isdn_tty_get_msnstr(char *n, char **p)
+static void
+isdn_tty_get_msnstr(char *n, char **p)
 {
 	while ((*p[0] >= '0' && *p[0] <= '9') || (*p[0] == ','))
 		*n++ = *p[0]++;
@@ -1995,13 +2444,17 @@
 /*
  * Get phone-number from modem-commandbuffer
  */
-static void isdn_tty_getdial(char *p, char *q)
+static void
+isdn_tty_getdial(char *p, char *q, int max)
 {
 	int first = 1;
 
-	while (strchr("0123456789,#.*WPTS-", *p) && *p) {
-		if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first))
+	max--;
+	while (strchr("0123456789,#.*WPTS-", *p) && *p && (max > 0)) {
+		if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first)) {
 			*q++ = *p;
+			max--;
+		}
 		p++;
 		first = 0;
 	}
@@ -2011,648 +2464,775 @@
 #define PARSE_ERROR { isdn_tty_modem_result(4, info); return; }
 #define PARSE_ERROR1 { isdn_tty_modem_result(4, info); return 1; }
 
+static void
+isdn_tty_report(modem_info * info)
+{
+	atemu *m = &info->emu;
+	char s[80];
+
+	isdn_tty_at_cout("\r\nStatistics of last connection:\r\n\r\n", info);
+	sprintf(s, "    Remote Number:    %s\r\n", info->last_num);
+	isdn_tty_at_cout(s, info);
+	sprintf(s, "    Direction:        %s\r\n", info->last_dir ? "outgoing" : "incoming");
+	isdn_tty_at_cout(s, info);
+	isdn_tty_at_cout("    Layer-2 Protocol: ", info);
+	switch (info->last_l2) {
+		case ISDN_PROTO_L2_X75I:
+			isdn_tty_at_cout("x75i", info);
+			break;
+		case ISDN_PROTO_L2_X75UI:
+			isdn_tty_at_cout("x75ui", info);
+			break;
+		case ISDN_PROTO_L2_X75BUI:
+			isdn_tty_at_cout("x75bui", info);
+			break;
+		case ISDN_PROTO_L2_HDLC:
+			isdn_tty_at_cout("hdlc", info);
+			break;
+		case ISDN_PROTO_L2_TRANS:
+			isdn_tty_at_cout("transparent", info);
+			break;
+		default:
+			isdn_tty_at_cout("unknown", info);
+			break;
+	}
+	isdn_tty_at_cout((m->mdmreg[13] & 2) ? "/t.70\r\n" : "\r\n", info);
+	isdn_tty_at_cout("    Service:          ", info);
+	switch (info->last_si) {
+		case 1:
+			isdn_tty_at_cout("audio\r\n", info);
+			break;
+		case 5:
+			isdn_tty_at_cout("btx\r\n", info);
+			break;
+		case 7:
+			isdn_tty_at_cout("data\r\n", info);
+			break;
+		default:
+			sprintf(s, "%d\r\n", info->last_si);
+			isdn_tty_at_cout(s, info);
+			break;
+	}
+	sprintf(s, "    Hangup location:  %s\r\n", info->last_lhup ? "local" : "remote");
+	isdn_tty_at_cout(s, info);
+	sprintf(s, "    Last cause:       %s\r\n", info->last_cause);
+	isdn_tty_at_cout(s, info);
+}
+
 /*
  * Parse AT&.. commands.
  */
-static int isdn_tty_cmd_ATand(char **p, modem_info * info)
+static int
+isdn_tty_cmd_ATand(char **p, modem_info * info)
 {
-        atemu *m = &info->emu;
-        int i;
-        char rb[100];
-
-        switch (*p[0]) {
-                case 'B':
-                        /* &B - Set Buffersize */
-                        p[0]++;
-                        i = isdn_getnum(p);
-                        if ((i < 0) || (i > ISDN_SERIAL_XMIT_SIZE))
-                                PARSE_ERROR1;
-#ifdef CONFIG_ISDN_AUDIO
-                        if ((m->mdmreg[18] & 1) && (i > VBUF))
-                                PARSE_ERROR1;
-#endif
-                        m->mdmreg[16] = i / 16;
-                        info->xmit_size = m->mdmreg[16] * 16;
-                        break;
-                case 'D':
-                        /* &D - Set DCD-Low-behavior */
-                        p[0]++;
-                        switch (isdn_getnum(p)) {
-                                case 0:
-                                        m->mdmreg[13] &= ~4;
-                                        m->mdmreg[12] &= ~32;
-                                        break;
-                                case 2:
-                                        m->mdmreg[13] |= 4;
-                                        m->mdmreg[12] &= ~32;
-                                        break;
-                                case 3:
-                                        m->mdmreg[13] |= 4;
-                                        m->mdmreg[12] |= 32;
-                                        break;
-                                default:
-                                        PARSE_ERROR1
-                        }
-                        break;
-                case 'E':
-                        /* &E -Set EAZ/MSN */
-                        p[0]++;
-                        isdn_tty_get_msnstr(m->msn, p);
-                        break;
-                case 'F':
-                        /* &F -Set Factory-Defaults */
-                        p[0]++;
-                        isdn_tty_reset_profile(m);
-                        isdn_tty_modem_reset_regs(info, 1);
-                        break;
-                case 'S':
-                        /* &S - Set Windowsize */
-                        p[0]++;
-                        i = isdn_getnum(p);
-                        if ((i > 0) && (i < 9))
-                                m->mdmreg[17] = i;
-                        else
-                                PARSE_ERROR1;
-                        break;
-                case 'V':
-                        /* &V - Show registers */
-                        p[0]++;
-                        for (i = 0; i < ISDN_MODEM_ANZREG; i++) {
-                                sprintf(rb, "S%d=%d%s", i, 
-                                        m->mdmreg[i], (i == 6) ? "\r\n" : " ");
-                                isdn_tty_at_cout(rb, info);
-                        }
-                        sprintf(rb, "\r\nEAZ/MSN: %s\r\n",
-                                strlen(m->msn) ? m->msn : "None");
-                        isdn_tty_at_cout(rb, info);
-                        break;
-                case 'W':
-                        /* &W - Write Profile */
-                        p[0]++;
-                        switch (*p[0]) {
-                                case '0':
-                                        p[0]++;
-                                        modem_write_profile(m);
-                                        break;
-                                default:
-                                        PARSE_ERROR1;
-                        }
-                        break;
-                case 'X':
-                        /* &X - Switch to BTX-Mode */
-                        p[0]++;
-                        switch (isdn_getnum(p)) {
-                                case 0:
-                                        m->mdmreg[13] &= ~2;
-                                        info->xmit_size = m->mdmreg[16] * 16;
-                                        break;
-                                case 1:
-                                        m->mdmreg[13] |= 2;
-                                        m->mdmreg[14] = 0;
-                                        info->xmit_size = 112;
-                                        m->mdmreg[18] = 4;
-                                        m->mdmreg[19] = 0;
-                                        break;
-                                default:
-                                        PARSE_ERROR1;
-                        }
-                        break;
-                default:
-                        PARSE_ERROR1;
-        }
-        return 0;
+	atemu *m = &info->emu;
+	int i;
+	char rb[100];
+
+	switch (*p[0]) {
+		case 'B':
+			/* &B - Set Buffersize */
+			p[0]++;
+			i = isdn_getnum(p);
+			if ((i < 0) || (i > ISDN_SERIAL_XMIT_SIZE))
+				PARSE_ERROR1;
+#ifdef CONFIG_ISDN_AUDIO
+			if ((m->mdmreg[18] & 1) && (i > VBUF))
+				PARSE_ERROR1;
+#endif
+			m->mdmreg[16] = i / 16;
+			info->xmit_size = m->mdmreg[16] * 16;
+			break;
+		case 'D':
+			/* &D - Set DCD-Low-behavior */
+			p[0]++;
+			switch (isdn_getnum(p)) {
+				case 0:
+					m->mdmreg[13] &= ~4;
+					m->mdmreg[12] &= ~32;
+					break;
+				case 2:
+					m->mdmreg[13] |= 4;
+					m->mdmreg[12] &= ~32;
+					break;
+				case 3:
+					m->mdmreg[13] |= 4;
+					m->mdmreg[12] |= 32;
+					break;
+				default:
+					PARSE_ERROR1
+			}
+			break;
+		case 'E':
+			/* &E -Set EAZ/MSN */
+			p[0]++;
+			isdn_tty_get_msnstr(m->msn, p);
+			break;
+		case 'F':
+			/* &F -Set Factory-Defaults */
+			p[0]++;
+			isdn_tty_reset_profile(m);
+			isdn_tty_modem_reset_regs(info, 1);
+			break;
+		case 'S':
+			/* &S - Set Windowsize */
+			p[0]++;
+			i = isdn_getnum(p);
+			if ((i > 0) && (i < 9))
+				m->mdmreg[17] = i;
+			else
+				PARSE_ERROR1;
+			break;
+		case 'V':
+			/* &V - Show registers */
+			p[0]++;
+			isdn_tty_at_cout("\r\n", info);
+			for (i = 0; i < ISDN_MODEM_ANZREG; i++) {
+				sprintf(rb, "S%02d=%03d%s", i,
+					m->mdmreg[i], ((i + 1) % 10) ? " " : "\r\n");
+				isdn_tty_at_cout(rb, info);
+			}
+			sprintf(rb, "\r\nEAZ/MSN: %s\r\n",
+				strlen(m->msn) ? m->msn : "None");
+			isdn_tty_at_cout(rb, info);
+			break;
+		case 'W':
+			/* &W - Write Profile */
+			p[0]++;
+			switch (*p[0]) {
+				case '0':
+					p[0]++;
+					modem_write_profile(m);
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		case 'X':
+			/* &X - Switch to BTX-Mode */
+			p[0]++;
+			switch (isdn_getnum(p)) {
+				case 0:
+					m->mdmreg[13] &= ~2;
+					info->xmit_size = m->mdmreg[16] * 16;
+					break;
+				case 1:
+					m->mdmreg[13] |= 2;
+					m->mdmreg[14] = 0;
+					info->xmit_size = 112;
+					m->mdmreg[18] = 4;
+					m->mdmreg[19] = 0;
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		default:
+			PARSE_ERROR1;
+	}
+	return 0;
+}
+
+static int
+isdn_tty_check_ats(int mreg, int mval, modem_info * info, atemu * m)
+{
+	/* Some plausibility checks */
+	switch (mreg) {
+		case 14:
+			if (mval > ISDN_PROTO_L2_TRANS)
+				return 1;
+			break;
+		case 16:
+			if ((mval * 16) > ISDN_SERIAL_XMIT_SIZE)
+				return 1;
+#ifdef CONFIG_ISDN_AUDIO
+			if ((m->mdmreg[18] & 1) && (mval > VBUFX))
+				return 1;
+#endif
+			info->xmit_size = mval * 16;
+			break;
+		case 20:
+		case 21:
+		case 22:
+			/* readonly registers */
+			return 1;
+	}
+	return 0;
 }
 
 /*
  * Perform ATS command
  */
-static int isdn_tty_cmd_ATS(char **p, modem_info * info)
+static int
+isdn_tty_cmd_ATS(char **p, modem_info * info)
 {
-        atemu *m = &info->emu;
-        int mreg;
-        int mval;
-
-        mreg = isdn_getnum(p);
-        if (mreg < 0 || mreg > ISDN_MODEM_ANZREG)
-                PARSE_ERROR1;
-        switch (*p[0]) {
-                case '=':
-                        p[0]++;
-                        mval = isdn_getnum(p);
-                        if (mval < 0 || mval > 255)
-                                PARSE_ERROR1;
-                        switch (mreg) {
-                                /* Some plausibility checks */
-                                case 14:
-                                        if (mval > ISDN_PROTO_L2_TRANS)
-                                                PARSE_ERROR1;
-                                        break;
-                                case 16:
-                                        if ((mval * 16) > ISDN_SERIAL_XMIT_SIZE)
-                                                PARSE_ERROR1;
-#ifdef CONFIG_ISDN_AUDIO
-                                        if ((m->mdmreg[18] & 1) && (mval > VBUFX))
-                                                PARSE_ERROR1;
-#endif
-                                        info->xmit_size = mval * 16;
-                                        break;
-                                case 20:
-                                        PARSE_ERROR1;
-                        }
-                        m->mdmreg[mreg] = mval;
-                        break;
-                case '?':
-                        p[0]++;
-                        isdn_tty_show_profile(mreg, info);
-                        break;
-                default:
-                        PARSE_ERROR1;
-                        break;
-        }
-        return 0;
+	atemu *m = &info->emu;
+	int bitpos;
+	int mreg;
+	int mval;
+	int bval;
+
+	mreg = isdn_getnum(p);
+	if (mreg < 0 || mreg > ISDN_MODEM_ANZREG)
+		PARSE_ERROR1;
+	switch (*p[0]) {
+		case '=':
+			p[0]++;
+			mval = isdn_getnum(p);
+			if (mval < 0 || mval > 255)
+				PARSE_ERROR1;
+			if (isdn_tty_check_ats(mreg, mval, info, m))
+				PARSE_ERROR1;
+			m->mdmreg[mreg] = mval;
+			break;
+		case '.':
+			/* Set/Clear a single bit */
+			p[0]++;
+			bitpos = isdn_getnum(p);
+			if ((bitpos < 0) || (bitpos > 7))
+				PARSE_ERROR1;
+			switch (*p[0]) {
+				case '=':
+					p[0]++;
+					bval = isdn_getnum(p);
+					if (bval < 0 || bval > 1)
+						PARSE_ERROR1;
+					if (bval)
+						mval = m->mdmreg[mreg] | (1 << bitpos);
+					else
+						mval = m->mdmreg[mreg] & ~(1 << bitpos);
+					if (isdn_tty_check_ats(mreg, mval, info, m))
+						PARSE_ERROR1;
+					m->mdmreg[mreg] = mval;
+					break;
+				case '?':
+					p[0]++;
+					isdn_tty_at_cout("\r\n", info);
+					isdn_tty_at_cout((m->mdmreg[mreg] & (1 << bitpos)) ? "1" : "0",
+							 info);
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		case '?':
+			p[0]++;
+			isdn_tty_show_profile(mreg, info);
+			break;
+		default:
+			PARSE_ERROR1;
+			break;
+	}
+	return 0;
 }
 
 /*
  * Perform ATA command
  */
-static void isdn_tty_cmd_ATA(modem_info * info)
+static void
+isdn_tty_cmd_ATA(modem_info * info)
 {
-        atemu *m = &info->emu;
-        isdn_ctrl cmd;
-        int l2;
-
-        if (info->msr & UART_MSR_RI) {
-                /* Accept incoming call */
-                m->mdmreg[1] = 0;
-                info->msr &= ~UART_MSR_RI;
-                l2 = m->mdmreg[14];
-#ifdef CONFIG_ISDN_AUDIO
-                /* If more than one bit set in reg18, autoselect Layer2 */
-                if ((m->mdmreg[18] & m->mdmreg[20]) != m->mdmreg[18])
-                        if (m->mdmreg[20] == 1) l2 = 4;
-#endif
-                cmd.driver = info->isdn_driver;
-                cmd.command = ISDN_CMD_SETL2;
-                cmd.arg = info->isdn_channel + (l2 << 8);
-                dev->drv[info->isdn_driver]->interface->command(&cmd);
-                cmd.driver = info->isdn_driver;
-                cmd.command = ISDN_CMD_SETL3;
-                cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8);
-                dev->drv[info->isdn_driver]->interface->command(&cmd);
-                cmd.driver = info->isdn_driver;
-                cmd.arg = info->isdn_channel;
-                cmd.command = ISDN_CMD_ACCEPTD;
-                dev->drv[info->isdn_driver]->interface->command(&cmd);
-        } else
-                isdn_tty_modem_result(8, info);
+	atemu *m = &info->emu;
+	isdn_ctrl cmd;
+	int l2;
+
+	if (info->msr & UART_MSR_RI) {
+		/* Accept incoming call */
+		info->last_dir = 0;
+		strcpy(info->last_num, dev->num[info->drv_index]);
+		m->mdmreg[1] = 0;
+		info->msr &= ~UART_MSR_RI;
+		l2 = m->mdmreg[14];
+#ifdef CONFIG_ISDN_AUDIO
+		/* If more than one bit set in reg18, autoselect Layer2 */
+		if ((m->mdmreg[18] & m->mdmreg[20]) != m->mdmreg[18]) {
+			if (m->mdmreg[20] == 1)
+				l2 = 4;
+			else
+				l2 = 0;
+		}
+#endif
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL2;
+		cmd.arg = info->isdn_channel + (l2 << 8);
+		info->last_l2 = l2;
+		dev->drv[info->isdn_driver]->interface->command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL3;
+		cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8);
+		dev->drv[info->isdn_driver]->interface->command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		cmd.command = ISDN_CMD_ACCEPTD;
+		dev->drv[info->isdn_driver]->interface->command(&cmd);
+	} else
+		isdn_tty_modem_result(8, info);
 }
 
 #ifdef CONFIG_ISDN_AUDIO
 /*
  * Parse AT+F.. commands
  */
-static int isdn_tty_cmd_PLUSF(char **p, modem_info * info)
+static int
+isdn_tty_cmd_PLUSF(char **p, modem_info * info)
 {
-        atemu *m = &info->emu;
-        int par;
+	atemu *m = &info->emu;
+	int par;
 	char rs[20];
 
-        if (!strncmp(p[0],"CLASS",5)) {
-                p[0] += 5;
-                switch (*p[0]) {
-                        case '?':
-                                p[0]++;
-                                sprintf(rs,"\r\n%d",
-                                        (m->mdmreg[18]&1)?8:0);
-                                isdn_tty_at_cout(rs, info);
-                                break;
-                        case '=':
-                                p[0]++;
-                                switch (*p[0]) {
-                                        case '0':
-                                                p[0]++;
-                                                m->mdmreg[18] = 4;
-                                                info->xmit_size =
-                                                        m->mdmreg[16] * 16;
-                                                break;
-                                        case '8':
-                                                p[0]++;
-                                                m->mdmreg[18] = 5;
-                                                info->xmit_size = VBUF;
-                                                break;
-                                        case '?':
-                                                p[0]++;
-                                                isdn_tty_at_cout("\r\n0,8",
-                                                                 info);
-                                                break;
-                                        default:
-                                                PARSE_ERROR1;
-                                }
-                                break;
-                        default:
-                                PARSE_ERROR1;
-                }
-                return 0;
-        }        
-        if (!strncmp(p[0],"AA",2)) {
-                p[0] += 2;
-                switch (*p[0]) {
-                        case '?':
-                                p[0]++;
-                                sprintf(rs,"\r\n%d",
-                                        m->mdmreg[0]);
-                                isdn_tty_at_cout(rs, info);
-                                break;
-                        case '=':
-                                p[0]++;
-                                par = isdn_getnum(p);
-                                if ((par < 0) || (par > 255))
-                                        PARSE_ERROR1;
-                                m->mdmreg[0]=par;
-                                break;
-                        default:
-                                PARSE_ERROR1;                                
-                }
-                return 0;
-        }
-        PARSE_ERROR1;
+	if (!strncmp(p[0], "CLASS", 5)) {
+		p[0] += 5;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d",
+					(m->mdmreg[18] & 1) ? 8 : 0);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				switch (*p[0]) {
+					case '0':
+						p[0]++;
+						m->mdmreg[18] = 4;
+						info->xmit_size =
+						    m->mdmreg[16] * 16;
+						break;
+					case '8':
+						p[0]++;
+						m->mdmreg[18] = 5;
+						info->xmit_size = VBUF;
+						break;
+					case '?':
+						p[0]++;
+						isdn_tty_at_cout("\r\n0,8",
+								 info);
+						break;
+					default:
+						PARSE_ERROR1;
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	if (!strncmp(p[0], "AA", 2)) {
+		p[0] += 2;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d",
+					m->mdmreg[0]);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				par = isdn_getnum(p);
+				if ((par < 0) || (par > 255))
+					PARSE_ERROR1;
+				m->mdmreg[0] = par;
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	PARSE_ERROR1;
 }
 
 /*
  * Parse AT+V.. commands
  */
-static int isdn_tty_cmd_PLUSV(char **p, modem_info * info)
+static int
+isdn_tty_cmd_PLUSV(char **p, modem_info * info)
 {
-        atemu *m = &info->emu;
-        static char *vcmd[] = {"NH","IP","LS","RX","SD","SM","TX",NULL};
-        int i;
+	atemu *m = &info->emu;
+	static char *vcmd[] =
+	{"NH", "IP", "LS", "RX", "SD", "SM", "TX", NULL};
+	int i;
 	int par1;
 	int par2;
 	char rs[20];
 
-        i = 0;
-        while (vcmd[i]) {
-                if (!strncmp(vcmd[i],p[0],2)) {
-                        p[0] += 2;
-                        break;
-                }
-                i++;
-        }
-        switch (i) {
-                case 0:
-                        /* AT+VNH - Auto hangup feature */
-                        switch (*p[0]) {
-                                case '?':
-                                        p[0]++;
-                                        isdn_tty_at_cout("\r\n1", info);
-                                        break;
-                                case '=':
-                                        p[0]++;
-                                        switch (*p[0]) {
-                                                case '1':
-                                                        p[0]++;
-                                                        break;
-                                                case '?':
-                                                        p[0]++;
-                                                        isdn_tty_at_cout("\r\n1", info);
-                                                        break;
-                                                default:
-                                                        PARSE_ERROR1;
-                                        }
-                                        break;
-                                default:
-                                        PARSE_ERROR1;
-                        }
-                        break;
-                case 1:
-                        /* AT+VIP - Reset all voice parameters */
-                        isdn_tty_modem_reset_vpar(m);
-                        break;
-                case 2:
-                        /* AT+VLS - Select device, accept incoming call */
-                        switch (*p[0]) {
-                                case '?':
-                                        p[0]++;
-                                        sprintf(rs,"\r\n%d",m->vpar[0]);
-                                        isdn_tty_at_cout(rs, info);
-                                        break;
-                                case '=':
-                                        p[0]++;
-                                        switch (*p[0]) {
-                                                case '0':
-                                                        p[0]++;
-                                                        m->vpar[0] = 0;
-                                                        break;
-                                                case '2':
-                                                        p[0]++;
-                                                        m->vpar[0] = 2;
-                                                        break;
-                                                case '?':
-                                                        p[0]++;
-                                                        isdn_tty_at_cout("\r\n0,2", info);
-                                                        break;
-                                                default:
-                                                        PARSE_ERROR1;
-                                        }
-                                        break;
-                                default:
-                                        PARSE_ERROR1;
-                        }
-                        break;
-                case 3:
-                        /* AT+VRX - Start recording */
-                        if (!m->vpar[0])
-                                PARSE_ERROR1;
-                        if (info->online != 1) {
-                                isdn_tty_modem_result(8, info);
-                                return 1;
-                        }
-                        info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state);
-                        if (!info->dtmf_state) {
-                                printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
-                                PARSE_ERROR1;
-                        }
-                        if (m->vpar[3] < 5) {
-                                info->adpcmr = isdn_audio_adpcm_init(info->adpcmr, m->vpar[3]);
-                                if (!info->adpcmr) {
-                                        printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
-                                        PARSE_ERROR1;
-                                }
-                        }
-                        info->vonline = 1;
-                        isdn_tty_modem_result(1, info);
-                        return 1;
-                        break;
-                case 4:
-                        /* AT+VSD - Silence detection */
-                        switch (*p[0]) {
-                                case '?':
-                                        p[0]++;
-                                        sprintf(rs,"\r\n<%d>,<%d>",
-                                                m->vpar[1],
-                                                m->vpar[2]);
-                                        isdn_tty_at_cout(rs, info);
-                                        break;
-                                case '=':
-                                        p[0]++;
-                                        switch (*p[0]) {
-                                                case '0':
-                                                case '1':
-                                                case '2':
-                                                case '3':
-                                                        par1 = isdn_getnum(p);
-                                                        if ((par1 < 0) || (par1 > 31))
-                                                                PARSE_ERROR1;
-                                                        if (*p[0] != ',')
-                                                                PARSE_ERROR1;
-                                                        p[0]++;
-                                                        par2 = isdn_getnum(p);
-                                                        if ((par2 < 0) || (par2 > 255))
-                                                                PARSE_ERROR1;
-                                                        m->vpar[1] = par1;
-                                                        m->vpar[2] = par2;
-                                                        break;
-                                                case '?':
-                                                        p[0]++;
-                                                        isdn_tty_at_cout("\r\n<0-31>,<0-255>",
-                                                                         info);
-                                                        break;
-                                                default:
-                                                        PARSE_ERROR1;
-                                        }
-                                        break;
-                                default:
-                                        PARSE_ERROR1;
-                        }
-                        break;
-                case 5:
-                        /* AT+VSM - Select compression */
-                        switch (*p[0]) {
-                                case '?':
-                                        p[0]++;
-                                        sprintf(rs,"\r\n<%d>,<%d><8000>",
-                                                m->vpar[3],
-                                                m->vpar[1]);
-                                        isdn_tty_at_cout(rs, info);
-                                        break;
-                                case '=':
-                                        p[0]++;
-                                        switch (*p[0]) {
-                                                case '2':
-                                                case '3':
-                                                case '4':
-                                                case '5':
-                                                case '6':
-                                                        par1 = isdn_getnum(p);
-                                                        if ((par1 < 2) || (par1 > 6))
-                                                                PARSE_ERROR1;
-                                                        m->vpar[3] = par1;
-                                                        break;
-                                                case '?':
-                                                        p[0]++;
-                                                        isdn_tty_at_cout("\r\n2;ADPCM;2;0;(8000)\r\n",
-                                                                         info);
-                                                        isdn_tty_at_cout("3;ADPCM;3;0;(8000)\r\n",
-                                                                         info);
-                                                        isdn_tty_at_cout("4;ADPCM;4;0;(8000)\r\n",
-                                                                         info);
-                                                        isdn_tty_at_cout("5;ALAW;8;0;(8000)",
-                                                                         info);
-                                                        isdn_tty_at_cout("6;ULAW;8;0;(8000)",
-                                                                         info);
-                                                        break;
-                                                default:
-                                                        PARSE_ERROR1;
-                                        }
-                                        break;
-                                default:
-                                        PARSE_ERROR1;
-                        }
-                        break;
-                case 6:
-                        /* AT+VTX - Start sending */
-                        if (!m->vpar[0])
-                                PARSE_ERROR1;
-                        if (info->online != 1) {
-                                isdn_tty_modem_result(8, info);
-                                return 1;
-                        }
-                        info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state);
-                        if (!info->dtmf_state) {
-                                printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
-                                PARSE_ERROR1;
-                        }
-                        if (m->vpar[3] < 5) {
-                                info->adpcms = isdn_audio_adpcm_init(info->adpcms, m->vpar[3]);
-                                if (!info->adpcms) {
-                                        printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
-                                        PARSE_ERROR1;
-                                }
-                        }
-                        m->lastDLE = 0;
-                        info->vonline = 2;
-                        isdn_tty_modem_result(1, info);
-                        return 1;
-                        break;
-                default:
-                        PARSE_ERROR1;
-        }
-        return 0;
+	i = 0;
+	while (vcmd[i]) {
+		if (!strncmp(vcmd[i], p[0], 2)) {
+			p[0] += 2;
+			break;
+		}
+		i++;
+	}
+	switch (i) {
+		case 0:
+			/* AT+VNH - Auto hangup feature */
+			switch (*p[0]) {
+				case '?':
+					p[0]++;
+					isdn_tty_at_cout("\r\n1", info);
+					break;
+				case '=':
+					p[0]++;
+					switch (*p[0]) {
+						case '1':
+							p[0]++;
+							break;
+						case '?':
+							p[0]++;
+							isdn_tty_at_cout("\r\n1", info);
+							break;
+						default:
+							PARSE_ERROR1;
+					}
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		case 1:
+			/* AT+VIP - Reset all voice parameters */
+			isdn_tty_modem_reset_vpar(m);
+			break;
+		case 2:
+			/* AT+VLS - Select device, accept incoming call */
+			switch (*p[0]) {
+				case '?':
+					p[0]++;
+					sprintf(rs, "\r\n%d", m->vpar[0]);
+					isdn_tty_at_cout(rs, info);
+					break;
+				case '=':
+					p[0]++;
+					switch (*p[0]) {
+						case '0':
+							p[0]++;
+							m->vpar[0] = 0;
+							break;
+						case '2':
+							p[0]++;
+							m->vpar[0] = 2;
+							break;
+						case '?':
+							p[0]++;
+							isdn_tty_at_cout("\r\n0,2", info);
+							break;
+						default:
+							PARSE_ERROR1;
+					}
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		case 3:
+			/* AT+VRX - Start recording */
+			if (!m->vpar[0])
+				PARSE_ERROR1;
+			if (info->online != 1) {
+				isdn_tty_modem_result(8, info);
+				return 1;
+			}
+			info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state);
+			if (!info->dtmf_state) {
+				printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
+				PARSE_ERROR1;
+			}
+			if (m->vpar[3] < 5) {
+				info->adpcmr = isdn_audio_adpcm_init(info->adpcmr, m->vpar[3]);
+				if (!info->adpcmr) {
+					printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
+					PARSE_ERROR1;
+				}
+			}
+#ifdef ISDN_DEBUG_AT
+			printk(KERN_DEBUG "AT: +VRX\n");
+#endif
+			info->vonline |= 1;
+			isdn_tty_modem_result(1, info);
+			return 0;
+			break;
+		case 4:
+			/* AT+VSD - Silence detection */
+			switch (*p[0]) {
+				case '?':
+					p[0]++;
+					sprintf(rs, "\r\n<%d>,<%d>",
+						m->vpar[1],
+						m->vpar[2]);
+					isdn_tty_at_cout(rs, info);
+					break;
+				case '=':
+					p[0]++;
+					switch (*p[0]) {
+						case '0':
+						case '1':
+						case '2':
+						case '3':
+							par1 = isdn_getnum(p);
+							if ((par1 < 0) || (par1 > 31))
+								PARSE_ERROR1;
+							if (*p[0] != ',')
+								PARSE_ERROR1;
+							p[0]++;
+							par2 = isdn_getnum(p);
+							if ((par2 < 0) || (par2 > 255))
+								PARSE_ERROR1;
+							m->vpar[1] = par1;
+							m->vpar[2] = par2;
+							break;
+						case '?':
+							p[0]++;
+							isdn_tty_at_cout("\r\n<0-31>,<0-255>",
+								   info);
+							break;
+						default:
+							PARSE_ERROR1;
+					}
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		case 5:
+			/* AT+VSM - Select compression */
+			switch (*p[0]) {
+				case '?':
+					p[0]++;
+					sprintf(rs, "\r\n<%d>,<%d><8000>",
+						m->vpar[3],
+						m->vpar[1]);
+					isdn_tty_at_cout(rs, info);
+					break;
+				case '=':
+					p[0]++;
+					switch (*p[0]) {
+						case '2':
+						case '3':
+						case '4':
+						case '5':
+						case '6':
+							par1 = isdn_getnum(p);
+							if ((par1 < 2) || (par1 > 6))
+								PARSE_ERROR1;
+							m->vpar[3] = par1;
+							break;
+						case '?':
+							p[0]++;
+							isdn_tty_at_cout("\r\n2;ADPCM;2;0;(8000)\r\n",
+								   info);
+							isdn_tty_at_cout("3;ADPCM;3;0;(8000)\r\n",
+								   info);
+							isdn_tty_at_cout("4;ADPCM;4;0;(8000)\r\n",
+								   info);
+							isdn_tty_at_cout("5;ALAW;8;0;(8000)",
+								   info);
+							isdn_tty_at_cout("6;ULAW;8;0;(8000)",
+								   info);
+							break;
+						default:
+							PARSE_ERROR1;
+					}
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		case 6:
+			/* AT+VTX - Start sending */
+			if (!m->vpar[0])
+				PARSE_ERROR1;
+			if (info->online != 1) {
+				isdn_tty_modem_result(8, info);
+				return 1;
+			}
+			info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state);
+			if (!info->dtmf_state) {
+				printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
+				PARSE_ERROR1;
+			}
+			if (m->vpar[3] < 5) {
+				info->adpcms = isdn_audio_adpcm_init(info->adpcms, m->vpar[3]);
+				if (!info->adpcms) {
+					printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
+					PARSE_ERROR1;
+				}
+			}
+#ifdef ISDN_DEBUG_AT
+			printk(KERN_DEBUG "AT: +VTX\n");
+#endif
+			m->lastDLE = 0;
+			info->vonline |= 2;
+			isdn_tty_modem_result(1, info);
+			return 0;
+			break;
+		default:
+			PARSE_ERROR1;
+	}
+	return 0;
 }
-#endif        /* CONFIG_ISDN_AUDIO */
+#endif                          /* CONFIG_ISDN_AUDIO */
 
 /*
  * Parse and perform an AT-command-line.
  */
-static void isdn_tty_parse_at(modem_info * info)
+static void
+isdn_tty_parse_at(modem_info * info)
 {
-        atemu *m = &info->emu;
-        char *p;
-        char ds[40];
+	atemu *m = &info->emu;
+	char *p;
+	char ds[40];
 
 #ifdef ISDN_DEBUG_AT
-        printk(KERN_DEBUG "AT: '%s'\n", m->mdmcmd);
+	printk(KERN_DEBUG "AT: '%s'\n", m->mdmcmd);
+#endif
+	for (p = &m->mdmcmd[2]; *p;) {
+		switch (*p) {
+			case 'A':
+				/* A - Accept incoming call */
+				p++;
+				isdn_tty_cmd_ATA(info);
+				return;
+				break;
+			case 'D':
+				/* D - Dial */
+				isdn_tty_getdial(++p, ds, sizeof(ds));
+				p += strlen(p);
+				if (!strlen(m->msn))
+					isdn_tty_modem_result(10, info);
+				else if (strlen(ds))
+					isdn_tty_dial(ds, info, m);
+				else
+					PARSE_ERROR;
+				return;
+			case 'E':
+				/* E - Turn Echo on/off */
+				p++;
+				switch (isdn_getnum(&p)) {
+					case 0:
+						m->mdmreg[12] &= ~4;
+						break;
+					case 1:
+						m->mdmreg[12] |= 4;
+						break;
+					default:
+						PARSE_ERROR;
+				}
+				break;
+			case 'H':
+				/* H - On/Off-hook */
+				p++;
+				switch (*p) {
+					case '0':
+						p++;
+						isdn_tty_on_hook(info);
+						break;
+					case '1':
+						p++;
+						isdn_tty_off_hook();
+						break;
+					default:
+						isdn_tty_on_hook(info);
+						break;
+				}
+				break;
+			case 'I':
+				/* I - Information */
+				p++;
+				isdn_tty_at_cout("\r\nLinux ISDN", info);
+				switch (*p) {
+					case '0':
+					case '1':
+						p++;
+						break;
+					case '2':
+						p++;
+						isdn_tty_report(info);
+						break;
+					default:
+				}
+				break;
+			case 'O':
+				/* O - Go online */
+				p++;
+				if (info->msr & UART_MSR_DCD)
+					/* if B-Channel is up */
+					isdn_tty_modem_result(5, info);
+				else
+					isdn_tty_modem_result(3, info);
+				return;
+			case 'Q':
+				/* Q - Turn Emulator messages on/off */
+				p++;
+				switch (isdn_getnum(&p)) {
+					case 0:
+						m->mdmreg[12] |= 1;
+						break;
+					case 1:
+						m->mdmreg[12] &= ~1;
+						break;
+					default:
+						PARSE_ERROR;
+				}
+				break;
+			case 'S':
+				/* S - Set/Get Register */
+				p++;
+				if (isdn_tty_cmd_ATS(&p, info))
+					return;
+				break;
+			case 'V':
+				/* V - Numeric or ASCII Emulator-messages */
+				p++;
+				switch (isdn_getnum(&p)) {
+					case 0:
+						m->mdmreg[12] |= 2;
+						break;
+					case 1:
+						m->mdmreg[12] &= ~2;
+						break;
+					default:
+						PARSE_ERROR;
+				}
+				break;
+			case 'Z':
+				/* Z - Load Registers from Profile */
+				p++;
+				isdn_tty_modem_reset_regs(info, 1);
+				break;
+#ifdef CONFIG_ISDN_AUDIO
+			case '+':
+				p++;
+				switch (*p) {
+					case 'F':
+						p++;
+						if (isdn_tty_cmd_PLUSF(&p, info))
+							return;
+						break;
+					case 'V':
+						if (!(m->mdmreg[18] & 1))
+							PARSE_ERROR;
+						p++;
+						if (isdn_tty_cmd_PLUSV(&p, info))
+							return;
+						break;
+					default:
+						PARSE_ERROR;
+				}
+				break;
+#endif                          /* CONFIG_ISDN_AUDIO */
+			case '&':
+				p++;
+				if (isdn_tty_cmd_ATand(&p, info))
+					return;
+				break;
+			default:
+				PARSE_ERROR;
+		}
+	}
+#ifdef CONFIG_ISDN_AUDIO
+	if (!info->vonline)
 #endif
-        for (p = &m->mdmcmd[2]; *p;) {
-                switch (*p) {
-                        case 'A':
-                                /* A - Accept incoming call */
-                                p++;
-                                isdn_tty_cmd_ATA(info);
-                                return;
-                                break;
-                        case 'D':
-                                /* D - Dial */
-                                isdn_tty_getdial(++p, ds);
-                                p += strlen(p);
-                                if (!strlen(m->msn))
-                                        isdn_tty_modem_result(10, info);
-                                else if (strlen(ds))
-                                        isdn_tty_dial(ds, info, m);
-                                else
-                                        isdn_tty_modem_result(4, info);
-                                return;
-                        case 'E':
-                                /* E - Turn Echo on/off */
-                                p++;
-                                switch (isdn_getnum(&p)) {
-                                        case 0:
-                                                m->mdmreg[12] &= ~4;
-                                                break;
-                                        case 1:
-                                                m->mdmreg[12] |= 4;
-                                                break;
-                                        default:
-                                                PARSE_ERROR;
-                                }
-                                break;
-                        case 'H':
-                                /* H - On/Off-hook */
-                                p++;
-                                switch (*p) {
-                                        case '0':
-                                                p++;
-                                                isdn_tty_on_hook(info);
-                                                break;
-                                        case '1':
-                                                p++;
-                                                isdn_tty_off_hook();
-                                                break;
-                                        default:
-                                                isdn_tty_on_hook(info);
-                                                break;
-                                }
-                                break;
-                        case 'I':
-                                /* I - Information */
-                                p++;
-                                isdn_tty_at_cout("\r\nLinux ISDN", info);
-                                switch (*p) {
-                                        case '0':
-                                        case '1':
-                                                p++;
-                                                break;
-                                        default:
-                                }
-                                break;
-                        case 'O':
-                                /* O - Go online */
-                                p++;
-                                if (info->msr & UART_MSR_DCD)
-                                        /* if B-Channel is up */
-                                        isdn_tty_modem_result(5, info);
-                                else
-                                        isdn_tty_modem_result(3, info);
-                                return;
-                        case 'Q':
-                                /* Q - Turn Emulator messages on/off */
-                                p++;
-                                switch (isdn_getnum(&p)) {
-                                        case 0:
-                                                m->mdmreg[12] |= 1;
-                                                break;
-                                        case 1:
-                                                m->mdmreg[12] &= ~1;
-                                                break;
-                                        default:
-                                                PARSE_ERROR;
-                                }
-                                break;
-                        case 'S':
-                                /* S - Set/Get Register */
-                                p++;
-                                if (isdn_tty_cmd_ATS(&p, info))
-                                        return;
-                                break;
-                        case 'V':
-                                /* V - Numeric or ASCII Emulator-messages */
-                                p++;
-                                switch (isdn_getnum(&p)) {
-                                        case 0:
-                                                m->mdmreg[12] |= 2;
-                                                break;
-                                        case 1:
-                                                m->mdmreg[12] &= ~2;
-                                                break;
-                                        default:
-                                                PARSE_ERROR;
-                                }
-                                break;
-                        case 'Z':
-                                /* Z - Load Registers from Profile */
-                                p++;
-                                isdn_tty_modem_reset_regs(info, 1);
-                                break;
-#ifdef CONFIG_ISDN_AUDIO
-                        case '+':
-                                p++;
-                                switch (*p) {
-                                        case 'F':
-                                                p++;
-                                                if (isdn_tty_cmd_PLUSF(&p, info))
-                                                        return;
-                                                break;
-                                        case 'V':
-                                                if (!(m->mdmreg[18] & 1))
-                                                        PARSE_ERROR;
-                                                p++;
-                                                if (isdn_tty_cmd_PLUSV(&p, info))
-                                                        return;
-                                                break;
-                                }
-                                break;
-#endif        /* CONFIG_ISDN_AUDIO */
-                        case '&':
-                                p++;
-                                if (isdn_tty_cmd_ATand(&p, info))
-                                        return;
-                                break;
-                        default:
-                                isdn_tty_modem_result(4, info);
-                                return;
-                }
-        }
-        isdn_tty_modem_result(0, info);
+		isdn_tty_modem_result(0, info);
 }
 
 /* Need own toupper() because standard-toupper is not available
@@ -2669,7 +3249,8 @@
  *   channel  index to line (minor-device)
  *   user     flag: buffer is in userspace
  */
-static int isdn_tty_edit_at(const char *p, int count, modem_info * info, int user)
+static int
+isdn_tty_edit_at(const char *p, int count, modem_info * info, int user)
 {
 	atemu *m = &info->emu;
 	int total = 0;
@@ -2715,16 +3296,16 @@
 			if (m->mdmcmdl < 255) {
 				c = my_toupper(c);
 				switch (m->mdmcmdl) {
-                                        case 0:
-                                                if (c == 'A')
-                                                        m->mdmcmd[m->mdmcmdl++] = c;
-                                                break;
-                                        case 1:
-                                                if (c == 'T')
-                                                        m->mdmcmd[m->mdmcmdl++] = c;
-                                                break;
-                                        default:
-                                                m->mdmcmd[m->mdmcmdl++] = c;
+					case 0:
+						if (c == 'A')
+							m->mdmcmd[m->mdmcmdl++] = c;
+						break;
+					case 1:
+						if (c == 'T')
+							m->mdmcmd[m->mdmcmdl++] = c;
+						break;
+					default:
+						m->mdmcmd[m->mdmcmdl++] = c;
 				}
 			}
 		}
@@ -2735,10 +3316,11 @@
 /*
  * Switch all modem-channels who are online and got a valid
  * escape-sequence 1.5 seconds ago, to command-mode.
- * This function is called every second via timer-interrupt from within 
+ * This function is called every second via timer-interrupt from within
  * timer-dispatcher isdn_timer_function()
  */
-void isdn_tty_modem_escape(void)
+void
+isdn_tty_modem_escape(void)
 {
 	int ton = 0;
 	int i;
@@ -2747,7 +3329,7 @@
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
 		if (USG_MODEM(dev->usage[i]))
 			if ((midx = dev->m_idx[i]) >= 0) {
-                                modem_info *info = &dev->mdm.info[midx];
+				modem_info *info = &dev->mdm.info[midx];
 				if (info->online) {
 					ton = 1;
 					if ((info->emu.pluscount == 3) &&
@@ -2757,27 +3339,28 @@
 						isdn_tty_modem_result(0, info);
 					}
 				}
-                        }
+			}
 	isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, ton);
 }
 
 /*
  * Put a RING-message to all modem-channels who have the RI-bit set.
- * This function is called every second via timer-interrupt from within 
+ * This function is called every second via timer-interrupt from within
  * timer-dispatcher isdn_timer_function()
  */
-void isdn_tty_modem_ring(void)
+void
+isdn_tty_modem_ring(void)
 {
 	int ton = 0;
 	int i;
 
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-                modem_info *info = &dev->mdm.info[i];
-                if (info->msr & UART_MSR_RI) {
-                        ton = 1;
-                        isdn_tty_modem_result(2, info);
-                }
-        }
+		modem_info *info = &dev->mdm.info[i];
+		if (info->msr & UART_MSR_RI) {
+			ton = 1;
+			isdn_tty_modem_result(2, info);
+		}
+	}
 	isdn_timer_ctrl(ISDN_TIMER_MODEMRING, ton);
 }
 
@@ -2785,41 +3368,19 @@
  * For all online tty's, try sending data to
  * the lower levels.
  */
-void isdn_tty_modem_xmit(void)
+void
+isdn_tty_modem_xmit(void)
 {
 	int ton = 1;
 	int i;
 
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-                modem_info *info = &dev->mdm.info[i];
-                if (info->online) {
-                        ton = 1;
-                        isdn_tty_senddown(info);
-                        isdn_tty_tint(info);
-                }
-        }
+		modem_info *info = &dev->mdm.info[i];
+		if (info->online) {
+			ton = 1;
+			isdn_tty_senddown(info);
+			isdn_tty_tint(info);
+		}
+	}
 	isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, ton);
-}
-
-/*
- * A packet has been output successfully.
- * Search the tty-devices for an appropriate device, decrement its
- * counter for outstanding packets, and set CTS.
- */
-void isdn_tty_bsent(int drv, int chan)
-{
-	int i;
-
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-                modem_info *info = &dev->mdm.info[i];
-                if ((info->isdn_driver == drv) &&
-                    (info->isdn_channel == chan) ) {
-                        info->msr |= UART_MSR_CTS;
-                        if (info->send_outstanding)
-                                if (!(--info->send_outstanding))
-                                        info->lsr |= UART_LSR_TEMT;
-                        isdn_tty_tint(info);
-                }
-        }
-	return;
 }
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_tty.h linux/drivers/isdn/isdn_tty.h
--- v2.0.30/linux/drivers/isdn/isdn_tty.h	Sun May 19 05:29:29 1996
+++ linux/drivers/isdn/isdn_tty.h	Mon Aug  4 17:34:00 1997
@@ -1,10 +1,10 @@
-/* $Id: isdn_tty.h,v 1.5 1996/05/17 03:52:31 fritz Exp $
- *
+/* $Id: isdn_tty.h,v 1.10 1997/03/02 14:29:26 fritz Exp $
+
  * header for Linux ISDN subsystem, tty related functions (linklevel).
  *
  * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
  * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
- * 
+ *
  * 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)
@@ -17,9 +17,26 @@
  *
  * 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. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_tty.h,v $
+ * Revision 1.10  1997/03/02 14:29:26  fritz
+ * More ttyI related cleanup.
+ *
+ * Revision 1.9  1997/02/28 02:32:49  fritz
+ * Cleanup: Moved some tty related stuff from isdn_common.c
+ *          to isdn_tty.c
+ * Bugfix:  Bisync protocol did not behave like documented.
+ *
+ * Revision 1.8  1997/02/10 20:12:50  fritz
+ * Changed interface for reporting incoming calls.
+ *
+ * Revision 1.7  1997/02/03 23:06:10  fritz
+ * Reformatted according CodingStyle
+ *
+ * Revision 1.6  1997/01/14 01:35:19  fritz
+ * Changed prototype of isdn_tty_modem_hup.
+ *
  * Revision 1.5  1996/05/17 03:52:31  fritz
  * Changed DLE handling for audio receive.
  *
@@ -37,15 +54,12 @@
  *
  */
 
-extern void  isdn_tty_modem_result(int, modem_info *);
-extern void  isdn_tty_modem_escape(void);
-extern void  isdn_tty_modem_ring(void);
-extern void  isdn_tty_modem_xmit(void);
-extern void  isdn_tty_modem_hup(modem_info *);
-extern int   isdn_tty_modem_init(void);
-extern void  isdn_tty_readmodem(void);
-extern int   isdn_tty_try_read(modem_info *, struct sk_buff *);
-extern int   isdn_tty_find_icall(int, int, char *);
-extern int   isdn_tty_countDLE(unsigned char *, int);
-extern void  isdn_tty_bsent(int, int);
-extern void  isdn_tty_cleanup_xmit(modem_info *);
+extern void isdn_tty_modem_escape(void);
+extern void isdn_tty_modem_ring(void);
+extern void isdn_tty_modem_xmit(void);
+extern int isdn_tty_modem_init(void);
+extern void isdn_tty_readmodem(void);
+extern int isdn_tty_find_icall(int, int, setup_parm);
+extern void isdn_tty_cleanup_xmit(modem_info *);
+extern int isdn_tty_stat_callback(int, isdn_ctrl *);
+extern int isdn_tty_rcv_skb(int, int, int, struct sk_buff *);
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/pcbit/callbacks.c linux/drivers/isdn/pcbit/callbacks.c
--- v2.0.30/linux/drivers/isdn/pcbit/callbacks.c	Sat Jun 29 10:36:22 1996
+++ linux/drivers/isdn/pcbit/callbacks.c	Mon Aug  4 17:34:00 1997
@@ -164,18 +164,12 @@
          *  ictl.num >= strlen() + strlen() + 5
          */
 
-        if (cbdata->data.setup.CalledPN) 
-		sprintf(ictl.num, "%s,%d,%d,%s", 
-			cbdata->data.setup.CallingPN, 
-			7, 0, 
-			cbdata->data.setup.CalledPN);
-	
-	else
-		sprintf(ictl.num, "%s,%d,%d,%s", 
-			cbdata->data.setup.CallingPN,
-			7, 0, 
-			"0");
-
+		strcpy(ictl.parm.setup.phone, cbdata->data.setup.CallingPN);
+		strcpy(ictl.parm.setup.eazmsn, cbdata->data.setup.CalledPN);
+		ictl.parm.setup.si1 = 7;
+		ictl.parm.setup.si2 = 0;
+		ictl.parm.setup.plan = 0;
+		ictl.parm.setup.screen = 0;
 
 #ifdef DEBUG
 	printk(KERN_DEBUG "statstr: %s\n", ictl.num);
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/pcbit/capi.c linux/drivers/isdn/pcbit/capi.c
--- v2.0.30/linux/drivers/isdn/pcbit/capi.c	Sat Jun 29 10:36:22 1996
+++ linux/drivers/isdn/pcbit/capi.c	Mon Aug  4 17:34:01 1997
@@ -147,7 +147,7 @@
 		return -1;
 	}
 
-        (*skb)->free = 1;
+        SET_SKB_FREE((*skb));
 
 
         *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
@@ -170,7 +170,7 @@
 		return -1;
 	}
 
-        (*skb)->free = 1;
+        SET_SKB_FREE((*skb));
 
         *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
 
@@ -200,7 +200,7 @@
 		return -1;
 	}
 
-        (*skb)->free = 1;
+        SET_SKB_FREE((*skb));
 
         *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
 
@@ -222,7 +222,7 @@
 		return -1;
 	}
 
-        (*skb)->free = 1;
+        SET_SKB_FREE((*skb));
   
         *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
 
@@ -285,7 +285,7 @@
 		return -1;
 	}
 
-        (*skb)->free = 1;
+        SET_SKB_FREE((*skb));
 
         *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
 
@@ -338,7 +338,7 @@
 		return -1;
 	}
 
-        (*skb)->free = 1;
+        SET_SKB_FREE((*skb));
 
         *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
 
@@ -357,7 +357,7 @@
 		return -1;
 	}
 
-        (*skb)->free = 1;
+        SET_SKB_FREE((*skb));
 
         *((ushort*) skb_put(*skb, 2) ) = callref;  
 
@@ -382,7 +382,7 @@
 		return -1;
 	}
 
-        (*skb)->free = 1;
+        SET_SKB_FREE((*skb));
 
         *((ushort*) skb_put(*skb, 2)) = chan->callref;  
 
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/pcbit/drv.c linux/drivers/isdn/pcbit/drv.c
--- v2.0.30/linux/drivers/isdn/pcbit/drv.c	Tue Nov 12 22:36:20 1996
+++ linux/drivers/isdn/pcbit/drv.c	Mon Aug  4 17:34:01 1997
@@ -226,7 +226,6 @@
 	struct pcbit_dev  *dev;
 	struct pcbit_chan *chan;
 	struct callb_data info;
-	char *cp;
 
 	dev = finddev(ctl->driver);
 
@@ -245,14 +244,7 @@
 		break;
 	case ISDN_CMD_DIAL:
 		info.type = EV_USR_SETUP_REQ;
-		info.data.setup.CalledPN = (char *) &ctl->num;
-		cp = strchr(info.data.setup.CalledPN, ',');
-		if (cp)
-			*cp = 0;
-		else {
-			printk(KERN_DEBUG "DIAL: error in CalledPN\n");
-			return -1;
-		}		
+		info.data.setup.CalledPN = (char *) &ctl->parm.setup.phone;
 		pcbit_fsm_event(dev, chan, EV_USR_SETUP_REQ, &info);
 		break;
 	case ISDN_CMD_ACCEPTD:
@@ -280,7 +272,7 @@
 		pcbit_clear_msn(dev);
 		break;
 	case ISDN_CMD_SETEAZ:
-		pcbit_set_msn(dev, ctl->num);
+		pcbit_set_msn(dev, ctl->parm.num);
 		break;
 	case ISDN_CMD_SETL3:
 		if ((ctl->arg >> 8) != ISDN_PROTO_L3_TRANS)
@@ -457,15 +449,9 @@
 		for (i=0; i < len; i++)
 		{
 			for(j=0; j < LOAD_RETRY; j++)
-			{
-				__volatile__ unsigned char * ptr;
-
-				ptr = dev->sh_mem + dev->loadptr;
-				if (*ptr == 0)
+				if (!(readb(dev->sh_mem + dev->loadptr)))
 					break;
 
-			}
-
 			if (j == LOAD_RETRY)
 			{
 				errstat = -ETIME;
@@ -745,7 +731,7 @@
 #endif
 	}
 
-	skb->free = 1;
+	SET_SKB_FREE(skb);
 
 	kfree_skb(skb, FREE_READ);
 
@@ -944,7 +930,7 @@
 		return -ENODEV;
 	}
 
-	cmd = (struct pcbit_ioctl *) ctl->num;
+	cmd = (struct pcbit_ioctl *) ctl->parm.num;
 
 	switch(ctl->arg) {
 	case PCBIT_IOCTL_GETSTAT:
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/pcbit/layer2.c linux/drivers/isdn/pcbit/layer2.c
--- v2.0.30/linux/drivers/isdn/pcbit/layer2.c	Sat Jun 29 10:36:22 1996
+++ linux/drivers/isdn/pcbit/layer2.c	Mon Aug  4 17:34:01 1997
@@ -1,13 +1,13 @@
 /*
  * Copyright (C) 1996 Universidade de Lisboa
- * 
+ *
  * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
  *
- * This software may be used and distributed according to the terms of 
+ * This software may be used and distributed according to the terms of
  * the GNU Public License, incorporated herein by reference.
  */
 
-/*        
+/*
  *        PCBIT-D low-layer interface
  */
 
@@ -19,7 +19,7 @@
 /*
  *        TODO: better handling of errors
  *              re-write/remove debug printks
- */ 
+ */
 
 #define __NO_VERSION__
 
@@ -57,7 +57,7 @@
 
 /*
  *  task queue struct
- */ 
+ */
 
 
 
@@ -66,16 +66,16 @@
  *  drv.c
  */
 
-extern void pcbit_l3_receive(struct pcbit_dev * dev, ulong msg, 
-			     struct sk_buff * skb,
+extern void pcbit_l3_receive(struct pcbit_dev *dev, ulong msg,
+			     struct sk_buff *skb,
 			     ushort hdr_len, ushort refnum);
 
 /*
  *  Prototypes
  */
 
-void pcbit_deliver(void * data);
-static void pcbit_transmit(struct pcbit_dev * dev);
+void pcbit_deliver(void *data);
+static void pcbit_transmit(struct pcbit_dev *dev);
 
 static void pcbit_recv_ack(struct pcbit_dev *dev, unsigned char ack);
 
@@ -83,9 +83,10 @@
 static void pcbit_l2_active_conf(struct pcbit_dev *dev, u_char info);
 static void pcbit_l2_err_recover(unsigned long data);
 
-static void pcbit_firmware_bug(struct pcbit_dev * dev);
+static void pcbit_firmware_bug(struct pcbit_dev *dev);
 
-static __inline__ void pcbit_sched_delivery(struct pcbit_dev *dev)
+static __inline__ void
+pcbit_sched_delivery(struct pcbit_dev *dev)
 {
 	queue_task(&dev->qdelivery, &tq_immediate);
 	mark_bh(IMMEDIATE_BH);
@@ -96,71 +97,67 @@
  *  Called from layer3
  */
 
-int pcbit_l2_write(struct pcbit_dev * dev, ulong msg, ushort refnum, 
-		   struct sk_buff *skb, unsigned short hdr_len)
-        
-{
-        struct frame_buf * frame, * ptr;
-        unsigned long flags;
-
-        if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) {
-                dev_kfree_skb(skb, FREE_WRITE);
-                return -1;
-        }
-
-        if ( (frame = (struct frame_buf *) kmalloc(sizeof(struct frame_buf), 
-						   GFP_ATOMIC)) == NULL ) {
-                printk(KERN_WARNING "pcbit_2_write: kmalloc failed\n");
+int
+pcbit_l2_write(struct pcbit_dev *dev, ulong msg, ushort refnum,
+	       struct sk_buff *skb, unsigned short hdr_len)
+{
+	struct frame_buf *frame,
+	*ptr;
+	unsigned long flags;
+
+	if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) {
+		dev_kfree_skb(skb, FREE_WRITE);
+		return -1;
+	}
+	if ((frame = (struct frame_buf *) kmalloc(sizeof(struct frame_buf),
+						  GFP_ATOMIC)) == NULL) {
+		printk(KERN_WARNING "pcbit_2_write: kmalloc failed\n");
 		dev_kfree_skb(skb, FREE_WRITE);
-                return -1;
-        }
+		return -1;
+	}
+	frame->msg = msg;
+	frame->refnum = refnum;
+	frame->copied = 0;
+	frame->hdr_len = hdr_len;
+
+	if (skb)
+		frame->dt_len = skb->len - hdr_len;
+	else
+		frame->dt_len = 0;
 
-        frame->msg = msg;
-        frame->refnum = refnum;
-        frame->copied = 0;        
-        frame->hdr_len = hdr_len;
-
-        if (skb) {
-                frame->dt_len = skb->len - hdr_len;
-		if (frame->dt_len == 0)
-                        skb->lock++;
-        }
-        else
-                frame->dt_len = 0;
-
-        frame->skb = skb;
+	frame->skb = skb;
 
-        frame->next = NULL;
+	frame->next = NULL;
 
-        save_flags(flags);
-        cli();
+	save_flags(flags);
+	cli();
+
+	if (dev->write_queue == NULL) {
+		dev->write_queue = frame;
+		restore_flags(flags);
+		pcbit_transmit(dev);
+	} else {
+		for (ptr = dev->write_queue; ptr->next; ptr = ptr->next);
+		ptr->next = frame;
 
-        if (dev->write_queue == NULL) {
-                dev->write_queue = frame;
 		restore_flags(flags);
-                pcbit_transmit(dev);
-        }
-        else {        
-                for(ptr=dev->write_queue; ptr->next; ptr=ptr->next);
-                ptr->next = frame;
-                
-                restore_flags(flags);
-        }
-        return 0;
+	}
+	return 0;
 }
 
-static __inline__ void pcbit_tx_update(struct pcbit_dev *dev, ushort len)
+static __inline__ void
+pcbit_tx_update(struct pcbit_dev *dev, ushort len)
 {
-        u_char info;
+	u_char info;
 
-        dev->send_seq = (dev->send_seq + 1) % 8;
+	dev->send_seq = (dev->send_seq + 1) % 8;
 
-        dev->fsize[dev->send_seq] = len;
-        info = 0;
-        info |= dev->rcv_seq  << 3;
-        info |= dev->send_seq;
+	dev->fsize[dev->send_seq] = len;
+	info = 0;
+	info |= dev->rcv_seq << 3;
+	info |= dev->send_seq;
 
-        writeb(info, dev->sh_mem + BANK4);
+	writeb(info, dev->sh_mem + BANK4);
 
 }
 
@@ -168,46 +165,47 @@
  * called by interrupt service routine or by write_2
  */
 
-static void pcbit_transmit(struct pcbit_dev * dev)
+static void
+pcbit_transmit(struct pcbit_dev *dev)
 {
-        struct frame_buf * frame = NULL;
-        unsigned char unacked;
-        int flen;           /* fragment frame length including all headers */
-	int totlen;         /* non-fragmented frame length */
-        int free;
-	int count, cp_len;
-        unsigned long flags;
+	struct frame_buf *frame = NULL;
+	unsigned char unacked;
+	int flen;               /* fragment frame length including all headers */
+	int totlen;             /* non-fragmented frame length */
+	int free;
+	int count,
+	 cp_len;
+	unsigned long flags;
 	unsigned short tt;
 
-        if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING)
-                return;
+	if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING)
+		return;
 
-        unacked = (dev->send_seq + (8 - dev->unack_seq) ) & 0x07;
+	unacked = (dev->send_seq + (8 - dev->unack_seq)) & 0x07;
 
 	save_flags(flags);
 	cli();
 
-        if (dev->free > 16 && dev->write_queue && unacked < 7) {
+	if (dev->free > 16 && dev->write_queue && unacked < 7) {
 
-                if (!dev->w_busy)
-                        dev->w_busy = 1;
-                else
-                {
-                        restore_flags(flags);
-                        return;
-                }
+		if (!dev->w_busy)
+			dev->w_busy = 1;
+		else {
+			restore_flags(flags);
+			return;
+		}
 
 
-                frame = dev->write_queue;
-                free = dev->free;
+		frame = dev->write_queue;
+		free = dev->free;
 
-                restore_flags(flags);
+		restore_flags(flags);
 
 		if (frame->copied == 0) {
 
-                        /* Type 0 frame */
+			/* Type 0 frame */
 
-                        struct msg_fmt * msg;
+			struct msg_fmt *msg;
 
 			if (frame->skb)
 				totlen = FRAME_HDR_LEN + PREHDR_LEN + frame->skb->len;
@@ -216,67 +214,65 @@
 
 			flen = MIN(totlen, free);
 
-                        msg = (struct msg_fmt *) &(frame->msg);
+			msg = (struct msg_fmt *) &(frame->msg);
+
+			/*
+			 *  Board level 2 header
+			 */
 
-                        /*
-                         *  Board level 2 header 
-                         */
+			pcbit_writew(dev, flen - FRAME_HDR_LEN);
 
-                        pcbit_writew(dev, flen - FRAME_HDR_LEN);
+			pcbit_writeb(dev, msg->cpu);
 
-                        pcbit_writeb(dev, msg->cpu);
+			pcbit_writeb(dev, msg->proc);
 
-                        pcbit_writeb(dev, msg->proc);
+			/* TH */
+			pcbit_writew(dev, frame->hdr_len + PREHDR_LEN);
 
-                        /* TH */
-                        pcbit_writew(dev, frame->hdr_len + PREHDR_LEN);
+			/* TD */
+			pcbit_writew(dev, frame->dt_len);
 
-                        /* TD */
-                        pcbit_writew(dev, frame->dt_len);
 
+			/*
+			 *  Board level 3 fixed-header
+			 */
 
-                        /*
-                         *  Board level 3 fixed-header 
-                         */
-	
-                        /* LEN = TH */
-                        pcbit_writew(dev, frame->hdr_len + PREHDR_LEN);
-	
-                        /* XX */
-                        pcbit_writew(dev, 0);
+			/* LEN = TH */
+			pcbit_writew(dev, frame->hdr_len + PREHDR_LEN);
 
-                        /* C + S */
-                        pcbit_writeb(dev, msg->cmd);
-                        pcbit_writeb(dev, msg->scmd);
+			/* XX */
+			pcbit_writew(dev, 0);
+
+			/* C + S */
+			pcbit_writeb(dev, msg->cmd);
+			pcbit_writeb(dev, msg->scmd);
+
+			/* NUM */
+			pcbit_writew(dev, frame->refnum);
 
-                        /* NUM */
-                        pcbit_writew(dev, frame->refnum);
-			
 			count = FRAME_HDR_LEN + PREHDR_LEN;
-		}
-		else {
+		} else {
 			/* Type 1 frame */
 
-                        totlen = 2 + (frame->skb->len - frame->copied);
-	  
+			totlen = 2 + (frame->skb->len - frame->copied);
+
 			flen = MIN(totlen, free);
 
-                        /* TT */
-                        tt = ((ushort) (flen - 2)) | 0x8000U; /* Type 1 */
-                        pcbit_writew(dev, tt);
+			/* TT */
+			tt = ((ushort) (flen - 2)) | 0x8000U;	/* Type 1 */
+			pcbit_writew(dev, tt);
 
 			count = 2;
 		}
 
 		if (frame->skb) {
-			cp_len = MIN(frame->skb->len - frame->copied, 
-                                     flen - count);
-		
-			memcpy_topcbit(dev, frame->skb->data + frame->copied, 
-                                       cp_len);
+			cp_len = MIN(frame->skb->len - frame->copied,
+				     flen - count);
+
+			memcpy_topcbit(dev, frame->skb->data + frame->copied,
+				       cp_len);
 			frame->copied += cp_len;
 		}
-
 		/* bookkeeping */
 		dev->free -= flen;
 		pcbit_tx_update(dev, flen);
@@ -285,28 +281,24 @@
 		cli();
 
 
-                if (frame->skb == NULL || frame->copied == frame->skb->len) {
-
-                        dev->write_queue = frame->next;
+		if (frame->skb == NULL || frame->copied == frame->skb->len) {
 
-                        if (frame->skb != NULL) {
-                                /* free frame */
-                                dev_kfree_skb(frame->skb, FREE_WRITE);
-                        }
-
-                        kfree(frame);
-                }
+			dev->write_queue = frame->next;
 
+			if (frame->skb != NULL) {
+				/* free frame */
+				dev_kfree_skb(frame->skb, FREE_WRITE);
+			}
+			kfree(frame);
+		}
 		dev->w_busy = 0;
-		restore_flags(flags);				
-        }
-        else
-	{
 		restore_flags(flags);
-#ifdef DEBUG		
-                printk(KERN_DEBUG "unacked %d free %d write_queue %s\n",
-                       unacked, dev->free, dev->write_queue ? "not empty" : 
-		       "empty");	
+	} else {
+		restore_flags(flags);
+#ifdef DEBUG
+		printk(KERN_DEBUG "unacked %d free %d write_queue %s\n",
+		     unacked, dev->free, dev->write_queue ? "not empty" :
+		       "empty");
 #endif
 	}
 }
@@ -316,18 +308,18 @@
  *  deliver a queued frame to the upper layer
  */
 
-void pcbit_deliver(void * data)
-{ 
-        struct frame_buf *frame;
-        unsigned long flags;
+void
+pcbit_deliver(void *data)
+{
+	struct frame_buf *frame;
+	unsigned long flags;
 	struct msg_fmt msg;
-        struct pcbit_dev *dev = (struct pcbit_dev *) data;
+	struct pcbit_dev *dev = (struct pcbit_dev *) data;
 
 	save_flags(flags);
-        cli();
+	cli();
 
-	while((frame=dev->read_queue))
-	{
+	while ((frame = dev->read_queue)) {
 		dev->read_queue = frame->next;
 		restore_flags(flags);
 
@@ -336,12 +328,12 @@
 		msg.cmd = frame->skb->data[2];
 		msg.scmd = frame->skb->data[3];
 
-		frame->refnum = *((ushort*) frame->skb->data + 4);
-		frame->msg = *((ulong*) &msg);
-		
+		frame->refnum = *((ushort *) frame->skb->data + 4);
+		frame->msg = *((ulong *) & msg);
+
 		skb_pull(frame->skb, 6);
 
-		pcbit_l3_receive(dev, frame->msg, frame->skb, frame->hdr_len, 
+		pcbit_l3_receive(dev, frame->msg, frame->skb, frame->hdr_len,
 				 frame->refnum);
 
 		kfree(frame);
@@ -354,110 +346,104 @@
 }
 
 /*
- * Reads BANK 2 & Reassembles 
+ * Reads BANK 2 & Reassembles
  */
 
-static void pcbit_receive(struct pcbit_dev * dev)
+static void
+pcbit_receive(struct pcbit_dev *dev)
 {
-        unsigned short tt;
-	u_char cpu, proc;
-        struct frame_buf * frame = NULL;
-        unsigned long flags;
-        u_char type1;
+	unsigned short tt;
+	u_char cpu,
+	 proc;
+	struct frame_buf *frame = NULL;
+	unsigned long flags;
+	u_char type1;
 
-        if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING)
-                return;
+	if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING)
+		return;
 
-        tt = pcbit_readw(dev);
+	tt = pcbit_readw(dev);
 
-        if ((tt & 0x7fffU) > 511) {
-                printk(KERN_INFO "pcbit: invalid frame length -> TT=%04x\n", 
+	if ((tt & 0x7fffU) > 511) {
+		printk(KERN_INFO "pcbit: invalid frame length -> TT=%04x\n",
 		       tt);
-                pcbit_l2_error(dev);
-                return;
-        }
-
-        if (!(tt & 0x8000U))
-        {                       /* Type 0 */
-                type1 = 0;
+		pcbit_l2_error(dev);
+		return;
+	}
+	if (!(tt & 0x8000U)) {  /* Type 0 */
+		type1 = 0;
 
-                if (dev->read_frame) {
-                        printk(KERN_DEBUG "pcbit_receive: Type 0 frame and read_frame != NULL\n");
+		if (dev->read_frame) {
+			printk(KERN_DEBUG "pcbit_receive: Type 0 frame and read_frame != NULL\n");
 #if 0
-                        pcbit_l2_error(dev);
-                        return;
+			pcbit_l2_error(dev);
+			return;
 #else
 			/* discard previous queued frame */
 			if (dev->read_frame->skb) {
-				dev->read_frame->skb->free = 1;
+				SET_SKB_FREE(dev->read_frame->skb);
 				kfree_skb(dev->read_frame->skb, FREE_READ);
 			}
 			kfree(dev->read_frame);
 			dev->read_frame = NULL;
 #endif
-                }
+		}
+		frame = kmalloc(sizeof(struct frame_buf), GFP_ATOMIC);
+
+		if (frame == NULL) {
+			printk(KERN_WARNING "kmalloc failed\n");
+			return;
+		}
+		memset(frame, 0, sizeof(struct frame_buf));
+
+		cpu = pcbit_readb(dev);
+		proc = pcbit_readb(dev);
 
-                frame = kmalloc(sizeof(struct frame_buf), GFP_ATOMIC);
-      
-                if (frame == NULL) {
-                        printk(KERN_WARNING "kmalloc failed\n");
-                        return;
-                }
-                memset(frame, 0, sizeof(struct frame_buf));
-      
-                cpu = pcbit_readb(dev);
-                proc = pcbit_readb(dev);
-
-
-                if (cpu != 0x06 && cpu != 0x02)
-                {
-                        printk (KERN_DEBUG "pcbit: invalid cpu value\n");
+
+		if (cpu != 0x06 && cpu != 0x02) {
+			printk(KERN_DEBUG "pcbit: invalid cpu value\n");
 			kfree(frame);
 			pcbit_l2_error(dev);
-                        return;
-                }
-
-                /*
-                 * we discard cpu & proc on receiving
-                 * but we read it to update the pointer
-                 */
-      
-                frame->hdr_len = pcbit_readw(dev);
-                frame->dt_len = pcbit_readw(dev);
-
-		/* 
-                 * 0 sized packet 
-                 * I don't know if they are an error or not...
-                 * But they are very frequent
-                 * Not documented
-                 */
+			return;
+		}
+		/*
+		 * we discard cpu & proc on receiving
+		 * but we read it to update the pointer
+		 */
+
+		frame->hdr_len = pcbit_readw(dev);
+		frame->dt_len = pcbit_readw(dev);
+
+		/*
+		   * 0 sized packet
+		   * I don't know if they are an error or not...
+		   * But they are very frequent
+		   * Not documented
+		 */
 
 		if (frame->hdr_len == 0) {
-                        kfree(frame);
+			kfree(frame);
 #ifdef DEBUG
-                        printk(KERN_DEBUG "0 sized frame\n");
+			printk(KERN_DEBUG "0 sized frame\n");
 #endif
 			pcbit_firmware_bug(dev);
-                        return;
+			return;
 		}
-		
-                /* sanity check the length values */
-                if (frame->hdr_len > 1024 || frame->dt_len > 2048)
-                {
+		/* sanity check the length values */
+		if (frame->hdr_len > 1024 || frame->dt_len > 2048) {
 #ifdef DEBUG
-                        printk(KERN_DEBUG "length problem: ");
-                        printk(KERN_DEBUG "TH=%04x TD=%04x\n", 
-			       frame->hdr_len, 
-                               frame->dt_len);
+			printk(KERN_DEBUG "length problem: ");
+			printk(KERN_DEBUG "TH=%04x TD=%04x\n",
+			       frame->hdr_len,
+			       frame->dt_len);
 #endif
 			pcbit_l2_error(dev);
 			kfree(frame);
-                        return;
-                }
-
-                /* minimum frame read */
+			return;
+		}
+		/* minimum frame read */
 
-		frame->skb = dev_alloc_skb(frame->hdr_len + frame->dt_len + 
+		frame->skb = dev_alloc_skb(frame->hdr_len + frame->dt_len +
 					   ((frame->hdr_len + 15) & ~15));
 
 		if (!frame->skb) {
@@ -465,62 +451,57 @@
 			kfree(frame);
 			return;
 		}
-
-                /* 16 byte alignment for IP */		       
+		/* 16 byte alignment for IP */
 		if (frame->dt_len)
-                        skb_reserve(frame->skb, (frame->hdr_len + 15) & ~15);
+			skb_reserve(frame->skb, (frame->hdr_len + 15) & ~15);
 
-        }
-        else {
+	} else {
 		/* Type 1 */
-                type1 = 1;
-                tt &= 0x7fffU;
+		type1 = 1;
+		tt &= 0x7fffU;
 
-                if (!(frame = dev->read_frame)) {                
-                        printk("Type 1 frame and no frame queued\n");
+		if (!(frame = dev->read_frame)) {
+			printk("Type 1 frame and no frame queued\n");
 #if 1
 			/* usually after an error: toss frame */
 			dev->readptr += tt;
 			if (dev->readptr > dev->sh_mem + BANK2 + BANKLEN)
 				dev->readptr -= BANKLEN;
 #else
-                        pcbit_l2_error(dev);
+			pcbit_l2_error(dev);
 #endif
-                        return;
+			return;
 
-                }
-        }
+		}
+	}
 
 	memcpy_frompcbit(dev, skb_put(frame->skb, tt), tt);
 
 	frame->copied += tt;
 
-        if (frame->copied == frame->hdr_len + frame->dt_len) {
-           
-                save_flags(flags);
-                cli();
-      
-                if (type1) {
-                        dev->read_frame = NULL;
-                }
-      
-                if (dev->read_queue) {
-                        struct frame_buf *ptr;
-                        for(ptr=dev->read_queue;ptr->next;ptr=ptr->next);
-                        ptr->next = frame;
-                }
-                else
-                        dev->read_queue = frame;
-                
-                restore_flags(flags);
-
-        }
-        else {        
-                save_flags(flags);
-                cli();
-                dev->read_frame = frame;
-                restore_flags(flags);
-        }
+	if (frame->copied == frame->hdr_len + frame->dt_len) {
+
+		save_flags(flags);
+		cli();
+
+		if (type1) {
+			dev->read_frame = NULL;
+		}
+		if (dev->read_queue) {
+			struct frame_buf *ptr;
+			for (ptr = dev->read_queue; ptr->next; ptr = ptr->next);
+			ptr->next = frame;
+		} else
+			dev->read_queue = frame;
+
+		restore_flags(flags);
+
+	} else {
+		save_flags(flags);
+		cli();
+		dev->read_frame = frame;
+		restore_flags(flags);
+	}
 }
 
 /*
@@ -529,194 +510,173 @@
  *  gotta send a fake acknowledgment to the upper layer somehow
  */
 
-static __inline__ void pcbit_fake_conf(struct pcbit_dev *dev, struct pcbit_chan * chan)
+static __inline__ void
+pcbit_fake_conf(struct pcbit_dev *dev, struct pcbit_chan *chan)
 {
-     isdn_ctrl ictl;
+	isdn_ctrl ictl;
 
-     if (chan->queued) {
-	     chan->queued--;
-	     
-	     ictl.driver = dev->id;
-	     ictl.command = ISDN_STAT_BSENT;
-	     ictl.arg = chan->id;
-	     dev->dev_if->statcallb(&ictl);     
-     }
+	if (chan->queued) {
+		chan->queued--;
+
+		ictl.driver = dev->id;
+		ictl.command = ISDN_STAT_BSENT;
+		ictl.arg = chan->id;
+		dev->dev_if->statcallb(&ictl);
+	}
 }
 
-static void pcbit_firmware_bug(struct pcbit_dev * dev) 
+static void
+pcbit_firmware_bug(struct pcbit_dev *dev)
 {
-        struct pcbit_chan *chan;
-        
-        chan = dev->b1;
+	struct pcbit_chan *chan;
 
-        if (chan->fsm_state == ST_ACTIVE) {
-	    pcbit_fake_conf(dev, chan);
-        }
+	chan = dev->b1;
 
-        chan = dev->b2;
+	if (chan->fsm_state == ST_ACTIVE) {
+		pcbit_fake_conf(dev, chan);
+	}
+	chan = dev->b2;
 
-        if (chan->fsm_state == ST_ACTIVE) {
-	    pcbit_fake_conf(dev, chan);
-        }
- 
+	if (chan->fsm_state == ST_ACTIVE) {
+		pcbit_fake_conf(dev, chan);
+	}
 }
 
-void pcbit_irq_handler(int interrupt, void * devptr, struct pt_regs *regs)
+void
+pcbit_irq_handler(int interrupt, void *devptr, struct pt_regs *regs)
 {
-        struct pcbit_dev * dev;
-        u_char info, ack_seq, read_seq;
+	struct pcbit_dev *dev;
+	u_char info,
+	 ack_seq,
+	 read_seq;
 
 	dev = (struct pcbit_dev *) devptr;
 
-        if (!dev)
-        {
-                printk(KERN_WARNING "pcbit_irq_handler: wrong device\n");
-                return;
-        }
-
+	if (!dev) {
+		printk(KERN_WARNING "pcbit_irq_handler: wrong device\n");
+		return;
+	}
 	if (dev->interrupt) {
-	        printk(KERN_DEBUG "pcbit: reentering interrupt hander\n");
+		printk(KERN_DEBUG "pcbit: reentering interrupt hander\n");
 		return;
 	}
-
 	dev->interrupt = 1;
 
-        info = readb(dev->sh_mem + BANK3);
+	info = readb(dev->sh_mem + BANK3);
 
-        if (dev->l2_state == L2_STARTING || dev->l2_state == L2_ERROR)
-        {
-                pcbit_l2_active_conf(dev, info);
+	if (dev->l2_state == L2_STARTING || dev->l2_state == L2_ERROR) {
+		pcbit_l2_active_conf(dev, info);
 		dev->interrupt = 0;
-                return;
-        }
-
-        if (info & 0x40U)       /* E bit set */
-        {
+		return;
+	}
+	if (info & 0x40U) {     /* E bit set */
 #ifdef DEBUG
 		printk(KERN_DEBUG "pcbit_irq_handler: E bit on\n");
 #endif
-                pcbit_l2_error(dev);
+		pcbit_l2_error(dev);
 		dev->interrupt = 0;
-                return;
-        }
-
-        if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) 
-	{
-	        dev->interrupt = 0;
-                return;
+		return;
 	}
+	if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) {
+		dev->interrupt = 0;
+		return;
+	}
+	ack_seq = (info >> 3) & 0x07U;
+	read_seq = (info & 0x07U);
 
-        ack_seq =  (info >> 3) & 0x07U;
-        read_seq = (info & 0x07U); 
-        
 	dev->interrupt = 0;
 
-        if (read_seq != dev->rcv_seq)
-        {
-		while (read_seq != dev->rcv_seq)
-		{
+	if (read_seq != dev->rcv_seq) {
+		while (read_seq != dev->rcv_seq) {
 			pcbit_receive(dev);
 			dev->rcv_seq = (dev->rcv_seq + 1) % 8;
 		}
 		pcbit_sched_delivery(dev);
-        }
-
-	if (ack_seq != dev->unack_seq)
-        {
-                pcbit_recv_ack(dev, ack_seq);
-        }
-
-
+	}
+	if (ack_seq != dev->unack_seq) {
+		pcbit_recv_ack(dev, ack_seq);
+	}
 	info = dev->rcv_seq << 3;
 	info |= dev->send_seq;
-    
-	writeb(info, dev->sh_mem + BANK4);  
+
+	writeb(info, dev->sh_mem + BANK4);
 }
 
 
-static void pcbit_l2_active_conf(struct pcbit_dev *dev, u_char info)
+static void
+pcbit_l2_active_conf(struct pcbit_dev *dev, u_char info)
 {
-        u_char state;
+	u_char state;
 
-        state = dev->l2_state;
+	state = dev->l2_state;
 
 #ifdef DEBUG
-        printk(KERN_DEBUG "layer2_active_confirm\n");
+	printk(KERN_DEBUG "layer2_active_confirm\n");
 #endif
-        
 
-        if (info & 0x80U ) {
-                dev->rcv_seq = info & 0x07U;
-                dev->l2_state = L2_RUNNING;
-        }
-        else
-                dev->l2_state = L2_DOWN;
-                
-	if (state == L2_STARTING)
-                wake_up_interruptible(&dev->set_running_wq);
 
-        if (state == L2_ERROR && dev->l2_state == L2_RUNNING) {
-                pcbit_transmit(dev);
-        }
+	if (info & 0x80U) {
+		dev->rcv_seq = info & 0x07U;
+		dev->l2_state = L2_RUNNING;
+	} else
+		dev->l2_state = L2_DOWN;
+
+	if (state == L2_STARTING)
+		wake_up_interruptible(&dev->set_running_wq);
 
+	if (state == L2_ERROR && dev->l2_state == L2_RUNNING) {
+		pcbit_transmit(dev);
+	}
 }
 
-static void pcbit_l2_err_recover(unsigned long data) 
+static void
+pcbit_l2_err_recover(unsigned long data)
 {
 
-        struct pcbit_dev * dev;
-        struct frame_buf *frame;
+	struct pcbit_dev *dev;
+	struct frame_buf *frame;
 
-        dev = (struct pcbit_dev *) data;
+	dev = (struct pcbit_dev *) data;
 
-        del_timer(&dev->error_recover_timer);
-	if (dev->w_busy || dev->r_busy)
-	{
+	del_timer(&dev->error_recover_timer);
+	if (dev->w_busy || dev->r_busy) {
 		init_timer(&dev->error_recover_timer);
 		dev->error_recover_timer.expires = jiffies + ERRTIME;
 		add_timer(&dev->error_recover_timer);
 		return;
 	}
-
 	dev->w_busy = dev->r_busy = 1;
 
-        if (dev->read_frame)
-	{
-                if (dev->read_frame->skb)
-		{
-			dev->read_frame->skb->free = 1; 
-                        kfree_skb(dev->read_frame->skb, FREE_READ);
-		}
-                kfree(dev->read_frame);
-                dev->read_frame = NULL;
-        }
-
-
-        if (dev->write_queue)
-	{
-                frame = dev->write_queue;
+	if (dev->read_frame) {
+		if (dev->read_frame->skb) {
+			SET_SKB_FREE(dev->read_frame->skb);
+			kfree_skb(dev->read_frame->skb, FREE_READ);
+		}
+		kfree(dev->read_frame);
+		dev->read_frame = NULL;
+	}
+	if (dev->write_queue) {
+		frame = dev->write_queue;
 #ifdef FREE_ON_ERROR
-                dev->write_queue = dev->write_queue->next;
+		dev->write_queue = dev->write_queue->next;
 
 		if (frame->skb) {
-                        dev_kfree_skb(frame->skb, FREE_WRITE);
+			dev_kfree_skb(frame->skb, FREE_WRITE);
 		}
-		
-                kfree(frame);
-#else		
-                frame->copied = 0;
+		kfree(frame);
+#else
+		frame->copied = 0;
 #endif
-        }
-
-        dev->rcv_seq = dev->send_seq = dev->unack_seq = 0;
-        dev->free = 511;
-        dev->l2_state = L2_ERROR;
+	}
+	dev->rcv_seq = dev->send_seq = dev->unack_seq = 0;
+	dev->free = 511;
+	dev->l2_state = L2_ERROR;
 
 	/* this is an hack... */
 	pcbit_firmware_bug(dev);
 
-        dev->writeptr = dev->sh_mem;
-        dev->readptr = dev->sh_mem + BANK2;
+	dev->writeptr = dev->sh_mem;
+	dev->readptr = dev->sh_mem + BANK2;
 
 	writeb((0x80U | ((dev->rcv_seq & 0x07) << 3) | (dev->send_seq & 0x07)),
 	       dev->sh_mem + BANK4);
@@ -724,24 +684,25 @@
 
 }
 
-static void pcbit_l2_error(struct pcbit_dev *dev)
+static void
+pcbit_l2_error(struct pcbit_dev *dev)
 {
-        if (dev->l2_state == L2_RUNNING) {
+	if (dev->l2_state == L2_RUNNING) {
 
-                printk(KERN_INFO "pcbit: layer 2 error\n");
+		printk(KERN_INFO "pcbit: layer 2 error\n");
 
 #ifdef DEBUG
-                log_state(dev);
+		log_state(dev);
 #endif
-                
-                dev->l2_state = L2_DOWN;
 
-                init_timer(&dev->error_recover_timer);
-                dev->error_recover_timer.function = &pcbit_l2_err_recover;
-                dev->error_recover_timer.data = (ulong) dev;
-                dev->error_recover_timer.expires = jiffies + ERRTIME;
-                add_timer(&dev->error_recover_timer);
-        }
+		dev->l2_state = L2_DOWN;
+
+		init_timer(&dev->error_recover_timer);
+		dev->error_recover_timer.function = &pcbit_l2_err_recover;
+		dev->error_recover_timer.data = (ulong) dev;
+		dev->error_recover_timer.expires = jiffies + ERRTIME;
+		add_timer(&dev->error_recover_timer);
+	}
 }
 
 /*
@@ -751,58 +712,52 @@
  *   call pcbit_transmit to write possible queued frames
  */
 
-static void  pcbit_recv_ack(struct pcbit_dev *dev, unsigned char ack)
+static void
+pcbit_recv_ack(struct pcbit_dev *dev, unsigned char ack)
 {
-        int i, count;
-        int unacked;
+	int i,
+	 count;
+	int unacked;
 
-        unacked = (dev->send_seq + (8 - dev->unack_seq) ) & 0x07;
+	unacked = (dev->send_seq + (8 - dev->unack_seq)) & 0x07;
 
-        /* dev->unack_seq < ack <= dev->send_seq; */
+	/* dev->unack_seq < ack <= dev->send_seq; */
 
-        if (unacked) 
-	{
+	if (unacked) {
 
 		if (dev->send_seq > dev->unack_seq)
-                        if (ack <= dev->unack_seq || ack > dev->send_seq)
-			{
-                                printk(KERN_DEBUG
-				       "layer 2 ack unacceptable - dev %d",
+			if (ack <= dev->unack_seq || ack > dev->send_seq) {
+				printk(KERN_DEBUG
+				     "layer 2 ack unacceptable - dev %d",
 				       dev->id);
 
-                                pcbit_l2_error(dev);
-                        }
-                        else
-                                if (ack > dev->send_seq && ack <= dev->unack_seq)
-				{
-					printk(KERN_DEBUG
-					       "layer 2 ack unacceptable - dev %d",
-					       dev->id);
-                                        pcbit_l2_error(dev);
-                                }
-
-                /* ack is acceptable */
-
-
-                i = dev->unack_seq;
-
-                do {
-                        dev->unack_seq = i = (i + 1) % 8;
-                        dev->free += dev->fsize[i];
-                } while (i != ack);
-
-                count = 0;
-                while (count < 7 && dev->write_queue) 
-		{
+				pcbit_l2_error(dev);
+			} else if (ack > dev->send_seq && ack <= dev->unack_seq) {
+				printk(KERN_DEBUG
+				     "layer 2 ack unacceptable - dev %d",
+				       dev->id);
+				pcbit_l2_error(dev);
+			}
+		/* ack is acceptable */
+
+
+		i = dev->unack_seq;
+
+		do {
+			dev->unack_seq = i = (i + 1) % 8;
+			dev->free += dev->fsize[i];
+		} while (i != ack);
+
+		count = 0;
+		while (count < 7 && dev->write_queue) {
 			u8 lsend_seq = dev->send_seq;
 
-                        pcbit_transmit(dev);
+			pcbit_transmit(dev);
 
 			if (dev->send_seq == lsend_seq)
 				break;
-                        count++;
-                }    
-        }
-        else
-                printk(KERN_DEBUG "recv_ack: unacked = 0\n");
+			count++;
+		}
+	} else
+		printk(KERN_DEBUG "recv_ack: unacked = 0\n");
 }
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/pcbit/module.c linux/drivers/isdn/pcbit/module.c
--- v2.0.30/linux/drivers/isdn/pcbit/module.c	Tue May 14 23:09:00 1996
+++ linux/drivers/isdn/pcbit/module.c	Mon Aug  4 17:34:01 1997
@@ -22,8 +22,8 @@
 #include <linux/isdnif.h>
 #include "pcbit.h"
 
-int mem[MAX_PCBIT_CARDS] = {0, };
-int irq[MAX_PCBIT_CARDS] = {0, };
+static int mem[MAX_PCBIT_CARDS] = {0, };
+static int irq[MAX_PCBIT_CARDS] = {0, };
 
 int num_boards;
 struct pcbit_dev * dev_pcbit[MAX_PCBIT_CARDS] = {0, 0, 0, 0};
@@ -35,6 +35,10 @@
 extern int pcbit_init_dev(int board, int mem_base, int irq);
 
 #ifdef MODULE
+#if (LINUX_VERSION_CODE > 0x020111)
+MODULE_PARM(mem, "1-" __MODULE_STRING(MAX_PCBIT_CARDS) "i");
+MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_PCBIT_CARDS) "i");
+#endif
 #define pcbit_init init_module
 #endif
 
@@ -83,7 +87,11 @@
 	}
 
 	/* No symbols to export, hide all symbols */
+#if (LINUX_VERSION_CODE < 0x020111)
 	register_symtab(NULL);
+#else
+	EXPORT_NO_SYMBOLS;
+#endif
 
 	return 0;
 }
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/Makefile linux/drivers/isdn/sc/Makefile
--- v2.0.30/linux/drivers/isdn/sc/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/Makefile	Mon Aug  4 17:34:01 1997
@@ -0,0 +1,44 @@
+#
+#  $Id: Makefile,v 1.1 1997/03/22 02:01:22 fritz Exp $
+#  Copyright (C) 1996  SpellCaster Telecommunications 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 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.
+#
+#  For more information, please contact gpl-info@spellcast.com or write:
+#
+#     SpellCaster Telecommunications Inc.
+#     5621 Finch Avenue East, Unit #3
+#     Scarborough, Ontario  Canada
+#     M1B 2T9
+#     +1 (416) 297-8565
+#     +1 (416) 297-6433 Facsimile
+#
+
+L_OBJS :=
+M_OBJS :=
+O_OBJS := shmem.o init.o debug.o packet.o command.o event.o \
+	ioctl.o interrupt.o message.o timer.o
+
+O_TARGET :=
+ifeq ($(CONFIG_ISDN_DRV_SC),y)
+	O_TARGET += sc.o
+else
+  ifeq ($(CONFIG_ISDN_DRV_SC),m)
+    O_TARGET += sc.o
+    M_OBJS += sc.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/card.h linux/drivers/isdn/sc/card.h
--- v2.0.30/linux/drivers/isdn/sc/card.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/card.h	Mon Aug  4 17:34:01 1997
@@ -0,0 +1,110 @@
+/*
+ *  $Id: card.h,v 1.1 1996/11/07 13:07:40 fritz Exp $
+ *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ *  card.h - Driver parameters for SpellCaster ISA ISDN adapters
+ *
+ *  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.
+ *
+ *  For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#ifndef CARD_H
+#define CARD_H
+
+/*
+ * We need these if they're not already included
+ */
+#include <linux/timer.h>
+#include <linux/isdnif.h>
+#include "message.h"
+
+/*
+ * Amount of time to wait for a reset to complete
+ */
+#define CHECKRESET_TIME		milliseconds(4000)
+
+/*
+ * Amount of time between line status checks
+ */
+#define CHECKSTAT_TIME		milliseconds(8000)
+
+/*
+ * The maximum amount of time to wait for a message response
+ * to arrive. Use exclusively by send_and_receive
+ */
+#define SAR_TIMEOUT		milliseconds(10000)
+
+/*
+ * Macro to determine is a card id is valid
+ */
+#define IS_VALID_CARD(x)	((x >= 0) && (x <= cinst))
+
+/*
+ * Per channel status and configuration
+ */
+typedef struct {
+	int l2_proto;
+	int l3_proto;
+	char dn[50];
+	unsigned long first_sendbuf;	/* Offset of first send buffer */
+	unsigned int num_sendbufs;	/* Number of send buffers */
+	unsigned int free_sendbufs;	/* Number of free sendbufs */
+	unsigned int next_sendbuf;	/* Next sequential buffer */
+	char eazlist[50];		/* Set with SETEAZ */
+	char sillist[50];		/* Set with SETSIL */
+	int eazclear;			/* Don't accept calls if TRUE */
+} bchan;
+
+/*
+ * Everything you want to know about the adapter ...
+ */
+typedef struct {
+	int model;
+	int driverId;			/* LL Id */
+	char devicename[20];		/* The device name */
+	isdn_if *card;			/* ISDN4Linux structure */
+	bchan *channel;			/* status of the B channels */
+	char nChannels;			/* Number of channels */
+	unsigned int interrupt;		/* Interrupt number */
+	int iobase;			/* I/O Base address */
+	int ioport[MAX_IO_REGS];	/* Index to I/O ports */
+	int shmem_pgport;		/* port for the exp mem page reg. */
+	int shmem_magic;		/* adapter magic number */
+	unsigned int rambase;		/* Shared RAM base address */
+	unsigned int ramsize;		/* Size of shared memory */
+	RspMessage async_msg;		/* Async response message */
+	int want_async_messages;	/* Snoop the Q ? */
+	unsigned char seq_no;		/* Next send seq. number */
+	struct timer_list reset_timer;	/* Check reset timer */
+	struct timer_list stat_timer;	/* Check startproc timer */
+	unsigned char nphystat;		/* Latest PhyStat info */
+	unsigned char phystat;		/* Last PhyStat info */
+	HWConfig_pl hwconfig;		/* Hardware config info */
+	char load_ver[11];		/* CommManage Version string */
+	char proc_ver[11];		/* CommEngine Version */
+	int StartOnReset;		/* Indicates startproc after reset */
+	int EngineUp;			/* Indicates CommEngine Up */
+	int trace_mode;			/* Indicate if tracing is on */
+} board;
+
+#endif /* CARD_H */
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/command.c linux/drivers/isdn/sc/command.c
--- v2.0.30/linux/drivers/isdn/sc/command.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/command.c	Mon Aug  4 17:34:01 1997
@@ -0,0 +1,563 @@
+/*
+ *  $Id: command.c,v 1.4 1997/03/30 16:51:34 calle Exp $
+ *  Copyright (C) 1996  SpellCaster Telecommunications 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 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.
+ *
+ *  For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#define __NO_VERSION__
+#include "includes.h"		/* This must be first */
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+#include "scioc.h"
+
+int dial(int card, unsigned long channel, setup_parm setup);
+int hangup(int card, unsigned long channel);
+int answer(int card, unsigned long channel);
+int clreaz(int card, unsigned long channel);
+int seteaz(int card, unsigned long channel, char *);
+int geteaz(int card, unsigned long channel, char *);
+int setsil(int card, unsigned long channel, char *);
+int getsil(int card, unsigned long channel, char *);
+int setl2(int card, unsigned long arg);
+int getl2(int card, unsigned long arg);
+int setl3(int card, unsigned long arg);
+int getl3(int card, unsigned long arg);
+int lock(void);
+int unlock(void);
+int acceptb(int card, unsigned long channel);
+
+extern int cinst;
+extern board *adapter[];
+
+extern int sc_ioctl(int, scs_ioctl *);
+extern int setup_buffers(int, int, unsigned int);
+extern int indicate_status(int, int,ulong,char*);
+extern void check_reset(unsigned long);
+extern int send_and_receive(int, unsigned int, unsigned char, unsigned char,
+                unsigned char, unsigned char, unsigned char, unsigned char *,
+                RspMessage *, int);
+extern int sendmessage(int, unsigned int, unsigned int, unsigned int,
+                unsigned int, unsigned int, unsigned int, unsigned int *);
+extern inline void pullphone(char *, char *);
+
+#ifdef DEBUG
+/*
+ * Translate command codes to strings
+ */
+static char *commands[] = { "ISDN_CMD_IOCTL",
+			    "ISDN_CMD_DIAL",
+			    "ISDN_CMD_ACCEPTB",
+			    "ISDN_CMD_ACCEPTB",
+			    "ISDN_CMD_HANGUP",
+			    "ISDN_CMD_CLREAZ",
+			    "ISDN_CMD_SETEAZ",
+			    "ISDN_CMD_GETEAZ",
+			    "ISDN_CMD_SETSIL",
+			    "ISDN_CMD_GETSIL",
+			    "ISDN_CMD_SETL2",
+			    "ISDN_CMD_GETL2",
+			    "ISDN_CMD_SETL3",
+			    "ISDN_CMD_GETL3",
+			    "ISDN_CMD_LOCK",
+			    "ISDN_CMD_UNLOCK",
+			    "ISDN_CMD_SUSPEND",
+			    "ISDN_CMD_RESUME" };
+
+/*
+ * Translates ISDN4Linux protocol codes to strings for debug messages
+ */
+static char *l3protos[] = { "ISDN_PROTO_L3_TRANS" };
+static char *l2protos[] = { "ISDN_PROTO_L2_X75I",
+			    "ISDN_PROTO_L2_X75UI",
+			    "ISDN_PROTO_L2_X75BUI",
+			    "ISDN_PROTO_L2_HDLC",
+			    "ISDN_PROTO_L2_TRANS" };
+#endif
+
+int get_card_from_id(int driver)
+{
+	int i;
+
+	for(i = 0 ; i < cinst ; i++) {
+		if(adapter[i]->driverId == driver)
+			return i;
+	}
+	return -NODEV;
+}
+
+/* 
+ * command
+ */
+
+int command(isdn_ctrl *cmd)
+{
+	int card;
+
+	card = get_card_from_id(cmd->driver);
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	pr_debug("%s: Received %s command from Link Layer\n",
+		adapter[card]->devicename, commands[cmd->command]);
+
+	/*
+	 * Dispatch the command
+	 */
+	switch(cmd->command) {
+	case ISDN_CMD_IOCTL:
+	{
+		unsigned long 	cmdptr;
+		scs_ioctl	ioc;
+		int		err;
+
+		memcpy(&cmdptr, cmd->parm.num, sizeof(unsigned long));
+		if((err = copy_from_user(&ioc, (scs_ioctl *) cmdptr, 
+			sizeof(scs_ioctl)))) {
+			pr_debug("%s: Failed to verify user space 0x%x\n",
+				adapter[card]->devicename, cmdptr);
+			return err;
+		}
+		return sc_ioctl(card, &ioc);
+	}
+	case ISDN_CMD_DIAL:
+		return dial(card, cmd->arg, cmd->parm.setup);
+	case ISDN_CMD_HANGUP:
+		return hangup(card, cmd->arg);
+	case ISDN_CMD_ACCEPTD:
+		return answer(card, cmd->arg);
+	case ISDN_CMD_ACCEPTB:
+		return acceptb(card, cmd->arg);
+	case ISDN_CMD_CLREAZ:
+		return clreaz(card, cmd->arg);
+	case ISDN_CMD_SETEAZ:
+		return seteaz(card, cmd->arg, cmd->parm.num);
+	case ISDN_CMD_GETEAZ:
+		return geteaz(card, cmd->arg, cmd->parm.num);
+	case ISDN_CMD_SETSIL:
+		return setsil(card, cmd->arg, cmd->parm.num);
+	case ISDN_CMD_GETSIL:
+		return getsil(card, cmd->arg, cmd->parm.num);
+	case ISDN_CMD_SETL2:
+		return setl2(card, cmd->arg);
+	case ISDN_CMD_GETL2:
+		return getl2(card, cmd->arg);
+	case ISDN_CMD_SETL3:
+		return setl3(card, cmd->arg);
+	case ISDN_CMD_GETL3:
+		return getl3(card, cmd->arg);
+	case ISDN_CMD_LOCK:
+		return lock();
+	case ISDN_CMD_UNLOCK:
+		return unlock();
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/*
+ * Confirm our ability to communicate with the board.  This test assumes no
+ * other message activity is present
+ */
+int loopback(int card) 
+{
+
+	int status;
+	static char testmsg[] = "Test Message";
+	RspMessage rspmsg;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	pr_debug("%s: Sending loopback message\n", adapter[card]->devicename);
+	
+
+	/*
+	 * Send the loopback message to confirm that memory transfer is
+	 * operational
+	 */
+	status = send_and_receive(card, CMPID, cmReqType1,
+				  cmReqClass0,
+				  cmReqMsgLpbk,
+				  0,
+				  (unsigned char) strlen(testmsg),
+				  (unsigned char *)testmsg,
+				  &rspmsg, SAR_TIMEOUT);
+
+
+	if (!status) {
+		pr_debug("%s: Loopback message successfully sent\n",
+			adapter[card]->devicename);
+		if(strcmp(rspmsg.msg_data.byte_array, testmsg)) {
+			pr_debug("%s: Loopback return != sent\n",
+				adapter[card]->devicename);
+			return -EIO;
+		}
+		return 0;
+	}
+	else {
+		pr_debug("%s: Send loopback message failed\n",
+			adapter[card]->devicename);
+		return -EIO;
+	}
+
+}
+
+/*
+ * start the onboard firmware
+ */
+int startproc(int card) 
+{
+	int status;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	/*
+	 * send start msg 
+	 */
+       	status = sendmessage(card, CMPID,cmReqType2,
+			  cmReqClass0,
+			  cmReqStartProc,
+			  0,0,0);
+	pr_debug("%s: Sent startProc\n", adapter[card]->devicename);
+	
+	return status;
+}
+
+
+int loadproc(int card, char *data) 
+{
+	return -1;
+}
+
+
+/*
+ * Dials the number passed in 
+ */
+int dial(int card, unsigned long channel, setup_parm setup) 
+{
+	int status;
+	char Phone[48];
+  
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	/*extract ISDN number to dial from eaz/msn string*/ 
+	strcpy(Phone,setup.phone); 
+
+	/*send the connection message*/
+	status = sendmessage(card, CEPID,ceReqTypePhy,
+				ceReqClass1,
+				ceReqPhyConnect,
+				(unsigned char) channel+1, 
+				strlen(Phone),
+				(unsigned int *) Phone);
+
+	pr_debug("%s: Dialing %s on channel %d\n",
+		adapter[card]->devicename, Phone, channel+1);
+	
+	return status;
+}
+
+/*
+ * Answer an incoming call 
+ */
+int answer(int card, unsigned long channel) 
+{
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	if(setup_buffers(card, channel+1, BUFFER_SIZE)) {
+		hangup(card, channel+1);
+		return -ENOBUFS;
+	}
+
+	indicate_status(card, ISDN_STAT_BCONN,channel,NULL);
+	pr_debug("%s: Answered incoming call on channel %s\n",
+		adapter[card]->devicename, channel+1);
+	return 0;
+}
+
+/*
+ * Hangup up the call on specified channel
+ */
+int hangup(int card, unsigned long channel) 
+{
+	int status;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	status = sendmessage(card, CEPID, ceReqTypePhy,
+						 ceReqClass1,
+						 ceReqPhyDisconnect,
+						 (unsigned char) channel+1,
+						 0,
+						 NULL);
+	pr_debug("%s: Sent HANGUP message to channel %d\n",
+		adapter[card]->devicename, channel+1);
+	return status;
+}
+
+/*
+ * Set the layer 2 protocol (X.25, HDLC, Raw)
+ */
+int setl2(int card, unsigned long arg) 
+{
+	int status =0;
+	int protocol,channel;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+	protocol = arg >> 8;
+	channel = arg & 0xff;
+	adapter[card]->channel[channel].l2_proto = protocol;
+	pr_debug("%s: Level 2 protocol for channel %d set to %s from %d\n",
+		adapter[card]->devicename, channel+1,l2protos[adapter[card]->channel[channel].l2_proto],protocol);
+
+	/*
+	 * check that the adapter is also set to the correct protocol
+	 */
+	pr_debug("%s: Sending GetFrameFormat for channel %d\n",
+		adapter[card]->devicename, channel+1);
+	status = sendmessage(card, CEPID, ceReqTypeCall,
+ 				ceReqClass0,
+ 				ceReqCallGetFrameFormat,
+ 				(unsigned char)channel+1,
+ 				1,
+ 				(unsigned int *) protocol);
+	if(status) 
+		return status;
+	return 0;
+}
+
+/*
+ * Get the layer 2 protocol
+ */
+int getl2(int card, unsigned long channel) {
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	pr_debug("%s: Level 2 protocol for channel %d reported as %s\n",
+		adapter[card]->devicename, channel+1,
+		l2protos[adapter[card]->channel[channel].l2_proto]);
+
+	return adapter[card]->channel[channel].l2_proto;
+}
+
+/*
+ * Set the layer 3 protocol
+ */
+int setl3(int card, unsigned long channel) 
+{
+	int protocol = channel >> 8;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	adapter[card]->channel[channel].l3_proto = protocol;
+	pr_debug("%s: Level 3 protocol for channel %d set to %s\n",
+		adapter[card]->devicename, channel+1, l3protos[protocol]);
+	return 0;
+}
+
+/*
+ * Get the layer 3 protocol
+ */
+int getl3(int card, unsigned long arg) 
+{
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	pr_debug("%s: Level 3 protocol for channel %d reported as %s\n",
+			adapter[card]->devicename, arg+1,
+			l3protos[adapter[card]->channel[arg].l3_proto]);
+	return adapter[card]->channel[arg].l3_proto;
+}
+
+
+int acceptb(int card, unsigned long channel)
+{
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	if(setup_buffers(card, channel+1, BUFFER_SIZE))
+	{
+		hangup(card, channel+1);
+		return -ENOBUFS;
+	}
+
+	pr_debug("%s: B-Channel connection accepted on channel %d\n",
+		adapter[card]->devicename, channel+1);
+	indicate_status(card, ISDN_STAT_BCONN, channel, NULL);
+	return 0;
+}
+
+int clreaz(int card, unsigned long arg)
+{
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	strcpy(adapter[card]->channel[arg].eazlist, "");
+	adapter[card]->channel[arg].eazclear = 1;
+	pr_debug("%s: EAZ List cleared for channel %d\n",
+		adapter[card]->devicename, arg+1);
+	return 0;
+}
+
+int seteaz(int card, unsigned long arg, char *num)
+{
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	strcpy(adapter[card]->channel[arg].eazlist, num);
+	adapter[card]->channel[arg].eazclear = 0;
+	pr_debug("%s: EAZ list for channel %d set to: %s\n",
+		adapter[card]->devicename, arg+1,
+		adapter[card]->channel[arg].eazlist);
+	return 0;
+}
+
+int geteaz(int card, unsigned long arg, char *num)
+{
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	strcpy(num, adapter[card]->channel[arg].eazlist);
+	pr_debug("%s: EAZ List for channel %d reported: %s\n",
+		adapter[card]->devicename, arg+1,
+		adapter[card]->channel[arg].eazlist);
+	return 0;
+}
+
+int setsil(int card, unsigned long arg, char *num)
+{
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	strcpy(adapter[card]->channel[arg].sillist, num);
+	pr_debug("%s: Service Indicators for channel %d set: %s\n",
+		adapter[card]->devicename, arg+1,
+		adapter[card]->channel[arg].sillist);
+	return 0;
+}
+
+int getsil(int card, unsigned long arg, char *num)
+{
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	strcpy(num, adapter[card]->channel[arg].sillist);
+	pr_debug("%s: SIL for channel %d reported: %s\n",
+		adapter[card]->devicename, arg+1,
+		adapter[card]->channel[arg].sillist);
+	return 0;
+}
+
+
+int lock()
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+int unlock()
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+int reset(int card)
+{
+	unsigned long flags;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	indicate_status(card, ISDN_STAT_STOP, 0, NULL);
+
+	if(adapter[card]->EngineUp) {
+		del_timer(&adapter[card]->stat_timer);	
+	}
+
+	adapter[card]->EngineUp = 0;
+
+	save_flags(flags);
+	cli();
+	init_timer(&adapter[card]->reset_timer);
+	adapter[card]->reset_timer.function = check_reset;
+	adapter[card]->reset_timer.data = card;
+	adapter[card]->reset_timer.expires = jiffies + CHECKRESET_TIME;
+	add_timer(&adapter[card]->reset_timer);
+	restore_flags(flags);
+
+	outb(0x1,adapter[card]->ioport[SFT_RESET]); 
+
+	pr_debug("%s: Adapter Reset\n", adapter[card]->devicename);
+	return 0;
+}
+
+void flushreadfifo (int card)
+{
+	while(inb(adapter[card]->ioport[FIFO_STATUS]) & RF_HAS_DATA)
+		inb(adapter[card]->ioport[FIFO_READ]);
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/debug.c linux/drivers/isdn/sc/debug.c
--- v2.0.30/linux/drivers/isdn/sc/debug.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/debug.c	Mon Aug  4 17:34:01 1997
@@ -0,0 +1,80 @@
+/*
+ *  $Id: debug.c,v 1.2 1996/11/20 17:49:50 fritz Exp $
+ *  Copyright (C) 1996  SpellCaster Telecommunications 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 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.
+ *
+ *  For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+#include <linux/kernel.h>
+
+#define NULL	0x0
+
+#if LINUX_VERSION_CODE < 66363	/* Linux 1.3.59 there was a change to interrupts */
+	#define REQUEST_IRQ(a,b,c,d,e) request_irq(a,b,c,d)
+	#define FREE_IRQ(a,b) free_irq(a)
+#else
+	#define REQUEST_IRQ(a,b,c,d,e) request_irq(a,b,c,d,e)
+	#define FREE_IRQ(a,b) free_irq(a,b)
+#endif
+
+inline char *strcpy(char *, const char *);
+
+int dbg_level = 0;
+static char dbg_funcname[255];
+
+void dbg_endfunc(void)
+{
+	if (dbg_level) {
+		printk("<-- Leaving function %s\n", dbg_funcname);
+		strcpy(dbg_funcname, "");
+	}
+}
+
+void dbg_func(char *func)
+{
+	strcpy(dbg_funcname, func);
+	if(dbg_level)
+		printk("--> Entering function %s\n", dbg_funcname);
+}
+
+inline char *strcpy(char *dest, const char *src)
+{
+	char *i = dest;
+	char *j = (char *) src;
+
+	while(*j) {
+		*i = *j;
+		i++; j++;
+	}
+	*(++i) = NULL;
+	return dest;
+}
+
+inline void pullphone(char *dn, char *str)
+{
+	int i = 0;
+
+	while(dn[i] != ',')
+		str[i] = dn[i++];
+	str[i] = 0x0;
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/debug.h linux/drivers/isdn/sc/debug.h
--- v2.0.30/linux/drivers/isdn/sc/debug.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/debug.h	Mon Aug  4 17:34:01 1997
@@ -0,0 +1,35 @@
+/*
+ *  $Id: debug.h,v 1.1 1996/11/07 13:07:42 fritz Exp $
+ *  Copyright (C) 1996  SpellCaster Telecommunications 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 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.
+ *
+ *  For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#if LINUX_VERSION_CODE < 131072
+	#error You cant use this driver on kernels older than 2.0
+#else
+	#define REQUEST_IRQ(a,b,c,d,e) request_irq(a,b,c,d,e)
+	#define FREE_IRQ(a,b) free_irq(a,b)
+#endif
+
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/event.c linux/drivers/isdn/sc/event.c
--- v2.0.30/linux/drivers/isdn/sc/event.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/event.c	Mon Aug  4 17:34:01 1997
@@ -0,0 +1,75 @@
+/*
+ *  $Id: event.c,v 1.3 1997/02/11 22:53:41 fritz Exp $
+ *  Copyright (C) 1996  SpellCaster Telecommunications 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 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.
+ *
+ *  For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#define __NO_VERSION__
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+
+extern int cinst;
+extern board *adapter[];
+
+#ifdef DEBUG
+static char *events[] = { "ISDN_STAT_STAVAIL",
+			  "ISDN_STAT_ICALL",
+			  "ISDN_STAT_RUN",
+			  "ISDN_STAT_STOP",
+			  "ISDN_STAT_DCONN",
+			  "ISDN_STAT_BCONN",
+			  "ISDN_STAT_DHUP",
+			  "ISDN_STAT_BHUP",
+			  "ISDN_STAT_CINF",
+			  "ISDN_STAT_LOAD",
+			  "ISDN_STAT_UNLOAD",
+			  "ISDN_STAT_BSENT",
+			  "ISDN_STAT_NODCH",
+			  "ISDN_STAT_ADDCH",
+			  "ISDN_STAT_CAUSE" };
+#endif
+
+int indicate_status(int card, int event,ulong Channel,char *Data)
+{
+	isdn_ctrl cmd;
+
+	pr_debug("%s: Indicating event %s on Channel %d\n",
+		adapter[card]->devicename, events[event-256], Channel);
+	if (Data != NULL){
+		pr_debug("%s: Event data: %s\n", adapter[card]->devicename,
+			Data);
+		if (event == ISDN_STAT_ICALL)
+			memcpy(&cmd.parm.setup, Data, sizeof(cmd.parm.setup));
+		else
+			strcpy(cmd.parm.num, Data);
+	}
+
+	cmd.command = event;
+	cmd.driver = adapter[card]->driverId;
+	cmd.arg = Channel;
+	return adapter[card]->card->statcallb(&cmd);
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/hardware.h linux/drivers/isdn/sc/hardware.h
--- v2.0.30/linux/drivers/isdn/sc/hardware.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/hardware.h	Mon Aug  4 17:34:01 1997
@@ -0,0 +1,117 @@
+/*
+ * Hardware specific macros, defines and structures
+ */
+
+#ifndef HARDWARE_H
+#define HARDWARE_H
+
+#include <asm/param.h>			/* For HZ */
+
+/*
+ * General hardware parameters common to all ISA adapters
+ */
+
+#define MAX_CARDS	4		/* The maximum number of cards to
+					   control or probe for. If you change
+					   this, you must also change the number
+					   of elements in io, irq, and ram to
+					   match. Initialized in init.c */
+/*
+extern unsigned int io[];
+extern unsigned char irq[];
+extern unsigned long ram[];
+*/
+
+#define SIGNATURE	0x87654321	/* Board reset signature */
+#define SIG_OFFSET	0x1004		/* Where to find signature in shared RAM */
+#define TRACE_OFFSET	0x1008		/* Trace enable word offset in shared RAM */
+#define BUFFER_OFFSET	0x1800		/* Beginning of buffers */
+
+/* I/O Port parameters */
+#define IOBASE_MIN	0x180		/* Lowest I/O port address */
+#define IOBASE_MAX	0x3C0		/* Highest I/O port address */
+#define IOBASE_OFFSET	0x20		/* Inter-board I/O port gap used during
+					   probing */
+#define FIFORD_OFFSET	0x0
+#define FIFOWR_OFFSET	0x400
+#define FIFOSTAT_OFFSET	0x1000
+#define RESET_OFFSET	0x2800
+#define PG0_OFFSET	0x3000		/* Offset from I/O Base for Page 0 register */
+#define PG1_OFFSET	0x3400		/* Offset from I/O Base for Page 1 register */
+#define PG2_OFFSET	0x3800		/* Offset from I/O Base for Page 2 register */
+#define PG3_OFFSET	0x3C00		/* Offset from I/O Base for Page 3 register */
+
+#define FIFO_READ	0		/* FIFO Read register */
+#define FIFO_WRITE	1		/* FIFO Write rgister */
+#define LO_ADDR_PTR	2		/* Extended RAM Low Addr Pointer */
+#define HI_ADDR_PTR	3		/* Extended RAM High Addr Pointer */
+#define NOT_USED_1	4
+#define FIFO_STATUS	5		/* FIFO Status Register */
+#define NOT_USED_2	6
+#define MEM_OFFSET	7
+#define SFT_RESET	10		/* Reset Register */
+#define EXP_BASE	11		/* Shared RAM Base address */
+#define EXP_PAGE0	12		/* Shared RAM Page0 register */
+#define EXP_PAGE1	13		/* Shared RAM Page1 register */
+#define EXP_PAGE2	14		/* Shared RAM Page2 register */
+#define EXP_PAGE3	15		/* Shared RAM Page3 register */
+#define IRQ_SELECT	16		/* IRQ selection register */
+#define MAX_IO_REGS	17		/* Total number of I/O ports */
+
+/* FIFO register values */
+#define RF_HAS_DATA	0x01		/* fifo has data */
+#define RF_QUART_FULL	0x02		/* fifo quarter full */
+#define RF_HALF_FULL	0x04		/* fifo half full */
+#define RF_NOT_FULL	0x08		/* fifo not full */
+#define WF_HAS_DATA	0x10		/* fifo has data */
+#define WF_QUART_FULL	0x20		/* fifo quarter full */
+#define WF_HALF_FULL	0x40		/* fifo half full */
+#define WF_NOT_FULL	0x80		/* fifo not full */
+
+/* Shared RAM parameters */
+#define SRAM_MIN	0xC0000         /* Lowest host shared RAM address */
+#define SRAM_MAX	0xEFFFF         /* Highest host shared RAM address */
+#define SRAM_PAGESIZE	0x4000		/* Size of one RAM page (16K) */
+
+/* Shared RAM buffer parameters */
+#define BUFFER_SIZE	0x800		/* The size of a buffer in bytes */
+#define BUFFER_BASE	BUFFER_OFFSET	/* Offset from start of shared RAM
+					   where buffer start */
+#define BUFFERS_MAX	16		/* Maximum number of send/receive
+					   buffers per channel */
+#define HDLC_PROTO	0x01		/* Frame Format for Layer 2 */
+
+#define BRI_BOARD	0
+#define POTS_BOARD	1
+#define PRI_BOARD	2
+
+/*
+ * Specific hardware parameters for the DataCommute/BRI
+ */
+#define BRI_CHANNELS	2		/* Number of B channels */
+#define BRI_BASEPG_VAL	0x98
+#define BRI_MAGIC	0x60000		/* Magic Number */
+#define BRI_MEMSIZE	0x10000		/* Ammount of RAM (64K) */
+#define BRI_PARTNO	"72-029"
+#define BRI_FEATURES	ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L3_TRANS;
+/*
+ * Specific hardware parameters for the DataCommute/PRI
+ */
+#define PRI_CHANNELS	23		/* Number of B channels */
+#define PRI_BASEPG_VAL	0x88
+#define PRI_MAGIC	0x20000		/* Magic Number */
+#define PRI_MEMSIZE	0x100000	/* Amount of RAM (1M) */
+#define PRI_PARTNO	"72-030"
+#define PRI_FEATURES	ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L3_TRANS;
+
+/*
+ * Some handy macros
+ */
+
+/* Return the number of jiffies in a given number of msecs */
+#define milliseconds(x)	(x/(1000/HZ))
+
+/* Determine if a channel number is valid for the adapter */
+#define IS_VALID_CHANNEL(y,x)	((x>0) && (x <= adapter[y]->channels))
+
+#endif
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/includes.h linux/drivers/isdn/sc/includes.h
--- v2.0.30/linux/drivers/isdn/sc/includes.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/includes.h	Mon Aug  4 17:34:01 1997
@@ -0,0 +1,15 @@
+#include <linux/module.h>
+#include <linux/version.h>
+#include <stdio.h>
+#include <linux/errno.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/isdnif.h>
+#include "debug.h"
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/init.c linux/drivers/isdn/sc/init.c
--- v2.0.30/linux/drivers/isdn/sc/init.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/init.c	Mon Aug  4 17:34:01 1997
@@ -0,0 +1,599 @@
+#include "includes.h"
+#include "hardware.h"
+#include "card.h"
+
+board *adapter[MAX_CARDS];
+int cinst;
+
+static char devname[] = "scX";
+const char version[] = "2.0b1";
+
+const char *boardname[] = { "DataCommute/BRI", "DataCommute/PRI", "TeleCommute/BRI" };
+
+/* insmod set parameters */
+unsigned int io[] = {0,0,0,0};
+unsigned char irq[] = {0,0,0,0};
+unsigned long ram[] = {0,0,0,0};
+int do_reset = 0;
+
+static int sup_irq[] = { 11, 10, 9, 5, 12, 14, 7, 3, 4, 6 };
+#define MAX_IRQS	10
+
+extern void interrupt_handler(int, void *, struct pt_regs *);
+extern int sndpkt(int, int, struct sk_buff *);
+extern int command(isdn_ctrl *);
+extern int indicate_status(int, int, ulong, char*);
+extern int reset(int);
+
+int identify_board(unsigned long, unsigned int);
+
+int irq_supported(int irq_x)
+{
+	int i;
+	for(i=0 ; i < MAX_IRQS ; i++) {
+		if(sup_irq[i] == irq_x)
+			return 1;
+	}
+	return 0;
+}
+
+#ifdef MODULE
+#if (LINUX_VERSION_CODE > 0x020111)
+MODULE_PARM(io, "1-4i");
+MODULE_PARM(irq, "1-4i");
+MODULE_PARM(ram, "1-4i");
+MODULE_PARM(do_reset, "i");
+#endif
+#define init_sc init_module
+#else
+/*
+Initialization code for non-module version to be included
+
+void sc_setup(char *str, int *ints)
+{
+}
+*/
+#endif
+
+int init_sc(void)
+{
+	int b = -1;
+	int i, j;
+	int status = -ENODEV;
+
+	unsigned long memsize = 0;
+	unsigned long features = 0;
+	isdn_if *interface;
+	unsigned char channels;
+	unsigned char pgport;
+	unsigned long magic;
+	int model;
+	int last_base = IOBASE_MIN;
+	int probe_exhasted = 0;
+
+#ifdef MODULE
+	pr_info("SpellCaster ISA ISDN Adapter Driver rev. %s Loaded\n", version);
+#else
+	pr_info("SpellCaster ISA ISDN Adapter Driver rev. %s\n", version);
+#endif
+	pr_info("Copyright (C) 1996 SpellCaster Telecommunications Inc.\n");
+
+	while(b++ < MAX_CARDS - 1) {
+		pr_debug("Probing for adapter #%d\n", b);
+		/*
+		 * Initialize reusable variables
+		 */
+		model = -1;
+		magic = 0;
+		channels = 0;
+		pgport = 0;
+
+		/* 
+		 * See if we should probe for IO base 
+		 */
+		pr_debug("I/O Base for board %d is 0x%x, %s probe\n", b, io[b],
+			io[b] == 0 ? "will" : "won't");
+		if(io[b]) {
+			/*
+			 * No, I/O Base has been provided
+			 */
+			for (i = 0 ; i < MAX_IO_REGS - 1 ; i++) {
+				if(check_region(io[b] + i * 0x400, 1)) {
+					pr_debug("check_region for 0x%x failed\n", io[b] + i * 0x400);
+					io[b] = 0;
+					break;
+				}
+			}
+
+			/*
+			 * Confirm the I/O Address with a test
+			 */
+			if(io[b] == 0) {
+				pr_debug("I/O Address 0x%x is in use.\n");
+				continue;
+			}
+
+			outb(0x18, io[b] + 0x400 * EXP_PAGE0);
+			if(inb(io[b] + 0x400 * EXP_PAGE0) != 0x18) {
+				pr_debug("I/O Base 0x%x fails test\n");
+				continue;
+			}
+		}
+		else {
+			/*
+			 * Yes, probe for I/O Base
+			 */
+			if(probe_exhasted) {
+				pr_debug("All probe addresses exhasted, skipping\n");
+				continue;
+			}
+			pr_debug("Probing for I/O...\n");
+			for (i = last_base ; i <= IOBASE_MAX ; i += IOBASE_OFFSET) {
+				int found_io = 1;
+				if (i == IOBASE_MAX) {
+					probe_exhasted = 1; /* No more addresses to probe */
+					pr_debug("End of Probes\n");
+				}
+				last_base = i + IOBASE_OFFSET;
+				pr_debug("  checking 0x%x...", i);
+				for ( j = 0 ; j < MAX_IO_REGS - 1 ; j++) {
+					if(check_region(i + j * 0x400, 1)) {
+						pr_debug("Failed\n");
+						found_io = 0;
+						break;
+					}
+				}	
+
+				if(found_io) {
+					io[b] = i;
+					outb(0x18, io[b] + 0x400 * EXP_PAGE0);
+					if(inb(io[b] + 0x400 * EXP_PAGE0) != 0x18) { 
+						pr_debug("Failed by test\n");
+						continue;
+					}
+					pr_debug("Passed\n");
+					break;
+				}
+			}
+			if(probe_exhasted) {
+				continue;
+			}
+		}
+
+		/*
+		 * See if we should probe for shared RAM
+		 */
+		if(do_reset) {
+			pr_debug("Doing a SAFE probe reset\n");
+			outb(0xFF, io[b] + RESET_OFFSET);
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + milliseconds(10000);
+			schedule();
+		}
+		pr_debug("RAM Base for board %d is 0x%x, %s probe\n", b, ram[b],
+			ram[b] == 0 ? "will" : "won't");
+
+		if(ram[b]) {
+			/*
+			 * No, the RAM base has been provided
+			 * Just look for a signature and ID the
+			 * board model
+			 */
+			if(!check_region(ram[b], SRAM_PAGESIZE)) {
+				pr_debug("check_region for RAM base 0x%x succeeded\n", ram[b]);
+			 	model = identify_board(ram[b], io[b]);
+			}
+		}
+		else {
+			/*
+			 * Yes, probe for free RAM and look for
+			 * a signature and id the board model
+			 */
+			for (i = SRAM_MIN ; i < SRAM_MAX ; i += SRAM_PAGESIZE) {
+				pr_debug("Checking RAM address 0x%x...\n", i);
+				if(!check_region(i, SRAM_PAGESIZE)) {
+					pr_debug("  check_region succeeded\n");
+					model = identify_board(i, io[b]);
+					if (model >= 0) {
+						pr_debug("  Identified a %s\n",
+							boardname[model]);
+						ram[b] = i;
+						break;
+					}
+					pr_debug("  Unidentifed or inaccessible\n");
+					continue;
+				}
+				pr_debug("  check_region failed\n");
+			}
+		}
+		/*
+		 * See if we found free RAM and the board model
+		 */
+		if(!ram[b] || model < 0) {
+			/*
+			 * Nope, there was no place in RAM for the
+			 * board, or it couldn't be identified
+			 */
+			 pr_debug("Failed to find an adapter at 0x%x\n", ram[b]);
+			 continue;
+		}
+
+		/*
+		 * Set the board's magic number, memory size and page register
+		 */
+		switch(model) {
+		case PRI_BOARD:
+			channels = 23;
+			magic = 0x20000;
+			memsize = 0x100000;
+			features = PRI_FEATURES;
+			break;
+
+		case BRI_BOARD:
+		case POTS_BOARD:
+			channels = 2;
+			magic = 0x60000;
+			memsize = 0x10000;
+			features = BRI_FEATURES;
+			break;
+		}
+		switch(ram[b] >> 12 & 0x0F) {
+		case 0x0:
+			pr_debug("RAM Page register set to EXP_PAGE0\n");
+			pgport = EXP_PAGE0;
+			break;
+
+		case 0x4:
+			pr_debug("RAM Page register set to EXP_PAGE1\n");
+			pgport = EXP_PAGE1;
+			break;
+
+		case 0x8:
+			pr_debug("RAM Page register set to EXP_PAGE2\n");
+			pgport = EXP_PAGE2;
+			break;
+
+		case 0xC:
+			pr_debug("RAM Page register set to EXP_PAGE3\n");
+			pgport = EXP_PAGE3;
+			break;
+
+		default:
+			pr_debug("RAM base address doesn't fall on 16K boundary\n");
+			continue;
+		}
+
+		pr_debug("current IRQ: %d  b: %d\n",irq[b],b);
+		/*
+		 * See if we should probe for an irq
+		 */
+		if(irq[b]) {
+			/*
+			 * No we were given one
+			 * See that it is supported and free
+			 */
+			pr_debug("Trying for IRQ: %d\n",irq[b]);
+			if (irq_supported(irq[b])) {
+				if(REQUEST_IRQ(irq[b], interrupt_handler, 
+					SA_PROBE, "sc_probe", NULL)) {
+					pr_debug("IRQ %d is already in use\n", 
+						irq[b]);
+					continue;
+				}
+				FREE_IRQ(irq[b], NULL);
+			}
+		}
+		else {
+			/*
+			 * Yes, we need to probe for an IRQ
+			 */
+			pr_debug("Probing for IRQ...\n");
+			for (i = 0; i < MAX_IRQS ; i++) {
+				if(!REQUEST_IRQ(sup_irq[i], interrupt_handler, SA_PROBE, "sc_probe", NULL)) {
+					pr_debug("Probed for and found IRQ %d\n", sup_irq[i]);
+					FREE_IRQ(sup_irq[i], NULL);
+					irq[b] = sup_irq[i];
+					break;
+				}
+			}
+		}
+
+		/*
+		 * Make sure we got an IRQ
+		 */
+		if(!irq[b]) {
+			/*
+			 * No interrupt could be used
+			 */
+			pr_debug("Failed to aquire an IRQ line\n");
+			continue;
+		}
+
+		/*
+		 * Horray! We found a board, Make sure we can register
+		 * it with ISDN4Linux
+		 */
+		interface = kmalloc(sizeof(isdn_if), GFP_KERNEL);
+		if (interface == NULL) {
+			/*
+			 * Oops, can't malloc isdn_if
+			 */
+			continue;
+		}
+		memset(interface, 0, sizeof(isdn_if));
+
+		interface->hl_hdrlen = 0;
+		interface->channels = channels;
+		interface->maxbufsize = BUFFER_SIZE;
+		interface->features = features;
+		interface->writebuf_skb = sndpkt;
+		interface->writecmd = NULL;
+		interface->command = command;
+		strcpy(interface->id, devname);
+		interface->id[2] = '0' + cinst;
+
+		/*
+		 * Allocate the board structure
+		 */
+		adapter[cinst] = kmalloc(sizeof(board), GFP_KERNEL);
+		if (adapter[cinst] == NULL) {
+			/*
+			 * Oops, can't alloc memory for the board
+			 */
+			kfree(interface);
+			continue;
+		}
+		memset(adapter[cinst], 0, sizeof(board));
+
+		if(!register_isdn(interface)) {
+			/*
+			 * Oops, couldn't register for some reason
+			 */
+			kfree(interface);
+			kfree(adapter[cinst]);
+			continue;
+		}
+
+		adapter[cinst]->card = interface;
+		adapter[cinst]->driverId = interface->channels;
+		strcpy(adapter[cinst]->devicename, interface->id);
+		adapter[cinst]->nChannels = channels;
+		adapter[cinst]->ramsize = memsize;
+		adapter[cinst]->shmem_magic = magic;
+		adapter[cinst]->shmem_pgport = pgport;
+		adapter[cinst]->StartOnReset = 1;
+
+		/*
+		 * Allocate channels status structures
+		 */
+		adapter[cinst]->channel = kmalloc(sizeof(bchan) * channels, GFP_KERNEL);
+		if (adapter[cinst]->channel == NULL) {
+			/*
+			 * Oops, can't alloc memory for the channels
+			 */
+			indicate_status(cinst, ISDN_STAT_UNLOAD, 0, NULL);	/* Fix me */
+			kfree(interface);
+			kfree(adapter[cinst]);
+			continue;
+		}
+		memset(adapter[cinst]->channel, 0, sizeof(bchan) * channels);
+
+		/*
+		 * Lock down the hardware resources
+		 */
+		adapter[cinst]->interrupt = irq[b];
+		REQUEST_IRQ(adapter[cinst]->interrupt, interrupt_handler, SA_INTERRUPT, 
+			interface->id, NULL);
+		adapter[cinst]->iobase = io[b];
+		for(i = 0 ; i < MAX_IO_REGS - 1 ; i++) {
+			adapter[cinst]->ioport[i] = io[b] + i * 0x400;
+			request_region(adapter[cinst]->ioport[i], 1, interface->id);
+			pr_debug("Requesting I/O Port %#x\n", adapter[cinst]->ioport[i]);
+		}
+		adapter[cinst]->ioport[IRQ_SELECT] = io[b] + 0x2;
+		request_region(adapter[cinst]->ioport[IRQ_SELECT], 1, interface->id);
+		pr_debug("Requesting I/O Port %#x\n", adapter[cinst]->ioport[IRQ_SELECT]);
+		adapter[cinst]->rambase = ram[b];
+		request_region(adapter[cinst]->rambase, SRAM_PAGESIZE, interface->id);
+
+		pr_info("  %s (%d) - %s %d channels IRQ %d, I/O Base 0x%x, RAM Base 0x%lx\n", 
+			adapter[cinst]->devicename, adapter[cinst]->driverId, 
+			boardname[model], channels, irq[b], io[b], ram[b]);
+		
+		/*
+		 * reset the adapter to put things in motion
+		 */
+		reset(cinst);
+
+		cinst++;
+		status = 0;
+	}
+	if (status) 
+		pr_info("Failed to find any adapters, driver unloaded\n");
+	return status;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	int i, j;
+
+	for(i = 0 ; i < cinst ; i++) {
+		pr_debug("Cleaning up after adapter %d\n", i);
+		/*
+		 * kill the timers
+		 */
+		del_timer(&(adapter[i]->reset_timer));
+		del_timer(&(adapter[i]->stat_timer));
+
+		/*
+		 * Tell I4L we're toast
+		 */
+		indicate_status(i, ISDN_STAT_STOP, 0, NULL);
+		indicate_status(i, ISDN_STAT_UNLOAD, 0, NULL);
+
+		/*
+		 * Release shared RAM
+		 */
+		release_region(adapter[i]->rambase, SRAM_PAGESIZE);
+
+		/*
+		 * Release the IRQ
+		 */
+		FREE_IRQ(adapter[i]->interrupt, NULL);
+
+		/*
+		 * Reset for a clean start
+		 */
+		outb(0xFF, adapter[i]->ioport[SFT_RESET]);
+
+		/*
+		 * Release the I/O Port regions
+		 */
+		for(j = 0 ; j < MAX_IO_REGS - 1; j++) {
+			release_region(adapter[i]->ioport[j], 1);
+			pr_debug("Releasing I/O Port %#x\n", adapter[i]->ioport[j]);
+		}
+		release_region(adapter[i]->ioport[IRQ_SELECT], 1);
+		pr_debug("Releasing I/O Port %#x\n", adapter[i]->ioport[IRQ_SELECT]);
+
+		/*
+		 * Release any memory we alloced
+		 */
+		kfree(adapter[i]->channel);
+		kfree(adapter[i]->card);
+		kfree(adapter[i]);
+	}
+	pr_info("SpellCaster ISA ISDN Adapter Driver Unloaded.\n");
+}
+#endif
+
+int identify_board(unsigned long rambase, unsigned int iobase) 
+{
+	unsigned int pgport;
+	unsigned long sig;
+	DualPortMemory *dpm;
+	RspMessage rcvmsg;
+	ReqMessage sndmsg;
+	HWConfig_pl hwci;
+	int x;
+
+	pr_debug("Attempting to identify adapter @ 0x%x io 0x%x\n",
+		rambase, iobase);
+
+	/*
+	 * Enable the base pointer
+	 */
+	outb(rambase >> 12, iobase + 0x2c00);
+
+	switch(rambase >> 12 & 0x0F) {
+	case 0x0:
+		pgport = iobase + PG0_OFFSET;
+		pr_debug("Page Register offset is 0x%x\n", PG0_OFFSET);
+		break;
+		
+	case 0x4:
+		pgport = iobase + PG1_OFFSET;
+		pr_debug("Page Register offset is 0x%x\n", PG1_OFFSET);
+		break;
+
+	case 0x8:
+		pgport = iobase + PG2_OFFSET;
+		pr_debug("Page Register offset is 0x%x\n", PG2_OFFSET);
+		break;
+
+	case 0xC:
+		pgport = iobase + PG3_OFFSET;
+		pr_debug("Page Register offset is 0x%x\n", PG3_OFFSET);
+		break;
+	default:
+		pr_debug("Invalid rambase 0x%lx\n", rambase);
+		return -1;
+	}
+
+	/*
+	 * Try to identify a PRI card
+	 */
+	outb(PRI_BASEPG_VAL, pgport);
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + HZ;
+	schedule();
+	sig = readl(rambase + SIG_OFFSET);
+	pr_debug("Looking for a signature, got 0x%x\n", sig);
+#if 0
+/*
+ * For Gary: 
+ * If it's a timing problem, it should be gone with the above schedule()
+ * Another possible reason may be the missing volatile in the original
+ * code. readl() does this for us.
+ */
+	printk("");	/* Hack! Doesn't work without this !!!??? */
+#endif
+	if(sig == SIGNATURE)
+		return PRI_BOARD;
+
+	/*
+	 * Try to identify a PRI card
+	 */
+	outb(BRI_BASEPG_VAL, pgport);
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + HZ;
+	schedule();
+	sig = readl(rambase + SIG_OFFSET);
+	pr_debug("Looking for a signature, got 0x%x\n", sig);
+#if 0
+	printk("");	/* Hack! Doesn't work without this !!!??? */
+#endif
+	if(sig == SIGNATURE)
+		return BRI_BOARD;
+
+	return -1;
+
+	/*
+	 * Try to spot a card
+	 */
+	sig = readl(rambase + SIG_OFFSET);
+	pr_debug("Looking for a signature, got 0x%x\n", sig);
+	if(sig != SIGNATURE)
+		return -1;
+
+	dpm = (DualPortMemory *) rambase;
+
+	memset(&sndmsg, 0, MSG_LEN);
+	sndmsg.msg_byte_cnt = 3;
+	sndmsg.type = cmReqType1;
+	sndmsg.class = cmReqClass0;
+	sndmsg.code = cmReqHWConfig;
+	memcpy_toio(&(dpm->req_queue[dpm->req_head++]), &sndmsg, MSG_LEN);
+	outb(0, iobase + 0x400);
+	pr_debug("Sent HWConfig message\n");
+	/*
+	 * Wait for the response
+	 */
+	x = 0;
+	while((inb(iobase + FIFOSTAT_OFFSET) & RF_HAS_DATA) && x < 100) {
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = jiffies + 1;
+		schedule();
+		x++;
+	}
+	if(x == 100) {
+		pr_debug("Timeout waiting for response\n");
+		return -1;
+	}
+
+	memcpy_fromio(&rcvmsg, &(dpm->rsp_queue[dpm->rsp_tail]), MSG_LEN);
+	pr_debug("Got HWConfig response, status = 0x%x\n", rcvmsg.rsp_status);
+	memcpy(&hwci, &(rcvmsg.msg_data.HWCresponse), sizeof(HWConfig_pl));
+	pr_debug("Hardware Config: Interface: %s, RAM Size: %d, Serial: %s\n"
+		 "                 Part: %s, Rev: %s\n",
+		 hwci.st_u_sense ? "S/T" : "U", hwci.ram_size,
+		 hwci.serial_no, hwci.part_no, hwci.rev_no);
+
+	if(!strncmp(PRI_PARTNO, hwci.part_no, 6))
+		return PRI_BOARD;
+	if(!strncmp(BRI_PARTNO, hwci.part_no, 6))
+		return BRI_BOARD;
+		
+	return -1;
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/interrupt.c linux/drivers/isdn/sc/interrupt.c
--- v2.0.30/linux/drivers/isdn/sc/interrupt.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/interrupt.c	Mon Aug  4 17:34:01 1997
@@ -0,0 +1,258 @@
+/*
+ *  $Id: interrupt.c,v 1.3 1997/02/11 22:53:43 fritz Exp $
+ *  Copyright (C) 1996  SpellCaster Telecommunications 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 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.
+ *
+ *  For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#define __NO_VERSION__
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+
+extern indicate_status(int, int, ulong, char *);
+extern void check_phystat(unsigned long);
+extern void dump_messages(int);
+extern int receivemessage(int, RspMessage *);
+extern int sendmessage(int, unsigned int, unsigned int, unsigned int,
+        unsigned int, unsigned int, unsigned int, unsigned int *);
+extern void rcvpkt(int, RspMessage *);
+
+extern int cinst;
+extern board *adapter[];
+
+int get_card_from_irq(int irq)
+{
+	int i;
+
+	for(i = 0 ; i < cinst ; i++) {
+		if(adapter[i]->interrupt == irq)
+			return i;
+	}
+	return -1;
+}
+
+/*
+ * 
+ */
+void interrupt_handler(int interrupt, void * cardptr, struct pt_regs *regs ) {
+
+	RspMessage rcvmsg;
+	int channel;
+	int card;
+
+	card = get_card_from_irq(interrupt);
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return;
+	}
+
+	pr_debug("%s: Entered Interrupt handler\n", adapter[card]->devicename);
+	
+ 	/*
+	 * Pull all of the waiting messages off the response queue
+	 */
+	while (!receivemessage(card, &rcvmsg)) {
+		/*
+		 * Push the message to the adapter structure for
+		 * send_and_receive to snoop
+		 */
+		if(adapter[card]->want_async_messages)
+			memcpy(&(adapter[card]->async_msg), &rcvmsg, sizeof(RspMessage));
+
+		channel = (unsigned int) rcvmsg.phy_link_no;
+		
+		/*
+		 * Trap Invalid request messages
+		 */
+		if(IS_CM_MESSAGE(rcvmsg, 0, 0, Invalid)) {
+			pr_debug("%s: Invalid request Message, rsp_status = %d\n", 
+				adapter[card]->devicename, rcvmsg.rsp_status);
+			break;	
+		}
+		
+		/*
+		 * Check for a linkRead message
+		 */
+		if (IS_CE_MESSAGE(rcvmsg, Lnk, 1, Read))
+		{
+			pr_debug("%s: Received packet 0x%x bytes long at 0x%x\n",
+						adapter[card]->devicename,
+						rcvmsg.msg_data.response.msg_len,
+						rcvmsg.msg_data.response.buff_offset);
+			rcvpkt(card, &rcvmsg);
+			continue;
+
+		}
+
+		/*
+		 * Handle a write acknoledgement
+		 */
+		if(IS_CE_MESSAGE(rcvmsg, Lnk, 1, Write)) {
+			pr_debug("%s: Packet Send ACK on channel %d\n", adapter[card]->devicename,
+				rcvmsg.phy_link_no);
+			adapter[card]->channel[rcvmsg.phy_link_no-1].free_sendbufs++;
+			continue;
+		}
+
+		/*
+		 * Handle a connection message
+		 */
+		if (IS_CE_MESSAGE(rcvmsg, Phy, 1, Connect)) 
+		{
+			unsigned int callid;
+			setup_parm setup;	
+			pr_debug("%s: Connect message: line %d: status %d: cause 0x%x\n",
+						adapter[card]->devicename,
+						rcvmsg.phy_link_no,
+						rcvmsg.rsp_status,
+						rcvmsg.msg_data.byte_array[2]);
+			
+			memcpy(&callid,rcvmsg.msg_data.byte_array,sizeof(int));
+			if(callid>=0x8000 && callid<=0xFFFF)
+			{		
+				pr_debug("%s: Got Dial-Out Rsp\n", adapter[card]->devicename);	
+				indicate_status(card, ISDN_STAT_DCONN,
+						(unsigned long)rcvmsg.phy_link_no-1,NULL);
+				
+			}
+			else if(callid>=0x0000 && callid<=0x7FFF)
+			{
+				pr_debug("%s: Got Incomming Call\n", adapter[card]->devicename);	
+				strcpy(setup.phone,&(rcvmsg.msg_data.byte_array[4]));
+				strcpy(setup.eazmsn,adapter[card]->channel[rcvmsg.phy_link_no-1].dn);
+				setup.si1 = 7;
+				setup.si2 = 0;
+				setup.plan = 0;
+				setup.screen = 0;
+
+				indicate_status(card, ISDN_STAT_ICALL,(unsigned long)rcvmsg.phy_link_no-1,(char *)&setup);
+				indicate_status(card, ISDN_STAT_DCONN,(unsigned long)rcvmsg.phy_link_no-1,NULL);
+			}
+			continue;
+		}
+
+		/*
+		 * Handle a disconnection message
+		 */
+		if (IS_CE_MESSAGE(rcvmsg, Phy, 1, Disconnect)) 
+		{
+			pr_debug("%s: disconnect message: line %d: status %d: cause 0x%x\n",
+						adapter[card]->devicename,
+						rcvmsg.phy_link_no,
+						rcvmsg.rsp_status,
+					 	rcvmsg.msg_data.byte_array[2]);
+
+			indicate_status(card, ISDN_STAT_BHUP,(unsigned long)rcvmsg.phy_link_no-1,NULL);
+			indicate_status(card, ISDN_STAT_DHUP,(unsigned long)rcvmsg.phy_link_no-1,NULL);
+			continue;
+
+		}
+
+		/*
+		 * Handle a startProc engine up message
+		 */
+		if (IS_CM_MESSAGE(rcvmsg, 5, 0, MiscEngineUp)) {
+			pr_debug("%s: Received EngineUp message\n", adapter[card]->devicename);
+			adapter[card]->EngineUp = 1;
+			sendmessage(card, CEPID,ceReqTypeCall,ceReqClass0,ceReqCallGetMyNumber,1,0,NULL);
+			sendmessage(card, CEPID,ceReqTypeCall,ceReqClass0,ceReqCallGetMyNumber,2,0,NULL);
+			init_timer(&adapter[card]->stat_timer);
+			adapter[card]->stat_timer.function = check_phystat;
+			adapter[card]->stat_timer.data = card;
+			adapter[card]->stat_timer.expires = jiffies + CHECKSTAT_TIME;
+			add_timer(&adapter[card]->stat_timer);
+			continue;
+		}
+
+		/*
+		 * Start proc response
+		 */
+		if (IS_CM_MESSAGE(rcvmsg, 2, 0, StartProc)) {
+			pr_debug("%s: StartProc Response Status %d\n", adapter[card]->devicename,
+				rcvmsg.rsp_status);
+			continue;
+		}
+
+		/*
+		 * Handle a GetMyNumber Rsp
+		 */
+		if (IS_CE_MESSAGE(rcvmsg,Call,0,GetMyNumber)){
+			strcpy(adapter[card]->channel[rcvmsg.phy_link_no-1].dn,rcvmsg.msg_data.byte_array);
+			continue;
+		}
+			
+		/*
+		 * PhyStatus response
+		 */
+		if(IS_CE_MESSAGE(rcvmsg, Phy, 2, Status)) {
+			unsigned int b1stat, b2stat;
+
+			/*
+			 * Covert the message data to the adapter->phystat code
+			 */
+			b1stat = (unsigned int) rcvmsg.msg_data.byte_array[0];
+			b2stat = (unsigned int) rcvmsg.msg_data.byte_array[1];
+
+			adapter[card]->nphystat = (b2stat >> 8) | b1stat; /* endian?? */
+			pr_debug("%s: PhyStat is 0x%2x\n", adapter[card]->devicename,
+				adapter[card]->nphystat);
+			continue;
+		}
+
+
+		/* 
+		 * Handle a GetFramFormat
+		 */
+		if(IS_CE_MESSAGE(rcvmsg, Call, 0, GetFrameFormat)) {
+			if(rcvmsg.msg_data.byte_array[0] != HDLC_PROTO) {
+				unsigned int proto = HDLC_PROTO;
+				/*
+				 * Set board format to HDLC if it wasn't already
+				 */
+				pr_debug("%s: current frame format: 0x%x, will change to HDLC\n",
+						adapter[card]->devicename,
+					rcvmsg.msg_data.byte_array[0]);
+				sendmessage(card, CEPID, ceReqTypeCall,
+						ceReqClass0,
+						ceReqCallSetFrameFormat,
+						(unsigned char) channel +1,
+						1,&proto);
+				}
+			continue;
+		}
+
+		/*
+		 * Hmm...
+		 */
+		pr_debug("%s: Received unhandled message (%d,%d,%d) link %d\n",
+			adapter[card]->devicename, rcvmsg.type, rcvmsg.class, rcvmsg.code, 
+			rcvmsg.phy_link_no);
+
+	}	/* while */
+
+	pr_debug("%s: Exiting Interrupt Handler\n", adapter[card]->devicename);
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/ioctl.c linux/drivers/isdn/sc/ioctl.c
--- v2.0.30/linux/drivers/isdn/sc/ioctl.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/ioctl.c	Mon Aug  4 17:34:01 1997
@@ -0,0 +1,513 @@
+#define __NO_VERSION__
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+#include "scioc.h"
+
+extern int indicate_status(int, int, unsigned long, char *);
+extern int startproc(int);
+extern int loadproc(int, char *record);
+extern int reset(int);
+extern int send_and_receive(int, unsigned int, unsigned char,unsigned char,
+		unsigned char,unsigned char, 
+		unsigned char, unsigned char *, RspMessage *, int);
+
+extern board *adapter[];
+
+#if 0
+static char *ChannelStates[] = { "Idle", "Ready", "Connecting", "Connected", "Disconnecting" };
+#endif
+
+int GetStatus(int card, boardInfo *);
+
+/*
+ * Process private IOCTL messages (typically from scctrl)
+ */
+int sc_ioctl(int card, scs_ioctl *data)
+{
+	switch(data->command) {
+	case SCIOCRESET:	/* Perform a hard reset of the adapter */
+	{
+		pr_debug("%s: SCIOCRESET: ioctl received\n", adapter[card]->devicename);
+		adapter[card]->StartOnReset = 0;
+		return (reset(card));
+	}
+
+	case SCIOCLOAD:
+	{
+		RspMessage	rcvmsg;
+		char		srec[SCIOC_SRECSIZE];
+		int		status, err;
+
+		pr_debug("%s: SCIOLOAD: ioctl received\n", adapter[card]->devicename);
+		if(adapter[card]->EngineUp) {
+			pr_debug("%s: SCIOCLOAD: Command Failed, LoadProc while engine running.\n",
+				adapter[card]->devicename);
+			return -1;
+		}
+
+		/*
+		 * Get the SRec from user space
+		 */
+		if ((err = copy_from_user(srec, (char *) data->dataptr, sizeof(srec))))
+			return err;
+
+		status = send_and_receive(card, CMPID, cmReqType2, cmReqClass0, cmReqLoadProc,
+				0, sizeof(srec), srec, &rcvmsg, SAR_TIMEOUT);
+		if(status) {
+			pr_debug("%s: SCIOCLOAD: Command Failed, status = %d\n", 
+				adapter[card]->devicename, status);
+			return -1;
+		}
+		else {
+			pr_debug("%s: SCIOCLOAD: Command Sucessful\n", adapter[card]->devicename);
+			return 0;
+		}
+	}
+
+	case SCIOCSTART:
+	{
+		pr_debug("%s: SCIOSTART: ioctl received\n", adapter[card]->devicename);
+		if(adapter[card]->EngineUp) {
+			pr_debug("%s: SCIOCSTART: Command Failed, Engine already running.\n",
+				adapter[card]->devicename);
+			return -1;
+		}
+
+		adapter[card]->StartOnReset = 1;
+		startproc(card);
+		return 0;
+	}
+
+	case SCIOCSETSWITCH:
+	{
+		RspMessage	rcvmsg;
+		char		switchtype;
+		int 		status, err;
+
+		pr_debug("%s: SCIOSETSWITCH: ioctl received\n", adapter[card]->devicename);
+
+		/*
+		 * Get the switch type from user space
+		 */
+		if ((err = copy_from_user(&switchtype, (char *) data->dataptr, sizeof(char))))
+			return err;
+
+		pr_debug("%s: SCIOCSETSWITCH: Setting switch type to %d\n", adapter[card]->devicename,
+			switchtype);
+		status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallSetSwitchType,
+						0, sizeof(char),&switchtype,&rcvmsg, SAR_TIMEOUT);
+		if(!status && !rcvmsg.rsp_status) {
+			pr_debug("%s: SCIOCSETSWITCH: Command Successful\n", adapter[card]->devicename);
+			return 0;
+		}
+		else {
+			pr_debug("%s: SCIOCSETSWITCH: Command Failed (status = %d)\n",
+				adapter[card]->devicename, status);
+			return status;
+		}
+	}
+		
+	case SCIOCGETSWITCH:
+	{
+		RspMessage 	rcvmsg;
+		char		switchtype;
+		int		status, err;
+
+		pr_debug("%s: SCIOGETSWITCH: ioctl received\n", adapter[card]->devicename);
+
+		/*
+		 * Get the switch type from the board
+		 */
+		status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, 
+			ceReqCallGetSwitchType, 0, 0, 0, &rcvmsg, SAR_TIMEOUT);
+		if (!status && !rcvmsg.rsp_status) {
+			pr_debug("%s: SCIOCGETSWITCH: Command Sucessful\n", adapter[card]->devicename);
+		}
+		else {
+			pr_debug("%s: SCIOCGETSWITCH: Command Failed (status = %d)\n",
+				adapter[card]->devicename, status);
+			return status;
+		}
+
+		switchtype = rcvmsg.msg_data.byte_array[0];
+
+		/*
+		 * Package the switch type and send to user space
+		 */
+		if ((err = copy_to_user((char *) data->dataptr, &switchtype, sizeof(char))))
+			return err;
+
+		return 0;
+	}
+
+	case SCIOCGETSPID:
+	{
+		RspMessage	rcvmsg;
+		char		spid[SCIOC_SPIDSIZE];
+		int		status, err;
+
+		pr_debug("%s: SCIOGETSPID: ioctl received\n", adapter[card]->devicename);
+
+		/*
+		 * Get the spid from the board
+		 */
+		status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetSPID,
+					data->channel, 0, 0, &rcvmsg, SAR_TIMEOUT);
+		if (!status) {
+			pr_debug("%s: SCIOCGETSPID: Command Sucessful\n", adapter[card]->devicename);
+		}
+		else {
+			pr_debug("%s: SCIOCGETSPID: Command Failed (status = %d)\n",
+				adapter[card]->devicename, status);
+			return status;
+		}
+		strcpy(spid, rcvmsg.msg_data.byte_array);
+
+		/*
+		 * Package the switch type and send to user space
+		 */
+		if ((err = copy_to_user((char *) data->dataptr, spid, sizeof(spid))))
+			return err;
+
+		return 0;
+	}	
+
+	case SCIOCSETSPID:
+	{
+		RspMessage	rcvmsg;
+		char		spid[SCIOC_SPIDSIZE];
+		int 		status, err;
+
+		pr_debug("%s: DCBIOSETSPID: ioctl received\n", adapter[card]->devicename);
+
+		/*
+		 * Get the spid from user space
+		 */
+		if ((err = copy_from_user(spid, (char *) data->dataptr, sizeof(spid))))
+			return err;
+
+		pr_debug("%s: SCIOCSETSPID: Setting channel %d spid to %s\n", 
+			adapter[card]->devicename, data->channel, spid);
+		status = send_and_receive(card, CEPID, ceReqTypeCall, 
+			ceReqClass0, ceReqCallSetSPID, data->channel, 
+			strlen(spid), spid, &rcvmsg, SAR_TIMEOUT);
+		if(!status && !rcvmsg.rsp_status) {
+			pr_debug("%s: SCIOCSETSPID: Command Successful\n", 
+				adapter[card]->devicename);
+			return 0;
+		}
+		else {
+			pr_debug("%s: SCIOCSETSPID: Command Failed (status = %d)\n",
+				adapter[card]->devicename, status);
+			return status;
+		}
+	}
+
+	case SCIOCGETDN:
+	{
+		RspMessage	rcvmsg;
+		char		dn[SCIOC_DNSIZE];
+		int		status, err;
+
+		pr_debug("%s: SCIOGETDN: ioctl received\n", adapter[card]->devicename);
+
+		/*
+		 * Get the dn from the board
+		 */
+		status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetMyNumber,
+					data->channel, 0, 0, &rcvmsg, SAR_TIMEOUT);
+		if (!status) {
+			pr_debug("%s: SCIOCGETDN: Command Sucessful\n", adapter[card]->devicename);
+		}
+		else {
+			pr_debug("%s: SCIOCGETDN: Command Failed (status = %d)\n",
+				adapter[card]->devicename, status);
+			return status;
+		}
+
+		strcpy(dn, rcvmsg.msg_data.byte_array);
+
+		/*
+		 * Package the dn and send to user space
+		 */
+		if ((err = copy_to_user((char *) data->dataptr, dn, sizeof(dn))))
+			return err;
+
+		return 0;
+	}	
+
+	case SCIOCSETDN:
+	{
+		RspMessage	rcvmsg;
+		char		dn[SCIOC_DNSIZE];
+		int 		status, err;
+
+		pr_debug("%s: SCIOSETDN: ioctl received\n", adapter[card]->devicename);
+
+		/*
+		 * Get the spid from user space
+		 */
+		if ((err = copy_from_user(dn, (char *) data->dataptr, sizeof(dn))))
+			return err;
+
+		pr_debug("%s: SCIOCSETDN: Setting channel %d dn to %s\n", 
+			adapter[card]->devicename, data->channel, dn);
+		status = send_and_receive(card, CEPID, ceReqTypeCall, 
+			ceReqClass0, ceReqCallSetMyNumber, data->channel, 
+			strlen(dn),dn,&rcvmsg, SAR_TIMEOUT);
+		if(!status && !rcvmsg.rsp_status) {
+			pr_debug("%s: SCIOCSETDN: Command Successful\n", 
+				adapter[card]->devicename);
+			return 0;
+		}
+		else {
+			pr_debug("%s: SCIOCSETDN: Command Failed (status = %d)\n",
+				adapter[card]->devicename, status);
+			return status;
+		}
+	}
+
+	case SCIOCTRACE:
+
+		pr_debug("%s: SCIOTRACE: ioctl received\n", adapter[card]->devicename);
+/*		adapter[card]->trace = !adapter[card]->trace; 
+		pr_debug("%s: SCIOCTRACE: Tracing turned %s\n", adapter[card]->devicename,
+			adapter[card]->trace ? "ON" : "OFF"); */
+		break;
+
+	case SCIOCSTAT:
+	{
+		boardInfo bi;
+		int err;
+
+		pr_debug("%s: SCIOSTAT: ioctl received\n", adapter[card]->devicename);
+		GetStatus(card, &bi);
+		
+		if ((err = copy_to_user((boardInfo *) data->dataptr, &bi, sizeof(boardInfo))))
+			return err;
+
+		return 0;
+	}
+
+	case SCIOCGETSPEED:
+	{
+		RspMessage	rcvmsg;
+		char		speed;
+		int		status, err;
+
+		pr_debug("%s: SCIOGETSPEED: ioctl received\n", adapter[card]->devicename);
+
+		/*
+		 * Get the speed from the board
+		 */
+		status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, 
+			ceReqCallGetCallType, data->channel, 0, 0, &rcvmsg, SAR_TIMEOUT);
+		if (!status && !rcvmsg.rsp_status) {
+			pr_debug("%s: SCIOCGETSPEED: Command Sucessful\n",
+				adapter[card]->devicename);
+		}
+		else {
+			pr_debug("%s: SCIOCGETSPEED: Command Failed (status = %d)\n",
+				adapter[card]->devicename, status);
+			return status;
+		}
+
+		speed = rcvmsg.msg_data.byte_array[0];
+
+		/*
+		 * Package the switch type and send to user space
+		 */
+		if ((err = copy_to_user((char *) data->dataptr, &speed, sizeof(char))))
+			return err;
+
+		return 0;
+	}
+
+	case SCIOCSETSPEED:
+		pr_debug("%s: SCIOCSETSPEED: ioctl received\n", adapter[card]->devicename);
+		break;
+
+	case SCIOCLOOPTST:
+		pr_debug("%s: SCIOCLOOPTST: ioctl received\n", adapter[card]->devicename);
+		break;
+
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+int GetStatus(int card, boardInfo *bi)
+{
+	RspMessage rcvmsg;
+	int i, status;
+
+	/*
+	 * Fill in some of the basic info about the board
+	 */
+	bi->modelid = adapter[card]->model;
+	strcpy(bi->serial_no, adapter[card]->hwconfig.serial_no);
+	strcpy(bi->part_no, adapter[card]->hwconfig.part_no);
+	bi->iobase = adapter[card]->iobase;
+	bi->rambase = adapter[card]->rambase;
+	bi->irq = adapter[card]->interrupt;
+	bi->ramsize = adapter[card]->hwconfig.ram_size;
+	bi->interface = adapter[card]->hwconfig.st_u_sense;
+	strcpy(bi->load_ver, adapter[card]->load_ver);
+	strcpy(bi->proc_ver, adapter[card]->proc_ver);
+
+	/*
+	 * Get the current PhyStats and LnkStats
+	 */
+	status = send_and_receive(card, CEPID, ceReqTypePhy, ceReqClass2,
+		ceReqPhyStatus, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+	if(!status) {
+		if(adapter[card]->model < PRI_BOARD) {
+			bi->l1_status = rcvmsg.msg_data.byte_array[2];
+			for(i = 0 ; i < BRI_CHANNELS ; i++)
+				bi->status.bristats[i].phy_stat =
+					rcvmsg.msg_data.byte_array[i];
+		}
+		else {
+			bi->l1_status = rcvmsg.msg_data.byte_array[0];
+			bi->l2_status = rcvmsg.msg_data.byte_array[1];
+			for(i = 0 ; i < PRI_CHANNELS ; i++)
+				bi->status.pristats[i].phy_stat = 
+					rcvmsg.msg_data.byte_array[i+2];
+		}
+	}
+	
+	/*
+	 * Get the call types for each channel
+	 */
+	for (i = 0 ; i < adapter[card]->nChannels ; i++) {
+		status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
+			ceReqCallGetCallType, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+		if(!status) {
+			if (adapter[card]->model == PRI_BOARD) {
+				bi->status.pristats[i].call_type = 
+					rcvmsg.msg_data.byte_array[0];
+			}
+			else {
+				bi->status.bristats[i].call_type =
+					rcvmsg.msg_data.byte_array[0];
+			}
+		}
+	}
+	
+	/*
+	 * If PRI, get the call states and service states for each channel
+	 */
+	if (adapter[card]->model == PRI_BOARD) {
+		/*
+		 * Get the call states
+		 */
+		status = send_and_receive(card, CEPID, ceReqTypeStat, ceReqClass2,
+			ceReqPhyChCallState, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+		if(!status) {
+			for( i = 0 ; i < PRI_CHANNELS ; i++ )
+				bi->status.pristats[i].call_state = 
+					rcvmsg.msg_data.byte_array[i];
+		}
+
+		/*
+		 * Get the service states
+		 */
+		status = send_and_receive(card, CEPID, ceReqTypeStat, ceReqClass2,
+			ceReqPhyChServState, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+		if(!status) {
+			for( i = 0 ; i < PRI_CHANNELS ; i++ )
+				bi->status.pristats[i].serv_state = 
+					rcvmsg.msg_data.byte_array[i];
+		}
+
+		/*
+		 * Get the link stats for the channels
+		 */
+		for (i = 1 ; i <= PRI_CHANNELS ; i++) {
+			status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0,
+				ceReqLnkGetStats, i, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+			if (!status) {
+				bi->status.pristats[i-1].link_stats.tx_good =
+					(unsigned long)rcvmsg.msg_data.byte_array[0];
+				bi->status.pristats[i-1].link_stats.tx_bad =
+					(unsigned long)rcvmsg.msg_data.byte_array[4];
+				bi->status.pristats[i-1].link_stats.rx_good =
+					(unsigned long)rcvmsg.msg_data.byte_array[8];
+				bi->status.pristats[i-1].link_stats.rx_bad =
+					(unsigned long)rcvmsg.msg_data.byte_array[12];
+			}
+		}
+
+		/*
+		 * Link stats for the D channel
+		 */
+		status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0,
+			ceReqLnkGetStats, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+		if (!status) {
+			bi->dch_stats.tx_good = (unsigned long)rcvmsg.msg_data.byte_array[0];
+			bi->dch_stats.tx_bad = (unsigned long)rcvmsg.msg_data.byte_array[4];
+			bi->dch_stats.rx_good = (unsigned long)rcvmsg.msg_data.byte_array[8];
+			bi->dch_stats.rx_bad = (unsigned long)rcvmsg.msg_data.byte_array[12];
+		}
+
+		return 0;
+	}
+
+	/*
+	 * If BRI or POTS, Get SPID, DN and call types for each channel
+	 */
+
+	/*
+	 * Get the link stats for the channels
+	 */
+	status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0,
+		ceReqLnkGetStats, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+	if (!status) {
+		bi->dch_stats.tx_good = (unsigned long)rcvmsg.msg_data.byte_array[0];
+		bi->dch_stats.tx_bad = (unsigned long)rcvmsg.msg_data.byte_array[4];
+		bi->dch_stats.rx_good = (unsigned long)rcvmsg.msg_data.byte_array[8];
+		bi->dch_stats.rx_bad = (unsigned long)rcvmsg.msg_data.byte_array[12];
+		bi->status.bristats[0].link_stats.tx_good = 
+			(unsigned long)rcvmsg.msg_data.byte_array[16];
+		bi->status.bristats[0].link_stats.tx_bad = 
+			(unsigned long)rcvmsg.msg_data.byte_array[20];
+		bi->status.bristats[0].link_stats.rx_good = 
+			(unsigned long)rcvmsg.msg_data.byte_array[24];
+		bi->status.bristats[0].link_stats.rx_bad = 
+			(unsigned long)rcvmsg.msg_data.byte_array[28];
+		bi->status.bristats[1].link_stats.tx_good = 
+			(unsigned long)rcvmsg.msg_data.byte_array[32];
+		bi->status.bristats[1].link_stats.tx_bad = 
+			(unsigned long)rcvmsg.msg_data.byte_array[36];
+		bi->status.bristats[1].link_stats.rx_good = 
+			(unsigned long)rcvmsg.msg_data.byte_array[40];
+		bi->status.bristats[1].link_stats.rx_bad = 
+			(unsigned long)rcvmsg.msg_data.byte_array[44];
+	}
+
+	/*
+	 * Get the SPIDs
+	 */
+	for (i = 0 ; i < BRI_CHANNELS ; i++) {
+		status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
+			ceReqCallGetSPID, i+1, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+		if (!status)
+			strcpy(bi->status.bristats[i].spid, rcvmsg.msg_data.byte_array);
+	}
+		
+	/*
+	 * Get the DNs
+	 */
+	for (i = 0 ; i < BRI_CHANNELS ; i++) {
+		status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
+			ceReqCallGetMyNumber, i+1, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+		if (!status)
+			strcpy(bi->status.bristats[i].dn, rcvmsg.msg_data.byte_array);
+	}
+		
+	return 0;
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/message.c linux/drivers/isdn/sc/message.c
--- v2.0.30/linux/drivers/isdn/sc/message.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/message.c	Mon Aug  4 17:34:01 1997
@@ -0,0 +1,300 @@
+/*
+ *  $Id: message.c,v 1.2 1996/11/20 17:49:54 fritz Exp $
+ *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ *  message.c - functions for sending and receiving control messages
+ *
+ *  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.
+ *
+ *  For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#define __NO_VERSION__
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+
+extern board *adapter[];
+extern unsigned int cinst;
+
+/*
+ * Obligitory function prototypes
+ */
+extern int indicate_status(int,ulong,char*);
+extern int scm_command(isdn_ctrl *);
+extern void *memcpy_fromshmem(int, void *, const void *, size_t);
+
+/*
+ * Dump message queue in shared memory to screen
+ */
+void dump_messages(int card) 
+{
+	DualPortMemory dpm;
+	unsigned long flags;
+
+	int i =0;
+	
+	if (!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+	}
+
+	save_flags(flags);
+	cli();
+	outb(adapter[card]->ioport[adapter[card]->shmem_pgport], 
+		(adapter[card]->shmem_magic >> 14) | 0x80);
+	memcpy_fromshmem(card, &dpm, 0, sizeof(dpm));
+	restore_flags(flags);
+
+	pr_debug("%s: Dumping Request Queue\n", adapter[card]->devicename);
+	for (i = 0; i < dpm.req_head; i++) {
+		pr_debug("%s: Message #%d: (%d,%d,%d), link: %d\n",
+				adapter[card]->devicename, i,
+				dpm.req_queue[i].type,
+				dpm.req_queue[i].class,
+				dpm.req_queue[i].code,
+				dpm.req_queue[i].phy_link_no);
+	}
+
+	pr_debug("%s: Dumping Response Queue\n", adapter[card]->devicename);
+	for (i = 0; i < dpm.rsp_head; i++) {
+		pr_debug("%s: Message #%d: (%d,%d,%d), link: %d, status: %d\n",
+				adapter[card]->devicename, i,
+				dpm.rsp_queue[i].type,
+				dpm.rsp_queue[i].class,
+				dpm.rsp_queue[i].code,
+				dpm.rsp_queue[i].phy_link_no,
+				dpm.rsp_queue[i].rsp_status);
+	}
+
+}	
+
+/*
+ * receive a message from the board
+ */
+int receivemessage(int card, RspMessage *rspmsg) 
+{
+	DualPortMemory *dpm;
+	unsigned long flags;
+
+	if (!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -EINVAL;
+	}
+	
+	pr_debug("%s: Entered receivemessage\n",adapter[card]->devicename);
+
+	/*
+	 * See if there are messages waiting
+	 */
+	if (inb(adapter[card]->ioport[FIFO_STATUS]) & RF_HAS_DATA) {
+		/*
+		 * Map in the DPM to the base page and copy the message
+		 */
+		save_flags(flags);
+		cli();
+		outb((adapter[card]->shmem_magic >> 14) | 0x80,
+			adapter[card]->ioport[adapter[card]->shmem_pgport]); 
+		dpm = (DualPortMemory *) adapter[card]->rambase;
+		memcpy_fromio(rspmsg, &(dpm->rsp_queue[dpm->rsp_tail]), 
+			MSG_LEN);
+		dpm->rsp_tail = (dpm->rsp_tail+1) % MAX_MESSAGES;
+		inb(adapter[card]->ioport[FIFO_READ]);
+		restore_flags(flags);
+		
+		/*
+		 * Tell the board that the message is received
+		 */
+		pr_debug("%s: Received Message seq:%d pid:%d time:%d cmd:%d "
+				"cnt:%d (type,class,code):(%d,%d,%d) "
+				"link:%d stat:0x%x\n",
+					adapter[card]->devicename,
+					rspmsg->sequence_no,
+					rspmsg->process_id,
+					rspmsg->time_stamp,
+					rspmsg->cmd_sequence_no,
+					rspmsg->msg_byte_cnt,
+					rspmsg->type,
+					rspmsg->class,
+					rspmsg->code,
+					rspmsg->phy_link_no, 
+					rspmsg->rsp_status);
+
+		return 0;
+	}
+	return -ENOMSG;
+}
+	
+/*
+ * send a message to the board
+ */
+int sendmessage(int card,
+		unsigned int procid,
+		unsigned int type, 
+		unsigned int class, 
+		unsigned int code,
+		unsigned int link, 
+		unsigned int data_len, 
+		unsigned int *data) 
+{
+	DualPortMemory *dpm;
+	ReqMessage sndmsg;
+	unsigned long flags;
+
+	if (!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -EINVAL;
+	}
+
+	/*
+	 * Make sure we only send CEPID messages when the engine is up
+	 * and CMPID messages when it is down
+	 */
+	if(adapter[card]->EngineUp && procid == CMPID) {
+		pr_debug("%s: Attempt to send CM message with engine up\n",
+			adapter[card]->devicename);
+		return -ESRCH;
+	}
+
+	if(!adapter[card]->EngineUp && procid == CEPID) {
+		pr_debug("%s: Attempt to send CE message with engine down\n",
+			adapter[card]->devicename);
+		return -ESRCH;
+	}
+
+	memset(&sndmsg, 0, MSG_LEN);
+	sndmsg.msg_byte_cnt = 4;
+	sndmsg.type = type;
+	sndmsg.class = class;
+	sndmsg.code = code;
+	sndmsg.phy_link_no = link;
+
+	if (data_len > 0) {
+		if (data_len > MSG_DATA_LEN)
+			data_len = MSG_DATA_LEN;
+		memcpy(&(sndmsg.msg_data), data, data_len);
+		sndmsg.msg_byte_cnt = data_len + 8;
+	}
+
+	sndmsg.process_id = procid;
+	sndmsg.sequence_no = adapter[card]->seq_no++ % 256;
+
+	/*
+	 * wait for an empty slot in the queue
+	 */
+	while (!(inb(adapter[card]->ioport[FIFO_STATUS]) & WF_NOT_FULL))
+		SLOW_DOWN_IO;
+
+	/*
+	 * Disable interrupts and map in shared memory
+	 */
+	save_flags(flags);
+	cli();
+	outb((adapter[card]->shmem_magic >> 14) | 0x80,
+		adapter[card]->ioport[adapter[card]->shmem_pgport]); 
+	dpm = (DualPortMemory *) adapter[card]->rambase;	/* Fix me */
+	memcpy_toio(&(dpm->req_queue[dpm->req_head]),&sndmsg,MSG_LEN);
+	dpm->req_head = (dpm->req_head+1) % MAX_MESSAGES;
+	outb(sndmsg.sequence_no, adapter[card]->ioport[FIFO_WRITE]);
+	restore_flags(flags);
+		
+	pr_debug("%s: Sent Message seq:%d pid:%d time:%d "
+			"cnt:%d (type,class,code):(%d,%d,%d) "
+			"link:%d\n ",
+				adapter[card]->devicename,
+				sndmsg.sequence_no,
+				sndmsg.process_id,
+				sndmsg.time_stamp,
+				sndmsg.msg_byte_cnt,
+				sndmsg.type,
+				sndmsg.class,
+				sndmsg.code,
+				sndmsg.phy_link_no); 
+		
+	return 0;
+}
+
+int send_and_receive(int card,
+		unsigned int procid, 
+		unsigned char type,
+		unsigned char class, 
+		unsigned char code,
+		unsigned char link,
+	 	unsigned char data_len, 
+		unsigned char *data, 
+		RspMessage *mesgdata,
+		int timeout) 
+{
+	int retval;
+	int tries;
+
+	if (!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -EINVAL;
+	}
+
+	adapter[card]->want_async_messages = 1;
+	retval = sendmessage(card, procid, type, class, code, link, 
+			data_len, (unsigned int *) data);
+  
+	if (retval) {
+		pr_debug("%s: SendMessage failed in SAR\n",
+			adapter[card]->devicename);
+		adapter[card]->want_async_messages = 0;
+		return -EIO;
+	}
+
+	tries = 0;
+	/* wait for the response */
+	while (tries < timeout) {
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = jiffies + 1; 
+		schedule();
+		
+		pr_debug("SAR waiting..\n");
+
+		/*
+		 * See if we got our message back
+		 */
+		if ((adapter[card]->async_msg.type == type) &&
+		    (adapter[card]->async_msg.class == class) &&
+		    (adapter[card]->async_msg.code == code) &&
+		    (adapter[card]->async_msg.phy_link_no == link)) {
+
+			/*
+			 * Got it!
+			 */
+			pr_debug("%s: Got ASYNC message\n",
+				adapter[card]->devicename);
+			memcpy(mesgdata, &(adapter[card]->async_msg), 
+				sizeof(RspMessage));
+			adapter[card]->want_async_messages = 0;
+			return 0;
+		}
+
+   		tries++;
+	}
+
+	pr_debug("%s: SAR message timeout\n", adapter[card]->devicename);
+	adapter[card]->want_async_messages = 0;
+	return -ETIME;
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/message.h linux/drivers/isdn/sc/message.h
--- v2.0.30/linux/drivers/isdn/sc/message.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/message.h	Mon Aug  4 17:34:01 1997
@@ -0,0 +1,256 @@
+/*
+ *  $Id: message.h,v 1.1 1996/11/07 13:07:47 fritz Exp $
+ *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ *  message.h - structures, macros and defines useful for sending
+ *              messages to the adapter
+ *
+ *  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.
+ *
+ *  For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+/*
+ * Board message macros, defines and structures
+ */
+ 
+#ifndef MESSAGE_H
+#define MESSAGE_H
+
+#define MAX_MESSAGES		32	/* Maximum messages that can be
+					   queued */
+#define MSG_DATA_LEN		48	/* Maximum size of message payload */
+#define MSG_LEN			64	/* Size of a message */
+#define CMPID			0	/* Loader message process ID */
+#define CEPID			64	/* Firmware message process ID */
+
+/*
+ * Macro to determine if a message is a loader message
+ */
+#define IS_CM_MESSAGE(mesg, tx, cx, dx)		\
+		((mesg.type == cmRspType##tx)		\
+		&&(mesg.class == cmRspClass##cx)	\
+		&&(mesg.code == cmRsp##dx))
+
+/*
+ * Macro to determine if a message is a firmware message
+ */
+#define IS_CE_MESSAGE(mesg, tx, cx, dx)		\
+		((mesg.type == ceRspType##tx)		\
+		&&(mesg.class == ceRspClass##cx)	\
+		&&(mesg.code == ceRsp##tx##dx))
+
+/* 
+ * Loader Request and Response Messages
+ */
+
+/* message types */
+#define cmReqType1			1
+#define cmReqType2			2
+#define cmRspType0			0
+#define cmRspType1			1
+#define cmRspType2			2
+#define cmRspType5			5
+
+/* message classes */
+#define cmReqClass0			0
+#define cmRspClass0			0
+
+/* message codes */
+#define cmReqHWConfig		1			/* 1,0,1 */
+#define cmReqMsgLpbk		2			/* 1,0,2 */
+#define cmReqVersion		3			/* 1,0,3 */
+#define cmReqLoadProc		1			/* 2,0,1 */
+#define cmReqStartProc		2			/* 2,0,2 */
+#define cmReqReadMem		6			/* 2,0,6 */
+#define cmRspHWConfig		cmReqHWConfig
+#define	cmRspMsgLpbk		cmReqMsgLpbk
+#define cmRspVersion		cmReqVersion
+#define cmRspLoadProc		cmReqLoadProc
+#define cmRspStartProc		cmReqStartProc
+#define	cmRspReadMem		cmReqReadMem
+#define cmRspMiscEngineUp	1			/* 5,0,1 */
+#define cmRspInvalid		0			/* 0,0,0 */
+
+
+/*
+ * Firmware Request and Response Messages
+ */
+
+/* message types */
+#define ceReqTypePhy		1
+#define ceReqTypeLnk		2
+#define ceReqTypeCall		3
+#define ceReqTypeStat		1
+#define ceRspTypeErr		0
+#define	ceRspTypePhy		ceReqTypePhy
+#define ceRspTypeLnk		ceReqTypeLnk
+#define ceRspTypeCall		ceReqTypeCall
+#define ceRspTypeStat		ceReqTypeStat
+
+/* message classes */
+#define ceReqClass0		0
+#define ceReqClass1		1
+#define ceReqClass2		2
+#define ceReqClass3		3
+#define ceRspClass0		ceReqClass0
+#define ceRspClass1		ceReqClass1
+#define ceRspClass2		ceReqClass2
+#define ceRspClass3		ceReqClass3
+
+/* message codes  (B) = BRI only, (P) = PRI only, (V) = POTS only */
+#define ceReqPhyProcInfo	1			/* 1,0,1 */
+#define ceReqPhyConnect		1			/* 1,1,1 */
+#define ceReqPhyDisconnect	2			/* 1,1,2 */
+#define ceReqPhySetParams	3			/* 1,1,3 (P) */
+#define ceReqPhyGetParams	4			/* 1,1,4 (P) */
+#define ceReqPhyStatus		1			/* 1,2,1 */
+#define ceReqPhyAcfaStatus	2			/* 1,2,2 (P) */
+#define ceReqPhyChCallState	3			/* 1,2,3 (P) */
+#define ceReqPhyChServState	4			/* 1,2,4 (P) */
+#define ceReqPhyRLoopBack	1			/* 1,3,1 */
+#define ceRspPhyProcInfo	ceReqPhyProcInfo
+#define	ceRspPhyConnect		ceReqPhyConnect
+#define ceRspPhyDisconnect	ceReqPhyDisconnect
+#define ceRspPhySetParams	ceReqPhySetParams
+#define ceRspPhyGetParams	ceReqPhyGetParams
+#define ceRspPhyStatus		ceReqPhyStatus
+#define ceRspPhyAcfaStatus	ceReqPhyAcfaStatus
+#define ceRspPhyChCallState	ceReqPhyChCallState
+#define ceRspPhyChServState	ceReqPhyChServState
+#define ceRspPhyRLoopBack	ceReqphyRLoopBack
+#define ceReqLnkSetParam	1			/* 2,0,1 */
+#define ceReqLnkGetParam	2			/* 2,0,2 */
+#define ceReqLnkGetStats	3			/* 2,0,3 */
+#define ceReqLnkWrite		1			/* 2,1,1 */
+#define ceReqLnkRead		2			/* 2,1,2 */
+#define ceReqLnkFlush		3			/* 2,1,3 */
+#define ceReqLnkWrBufTrc	4			/* 2,1,4 */
+#define ceReqLnkRdBufTrc	5			/* 2,1,5 */
+#define ceRspLnkSetParam	ceReqLnkSetParam
+#define ceRspLnkGetParam	ceReqLnkGetParam
+#define ceRspLnkGetStats	ceReqLnkGetStats
+#define ceRspLnkWrite		ceReqLnkWrite
+#define ceRspLnkRead		ceReqLnkRead
+#define ceRspLnkFlush		ceReqLnkFlush
+#define ceRspLnkWrBufTrc	ceReqLnkWrBufTrc
+#define ceRspLnkRdBufTrc	ceReqLnkRdBufTrc
+#define ceReqCallSetSwitchType	1			/* 3,0,1 */
+#define ceReqCallGetSwitchType	2			/* 3,0,2 */
+#define ceReqCallSetFrameFormat	3			/* 3,0,3 */
+#define ceReqCallGetFrameFormat	4			/* 3,0,4 */
+#define ceReqCallSetCallType	5			/* 3,0,5 */
+#define ceReqCallGetCallType	6			/* 3,0,6 */
+#define ceReqCallSetSPID	7			/* 3,0,7 (!P) */
+#define ceReqCallGetSPID	8			/* 3,0,8 (!P) */
+#define ceReqCallSetMyNumber	9			/* 3,0,9 (!P) */
+#define ceReqCallGetMyNumber	10			/* 3,0,10 (!P) */
+#define	ceRspCallSetSwitchType	ceReqCallSetSwitchType
+#define ceRspCallGetSwitchType	ceReqCallSetSwitchType
+#define ceRspCallSetFrameFormat	ceReqCallSetFrameFormat
+#define ceRspCallGetFrameFormat	ceReqCallGetFrameFormat
+#define ceRspCallSetCallType	ceReqCallSetCallType
+#define ceRspCallGetCallType	ceReqCallGetCallType
+#define ceRspCallSetSPID	ceReqCallSetSPID
+#define ceRspCallGetSPID	ceReqCallGetSPID
+#define ceRspCallSetMyNumber	ceReqCallSetMyNumber
+#define ceRspCallGetMyNumber	ceReqCallGetMyNumber
+#define ceRspStatAcfaStatus	2
+#define ceRspStat
+#define ceRspErrError		0			/* 0,0,0 */
+
+/*
+ * Call Types
+ */
+#define CALLTYPE_64K		0
+#define CALLTYPE_56K		1
+#define CALLTYPE_SPEECH		2
+#define CALLTYPE_31KHZ		3
+
+/*
+ * Link Level data contains a pointer to and the length of
+ * a buffer in shared RAM. Used by LnkRead and LnkWrite message
+ * types. Part of RspMsgStruct and ReqMsgStruct.
+ */
+typedef struct {
+	unsigned long buff_offset;
+	unsigned short msg_len;
+} LLData;
+
+
+/* 
+ * Message payload template for an HWConfig message
+ */
+typedef struct {
+	char st_u_sense;
+	char powr_sense;
+	char sply_sense;
+	unsigned char asic_id;
+	long ram_size;
+	char serial_no[13];
+	char part_no[13];
+	char rev_no[2];
+} HWConfig_pl;
+
+/*
+ * A Message
+ */
+struct message {
+	unsigned char sequence_no;
+	unsigned char process_id;
+	unsigned char time_stamp;
+	unsigned char cmd_sequence_no;	/* Rsp messages only */
+	unsigned char reserved1[3];
+	unsigned char msg_byte_cnt;
+	unsigned char type;
+	unsigned char class;
+	unsigned char code;
+	unsigned char phy_link_no;
+	unsigned char rsp_status;	/* Rsp messages only */
+	unsigned char reseved2[3];
+	union {
+		unsigned char byte_array[MSG_DATA_LEN];
+		LLData response;
+		HWConfig_pl HWCresponse;
+	} msg_data;
+};
+
+typedef struct message ReqMessage;	/* Request message */
+typedef struct message RspMessage;	/* Response message */
+
+/*
+ * The first 5010 bytes of shared memory contain the message queues,
+ * indexes and other data. This structure is its template
+ */
+typedef struct {
+	volatile ReqMessage req_queue[MAX_MESSAGES];
+	volatile RspMessage rsp_queue[MAX_MESSAGES];
+	volatile unsigned char req_head;
+	volatile unsigned char req_tail;
+	volatile unsigned char rsp_head;
+	volatile unsigned char rsp_tail;
+	volatile unsigned long signature;
+	volatile unsigned long trace_enable;
+	volatile unsigned char reserved[4];
+} DualPortMemory;
+
+#endif
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/packet.c linux/drivers/isdn/sc/packet.c
--- v2.0.30/linux/drivers/isdn/sc/packet.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/packet.c	Mon Aug  4 17:34:01 1997
@@ -0,0 +1,228 @@
+/*
+ *  $Id: packet.c,v 1.2 1996/11/20 17:49:55 fritz Exp $
+ *  Copyright (C) 1996  SpellCaster Telecommunications 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 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.
+ *
+ *  For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#define __NO_VERSION__
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+
+extern board *adapter[];
+extern unsigned int cinst;
+
+extern int get_card_from_id(int);
+extern int indicate_status(int, int,ulong,char*);
+extern void *memcpy_toshmem(int, void *, const void *, size_t);
+extern void *memcpy_fromshmem(int, void *, const void *, size_t);
+extern int sendmessage(int, unsigned int, unsigned int, unsigned int,
+                unsigned int, unsigned int, unsigned int, unsigned int *);
+
+int sndpkt(int devId, int channel, struct sk_buff *data)
+{
+	LLData	ReqLnkWrite;
+	int status;
+	int card;
+
+	card = get_card_from_id(devId);
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	pr_debug("%s: Send Packet: frst = 0x%x nxt = %d  f = %d n = %d\n",
+		adapter[card]->devicename,
+		adapter[card]->channel[channel].first_sendbuf,
+		adapter[card]->channel[channel].next_sendbuf,
+		adapter[card]->channel[channel].free_sendbufs,
+		adapter[card]->channel[channel].num_sendbufs);
+
+	if(!adapter[card]->channel[channel].free_sendbufs) {
+		pr_debug("%s: Out out TX buffers\n", adapter[card]->devicename);
+		return -EINVAL;
+	}
+
+	if(data->len > BUFFER_SIZE) {
+		pr_debug("%s: Data overflows buffer size (data > buffer)\n", adapter[card]->devicename);
+		return -EINVAL;
+	}
+
+	ReqLnkWrite.buff_offset = adapter[card]->channel[channel].next_sendbuf *
+		BUFFER_SIZE + adapter[card]->channel[channel].first_sendbuf;
+	ReqLnkWrite.msg_len = data->len; /* sk_buff size */
+	pr_debug("%s: Writing %d bytes to buffer offset 0x%x\n", adapter[card]->devicename,
+			ReqLnkWrite.msg_len, ReqLnkWrite.buff_offset);
+	memcpy_toshmem(card, (char *)ReqLnkWrite.buff_offset, data->data, ReqLnkWrite.msg_len);
+
+	/*
+	 * sendmessage
+	 */
+	pr_debug("%s: Send Packet size=%d, buf_offset=0x%x buf_indx=%d\n",
+		adapter[card]->devicename,
+		ReqLnkWrite.msg_len, ReqLnkWrite.buff_offset,
+		adapter[card]->channel[channel].next_sendbuf);
+
+	status = sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkWrite,
+				channel+1, sizeof(LLData), (unsigned int*)&ReqLnkWrite);
+	if(status) {
+		pr_debug("%s: Failed to send packet, status = %d\n", adapter[card]->devicename, status);
+		return -1;
+	}
+	else {
+		adapter[card]->channel[channel].free_sendbufs--;
+		adapter[card]->channel[channel].next_sendbuf =
+			++adapter[card]->channel[channel].next_sendbuf ==
+			adapter[card]->channel[channel].num_sendbufs ? 0 :
+			adapter[card]->channel[channel].next_sendbuf;
+			pr_debug("%s: Packet sent successfully\n", adapter[card]->devicename);
+		dev_kfree_skb(data, FREE_WRITE);
+		indicate_status(card,ISDN_STAT_BSENT,channel,NULL);
+	}
+	return data->len;
+}
+
+void rcvpkt(int card, RspMessage *rcvmsg)
+{
+	LLData newll;
+	struct sk_buff *skb;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return;
+	}
+
+	switch(rcvmsg->rsp_status){
+	case 0x01:
+	case 0x02:
+	case 0x70:
+		pr_debug("%s: Error status code: 0x%x\n", adapter[card]->devicename, rcvmsg->rsp_status);
+		return;
+	case 0x00: 
+	    if (!(skb = dev_alloc_skb(rcvmsg->msg_data.response.msg_len))) {
+			printk(KERN_WARNING "%s: rcvpkt out of memory, dropping packet\n",
+				adapter[card]->devicename);
+			return;
+		}
+		skb_put(skb, rcvmsg->msg_data.response.msg_len);
+		pr_debug("%s: getting data from offset: 0x%x\n",
+			adapter[card]->devicename,rcvmsg->msg_data.response.buff_offset);
+		memcpy_fromshmem(card,
+			skb_put(skb, rcvmsg->msg_data.response.msg_len),
+		 	(char *)rcvmsg->msg_data.response.buff_offset,
+			rcvmsg->msg_data.response.msg_len);
+		adapter[card]->card->rcvcallb_skb(adapter[card]->driverId,
+			rcvmsg->phy_link_no-1, skb);
+
+	case 0x03:
+		/*
+	 	 * Recycle the buffer
+	 	 */
+		pr_debug("%s: Buffer size : %d\n", adapter[card]->devicename, BUFFER_SIZE);
+/*		memset_shmem(card, rcvmsg->msg_data.response.buff_offset, 0, BUFFER_SIZE); */
+		newll.buff_offset = rcvmsg->msg_data.response.buff_offset;
+		newll.msg_len = BUFFER_SIZE;
+		pr_debug("%s: recycled buffer at offset 0x%x size %d\n", adapter[card]->devicename,
+			newll.buff_offset, newll.msg_len);
+		sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkRead,
+			rcvmsg->phy_link_no, sizeof(LLData), (unsigned int *)&newll);
+	}
+
+}
+
+int setup_buffers(int card, int c)
+{
+	unsigned int nBuffers, i, cBase;
+	unsigned int buffer_size;
+	LLData	RcvBuffOffset;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	/*
+	 * Calculate the buffer offsets (send/recv/send/recv)
+	 */
+	pr_debug("%s: Seting up channel buffer space in shared RAM\n", adapter[card]->devicename);
+	buffer_size = BUFFER_SIZE;
+	nBuffers = ((adapter[card]->ramsize - BUFFER_BASE) / buffer_size) / 2;
+	nBuffers = nBuffers > BUFFERS_MAX ? BUFFERS_MAX : nBuffers;
+	pr_debug("%s: Calculating buffer space: %d buffers, %d big\n", adapter[card]->devicename,
+		nBuffers, buffer_size);
+	if(nBuffers < 2) {
+		pr_debug("%s: Not enough buffer space\n", adapter[card]->devicename);
+		return -1;
+	}
+	cBase = (nBuffers * buffer_size) * (c - 1);
+	pr_debug("%s: Channel buffer offset from Shared RAM: 0x%x\n", adapter[card]->devicename, cBase);
+	adapter[card]->channel[c-1].first_sendbuf = BUFFER_BASE + cBase;
+	adapter[card]->channel[c-1].num_sendbufs = nBuffers / 2;
+	adapter[card]->channel[c-1].free_sendbufs = nBuffers / 2;
+	adapter[card]->channel[c-1].next_sendbuf = 0;
+	pr_debug("%s: Send buffer setup complete: first=0x%x n=%d f=%d, nxt=%d\n",
+				adapter[card]->devicename,
+				adapter[card]->channel[c-1].first_sendbuf,
+				adapter[card]->channel[c-1].num_sendbufs,
+				adapter[card]->channel[c-1].free_sendbufs,
+				adapter[card]->channel[c-1].next_sendbuf);
+
+	/*
+	 * Prep the receive buffers
+	 */
+	pr_debug("%s: Adding %d RecvBuffers:\n", adapter[card]->devicename, nBuffers /2);
+	for (i = 0 ; i < nBuffers / 2; i++) {
+		RcvBuffOffset.buff_offset = 
+			((adapter[card]->channel[c-1].first_sendbuf +
+			(nBuffers / 2) * buffer_size) + (buffer_size * i));
+		RcvBuffOffset.msg_len = buffer_size;
+		pr_debug("%s: Adding RcvBuffer #%d offset=0x%x sz=%d buffsz:%d\n",
+				adapter[card]->devicename,
+				i + 1, RcvBuffOffset.buff_offset, 
+				RcvBuffOffset.msg_len,buffer_size);
+		sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkRead,
+				c, sizeof(LLData), (unsigned int *)&RcvBuffOffset);
+	} 
+	return 0;
+}
+
+int print_skb(int card,char *skb_p, int len){
+	int i,data;
+	pr_debug("%s: data at 0x%x len: 0x%x\n",adapter[card]->devicename,
+			skb_p,len);
+	for(i=1;i<=len;i++,skb_p++){
+		data = (int) (0xff & (*skb_p));
+		pr_debug("%s: data =  0x%x",adapter[card]->devicename,data);
+		if(!(i%4))
+			pr_debug(" ");
+		if(!(i%32))
+			pr_debug("\n");
+	}
+	pr_debug("\n");
+	return 0;
+}		
+
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/scioc.h linux/drivers/isdn/sc/scioc.h
--- v2.0.30/linux/drivers/isdn/sc/scioc.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/scioc.h	Mon Aug  4 17:34:01 1997
@@ -0,0 +1,101 @@
+
+/*
+ * IOCTL Command Codes
+ */
+#define SCIOCLOAD	0x01	/* Load a firmware record */
+#define SCIOCRESET	0x02	/* Perform hard reset */
+#define SCIOCDEBUG	0x03	/* Set debug level */
+#define SCIOCREV	0x04	/* Get driver revision(s) */
+#define SCIOCSTART	0x05	/* Start the firmware */
+#define SCIOCGETSWITCH	0x06	/* Get switch type */
+#define SCIOCSETSWITCH	0x07	/* Set switch type */
+#define SCIOCGETSPID	0x08	/* Get channel SPID */
+#define SCIOCSETSPID	0x09 	/* Set channel SPID */
+#define SCIOCGETDN	0x0A	/* Get channel DN */
+#define SCIOCSETDN	0x0B 	/* Set channel DN */
+#define SCIOCTRACE	0x0C	/* Toggle trace mode */
+#define SCIOCSTAT	0x0D	/* Get line status */
+#define SCIOCGETSPEED	0x0E	/* Set channel speed */
+#define SCIOCSETSPEED	0x0F	/* Set channel speed */
+#define SCIOCLOOPTST	0x10	/* Perform loopback test */
+
+typedef struct {
+	int device;
+	int channel;
+	unsigned long command;
+	void *dataptr;
+} scs_ioctl;
+
+/* Size of strings */
+#define SCIOC_SPIDSIZE		49
+#define SCIOC_DNSIZE		SCIOC_SPIDSIZE
+#define SCIOC_REVSIZE		SCIOC_SPIDSIZE
+#define SCIOC_SRECSIZE		49
+
+typedef struct {
+	unsigned long tx_good;
+	unsigned long tx_bad;
+	unsigned long rx_good;
+	unsigned long rx_bad;
+} ChLinkStats;
+
+typedef struct {
+	char spid[49];
+	char dn[49];
+	char call_type;
+	char phy_stat;
+	ChLinkStats link_stats;
+} BRIStat;
+
+typedef BRIStat POTStat;
+
+typedef struct {
+	char call_type;
+	char call_state;
+	char serv_state;
+	char phy_stat;
+	ChLinkStats link_stats;
+} PRIStat;
+
+typedef char PRIInfo;
+typedef char BRIInfo;
+typedef char POTInfo;
+
+
+typedef struct {
+	char acfa_nos;
+	char acfa_ais;
+	char acfa_los;
+	char acfa_rra;
+	char acfa_slpp;
+	char acfa_slpn;
+	char acfa_fsrf;
+} ACFAStat;
+
+typedef struct {
+	unsigned char modelid;
+	char serial_no[13];
+	char part_no[13];
+	char load_ver[11];
+	char proc_ver[11];
+	int iobase;
+	long rambase;
+	char irq;
+	long ramsize;
+	char interface;
+	char switch_type;
+	char l1_status;
+	char l2_status;
+	ChLinkStats dch_stats;
+	ACFAStat AcfaStats;
+	union {
+		PRIStat pristats[23];
+		BRIStat bristats[2];
+		POTStat potsstats[2];
+	} status;
+	union {
+		PRIInfo priinfo;
+		BRIInfo briinfo;
+		POTInfo potsinfo;
+	} info;
+} boardInfo;
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/shmem.c linux/drivers/isdn/sc/shmem.c
--- v2.0.30/linux/drivers/isdn/sc/shmem.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/shmem.c	Mon Aug  4 17:34:01 1997
@@ -0,0 +1,165 @@
+/*
+ *  $Id: shmem.c,v 1.2 1996/11/20 17:49:56 fritz Exp $
+ *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ *  card.c - Card functions implementing ISDN4Linux functionality
+ *
+ *  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.
+ *
+ *  For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#define __NO_VERSION__
+#include "includes.h"		/* This must be first */
+#include "hardware.h"
+#include "card.h"
+
+/*
+ * Main adapter array
+ */
+extern board *adapter[];
+extern int cinst;
+
+/*
+ *
+ */
+void *memcpy_toshmem(int card, void *dest, const void *src, size_t n)
+{
+	unsigned long flags;
+	void *ret;
+	unsigned char ch;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return NULL;
+	}
+
+	if(n > SRAM_PAGESIZE) {
+		return NULL;
+	}
+
+	/*
+	 * determine the page to load from the address
+	 */
+	ch = (unsigned long) dest / SRAM_PAGESIZE;
+	pr_debug("%s: loaded page %d\n",adapter[card]->devicename,ch);
+	/*
+	 * Block interrupts and load the page
+	 */
+	save_flags(flags);
+	cli();
+
+	outb(((adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80,
+		adapter[card]->ioport[adapter[card]->shmem_pgport]);
+	pr_debug("%s: set page to %#x\n",adapter[card]->devicename,
+		((adapter[card]->shmem_magic + ch * SRAM_PAGESIZE)>>14)|0x80);
+	ret = memcpy_toio(adapter[card]->rambase + 
+		((unsigned long) dest % 0x4000), src, n);
+	pr_debug("%s: copying %d bytes from %#x to %#x\n",adapter[card]->devicename, n,
+		 (unsigned long) src, adapter[card]->rambase + ((unsigned long) dest %0x4000));
+	restore_flags(flags);
+
+	return ret;
+}
+
+/*
+ * Reverse of above
+ */
+void *memcpy_fromshmem(int card, void *dest, const void *src, size_t n)
+{
+	unsigned long flags;
+	void *ret;
+	unsigned char ch;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return NULL;
+	}
+
+	if(n > SRAM_PAGESIZE) {
+		return NULL;
+	}
+
+	/*
+	 * determine the page to load from the address
+	 */
+	ch = (unsigned long) src / SRAM_PAGESIZE;
+	pr_debug("%s: loaded page %d\n",adapter[card]->devicename,ch);
+	
+	
+	/*
+	 * Block interrupts and load the page
+	 */
+	save_flags(flags);
+	cli();
+
+	outb(((adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80,
+		adapter[card]->ioport[adapter[card]->shmem_pgport]);
+	pr_debug("%s: set page to %#x\n",adapter[card]->devicename,
+		((adapter[card]->shmem_magic + ch * SRAM_PAGESIZE)>>14)|0x80);
+	ret = memcpy_fromio(dest,(void *)(adapter[card]->rambase + 
+		((unsigned long) src % 0x4000)), n);
+/*	pr_debug("%s: copying %d bytes from %#x to %#x\n",
+		adapter[card]->devicename, n,
+		adapter[card]->rambase + ((unsigned long) src %0x4000), (unsigned long) dest); */
+	restore_flags(flags);
+
+	return ret;
+}
+
+void *memset_shmem(int card, void *dest, int c, size_t n)
+{
+	unsigned long flags;
+	unsigned char ch;
+	void *ret;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return NULL;
+	}
+
+	if(n > SRAM_PAGESIZE) {
+		return NULL;
+	}
+
+	/*
+	 * determine the page to load from the address
+	 */
+	ch = (unsigned long) dest / SRAM_PAGESIZE;
+	pr_debug("%s: loaded page %d\n",adapter[card]->devicename,ch);
+
+	/*
+	 * Block interrupts and load the page
+	 */
+	save_flags(flags);
+	cli();
+
+	outb(((adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80,
+		adapter[card]->ioport[adapter[card]->shmem_pgport]);
+	pr_debug("%s: set page to %#x\n",adapter[card]->devicename,
+		((adapter[card]->shmem_magic + ch * SRAM_PAGESIZE)>>14)|0x80);
+	ret = memset_io(adapter[card]->rambase + 
+		((unsigned long) dest % 0x4000), c, n);
+	restore_flags(flags);
+
+	return ret;
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/timer.c linux/drivers/isdn/sc/timer.c
--- v2.0.30/linux/drivers/isdn/sc/timer.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/timer.c	Mon Aug  4 17:34:01 1997
@@ -0,0 +1,172 @@
+/*
+ *  $Id: timer.c,v 1.2 1996/11/20 17:49:57 fritz Exp $
+ *  Copyright (C) 1996  SpellCaster Telecommunications 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 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.
+ *
+ *  For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#define __NO_VERSION__
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+
+extern board *adapter[];
+
+extern void flushreadfifo(int);
+extern int  startproc(int);
+extern int  indicate_status(int, int, unsigned long, char *);
+extern int  sendmessage(int, unsigned int, unsigned int, unsigned int,
+        unsigned int, unsigned int, unsigned int, unsigned int *);
+
+
+/*
+ * Write the proper values into the I/O ports following a reset
+ */
+void setup_ports(int card)
+{
+
+	outb((adapter[card]->rambase >> 12), adapter[card]->ioport[EXP_BASE]);
+
+	/* And the IRQ */
+	outb((adapter[card]->interrupt | 0x80), 
+		adapter[card]->ioport[IRQ_SELECT]);
+}
+
+/*
+ * Timed function to check the status of a previous reset
+ * Must be very fast as this function runs in the context of
+ * an interrupt handler.
+ *
+ * Setup the ioports for the board that were cleared by the reset.
+ * Then, check to see if the signate has been set. Next, set the
+ * signature to a known value and issue a startproc if needed.
+ */
+void check_reset(unsigned long data)
+{
+	unsigned long flags;
+	unsigned long sig;
+	int card = (unsigned int) data;
+
+	pr_debug("%s: check_timer timer called\n", adapter[card]->devicename);
+
+	/* Setup the io ports */
+	setup_ports(card);
+
+  	save_flags(flags);
+	cli();
+	outb(adapter[card]->ioport[adapter[card]->shmem_pgport],
+		(adapter[card]->shmem_magic>>14) | 0x80);	
+	sig = (unsigned long) *((unsigned long *)(adapter[card]->rambase + SIG_OFFSET));	
+
+	/* check the signature */
+	if(sig == SIGNATURE) {
+		flushreadfifo(card);
+		restore_flags(flags);
+		/* See if we need to do a startproc */
+		if (adapter[card]->StartOnReset)
+			startproc(card);
+	}
+	else  {
+		pr_debug("%s: No signature yet, waiting another %d jiffies.\n", 
+			adapter[card]->devicename, CHECKRESET_TIME);
+		del_timer(&adapter[card]->reset_timer);
+		adapter[card]->reset_timer.expires = jiffies + CHECKRESET_TIME;
+		add_timer(&adapter[card]->reset_timer);
+	}
+	restore_flags(flags);
+		
+}
+
+/*
+ * Timed function to check the status of a previous reset
+ * Must be very fast as this function runs in the context of
+ * an interrupt handler.
+ *
+ * Send check adapter->phystat to see if the channels are up
+ * If they are, tell ISDN4Linux that the board is up. If not,
+ * tell IADN4Linux that it is up. Always reset the timer to
+ * fire again (endless loop).
+ */
+void check_phystat(unsigned long data)
+{
+	unsigned long flags;
+	int card = (unsigned int) data;
+
+	pr_debug("%s: Checking status...\n", adapter[card]->devicename);
+	/* 
+	 * check the results of the last PhyStat and change only if
+	 * has changed drastically
+	 */
+	if (adapter[card]->nphystat && !adapter[card]->phystat) {   /* All is well */
+		pr_debug("PhyStat transition to RUN\n");
+		pr_info("%s: Switch contacted, transmitter enabled\n", 
+			adapter[card]->devicename);
+		indicate_status(card, ISDN_STAT_RUN, 0, NULL);
+	}
+	else if (!adapter[card]->nphystat && adapter[card]->phystat) {   /* All is not well */
+		pr_debug("PhyStat transition to STOP\n");
+		pr_info("%s: Switch connection lost, transmitter disabled\n", 
+			adapter[card]->devicename);
+
+		indicate_status(card, ISDN_STAT_STOP, 0, NULL);
+	}
+
+	adapter[card]->phystat = adapter[card]->nphystat;
+
+	/* Reinitialize the timer */
+	save_flags(flags);
+	cli();
+	del_timer(&adapter[card]->stat_timer);
+	adapter[card]->stat_timer.expires = jiffies + CHECKSTAT_TIME;
+	add_timer(&adapter[card]->stat_timer);
+	restore_flags(flags);
+
+	/* Send a new cePhyStatus message */
+	sendmessage(card, CEPID,ceReqTypePhy,ceReqClass2,
+		ceReqPhyStatus,0,0,NULL);
+}
+
+/*
+ * When in trace mode, this callback is used to swap the working shared
+ * RAM page to the trace page(s) and process all received messages. It
+ * must be called often enough to get all of the messages out of RAM before
+ * it loops around.
+ * Trace messages are \n terminated strings.
+ * We output the messages in 64 byte chunks through readstat. Each chunk
+ * is scanned for a \n followed by a time stamp. If the timerstamp is older
+ * than the current time, scanning stops and the page and offset are recorded
+ * as the starting point the next time the trace timer is called. The final
+ * step is to restore the working page and reset the timer.
+ */
+void trace_timer(unsigned long data)
+{
+	unsigned long flags;
+
+	/*
+	 * Disable interrupts and swap the first page
+	 */
+	save_flags(flags);
+	cli();
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/Makefile linux/drivers/isdn/teles/Makefile
--- v2.0.30/linux/drivers/isdn/teles/Makefile	Mon Feb 26 01:58:05 1996
+++ linux/drivers/isdn/teles/Makefile	Wed Dec 31 16:00:00 1969
@@ -1,17 +0,0 @@
-L_OBJS :=
-M_OBJS :=
-O_OBJS := mod.o card.o config.o buffers.o tei.o isdnl2.o isdnl3.o \
-llglue.o q931.o callc.o fsm.o
-
-O_TARGET :=
-ifeq ($(CONFIG_ISDN_DRV_TELES),y)
-  O_TARGET += teles.o
-else
-  ifeq ($(CONFIG_ISDN_DRV_TELES),m)
-    O_TARGET += teles.o
-    M_OBJS += teles.o
-  endif
-endif
-
-include $(TOPDIR)/Rules.make
- 
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/buffers.c linux/drivers/isdn/teles/buffers.c
--- v2.0.30/linux/drivers/isdn/teles/buffers.c	Sat Jun  1 01:56:51 1996
+++ linux/drivers/isdn/teles/buffers.c	Wed Dec 31 16:00:00 1969
@@ -1,326 +0,0 @@
-/* $Id: buffers.c,v 1.3 1996/05/31 00:56:53 fritz Exp $
- *
- * $Log: buffers.c,v $
- * Revision 1.3  1996/05/31 00:56:53  fritz
- * removed cli() from BufPoolAdd, since it is called
- * with interrupts off anyway.
- *
- * Revision 1.2  1996/04/29 22:48:14  fritz
- * Removed compatibility-macros. No longer needed.
- *
- * Revision 1.1  1996/04/13 10:19:28  fritz
- * Initial revision
- *
- *
- */
-#define __NO_VERSION__
-#include "teles.h"
-#include <linux/mm.h>
-#include <linux/malloc.h>
-
-
-void
-BufPoolInit(struct BufPool *bp, int order, int bpps,
-	    int maxpages)
-{
-#ifdef DEBUG_MAGIC
-	generateerror
-	    bp->magic = 010167;
-#endif
-
-#if 0
-	printk(KERN_DEBUG "BufPoolInit bp %x\n", bp);
-#endif
-
-	bp->freelist = NULL;
-	bp->pageslist = NULL;
-	bp->pageorder = order;
-	bp->pagescount = 0;
-	bp->bpps = bpps;
-	bp->bufsize = BUFFER_SIZE(order, bpps);
-	bp->maxpages = maxpages;
-}
-
-int
-BufPoolAdd(struct BufPool *bp, int priority)
-{
-	struct Pages   *ptr;
-	byte           *bptr;
-	int             i;
-	struct BufHeader *bh = NULL, *prev, *first;
-
-#if 0
-	printk(KERN_DEBUG "BufPoolAdd bp %x\n", bp);
-#endif
-
-	ptr = (struct Pages *) __get_free_pages(priority, bp->pageorder, 0);
-	if (!ptr) {
-		printk(KERN_WARNING "BufPoolAdd couldn't get pages!\n");
-		return (-1);
-	}
-#if 0
-	printk(KERN_DEBUG "Order %d pages allocated at %x\n", bp->pageorder, ptr);
-#endif
-
-	ptr->next = bp->pageslist;
-	bp->pageslist = ptr;
-	bp->pagescount++;
-
-	bptr = (byte *) ptr + sizeof(struct Pages *);
-
-	i = bp->bpps;
-	first = (struct BufHeader *) bptr;
-	prev = NULL;
-	while (i--) {
-		bh = (struct BufHeader *) bptr;
-#ifdef DEBUG_MAGIC
-		bh->magic = 020167;
-#endif
-		bh->next = prev;
-		prev = bh;
-		bh->bp = bp;
-		bptr += PART_SIZE(bp->pageorder, bp->bpps);
-	}
-
-	first->next = bp->freelist;
-	bp->freelist = bh;
-	return (0);
-}
-
-void
-BufPoolFree(struct BufPool *bp)
-{
-	struct Pages   *p;
-
-#if 0
-	printk(KERN_DEBUG "BufPoolFree bp %x\n", bp);
-#endif
-
-	while (bp->pagescount--) {
-		p = bp->pageslist->next;
-		free_pages((unsigned long) bp->pageslist, bp->pageorder);
-#if 0
-		printk(KERN_DEBUG "Free pages %x order %d\n", bp->pageslist, bp->pageorder);
-#endif
-		bp->pageslist = p;
-	}
-}
-
-int
-BufPoolGet(struct BufHeader **bh,
-	   struct BufPool *bp, int priority, void *heldby, int where)
-{
-	long            flags;
-	int             i;
-
-#ifdef DEBUG_MAGIC
-	if (bp->magic != 010167) {
-		printk(KERN_DEBUG "BufPoolGet: not a BufHeader\n");
-		return (-1);
-	}
-#endif
-
-	save_flags(flags);
-	cli();
-	i = 0;
-	while (!0) {
-		if (bp->freelist) {
-			*bh = bp->freelist;
-			bp->freelist = bp->freelist->next;
-			(*bh)->heldby = heldby;
-			(*bh)->where = where;
-			restore_flags(flags);
-			return (0);
-		}
-		if ((i == 0) && (bp->pagescount < bp->maxpages)) {
-                        if (BufPoolAdd(bp, priority)) {
-                                restore_flags(flags);
-                                return -1;
-                        }
-			i++;
-		} else {
-			*bh = NULL;
-			restore_flags(flags);
-			return (-1);
-		}
-	}
-
-}
-
-void
-BufPoolRelease(struct BufHeader *bh)
-{
-	struct BufPool *bp;
-	long            flags;
-
-#ifdef DEBUG_MAGIC
-	if (bh->magic != 020167) {
-		printk(KERN_DEBUG "BufPoolRelease: not a BufHeader\n");
-		printk(KERN_DEBUG "called from %x\n", __builtin_return_address(0));
-		return;
-	}
-#endif
-
-	bp = bh->bp;
-
-#ifdef DEBUG_MAGIC
-	if (bp->magic != 010167) {
-		printk(KERN_DEBUG "BufPoolRelease: not a BufPool\n");
-		return;
-	}
-#endif
-
-	save_flags(flags);
-	cli();
-	bh->next = bp->freelist;
-	bp->freelist = bh;
-	restore_flags(flags);
-}
-
-void
-BufQueueLink(struct BufQueue *bq,
-	     struct BufHeader *bh)
-{
-	unsigned long   flags;
-
-	save_flags(flags);
-	cli();
-	if (!bq->head)
-		bq->head = bh;
-	if (bq->tail)
-		bq->tail->next = bh;
-	bq->tail = bh;
-	bh->next = NULL;
-	restore_flags(flags);
-}
-
-void
-BufQueueLinkFront(struct BufQueue *bq,
-		  struct BufHeader *bh)
-{
-	unsigned long   flags;
-
-	save_flags(flags);
-	cli();
-	bh->next = bq->head;
-	bq->head = bh;
-	if (!bq->tail)
-		bq->tail = bh;
-	restore_flags(flags);
-}
-
-int
-BufQueueUnlink(struct BufHeader **bh, struct BufQueue *bq)
-{
-	long            flags;
-
-	save_flags(flags);
-	cli();
-
-	if (bq->head) {
-		if (bq->tail == bq->head)
-			bq->tail = NULL;
-		*bh = bq->head;
-		bq->head = (*bh)->next;
-		restore_flags(flags);
-		return (0);
-	} else {
-		restore_flags(flags);
-		return (-1);
-	}
-}
-
-void
-BufQueueInit(struct BufQueue *bq)
-{
-#ifdef DEBUG_MAGIC
-	bq->magic = 030167;
-#endif
-	bq->head = NULL;
-	bq->tail = NULL;
-}
-
-void
-BufQueueRelease(struct BufQueue *bq)
-{
-	struct BufHeader *bh;
-
-	while (bq->head) {
-		BufQueueUnlink(&bh, bq);
-		BufPoolRelease(bh);
-	}
-}
-
-int
-BufQueueLength(struct BufQueue *bq)
-{
-	int             i = 0;
-	struct BufHeader *bh;
-
-	bh = bq->head;
-	while (bh) {
-		i++;
-		bh = bh->next;
-	}
-	return (i);
-}
-
-void
-BufQueueDiscard(struct BufQueue *q, int pr, void *heldby,
-		int releasetoo)
-{
-	long            flags;
-	struct BufHeader *sp;
-
-	save_flags(flags);
-	cli();
-
-	while (!0) {
-		sp = q->head;
-		if (!sp)
-			break;
-		if ((sp->primitive == pr) && (sp->heldby == heldby)) {
-			q->head = sp->next;
-			if (q->tail == sp)
-				q->tail = NULL;
-			if (releasetoo)
-				BufPoolRelease(sp);
-		} else
-			break;
-	}
-
-	sp = q->head;
-	if (sp)
-		while (sp->next) {
-			if ((sp->next->primitive == pr) && (sp->next->heldby == heldby)) {
-				if (q->tail == sp->next)
-					q->tail = sp;
-				if (releasetoo)
-					BufPoolRelease(sp->next);
-				sp->next = sp->next->next;
-			} else
-				sp = sp->next;
-		}
-	restore_flags(flags);
-}
-
-void
-Sfree(byte * ptr)
-{
-#if 0
-	printk(KERN_DEBUG "Sfree %x\n", ptr);
-#endif
-	kfree(ptr);
-}
-
-byte           *
-Smalloc(int size, int pr, char *why)
-{
-	byte           *p;
-
-	p = (byte *) kmalloc(size, pr);
-#if 0
-	printk(KERN_DEBUG "Smalloc %s size %d res %x\n", why, size, p);
-#endif
-	return (p);
-}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/callc.c linux/drivers/isdn/teles/callc.c
--- v2.0.30/linux/drivers/isdn/teles/callc.c	Tue Nov 12 22:36:20 1996
+++ linux/drivers/isdn/teles/callc.c	Wed Dec 31 16:00:00 1969
@@ -1,1482 +0,0 @@
-/* $Id: callc.c,v 1.14 1996/10/22 23:14:14 fritz Exp $
- *
- * $Log: callc.c,v $
- * Revision 1.14  1996/10/22 23:14:14  fritz
- * Changes for compatibility to 2.0.X and 2.1.X kernels.
- *
- * Revision 1.13  1996/06/24 17:15:55  fritz
- * corrected return code of teles_writebuf()
- *
- * Revision 1.12  1996/06/12 16:15:33  fritz
- * Extended user-configurable debugging flags.
- *
- * Revision 1.11  1996/06/07 12:32:20  fritz
- * More changes to support suspend/resume.
- *
- * Revision 1.10  1996/06/06 21:24:21  fritz
- * Started adding support for suspend/resume.
- *
- * Revision 1.9  1996/05/31 12:23:57  jdenoud
- * Jan: added channel open check to teles_writebuf
- *
- * Revision 1.8  1996/05/31 01:00:38  fritz
- * Changed return code of teles_writebuf, when out of memory.
- *
- * Revision 1.7  1996/05/17 03:40:37  fritz
- * General cleanup.
- *
- * Revision 1.6  1996/05/10 22:42:07  fritz
- * Added entry for EV_RELEASE_CNF in ST_OUT (if no D-Channel avail.)
- *
- * Revision 1.5  1996/05/06 10:16:15  fritz
- * Added voice stuff.
- *
- * Revision 1.4  1996/04/30 22:04:05  isdn4dev
- *   improved callback  Karsten Keil
- *
- * Revision 1.3  1996/04/30 10:04:19  fritz
- * Started voice support.
- * Added printk() to debug-switcher for easier
- * synchronization between printk()'s and output
- * of /dev/isdnctrl.
- *
- * Revision 1.2  1996/04/20 16:42:29  fritz
- * Changed statemachine to allow reject of incoming calls.
- *
- * Revision 1.1  1996/04/13 10:20:59  fritz
- * Initial revision
- *
- *
- */
-#define __NO_VERSION__
-#include "teles.h"
-
-extern struct IsdnCard cards[];
-extern int      nrcards;
-extern int      drid;
-extern isdn_if  iif;
-extern void     teles_mod_dec_use_count(void);
-extern void     teles_mod_inc_use_count(void);
-
-static int      init_ds(int chan, int incoming);
-static void     release_ds(int chan);
-static char    *strcpyupto(char *dest, char *src, char upto);
-
-static struct Fsm callcfsm =
-{NULL, 0, 0},   lcfsm =
-{NULL, 0, 0};
-
-struct Channel *chanlist;
-static int      chancount = 0;
-unsigned int    debugflags = 0;
-
-#define TMR_DCHAN_EST 2000
-
-static void
-stat_debug(struct Channel *chanp, char *s)
-{
-	char            tmp[100], tm[32];
-
-	jiftime(tm, jiffies);
-	sprintf(tmp, "%s Channel %d HL->LL %s\n", tm, chanp->chan, s);
-	teles_putstatus(tmp);
-}
-
-enum {
-        ST_NULL,           /*  0 inactive                                               */
-        ST_OUT,            /*  1 outgoing, awaiting SETUP confirm                       */
-        ST_CLEAR,          /*  2 call release, awaiting RELEASE confirm                 */
-        ST_OUT_W,          /*  3 outgoing, awaiting d-channel establishment             */
-        ST_REL_W,          /*  4 awaiting d-channel release                             */
-        ST_IN_W,           /*  5 incoming, awaiting d-channel establishment             */
-        ST_IN,             /*  6 incoming call received                                 */
-        ST_IN_SETUP,       /*  7 incoming, SETUP response sent                          */
-        ST_IN_DACT,        /*  8 incoming connected, no b-channel prot.                 */
-        ST_OUT_ESTB,       /* 10 outgoing connected, awaiting b-channel prot. estbl.    */
-        ST_ACTIVE,         /* 11 active, b channel prot. established                    */
-        ST_BC_HANGUP,      /* 12 call clear. (initiator), awaiting b channel prot. rel. */
-        ST_PRO_W,          /* 13 call clear. (initiator), DISCONNECT req. sent          */
-        ST_ANT_W,          /* 14 call clear. (receiver), awaiting DISCONNECT ind.       */
-        ST_DISC_BC_HANGUP, /*    d channel gone, wait for b channel deactivation        */
-	ST_OUT_W_HANGUP,      /* Outgoing waiting for D-Channel hangup received */ 
-        ST_D_ERR,          /*    d channel released while active                        */
-};
-
-#define STATE_COUNT (ST_D_ERR+1)
-
-static char    *strState[] =
-{
-        "ST_NULL",
-        "ST_OUT",
-        "ST_CLEAR",
-        "ST_OUT_W",
-        "ST_REL_W",
-        "ST_IN_W",
-        "ST_IN",
-        "ST_IN_SETUP",
-        "ST_IN_DACT",
-        "ST_OUT_ESTB",
-        "ST_ACTIVE",
-        "ST_BC_HANGUP",
-        "ST_PRO_W",
-        "ST_ANT_W",
-        "ST_DISC_BC_HANGUP",
-        "ST_OUT_W_HANGUP",
-        "ST_D_ERR",
-};
-
-enum {
-        EV_DIAL,           /*  0 */
-        EV_SETUP_CNF,      /*  1 */
-        EV_ACCEPTB,        /*  2 */
-        EV_DISCONNECT_CNF, /*  5 */
-        EV_DISCONNECT_IND, /*  6 */
-        EV_RELEASE_CNF,    /*  7 */
-        EV_DLEST,          /*  8 */
-        EV_DLRL,           /*  9 */
-        EV_SETUP_IND,      /* 10 */
-        EV_RELEASE_IND,    /* 11 */
-        EV_ACCEPTD,        /* 12 */
-        EV_SETUP_CMPL_IND, /* 13 */
-        EV_BC_EST,         /* 14 */
-        EV_WRITEBUF,       /* 15 */
-        EV_DATAIN,         /* 16 */
-        EV_HANGUP,         /* 17 */
-        EV_BC_REL,         /* 18 */
-        EV_CINF,           /* 19 */
-        EV_SUSPEND,        /* 20 */
-        EV_RESUME,         /* 21 */
-};
-
-#define EVENT_COUNT (EV_CINF+1)
-
-static char    *strEvent[] =
-{
-        "EV_DIAL",
-        "EV_SETUP_CNF",
-        "EV_ACCEPTB",
-        "EV_DISCONNECT_CNF",
-        "EV_DISCONNECT_IND",
-        "EV_RELEASE_CNF",
-        "EV_DLEST",
-        "EV_DLRL",
-        "EV_SETUP_IND",
-        "EV_RELEASE_IND",
-        "EV_ACCEPTD",
-        "EV_SETUP_CMPL_IND",
-        "EV_BC_EST",
-        "EV_WRITEBUF",
-        "EV_DATAIN",
-        "EV_HANGUP",
-        "EV_BC_REL",
-        "EV_CINF",
-        "EV_SUSPEND",
-        "EV_RESUME",
-};
-
-enum {
-        ST_LC_NULL,
-        ST_LC_ACTIVATE_WAIT,
-        ST_LC_DELAY,
-        ST_LC_ESTABLISH_WAIT,
-        ST_LC_CONNECTED,
-        ST_LC_RELEASE_WAIT,
-};
-
-#define LC_STATE_COUNT (ST_LC_RELEASE_WAIT+1)
-
-static char    *strLcState[] =
-{
-        "ST_LC_NULL",
-        "ST_LC_ACTIVATE_WAIT",
-        "ST_LC_DELAY",
-        "ST_LC_ESTABLISH_WAIT",
-        "ST_LC_CONNECTED",
-        "ST_LC_RELEASE_WAIT",
-};
-
-enum {
-        EV_LC_ESTABLISH,
-        EV_LC_PH_ACTIVATE,
-        EV_LC_PH_DEACTIVATE,
-        EV_LC_DL_ESTABLISH,
-        EV_LC_TIMER,
-        EV_LC_DL_RELEASE,
-        EV_LC_RELEASE,
-};
-
-#define LC_EVENT_COUNT (EV_LC_RELEASE+1)
-
-static char    *strLcEvent[] =
-{
-        "EV_LC_ESTABLISH",
-        "EV_LC_PH_ACTIVATE",
-        "EV_LC_PH_DEACTIVATE",
-        "EV_LC_DL_ESTABLISH",
-        "EV_LC_TIMER",
-        "EV_LC_DL_RELEASE",
-        "EV_LC_RELEASE",
-};
-
-#define LC_D  0
-#define LC_B  1
-
-static int
-my_atoi(char *s)
-{
-        int             i, n;
-
-        n = 0;
-        if (!s)
-                return -1;
-        for (i = 0; *s >= '0' && *s <= '9'; i++, s++)
-                n = 10 * n + (*s - '0');
-        return n;
-}
-
-/*
- * Dial out
- */
-static void
-r1(struct FsmInst *fi, int event, void *arg)
-{
-        isdn_ctrl      *ic = arg;
-        struct Channel *chanp = fi->userdata;
-        char           *ptr;
-        char            sis[3];
-
-        /* Destination Phone-Number */
-        ptr = strcpyupto(chanp->para.called, ic->num, ',');
-        /* Source Phone-Number */
-        ptr = strcpyupto(chanp->para.calling, ptr + 1, ',');
-        if (!strcmp(chanp->para.calling, "0"))
-                chanp->para.calling[0] = '\0';
-
-        /* Service-Indicator 1 */
-        ptr = strcpyupto(sis, ptr + 1, ',');
-        chanp->para.info = my_atoi(sis);
-
-        /* Service-Indicator 2 */
-        ptr = strcpyupto(sis, ptr + 1, '\0');
-        chanp->para.info2 = my_atoi(sis);
-
-        chanp->l2_active_protocol = chanp->l2_protocol;
-        chanp->incoming = 0;
-        chanp->lc_b.l2_start = !0;
-
-        switch (chanp->l2_active_protocol) {
-          case (ISDN_PROTO_L2_X75I):
-                  chanp->lc_b.l2_establish = !0;
-                  break;
-          case (ISDN_PROTO_L2_HDLC):
-          case (ISDN_PROTO_L2_TRANS):
-                  chanp->lc_b.l2_establish = 0;
-                  break;
-          default:
-                  printk(KERN_WARNING "r1 unknown protocol\n");
-                  break;
-        }
-
-        FsmChangeState(fi, ST_OUT_W);
-        FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL);
-}
-
-static void
-ll_hangup(struct Channel *chanp, int bchantoo)
-{
-        isdn_ctrl       ic;
-
-        if (bchantoo) {
-                if (chanp->debug & 1)
-                        stat_debug(chanp, "STAT_BHUP");
-                ic.driver = drid;
-                ic.command = ISDN_STAT_BHUP;
-                ic.arg = chanp->chan;
-                iif.statcallb(&ic);
-        }
-        if (chanp->debug & 1)
-                stat_debug(chanp, "STAT_DHUP");
-        ic.driver = drid;
-        ic.command = ISDN_STAT_DHUP;
-        ic.arg = chanp->chan;
-        iif.statcallb(&ic);
-}
-
-static void
-r2(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-
-        chanp->is.l4.l4l3(&chanp->is, CC_RELEASE_REQ, NULL);
-
-        FsmChangeState(fi, ST_CLEAR);
-        ll_hangup(chanp, 0);
-}
-
-
-static void
-r2_1(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-
-        chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL);
-
-        FsmChangeState(fi, ST_OUT_W_HANGUP);
-}
-
-
-static void
-r2_2(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-
-        FsmChangeState(fi, ST_REL_W);
-        FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
-        ll_hangup(chanp, 0);
-}
-
-
-static void
-r3(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-
-        FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
-        FsmChangeState(fi, ST_REL_W);
-}
-
-
-static void
-r3_1(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-
-	chanp->is.l4.l4l3(&chanp->is,CC_DLRL,NULL); 
-	
-        FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
-        FsmChangeState(fi, ST_REL_W);
-        ll_hangup(chanp, 0);
-}
-
-
-static void
-r4(struct FsmInst *fi, int event, void *arg)
-{
-	struct Channel *chanp=fi->userdata;  
-  
-        chanp->is.l4.l4l3(&chanp->is,CC_DLRL,NULL);                                  
-        FsmChangeState(fi, ST_NULL);
-}
-
-static void
-r5(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-
-        chanp->para.callref = chanp->outcallref;
-
-        chanp->outcallref++;
-        if (chanp->outcallref == 128)
-                chanp->outcallref = 64;
-
-        chanp->is.l4.l4l3(&chanp->is, CC_SETUP_REQ, NULL);
-
-        FsmChangeState(fi, ST_OUT);
-}
-
-static void
-r6(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-
-        FsmChangeState(fi, ST_IN_W);
-        FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL);
-}
-
-static void
-r7(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-        isdn_ctrl       ic;
-
-        /*
-         * Report incoming calls only once to linklevel, use octet 3 of
-         * channel identification information element. (it's value
-         * is copied to chanp->para.bchannel in l3s12(), file isdnl3.c)
-         */
-        if (((chanp->chan & 1) + 1) & chanp->para.bchannel) {
-                chanp->is.l4.l4l3(&chanp->is, CC_ALERTING_REQ, NULL);
-                FsmChangeState(fi, ST_IN);
-                if (chanp->debug & 1)
-                        stat_debug(chanp, "STAT_ICALL");
-                ic.driver = drid;
-                ic.command = ISDN_STAT_ICALL;
-                ic.arg = chanp->chan;
-                /*
-                 * No need to return "unknown" for calls without OAD,
-                 * cause that's handled in linklevel now (replaced by '0')
-                 */
-                sprintf(ic.num, "%s,%d,0,%s", chanp->para.calling, chanp->para.info,
-                        chanp->para.called);
-                iif.statcallb(&ic);
-        } else {
-                chanp->is.l4.l4l3(&chanp->is,CC_DLRL,NULL);
-                FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
-                FsmChangeState(fi, ST_REL_W);
-        }
-}
-
-static void
-r8(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-
-        FsmChangeState(fi, ST_IN_SETUP);
-        chanp->is.l4.l4l3(&chanp->is, CC_SETUP_RSP, NULL);
-
-}
-
-static void
-r9(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-
-        FsmChangeState(fi, ST_IN_DACT);
-
-        chanp->l2_active_protocol = chanp->l2_protocol;
-        chanp->incoming = !0;
-        chanp->lc_b.l2_start = 0;
-
-        switch (chanp->l2_active_protocol) {
-          case (ISDN_PROTO_L2_X75I):
-                  chanp->lc_b.l2_establish = !0;
-                  break;
-          case (ISDN_PROTO_L2_HDLC):
-          case (ISDN_PROTO_L2_TRANS):
-                  chanp->lc_b.l2_establish = 0;
-                  break;
-          default:
-                  printk(KERN_WARNING "r9 unknown protocol\n");
-                  break;
-        }
-
-        init_ds(chanp->chan, !0);
-
-        FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL);
-}
-
-static void
-r10(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-
-        FsmChangeState(fi, ST_OUT_ESTB);
-
-        init_ds(chanp->chan, 0);
-        FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL);
-
-}
-
-static void
-r12(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-        isdn_ctrl       ic;
-
-        FsmChangeState(fi, ST_ACTIVE);
-        chanp->data_open = !0;
-
-        if (chanp->debug & 1)
-                stat_debug(chanp, "STAT_DCONN");
-        ic.driver = drid;
-        ic.command = ISDN_STAT_DCONN;
-        ic.arg = chanp->chan;
-        iif.statcallb(&ic);
-
-        if (chanp->debug & 1)
-                stat_debug(chanp, "STAT_BCONN");
-        ic.driver = drid;
-        ic.command = ISDN_STAT_BCONN;
-        ic.arg = chanp->chan;
-        iif.statcallb(&ic);
-}
-
-static void
-r15(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-
-        chanp->data_open = 0;
-        FsmChangeState(fi, ST_BC_HANGUP);
-        FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
-}
-
-static void
-r16(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-
-        release_ds(chanp->chan);
-
-        FsmChangeState(fi, ST_PRO_W);
-        chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL);
-}
-
-static void
-r17(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-
-        chanp->data_open = 0;
-        release_ds(chanp->chan);
-
-        FsmChangeState(fi, ST_ANT_W);
-}
-
-
-static void
-r17_1(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-
-        chanp->data_open = 0;
-        release_ds(chanp->chan);
-
-	chanp->is.l4.l4l3(&chanp->is,CC_DLRL,NULL); 
-	
-	FsmEvent(&chanp->lc_d.lcfi,EV_LC_RELEASE,NULL); 
-	
-        FsmChangeState(fi, ST_NULL);
-        
-        ll_hangup(chanp,!0); 
-}
-
-static void
-r18(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-
-        FsmChangeState(fi, ST_REL_W);
-        FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
-
-        ll_hangup(chanp, !0);
-}
-
-static void
-r19(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-
-        FsmChangeState(fi, ST_CLEAR);
-
-        chanp->is.l4.l4l3(&chanp->is, CC_RELEASE_REQ, NULL);
-
-        ll_hangup(chanp, !0);
-}
-
-static void
-r20(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-        
-        chanp->is.l4.l4l3(&chanp->is,CC_DLRL,NULL); 
-        
-        FsmEvent(&chanp->lc_d.lcfi,EV_LC_RELEASE,NULL); 
-
-        FsmChangeState(fi, ST_NULL);
-
-        ll_hangup(chanp, 0);
-}
-
-
-static void
-r21(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-
-        chanp->data_open = 0;
-        FsmChangeState(fi, ST_DISC_BC_HANGUP);
-        FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
-}
-
-static void
-r22(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-
-        release_ds(chanp->chan);
-
-        FsmChangeState(fi, ST_CLEAR);
-
-        chanp->is.l4.l4l3(&chanp->is, CC_RELEASE_REQ, NULL);
-
-        ll_hangup(chanp, !0);
-}
-
-static void
-r23(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-
-        release_ds(chanp->chan);
-
-        FsmChangeState(fi, ST_PRO_W);
-        chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL);
-}
-
-static void
-r23_1(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-
-        release_ds(chanp->chan);
-
-	chanp->is.l4.l4l3(&chanp->is, CC_DLRL,NULL); 
-	
-	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE,NULL); 
-	
-        FsmChangeState(fi, ST_NULL);
-        
-        ll_hangup(chanp,!0); 
-}
-
-static void
-r24(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-
-        chanp->data_open = 0;
-        FsmChangeState(fi, ST_D_ERR);
-        FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
-}
-
-static void
-r25(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-
-        release_ds(chanp->chan);
-
-        FsmChangeState(fi, ST_NULL);
-
-        ll_hangup(chanp, !0);
-}
-
-static void
-r26(struct FsmInst *fi, int event, void *arg)
-{
-        struct Channel *chanp = fi->userdata;
-        isdn_ctrl       ic;
-
-
-        ic.driver = drid;
-        ic.command = ISDN_STAT_CINF;
-        ic.arg = chanp->chan;
-        sprintf(ic.num, "%d", chanp->para.chargeinfo);
-        iif.statcallb(&ic);
-}
-
-
-
-static struct FsmNode fnlist[] =
-{
-        {ST_NULL,             EV_DIAL,                r1},
-        {ST_OUT_W,            EV_DLEST,               r5},
-        {ST_OUT_W,            EV_DLRL,                r20},
-        {ST_OUT_W,            EV_RELEASE_CNF,         r2_2 },   
-        {ST_OUT,              EV_DISCONNECT_IND,      r2},
-        {ST_OUT,              EV_SETUP_CNF,           r10},
-        {ST_OUT,              EV_HANGUP,              r2_1},
-        {ST_OUT,              EV_RELEASE_IND,         r20},
-        {ST_OUT,              EV_RELEASE_CNF,         r20},
-        {ST_OUT,              EV_DLRL,                r2_2},
-        {ST_OUT_W_HANGUP,     EV_RELEASE_IND,         r2_2},
-        {ST_OUT_W_HANGUP,     EV_DLRL,                r20},
-        {ST_CLEAR,            EV_RELEASE_CNF,         r3},
-        {ST_CLEAR,            EV_DLRL,                r20},
-        {ST_REL_W,            EV_DLRL,                r4},
-        {ST_NULL,             EV_SETUP_IND,           r6},
-        {ST_IN_W,             EV_DLEST,               r7},
-        {ST_IN_W,             EV_DLRL,                r3_1},
-        {ST_IN,               EV_DLRL,                r3_1},
-        {ST_IN,               EV_HANGUP,              r2_1},
-        {ST_IN,               EV_RELEASE_IND,         r2_2},
-        {ST_IN,               EV_RELEASE_CNF,         r2_2},
-        {ST_IN,               EV_ACCEPTD,             r8},
-        {ST_IN_SETUP,         EV_HANGUP,              r2_1},
-        {ST_IN_SETUP,         EV_SETUP_CMPL_IND,      r9},
-        {ST_IN_SETUP,         EV_RELEASE_IND,         r2_2},
-        {ST_IN_SETUP,         EV_DISCONNECT_IND,      r2},
-        {ST_IN_SETUP,         EV_DLRL,                r20},
-        {ST_OUT_ESTB,         EV_BC_EST,              r12},
-        {ST_OUT_ESTB,         EV_BC_REL,              r23},
-        {ST_OUT_ESTB,         EV_DLRL,                r23_1},
-        {ST_IN_DACT,          EV_BC_EST,              r12},
-        {ST_IN_DACT,          EV_BC_REL,              r17},
-        {ST_IN_DACT,          EV_DLRL,                r17_1},
-        {ST_ACTIVE,           EV_HANGUP,              r15},
-        {ST_ACTIVE,           EV_BC_REL,              r17},
-        {ST_ACTIVE,           EV_DISCONNECT_IND,      r21},
-        {ST_ACTIVE,           EV_DLRL,                r24},
-        {ST_ACTIVE,           EV_CINF,                r26},
-        {ST_ACTIVE,           EV_RELEASE_IND,         r17},
-        {ST_BC_HANGUP,        EV_BC_REL,              r16},
-        {ST_BC_HANGUP,        EV_DISCONNECT_IND,      r21},
-        {ST_PRO_W,            EV_RELEASE_IND,         r18},
-        {ST_ANT_W,            EV_DISCONNECT_IND,      r19},
-        {ST_DISC_BC_HANGUP,   EV_BC_REL,              r22},
-        {ST_D_ERR,            EV_BC_REL,              r25},
-};
-
-#define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode))
-
-static void
-lc_r1(struct FsmInst *fi, int event, void *arg)
-{
-        struct LcFsm   *lf = fi->userdata;
-
-        FsmChangeState(fi, ST_LC_ACTIVATE_WAIT);
-        FsmAddTimer(&lf->act_timer, 1000, EV_LC_TIMER, NULL, 50);
-        lf->st->ma.manl1(lf->st, PH_ACTIVATE, NULL);
-
-}
-
-static void
-lc_r6(struct FsmInst *fi, int event, void *arg)
-{
-        struct LcFsm   *lf = fi->userdata;
-
-        FsmDelTimer(&lf->act_timer, 50);
-        FsmChangeState(fi, ST_LC_DELAY);
-        FsmAddTimer(&lf->act_timer, 40, EV_LC_TIMER, NULL, 51);
-}
-
-static void
-lc_r2(struct FsmInst *fi, int event, void *arg)
-{
-        struct LcFsm   *lf = fi->userdata;
-
-        if (lf->l2_establish) {
-                FsmChangeState(fi, ST_LC_ESTABLISH_WAIT);
-                if (lf->l2_start)
-                        lf->st->ma.manl2(lf->st, DL_ESTABLISH, NULL);
-        } else {
-                FsmChangeState(fi, ST_LC_CONNECTED);
-                lf->lccall(lf, LC_ESTABLISH, NULL);
-        }
-}
-
-static void
-lc_r3(struct FsmInst *fi, int event, void *arg)
-{
-        struct LcFsm   *lf = fi->userdata;
-
-        FsmChangeState(fi, ST_LC_CONNECTED);
-        lf->lccall(lf, LC_ESTABLISH, NULL);
-}
-
-static void
-lc_r4(struct FsmInst *fi, int event, void *arg)
-{
-        struct LcFsm   *lf = fi->userdata;
-
-        if (lf->l2_establish) {
-                FsmChangeState(fi, ST_LC_RELEASE_WAIT);
-                lf->st->ma.manl2(lf->st, DL_RELEASE, NULL);
-        } else {
-                FsmChangeState(fi, ST_LC_NULL);
-                lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL);
-                lf->lccall(lf, LC_RELEASE, NULL);
-        }
-}
-
-static void
-lc_r5(struct FsmInst *fi, int event, void *arg)
-{
-        struct LcFsm   *lf = fi->userdata;
-
-        FsmChangeState(fi, ST_LC_NULL);
-        lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL);
-        lf->lccall(lf, LC_RELEASE, NULL);
-}
-
-static struct FsmNode LcFnList[] =
-{
-        {ST_LC_NULL,                  EV_LC_ESTABLISH,        lc_r1},
-        {ST_LC_ACTIVATE_WAIT,         EV_LC_PH_ACTIVATE,      lc_r6},
-        {ST_LC_DELAY,                 EV_LC_TIMER,            lc_r2},
-        {ST_LC_ESTABLISH_WAIT,        EV_LC_DL_ESTABLISH,     lc_r3},
-        {ST_LC_CONNECTED,             EV_LC_RELEASE,          lc_r4},
-        {ST_LC_CONNECTED,             EV_LC_DL_RELEASE,       lc_r5},
-        {ST_LC_RELEASE_WAIT,          EV_LC_DL_RELEASE,       lc_r5},
-        {ST_LC_ACTIVATE_WAIT,         EV_LC_TIMER,            lc_r5},
-        {ST_LC_ESTABLISH_WAIT,        EV_LC_DL_RELEASE,       lc_r5},
-};
-
-#define LC_FN_COUNT (sizeof(LcFnList)/sizeof(struct FsmNode))
-
-void
-CallcNew(void)
-{
-        callcfsm.state_count = STATE_COUNT;
-        callcfsm.event_count = EVENT_COUNT;
-        callcfsm.strEvent = strEvent;
-        callcfsm.strState = strState;
-        FsmNew(&callcfsm, fnlist, FNCOUNT);
-
-        lcfsm.state_count = LC_STATE_COUNT;
-        lcfsm.event_count = LC_EVENT_COUNT;
-        lcfsm.strEvent = strLcEvent;
-        lcfsm.strState = strLcState;
-        FsmNew(&lcfsm, LcFnList, LC_FN_COUNT);
-}
-
-void
-CallcFree(void)
-{
-        FsmFree(&lcfsm);
-        FsmFree(&callcfsm);
-}
-
-static void
-release_ds(int chan)
-{
-        struct PStack  *st = &chanlist[chan].ds;
-        struct IsdnCardState *sp;
-        struct HscxState *hsp;
-
-        sp = st->l1.hardware;
-        hsp = sp->hs + chanlist[chan].hscx;
-
-        close_hscxstate(hsp);
-
-        switch (chanlist[chan].l2_active_protocol) {
-          case (ISDN_PROTO_L2_X75I):
-                  releasestack_isdnl2(st);
-                  break;
-          case (ISDN_PROTO_L2_HDLC):
-          case (ISDN_PROTO_L2_TRANS):
-                  releasestack_transl2(st);
-                  break;
-        }
-}
-
-static void
-cc_l1man(struct PStack *st, int pr, void *arg)
-{
-        struct Channel *chanp = (struct Channel *) st->l4.userdata;
-
-        switch (pr) {
-          case (PH_ACTIVATE):
-                  FsmEvent(&chanp->lc_d.lcfi, EV_LC_PH_ACTIVATE, NULL);
-                  break;
-          case (PH_DEACTIVATE):
-                  FsmEvent(&chanp->lc_d.lcfi, EV_LC_PH_DEACTIVATE, NULL);
-                  break;
-        }
-}
-
-static void
-cc_l2man(struct PStack *st, int pr, void *arg)
-{
-        struct Channel *chanp = (struct Channel *) st->l4.userdata;
-
-        switch (pr) {
-          case (DL_ESTABLISH):
-                  FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_ESTABLISH, NULL);
-                  break;
-          case (DL_RELEASE):
-                  FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_RELEASE, NULL);
-                  break;
-        }
-}
-
-static void
-dcc_l1man(struct PStack *st, int pr, void *arg)
-{
-        struct Channel *chanp = (struct Channel *) st->l4.userdata;
-
-        switch (pr) {
-          case (PH_ACTIVATE):
-                  FsmEvent(&chanp->lc_b.lcfi, EV_LC_PH_ACTIVATE, NULL);
-                  break;
-          case (PH_DEACTIVATE):
-                  FsmEvent(&chanp->lc_b.lcfi, EV_LC_PH_DEACTIVATE, NULL);
-                  break;
-        }
-}
-
-static void
-dcc_l2man(struct PStack *st, int pr, void *arg)
-{
-        struct Channel *chanp = (struct Channel *) st->l4.userdata;
-
-        switch (pr) {
-          case (DL_ESTABLISH):
-                  FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_ESTABLISH, NULL);
-                  break;
-          case (DL_RELEASE):
-                  FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_RELEASE, NULL);
-                  break;
-        }
-}
-
-static void
-ll_handler(struct PStack *st, int pr,
-           struct BufHeader *ibh)
-{
-        struct Channel *chanp = (struct Channel *) st->l4.userdata;
-
-        switch (pr) {
-          case (CC_DISCONNECT_IND):
-                  FsmEvent(&chanp->fi, EV_DISCONNECT_IND, NULL);
-                  break;
-          case (CC_RELEASE_CNF):
-                  FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL);
-                  break;
-          case (CC_SETUP_IND):
-                  FsmEvent(&chanp->fi, EV_SETUP_IND, NULL);
-                  break;
-          case (CC_RELEASE_IND):
-                  FsmEvent(&chanp->fi, EV_RELEASE_IND, NULL);
-                  break;
-          case (CC_SETUP_COMPLETE_IND):
-                  FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL);
-                  break;
-          case (CC_SETUP_CNF):
-                  FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
-                  break;
-          case (CC_INFO_CHARGE):
-                  FsmEvent(&chanp->fi, EV_CINF, NULL);
-                  break;
-        }
-}
-
-static void
-init_is(int chan, unsigned int ces)
-{
-        struct PStack  *st = &(chanlist[chan].is);
-        struct IsdnCardState *sp = chanlist[chan].sp;
-        char            tmp[128];
-
-        setstack_teles(st, sp);
-
-        st->l2.sap = 0;
-
-        st->l2.tei = 255;
-
-        st->l2.ces = ces;
-        st->l2.extended = !0;
-        st->l2.laptype = LAPD;
-        st->l2.window = 1;
-        st->l2.orig = !0;
-        st->l2.t200 = 1000;               /* 1000 milliseconds  */
-        if (st->protocol == ISDN_PTYPE_1TR6) {
-                st->l2.n200 = 3;          /* try 3 times        */
-                st->l2.t203 = 10000;      /* 10000 milliseconds */
-        } else {
-                st->l2.n200 = 4;          /* try 4 times        */
-                st->l2.t203 = 5000;       /* 5000 milliseconds  */
-        }
-
-        sprintf(tmp, "Channel %d q.921", chan);
-        setstack_isdnl2(st, tmp);
-        setstack_isdnl3(st);
-        st->l2.debug = 2;
-        st->l3.debug = 2;
-        st->l2.debug = 0xff;
-        st->l3.debug = 0xff;
-        st->l4.userdata = chanlist + chan;
-        st->l4.l2writewakeup = NULL;
-
-        st->l3.l3l4 = ll_handler;
-        st->l1.l1man = cc_l1man;
-        st->l2.l2man = cc_l2man;
-
-        st->pa = &chanlist[chan].para;
-        teles_addlist(sp, st);
-}
-
-static void
-callc_debug(struct FsmInst *fi, char *s)
-{
-        char            str[80], tm[32];
-        struct Channel *chanp = fi->userdata;
-
-        jiftime(tm, jiffies);
-        sprintf(str, "%s Channel %d callc %s\n", tm, chanp->chan, s);
-        teles_putstatus(str);
-}
-
-static void
-lc_debug(struct FsmInst *fi, char *s)
-{
-        char            str[256], tm[32];
-        struct LcFsm   *lf = fi->userdata;
-
-        jiftime(tm, jiffies);
-        sprintf(str, "%s Channel %d lc %s\n", tm, lf->ch->chan, s);
-        teles_putstatus(str);
-}
-
-static void
-dlc_debug(struct FsmInst *fi, char *s)
-{
-        char            str[256], tm[32];
-        struct LcFsm   *lf = fi->userdata;
-
-        jiftime(tm, jiffies);
-        sprintf(str, "%s Channel %d dlc %s\n", tm, lf->ch->chan, s);
-        teles_putstatus(str);
-}
-
-static void
-lccall_d(struct LcFsm *lf, int pr, void *arg)
-{
-        struct Channel *chanp = lf->ch;
-
-        switch (pr) {
-          case (LC_ESTABLISH):
-                  FsmEvent(&chanp->fi, EV_DLEST, NULL);
-                  break;
-          case (LC_RELEASE):
-                  FsmEvent(&chanp->fi, EV_DLRL, NULL);
-                  break;
-        }
-}
-
-static void
-lccall_b(struct LcFsm *lf, int pr, void *arg)
-{
-        struct Channel *chanp = lf->ch;
-
-        switch (pr) {
-          case (LC_ESTABLISH):
-                  FsmEvent(&chanp->fi, EV_BC_EST, NULL);
-                  break;
-          case (LC_RELEASE):
-                  FsmEvent(&chanp->fi, EV_BC_REL, NULL);
-                  break;
-        }
-}
-
-static void
-init_chan(int chan, int cardnr, int hscx,
-          unsigned int ces)
-{
-        struct IsdnCard *card = cards + cardnr;
-        struct Channel *chanp = chanlist + chan;
-
-        chanp->sp = card->sp;
-        chanp->hscx = hscx;
-        chanp->chan = chan;
-        chanp->incoming = 0;
-        chanp->debug = 0;
-        init_is(chan, ces);
-
-        chanp->fi.fsm = &callcfsm;
-        chanp->fi.state = ST_NULL;
-        chanp->fi.debug = 0;
-        chanp->fi.userdata = chanp;
-        chanp->fi.printdebug = callc_debug;
-
-        chanp->lc_d.lcfi.fsm = &lcfsm;
-        chanp->lc_d.lcfi.state = ST_LC_NULL;
-        chanp->lc_d.lcfi.debug = 0;
-        chanp->lc_d.lcfi.userdata = &chanp->lc_d;
-        chanp->lc_d.lcfi.printdebug = lc_debug;
-        chanp->lc_d.type = LC_D;
-        chanp->lc_d.ch = chanp;
-        chanp->lc_d.st = &chanp->is;
-        chanp->lc_d.l2_establish = !0;
-        chanp->lc_d.l2_start = !0;
-        chanp->lc_d.lccall = lccall_d;
-        FsmInitTimer(&chanp->lc_d.lcfi, &chanp->lc_d.act_timer);
-
-        chanp->lc_b.lcfi.fsm = &lcfsm;
-        chanp->lc_b.lcfi.state = ST_LC_NULL;
-        chanp->lc_b.lcfi.debug = 0;
-        chanp->lc_b.lcfi.userdata = &chanp->lc_b;
-        chanp->lc_b.lcfi.printdebug = dlc_debug;
-        chanp->lc_b.type = LC_B;
-        chanp->lc_b.ch = chanp;
-        chanp->lc_b.st = &chanp->ds;
-        chanp->lc_b.l2_establish = !0;
-        chanp->lc_b.l2_start = !0;
-        chanp->lc_b.lccall = lccall_b;
-        FsmInitTimer(&chanp->lc_b.lcfi, &chanp->lc_b.act_timer);
-
-        chanp->outcallref = 64;
-        chanp->data_open = 0;
-}
-
-int
-CallcNewChan(void)
-{
-        int             i, ces, c;
-
-        chancount = 0;
-        for (i = 0; i < nrcards; i++)
-                if (cards[i].sp)
-                        chancount += 2;
-
-        chanlist = (struct Channel *) Smalloc(sizeof(struct Channel) *
-                                      chancount, GFP_KERNEL, "chanlist");
-
-        c = 0;
-        ces = randomces();
-        for (i = 0; i < nrcards; i++)
-                if (cards[i].sp) {
-                        init_chan(c++, i, 1, ces++);
-                        ces %= 0xffff;
-                        init_chan(c++, i, 0, ces++);
-                        ces %= 0xffff;
-                }
-        printk(KERN_INFO "channels %d\n", chancount);
-        return (chancount);
-
-}
-
-static void
-release_is(int chan)
-{
-        struct PStack  *st = &chanlist[chan].is;
-
-        releasestack_isdnl2(st);
-        teles_rmlist(st->l1.hardware, st);
-        BufQueueRelease(&st->l2.i_queue);
-}
-
-void
-CallcFreeChan(void)
-{
-        int             i;
-
-        for (i = 0; i < chancount; i++)
-                release_is(i);
-        Sfree((void *) chanlist);
-}
-
-static void
-lldata_handler(struct PStack *st, int pr,
-               void *arg)
-{
-        struct Channel *chanp = (struct Channel *) st->l4.userdata;
-        byte           *ptr;
-        int             size;
-        struct BufHeader *ibh = arg;
-
-        switch (pr) {
-          case (DL_DATA):
-                  if (chanp->data_open) {
-                          ptr = DATAPTR(ibh);
-                          ptr += chanp->ds.l2.ihsize;
-                          size = ibh->datasize - chanp->ds.l2.ihsize;
-                          iif.rcvcallb(drid, chanp->chan, ptr, size);
-                  }
-                  BufPoolRelease(ibh);
-                  break;
-          default:
-                  printk(KERN_WARNING "lldata_handler unknown primitive\n");
-                  break;
-        }
-}
-
-static void
-lltrans_handler(struct PStack *st, int pr,
-                struct BufHeader *ibh)
-{
-        struct Channel *chanp = (struct Channel *) st->l4.userdata;
-        byte           *ptr;
-
-        switch (pr) {
-          case (PH_DATA):
-                  if (chanp->data_open) {
-                          ptr = DATAPTR(ibh);
-                          iif.rcvcallb(drid, chanp->chan, ptr, ibh->datasize);
-                  }
-                  BufPoolRelease(ibh);
-                  break;
-          default:
-                  printk(KERN_WARNING "lltrans_handler unknown primitive\n");
-                  break;
-        }
-}
-
-static void
-ll_writewakeup(struct PStack *st)
-{
-        struct Channel *chanp = st->l4.userdata;
-        isdn_ctrl       ic;
-
-        ic.driver = drid;
-        ic.command = ISDN_STAT_BSENT;
-        ic.arg = chanp->chan;
-        iif.statcallb(&ic);
-}
-
-static int
-init_ds(int chan, int incoming)
-{
-        struct PStack  *st = &(chanlist[chan].ds);
-        struct IsdnCardState *sp = (struct IsdnCardState *)
-        chanlist[chan].is.l1.hardware;
-        struct HscxState *hsp = sp->hs + chanlist[chan].hscx;
-        char            tmp[128];
-
-        st->l1.hardware = sp;
-
-        hsp->mode = 2;
-        hsp->transbufsize = 4000;
-
-        if (setstack_hscx(st, hsp))
-                return (-1);
-
-        st->l2.extended = 0;
-        st->l2.laptype = LAPB;
-        st->l2.orig = !incoming;
-        st->l2.t200 = 1000;        /* 1000 milliseconds */
-        st->l2.window = 3;
-        st->l2.n200 = 4;           /* try 4 times       */
-        st->l2.t203 = 5000;        /* 5000 milliseconds */
-
-        st->l2.debug = 0xff;
-        st->l3.debug = 0xff;
-        switch (chanlist[chan].l2_active_protocol) {
-          case (ISDN_PROTO_L2_X75I):
-                  sprintf(tmp, "Channel %d x.75", chan);
-                  setstack_isdnl2(st, tmp);
-                  st->l2.l2l3 = lldata_handler;
-                  st->l1.l1man = dcc_l1man;
-                  st->l2.l2man = dcc_l2man;
-                  st->l4.userdata = chanlist + chan;
-                  st->l4.l1writewakeup = NULL;
-                  st->l4.l2writewakeup = ll_writewakeup;
-                  st->l2.l2m.debug = debugflags & 16;
-                  st->ma.manl2(st, MDL_NOTEIPROC, NULL);
-                  st->l1.hscxmode = 2;        /* Packet-Mode ? */
-                  st->l1.hscxchannel = chanlist[chan].para.bchannel - 1;
-                  break;
-          case (ISDN_PROTO_L2_HDLC):
-                  st->l1.l1l2 = lltrans_handler;
-                  st->l1.l1man = dcc_l1man;
-                  st->l4.userdata = chanlist + chan;
-                  st->l4.l1writewakeup = ll_writewakeup;
-                  st->l1.hscxmode = 2;
-                  st->l1.hscxchannel = chanlist[chan].para.bchannel - 1;
-                  break;
-          case (ISDN_PROTO_L2_TRANS):
-                  st->l1.l1l2 = lltrans_handler;
-                  st->l1.l1man = dcc_l1man;
-                  st->l4.userdata = chanlist + chan;
-                  st->l4.l1writewakeup = ll_writewakeup;
-                  st->l1.hscxmode = 1;
-                  st->l1.hscxchannel = chanlist[chan].para.bchannel - 1;
-                  break;
-        }
-
-        return (0);
-
-}
-
-static void
-channel_report(int i)
-{
-}
-
-static void
-command_debug(struct Channel *chanp, char *s)
-{
-        char            tmp[64], tm[32];
-
-        jiftime(tm, jiffies);
-        sprintf(tmp, "%s Channel %d LL->HL %s\n", tm, chanp->chan, s);
-        teles_putstatus(tmp);
-}
-
-static void
-distr_debug(void)
-{
-        int             i;
-
-        for (i = 0; i < chancount; i++) {
-                chanlist[i].debug = debugflags & 1;
-                chanlist[i].fi.debug = debugflags & 2;
-                chanlist[i].is.l2.l2m.debug = debugflags & 8;
-                chanlist[i].ds.l2.l2m.debug = debugflags & 16;
-        }
-        for (i = 0; i < nrcards; i++)
-                if (cards[i].sp) {
-                        cards[i].sp->dlogflag = debugflags & 4;
-                        cards[i].sp->debug = debugflags & 32;
-                }
-}
-
-int
-teles_command(isdn_ctrl * ic)
-{
-        struct Channel *chanp;
-        char            tmp[64];
-        int             i;
-        unsigned int    num;
-
-        switch (ic->command) {
-          case (ISDN_CMD_SETEAZ):
-                  chanp = chanlist + ic->arg;
-                  if (chanp->debug & 1)
-                          command_debug(chanp, "SETEAZ");
-                  return (0);
-          case (ISDN_CMD_DIAL):
-                  chanp = chanlist + (ic->arg & 0xff);
-                  if (chanp->debug & 1) {
-                          sprintf(tmp, "DIAL %s", ic->num);
-                          command_debug(chanp, tmp);
-                  }
-                  FsmEvent(&chanp->fi, EV_DIAL, ic);
-                  return (0);
-          case (ISDN_CMD_ACCEPTB):
-                  chanp = chanlist + ic->arg;
-                  if (chanp->debug & 1)
-                          command_debug(chanp, "ACCEPTB");
-                  FsmEvent(&chanp->fi, EV_ACCEPTB, NULL);
-                  break;
-          case (ISDN_CMD_ACCEPTD):
-                  chanp = chanlist + ic->arg;
-                  if (chanp->debug & 1)
-                          command_debug(chanp, "ACCEPTD");
-                  FsmEvent(&chanp->fi, EV_ACCEPTD, NULL);
-                  break;
-          case (ISDN_CMD_HANGUP):
-                  chanp = chanlist + ic->arg;
-                  if (chanp->debug & 1)
-                          command_debug(chanp, "HANGUP");
-                  FsmEvent(&chanp->fi, EV_HANGUP, NULL);
-                  break;
-          case (ISDN_CMD_SUSPEND):
-                  chanp = chanlist + ic->arg;
-                  if (chanp->debug & 1) {
-                          sprintf(tmp, "SUSPEND %s", ic->num);
-                          command_debug(chanp, tmp);
-                  }
-                 FsmEvent(&chanp->fi, EV_SUSPEND, ic);
-                  break;
-          case (ISDN_CMD_RESUME):
-                  chanp = chanlist + ic->arg;
-                  if (chanp->debug & 1) {
-                          sprintf(tmp, "RESUME %s", ic->num);
-                          command_debug(chanp, tmp);
-                  }
-                  FsmEvent(&chanp->fi, EV_RESUME, ic);
-                  break;
-          case (ISDN_CMD_LOCK):
-                  teles_mod_inc_use_count();
-                  break;
-          case (ISDN_CMD_UNLOCK):
-                  teles_mod_dec_use_count();
-                  break;
-          case (ISDN_CMD_IOCTL):
-                  switch (ic->arg) {
-                    case (0):
-                            for (i = 0; i < nrcards; i++)
-                                    if (cards[i].sp)
-                                            teles_reportcard(i);
-                            for (i = 0; i < chancount; i++)
-                                    channel_report(i);
-                            break;
-                    case (1):
-                            debugflags = *(unsigned int *) ic->num;
-                            distr_debug();
-                            sprintf(tmp, "debugging flags set to %x\n", debugflags);
-                            teles_putstatus(tmp);
-			    printk(KERN_DEBUG "%s", tmp);
-                            break;
-                    case (2):
-                            num = *(unsigned int *) ic->num;
-                            i = num >> 8;
-                            if (i >= chancount)
-                                    break;
-                            chanp = chanlist + i;
-                            chanp->impair = num & 0xff;
-                            if (chanp->debug & 1) {
-                                    sprintf(tmp, "IMPAIR %x", chanp->impair);
-                                    command_debug(chanp, tmp);
-                            }
-                            break;
-                  }
-                  break;
-          case (ISDN_CMD_SETL2):
-                  chanp = chanlist + (ic->arg & 0xff);
-                  if (chanp->debug & 1) {
-                          sprintf(tmp, "SETL2 %ld", ic->arg >> 8);
-                          command_debug(chanp, tmp);
-                  }
-                  chanp->l2_protocol = ic->arg >> 8;
-                  break;
-          default:
-                  break;
-        }
-
-        return (0);
-}
-
-int
-teles_writebuf(int id, int chan, const u_char * buf, int count, int user)
-{
-        struct Channel *chanp = chanlist + chan;
-        struct PStack  *st = &chanp->ds;
-        struct BufHeader *ibh;
-        int             err, i;
-        byte           *ptr;
-
-	if (!chanp->data_open) {
-		printk(KERN_DEBUG "teles_writebuf: channel not open\n");
-		return -EIO;
-	}
-	
-        err = BufPoolGet(&ibh, st->l1.sbufpool, GFP_ATOMIC, st, 21);
-        if (err)
-                /* Must return 0 here, since this is not an error
-                 * but a temporary lack of resources.
-                 */
-                return 0;
-
-        ptr = DATAPTR(ibh);
-        if (chanp->lc_b.l2_establish)
-                i = st->l2.ihsize;
-        else
-                i = 0;
-
-        if ((count+i) > BUFFER_SIZE(HSCX_SBUF_ORDER, HSCX_SBUF_BPPS)) {
-                printk(KERN_WARNING "teles_writebuf: packet too large!\n");
-                return (-EINVAL);
-        }
-
-        ptr += i;
-
-        if (user)
-                copy_from_user(ptr, buf, count);
-        else
-                memcpy(ptr, buf, count);
-        ibh->datasize = count + i;
-
-        if (chanp->data_open) {
-                if (chanp->lc_b.l2_establish)
-                        chanp->ds.l3.l3l2(&chanp->ds, DL_DATA, ibh);
-                else
-                        chanp->ds.l2.l2l1(&chanp->ds, PH_DATA, ibh);
-                return (count);
-        } else {
-                BufPoolRelease(ibh);
-                return (0);
-        }
-
-}
-
-static char    *
-strcpyupto(char *dest, char *src, char upto)
-{
-        while (*src && (*src != upto) && (*src != '\0'))
-                *dest++ = *src++;
-        *dest = '\0';
-        return (src);
-}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/card.c linux/drivers/isdn/teles/card.c
--- v2.0.30/linux/drivers/isdn/teles/card.c	Tue Nov 12 22:36:20 1996
+++ linux/drivers/isdn/teles/card.c	Wed Dec 31 16:00:00 1969
@@ -1,1900 +0,0 @@
-/* $Id: card.c,v 1.16 1996/10/22 23:14:16 fritz Exp $
- *
- * card.c     low level stuff for the Teles S0 isdn card
- * 
- * Author     Jan den Ouden
- * 
- * Beat Doebeli         log all D channel traffic
- * 
- * $Log: card.c,v $
- * Revision 1.16  1996/10/22 23:14:16  fritz
- * Changes for compatibility to 2.0.X and 2.1.X kernels.
- *
- * Revision 1.15  1996/09/29 19:41:56  fritz
- * Bugfix: ignore unknown frames.
- *
- * Revision 1.14  1996/09/23 01:53:49  fritz
- * Bugfix: discard unknown frames (non-EDSS1 and non-1TR6).
- *
- * Revision 1.13  1996/07/18 11:21:24  jdenoud
- * Use small buffers for incoming audio data
- *
- * Revision 1.12  1996/06/24 17:16:52  fritz
- * Added check for misconfigured membase.
- *
- * Revision 1.11  1996/06/14 03:30:37  fritz
- * Added recovery from EXIR 40 interrupt.
- * Some cleanup.
- *
- * Revision 1.10  1996/06/11 14:57:20  hipp
- * minor changes to ensure, that SKBs are sent in the right order
- *
- * Revision 1.9  1996/06/06 14:42:09  fritz
- * Bugfix: forgot hsp-> in last change.
- *
- * Revision 1.7  1996/05/31 01:02:21  fritz
- * Cosmetic changes.
- *
- * Revision 1.6  1996/05/26 14:58:10  fritz
- * Bugfix: Did not show port correctly, when no card found.
- *
- * Revision 1.5  1996/05/17 03:45:02  fritz
- * Made error messages more clearly.
- * Bugfix: Only 31 bytes of 32-byte audio frames
- *         have been transfered to upper layers.
- *
- * Revision 1.4  1996/05/06 10:17:57  fritz
- * Added voice-send stuff
- *  (Not reporting EXIR when in voice-mode, since it's normal).
- *
- * Revision 1.3  1996/04/30 22:02:40  isdn4dev
- * Bugfixes for 16.3
- *     -improved IO allocation
- *     -fix second B channel problem
- *     -correct ph_command patch
- *
- * Revision 1.2  1996/04/30 10:00:59  fritz
- * Bugfix: Added ph_command(8) for 16.3.
- * Bugfix: Ports did not get registered correctly
- *         when using a 16.3.
- *         Started voice support.
- *         Some experimental changes of waitforXFW().
- *
- * Revision 1.1  1996/04/13 10:22:42  fritz
- * Initial revision
- *
- *
- */
-
-#define __NO_VERSION__
-#include "teles.h"
-#include "proto.h"
-
-#define INCLUDE_INLINE_FUNCS
-#include <linux/tqueue.h>
-#include <linux/interrupt.h>
-
-#undef DCHAN_VERBOSE
-
-extern void     tei_handler(struct PStack *st, byte pr,
-			    struct BufHeader *ibh);
-extern struct   IsdnCard cards[];
-extern int      nrcards;
-
-#define byteout(addr,val) outb_p(val,addr)
-#define bytein(addr) inb_p(addr)
-
-static inline   byte
-readisac_0(byte * cardm, byte offset)
-{
-	return readb(cardm + 0x100 + ((offset & 1) ? 0x1ff : 0) + offset);
-}
-
-static inline   byte
-readisac_3(int iobase, byte offset)
-{
-        return (bytein(iobase - 0x420 + offset));
-}
-
-#define READISAC(mbase,ibase,ofs) \
-        ((mbase)?readisac_0(mbase,ofs):readisac_3(ibase,ofs))
-
-static inline void
-writeisac_0(byte * cardm, byte offset, byte value)
-{
-	writeb(value, cardm + 0x100 + ((offset & 1) ? 0x1ff : 0) + offset);
-}
-
-static inline void
-writeisac_3(int iobase, byte offset, byte value)
-{
-	byteout(iobase - 0x420 + offset, value);
-}
-
-#define WRITEISAC(mbase,ibase,ofs,val) \
-        ((mbase)?writeisac_0(mbase,ofs,val):writeisac_3(ibase,ofs,val))
-
-static inline void
-readisac_s(int iobase, byte offset, byte * dest, int count)
-{
-	insb(iobase - 0x420 + offset, dest, count);
-}
-
-static inline void
-writeisac_s(int iobase, byte offset, byte * src, int count)
-{
-	outsb(iobase - 0x420 + offset, src, count);
-}
-
-static inline   byte
-readhscx_0(byte * base, byte hscx, byte offset)
-{
-	return readb(base + 0x180 + ((offset & 1) ? 0x1FF : 0) +
-			  ((hscx & 1) ? 0x40 : 0) + offset);
-}
-
-static inline   byte
-readhscx_3(int iobase, byte hscx, byte offset)
-{
-	return (bytein(iobase - (hscx ? 0x820 : 0xc20) + offset));
-}
-
-#define READHSCX(mbase,ibase,hscx,ofs) \
-        ((mbase)?readhscx_0(mbase,hscx,ofs):readhscx_3(ibase,hscx,ofs))
-
-static inline void
-writehscx_0(byte * base, byte hscx, byte offset, byte data)
-{
-	writeb(data, base + 0x180 + ((offset & 1) ? 0x1FF : 0) +
-		   ((hscx & 1) ? 0x40 : 0) + offset);
-}
-
-static inline void
-writehscx_3(int iobase, byte hscx, byte offset, byte data)
-{
-	byteout(iobase - (hscx ? 0x820 : 0xc20) + offset, data);
-}
-
-static inline void
-readhscx_s(int iobase, byte hscx, byte offset, byte * dest, int count)
-{
-	insb(iobase - (hscx ? 0x820 : 0xc20) + offset, dest, count);
-}
-
-static inline void
-writehscx_s(int iobase, byte hscx, byte offset, byte * src, int count)
-{
-	outsb(iobase - (hscx ? 0x820 : 0xc20) + offset, src, count);
-}
-
-#define ISAC_MASK 0x20
-#define ISAC_ISTA 0x20
-#define ISAC_STAR 0x21
-#define ISAC_CMDR 0x21
-#define ISAC_EXIR 0x24
-
-#define ISAC_RBCH 0x2a
-
-#define ISAC_ADF2 0x39
-#define ISAC_SPCR 0x30
-#define ISAC_ADF1 0x38
-#define ISAC_CIX0 0x31
-#define ISAC_STCR 0x37
-#define ISAC_MODE 0x22
-#define ISAC_RSTA 0x27
-#define ISAC_RBCL 0x25
-#define ISAC_TIMR 0x23
-#define ISAC_SQXR 0x3b
-
-#define HSCX_ISTA 0x20
-#define HSCX_CCR1 0x2f
-#define HSCX_CCR2 0x2c
-#define HSCX_TSAR 0x31
-#define HSCX_TSAX 0x30
-#define HSCX_XCCR 0x32
-#define HSCX_RCCR 0x33
-#define HSCX_MODE 0x22
-#define HSCX_CMDR 0x21
-#define HSCX_EXIR 0x24
-#define HSCX_XAD1 0x24
-#define HSCX_XAD2 0x25
-#define HSCX_RAH2 0x27
-#define HSCX_RSTA 0x27
-#define HSCX_TIMR 0x23
-#define HSCX_STAR 0x21
-#define HSCX_RBCL 0x25
-#define HSCX_XBCH 0x2d
-#define HSCX_VSTR 0x2e
-#define HSCX_RLCR 0x2e
-#define HSCX_MASK 0x20
-
-static inline void
-waitforCEC_0(byte * base, byte hscx)
-{
-	long            to = 10;
-
-	while ((readhscx_0(base, hscx, HSCX_STAR) & 0x04) && to) {
-		udelay(5);
-		to--;
-	}
-	if (!to)
-		printk(KERN_WARNING "waitforCEC timeout\n");
-}
-
-static inline void
-waitforCEC_3(int iobase, byte hscx)
-{
-	long            to = 10;
-
-	while ((readhscx_3(iobase, hscx, HSCX_STAR) & 0x04) && to) {
-		udelay(5);
-		to--;
-	}
-	if (!to)
-		printk(KERN_WARNING "waitforCEC timeout\n");
-}
-
-static inline void
-waitforXFW_0(byte * base, byte hscx)
-{
-	long            to = 20;
-
-	while ((!(readhscx_0(base, hscx, HSCX_STAR) & 0x44)==0x40) && to) {
-		udelay(5);
-		to--;
-	}
-	if (!to)
-		printk(KERN_WARNING "waitforXFW timeout\n");
-}
-
-static inline void
-waitforXFW_3(int iobase, byte hscx)
-{
-	long            to = 20;
-
-	while ((!(readhscx_3(iobase, hscx, HSCX_STAR) & 0x44)==0x40) && to) {
-		udelay(5);
-		to--;
-	}
-	if (!to)
-		printk(KERN_WARNING "waitforXFW timeout\n");
-}
-
-static inline void
-writehscxCMDR_0(byte * base, byte hscx, byte data)
-{
-	long            flags;
-
-	save_flags(flags);
-	cli();
-	waitforCEC_0(base, hscx);
-	writehscx_0(base, hscx, HSCX_CMDR, data);
-	restore_flags(flags);
-}
-
-static inline void
-writehscxCMDR_3(int iobase, byte hscx, byte data)
-{
-	long            flags;
-
-	save_flags(flags);
-	cli();
-	waitforCEC_3(iobase, hscx);
-	writehscx_3(iobase, hscx, HSCX_CMDR, data);
-	restore_flags(flags);
-}
-
-#define WRITEHSCX_CMDR(mbase,ibase,hscx,data) \
-        ((mbase)?writehscxCMDR_0(mbase,hscx,data):writehscxCMDR_3(ibase,hscx,data))
-
-/*
- * fast interrupt here
- */
-
-#define ISAC_RCVBUFREADY 0
-#define ISAC_XMTBUFREADY 1
-#define ISAC_PHCHANGE    2
-
-#define HSCX_RCVBUFREADY 0
-#define HSCX_XMTBUFREADY 1
-
-void
-teles_hscxreport(struct IsdnCardState *sp, int hscx)
-{
-        printk(KERN_DEBUG "HSCX %d\n", hscx);
-        if (sp->membase) {
-                printk(KERN_DEBUG "  ISTA %x\n", readhscx_0(sp->membase,
-                                                          hscx, HSCX_ISTA));
-                printk(KERN_DEBUG "  STAR %x\n", readhscx_0(sp->membase,
-                                                          hscx, HSCX_STAR));
-                printk(KERN_DEBUG "  EXIR %x\n", readhscx_0(sp->membase,
-                                                          hscx, HSCX_EXIR));
-        } else {
-                printk(KERN_DEBUG "  ISTA %x\n", readhscx_3(sp->iobase,
-                                                          hscx, HSCX_ISTA));
-                printk(KERN_DEBUG "  STAR %x\n", readhscx_3(sp->iobase,
-                                                          hscx, HSCX_STAR));
-                printk(KERN_DEBUG "  EXIR %x\n", readhscx_3(sp->iobase,
-                                                          hscx, HSCX_EXIR));
-        }
-}
-
-void
-teles_report(struct IsdnCardState *sp)
-{
-	printk(KERN_DEBUG "ISAC\n");
-        if (sp->membase) {
-               printk(KERN_DEBUG "  ISTA %x\n", readisac_0(sp->membase,
-                                                            ISAC_ISTA));
-               printk(KERN_DEBUG "  STAR %x\n", readisac_0(sp->membase,
-                                                           ISAC_STAR));
-               printk(KERN_DEBUG "  EXIR %x\n", readisac_0(sp->membase,
-                                                           ISAC_EXIR));
-        } else {
-                printk(KERN_DEBUG "  ISTA %x\n", readisac_3(sp->iobase,
-                                                          ISAC_ISTA));
-                printk(KERN_DEBUG "  STAR %x\n", readisac_3(sp->iobase,
-                                                          ISAC_STAR));
-                printk(KERN_DEBUG "  EXIR %x\n", readisac_3(sp->iobase,
-                                                          ISAC_EXIR));
-        }
-	teles_hscxreport(sp, 0);
-	teles_hscxreport(sp, 1);
-}
-
-/*
- * HSCX stuff goes here
- */
-
-static void
-hscx_sched_event(struct HscxState *hsp, int event)
-{
-	hsp->event |= 1 << event;
-	queue_task_irq_off(&hsp->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
-}
-
-static void
-hscx_empty_fifo(struct HscxState *hsp, int count)
-{
-	byte             *ptr;
-	struct BufHeader *ibh = hsp->rcvibh;
-
-	if (hsp->sp->debug)
-		printk(KERN_DEBUG "hscx_empty_fifo\n");
-
-	if (hsp->rcvptr + count > BUFFER_SIZE(HSCX_RBUF_ORDER,
-					      HSCX_RBUF_BPPS)) {
-		printk(KERN_WARNING
-                       "hscx_empty_fifo: incoming packet too large\n");
-		WRITEHSCX_CMDR(hsp->membase, hsp->iobase, hsp->hscx, 0x80);
-		return;
-	}
-	ptr = DATAPTR(ibh);
-	ptr += hsp->rcvptr;
-
-	hsp->rcvptr += count;
-        if (hsp->membase) {
-                while (count--)
-                        *ptr++ = readhscx_0(hsp->membase, hsp->hscx, 0x0);
-                writehscxCMDR_0(hsp->membase, hsp->hscx, 0x80);
-        } else {
-                readhscx_s(hsp->iobase, hsp->hscx, 0x3e, ptr, count);
-                writehscxCMDR_3(hsp->iobase, hsp->hscx, 0x80);
-        }
-}
-
-static void
-hscx_fill_fifo(struct HscxState *hsp)
-{
-	struct BufHeader *ibh;
-	int              more, count;
-	byte             *ptr;
-
-	if (hsp->sp->debug)
-		printk(KERN_DEBUG "hscx_fill_fifo\n");
-
-	ibh = hsp->xmtibh;
-	if (!ibh)
-                return;
-
-	count = ibh->datasize - hsp->sendptr;
-	if (count <= 0)
-                return;
-
-	more = (hsp->mode == 1)?1:0;
-	if (count > 32) {
-		more = !0;
-		count = 32;
-	}
-	ptr = DATAPTR(ibh);
-	ptr += hsp->sendptr;
-	hsp->sendptr += count;
-
-#ifdef BCHAN_VERBOSE
-        {
-                int i;
-                printk(KERN_DEBUG "hscx_fill_fifo ");
-                for (i = 0; i < count; i++)
-                        printk(" %2x", ptr[i]);
-                printk("\n");
-        }
-#endif
-        if (hsp->membase) {
-                waitforXFW_0(hsp->membase, hsp->hscx);
-                while (count--)
-                        writehscx_0(hsp->membase, hsp->hscx, 0x0, *ptr++);
-                writehscxCMDR_0(hsp->membase, hsp->hscx, more ? 0x8 : 0xa);
-        } else {
-                waitforXFW_3(hsp->iobase, hsp->hscx);
-                writehscx_s(hsp->iobase, hsp->hscx, 0x3e, ptr, count);
-                writehscxCMDR_3(hsp->iobase, hsp->hscx, more ? 0x8 : 0xa);
-        }
-}
-
-static inline void
-hscx_interrupt(struct IsdnCardState *sp, byte val, byte hscx)
-{
-	byte             r;
-	struct HscxState *hsp = sp->hs + hscx;
-	int              count, err;
-
-	if (!hsp->init)
-		return;
-
-	if (val & 0x80) {	/* RME */
-
-		r = READHSCX(hsp->membase, sp->iobase, hsp->hscx, HSCX_RSTA);
-		if ((r & 0xf0) != 0xa0) {
-			if (!r & 0x80)
-				printk(KERN_WARNING
-                                       "Teles: HSCX invalid frame\n");
-			if ((r & 0x40) && hsp->mode)
-				printk(KERN_WARNING "Teles: HSCX RDO mode=%d\n",hsp->mode);
-			if (!r & 0x20)
-				printk(KERN_WARNING "Teles: HSCX CRC error\n");
-			if (hsp->rcvibh)
-				BufPoolRelease(hsp->rcvibh);
-			hsp->rcvibh = NULL;
-			WRITEHSCX_CMDR(hsp->membase, hsp->iobase, hsp->hscx,
-                                       0x80);
-			goto afterRME;
-		}
-		if (!hsp->rcvibh)
-			if (BufPoolGet(&hsp->rcvibh, &hsp->rbufpool,
-                                       GFP_ATOMIC, (void *) 1, 1)) {
-				printk(KERN_WARNING
-                                       "HSCX RME out of buffers at %ld\n",
-                                       jiffies);
-				WRITEHSCX_CMDR(hsp->membase, hsp->iobase,
-                                               hsp->hscx, 0x80);
-				goto afterRME;
-			} else
-				hsp->rcvptr = 0;
-
-		count = READHSCX(hsp->membase, sp->iobase, hsp->hscx,
-                                 HSCX_RBCL) & 0x1f;
-		if (count == 0)
-			count = 32;
-		hscx_empty_fifo(hsp, count);
-		hsp->rcvibh->datasize = hsp->rcvptr - 1;
-		BufQueueLink(&hsp->rq, hsp->rcvibh);
-		hsp->rcvibh = NULL;
-		hscx_sched_event(hsp, HSCX_RCVBUFREADY);
-	}
-      afterRME:
-	if (val & 0x40) {	/* RPF */
-		if (!hsp->rcvibh) {
-			if (hsp->mode == 1)
-				err=BufPoolGet(&hsp->rcvibh, &hsp->smallpool,
-					GFP_ATOMIC, (void *)1, 2);
-			else
-				err=BufPoolGet(&hsp->rcvibh, &hsp->rbufpool,
-					GFP_ATOMIC, (void *)1, 2);
-			
-			if (err) {
-				printk(KERN_WARNING
-                                       "HSCX RPF out of buffers at %ld\n",
-                                       jiffies);
-				WRITEHSCX_CMDR(hsp->membase, hsp->iobase,
-                                               hsp->hscx, 0x80);
-				goto afterRPF;
-			} else
-				hsp->rcvptr = 0;
-		}
-		
-		hscx_empty_fifo(hsp, 32);
-                if (hsp->mode == 1) {
-                        /* receive audio data */
-                        hsp->rcvibh->datasize = hsp->rcvptr;
-                        BufQueueLink(&hsp->rq, hsp->rcvibh);
-                        hsp->rcvibh = NULL;
-                        hscx_sched_event(hsp, HSCX_RCVBUFREADY);
-                }
-                
-	}
-      afterRPF:
-	if (val & 0x10) {	/* XPR */
-		if (hsp->xmtibh)
-			if (hsp->xmtibh->datasize > hsp->sendptr) {
-				hscx_fill_fifo(hsp);
-				goto afterXPR;
-			} else {
-				if (hsp->releasebuf)
-					BufPoolRelease(hsp->xmtibh);
-				hsp->sendptr = 0;
-				if (hsp->st->l4.l1writewakeup)
-					hsp->st->l4.l1writewakeup(hsp->st);
-				hsp->xmtibh = NULL;
-			}
-		if (!BufQueueUnlink(&hsp->xmtibh, &hsp->sq)) {
-			hsp->releasebuf = !0;
-			hscx_fill_fifo(hsp);
-		} else
-			hscx_sched_event(hsp, HSCX_XMTBUFREADY);
-	}
-      afterXPR:
-}
-
-/*
- * ISAC stuff goes here
- */
-
-static void
-isac_sched_event(struct IsdnCardState *sp, int event)
-{
-	sp->event |= 1 << event;
-	queue_task_irq_off(&sp->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
-}
-
-static void
-empty_fifo(struct IsdnCardState *sp, int count)
-{
-	byte             *ptr;
-	struct BufHeader *ibh = sp->rcvibh;
-
-	if (sp->debug)
-		printk(KERN_DEBUG "empty_fifo\n");
-
-	if (sp->rcvptr >= 3072) {
-		printk(KERN_WARNING "empty_fifo rcvptr %d\n", sp->rcvptr);
-		return;
-	}
-	ptr = DATAPTR(ibh);
-	ptr += sp->rcvptr;
-	sp->rcvptr += count;
-
-        if (sp->membase) {
-#ifdef DCHAN_VERBOSE
-                printk(KERN_DEBUG "empty_fifo ");
-                while (count--) {
-                        *ptr = readisac_0(sp->membase, 0x0);
-                        printk("%2x ", *ptr);
-                        ptr++;
-                }
-                printk("\n");
-#else
-                while (count--)
-                        *ptr++ = readisac_0(sp->membase, 0x0);
-#endif
-                writeisac_0(sp->membase, ISAC_CMDR, 0x80);
-        } else {
-#ifdef DCHAN_VERBOSE
-                int i;
-                printk(KERN_DEBUG "empty_fifo ");
-                readisac_s(sp->iobase, 0x3e, ptr, count);
-                for (i = 0; i < count; i++)
-                        printk("%2x ", ptr[i]);
-                printk("\n");
-#else
-                readisac_s(sp->iobase, 0x3e, ptr, count);
-#endif
-                writeisac_3(sp->iobase, ISAC_CMDR, 0x80);
-        }
-}
-
-static void
-fill_fifo(struct IsdnCardState *sp)
-{
-	struct BufHeader *ibh;
-	int              count, more;
-	byte             *ptr;
-
-	if (sp->debug)
-		printk(KERN_DEBUG "fill_fifo\n");
-
-	ibh = sp->xmtibh;
-	if (!ibh)
-		return;
-
-	count = ibh->datasize - sp->sendptr;
-	if (count <= 0)
-		return;
-	if (count >= 3072)
-		return;
-
-	more = 0;
-	if (count > 32) {
-		more = !0;
-		count = 32;
-	}
-	ptr = DATAPTR(ibh);
-	ptr += sp->sendptr;
-	sp->sendptr += count;
-
-        if (sp->membase) {
-#ifdef DCHAN_VERBOSE
-                printk(KERN_DEBUG "fill_fifo ");
-                while (count--) {
-                        writeisac_0(sp->membase, 0x0, *ptr);
-                        printk("%2x ", *ptr);
-                        ptr++;
-                }
-                printk("\n");
-#else
-                while (count--)
-                        writeisac_0(sp->membase, 0x0, *ptr++);
-#endif
-                writeisac_0(sp->membase, ISAC_CMDR, more ? 0x8 : 0xa);
-        } else {
-#ifdef DCHAN_VERBOSE
-                int i;
-                writeisac_s(sp->iobase, 0x3e, ptr, count);
-                printk(KERN_DEBUG "fill_fifo ");
-                for (i = 0; i < count; i++)
-                        printk("%2x ", ptr[i]);
-                printk("\n");
-#else
-                writeisac_s(sp->iobase, 0x3e, ptr, count);
-#endif
-                writeisac_3(sp->iobase, ISAC_CMDR, more ? 0x8 : 0xa);
-        }
-}
-
-static int
-act_wanted(struct IsdnCardState *sp)
-{
-	struct PStack  *st;
-
-	st = sp->stlist;
-	while (st)
-		if (st->l1.act_state)
-			return (!0);
-		else
-			st = st->next;
-	return (0);
-}
-
-static void
-ph_command(struct IsdnCardState *sp, unsigned int command)
-{
-	printk(KERN_DEBUG "ph_command %d\n", command);
-	WRITEISAC(sp->membase, sp->iobase, ISAC_CIX0, (command << 2) | 3);
-}
-
-static void
-isac_new_ph(struct IsdnCardState *sp)
-{
-	int             enq;
-
-	enq = act_wanted(sp);
-
-	switch (sp->ph_state) {
-	  case (0):
-	  case (6):
-		  if (enq)
-			  ph_command(sp, 0);
-		  else
-			  ph_command(sp, 15);
-		  break;
-	  case (7):
-		  if (enq)
-			  ph_command(sp, 9);
-		  break;
-	  case (12):
-	          ph_command(sp, 8);
-		  sp->ph_active = 5;
-		  isac_sched_event(sp, ISAC_PHCHANGE);
-		  if (!sp->xmtibh)
-			  if (!BufQueueUnlink(&sp->xmtibh, &sp->sq))
-				  sp->sendptr = 0;
-		  if (sp->xmtibh)
-			  fill_fifo(sp);
-		  break;
-	  case (13):
-	          ph_command(sp, 9);
-		  sp->ph_active = 5;
-		  isac_sched_event(sp, ISAC_PHCHANGE);
-		  if (!sp->xmtibh)
-			  if (!BufQueueUnlink(&sp->xmtibh, &sp->sq))
-				  sp->sendptr = 0;
-		  if (sp->xmtibh)
-			  fill_fifo(sp);
-		  break;
-	  case (4):
-	  case (8):
-		  break;
-	  default:
-		  sp->ph_active = 0;
-		  break;
-	}
-}
-
-static void
-teles_interrupt(int intno, void *dev_id, struct pt_regs *regs)
-{
-	byte                 val, r, exval;
-	struct IsdnCardState *sp;
-	unsigned int         count;
-	struct HscxState     *hsp;
-
-	sp = (struct IsdnCardState *) irq2dev_map[intno];
-
-	if (!sp) {
-		printk(KERN_WARNING "Teles: Spurious interrupt!\n");
-		return;
-	}
-	val = READHSCX(sp->membase, sp->iobase, 1, HSCX_ISTA);
-
-	if (val & 0x01) {
-		hsp = sp->hs + 1;
-                exval = READHSCX(sp->membase, sp->iobase, 1, HSCX_EXIR);
-                if (exval == 0x40) {
-                        if (hsp->mode == 1)
-                                hscx_fill_fifo(hsp);
-                        else {
-                                /* Here we lost an TX interrupt, so
-                                 * restart transmitting the whole frame.
-                                 */
-                                hsp->sendptr = 0;
-			        WRITEHSCX_CMDR(hsp->membase, hsp->iobase,
-                                               hsp->hscx, 0x01);
-                                printk(KERN_DEBUG "HSCX B EXIR %x\n", exval);
-                        }
-                } else
-                        printk(KERN_WARNING "HSCX B EXIR %x\n", exval);
-	}
-	if (val & 0xf8) {
-		if (sp->debug)
-			printk(KERN_DEBUG "HSCX B interrupt %x\n", val);
-		hscx_interrupt(sp, val, 1);
-	}
-	if (val & 0x02) {
-		hsp = sp->hs;
-                exval = READHSCX(sp->membase, sp->iobase, 0, HSCX_EXIR);
-                if (exval == 0x40) {
-                        if (hsp->mode == 1)
-                                hscx_fill_fifo(hsp);
-                        else {
-                                /* Here we lost an TX interrupt, so
-                                 * restart transmitting the whole frame.
-                                 */
-                                hsp->sendptr = 0;
-			        WRITEHSCX_CMDR(hsp->membase, hsp->iobase,
-                                               hsp->hscx, 0x01);
-                                printk(KERN_DEBUG "HSCX A EXIR %x\n", exval);
-                        }
-                } else
-                        printk(KERN_WARNING "HSCX A EXIR %x\n", exval);
-	}
-        if (val & 0x04) {
-                val = READHSCX(sp->membase, sp->iobase, 0, HSCX_ISTA);
-                if (sp->debug)
-                        printk(KERN_DEBUG "HSCX A interrupt %x\n",
-                               val);
-                hscx_interrupt(sp, val, 0);
-        }
-
-	val = READISAC(sp->membase, sp->iobase, ISAC_ISTA);
-
-	if (sp->debug)
-		printk(KERN_DEBUG "ISAC interrupt %x\n", val);
-
-	if (val & 0x80) {	/* RME */
-
-		r = READISAC(sp->membase, sp->iobase, ISAC_RSTA);
-		if ((r & 0x70) != 0x20) {
-			if (r & 0x40)
-				printk(KERN_WARNING "Teles: ISAC RDO\n");
-			if (!r & 0x20)
-				printk(KERN_WARNING "Teles: ISAC CRC error\n");
-			if (sp->rcvibh)
-				BufPoolRelease(sp->rcvibh);
-			sp->rcvibh = NULL;
-			WRITEISAC(sp->membase, sp->iobase, ISAC_CMDR, 0x80);
-			goto afterRME;
-		}
-		if (!sp->rcvibh)
-			if (BufPoolGet(&(sp->rcvibh), &(sp->rbufpool),
-                                       GFP_ATOMIC,
-				       (void *) 1, 3)) {
-				printk(KERN_WARNING
-                                       "ISAC RME out of buffers!\n");
-				WRITEISAC(sp->membase, sp->iobase, 
-                                          ISAC_CMDR, 0x80);
-				goto afterRME;
-			} else
-				sp->rcvptr = 0;
-
-		count = READISAC(sp->membase, sp->iobase, ISAC_RBCL) & 0x1f;
-		if (count == 0)
-			count = 32;
-		empty_fifo(sp, count);
-		sp->rcvibh->datasize = sp->rcvptr;
-		BufQueueLink(&(sp->rq), sp->rcvibh);
-		sp->rcvibh = NULL;
-		isac_sched_event(sp, ISAC_RCVBUFREADY);
-	}
-      afterRME:
-	if (val & 0x40) {	/* RPF */
-		if (!sp->rcvibh)
-			if (BufPoolGet(&(sp->rcvibh), &(sp->rbufpool),
-                                       GFP_ATOMIC,
-				       (void *) 1, 4)) {
-				printk(KERN_WARNING
-                                       "ISAC RME out of buffers!\n");
-				WRITEISAC(sp->membase, sp->iobase,
-                                          ISAC_CMDR, 0x80);
-				goto afterRPF;
-			} else
-				sp->rcvptr = 0;
-		empty_fifo(sp, 32);
-	}
-      afterRPF:
-	if (val & 0x20) {
-	}
-	if (val & 0x10) {	/* XPR */
-		if (sp->xmtibh)
-			if (sp->xmtibh->datasize > sp->sendptr) {
-				fill_fifo(sp);
-				goto afterXPR;
-			} else {
-				if (sp->releasebuf)
-					BufPoolRelease(sp->xmtibh);
-				sp->xmtibh = NULL;
-				sp->sendptr = 0;
-			}
-		if (!BufQueueUnlink(&sp->xmtibh, &sp->sq)) {
-			sp->releasebuf = !0;
-			fill_fifo(sp);
-		} else
-			isac_sched_event(sp, ISAC_XMTBUFREADY);
-	}
-      afterXPR:
-	if (val & 0x04) {	/* CISQ */
-		sp->ph_state = (READISAC(sp->membase, sp->iobase, ISAC_CIX0)
-                                >> 2) & 0xf;
-		printk(KERN_DEBUG "l1state %d\n", sp->ph_state);
-		isac_new_ph(sp);
-	}
-        if (sp->membase) {
-                writeisac_0(sp->membase, ISAC_MASK, 0xFF);
-                writehscx_0(sp->membase, 0, HSCX_MASK, 0xFF);
-                writehscx_0(sp->membase, 1, HSCX_MASK, 0xFF);
-                writeisac_0(sp->membase, ISAC_MASK, 0x0);
-                writehscx_0(sp->membase, 0, HSCX_MASK, 0x0);
-                writehscx_0(sp->membase, 1, HSCX_MASK, 0x0);
-        } else {
-                writeisac_3(sp->iobase, ISAC_MASK, 0xFF);
-                writehscx_3(sp->iobase, 0, HSCX_MASK, 0xFF);
-                writehscx_3(sp->iobase, 1, HSCX_MASK, 0xFF);
-                writeisac_3(sp->iobase, ISAC_MASK, 0x0);
-                writehscx_3(sp->iobase, 0, HSCX_MASK, 0x0);
-                writehscx_3(sp->iobase, 1, HSCX_MASK, 0x0);
-        }
-}
-
-/*
- * soft interrupt
- */
-
-static void
-act_ivated(struct IsdnCardState *sp)
-{
-	struct PStack  *st;
-
-	st = sp->stlist;
-	while (st) {
-		if (st->l1.act_state == 1) {
-			st->l1.act_state = 2;
-			st->l1.l1man(st, PH_ACTIVATE, NULL);
-		}
-		st = st->next;
-	}
-}
-
-static void
-process_new_ph(struct IsdnCardState *sp)
-{
-	if (sp->ph_active == 5)
-		act_ivated(sp);
-}
-
-static void
-process_xmt(struct IsdnCardState *sp)
-{
-	struct PStack  *stptr;
-
-	if (sp->xmtibh)
-		return;
-
-	stptr = sp->stlist;
-	while (stptr != NULL)
-		if (stptr->l1.requestpull) {
-			stptr->l1.requestpull = 0;
-			stptr->l1.l1l2(stptr, PH_PULL_ACK, NULL);
-			break;
-		} else
-			stptr = stptr->next;
-}
-
-static void
-process_rcv(struct IsdnCardState *sp)
-{
-	struct BufHeader *ibh, *cibh;
-	struct PStack    *stptr;
-	byte             *ptr;
-	int              found, broadc;
-	char             tmp[64];
-
-	while (!BufQueueUnlink(&ibh, &sp->rq)) {
-		stptr = sp->stlist;
-		ptr = DATAPTR(ibh);
-		broadc = (ptr[1] >> 1) == 127;
-
-		if (broadc && sp->dlogflag && (!(ptr[0] >> 2)))
-			dlogframe(sp, ptr + 3, ibh->datasize - 3,
-				  "Q.931 frame network->user broadcast");
-
-		if (broadc) {
-			while (stptr != NULL) {
-				if ((ptr[0] >> 2) == stptr->l2.sap)
-					if (!BufPoolGet(&cibh, &sp->rbufpool, GFP_ATOMIC,
-							(void *) 1, 5)) {
-						memcpy(DATAPTR(cibh), DATAPTR(ibh), ibh->datasize);
-						cibh->datasize = ibh->datasize;
-						stptr->l1.l1l2(stptr, PH_DATA, cibh);
-					} else
-						printk(KERN_WARNING "isdn broadcast buffer shortage\n");
-				stptr = stptr->next;
-			}
-			BufPoolRelease(ibh);
-		} else {
-			found = 0;
-			while (stptr != NULL)
-				if (((ptr[0] >> 2) == stptr->l2.sap) &&
-				    ((ptr[1] >> 1) == stptr->l2.tei)) {
-					stptr->l1.l1l2(stptr, PH_DATA, ibh);
-					found = !0;
-					break;
-				} else
-					stptr = stptr->next;
-			if (!found) {
-				/* BD 10.10.95
-				 * Print out D-Channel msg not processed
-				 * by isdn4linux
-                                 */
-
-				if ((!(ptr[0] >> 2)) && (!(ptr[2] & 0x01))) {
-					sprintf(tmp, "Q.931 frame network->user with tei %d (not for us)", ptr[1] >> 1);
-					dlogframe(sp, ptr + 4, ibh->datasize - 4, tmp);
-				}
-				BufPoolRelease(ibh);
-			}
-		}
-
-	}
-
-}
-
-static void
-isac_bh(struct IsdnCardState *sp)
-{
-	if (!sp)
-		return;
-
-	if (clear_bit(ISAC_PHCHANGE, &sp->event))
-		process_new_ph(sp);
-	if (clear_bit(ISAC_RCVBUFREADY, &sp->event))
-		process_rcv(sp);
-	if (clear_bit(ISAC_XMTBUFREADY, &sp->event))
-		process_xmt(sp);
-}
-
-
-static void
-hscx_process_xmt(struct HscxState *hsp)
-{
-	struct PStack  *st = hsp->st;
-
-	if (hsp->xmtibh)
-		return;
-
-	if (st->l1.requestpull) {
-		st->l1.requestpull = 0;
-		st->l1.l1l2(st, PH_PULL_ACK, NULL);
-	}
-	if (!hsp->active)
-		if ((!hsp->xmtibh) && (!hsp->sq.head))
-			modehscx(hsp, 0, 0);
-}
-
-static void
-hscx_process_rcv(struct HscxState *hsp)
-{
-	struct BufHeader *ibh;
-
-#ifdef DEBUG_MAGIC
-	if (hsp->magic != 301270) {
-		printk(KERN_DEBUG "hscx_process_rcv magic not 301270\n");
-		return;
-	}
-#endif
-	while (!BufQueueUnlink(&ibh, &hsp->rq)) {
-		hsp->st->l1.l1l2(hsp->st, PH_DATA, ibh);
-	}
-}
-
-static void
-hscx_bh(struct HscxState *hsp)
-{
-
-	if (!hsp)
-		return;
-
-	if (clear_bit(HSCX_RCVBUFREADY, &hsp->event))
-		hscx_process_rcv(hsp);
-	if (clear_bit(HSCX_XMTBUFREADY, &hsp->event))
-		hscx_process_xmt(hsp);
-
-}
-
-/*
- * interrupt stuff ends here
- */
-
-static void
-restart_ph(struct IsdnCardState *sp)
-{
-	switch (sp->ph_active) {
-	  case (0):
-		  if (sp->ph_state == 6)
-			  ph_command(sp, 0);
-		  else
-			  ph_command(sp, 1);
-		  sp->ph_active = 1;
-		  break;
-	}
-}
-
-static void
-initisac(byte * cardmem, int iobase)
-{
-        if (cardmem) {
-                writeisac_0(cardmem, ISAC_MASK, 0xff);
-                writeisac_0(cardmem, ISAC_ADF2, 0x0);
-                writeisac_0(cardmem, ISAC_SPCR, 0xa);
-                writeisac_0(cardmem, ISAC_ADF1, 0x2);
-                writeisac_0(cardmem, ISAC_STCR, 0x70);
-                writeisac_0(cardmem, ISAC_MODE, 0xc9);
-                writeisac_0(cardmem, ISAC_CMDR, 0x41);
-                writeisac_0(cardmem, ISAC_CIX0, (1 << 2) | 3);
-        } else {
-                writeisac_3(iobase, ISAC_MASK, 0xff);
-                writeisac_3(iobase, ISAC_ADF2, 0x80);
-                writeisac_3(iobase, ISAC_SQXR, 0x2f);
-                writeisac_3(iobase, ISAC_SPCR, 0x00);
-                writeisac_3(iobase, ISAC_ADF1, 0x02);
-                writeisac_3(iobase, ISAC_STCR, 0x70);
-                writeisac_3(iobase, ISAC_MODE, 0xc9);
-                writeisac_3(iobase, ISAC_TIMR, 0x00);
-                writeisac_3(iobase, ISAC_ADF1, 0x00);
-                writeisac_3(iobase, ISAC_CMDR, 0x41);
-                writeisac_3(iobase, ISAC_CIX0, (1 << 2) | 3);
-        }
-}
-
-static int
-checkcard(int cardnr)
-{
-	int             timout;
-	byte            cfval, val;
-	struct IsdnCard *card = cards + cardnr;
-
-        if (card->membase)
-                if ((unsigned long)card->membase < 0x10000) {
-                        (unsigned long)card->membase <<= 4;
-                        printk(KERN_INFO
-                               "Teles membase configured DOSish, assuming 0x%lx\n",
-                               (unsigned long)card->membase);
-                }
-        if (!card->iobase) {
-                if (card->membase) {
-                        printk(KERN_NOTICE
-                               "Teles 8 assumed, mem: %lx irq: %d proto: %s\n",
-                               (long) card->membase, card->interrupt,
-                               (card->protocol == ISDN_PTYPE_1TR6) ?
-                               "1TR6" : "EDSS1");
-                        printk(KERN_INFO "HSCX version A:%x B:%x\n",
-                               readhscx_0(card->membase, 0, HSCX_VSTR) & 0xf,
-                               readhscx_0(card->membase, 1, HSCX_VSTR) & 0xf);
-                }
-        } else {
-                switch (card->iobase) {
-                        case 0x180:
-                        case 0x280:
-                        case 0x380:
-                                card->iobase |= 0xc00;
-                                break;
-                }
-                if (card->membase) {  /* 16.0 */
-                	if (check_region(card->iobase, 8)) {
-                        	printk(KERN_WARNING
-                               		"teles: ports %x-%x already in use\n",
-                               		card->iobase,
-                               		card->iobase + 8 );
-                        	return -1;
-                	}
-                } else { /* 16.3 */
-                	if (check_region(card->iobase, 16)) {
-                        	printk(KERN_WARNING
-                               		"teles: 16.3 ports %x-%x already in use\n",
-                               		card->iobase,
-                               		card->iobase + 16 );
-                        	return -1;
-                	}
-                	if (check_region((card->iobase - 0xc00) , 32)) {
-                        	printk(KERN_WARNING
-                  			"teles: 16.3 ports %x-%x already in use\n",
-                               		card->iobase - 0xc00,
-                               		card->iobase - 0xc00 + 32);
-                        	return -1;
-                        }
-                	if (check_region((card->iobase - 0x800) , 32)) {
-                        	printk(KERN_WARNING
-                  			"teles: 16.3 ports %x-%x already in use\n",
-                               		card->iobase - 0x800,
-                               		card->iobase - 0x800 + 32);
-                        	return -1;
-                        }
-                	if (check_region((card->iobase - 0x400) , 32)) {
-                        	printk(KERN_WARNING
-                  			"teles: 16.3 ports %x-%x already in use\n",
-                               		card->iobase - 0x400,
-                               		card->iobase - 0x400 + 32);
-                        	return -1;
-                        }
-                }
-                switch (card->interrupt) {
-                case 2:
-                        cfval = 0x00;
-                        break;
-                case 3:
-                        cfval = 0x02;
-                        break;
-                case 4:
-                        cfval = 0x04;
-                        break;
-                case 5:
-                        cfval = 0x06;
-                        break;
-                case 10:
-                        cfval = 0x08;
-                        break;
-                case 11:
-                        cfval = 0x0A;
-                        break;
-                case 12:
-                        cfval = 0x0C;
-                        break;
-                case 15:
-                        cfval = 0x0E;
-                        break;
-                default:
-                        cfval = 0x00;
-                        break;
-                }
-                if (card->membase) {
-                        cfval |= (((unsigned int) card->membase >> 9) & 0xF0);
-                }   
-                if (bytein(card->iobase + 0) != 0x51) {
-                        printk(KERN_INFO "XXX Byte at %x is %x\n",
-                                card->iobase + 0,
-                                bytein(card->iobase + 0));
-                        return -2;
-                }
-                if (bytein(card->iobase + 1) != 0x93) {
-                        printk(KERN_INFO "XXX Byte at %x is %x\n",
-                                card->iobase + 1,
-                                bytein(card->iobase + 1));
-                        return -2;
-                }
-                val = bytein(card->iobase + 2);	/* 0x1e=without AB
-                                                 * 0x1f=with AB
-                                                 * 0x1c 16.3 ???
-                                                 */
-                if (val != 0x1c && val != 0x1e && val != 0x1f) {
-                        printk(KERN_INFO "XXX Byte at %x is %x\n",
-                                card->iobase + 2,
-                                bytein(card->iobase + 2));
-                        return -2;
-                }
-                if (card->membase) {  /* 16.0 */
-                        request_region(card->iobase, 8, "teles 16.0");
-                } else {
-                	request_region(card->iobase, 16, "teles 16.3");
-                	request_region(card->iobase - 0xC00, 32, "teles HSCX0");
-                	request_region(card->iobase - 0x800, 32, "teles HSCX1");
-                	request_region(card->iobase - 0x400, 32, "teles ISAC");
-                }
-                cli();
-                timout = jiffies + (HZ / 10) + 1;
-                byteout(card->iobase + 4, cfval);
-                sti();
-                while (jiffies <= timout);
-                
-                cli();
-                timout = jiffies + (HZ / 10) + 1;
-                byteout(card->iobase + 4, cfval | 1);
-                sti();
-                while (jiffies <= timout);
-                
-                if (card->membase)
-                        printk(KERN_NOTICE
-                               "Teles 16.0 found, io: %x mem: %lx irq: %d proto: %s\n",
-                               card->iobase, (long) card->membase,
-                               card->interrupt,
-                               (card->protocol == ISDN_PTYPE_1TR6) ?
-                               "1TR6" : "EDSS1");
-                else
-                        printk(KERN_NOTICE
-                               "Teles 16.3 found, io: %x irq: %d proto: %s\n",
-                               card->iobase, card->interrupt,
-                               (card->protocol == ISDN_PTYPE_1TR6) ?
-                               "1TR6" : "EDSS1");
-                printk(KERN_INFO "HSCX version A:%x B:%x\n",
-                       READHSCX(card->membase, card->iobase, 0,
-                                HSCX_VSTR) & 0xf,
-                       READHSCX(card->membase, card->iobase, 1,
-                                HSCX_VSTR) & 0xf);
-                
-        }
-        if (card->membase) {
-                cli();
-                timout = jiffies + (HZ / 5) + 1;
-                writeb(0, card->membase + 0x80);
-                sti();
-                while (jiffies <= timout);
-                
-                cli();
-                writeb(1, card->membase + 0x80);
-                timout = jiffies + (HZ / 5) + 1;
-                sti();
-                while (jiffies <= timout);
-        }
-	return (0);
-}
-
-void
-modehscx(struct HscxState *hs, int mode,
-	 int ichan)
-{
-	struct IsdnCardState *sp = hs->sp;
-	int             hscx = hs->hscx;
-
-	printk(KERN_DEBUG "modehscx hscx %d mode %d ichan %d\n",
-	       hscx, mode, ichan);
-
-        hs->mode = mode;
-        if (sp->membase) {
-                /* What's that ??? KKeil */
-		if (hscx == 0)
-			ichan = 1 - ichan;	/* raar maar waar... */
-                writehscx_0(sp->membase, hscx, HSCX_CCR1, 0x85);
-                writehscx_0(sp->membase, hscx, HSCX_XAD1, 0xFF);
-                writehscx_0(sp->membase, hscx, HSCX_XAD2, 0xFF);
-                writehscx_0(sp->membase, hscx, HSCX_RAH2, 0xFF);
-                writehscx_0(sp->membase, hscx, HSCX_XBCH, 0x0);
-
-                switch (mode) {
-                case (0):
-                        writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30);
-                        writehscx_0(sp->membase, hscx, HSCX_TSAX, 0xff);
-                        writehscx_0(sp->membase, hscx, HSCX_TSAR, 0xff);
-                        writehscx_0(sp->membase, hscx, HSCX_XCCR, 7);
-                        writehscx_0(sp->membase, hscx, HSCX_RCCR, 7);
-                        writehscx_0(sp->membase, hscx, HSCX_MODE, 0x84);
-                        break;
-                case (1):
-                        if (ichan == 0) {
-                                writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30);
-                                writehscx_0(sp->membase, hscx, HSCX_TSAX, 0x7);
-                                writehscx_0(sp->membase, hscx, HSCX_TSAR, 0x7);
-                                writehscx_0(sp->membase, hscx, HSCX_XCCR, 7);
-                                writehscx_0(sp->membase, hscx, HSCX_RCCR, 7);
-                        } else {
-                                writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30);
-                                writehscx_0(sp->membase, hscx, HSCX_TSAX, 0x3);
-                                writehscx_0(sp->membase, hscx, HSCX_TSAR, 0x3);
-                                writehscx_0(sp->membase, hscx, HSCX_XCCR, 7);
-                                writehscx_0(sp->membase, hscx, HSCX_RCCR, 7);
-                        }
-                        writehscx_0(sp->membase, hscx, HSCX_MODE, 0xe4);
-                        writehscx_0(sp->membase, hscx, HSCX_CMDR, 0x41);
-                        break;
-                case (2):
-                        if (ichan == 0) {
-                                writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30);
-                                writehscx_0(sp->membase, hscx, HSCX_TSAX, 0x7);
-                                writehscx_0(sp->membase, hscx, HSCX_TSAR, 0x7);
-                                writehscx_0(sp->membase, hscx, HSCX_XCCR, 7);
-                                writehscx_0(sp->membase, hscx, HSCX_RCCR, 7);
-                        } else {
-                                writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30);
-                                writehscx_0(sp->membase, hscx, HSCX_TSAX, 0x3);
-                                writehscx_0(sp->membase, hscx, HSCX_TSAR, 0x3);
-                                writehscx_0(sp->membase, hscx, HSCX_XCCR, 7);
-                                writehscx_0(sp->membase, hscx, HSCX_RCCR, 7);
-                        }
-                        writehscx_0(sp->membase, hscx, HSCX_MODE, 0x8c);
-                        writehscx_0(sp->membase, hscx, HSCX_CMDR, 0x41);
-                        break;
-                }
-                writehscx_0(sp->membase, hscx, HSCX_ISTA, 0x00);
-        } else {
-                writehscx_3(sp->iobase, hscx, HSCX_CCR1, 0x85);
-                writehscx_3(sp->iobase, hscx, HSCX_XAD1, 0xFF);
-                writehscx_3(sp->iobase, hscx, HSCX_XAD2, 0xFF);
-                writehscx_3(sp->iobase, hscx, HSCX_RAH2, 0xFF);
-                writehscx_3(sp->iobase, hscx, HSCX_XBCH, 0x00);
-                writehscx_3(sp->iobase, hscx, HSCX_RLCR, 0x00);
-                
-                switch (mode) {
-                case (0):
-                        writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30);
-                        writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0xff);
-                        writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0xff);
-                        writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7);
-                        writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7);
-                        writehscx_3(sp->iobase, hscx, HSCX_MODE, 0x84);
-                        break;
-                case (1):
-                        if (ichan == 0) {
-                                writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30);
-                                writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0x2f);
-                                writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0x2f);
-                                writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7);
-                                writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7);
-                        } else {
-                                writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30);
-                                writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0x3);
-                                writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0x3);
-                                writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7);
-                                writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7);
-                        }
-                        writehscx_3(sp->iobase, hscx, HSCX_MODE, 0xe4);
-                        writehscx_3(sp->iobase, hscx, HSCX_CMDR, 0x41);
-                        break;
-                case (2):
-                        if (ichan == 0) {
-                                writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30);
-                                writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0x2f);
-                                writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0x2f);
-                                writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7);
-                                writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7);
-                        } else {
-                                writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30);
-                                writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0x3);
-                                writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0x3);
-                                writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7);
-                                writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7);
-                        }
-                        writehscx_3(sp->iobase, hscx, HSCX_MODE, 0x8c);
-                        writehscx_3(sp->iobase, hscx, HSCX_CMDR, 0x41);
-                        break;
-                }
-                writehscx_3(sp->iobase, hscx, HSCX_ISTA, 0x00);
-        }
-}
-
-void
-teles_addlist(struct IsdnCardState *sp,
-	      struct PStack *st)
-{
-	st->next = sp->stlist;
-	sp->stlist = st;
-}
-
-void
-teles_rmlist(struct IsdnCardState *sp,
-	     struct PStack *st)
-{
-	struct PStack  *p;
-
-	if (sp->stlist == st)
-		sp->stlist = st->next;
-	else {
-		p = sp->stlist;
-		while (p)
-			if (p->next == st) {
-				p->next = st->next;
-				return;
-			} else
-				p = p->next;
-	}
-}
-
-
-static void
-teles_l2l1(struct PStack *st, int pr,
-	   struct BufHeader *ibh)
-{
-	struct IsdnCardState *sp = (struct IsdnCardState *)
-	st->l1.hardware;
-
-
-	switch (pr) {
-	  case (PH_DATA):
-		  if (sp->xmtibh)
-			  BufQueueLink(&sp->sq, ibh);
-		  else {
-			  sp->xmtibh = ibh;
-			  sp->sendptr = 0;
-			  sp->releasebuf = !0;
-			  fill_fifo(sp);
-		  }
-		  break;
-	  case (PH_DATA_PULLED):
-		  if (sp->xmtibh) {
-			  printk(KERN_DEBUG "teles_l2l1: this shouldn't happen\n");
-			  break;
-		  }
-		  sp->xmtibh = ibh;
-		  sp->sendptr = 0;
-		  sp->releasebuf = 0;
-		  fill_fifo(sp);
-		  break;
-	  case (PH_REQUEST_PULL):
-		  if (!sp->xmtibh) {
-			  st->l1.requestpull = 0;
-			  st->l1.l1l2(st, PH_PULL_ACK, NULL);
-		  } else
-			  st->l1.requestpull = !0;
-		  break;
-	}
-}
-
-static void
-check_ph_act(struct IsdnCardState *sp)
-{
-	struct PStack  *st = sp->stlist;
-
-	while (st) {
-		if (st->l1.act_state)
-			return;
-		st = st->next;
-	}
-	sp->ph_active = 0;
-}
-
-static void
-teles_manl1(struct PStack *st, int pr,
-	    void *arg)
-{
-	struct IsdnCardState *sp = (struct IsdnCardState *)
-	st->l1.hardware;
-	long            flags;
-
-	switch (pr) {
-	  case (PH_ACTIVATE):
-		  save_flags(flags);
-		  cli();
-		  if (sp->ph_active == 5) {
-			  st->l1.act_state = 2;
-			  restore_flags(flags);
-			  st->l1.l1man(st, PH_ACTIVATE, NULL);
-		  } else {
-			  st->l1.act_state = 1;
-			  if (sp->ph_active == 0)
-				  restart_ph(sp);
-			  restore_flags(flags);
-		  }
-		  break;
-	  case (PH_DEACTIVATE):
-		  st->l1.act_state = 0;
-		  check_ph_act(sp);
-		  break;
-	}
-}
-
-static void
-teles_l2l1discardq(struct PStack *st, int pr,
-		   void *heldby, int releasetoo)
-{
-	struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware;
-
-#ifdef DEBUG_MAGIC
-	if (sp->magic != 301271) {
-		printk(KERN_DEBUG "isac_discardq magic not 301271\n");
-		return;
-	}
-#endif
-
-	BufQueueDiscard(&sp->sq, pr, heldby, releasetoo);
-}
-
-void
-setstack_teles(struct PStack *st, struct IsdnCardState *sp)
-{
-	st->l1.hardware = sp;
-	st->l1.sbufpool = &(sp->sbufpool);
-	st->l1.rbufpool = &(sp->rbufpool);
-	st->l1.smallpool = &(sp->smallpool);
-	st->protocol = sp->teistack->protocol;
-
-	setstack_tei(st);
-
-	st->l1.stlistp = &(sp->stlist);
-	st->l1.act_state = 0;
-	st->l2.l2l1 = teles_l2l1;
-	st->l2.l2l1discardq = teles_l2l1discardq;
-	st->ma.manl1 = teles_manl1;
-	st->l1.requestpull = 0;
-}
-
-void
-init_hscxstate(struct IsdnCardState *sp,
-	       int hscx)
-{
-	struct HscxState *hsp = sp->hs + hscx;
-
-	hsp->sp = sp;
-	hsp->hscx = hscx;
-	hsp->membase = sp->membase;
-	hsp->iobase = sp->iobase;
-
-	hsp->tqueue.next = 0;
-	hsp->tqueue.sync = 0;
-	hsp->tqueue.routine = (void *) (void *) hscx_bh;
-	hsp->tqueue.data = hsp;
-
-	hsp->inuse = 0;
-	hsp->init = 0;
-	hsp->active = 0;
-
-#ifdef DEBUG_MAGIC
-	hsp->magic = 301270;
-#endif
-}
-
-void
-initcard(int cardnr)
-{
-	struct IsdnCardState *sp;
-	struct IsdnCard *card = cards + cardnr;
-
-	sp = (struct IsdnCardState *)
-	    Smalloc(sizeof(struct IsdnCardState), GFP_KERNEL,
-		    "struct IsdnCardState");
-
-	sp->membase = card->membase;
-	sp->iobase = card->iobase;
-	sp->cardnr = cardnr;
-
-	BufPoolInit(&sp->sbufpool, ISAC_SBUF_ORDER, ISAC_SBUF_BPPS,
-		    ISAC_SBUF_MAXPAGES);
-	BufPoolInit(&sp->rbufpool, ISAC_RBUF_ORDER, ISAC_RBUF_BPPS,
-		    ISAC_RBUF_MAXPAGES);
-	BufPoolInit(&sp->smallpool, ISAC_SMALLBUF_ORDER, ISAC_SMALLBUF_BPPS,
-		    ISAC_SMALLBUF_MAXPAGES);
-
-	sp->dlogspace = Smalloc(4096, GFP_KERNEL, "dlogspace");
-
-	initisac(card->membase, card->iobase);
-
-	sp->rcvibh = NULL;
-	sp->rcvptr = 0;
-	sp->xmtibh = NULL;
-	sp->sendptr = 0;
-	sp->event = 0;
-	sp->tqueue.next = 0;
-	sp->tqueue.sync = 0;
-	sp->tqueue.routine = (void *) (void *) isac_bh;
-	sp->tqueue.data = sp;
-
-	BufQueueInit(&sp->rq);
-	BufQueueInit(&sp->sq);
-
-	sp->stlist = NULL;
-
-	sp->ph_active = 0;
-
-	sp->dlogflag = 0;
-	sp->debug = 0;
-
-	sp->releasebuf = 0;
-#ifdef DEBUG_MAGIC
-	sp->magic = 301271;
-#endif
-
-	cards[sp->cardnr].sp = sp;
-
-	init_hscxstate(sp, 0);
-	init_hscxstate(sp, 1);
-
-	modehscx(sp->hs, 0, 0);
-	modehscx(sp->hs + 1, 0, 0);
-
-	WRITEISAC(sp->membase, sp->iobase, ISAC_MASK, 0x0);
-}
-
-static int
-get_irq(int cardnr)
-{
-	struct IsdnCard *card = cards + cardnr;
-	long            flags;
-
-	save_flags(flags);
-	cli();
-	if (request_irq(card->interrupt, &teles_interrupt,
-			SA_INTERRUPT, "teles", NULL)) {
-		printk(KERN_WARNING "Teles couldn't get interrupt %d\n",
-                       card->interrupt);
-		restore_flags(flags);
-		return (!0);
-	}
-	irq2dev_map[card->interrupt] = (void *) card->sp;
-	restore_flags(flags);
-	return (0);
-}
-
-static void
-release_irq(int cardnr)
-{
-	struct	IsdnCard *card = cards + cardnr;
-
-	irq2dev_map[card->interrupt] = NULL;
-	free_irq(card->interrupt, NULL);
-}
-
-void
-close_hscxstate(struct HscxState *hs)
-{
-	modehscx(hs, 0, 0);
-	hs->inuse = 0;
-
-	if (hs->init) {
-		BufPoolFree(&hs->smallpool);
-		BufPoolFree(&hs->rbufpool);
-		BufPoolFree(&hs->sbufpool);
-	}
-	hs->init = 0;
-}
-
-void
-closecard(int cardnr)
-{
-	struct IsdnCardState *sp = cards[cardnr].sp;
-
-	cards[cardnr].sp = NULL;
-
-	Sfree(sp->dlogspace);
-
-	BufPoolFree(&sp->smallpool);
-	BufPoolFree(&sp->rbufpool);
-	BufPoolFree(&sp->sbufpool);
-
-	close_hscxstate(sp->hs + 1);
-	close_hscxstate(sp->hs);
-
-	if (cards[cardnr].iobase)
-        	if (cards[cardnr].membase) {  /* 16.0 */
-			release_region(cards[cardnr].iobase, 8);
-        	} else {
-			release_region(cards[cardnr].iobase, 16);
-                	release_region(cards[cardnr].iobase - 0xC00, 32);
-                	release_region(cards[cardnr].iobase - 0x800, 32);
-                	release_region(cards[cardnr].iobase - 0x400, 32);
-                }
-
-	Sfree((void *) sp);
-}
-
-void
-teles_shiftcards(int idx)
-{
-        int i;
-
-        for (i = idx; i < 15; i++)
-                memcpy(&cards[i],&cards[i+1],sizeof(cards[i]));
-}
-
-int
-teles_inithardware(void)
-{
-        int             foundcards = 0;
-	int             i = 0;
-
-	while (i < nrcards) {
-                if (!cards[i].protocol)
-                        break;
-		switch (checkcard(i)) {
-		  case (0):
-			  initcard(i);
-			  if (get_irq(i)) {
-				  closecard(i);
-                                  teles_shiftcards(i);
-                          } else {
-                                  foundcards++;
-                                  i++;
-                          }
-			  break;
-		  case (-1):
-                          teles_shiftcards(i);
-			  break;
-		  case (-2):
-			  release_region(cards[i].iobase, 8);
-			  printk(KERN_WARNING "NO Teles card found at 0x%x!\n", cards[i].iobase);
-                          teles_shiftcards(i);
-			  break;
-		}
-        }
-        return foundcards;
-}
-
-void
-teles_closehardware(void)
-{
-	int             i;
-
-	for (i = 0; i < nrcards; i++)
-		if (cards[i].sp) {
-			release_irq(i);
-			closecard(i);
-		}
-}
-
-static void
-hscx_l2l1(struct PStack *st, int pr,
-	  struct BufHeader *ibh)
-{
-	struct IsdnCardState *sp = (struct IsdnCardState *)
-	st->l1.hardware;
-	struct HscxState *hsp = sp->hs + st->l1.hscx;
-	long flags;
-
-	switch (pr) {
-                case (PH_DATA):
-			save_flags(flags);
-			cli();
-                        if (hsp->xmtibh) {
-                                BufQueueLink(&hsp->sq, ibh);
-				restore_flags(flags);
-			}
-                        else {
-				restore_flags(flags);
-                                hsp->xmtibh = ibh;
-                                hsp->sendptr = 0;
-                                hsp->releasebuf = !0;
-                                hscx_fill_fifo(hsp);
-                        }
-                        break;
-                case (PH_DATA_PULLED):
-                        if (hsp->xmtibh) {
-                                printk(KERN_DEBUG "hscx_l2l1: this shouldn't happen\n");
-                                break;
-                        }
-                        hsp->xmtibh = ibh;
-                        hsp->sendptr = 0;
-                        hsp->releasebuf = 0;
-                        hscx_fill_fifo(hsp);
-                        break;
-                case (PH_REQUEST_PULL):
-                        if (!hsp->xmtibh) {
-                                st->l1.requestpull = 0;
-                                st->l1.l1l2(st, PH_PULL_ACK, NULL);
-                        } else
-                                st->l1.requestpull = !0;
-                        break;
-	}
-        
-}
-
-extern struct IsdnBuffers *tracebuf;
-
-static void
-hscx_l2l1discardq(struct PStack *st, int pr, void *heldby,
-		  int releasetoo)
-{
-	struct IsdnCardState *sp = (struct IsdnCardState *)
-	st->l1.hardware;
-	struct HscxState *hsp = sp->hs + st->l1.hscx;
-
-#ifdef DEBUG_MAGIC
-	if (hsp->magic != 301270) {
-		printk(KERN_DEBUG "hscx_discardq magic not 301270\n");
-		return;
-	}
-#endif
-
-	BufQueueDiscard(&hsp->sq, pr, heldby, releasetoo);
-}
-
-static int
-open_hscxstate(struct IsdnCardState *sp,
-	       int hscx)
-{
-	struct HscxState *hsp = sp->hs + hscx;
-
-	if (!hsp->init) {
-		BufPoolInit(&hsp->sbufpool, HSCX_SBUF_ORDER, HSCX_SBUF_BPPS,
-			    HSCX_SBUF_MAXPAGES);
-		BufPoolInit(&hsp->rbufpool, HSCX_RBUF_ORDER, HSCX_RBUF_BPPS,
-			    HSCX_RBUF_MAXPAGES);
-		BufPoolInit(&hsp->smallpool, HSCX_SMALLBUF_ORDER, HSCX_SMALLBUF_BPPS,
-			    HSCX_SMALLBUF_MAXPAGES);
-	}
-	hsp->init = !0;
-
-	BufQueueInit(&hsp->rq);
-	BufQueueInit(&hsp->sq);
-
-	hsp->releasebuf = 0;
-	hsp->rcvibh = NULL;
-	hsp->xmtibh = NULL;
-	hsp->rcvptr = 0;
-	hsp->sendptr = 0;
-	hsp->event = 0;
-	return (0);
-}
-
-static void
-hscx_manl1(struct PStack *st, int pr,
-	   void *arg)
-{
-	struct IsdnCardState *sp = (struct IsdnCardState *)
-	st->l1.hardware;
-	struct HscxState *hsp = sp->hs + st->l1.hscx;
-
-	switch (pr) {
-	  case (PH_ACTIVATE):
-		  hsp->active = !0;
-		  modehscx(hsp, st->l1.hscxmode, st->l1.hscxchannel);
-		  st->l1.l1man(st, PH_ACTIVATE, NULL);
-		  break;
-	  case (PH_DEACTIVATE):
-		  if (!hsp->xmtibh)
-			  modehscx(hsp, 0, 0);
-
-		  hsp->active = 0;
-		  break;
-	}
-}
-
-int
-setstack_hscx(struct PStack *st, struct HscxState *hs)
-{
-	if (open_hscxstate(st->l1.hardware, hs->hscx))
-		return (-1);
-
-	st->l1.hscx = hs->hscx;
-	st->l2.l2l1 = hscx_l2l1;
-	st->ma.manl1 = hscx_manl1;
-	st->l2.l2l1discardq = hscx_l2l1discardq;
-
-	st->l1.sbufpool = &hs->sbufpool;
-	st->l1.rbufpool = &hs->rbufpool;
-	st->l1.smallpool = &hs->smallpool;
-	st->l1.act_state = 0;
-	st->l1.requestpull = 0;
-
-	hs->st = st;
-	return (0);
-}
-
-void
-teles_reportcard(int cardnr)
-{
-	printk(KERN_DEBUG "teles_reportcard\n");
-}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/config.c linux/drivers/isdn/teles/config.c
--- v2.0.30/linux/drivers/isdn/teles/config.c	Sun Apr 21 01:56:15 1996
+++ linux/drivers/isdn/teles/config.c	Wed Dec 31 16:00:00 1969
@@ -1,48 +0,0 @@
-/* $Id: config.c,v 1.1 1996/04/13 10:23:11 fritz Exp $
- *
- * $Log: config.c,v $
- * Revision 1.1  1996/04/13 10:23:11  fritz
- * Initial revision
- *
- *
- */
-#define __NO_VERSION__
-#include <linux/types.h>
-#include <linux/stddef.h>
-#include <linux/timer.h>
-#include "teles.h"
-
-/*
- * This structure array contains one entry per card. An entry looks
- * like this:
- * 
- * { membase,irq,portbase,protocol,NULL }
- *
- * protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6
- *
- * Cards which don't have an io port (Teles 8 bit cards for
- * example) can be entered with io port 0x0
- *
- * For the Teles 16.3, membase has to be set to 0.
- *
- */
-
-struct IsdnCard cards[] =
-{
-	{(byte *) 0xd0000, 15, 0xd80, ISDN_PTYPE_EURO, NULL},	/* example */
-	{NULL, 0, 0, 0, NULL},
-	{NULL, 0, 0, 0, NULL},
-	{NULL, 0, 0, 0, NULL},
-	{NULL, 0, 0, 0, NULL},
-	{NULL, 0, 0, 0, NULL},
-	{NULL, 0, 0, 0, NULL},
-	{NULL, 0, 0, 0, NULL},
-	{NULL, 0, 0, 0, NULL},
-	{NULL, 0, 0, 0, NULL},
-	{NULL, 0, 0, 0, NULL},
-	{NULL, 0, 0, 0, NULL},
-	{NULL, 0, 0, 0, NULL},
-	{NULL, 0, 0, 0, NULL},
-	{NULL, 0, 0, 0, NULL},
-	{NULL, 0, 0, 0, NULL},
-};
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/fsm.c linux/drivers/isdn/teles/fsm.c
--- v2.0.30/linux/drivers/isdn/teles/fsm.c	Sun May 19 05:29:30 1996
+++ linux/drivers/isdn/teles/fsm.c	Wed Dec 31 16:00:00 1969
@@ -1,159 +0,0 @@
-/* $Id: fsm.c,v 1.2 1996/04/29 22:49:57 fritz Exp $
- *
- * $Log: fsm.c,v $
- * Revision 1.2  1996/04/29 22:49:57  fritz
- * Removed compatibility-macros.
- *
- * Revision 1.1  1996/04/13 10:23:41  fritz
- * Initial revision
- *
- *
- */
-#define __NO_VERSION__
-#include "teles.h"
-
-void
-FsmNew(struct Fsm *fsm,
-       struct FsmNode *fnlist, int fncount)
-{
-	int             i;
-
-	fsm->jumpmatrix = (int *) Smalloc(4L * fsm->state_count * fsm->event_count,
-					  GFP_KERNEL, "Fsm jumpmatrix");
-	memset(fsm->jumpmatrix, 0, 4L * fsm->state_count * fsm->event_count);
-
-	for (i = 0; i < fncount; i++)
-		fsm->jumpmatrix[fsm->state_count * fnlist[i].event +
-			      fnlist[i].state] = (int) fnlist[i].routine;
-}
-
-void
-FsmFree(struct Fsm *fsm)
-{
-	Sfree((void *) fsm->jumpmatrix);
-}
-
-int
-FsmEvent(struct FsmInst *fi, int event, void *arg)
-{
-	void            (*r) (struct FsmInst *, int, void *);
-	char            str[80];
-
-	r = (void (*)) fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state];
-	if (r) {
-		if (fi->debug) {
-			sprintf(str, "State %s Event %s",
-				fi->fsm->strState[fi->state],
-				fi->fsm->strEvent[event]);
-			fi->printdebug(fi, str);
-		}
-		r(fi, event, arg);
-		return (0);
-	} else {
-		if (fi->debug) {
-			sprintf(str, "State %s Event %s no routine",
-				fi->fsm->strState[fi->state],
-				fi->fsm->strEvent[event]);
-			fi->printdebug(fi, str);
-		}
-		return (!0);
-	}
-}
-
-void
-FsmChangeState(struct FsmInst *fi, int newstate)
-{
-	char            str[80];
-
-	fi->state = newstate;
-	if (fi->debug) {
-		sprintf(str, "ChangeState %s",
-			fi->fsm->strState[newstate]);
-		fi->printdebug(fi, str);
-	}
-}
-
-static void
-FsmExpireTimer(struct FsmTimer *ft)
-{
-	FsmEvent(ft->fi, ft->event, ft->arg);
-}
-
-void
-FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft)
-{
-	ft->fi = fi;
-	ft->tl.function = (void *) FsmExpireTimer;
-	ft->tl.data = (long) ft;
-	init_timer(&ft->tl);
-}
-
-void
-FsmDelTimer(struct FsmTimer *ft, int where)
-{
-	long            flags;
-
-#if 0
-	if (ft->fi->debug) {
-		sprintf(str, "FsmDelTimer %lx %d", ft, where);
-		ft->fi->printdebug(ft->fi, str);
-	}
-#endif
-
-	save_flags(flags);
-	cli();
-	if (ft->tl.next)
-		del_timer(&ft->tl);
-	restore_flags(flags);
-}
-
-int
-FsmAddTimer(struct FsmTimer *ft,
-	    int millisec, int event, void *arg, int where)
-{
-
-#if 0
-	if (ft->fi->debug) {
-		sprintf(str, "FsmAddTimer %lx %d %d", ft, millisec, where);
-		ft->fi->printdebug(ft->fi, str);
-	}
-#endif
-
-	if (ft->tl.next) {
-		printk(KERN_WARNING "FsmAddTimer: timer already active!\n");
-		return -1;
-	}
-	init_timer(&ft->tl);
-	ft->event = event;
-	ft->arg = arg;
-	ft->tl.expires = jiffies + (millisec * HZ) / 1000;
-	add_timer(&ft->tl);
-	return 0;
-}
-
-int
-FsmTimerRunning(struct FsmTimer *ft)
-{
-	return (ft->tl.next != NULL);
-}
-
-void
-jiftime(char *s, long mark)
-{
-	s += 8;
-
-	*s-- = '\0';
-	*s-- = mark % 10 + '0';
-	mark /= 10;
-	*s-- = mark % 10 + '0';
-	mark /= 10;
-	*s-- = '.';
-	*s-- = mark % 10 + '0';
-	mark /= 10;
-	*s-- = mark % 6 + '0';
-	mark /= 6;
-	*s-- = ':';
-	*s-- = mark % 10 + '0';
-	mark /= 10;
-	*s-- = mark % 10 + '0';
-}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/isdnl2.c linux/drivers/isdn/teles/isdnl2.c
--- v2.0.30/linux/drivers/isdn/teles/isdnl2.c	Sun May 19 05:29:31 1996
+++ linux/drivers/isdn/teles/isdnl2.c	Wed Dec 31 16:00:00 1969
@@ -1,1317 +0,0 @@
-/* $Id: isdnl2.c,v 1.2 1996/05/17 03:46:15 fritz Exp $
- *
- * $Log: isdnl2.c,v $
- * Revision 1.2  1996/05/17 03:46:15  fritz
- * General cleanup.
- *
- * Revision 1.1  1996/04/13 10:24:16  fritz
- * Initial revision
- *
- *
- */
-#define __NO_VERSION__
-#include "teles.h"
-
-#define TIMER_1 2000
-
-static void     l2m_debug(struct FsmInst *fi, char *s);
-
-struct Fsm      l2fsm =
-{NULL, 0, 0};
-
-enum {
-	ST_L2_1,
-	ST_L2_3,
-	ST_L2_4,
-	ST_L2_5,
-	ST_L2_6,
-	ST_L2_7,
-	ST_L2_8,
-};
-
-#define L2_STATE_COUNT (ST_L2_8+1)
-
-static char    *strL2State[] =
-{
-	"ST_L2_1",
-	"ST_L2_3",
-	"ST_L2_4",
-	"ST_L2_5",
-	"ST_L2_6",
-	"ST_L2_7",
-	"ST_L2_8",
-};
-
-enum {
-	EV_L2_UI,
-	EV_L2_SABMX,
-	EV_L2_UA,
-	EV_L2_DISC,
-	EV_L2_I,
-	EV_L2_RR,
-	EV_L2_REJ,
-	EV_L2_FRMR,
-	EV_L2_DL_DATA,
-	EV_L2_DL_ESTABLISH,
-	EV_L2_MDL_ASSIGN,
-	EV_L2_DL_UNIT_DATA,
-	EV_L2_DL_RELEASE,
-	EV_L2_MDL_NOTEIPROC,
-	EV_L2_T200,
-	EV_L2_ACK_PULL,
-	EV_L2_T203,
-	EV_L2_RNR,
-};
-
-#define L2_EVENT_COUNT (EV_L2_RNR+1)
-
-static char    *strL2Event[] =
-{
-	"EV_L2_UI",
-	"EV_L2_SABMX",
-	"EV_L2_UA",
-	"EV_L2_DISC",
-	"EV_L2_I",
-	"EV_L2_RR",
-	"EV_L2_REJ",
-	"EV_L2_FRMR",
-	"EV_L2_DL_DATA",
-	"EV_L2_DL_ESTABLISH",
-	"EV_L2_MDL_ASSIGN",
-	"EV_L2_DL_UNIT_DATA",
-	"EV_L2_DL_RELEASE",
-	"EV_L2_MDL_NOTEIPROC",
-	"EV_L2_T200",
-	"EV_L2_ACK_PULL",
-	"EV_L2_T203",
-	"EV_L2_RNR",
-};
-
-int             errcount = 0;
-
-static int      l2addrsize(struct Layer2 *tsp);
-
-static int
-cansend(struct PStack *st)
-{
-	int             p1;
-
-	p1 = (st->l2.va + st->l2.window) % (st->l2.extended ? 128 : 8);
-	return (st->l2.vs != p1);
-}
-
-static void
-discard_i_queue(struct PStack *st)
-{
-	struct BufHeader *ibh;
-
-	while (!BufQueueUnlink(&ibh, &st->l2.i_queue))
-		BufPoolRelease(ibh);
-}
-
-int
-l2headersize(struct Layer2 *tsp, int UI)
-{
-	return ((tsp->extended && (!UI) ? 2 : 1) + (tsp->laptype == LAPD ? 2 : 1));
-}
-
-int
-l2addrsize(struct Layer2 *tsp)
-{
-	return (tsp->laptype == LAPD ? 2 : 1);
-}
-
-static int
-sethdraddr(struct Layer2 *tsp,
-	   struct BufHeader *ibh, int rsp)
-{
-	byte           *ptr = DATAPTR(ibh);
-	int             crbit;
-
-	if (tsp->laptype == LAPD) {
-		crbit = rsp;
-		if (!tsp->orig)
-			crbit = !crbit;
-		*ptr++ = (tsp->sap << 2) | (crbit ? 2 : 0);
-		*ptr++ = (tsp->tei << 1) | 1;
-		return (2);
-	} else {
-		crbit = rsp;
-		if (tsp->orig)
-			crbit = !crbit;
-		if (crbit)
-			*ptr++ = 1;
-		else
-			*ptr++ = 3;
-		return (1);
-	}
-}
-
-static void
-enqueue_ui(struct PStack *st,
-	   struct BufHeader *ibh)
-{
-	st->l2.l2l1(st, PH_DATA, ibh);
-}
-
-static void
-enqueue_super(struct PStack *st,
-	      struct BufHeader *ibh)
-{
-	st->l2.l2l1(st, PH_DATA, ibh);
-}
-
-static int
-legalnr(struct PStack *st, int nr)
-{
-	struct Layer2  *l2 = &st->l2;
-	int             lnr, lvs;
-
-	lvs = (l2->vs >= l2->va) ? l2->vs : (l2->vs + l2->extended ? 128 : 8);
-	lnr = (nr >= l2->va) ? nr : (nr + l2->extended ? 128 : 8);
-	return (lnr <= lvs);
-}
-
-static void
-setva(struct PStack *st, int nr)
-{
-	struct Layer2  *l2 = &st->l2;
-
-	if (l2->va != nr) {
-                while (l2->va != nr) {
-                        l2->va = (l2->va + 1) % (l2->extended ? 128 : 8);
-                        BufPoolRelease(l2->windowar[l2->sow]);
-                        l2->sow = (l2->sow + 1) % l2->window;
-                }
-                if (st->l4.l2writewakeup)
-                        st->l4.l2writewakeup(st);
-        }
-}
-
-static void
-l2s1(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack  *st = fi->userdata;
-
-	st->l2.l2tei(st, MDL_ASSIGN, (void *)st->l2.ces);
-	FsmChangeState(fi, ST_L2_3);
-}
-
-static void
-l2s2(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack  *st = fi->userdata;
-	struct BufHeader *ibh = arg;
-
-	byte           *ptr;
-	int             i;
-
-	i = sethdraddr(&(st->l2), ibh, 0);
-	ptr = DATAPTR(ibh);
-	ptr += i;
-	*ptr = 0x3;
-
-	enqueue_ui(st, ibh);
-}
-
-static void
-l2s3(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack  *st = fi->userdata;
-	struct BufHeader *ibh = arg;
-
-	st->l2.l2l3(st, DL_UNIT_DATA, ibh);
-}
-
-static void
-establishlink(struct FsmInst *fi)
-{
-	struct PStack  *st = fi->userdata;
-	struct BufHeader *ibh;
-	int             i;
-	byte           *ptr;
-
-	FsmChangeState(fi, ST_L2_5);
-	st->l2.rc = 0;
-
-	if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 1))
-		if (st->l2.l2m.debug)
-			l2m_debug(&st->l2.l2m, "FAT 1");
-
-
-	if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 15))
-		return;
-	i = sethdraddr(&st->l2, ibh, 0);
-	ptr = DATAPTR(ibh);
-	ptr += i;
-	if (st->l2.extended)
-		*ptr = 0x7f;
-	else
-		*ptr = 0x3f;
-	ibh->datasize = i + 1;
-
-	enqueue_super(st, ibh);
-}
-
-static void
-l2s11(struct FsmInst *fi, int event, void *arg)
-{
-	establishlink(fi);
-}
-
-static void
-l2s13(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack  *st = fi->userdata;
-	struct Channel *chanp = st->l4.userdata;
-	byte           *ptr;
-	struct BufHeader *ibh;
-	int             i;
-
-	FsmChangeState(fi, ST_L2_6);
-
-	FsmDelTimer(&st->l2.t203_timer, 1);
-	if (st->l2.t200_running) {
-		FsmDelTimer(&st->l2.t200_timer, 2);
-		st->l2.t200_running = 0;
-	}
-	st->l2.rc = 0;
-	if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 2))
-		if (st->l2.l2m.debug)
-			l2m_debug(&st->l2.l2m, "FAT 2");
-
-
-	if ((chanp->impair == 2) && (st->l2.laptype == LAPB))
-		goto nodisc;
-
-	if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 9))
-		return;
-	i = sethdraddr(&(st->l2), ibh, 0);
-	ptr = DATAPTR(ibh);
-	ptr += i;
-	*ptr = 0x53;
-	ibh->datasize = i + 1;
-	enqueue_super(st, ibh);
-
-      nodisc:
-	discard_i_queue(st);
-}
-
-static void
-l2s12(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack  *st = fi->userdata;
-	struct BufHeader *ibh = arg;
-	byte           *ptr;
-	int             i;
-
-	BufPoolRelease(ibh);
-	st->l2.vs = 0;
-	st->l2.va = 0;
-	st->l2.vr = 0;
-	st->l2.sow = 0;
-	FsmChangeState(fi, ST_L2_7);
-	if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 3))
-		if (st->l2.l2m.debug)
-			l2m_debug(&st->l2.l2m, "FAT 3");
-
-	st->l2.l2man(st, DL_ESTABLISH, NULL);
-
-	if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 10))
-		return;
-	i = sethdraddr(&(st->l2), ibh, 0);
-	ptr = DATAPTR(ibh);
-	ptr += i;
-	*ptr = 0x73;
-	ibh->datasize = i + 1;
-	enqueue_super(st, ibh);
-
-}
-
-static void
-l2s14(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack  *st = fi->userdata;
-	struct BufHeader *ibh = arg;
-	struct Channel *chanp = st->l4.userdata;
-	byte           *ptr;
-	int             i, p;
-
-	ptr = DATAPTR(ibh);
-	ptr += l2addrsize(&(st->l2));
-	p = (*ptr) & 0x10;
-	BufPoolRelease(ibh);
-
-	FsmChangeState(fi, ST_L2_4);
-
-	FsmDelTimer(&st->l2.t203_timer, 3);
-	if (st->l2.t200_running) {
-		FsmDelTimer(&st->l2.t200_timer, 4);
-		st->l2.t200_running = 0;
-	}
-	if ((chanp->impair == 1) && (st->l2.laptype == LAPB))
-		goto noresponse;
-
-	if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 11))
-		return;
-	i = sethdraddr(&(st->l2), ibh, 0);
-	ptr = DATAPTR(ibh);
-	ptr += i;
-	*ptr = 0x63 | (p ? 0x10 : 0x0);
-	ibh->datasize = i + 1;
-	enqueue_super(st, ibh);
-
-      noresponse:
-	st->l2.l2man(st, DL_RELEASE, NULL);
-
-}
-
-static void
-l2s5(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack  *st = fi->userdata;
-	struct BufHeader *ibh = arg;
-	int             f;
-	byte           *data;
-
-	data = DATAPTR(ibh);
-	data += l2addrsize(&(st->l2));
-
-	f = *data & 0x10;
-	BufPoolRelease(ibh);
-
-	if (f) {
-		st->l2.vs = 0;
-		st->l2.va = 0;
-		st->l2.vr = 0;
-		st->l2.sow = 0;
-		FsmChangeState(fi, ST_L2_7);
-
-		FsmDelTimer(&st->l2.t200_timer, 5);
-		if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 4))
-			if (st->l2.l2m.debug)
-				l2m_debug(&st->l2.l2m, "FAT 4");
-
-
-		st->l2.l2man(st, DL_ESTABLISH, NULL);
-	}
-}
-
-static void
-l2s15(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack  *st = fi->userdata;
-	struct BufHeader *ibh = arg;
-	int             f;
-	byte           *data;
-
-	data = DATAPTR(ibh);
-	data += l2addrsize(&st->l2);
-
-	f = *data & 0x10;
-	BufPoolRelease(ibh);
-
-	if (f) {
-		FsmDelTimer(&st->l2.t200_timer, 6);
-		FsmChangeState(fi, ST_L2_4);
-		st->l2.l2man(st, DL_RELEASE, NULL);
-	}
-}
-
-static void
-l2s6(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack  *st = fi->userdata;
-	struct Channel *chanp = st->l4.userdata;
-	struct BufHeader *ibh = arg;
-	int             p, i, seq, rsp;
-	byte           *ptr;
-	struct Layer2  *l2;
-
-	l2 = &st->l2;
-	ptr = DATAPTR(ibh);
-
-	if (l2->laptype == LAPD) {
-		rsp = ptr[0] & 0x2;
-		if (l2->orig)
-			rsp = !rsp;
-	} else {
-		rsp = ptr[0] == 0x3;
-		if (l2->orig)
-			rsp = !rsp;
-	}
-
-	ptr += l2addrsize(l2);
-
-	if (l2->extended) {
-		p = (ptr[1] & 0x1) == 0x1;
-		seq = ptr[1] >> 1;
-	} else {
-		p = (ptr[0] & 0x10);
-		seq = (ptr[0] >> 5) & 0x7;
-	}
-	BufPoolRelease(ibh);
-
-	if ((chanp->impair == 4) && (st->l2.laptype == LAPB))
-		goto noresp;
-
-	if ((!rsp) && p) {
-		if (!BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 12)) {
-			i = sethdraddr(l2, ibh, !0);
-			ptr = DATAPTR(ibh);
-			ptr += i;
-
-			if (l2->extended) {
-				*ptr++ = 0x1;
-				*ptr++ = (l2->vr << 1) | (p ? 1 : 0);
-				i += 2;
-			} else {
-				*ptr++ = (l2->vr << 5) | 0x1 | (p ? 0x10 : 0x0);
-				i += 1;
-			}
-			ibh->datasize = i;
-			enqueue_super(st, ibh);
-		}
-	}
-      noresp:
-	if (legalnr(st, seq))
-		if (seq == st->l2.vs) {
-			setva(st, seq);
-			FsmDelTimer(&st->l2.t200_timer, 7);
-			st->l2.t200_running = 0;
-			FsmDelTimer(&st->l2.t203_timer, 8);
-			if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 5))
-				if (st->l2.l2m.debug)
-					l2m_debug(&st->l2.l2m, "FAT 5");
-
-			if (st->l2.i_queue.head)
-				st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
-		} else if (st->l2.va != seq) {
-			setva(st, seq);
-			FsmDelTimer(&st->l2.t200_timer, 9);
-			if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 6))
-				if (st->l2.l2m.debug)
-					l2m_debug(&st->l2.l2m, "FAT 6");
-
-			if (st->l2.i_queue.head)
-				st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
-		}
-}
-
-static void
-l2s7(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack  *st = fi->userdata;
-	struct BufHeader *ibh = arg;
-	int             i;
-	byte           *ptr;
-	struct IsdnCardState *sp = st->l1.hardware;
-	char            str[64];
-
-	i = sethdraddr(&st->l2, ibh, 0);
-	ptr = DATAPTR(ibh);
-
-	if (st->l2.laptype == LAPD)
-		if (sp->dlogflag) {
-			sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
-			dlogframe(sp, ptr + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
-				  str);
-		}
-	BufQueueLink(&st->l2.i_queue, ibh);
-
-	st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
-}
-
-static void
-l2s8(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack  *st = fi->userdata;
-	struct Channel *chanp = st->l4.userdata;
-	struct BufHeader *ibh = arg;
-	byte           *ptr;
-	struct BufHeader *ibh2;
-	struct IsdnCardState *sp = st->l1.hardware;
-	struct Layer2  *l2 = &(st->l2);
-	int             i, p, seq, nr, wasok;
-	char            str[64];
-
-	ptr = DATAPTR(ibh);
-	ptr += l2addrsize(l2);
-	if (l2->extended) {
-		p = (ptr[1] & 0x1) == 0x1;
-		seq = ptr[0] >> 1;
-		nr = (ptr[1] >> 1) & 0x7f;
-	} else {
-		p = (ptr[0] & 0x10);
-		seq = (ptr[0] >> 1) & 0x7;
-		nr = (ptr[0] >> 5) & 0x7;
-	}
-
-	if (l2->vr == seq) {
-		wasok = !0;
-
-		l2->vr = (l2->vr + 1) % (l2->extended ? 128 : 8);
-		l2->rejexp = 0;
-
-		ptr = DATAPTR(ibh);
-		if (st->l2.laptype == LAPD)
-			if (sp->dlogflag) {
-				sprintf(str, "Q.931 frame network->user tei %d", st->l2.tei);
-				dlogframe(st->l1.hardware, ptr + l2->ihsize,
-					ibh->datasize - l2->ihsize, str);
-			}
-	      label8_1:
-		if ((chanp->impair == 3) && (st->l2.laptype == LAPB))
-			goto noRR;
-
-		if (!BufPoolGet(&ibh2, st->l1.smallpool, GFP_ATOMIC, (void *) st, 13)) {
-			i = sethdraddr(&(st->l2), ibh2, p);
-			ptr = DATAPTR(ibh2);
-			ptr += i;
-
-			if (l2->extended) {
-				*ptr++ = 0x1;
-				*ptr++ = (l2->vr << 1) | (p ? 1 : 0);
-				i += 2;
-			} else {
-				*ptr++ = (l2->vr << 5) | 0x1 | (p ? 0x10 : 0x0);
-				i += 1;
-			}
-			ibh2->datasize = i;
-			enqueue_super(st, ibh2);
-		      noRR:
-		}
-	} else {
-	        /* n(s)!=v(r) */
-		wasok = 0;
-		BufPoolRelease(ibh);
-		if (st->l2.rejexp) {
-			if (p)
-				goto label8_1;
-		} else {
-			st->l2.rejexp = !0;
-			if (!BufPoolGet(&ibh2, st->l1.smallpool, GFP_ATOMIC, (void *) st, 14)) {
-				i = sethdraddr(&(st->l2), ibh2, p);
-				ptr = DATAPTR(ibh2);
-				ptr += i;
-
-				if (l2->extended) {
-					*ptr++ = 0x9;
-					*ptr++ = (l2->vr << 1) | (p ? 1 : 0);
-					i += 2;
-				} else {
-					*ptr++ = (l2->vr << 5) | 0x9 | (p ? 0x10 : 0x0);
-					i += 1;
-				}
-				ibh2->datasize = i;
-				enqueue_super(st, ibh2);
-			}
-		}
-	}
-
-	if (legalnr(st, nr))
-		if (nr == st->l2.vs) {
-			setva(st, nr);
-			FsmDelTimer(&st->l2.t200_timer, 10);
-			st->l2.t200_running = 0;
-			FsmDelTimer(&st->l2.t203_timer, 11);
-			if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 7))
-				if (st->l2.l2m.debug)
-					l2m_debug(&st->l2.l2m, "FAT 5");
-
-			if (st->l2.i_queue.head)
-				st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
-		} else if (nr != st->l2.va) {
-			setva(st, nr);
-			FsmDelTimer(&st->l2.t200_timer, 12);
-			if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 8))
-				if (st->l2.l2m.debug)
-					l2m_debug(&st->l2.l2m, "FAT 6");
-
-			if (st->l2.i_queue.head)
-				st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
-		}
-	if (wasok)
-		st->l2.l2l3(st, DL_DATA, ibh);
-
-}
-
-static void
-l2s17(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack  *st = fi->userdata;
-
-	st->l2.tei = (int) arg;
-	establishlink(fi);
-}
-
-static void
-enquiry_response(struct PStack *st)
-{
-	struct BufHeader *ibh2;
-	int             i;
-	byte           *ptr;
-	struct Layer2  *l2;
-
-	l2 = &st->l2;
-	if (!BufPoolGet(&ibh2, st->l1.smallpool, GFP_ATOMIC, (void *) st, 16)) {
-		i = sethdraddr(&(st->l2), ibh2, !0);
-		ptr = DATAPTR(ibh2);
-		ptr += i;
-
-		if (l2->extended) {
-			*ptr++ = 0x1;
-			*ptr++ = (l2->vr << 1) | 0x1;
-			i += 2;
-		} else {
-			*ptr++ = (l2->vr << 5) | 0x1 | 0x10;
-			i += 1;
-		}
-		ibh2->datasize = i;
-		enqueue_super(st, ibh2);
-	}
-}
-
-static void
-invoke_retransmission(struct PStack *st, int nr)
-{
-	struct Layer2  *l2 = &st->l2;
-	int             p1;
-
-	if (l2->vs != nr) {
-		while (l2->vs != nr) {
-
-			l2->vs = l2->vs - 1;
-			if (l2->vs < 0)
-				l2->vs += l2->extended ? 128 : 8;
-
-			p1 = l2->vs - l2->va;
-			if (p1 < 0)
-				p1 += l2->extended ? 128 : 8;
-			p1 = (p1 + l2->sow) % l2->window;
-
-			BufQueueLinkFront(&l2->i_queue, l2->windowar[p1]);
-		}
-		st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
-	}
-}
-
-static void
-l2s16(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack  *st = fi->userdata;
-	struct BufHeader *ibh = arg;
-	int             p, seq, rsp;
-	byte           *ptr;
-	struct Layer2  *l2;
-
-	l2 = &(st->l2);
-	ptr = DATAPTR(ibh);
-
-	if (l2->laptype == LAPD) {
-		rsp = ptr[0] & 0x2;
-		if (l2->orig)
-			rsp = !rsp;
-	} else {
-		rsp = ptr[0] == 0x3;
-		if (l2->orig)
-			rsp = !rsp;
-	}
-
-
-	ptr += l2addrsize(l2);
-
-	if (l2->extended) {
-		p = (ptr[1] & 0x1) == 0x1;
-		seq = ptr[1] >> 1;
-	} else {
-		p = (ptr[0] & 0x10);
-		seq = (ptr[0] >> 5) & 0x7;
-	}
-	BufPoolRelease(ibh);
-
-	if ((!rsp) && p)
-		enquiry_response(st);
-
-	if (!legalnr(st, seq))
-		return;
-
-	setva(st, seq);
-	invoke_retransmission(st, seq);
-
-}
-
-static void
-l2s19(struct FsmInst *fi, int event, void *arg)
-{
-	FsmChangeState(fi, ST_L2_4);
-}
-
-static void
-l2s20(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack  *st = fi->userdata;
-	int             i;
-	struct BufHeader *ibh;
-	byte           *ptr;
-
-	if (st->l2.rc == st->l2.n200) {
-		FsmChangeState(fi, ST_L2_4);
-		st->l2.l2man(st, DL_RELEASE, NULL);
-	} else {
-		st->l2.rc++;
-
-		if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 9))
-			if (st->l2.l2m.debug)
-				l2m_debug(&st->l2.l2m, "FAT 7");
-
-		if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 15))
-			return;
-
-		i = sethdraddr(&st->l2, ibh, 0);
-		ptr = DATAPTR(ibh);
-		ptr += i;
-		if (st->l2.extended)
-			*ptr = 0x7f;
-		else
-			*ptr = 0x3f;
-		ibh->datasize = i + 1;
-		enqueue_super(st, ibh);
-	}
-}
-
-static void
-l2s21(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack  *st = fi->userdata;
-	struct Channel *chanp = st->l4.userdata;
-	int             i;
-	struct BufHeader *ibh;
-	byte           *ptr;
-
-	if (st->l2.rc == st->l2.n200) {
-		FsmChangeState(fi, ST_L2_4);
-		st->l2.l2man(st, DL_RELEASE, NULL);
-	} else {
-		st->l2.rc++;
-
-		if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 10))
-			if (st->l2.l2m.debug)
-				l2m_debug(&st->l2.l2m, "FAT 8");
-
-
-		if ((chanp->impair == 2) && (st->l2.laptype == LAPB))
-			goto nodisc;
-
-		if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 15))
-			return;
-
-		i = sethdraddr(&st->l2, ibh, 0);
-		ptr = DATAPTR(ibh);
-		ptr += i;
-		*ptr = 0x53;
-		ibh->datasize = i + 1;
-		enqueue_super(st, ibh);
-	      nodisc:
-
-	}
-}
-
-static void
-l2s22(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack  *st = fi->userdata;
-	struct BufHeader *ibh;
-	struct Layer2  *l2 = &st->l2;
-	byte           *ptr;
-	int             p1;
-
-	if (!cansend(st))
-		return;
-
-	if (BufQueueUnlink(&ibh, &l2->i_queue))
-		return;
-
-
-	p1 = l2->vs - l2->va;
-	if (p1 < 0)
-		p1 += l2->extended ? 128 : 8;
-	p1 = (p1 + l2->sow) % l2->window;
-	l2->windowar[p1] = ibh;
-
-	ptr = DATAPTR(ibh);
-	ptr += l2addrsize(l2);
-
-	if (l2->extended) {
-		*ptr++ = l2->vs << 1;
-		*ptr++ = (l2->vr << 1) | 0x1;
-		l2->vs = (l2->vs + 1) % 128;
-	} else {
-		*ptr++ = (l2->vr << 5) | (l2->vs << 1) | 0x10;
-		l2->vs = (l2->vs + 1) % 8;
-	}
-
-	st->l2.l2l1(st, PH_DATA_PULLED, ibh);
-
-	if (!st->l2.t200_running) {
-		FsmDelTimer(&st->l2.t203_timer, 13);
-		if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 11))
-			if (st->l2.l2m.debug)
-				l2m_debug(&st->l2.l2m, "FAT 9");
-
-		st->l2.t200_running = !0;
-	}
-	if (l2->i_queue.head && cansend(st))
-		st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
-
-}
-
-static void
-transmit_enquiry(struct PStack *st)
-{
-	struct BufHeader *ibh;
-	byte           *ptr;
-
-	if (!BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 12)) {
-		ptr = DATAPTR(ibh);
-		ptr += sethdraddr(&st->l2, ibh, 0);
-
-		if (st->l2.extended) {
-			*ptr++ = 0x1;
-			*ptr++ = (st->l2.vr << 1) | 1;
-		} else {
-			*ptr++ = (st->l2.vr << 5) | 0x11;
-		}
-		ibh->datasize = ptr - DATAPTR(ibh);
-		enqueue_super(st, ibh);
-		if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 12))
-			if (st->l2.l2m.debug)
-				l2m_debug(&st->l2.l2m, "FAT 10");
-
-		st->l2.t200_running = !0;
-	}
-}
-
-static void
-l2s23(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack  *st = fi->userdata;
-
-	st->l2.t200_running = 0;
-
-	st->l2.rc = 1;
-	FsmChangeState(fi, ST_L2_8);
-	transmit_enquiry(st);
-}
-
-static void
-l2s24(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack  *st = fi->userdata;
-	struct BufHeader *ibh = arg;
-	int             p, seq, rsp;
-	byte           *ptr;
-	struct Layer2  *l2;
-
-	l2 = &st->l2;
-	ptr = DATAPTR(ibh);
-
-	if (l2->laptype == LAPD) {
-		rsp = ptr[0] & 0x2;
-		if (l2->orig)
-			rsp = !rsp;
-	} else {
-		rsp = ptr[0] == 0x3;
-		if (l2->orig)
-			rsp = !rsp;
-	}
-
-
-	ptr += l2addrsize(l2);
-
-	if (l2->extended) {
-		p = (ptr[1] & 0x1) == 0x1;
-		seq = ptr[1] >> 1;
-	} else {
-		p = (ptr[0] & 0x10);
-		seq = (ptr[0] >> 5) & 0x7;
-	}
-	BufPoolRelease(ibh);
-
-	if (rsp && p) {
-		if (legalnr(st, seq)) {
-			FsmChangeState(fi, ST_L2_7);
-			setva(st, seq);
-			if (st->l2.t200_running) {
-				FsmDelTimer(&st->l2.t200_timer, 14);
-				st->l2.t200_running = 0;
-			}
-			if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 13))
-				if (st->l2.l2m.debug)
-					l2m_debug(&st->l2.l2m, "FAT 11");
-
-			invoke_retransmission(st, seq);
-		}
-	} else {
-		if (!rsp && p)
-			enquiry_response(st);
-		if (legalnr(st, seq)) {
-			setva(st, seq);
-		}
-	}
-}
-
-static void
-l2s25(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack  *st = fi->userdata;
-
-	st->l2.rc = 0;
-	FsmChangeState(fi, ST_L2_8);
-	transmit_enquiry(st);
-}
-
-static void
-l2s26(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack  *st = fi->userdata;
-
-	if (st->l2.rc == st->l2.n200) {
-		l2s13(fi, event, NULL);
-	} else {
-		st->l2.rc++;
-		transmit_enquiry(st);
-	}
-}
-
-static void
-l2s27(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack  *st = fi->userdata;
-	struct BufHeader *ibh = arg;
-	byte           *ptr;
-	int             i, p, est;
-
-	ptr = DATAPTR(ibh);
-	ptr += l2addrsize(&st->l2);
-
-	if (st->l2.extended)
-		p = ptr[1] & 0x1;
-	else
-		p = ptr[0] & 0x10;
-
-	BufPoolRelease(ibh);
-
-	if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 10))
-		return;
-	i = sethdraddr(&st->l2, ibh, 0);
-	ptr = DATAPTR(ibh);
-	ptr += i;
-	*ptr = 0x63 | p;
-	ibh->datasize = i + 1;
-	enqueue_super(st, ibh);
-
-	if (st->l2.vs != st->l2.va) {
-		discard_i_queue(st);
-		est = !0;
-	} else
-		est = 0;
-
-	FsmDelTimer(&st->l2.t200_timer, 15);
-	st->l2.t200_running = 0;
-
-	if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 3))
-		if (st->l2.l2m.debug)
-			l2m_debug(&st->l2.l2m, "FAT 12");
-
-	st->l2.vs = 0;
-	st->l2.va = 0;
-	st->l2.vr = 0;
-	st->l2.sow = 0;
-
-
-	if (est)
-		st->l2.l2man(st, DL_ESTABLISH, NULL);
-
-}
-
-static void
-l2s28(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack  *st = fi->userdata;
-	struct BufHeader *ibh = arg;
-	byte           *ptr;
-	char            tmp[64];
-
-	ptr = DATAPTR(ibh);
-	ptr += l2addrsize(&st->l2);
-	ptr++;
-
-	if (st->l2.l2m.debug) {
-		if (st->l2.extended)
-			sprintf(tmp, "FRMR information %2x %2x %2x %2x %2x",
-				ptr[0], ptr[1], ptr[2], ptr[3], ptr[4]);
-		else
-			sprintf(tmp, "FRMR information %2x %2x %2x",
-				ptr[0], ptr[1], ptr[2]);
-
-		l2m_debug(&st->l2.l2m, tmp);
-	}
-	BufPoolRelease(ibh);
-}
-
-static int
-IsUI(byte * data, int ext)
-{
-	return ((data[0] & 0xef) == 0x3);
-}
-
-static int
-IsUA(byte * data, int ext)
-{
-	return ((data[0] & 0xef) == 0x63);
-}
-
-static int
-IsDISC(byte * data, int ext)
-{
-	return ((data[0] & 0xef) == 0x43);
-}
-
-static int
-IsRR(byte * data, int ext)
-{
-	if (ext)
-		return (data[0] == 0x1);
-	else
-		return ((data[0] & 0xf) == 1);
-}
-
-static int
-IsI(byte * data, int ext)
-{
-	return ((data[0] & 0x1) == 0x0);
-}
-
-static int
-IsSABMX(byte * data, int ext)
-{
-	return (ext ? data[0] == 0x7f : data[0] == 0x3f);
-}
-
-static int
-IsREJ(byte * data, int ext)
-{
-	return (ext ? data[0] == 0x9 : (data[0] & 0xf) == 0x9);
-}
-
-static int
-IsFRMR(byte * data, int ext)
-{
-	return ((data[0] & 0xef) == 0x87);
-}
-
-static int
-IsRNR(byte * data, int ext)
-{
-	if (ext)
-		return (data[0] == 0x5);
-	else
-		return ((data[0] & 0xf) == 5);
-}
-
-static struct FsmNode L2FnList[] =
-{
-	{ST_L2_1, EV_L2_DL_ESTABLISH, l2s1},
-	{ST_L2_1, EV_L2_MDL_NOTEIPROC, l2s19},
-	{ST_L2_3, EV_L2_MDL_ASSIGN, l2s17},
-	{ST_L2_4, EV_L2_DL_UNIT_DATA, l2s2},
-	{ST_L2_4, EV_L2_DL_ESTABLISH, l2s11},
-	{ST_L2_7, EV_L2_DL_UNIT_DATA, l2s2},
-	{ST_L2_7, EV_L2_DL_DATA, l2s7},
-	{ST_L2_7, EV_L2_DL_RELEASE, l2s13},
-	{ST_L2_7, EV_L2_ACK_PULL, l2s22},
-	{ST_L2_8, EV_L2_DL_RELEASE, l2s13},
-
-	{ST_L2_1, EV_L2_UI, l2s3},
-	{ST_L2_4, EV_L2_UI, l2s3},
-	{ST_L2_4, EV_L2_SABMX, l2s12},
-	{ST_L2_5, EV_L2_UA, l2s5},
-	{ST_L2_6, EV_L2_UA, l2s15},
-	{ST_L2_7, EV_L2_UI, l2s3},
-	{ST_L2_7, EV_L2_DISC, l2s14},
-	{ST_L2_7, EV_L2_I, l2s8},
-	{ST_L2_7, EV_L2_RR, l2s6},
-	{ST_L2_7, EV_L2_REJ, l2s16},
-	{ST_L2_7, EV_L2_SABMX, l2s27},
-	{ST_L2_7, EV_L2_FRMR, l2s28},
-	{ST_L2_8, EV_L2_RR, l2s24},
-	{ST_L2_8, EV_L2_DISC, l2s14},
-	{ST_L2_8, EV_L2_FRMR, l2s28},
-
-	{ST_L2_5, EV_L2_T200, l2s20},
-	{ST_L2_6, EV_L2_T200, l2s21},
-	{ST_L2_7, EV_L2_T200, l2s23},
-	{ST_L2_7, EV_L2_T203, l2s25},
-	{ST_L2_8, EV_L2_T200, l2s26},
-};
-
-#define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode))
-
-static void
-isdnl2_l1l2(struct PStack *st, int pr, struct BufHeader *arg)
-{
-	struct BufHeader *ibh;
-	byte           *datap;
-	int             ret = !0;
-
-	switch (pr) {
-	  case (PH_DATA):
-
-		  ibh = arg;
-		  datap = DATAPTR(ibh);
-		  datap += l2addrsize(&st->l2);
-
-		  if (IsI(datap, st->l2.extended))
-			  ret = FsmEvent(&st->l2.l2m, EV_L2_I, ibh);
-		  else if (IsRR(datap, st->l2.extended))
-			  ret = FsmEvent(&st->l2.l2m, EV_L2_RR, ibh);
-		  else if (IsUI(datap, st->l2.extended))
-			  ret = FsmEvent(&st->l2.l2m, EV_L2_UI, ibh);
-		  else if (IsSABMX(datap, st->l2.extended))
-			  ret = FsmEvent(&st->l2.l2m, EV_L2_SABMX, ibh);
-		  else if (IsUA(datap, st->l2.extended))
-			  ret = FsmEvent(&st->l2.l2m, EV_L2_UA, ibh);
-		  else if (IsDISC(datap, st->l2.extended))
-			  ret = FsmEvent(&st->l2.l2m, EV_L2_DISC, ibh);
-		  else if (IsREJ(datap, st->l2.extended))
-			  ret = FsmEvent(&st->l2.l2m, EV_L2_REJ, ibh);
-		  else if (IsFRMR(datap, st->l2.extended))
-			  ret = FsmEvent(&st->l2.l2m, EV_L2_FRMR, ibh);
-		  else if (IsRNR(datap, st->l2.extended))
-			  ret = FsmEvent(&st->l2.l2m, EV_L2_RNR, ibh);
-
-		  if (ret)
-			  BufPoolRelease(ibh);
-
-		  break;
-	  case (PH_PULL_ACK):
-		  FsmEvent(&st->l2.l2m, EV_L2_ACK_PULL, arg);
-		  break;
-	}
-}
-
-static void
-isdnl2_l3l2(struct PStack *st, int pr,
-	    void *arg)
-{
-	switch (pr) {
-	  case (DL_DATA):
-		  if (FsmEvent(&st->l2.l2m, EV_L2_DL_DATA, arg))
-			  BufPoolRelease((struct BufHeader *) arg);
-		  break;
-	  case (DL_UNIT_DATA):
-		  if (FsmEvent(&st->l2.l2m, EV_L2_DL_UNIT_DATA, arg))
-			  BufPoolRelease((struct BufHeader *) arg);
-		  break;
-	}
-}
-
-static void
-isdnl2_manl2(struct PStack *st, int pr,
-	     void *arg)
-{
-	switch (pr) {
-	  case (DL_ESTABLISH):
-		  FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH, arg);
-		  break;
-	  case (DL_RELEASE):
-		  FsmEvent(&st->l2.l2m, EV_L2_DL_RELEASE, arg);
-		  break;
-	  case (MDL_NOTEIPROC):
-		  FsmEvent(&st->l2.l2m, EV_L2_MDL_NOTEIPROC, NULL);
-		  break;
-	}
-}
-
-static void
-isdnl2_teil2(struct PStack *st, int pr,
-	     void *arg)
-{
-	switch (pr) {
-	  case (MDL_ASSIGN):
-		  FsmEvent(&st->l2.l2m, EV_L2_MDL_ASSIGN, arg);
-		  break;
-	}
-}
-
-void
-releasestack_isdnl2(struct PStack *st)
-{
-	FsmDelTimer(&st->l2.t200_timer, 15);
-	FsmDelTimer(&st->l2.t203_timer, 16);
-}
-
-static void
-l2m_debug(struct FsmInst *fi, char *s)
-{
-	struct PStack  *st = fi->userdata;
-	char            tm[32], str[256];
-
-	jiftime(tm, jiffies);
-	sprintf(str, "%s %s %s\n", tm, st->l2.debug_id, s);
-	teles_putstatus(str);
-}
-
-
-void
-setstack_isdnl2(struct PStack *st, char *debug_id)
-{
-	st->l1.l1l2 = isdnl2_l1l2;
-	st->l3.l3l2 = isdnl2_l3l2;
-	st->ma.manl2 = isdnl2_manl2;
-	st->ma.teil2 = isdnl2_teil2;
-
-	st->l2.uihsize = l2headersize(&st->l2, !0);
-	st->l2.ihsize = l2headersize(&st->l2, 0);
-	BufQueueInit(&(st->l2.i_queue));
-	st->l2.rejexp = 0;
-	st->l2.debug = 1;
-
-	st->l2.l2m.fsm = &l2fsm;
-	st->l2.l2m.state = ST_L2_1;
-	st->l2.l2m.debug = 0;
-	st->l2.l2m.userdata = st;
-	st->l2.l2m.printdebug = l2m_debug;
-	strcpy(st->l2.debug_id, debug_id);
-
-	FsmInitTimer(&st->l2.l2m, &st->l2.t200_timer);
-	FsmInitTimer(&st->l2.l2m, &st->l2.t203_timer);
-	st->l2.t200_running = 0;
-}
-
-void
-setstack_transl2(struct PStack *st)
-{
-}
-
-void
-releasestack_transl2(struct PStack *st)
-{
-}
-
-void
-Isdnl2New(void)
-{
-	l2fsm.state_count = L2_STATE_COUNT;
-	l2fsm.event_count = L2_EVENT_COUNT;
-	l2fsm.strEvent = strL2Event;
-	l2fsm.strState = strL2State;
-	FsmNew(&l2fsm, L2FnList, L2_FN_COUNT);
-}
-
-void
-Isdnl2Free(void)
-{
-	FsmFree(&l2fsm);
-}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/isdnl3.c linux/drivers/isdn/teles/isdnl3.c
--- v2.0.30/linux/drivers/isdn/teles/isdnl3.c	Tue Nov 12 22:36:20 1996
+++ linux/drivers/isdn/teles/isdnl3.c	Wed Dec 31 16:00:00 1969
@@ -1,663 +0,0 @@
-/* $Id: isdnl3.c,v 1.11 1996/09/29 19:41:58 fritz Exp $
- *
- * $Log: isdnl3.c,v $
- * Revision 1.11  1996/09/29 19:41:58  fritz
- * Bugfix: ignore unknown frames.
- *
- * Revision 1.10  1996/09/25 18:32:43  keil
- * response for STATUS_ENQ message added
- *
- * Revision 1.9  1996/06/06 14:22:27  fritz
- * Changed level of "non-digital call..." message, since
- * with audio support, this is quite normal.
- *
- * Revision 1.8  1996/06/03 20:35:04  fritz
- * Fixed typos.
- *
- * Revision 1.7  1996/06/03 20:03:39  fritz
- * Fixed typos.
- *
- * Revision 1.6  1996/05/21 11:33:50  keil
- * Adding SETUP_ACKNOWLEDGE as answer of a SETUP message.
- *
- * Revision 1.5  1996/05/18 01:37:16  fritz
- * Added spelling corrections and some minor changes
- * to stay in sync with kernel.
- *
- * Revision 1.4  1996/05/17 03:46:16  fritz
- * General cleanup.
- *
- * Revision 1.3  1996/04/30 21:57:53  isdn4dev
- * remove some debugging code, improve callback   Karsten Keil
- *
- * Revision 1.2  1996/04/20 16:45:05  fritz
- * Changed to report all incoming calls to Linklevel, not just those
- * with Service 7.
- * Misc. typos
- *
- * Revision 1.1  1996/04/13 10:24:45  fritz
- * Initial revision
- *
- *
- */
-#define __NO_VERSION__
-#define P_1TR6
-#include "teles.h"
-#include "l3_1TR6.h"
-#define DEBUG_1TR6 0
-
-static void
-i_down(struct PStack *st,
-       struct BufHeader *ibh)
-{
-	st->l3.l3l2(st, DL_DATA, ibh);
-}
-
-static void
-newl3state(struct PStack *st, int state)
-{
-	st->l3.state = state;
-	if (DEBUG_1TR6 > 4)
-		printk(KERN_INFO "isdnl3: bc:%d cr:%x new state %d\n",
-		       st->pa->bchannel, st->pa->callref, state);
-
-}
-
-static void
-l3_message(struct PStack *st, int mt)
-{
-	struct BufHeader *dibh;
-	byte           *p;
-	int             size;
-
-	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 18);
-	p = DATAPTR(dibh);
-	p += st->l2.ihsize;
-	size = st->l2.ihsize;
-
-	*p++ = 0x8;
-	*p++ = 0x1;
-	*p++ = st->l3.callref;
-	*p++ = mt;
-	size += 4;
-
-	dibh->datasize = size;
-	i_down(st, dibh);
-}
-
-static void
-l3s3(struct PStack *st, byte pr, void *arg)
-{
-	l3_message(st, MT_RELEASE);
-	newl3state(st, 19);
-}
-
-static void
-l3s4(struct PStack *st, byte pr, void *arg)
-{
-	struct BufHeader *ibh = arg;
-
-	BufPoolRelease(ibh);
-	newl3state(st, 0);
-	st->l3.l3l4(st, CC_RELEASE_CNF, NULL);
-}
-
-static void
-l3s4_1(struct PStack *st, byte pr, void *arg)
-{
-	struct BufHeader *ibh = arg;
-
-	BufPoolRelease(ibh);
-	newl3state(st, 19);
-	l3_message(st, MT_RELEASE);
-	st->l3.l3l4(st, CC_RELEASE_CNF, NULL);
-}
-
-static void
-l3s5(struct PStack *st, byte pr,
-     void *arg)
-{
-	struct BufHeader *dibh;
-	byte           *p;
-	char           *teln;
-
-	st->l3.callref = st->pa->callref;
-	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 19);
-	p = DATAPTR(dibh);
-	p += st->l2.ihsize;
-
-	*p++ = 0x8;
-	*p++ = 0x1;
-	*p++ = st->l3.callref;
-	*p++ = MT_SETUP;
-	*p++ = 0xa1;
-
-	/*
-         * Set Bearer Capability, Map info from 1TR6-convention to EDSS1
-         */
-	switch (st->pa->info) {
-	  case 1:		/* Telephony                               */
-		  *p++ = 0x4;	/* BC-IE-code                              */
-		  *p++ = 0x3;	/* Length                                  */
-		  *p++ = 0x90;	/* Coding Std. national, 3.1 kHz audio     */
-		  *p++ = 0x90;	/* Circuit-Mode 64kbps                     */
-		  *p++ = 0xa3;	/* A-Law Audio                             */
-		  break;
-	  case 5:		/* Datatransmission 64k, BTX               */
-	  case 7:		/* Datatransmission 64k                    */
-	  default:
-		  *p++ = 0x4;	/* BC-IE-code                              */
-		  *p++ = 0x2;	/* Length                                  */
-		  *p++ = 0x88;	/* Coding Std. nat., unrestr. dig. Inform. */
-		  *p++ = 0x90;	/* Packet-Mode 64kbps                      */
-		  break;
-	}
-	/*
-	 * What about info2? Mapping to High-Layer-Compatibility?
-	 */
-	if (st->pa->calling[0] != '\0') {
-		*p++ = 0x6c;
-		*p++ = strlen(st->pa->calling) + 1;
-		/* Classify as AnyPref. */
-		*p++ = 0x81;	/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
-		teln = st->pa->calling;
-		while (*teln)
-			*p++ = *teln++ & 0x7f;
-	}
-	*p++ = 0x70;
-	*p++ = strlen(st->pa->called) + 1;
-	/* Classify as AnyPref. */
-	*p++ = 0x81;		/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
-
-	teln = st->pa->called;
-	while (*teln)
-		*p++ = *teln++ & 0x7f;
-
-
-	dibh->datasize = p - DATAPTR(dibh);
-
-	newl3state(st, 1);
-	i_down(st, dibh);
-
-}
-
-static void
-l3s6(struct PStack *st, byte pr, void *arg)
-{
-	byte           *p;
-	struct BufHeader *ibh = arg;
-
-	p = DATAPTR(ibh);
-	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
-			0x18, 0))) {
-		st->pa->bchannel = p[2] & 0x3;
-	} else
-		printk(KERN_WARNING "octect 3 not found\n");
-
-	BufPoolRelease(ibh);
-	newl3state(st, 3);
-	st->l3.l3l4(st, CC_PROCEEDING_IND, NULL);
-}
-
-static void
-l3s7(struct PStack *st, byte pr, void *arg)
-{
-	struct BufHeader *ibh = arg;
-
-	BufPoolRelease(ibh);
-	newl3state(st, 12);
-	st->l3.l3l4(st, CC_DISCONNECT_IND, NULL);
-}
-
-static void
-l3s8(struct PStack *st, byte pr, void *arg)
-{
-	struct BufHeader *ibh = arg;
-
-	BufPoolRelease(ibh);
-	st->l3.l3l4(st, CC_SETUP_CNF, NULL);
-	newl3state(st, 10);
-}
-
-static void
-l3s11(struct PStack *st, byte pr, void *arg)
-{
-	struct BufHeader *ibh = arg;
-
-	BufPoolRelease(ibh);
-	newl3state(st, 4);
-	st->l3.l3l4(st, CC_ALERTING_IND, NULL);
-}
-
-static void
-l3s12(struct PStack *st, byte pr, void *arg)
-{
-	byte           *p;
-	int		bcfound = 0;
-	struct BufHeader *ibh = arg;
-
-	p = DATAPTR(ibh);
-	p += st->l2.uihsize;
-	st->pa->callref = getcallref(p);
-	st->l3.callref = 0x80 + st->pa->callref;
-
-	/*
-         * Channel Identification
-         */
-	p = DATAPTR(ibh);
-	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
-			0x18, 0))) {
-		st->pa->bchannel = p[2] & 0x3;
-		bcfound++ ;
-	} else
-		printk(KERN_WARNING "l3s12: Channel ident not found\n");
-
-	p = DATAPTR(ibh);
-	if (st->protocol == ISDN_PTYPE_1TR6) {
-		if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, 0x01, 6))) {
-			st->pa->info = p[2];
-			st->pa->info2 = p[3];
-		} else
-			printk(KERN_WARNING "l3s12(1TR6): ServiceIndicator not found\n");
-	} else {
-		/*
-                 * Bearer Capabilities
-                 */
-		if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, 0x04, 0))) {
-			switch (p[2] & 0x1f) {
-			  case 0x00:
-                                  /* Speech */
-			  case 0x10:
-                                  /* 3.1 Khz audio */
-				  st->pa->info = 1;
-				  break;
-			  case 0x08:
-                                  /* Unrestricted digital information */
-				  st->pa->info = 7;
-				  break;
-			  case 0x09:
-                                  /* Restricted digital information */
-				  st->pa->info = 2;
-				  break;
-			  case 0x11:
-                                  /* Unrestr. digital information  with tones/announcements */
-				  st->pa->info = 3;
-				  break;
-			  case 0x18:
-                                  /* Video */
-				  st->pa->info = 4;
-				  break;
-			  default:
-				  st->pa->info = 0;
-			}
-		} else
-			printk(KERN_WARNING "l3s12: Bearer capabilities not found\n");
-	}
-
-	p = DATAPTR(ibh);
-	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
-			0x70, 0)))
-		iecpy(st->pa->called, p, 1);
-	else
-		strcpy(st->pa->called, "");
-
-	p = DATAPTR(ibh);
-	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
-			0x6c, 0))) {
-		if (st->protocol == ISDN_PTYPE_1TR6)
-			iecpy(st->pa->calling, p, 1);
-		else
-			iecpy(st->pa->calling, p, 2);
-	} else
-		strcpy(st->pa->calling, "");
-	BufPoolRelease(ibh);
-
-        if (bcfound) {
-                if (st->pa->info != 7) {
-                        printk(KERN_DEBUG "non-digital call: %s -> %s\n",
-                               st->pa->calling,
-                               st->pa->called);
-                }
-                newl3state(st, 6);
-                st->l3.l3l4(st, CC_SETUP_IND, NULL);
-        }
-}
-
-static void
-l3s13(struct PStack *st, byte pr, void *arg)
-{
-	newl3state(st, 0);
-}
-
-static void
-l3s16(struct PStack *st, byte pr,
-      void *arg)
-{
-	st->l3.callref = 0x80 + st->pa->callref;
-	l3_message(st, MT_CONNECT);
-	newl3state(st, 8);
-}
-
-static void
-l3s17(struct PStack *st, byte pr, void *arg)
-{
-	struct BufHeader *ibh = arg;
-
-	BufPoolRelease(ibh);
-	st->l3.l3l4(st, CC_SETUP_COMPLETE_IND, NULL);
-	newl3state(st, 10);
-}
-
-static void
-l3s18(struct PStack *st, byte pr, void *arg)
-{
-	struct BufHeader *dibh;
-	byte           *p;
-	int             size;
-
-	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 20);
-	p = DATAPTR(dibh);
-	p += st->l2.ihsize;
-	size = st->l2.ihsize;
-
-	*p++ = 0x8;
-	*p++ = 0x1;
-	*p++ = st->l3.callref;
-	*p++ = MT_DISCONNECT;
-	size += 4;
-
-	*p++ = IE_CAUSE;
-	*p++ = 0x2;
-	*p++ = 0x80;
-	*p++ = 0x90;
-	size += 4;
-
-	dibh->datasize = size;
-	i_down(st, dibh);
-
-	newl3state(st, 11);
-}
-
-static void
-l3s19(struct PStack *st, byte pr, void *arg)
-{
-	struct BufHeader *ibh = arg;
-
-	BufPoolRelease(ibh);
-	newl3state(st, 0);
-	l3_message(st, MT_RELEASE_COMPLETE);
-	st->l3.l3l4(st, CC_RELEASE_IND, NULL);
-}
-
-static void
-l3s20(struct PStack *st, byte pr,
-      void *arg)
-{
-	l3_message(st, MT_ALERTING);
-	newl3state(st, 7);
-}
-
-static void
-l3s21(struct PStack *st, byte pr, void *arg)
-{
-	struct BufHeader *dibh=arg;
-	byte           *p;
-	int             size;
-
-	BufPoolRelease(dibh);
-	
-	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 20);
-	p = DATAPTR(dibh);
-	p += st->l2.ihsize;
-	size = st->l2.ihsize;
-
-	*p++ = 0x8;
-	*p++ = 0x1;
-	*p++ = st->l3.callref;
-	*p++ = MT_STATUS;
-	size += 4;
-
-	*p++ = IE_CAUSE;
-	*p++ = 0x2;
-	*p++ = 0x80;
-	*p++ = 0x9E; /* answer status enquire */
-	size += 4;
-
-	*p++ = 0x14; /* CallState */
-	*p++ = 0x1;
-	*p++ = st->l3.state & 0x3f; /* ISO L3 CallState */
-	size += 3;
-
-	dibh->datasize = size;
-	i_down(st, dibh);
-
-}
-
-struct stateentry {
-	int             state;
-	byte            primitive;
-	void            (*rout) (struct PStack *, byte, void *);
-};
-
-static struct stateentry downstatelist[] =
-{
-        {0,CC_SETUP_REQ,l3s5},
-        {1,CC_DISCONNECT_REQ,l3s18},
-        {1,CC_RELEASE_REQ,l3s3},
-        {1,CC_DLRL,l3s13},
-        {3,CC_DISCONNECT_REQ,l3s18},
-        {3,CC_RELEASE_REQ,l3s3},
-        {3,CC_DLRL,l3s13},
-        {4,CC_RELEASE_REQ,l3s3},
-        {4,CC_DISCONNECT_REQ,l3s18},
-        {4,CC_DLRL,l3s13},
-        {6,CC_RELEASE_REQ,l3s3},
-        {6,CC_DISCONNECT_REQ,l3s18},
-        {6,CC_ALERTING_REQ,l3s20},
-        {6,CC_DLRL,l3s13},
-        {7,CC_RELEASE_REQ,l3s3},
-        {7,CC_SETUP_RSP,l3s16},
-        {7,CC_DLRL,l3s13},
-        {8,CC_RELEASE_REQ,l3s3},
-        {8,CC_DISCONNECT_REQ,l3s18},
-        {8,CC_DLRL,l3s13},
-        {10,CC_DISCONNECT_REQ,l3s18},
-        {10,CC_RELEASE_REQ,l3s3},
-        {10,CC_DLRL,l3s13},
-        {11,CC_RELEASE_REQ,l3s3},
-        {12,CC_RELEASE_REQ,l3s3},
-        {19,CC_DLRL,l3s13},
-};
-
-static int      downsllen = sizeof(downstatelist) /
-sizeof(struct stateentry);
-
-static struct stateentry datastatelist[] =
-{
-        {0,MT_STATUS_ENQUIRY,l3s21},
-        {0,MT_SETUP,l3s12},
-        {1,MT_STATUS_ENQUIRY,l3s21},
-        {1,MT_CALL_PROCEEDING,l3s6},
-        {1,MT_SETUP_ACKNOWLEDGE,l3s6},
-        {1,MT_RELEASE_COMPLETE,l3s4},
-        {1,MT_RELEASE,l3s19},
-        {1,MT_DISCONNECT,l3s7},
-        {3,MT_STATUS_ENQUIRY,l3s21},
-        {3,MT_DISCONNECT,l3s7},
-        {3,MT_CONNECT,l3s8},
-        {3,MT_ALERTING,l3s11},
-        {3,MT_RELEASE,l3s19},
-        {3,MT_RELEASE_COMPLETE,l3s4},
-        {4,MT_STATUS_ENQUIRY,l3s21},
-        {4,MT_CONNECT,l3s8},
-        {4,MT_DISCONNECT,l3s7},
-        {4,MT_RELEASE,l3s19},
-        {4,MT_RELEASE_COMPLETE,l3s4},
-        {8,MT_STATUS_ENQUIRY,l3s21},
-        {6,MT_SETUP,l3s12},
-        {8,MT_STATUS_ENQUIRY,l3s21},
-        {7,MT_RELEASE,l3s19},
-        {7,MT_RELEASE_COMPLETE,l3s4_1},
-        {7,MT_DISCONNECT,l3s7},
-        {8,MT_STATUS_ENQUIRY,l3s21},
-        {8,MT_RELEASE,l3s19},
-        {8,MT_CONNECT_ACKNOWLEDGE,l3s17},
-        {8,MT_DISCONNECT,l3s7},
-        {8,MT_RELEASE_COMPLETE,l3s4_1},
-        {10,MT_STATUS_ENQUIRY,l3s21},
-        {10,MT_DISCONNECT,l3s7},
-        {10,MT_RELEASE,l3s19},
-        {10,MT_RELEASE_COMPLETE,l3s4_1},
-        {11,MT_STATUS_ENQUIRY,l3s21},
-        {11,MT_RELEASE,l3s19},
-        {11,MT_RELEASE_COMPLETE,l3s4},
-        {19,MT_STATUS_ENQUIRY,l3s21},
-        {19,MT_RELEASE_COMPLETE,l3s4},
-};
-
-static int      datasllen = sizeof(datastatelist) /
-sizeof(struct stateentry);
-
-#ifdef P_1TR6
-#include "l3_1TR6.c"
-#endif
-
-static void
-l3up(struct PStack *st,
-     int pr, void *arg)
-{
-	int             i, mt, size;
-	byte           *ptr;
-	struct BufHeader *ibh = arg;
-
-	if (pr == DL_DATA) {
-		ptr = DATAPTR(ibh);
-		ptr += st->l2.ihsize;
-		size = ibh->datasize - st->l2.ihsize;
-		mt = ptr[3];
-		switch (ptr[0]) {
-#ifdef P_1TR6
-		  case PROTO_DIS_N0:
-			  BufPoolRelease(ibh);
-			  break;
-		  case PROTO_DIS_N1:
-			  for (i = 0; i < datasl_1tr6t_len; i++)
-				  if ((st->l3.state == datastatelist_1tr6t[i].state) &&
-				      (mt == datastatelist_1tr6t[i].primitive))
-					  break;
-			  if (i == datasl_1tr6t_len) {
-				  BufPoolRelease(ibh);
-				  if (DEBUG_1TR6 > 0)
-					  printk(KERN_INFO "isdnl3up unhandled 1tr6 state %d MT %x\n",
-						 st->l3.state, mt);
-			  } else
-				  datastatelist_1tr6t[i].rout(st, pr, ibh);
-			  break;
-#endif
-		  case PROTO_EURO:	/* E-DSS1 */
-			  for (i = 0; i < datasllen; i++)
-				  if ((st->l3.state == datastatelist[i].state) &&
-				      (mt == datastatelist[i].primitive))
-					  break;
-			  if (i == datasllen) {
-				  BufPoolRelease(ibh);
-				  if (DEBUG_1TR6 > 0)
-			  	  	printk(KERN_INFO "isdnl3up unhandled E-DSS1 state %d MT %x\n",
-				 		st->l3.state, mt);
-			  } else
-				  datastatelist[i].rout(st, pr, ibh);
-			  break;
-		  default:
-		          BufPoolRelease(ibh);
-			  break;
-		}
-	} else if (pr == DL_UNIT_DATA) {
-		ptr = DATAPTR(ibh);
-		ptr += st->l2.uihsize;
-		size = ibh->datasize - st->l2.uihsize;
-		mt = ptr[3];
-		switch (ptr[0]) {
-#ifdef P_1TR6
-		  case PROTO_DIS_N0:
-			  BufPoolRelease(ibh);
-			  break;
-		  case PROTO_DIS_N1:
-			  for (i = 0; i < datasl_1tr6t_len; i++)
-				  if ((st->l3.state == datastatelist_1tr6t[i].state) &&
-				      (mt == datastatelist_1tr6t[i].primitive))
-					  break;
-			  if (i == datasl_1tr6t_len) {
-				  if (DEBUG_1TR6 > 0) {
-					  printk(KERN_INFO "isdnl3up unhandled 1tr6 state %d MT %x\n"
-						 ,st->l3.state, mt);
-				  }
-				  BufPoolRelease(ibh);
-			  } else
-				  datastatelist_1tr6t[i].rout(st, pr, ibh);
-			  break;
-#endif
-		  case PROTO_EURO:	/* E-DSS1 */
-			  for (i = 0; i < datasllen; i++)
-				  if ((st->l3.state == datastatelist[i].state) &&
-				      (mt == datastatelist[i].primitive))
-					  break;
-			  if (i == datasllen) {
-				  BufPoolRelease(ibh);
-				  if (DEBUG_1TR6 > 0)
-			  	  	printk(KERN_INFO "isdnl3up unhandled E-DSS1 state %d MT %x\n",
-				 		st->l3.state, mt);
-			  } else
-				  datastatelist[i].rout(st, pr, ibh);
-			  break;
-		  default:
-		          BufPoolRelease(ibh);
-			  break;
-		}
-	}
-}
-
-static void
-l3down(struct PStack *st,
-       int pr, void *arg)
-{
-	int             i;
-	struct BufHeader *ibh = arg;
-
-	switch (st->protocol) {
-#ifdef P_1TR6
-	  case ISDN_PTYPE_1TR6:
-		  for (i = 0; i < downsl_1tr6t_len; i++)
-			  if ((st->l3.state == downstatelist_1tr6t[i].state) &&
-			      (pr == downstatelist_1tr6t[i].primitive))
-				  break;
-		  if (i == downsl_1tr6t_len) {
-			  if (DEBUG_1TR6 > 0) {
-				  printk(KERN_INFO "isdnl3down unhandled 1tr6 state %d primitive %x\n", st->l3.state, pr);
-			  }
-		  } else
-			  downstatelist_1tr6t[i].rout(st, pr, ibh);
-		  break;
-#endif
-	  default:
-		  for (i = 0; i < downsllen; i++)
-			  if ((st->l3.state == downstatelist[i].state) &&
-			      (pr == downstatelist[i].primitive))
-				  break;
-		  if (i == downsllen) {
-			  if (DEBUG_1TR6 > 0) {
-				  printk(KERN_INFO "isdnl3down unhandled E-DSS1 state %d primitive %x\n", st->l3.state, pr);
-			  }
-		  } else
-			  downstatelist[i].rout(st, pr, ibh);
-	}
-}
-
-void
-setstack_isdnl3(struct PStack *st)
-{
-	st->l4.l4l3 = l3down;
-	st->l2.l2l3 = l3up;
-	st->l3.state = 0;
-	st->l3.callref = 0;
-	st->l3.debug = 0;
-}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/l3_1TR6.c linux/drivers/isdn/teles/l3_1TR6.c
--- v2.0.30/linux/drivers/isdn/teles/l3_1TR6.c	Tue Nov 12 22:36:20 1996
+++ linux/drivers/isdn/teles/l3_1TR6.c	Wed Dec 31 16:00:00 1969
@@ -1,535 +0,0 @@
-/* $Id: l3_1TR6.c,v 1.6 1996/09/25 18:34:57 keil Exp $
- *
- * $Log: l3_1TR6.c,v $
- * Revision 1.6  1996/09/25 18:34:57  keil
- * missing states in 1TR6 Statemachine added
- *
- * Revision 1.5  1996/09/23 01:53:51  fritz
- * Bugfix: discard unknown frames (non-EDSS1 and non-1TR6).
- *
- * Revision 1.4  1996/06/06 14:22:28  fritz
- * Changed level of "non-digital call..." message, since
- * with audio support, this is quite normal.
- *
- * Revision 1.3  1996/04/30 21:54:42  isdn4dev
- * SPV, callback , remove some debugging code  Karsten Keil
- *
- * Revision 1.2  1996/04/20 16:47:23  fritz
- * Changed statemachine to allow reject of an incoming call.
- * Report all incoming calls, not just those with Service = 7.
- * Misc. typos
- *
- * Revision 1.1  1996/04/13 10:25:16  fritz
- * Initial revision
- *
- *
- */
-
-#include	"proto.h"
-
-static void
-l3_1TR6_message(struct PStack *st, int mt, int pd)
-{
-	struct BufHeader *dibh;
-	byte           *p;
-
-	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 18);
-	p = DATAPTR(dibh);
-	p += st->l2.ihsize;
-
-	*p++ = pd;
-	*p++ = 0x1;
-	*p++ = st->l3.callref;
-	*p++ = mt;
-
-	dibh->datasize = p - DATAPTR(dibh);
-	i_down(st, dibh);
-}
-
-static void
-l3_1tr6_setup(struct PStack *st, byte pr, void *arg)
-{
-	struct BufHeader *dibh;
-	byte           *p;
-	char           *teln;
-
-	st->l3.callref = st->pa->callref;
-	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 19);
-	p = DATAPTR(dibh);
-	p += st->l2.ihsize;
-
-	*p++ = PROTO_DIS_N1;
-	*p++ = 0x1;
-	*p++ = st->l3.callref;
-	*p++ = MT_N1_SETUP;
-
-	if ('S' == (st->pa->called[0] & 0x5f)) {	/* SPV ??? */
-		/* NSF SPV */
-		*p++ = WE0_netSpecFac;
-		*p++ = 4;	/* Laenge */
-		*p++ = 0;
-		*p++ = FAC_SPV;	/* SPV */
-		*p++ = st->pa->info; /* 0 for all Services */
-		*p++ = st->pa->info2; /* 0 for all Services */
-		*p++ = WE0_netSpecFac;
-		*p++ = 4;	/* Laenge */
-		*p++ = 0;
-		*p++ = FAC_Activate;	/* aktiviere SPV (default) */
-		*p++ = st->pa->info; /* 0 for all Services */
-		*p++ = st->pa->info2; /* 0 for all Services */
-	}
-	if (st->pa->calling[0] != '\0') {
-		*p++ = WE0_origAddr;
-		*p++ = strlen(st->pa->calling) + 1;
-		/* Classify as AnyPref. */
-		*p++ = 0x81;	/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
-		teln = st->pa->calling;
-		while (*teln)
-			*p++ = *teln++ & 0x7f;
-	}
-	*p++ = WE0_destAddr;
-	teln = st->pa->called;
-	if ('S' != (st->pa->called[0] & 0x5f)) {	/* Keine SPV ??? */
-		*p++ = strlen(st->pa->called) + 1;
-		st->pa->spv = 0;
-	} else {		/* SPV */
-		*p++ = strlen(st->pa->called);
-		teln++;		/* skip S */
-		st->pa->spv = 1;
-	}
-	/* Classify as AnyPref. */
-	*p++ = 0x81;		/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
-	while (*teln)
-		*p++ = *teln++ & 0x7f;
-
-	*p++ = WE_Shift_F6;
-	/* Codesatz 6 fuer Service */
-	*p++ = WE6_serviceInd;
-	*p++ = 2;		/* len=2 info,info2 */
-	*p++ = st->pa->info;
-	*p++ = st->pa->info2;
-
-	dibh->datasize = p - DATAPTR(dibh);
-
-	newl3state(st, 1);
-	i_down(st, dibh);
-
-}
-
-static void
-l3_1tr6_tu_setup(struct PStack *st, byte pr, void *arg)
-{
-	byte           *p;
-	struct BufHeader *ibh = arg;
-
-	p = DATAPTR(ibh);
-	p += st->l2.uihsize;
-	st->pa->callref = getcallref(p);
-	st->l3.callref = 0x80 + st->pa->callref;
-
-	/* Channel Identification */
-	p = DATAPTR(ibh);
-	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
-			WE0_chanID, 0))) {
-		st->pa->bchannel = p[2] & 0x3;
-	} else
-		printk(KERN_INFO "l3tu_setup: Channel ident not found\n");
-
-	p = DATAPTR(ibh);
-
-	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, WE6_serviceInd, 6))) {
-		st->pa->info = p[2];
-		st->pa->info2 = p[3];
-	} else
-		printk(KERN_INFO "l3s12(1TR6): ServiceIndicator not found\n");
-
-	p = DATAPTR(ibh);
-	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
-			WE0_destAddr, 0)))
-		iecpy(st->pa->called, p, 1);
-	else
-		strcpy(st->pa->called, "");
-
-	p = DATAPTR(ibh);
-	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
-			WE0_origAddr, 0))) {
-		iecpy(st->pa->calling, p, 1);
-	} else
-		strcpy(st->pa->calling, "");
-
-	p = DATAPTR(ibh);
-	st->pa->spv = 0;
-	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
-			WE0_netSpecFac, 0))) {
-		if ((FAC_SPV == p[3]) || (FAC_Activate == p[3]))
-			st->pa->spv = 1;
-	}
-	BufPoolRelease(ibh);
-
-        /* Signal all services, linklevel takes care of Service-Indicator */
-	if (st->pa->info != 7) {
-                printk(KERN_DEBUG "non-digital call: %s -> %s\n",
-                       st->pa->calling,
-                       st->pa->called);
-	}
-        newl3state(st, 6);
-        st->l3.l3l4(st, CC_SETUP_IND, NULL);
-}
-
-static void
-l3_1tr6_tu_setup_ack(struct PStack *st, byte pr, void *arg)
-{
-	byte           *p;
-	struct BufHeader *ibh = arg;
-
-	p = DATAPTR(ibh);
-	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
-			WE0_chanID, 0))) {
-		st->pa->bchannel = p[2] & 0x3;
-	} else
-		printk(KERN_INFO "octect 3 not found\n");
-
-
-	BufPoolRelease(ibh);
-	newl3state(st, 2);
-}
-
-static void
-l3_1tr6_tu_call_sent(struct PStack *st, byte pr, void *arg)
-{
-	byte           *p;
-	struct BufHeader *ibh = arg;
-
-	p = DATAPTR(ibh);
-	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
-			WE0_chanID, 0))) {
-		st->pa->bchannel = p[2] & 0x3;
-	} else
-		printk(KERN_INFO "octect 3 not found\n");
-
-	BufPoolRelease(ibh);
-	newl3state(st, 3);
-	st->l3.l3l4(st, CC_PROCEEDING_IND, NULL);
-}
-
-static void
-l3_1tr6_tu_alert(struct PStack *st, byte pr, void *arg)
-{
-	byte           *p;
-	struct BufHeader *ibh = arg;
-
-
-	p = DATAPTR(ibh);
-	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
-			WE6_statusCalled, 6))) {
-		if (DEBUG_1TR6 > 2)
-			printk(KERN_INFO "status called %x\n", p[2]);
-	} else if (DEBUG_1TR6 > 0)
-		printk(KERN_INFO "statusCalled not found\n");
-
-	BufPoolRelease(ibh);
-	newl3state(st, 4);
-	st->l3.l3l4(st, CC_ALERTING_IND, NULL);
-}
-
-static void
-l3_1tr6_tu_info(struct PStack *st, byte pr, void *arg)
-{
-	byte           *p;
-	int             i,tmpcharge=0;
-	char            a_charge[8];
-	struct BufHeader *ibh = arg;
-
-	p = DATAPTR(ibh);
-	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
-			WE6_chargingInfo, 6))) {
-		iecpy(a_charge, p, 1);
-                for (i = 0; i < strlen (a_charge); i++) {
-     	                tmpcharge *= 10;
-     	                tmpcharge += a_charge[i] & 0xf;
-     	        }
-                if (tmpcharge > st->pa->chargeinfo) {
-     	                st->pa->chargeinfo = tmpcharge;
-     	                st->l3.l3l4 (st, CC_INFO_CHARGE, NULL);
-     	        }
-		if (DEBUG_1TR6 > 2)
-			printk(KERN_INFO "chargingInfo %d\n", st->pa->chargeinfo);
-	} else if (DEBUG_1TR6 > 2)
-		printk(KERN_INFO "chargingInfo not found\n");
-
-	BufPoolRelease(ibh);
-}
-
-static void
-l3_1tr6_tu_info_s2(struct PStack *st, byte pr, void *arg)
-{
-	byte           *p;
-	int             i;
-	struct BufHeader *ibh = arg;
-
-	if (DEBUG_1TR6 > 4) {
-		p = DATAPTR(ibh);
-		for (i = 0; i < ibh->datasize; i++) {
-			printk(KERN_INFO "Info DATA %x\n", p[i]);
-		}
-	}
-	BufPoolRelease(ibh);
-}
-
-static void
-l3_1tr6_tu_connect(struct PStack *st, byte pr, void *arg)
-{
-	struct BufHeader *ibh = arg;
-
-        st->pa->chargeinfo=0;
-	BufPoolRelease(ibh);
-	st->l3.l3l4(st, CC_SETUP_CNF, NULL);
-	newl3state(st, 10);
-}
-
-static void
-l3_1tr6_tu_rel(struct PStack *st, byte pr, void *arg)
-{
-	struct BufHeader *ibh = arg;
-
-	BufPoolRelease(ibh);
-	l3_1TR6_message(st, MT_N1_REL_ACK, PROTO_DIS_N1);
-	st->l3.l3l4(st, CC_RELEASE_IND, NULL);
-	newl3state(st, 0);
-}
-
-static void
-l3_1tr6_tu_rel_ack(struct PStack *st, byte pr, void *arg)
-{
-	struct BufHeader *ibh = arg;
-
-	BufPoolRelease(ibh);
-	newl3state(st, 0);
-	st->l3.l3l4(st, CC_RELEASE_CNF, NULL);
-}
-
-static void
-l3_1tr6_tu_disc(struct PStack *st, byte pr, void *arg)
-{
-	struct BufHeader *ibh = arg;
-	byte           *p;
-	int             i,tmpcharge=0;
-	char            a_charge[8];
-
-	p = DATAPTR(ibh);
-	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
-			WE6_chargingInfo, 6))) {
-		iecpy(a_charge, p, 1);
-                for (i = 0; i < strlen (a_charge); i++) {
-     	                tmpcharge *= 10;
-     	                tmpcharge += a_charge[i] & 0xf;
-     	        }
-                if (tmpcharge > st->pa->chargeinfo) {
-     	                st->pa->chargeinfo = tmpcharge;
-     	                st->l3.l3l4 (st, CC_INFO_CHARGE, NULL);
-     	        }
-		if (DEBUG_1TR6 > 2)
-			printk(KERN_INFO "chargingInfo %d\n", st->pa->chargeinfo);
-	} else if (DEBUG_1TR6 > 2)
-		printk(KERN_INFO "chargingInfo not found\n");
-
-	p = DATAPTR(ibh);
-	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
-			WE0_cause, 0))) {
-		if (p[1] > 0) {
-			st->pa->cause = p[2];
-		} else {
-			st->pa->cause = 0;
-		}
-		if (DEBUG_1TR6 > 1)
-			printk(KERN_INFO "Cause %x\n", st->pa->cause);
-	} else if (DEBUG_1TR6 > 0)
-		printk(KERN_INFO "Cause not found\n");
-
-	BufPoolRelease(ibh);
-	newl3state(st, 12);
-	st->l3.l3l4(st, CC_DISCONNECT_IND, NULL);
-}
-
-
-static void
-l3_1tr6_tu_connect_ack(struct PStack *st, byte pr, void *arg)
-{
-	struct BufHeader *ibh = arg;
-
-	BufPoolRelease(ibh);
-	st->pa->chargeinfo = 0;
-	st->l3.l3l4(st, CC_SETUP_COMPLETE_IND, NULL);
-	newl3state(st, 10);
-}
-
-static void
-l3_1tr6_alert(struct PStack *st, byte pr,
-	      void *arg)
-{
-	l3_1TR6_message(st, MT_N1_ALERT, PROTO_DIS_N1);
-	newl3state(st, 7);
-}
-
-static void
-l3_1tr6_conn(struct PStack *st, byte pr,
-	     void *arg)
-{
-	struct BufHeader *dibh;
-	byte *p;
-
-	st->l3.callref = 0x80 + st->pa->callref;
-
-	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 20);
-	p = DATAPTR(dibh);
-	p += st->l2.ihsize;
-
-	*p++ = PROTO_DIS_N1;
-	*p++ = 0x1;
-	*p++ = st->l3.callref;
-	*p++ = MT_N1_CONN;
-
-	if (st->pa->spv) {	/* SPV ??? */
-		/* NSF SPV */
-		*p++ = WE0_netSpecFac;
-		*p++ = 4;	/* Laenge */
-		*p++ = 0;
-		*p++ = FAC_SPV;	/* SPV */
-		*p++ = st->pa->info;
-		*p++ = st->pa->info2;
-		*p++ = WE0_netSpecFac;
-		*p++ = 4;	/* Laenge */
-		*p++ = 0;
-		*p++ = FAC_Activate;	/* aktiviere SPV */
-		*p++ = st->pa->info;
-		*p++ = st->pa->info2;
-	}
-	dibh->datasize = p - DATAPTR(dibh);
-
-	i_down(st, dibh);
-
-	newl3state(st, 8);
-}
-
-static void
-l3_1tr6_reset(struct PStack *st, byte pr, void *arg)
-{
-	newl3state(st, 0);
-}
-
-static void
-l3_1tr6_disconn_req(struct PStack *st, byte pr, void *arg)
-{
-	struct BufHeader *dibh;
-	byte             *p;
-        byte             rejflg;
-
-	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 21);
-	p = DATAPTR(dibh);
-	p += st->l2.ihsize;
-
-	*p++ = PROTO_DIS_N1;
-	*p++ = 0x1;
-	*p++ = st->l3.callref;
-	*p++ = MT_N1_DISC;
-
-        if (st->l3.state == 7) {
-                rejflg = 1;
-                *p++ = WE0_cause;       /* Anruf abweisen                */
-                *p++ = 0x01;            /* Laenge = 1                    */
-                *p++ = CAUSE_CallRejected;
-        } else {
-                rejflg = 0;
-                *p++ = WE0_cause;
-                *p++ = 0x0;             /* Laenge = 0 normales Ausloesen */
-        }
-
-	dibh->datasize = p - DATAPTR(dibh);
-
-	i_down(st, dibh);
-
-        newl3state(st, 11);
-}
-
-static void
-l3_1tr6_rel_req(struct PStack *st, byte pr, void *arg)
-{
-	l3_1TR6_message(st, MT_N1_REL, PROTO_DIS_N1);
-	newl3state(st, 19);
-}
-
-static struct stateentry downstatelist_1tr6t[] =
-{
-	{0, CC_SETUP_REQ, l3_1tr6_setup},
-	{1, CC_DISCONNECT_REQ, l3_1tr6_disconn_req},
-	{1, CC_RELEASE_REQ, l3_1tr6_rel_req},
-        {1, CC_DLRL, l3_1tr6_reset},
-	{2, CC_DISCONNECT_REQ, l3_1tr6_disconn_req},
-	{2, CC_RELEASE_REQ, l3_1tr6_rel_req},
-        {2, CC_DLRL, l3_1tr6_reset},
-	{3, CC_DISCONNECT_REQ, l3_1tr6_disconn_req},
-	{3, CC_RELEASE_REQ, l3_1tr6_rel_req},
-        {3, CC_DLRL, l3_1tr6_reset},
-	{4, CC_DISCONNECT_REQ, l3_1tr6_disconn_req},
-	{4, CC_RELEASE_REQ, l3_1tr6_rel_req},
-        {4, CC_DLRL, l3_1tr6_reset},
-	{6, CC_REJECT_REQ, l3_1tr6_reset},
-	{6, CC_RELEASE_REQ, l3_1tr6_rel_req},
-	{6, CC_SETUP_RSP, l3_1tr6_conn},
-	{6, CC_ALERTING_REQ, l3_1tr6_alert},
-        {6, CC_DLRL, l3_1tr6_reset},
-	{7, CC_SETUP_RSP, l3_1tr6_conn},
-	{7, CC_RELEASE_REQ, l3_1tr6_rel_req},
-	{7, CC_DISCONNECT_REQ, l3_1tr6_disconn_req},
-        {7, CC_DLRL, l3_1tr6_reset},
-	{8, CC_DISCONNECT_REQ, l3_1tr6_disconn_req},
-	{8, CC_RELEASE_REQ, l3_1tr6_rel_req},
-        {8, CC_DLRL, l3_1tr6_reset},
-	{10, CC_DISCONNECT_REQ, l3_1tr6_disconn_req},
-	{10, CC_RELEASE_REQ, l3_1tr6_rel_req},
-        {10, CC_DLRL, l3_1tr6_reset},
-	{12, CC_RELEASE_REQ, l3_1tr6_rel_req},
-        {12, CC_DLRL, l3_1tr6_reset},
-        {19, CC_DLRL, l3_1tr6_reset},
-};
-
-static int      downsl_1tr6t_len = sizeof(downstatelist_1tr6t) /
-sizeof(struct stateentry);
-
-static struct stateentry datastatelist_1tr6t[] =
-{
-	{0, MT_N1_SETUP, l3_1tr6_tu_setup},
-	{0, MT_N1_REL, l3_1tr6_tu_rel},
-	{1, MT_N1_SETUP_ACK, l3_1tr6_tu_setup_ack},
-	{1, MT_N1_CALL_SENT, l3_1tr6_tu_call_sent},
-	{1, MT_N1_REL, l3_1tr6_tu_rel},
-	{1, MT_N1_DISC, l3_1tr6_tu_disc},
-	{2, MT_N1_CALL_SENT, l3_1tr6_tu_call_sent},
-	{2, MT_N1_ALERT, l3_1tr6_tu_alert},
-	{2, MT_N1_CONN, l3_1tr6_tu_connect},
-	{2, MT_N1_REL, l3_1tr6_tu_rel},
-	{2, MT_N1_DISC, l3_1tr6_tu_disc},
-	{2, MT_N1_INFO, l3_1tr6_tu_info_s2},
-	{3, MT_N1_ALERT, l3_1tr6_tu_alert},
-	{3, MT_N1_CONN, l3_1tr6_tu_connect},
-	{3, MT_N1_REL, l3_1tr6_tu_rel},
-	{3, MT_N1_DISC, l3_1tr6_tu_disc},
-	{4, MT_N1_ALERT, l3_1tr6_tu_alert},
-	{4, MT_N1_CONN, l3_1tr6_tu_connect},
-	{4, MT_N1_REL, l3_1tr6_tu_rel},
-	{4, MT_N1_DISC, l3_1tr6_tu_disc},
-	{7, MT_N1_REL, l3_1tr6_tu_rel},
-	{7, MT_N1_DISC, l3_1tr6_tu_disc},
-	{8, MT_N1_REL, l3_1tr6_tu_rel},
-	{8, MT_N1_DISC, l3_1tr6_tu_disc},
-	{8, MT_N1_CONN_ACK, l3_1tr6_tu_connect_ack},
-	{10, MT_N1_REL, l3_1tr6_tu_rel},
-	{10, MT_N1_DISC, l3_1tr6_tu_disc},
-	{10, MT_N1_INFO, l3_1tr6_tu_info},
-	{11, MT_N1_REL, l3_1tr6_tu_rel},
-	{12, MT_N1_REL, l3_1tr6_tu_rel},
-	{19, MT_N1_REL_ACK, l3_1tr6_tu_rel_ack}
-};
-
-static int      datasl_1tr6t_len = sizeof(datastatelist_1tr6t) /
-sizeof(struct stateentry);
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/l3_1TR6.h linux/drivers/isdn/teles/l3_1TR6.h
--- v2.0.30/linux/drivers/isdn/teles/l3_1TR6.h	Tue Nov 12 22:36:20 1996
+++ linux/drivers/isdn/teles/l3_1TR6.h	Wed Dec 31 16:00:00 1969
@@ -1,160 +0,0 @@
-/* $Id: l3_1TR6.h,v 1.4 1996/09/23 01:53:52 fritz Exp $
- *
- * $Log: l3_1TR6.h,v $
- * Revision 1.4  1996/09/23 01:53:52  fritz
- * Bugfix: discard unknown frames (non-EDSS1 and non-1TR6).
- *
- * Revision 1.3  1996/04/30 21:53:48  isdn4dev
- * Bugs, SPV, Logging in q931.c  Karsten Keil
- *
- * Revision 1.1  1996/04/13 10:25:42  fritz
- * Initial revision
- *
- *
- */
-#ifndef l3_1TR6
-#define l3_1TR6
-
-/*
- * MsgType N0
- */
-#define MT_N0_REG_IND 0x61
-#define MT_N0_CANC_IND 0x62
-#define MT_N0_FAC_STA 0x63
-#define MT_N0_STA_ACK 0x64
-#define MT_N0_STA_REJ 0x65
-#define MT_N0_FAC_INF 0x66
-#define MT_N0_INF_ACK 0x67
-#define MT_N0_INF_REJ 0x68
-#define MT_N0_CLOSE   0x75
-#define MT_N0_CLO_ACK 0x77
-
-
-/*
- * MsgType N1
- */
-
-#define MT_N1_ESC 0x00
-#define MT_N1_ALERT 0x01
-#define MT_N1_CALL_SENT 0x02
-#define MT_N1_CONN 0x07
-#define MT_N1_CONN_ACK 0x0F
-#define MT_N1_SETUP 0x05
-#define MT_N1_SETUP_ACK 0x0D
-#define MT_N1_RES 0x26
-#define MT_N1_RES_ACK 0x2E
-#define MT_N1_RES_REJ 0x22
-#define MT_N1_SUSP 0x25
-#define MT_N1_SUSP_ACK 0x2D
-#define MT_N1_SUSP_REJ 0x21
-#define MT_N1_USER_INFO 0x20
-#define MT_N1_DET 0x40
-#define MT_N1_DISC 0x45
-#define MT_N1_REL 0x4D
-#define MT_N1_REL_ACK 0x5A
-#define MT_N1_CANC_ACK 0x6E
-#define MT_N1_CANC_REJ 0x67
-#define MT_N1_CON_CON 0x69
-#define MT_N1_FAC 0x60
-#define MT_N1_FAC_ACK 0x68
-#define MT_N1_FAC_CAN 0x66
-#define MT_N1_FAC_REG 0x64
-#define MT_N1_FAC_REJ 0x65
-#define MT_N1_INFO 0x6D
-#define MT_N1_REG_ACK 0x6C
-#define MT_N1_REG_REJ 0x6F
-#define MT_N1_STAT 0x63
-
-
-
-/*
- * W Elemente
- */
-
-#define WE_Shift_F0 0x90
-#define WE_Shift_F6 0x96
-#define WE_Shift_OF0 0x98
-#define WE_Shift_OF6 0x9E
-
-#define WE0_cause 0x08
-#define WE0_connAddr 0x0C
-#define WE0_callID 0x10
-#define WE0_chanID 0x18
-#define WE0_netSpecFac 0x20
-#define WE0_display 0x28
-#define WE0_keypad 0x2C
-#define WE0_origAddr 0x6C
-#define WE0_destAddr 0x70
-#define WE0_userInfo 0x7E
-
-#define WE0_moreData 0xA0
-#define WE0_congestLevel 0xB0
-
-#define WE6_serviceInd 0x01
-#define WE6_chargingInfo 0x02
-#define WE6_date 0x03
-#define WE6_facSelect 0x05
-#define WE6_facStatus 0x06
-#define WE6_statusCalled 0x07
-#define WE6_addTransAttr 0x08
-
-/*
- * FacCodes
- */
-#define FAC_Sperre 0x01
-#define FAC_Sperre_All 0x02
-#define FAC_Sperre_Fern 0x03
-#define FAC_Sperre_Intl 0x04
-#define FAC_Sperre_Interk 0x05
-
-#define FAC_Forward1 0x02
-#define FAC_Forward2 0x03
-#define FAC_Konferenz 0x06
-#define FAC_GrabBchan 0x0F
-#define FAC_Reactivate 0x10
-#define FAC_Konferenz3 0x11
-#define FAC_Dienstwechsel1 0x12
-#define FAC_Dienstwechsel2 0x13
-#define FAC_NummernIdent 0x14
-#define FAC_GBG 0x15
-#define FAC_DisplayUebergeben 0x17
-#define FAC_DisplayUmgeleitet 0x1A
-#define FAC_Unterdruecke 0x1B
-#define FAC_Deactivate 0x1E
-#define FAC_Activate 0x1D
-#define FAC_SPV 0x1F
-#define FAC_Rueckwechsel 0x23
-#define FAC_Umleitung 0x24
-
-/*
- * Cause codes
- */
-#define CAUSE_InvCRef 0x01
-#define CAUSE_BearerNotImpl 0x03
-#define CAUSE_CIDunknown 0x07
-#define CAUSE_CIDinUse 0x08
-#define CAUSE_NoChans 0x0A
-#define CAUSE_FacNotImpl 0x10
-#define CAUSE_FacNotSubscr 0x11
-#define CAUSE_OutgoingBarred 0x20
-#define CAUSE_UserAccessBusy 0x21
-#define CAUSE_NegativeGBG 0x22
-#define CAUSE_UnknownGBG 0x23
-#define CAUSE_NoSPVknown 0x25
-#define CAUSE_DestNotObtain 0x35
-#define CAUSE_NumberChanged 0x38
-#define CAUSE_OutOfOrder 0x39
-#define CAUSE_NoUserResponse 0x3A
-#define CAUSE_UserBusy 0x3B
-#define CAUSE_IncomingBarred 0x3D
-#define CAUSE_CallRejected 0x3E
-#define CAUSE_NetworkCongestion 0x59
-#define CAUSE_RemoteUser 0x5A
-#define CAUSE_LocalProcErr 0x70
-#define CAUSE_RemoteProcErr 0x71
-#define CAUSE_RemoteUserSuspend 0x72
-#define CAUSE_RemoteUserResumed 0x73
-#define CAUSE_UserInfoDiscarded 0x7F
-
-
-#endif
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/llglue.c linux/drivers/isdn/teles/llglue.c
--- v2.0.30/linux/drivers/isdn/teles/llglue.c	Tue Nov 12 22:36:20 1996
+++ linux/drivers/isdn/teles/llglue.c	Wed Dec 31 16:00:00 1969
@@ -1,151 +0,0 @@
-/* $Id: llglue.c,v 1.7 1996/10/22 23:14:17 fritz Exp $
- *
- * $Log: llglue.c,v $
- * Revision 1.7  1996/10/22 23:14:17  fritz
- * Changes for compatibility to 2.0.X and 2.1.X kernels.
- *
- * Revision 1.6  1996/06/03 20:03:39  fritz
- * Fixed typos.
- *
- * Revision 1.5  1996/05/31 00:58:47  fritz
- * Errata: Reverted change from rev 1.4.
- *
- * Revision 1.4  1996/05/26 14:59:57  fritz
- * Bugfix: maxbufsize had been set without respect to possible X.75 header.
- *
- * Revision 1.3  1996/05/01 14:19:57  fritz
- * Added ISDN_FEATURE_L2_TRANS
- *
- * Revision 1.2  1996/04/29 23:01:46  fritz
- * Added driverId and channel to readstatus().
- *
- * Revision 1.1  1996/04/13 10:26:29  fritz
- * Initial revision
- *
- *
- */
-#define __NO_VERSION__
-#include "teles.h"
-#include <linux/malloc.h>
-#include <linux/timer.h>
-
-
-extern struct Channel *chanlist;
-int             drid;
-char            *teles_id = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
-
-isdn_if         iif;
-
-#define TELES_STATUS_BUFSIZE 4096
-static byte    *teles_status_buf = NULL;
-static byte    *teles_status_read = NULL;
-static byte    *teles_status_write = NULL;
-static byte    *teles_status_end = NULL;
-
-int
-teles_readstatus(byte * buf, int len, int user, int id, int channel)
-{
-	int             count;
-	byte           *p;
-
-	for (p = buf, count = 0; count < len; p++, count++) {
-		if (user)
-			put_user(*teles_status_read++, p);
-		else
-			*p++ = *teles_status_read++;
-		if (teles_status_read > teles_status_end)
-			teles_status_read = teles_status_buf;
-	}
-	return count;
-}
-
-void
-teles_putstatus(char *buf)
-{
-	long            flags;
-	int             len, count, i;
-	byte           *p;
-	isdn_ctrl       ic;
-
-	save_flags(flags);
-	cli();
-	count = 0;
-	len = strlen(buf);
-	for (p = buf, i = len; i > 0; i--, p++) {
-		*teles_status_write++ = *p;
-		if (teles_status_write > teles_status_end)
-			teles_status_write = teles_status_buf;
-		count++;
-	}
-	restore_flags(flags);
-	if (count) {
-		ic.command = ISDN_STAT_STAVAIL;
-		ic.driver = drid;
-		ic.arg = count;
-		iif.statcallb(&ic);
-	}
-}
-
-
-int
-ll_init(void)
-{
-	isdn_ctrl       ic;
-
-	teles_status_buf = Smalloc(TELES_STATUS_BUFSIZE,
-				   GFP_KERNEL, "teles_status_buf");
-	if (!teles_status_buf) {
-		printk(KERN_ERR "teles: Could not allocate status-buffer\n");
-		return (-EIO);
-	} else {
-		teles_status_read = teles_status_buf;
-		teles_status_write = teles_status_buf;
-		teles_status_end = teles_status_buf + TELES_STATUS_BUFSIZE - 1;
-	}
-
-	iif.channels = CallcNewChan();
-	iif.maxbufsize = BUFFER_SIZE(HSCX_SBUF_ORDER, HSCX_SBUF_BPPS);
-	iif.features =
-	    ISDN_FEATURE_L2_X75I |
-	    ISDN_FEATURE_L2_HDLC |
-	    ISDN_FEATURE_L2_TRANS |
-	    ISDN_FEATURE_L3_TRANS |
-	    ISDN_FEATURE_P_1TR6 |
-	    ISDN_FEATURE_P_EURO;
-
-	iif.command = teles_command;
-	iif.writebuf = teles_writebuf;
-	iif.writecmd = NULL;
-	iif.readstat = teles_readstatus;
-	strncpy(iif.id, teles_id, sizeof(iif.id) - 1);
-
-	register_isdn(&iif);
-	drid = iif.channels;
-
-	ic.driver = drid;
-	ic.command = ISDN_STAT_RUN;
-	iif.statcallb(&ic);
-	return 0;
-}
-
-void
-ll_stop(void)
-{
-	isdn_ctrl       ic;
-
-	ic.command = ISDN_STAT_STOP;
-	ic.driver = drid;
-	iif.statcallb(&ic);
-
-	CallcFreeChan();
-}
-
-void
-ll_unload(void)
-{
-	isdn_ctrl       ic;
-
-	ic.command = ISDN_STAT_UNLOAD;
-	ic.driver = drid;
-	iif.statcallb(&ic);
-}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/mod.c linux/drivers/isdn/teles/mod.c
--- v2.0.30/linux/drivers/isdn/teles/mod.c	Sun Apr 21 01:56:15 1996
+++ linux/drivers/isdn/teles/mod.c	Wed Dec 31 16:00:00 1969
@@ -1,143 +0,0 @@
-/* $Id: mod.c,v 1.1 1996/04/13 10:27:02 fritz Exp $
- *
- * $Log: mod.c,v $
- * Revision 1.1  1996/04/13 10:27:02  fritz
- * Initial revision
- *
- *
- */
-#include "teles.h"
-
-extern struct IsdnCard cards[];
-extern char   *teles_id;
-
-int             nrcards;
-
-typedef struct {
-	byte           *membase;
-	int             interrupt;
-	unsigned int    iobase;
-	unsigned int    protocol;
-} io_type;
-
-io_type         io[] =
-{
-	{0, 0, 0, 0},
-	{0, 0, 0, 0},
-	{0, 0, 0, 0},
-	{0, 0, 0, 0},
-	{0, 0, 0, 0},
-	{0, 0, 0, 0},
-	{0, 0, 0, 0},
-	{0, 0, 0, 0},
-	{0, 0, 0, 0},
-	{0, 0, 0, 0},
-	{0, 0, 0, 0},
-	{0, 0, 0, 0},
-	{0, 0, 0, 0},
-	{0, 0, 0, 0},
-	{0, 0, 0, 0},
-	{0, 0, 0, 0},
-};
-
-void
-teles_mod_dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
-void
-teles_mod_inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-#ifdef MODULE
-#define teles_init init_module
-#else
-void teles_setup(char *str, int *ints)
-{
-        int  i, j, argc;
-	static char sid[20];
-
-        argc = ints[0];
-        i = 0;
-        j = 1;
-        while (argc && (i<16)) {
-                if (argc) {
-                        io[i].iobase    = ints[j];
-                        j++; argc--;
-                }
-                if (argc) {
-                        io[i].interrupt = ints[j];
-                        j++; argc--;
-                }
-                if (argc) {
-                        io[i].membase   = (byte *)ints[j];
-                        j++; argc--;
-                }
-                if (argc) {
-                        io[i].protocol  = ints[j];
-                        j++; argc--;
-                }
-                i++;
-        }
-	if (strlen(str)) {
-		strcpy(sid,str);
-		teles_id = sid;
-	}
-}
-#endif
-
-int
-teles_init(void)
-{
-	int             i;
-
-	nrcards = 0;
-	for (i = 0; i < 16; i++) {
-		if (io[i].protocol) {
-			cards[i].membase   = io[i].membase;
-			cards[i].interrupt = io[i].interrupt;
-			cards[i].iobase    = io[i].iobase;
-			cards[i].protocol  = io[i].protocol;
-		}
-	}
-	for (i = 0; i < 16; i++)
-                if (cards[i].protocol)
-                        nrcards++;
-	printk(KERN_DEBUG "teles: Total %d card%s defined\n",
-               nrcards, (nrcards > 1) ? "s" : "");
-	if (teles_inithardware()) {
-                /* Install only, if at least one card found */
-                Isdnl2New();
-                TeiNew();
-                CallcNew();
-                ll_init();
-
-		/* No symbols to export, hide all symbols */
-		register_symtab(NULL);
-
-#ifdef MODULE
-                printk(KERN_NOTICE "Teles module installed\n");
-#endif
-                return (0);
-        } else
-                return -EIO;
-}
-
-#ifdef MODULE
-void
-cleanup_module(void)
-{
-
-	ll_stop();
-	TeiFree();
-	Isdnl2Free();
-	CallcFree();
-	teles_closehardware();
-	ll_unload();
-	printk(KERN_NOTICE "Teles module removed\n");
-
-}
-#endif
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/proto.h linux/drivers/isdn/teles/proto.h
--- v2.0.30/linux/drivers/isdn/teles/proto.h	Fri Nov 29 01:10:13 1996
+++ linux/drivers/isdn/teles/proto.h	Wed Dec 31 16:00:00 1969
@@ -1,18 +0,0 @@
-/* $Id: proto.h,v 1.1 1996/09/23 01:53:52 fritz Exp $
- *
- * not much now - just the l3 proto discriminator
- *
- * $Log: proto.h,v $
- * Revision 1.1  1996/09/23 01:53:52  fritz
- * Bugfix: discard unknown frames (non-EDSS1 and non-1TR6).
- *
- */
-
-#ifndef	PROTO_H
-#define	PROTO_H
-
-#define	PROTO_EURO		0x08
-#define	PROTO_DIS_N0	0x40
-#define	PROTO_DIS_N1	0x41
-
-#endif
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/q931.c linux/drivers/isdn/teles/q931.c
--- v2.0.30/linux/drivers/isdn/teles/q931.c	Tue Nov 12 22:36:20 1996
+++ linux/drivers/isdn/teles/q931.c	Wed Dec 31 16:00:00 1969
@@ -1,1155 +0,0 @@
-/* $Id: q931.c,v 1.6 1996/09/23 01:53:53 fritz Exp $
- *
- * q931.c               code to decode ITU Q.931 call control messages
- * 
- * Author               Jan den Ouden
- * 
- * Changelog
- * 
- * Pauline Middelink    general improvements
- * 
- * Beat Doebeli         cause texts, display information element
- * 
- * Karsten Keil         cause texts, display information element for 1TR6 
- *
- * 
- * $Log: q931.c,v $
- * Revision 1.6  1996/09/23 01:53:53  fritz
- * Bugfix: discard unknown frames (non-EDSS1 and non-1TR6).
- *
- * Revision 1.5  1996/06/03 20:03:40  fritz
- * Fixed typos.
- *
- * Revision 1.4  1996/05/17 03:46:17  fritz
- * General cleanup.
- *
- * Revision 1.3  1996/04/30 22:06:50  isdn4dev
- *   logging 1TR6 messages correctly   Karsten Keil
- *
- * Revision 1.2  1996/04/20 16:48:19  fritz
- * Misc. typos
- *
- * Revision 1.1  1996/04/13 10:27:49  fritz
- * Initial revision
- *
- *
- */
-
-
-#define __NO_VERSION__
-#include "teles.h"
-#include "proto.h"
-#include "l3_1TR6.h"
-
-byte           *
-findie(byte * p, int size, byte ie, int wanted_set)
-{
-	int             l, codeset, maincodeset;
-	byte           *pend = p + size;
-
-	/* skip protocol discriminator, callref and message type */
-	p++;
-	l = (*p++) & 0xf;
-	p += l;
-	p++;
-	codeset = 0;
-	maincodeset = 0;
-	/* while there are bytes left... */
-	while (p < pend) {
-		if ((*p & 0xf0) == 0x90) {
-			codeset = *p & 0x07;
-			if (!(*p & 0x08))
-				maincodeset = codeset;
-		}
-		if (*p & 0x80)
-			p++;
-		else {
-			if (codeset == wanted_set) {
-				if (*p == ie)
-					return (p);
-				if (*p > ie)
-					return (NULL);
-			}
-			p++;
-			l = *p++;
-			p += l;
-			codeset = maincodeset;
-		}
-	}
-	return (NULL);
-}
-
-void
-iecpy(byte * dest, byte * iestart, int ieoffset)
-{
-	byte           *p;
-	int             l;
-
-	p = iestart + ieoffset + 2;
-	l = iestart[1] - ieoffset;
-	while (l--)
-		*dest++ = *p++;
-	*dest++ = '\0';
-}
-
-int
-getcallref(byte * p)
-{
-	p++;			/* prot discr */
-	p++;			/* callref length */
-	return (*p);		/* assuming one-byte callref */
-}
-
-/*
- * According to Table 4-2/Q.931
- */
-static
-struct MessageType {
-	byte            nr;
-	char           *descr;
-} mtlist[] = {
-
-	{
-		0x1, "ALERTING"
-	},
-	{
-		0x2, "CALL PROCEEDING"
-	},
-	{
-		0x7, "CONNECT"
-	},
-	{
-		0xf, "CONNECT ACKNOWLEDGE"
-	},
-	{
-		0x3, "PROGRESS"
-	},
-	{
-		0x5, "SETUP"
-	},
-	{
-		0xd, "SETUP ACKNOWLEDGE"
-	},
-	{
-		0x26, "RESUME"
-	},
-	{
-		0x2e, "RESUME ACKNOWLEDGE"
-	},
-	{
-		0x22, "RESUME REJECT"
-	},
-	{
-		0x25, "SUSPEND"
-	},
-	{
-		0x2d, "SUSPEND ACKNOWLEDGE"
-	},
-	{
-		0x21, "SUSPEND REJECT"
-	},
-	{
-		0x20, "USER INFORMATION"
-	},
-	{
-		0x45, "DISCONNECT"
-	},
-	{
-		0x4d, "RELEASE"
-	},
-	{
-		0x5a, "RELEASE COMPLETE"
-	},
-	{
-		0x46, "RESTART"
-	},
-	{
-		0x4e, "RESTART ACKNOWLEDGE"
-	},
-	{
-		0x60, "SEGMENT"
-	},
-	{
-		0x79, "CONGESTION CONTROL"
-	},
-	{
-		0x7b, "INFORMATION"
-	},
-	{
-		0x62, "FACILITY"
-	},
-	{
-		0x6e, "NOTIFY"
-	},
-	{
-		0x7d, "STATUS"
-	},
-	{
-		0x75, "STATUS ENQUIRY"
-	}
-};
-
-#define MTSIZE sizeof(mtlist)/sizeof(struct MessageType)
-
-static
-struct MessageType mt_n0[] =
-{
-	{MT_N0_REG_IND, "REGister INDication"},
-	{MT_N0_CANC_IND, "CANCel INDication"},
-	{MT_N0_FAC_STA, "FACility STAtus"},
-	{MT_N0_STA_ACK, "STAtus ACKnowledge"},
-	{MT_N0_STA_REJ, "STAtus REJect"},
-	{MT_N0_FAC_INF, "FACility INFormation"},
-	{MT_N0_INF_ACK, "INFormation ACKnowledge"},
-	{MT_N0_INF_REJ, "INFormation REJect"},
-	{MT_N0_CLOSE, "CLOSE"},
-	{MT_N0_CLO_ACK, "CLOse ACKnowledge"}
-};
-
-int mt_n0_len = (sizeof(mt_n0) / sizeof(struct MessageType));
-
-static
-struct MessageType mt_n1[] =
-{
-	{MT_N1_ESC, "ESCape"},
-	{MT_N1_ALERT, "ALERT"},
-	{MT_N1_CALL_SENT, "CALL SENT"},
-	{MT_N1_CONN, "CONNect"},
-	{MT_N1_CONN_ACK, "CONNect ACKnowledge"},
-	{MT_N1_SETUP, "SETUP"},
-	{MT_N1_SETUP_ACK, "SETUP ACKnowledge"},
-	{MT_N1_RES, "RESume"},
-	{MT_N1_RES_ACK, "RESume ACKnowledge"},
-	{MT_N1_RES_REJ, "RESume REJect"},
-	{MT_N1_SUSP, "SUSPend"},
-	{MT_N1_SUSP_ACK, "SUSPend ACKnowledge"},
-	{MT_N1_SUSP_REJ, "SUSPend REJect"},
-	{MT_N1_USER_INFO, "USER INFO"},
-	{MT_N1_DET, "DETach"},
-	{MT_N1_DISC, "DISConnect"},
-	{MT_N1_REL, "RELease"},
-	{MT_N1_REL_ACK, "RELease ACKnowledge"},
-	{MT_N1_CANC_ACK, "CANCel ACKnowledge"},
-	{MT_N1_CANC_REJ, "CANCel REJect"},
-	{MT_N1_CON_CON, "CONgestion CONtrol"},
-	{MT_N1_FAC, "FACility"},
-	{MT_N1_FAC_ACK, "FACility ACKnowledge"},
-	{MT_N1_FAC_CAN, "FACility CANcel"},
-	{MT_N1_FAC_REG, "FACility REGister"},
-	{MT_N1_FAC_REJ, "FACility REJect"},
-	{MT_N1_INFO, "INFOrmation"},
-	{MT_N1_REG_ACK, "REGister ACKnowledge"},
-	{MT_N1_REG_REJ, "REGister REJect"},
-	{MT_N1_STAT, "STATus"}
-};
-
-int mt_n1_len = (sizeof(mt_n1) / sizeof(struct MessageType));
-
-static struct MessageType fac_1tr6[] =
-{
-	{FAC_Sperre, "Sperre"},
-	{FAC_Forward1, "Forward 1"},
-	{FAC_Forward2, "Forward 2"},
-	{FAC_Konferenz, "Konferenz"},
-	{FAC_GrabBchan, "Grab Bchannel"},
-	{FAC_Reactivate, "Reactivate"},
-	{FAC_Konferenz3, "Dreier Konferenz"},
-	{FAC_Dienstwechsel1, "Einseitiger Dienstwechsel"},
-	{FAC_Dienstwechsel2, "Zweiseitiger Dienstwechsel"},
-	{FAC_NummernIdent, "Rufnummer-Identifizierung"},
-	{FAC_GBG, "GBG"},
-	{FAC_DisplayUebergeben, "Display Uebergeben"},
-	{FAC_DisplayUmgeleitet, "Display Umgeleitet"},
-	{FAC_Unterdruecke, "Unterdruecke Rufnummer"},
-	{FAC_Deactivate, "Deactivate"},
-	{FAC_Activate, "Activate"},
-	{FAC_SPV, "SPV"},
-	{FAC_Rueckwechsel, "Rueckwechsel"},
-	{FAC_Umleitung, "Umleitung"}
-};
-int fac_1tr6_len = (sizeof(fac_1tr6) / sizeof(struct MessageType));
-
-
-
-static int 
-prbits(char *dest, byte b, int start, int len)
-{
-	char           *dp = dest;
-
-	b = b << (8 - start);
-	while (len--) {
-		if (b & 0x80)
-			*dp++ = '1';
-		else
-			*dp++ = '0';
-		b = b << 1;
-	}
-	return (dp - dest);
-}
-
-static
-byte           *
-skipext(byte * p)
-{
-	while (!(*p++ & 0x80));
-	return (p);
-}
-
-/*
- * Cause Values According to Q.850
- * edescr: English description
- * ddescr: German description used by Swissnet II (Swiss Telecom
- *         not yet written...
- */
-
-static
-struct CauseValue {
-	byte            nr;
-	char           *edescr;
-	char           *ddescr;
-} cvlist[] = {
-
-	{
-		0x01, "Unallocated (unassigned) number", "Nummer nicht zugeteilt"
-	},
-	{
-		0x02, "No route to specified transit network", ""
-	},
-	{
-		0x03, "No route to destination", ""
-	},
-	{
-		0x04, "Send special information tone", ""
-	},
-	{
-		0x05, "Misdialled trunk prefix", ""
-	},
-	{
-		0x06, "Channel unacceptable", "Kanal nicht akzeptierbar"
-	},
-	{
-		0x07, "Channel awarded and being delivered in an established channel", ""
-	},
-	{
-		0x08, "Preemption", ""
-	},
-	{
-		0x09, "Preemption - circuit reserved for reuse", ""
-	},
-	{
-		0x10, "Normal call clearing", "Normale Ausloesung"
-	},
-	{
-		0x11, "User busy", "TNB besetzt"
-	},
-	{
-		0x12, "No user responding", ""
-	},
-	{
-		0x13, "No answer from user (user alerted)", ""
-	},
-	{
-		0x14, "Subscriber absent", ""
-	},
-	{
-		0x15, "Call rejected", ""
-	},
-	{
-		0x16, "Number changed", ""
-	},
-	{
-		0x1a, "non-selected user clearing", ""
-	},
-	{
-		0x1b, "Destination out of order", ""
-	},
-	{
-		0x1c, "Invalid number format (address incomplete)", ""
-	},
-	{
-		0x1d, "Facility rejected", ""
-	},
-	{
-		0x1e, "Response to Status enquiry", ""
-	},
-	{
-		0x1f, "Normal, unspecified", ""
-	},
-	{
-		0x22, "No circuit/channel available", ""
-	},
-	{
-		0x26, "Network out of order", ""
-	},
-	{
-		0x27, "Permanent frame mode connection out-of-service", ""
-	},
-	{
-		0x28, "Permanent frame mode connection operational", ""
-	},
-	{
-		0x29, "Temporary failure", ""
-	},
-	{
-		0x2a, "Switching equipment congestion", ""
-	},
-	{
-		0x2b, "Access information discarded", ""
-	},
-	{
-		0x2c, "Requested circuit/channel not available", ""
-	},
-	{
-		0x2e, "Precedence call blocked", ""
-	},
-	{
-		0x2f, "Resource unavailable, unspecified", ""
-	},
-	{
-		0x31, "Quality of service unavailable", ""
-	},
-	{
-		0x32, "Requested facility not subscribed", ""
-	},
-	{
-		0x35, "Outgoing calls barred within CUG", ""
-	},
-	{
-		0x37, "Incoming calls barred within CUG", ""
-	},
-	{
-		0x39, "Bearer capability not authorized", ""
-	},
-	{
-		0x3a, "Bearer capability not presently available", ""
-	},
-	{
-		0x3e, "Inconsistency in designated outgoing access information and subscriber class ", " "
-	},
-	{
-		0x3f, "Service or option not available, unspecified", ""
-	},
-	{
-		0x41, "Bearer capability not implemented", ""
-	},
-	{
-		0x42, "Channel type not implemented", ""
-	},
-	{
-		0x43, "Requested facility not implemented", ""
-	},
-	{
-		0x44, "Only restricted digital information bearer capability is available", ""
-	},
-	{
-		0x4f, "Service or option not implemented", ""
-	},
-	{
-		0x51, "Invalid call reference value", ""
-	},
-	{
-		0x52, "Identified channel does not exist", ""
-	},
-	{
-		0x53, "A suspended call exists, but this call identity does not", ""
-	},
-	{
-		0x54, "Call identity in use", ""
-	},
-	{
-		0x55, "No call suspended", ""
-	},
-	{
-		0x56, "Call having the requested call identity has been cleared", ""
-	},
-	{
-		0x57, "User not member of CUG", ""
-	},
-	{
-		0x58, "Incompatible destination", ""
-	},
-	{
-		0x5a, "Non-existent CUG", ""
-	},
-	{
-		0x5b, "Invalid transit network selection", ""
-	},
-	{
-		0x5f, "Invalid message, unspecified", ""
-	},
-	{
-		0x60, "Mandatory information element is missing", ""
-	},
-	{
-		0x61, "Message type non-existent or not implemented", ""
-	},
-	{
-		0x62, "Message not compatible with call state or message type non-existent or not implemented ", " "
-	},
-	{
-		0x63, "Information element/parameter non-existent or not implemented", ""
-	},
-	{
-		0x64, "Invalid information element contents", ""
-	},
-	{
-		0x65, "Message not compatible with call state", ""
-	},
-	{
-		0x66, "Recovery on timer expiry", ""
-	},
-	{
-		0x67, "Parameter non-existent or not implemented - passed on", ""
-	},
-	{
-		0x6e, "Message with unrecognized parameter discarded", ""
-	},
-	{
-		0x6f, "Protocol error, unspecified", ""
-	},
-	{
-		0x7f, "Interworking, unspecified", ""
-	},
-};
-
-#define CVSIZE sizeof(cvlist)/sizeof(struct CauseValue)
-
-static
-int
-prcause(char *dest, byte * p)
-{
-	byte           *end;
-	char           *dp = dest;
-	int             i, cause;
-
-	end = p + p[1] + 1;
-	p += 2;
-	dp += sprintf(dp, "    coding ");
-	dp += prbits(dp, *p, 7, 2);
-	dp += sprintf(dp, " location ");
-	dp += prbits(dp, *p, 4, 4);
-	*dp++ = '\n';
-	p = skipext(p);
-
-	cause = 0x7f & *p++;
-
-	/* locate cause value */
-	for (i = 0; i < CVSIZE; i++)
-		if (cvlist[i].nr == cause)
-			break;
-
-	/* display cause value if it exists */
-	if (i == CVSIZE)
-		dp += sprintf(dp, "Unknown cause type %x!\n", cause);
-	else
-		dp += sprintf(dp, "  cause value %x : %s \n", cause, cvlist[i].edescr);
-
-	while (!0) {
-		if (p > end)
-			break;
-		dp += sprintf(dp, "    diag attribute %d ", *p++ & 0x7f);
-		dp += sprintf(dp, " rej %d ", *p & 0x7f);
-		if (*p & 0x80) {
-			*dp++ = '\n';
-			break;
-		} else
-			dp += sprintf(dp, " av %d\n", (*++p) & 0x7f);
-	}
-	return (dp - dest);
-
-}
-
-static
-struct MessageType cause_1tr6[] =
-{
-	{CAUSE_InvCRef, "Invalid Call Reference"},
-	{CAUSE_BearerNotImpl, "Bearer Service Not Implemented"},
-	{CAUSE_CIDunknown, "Caller Identity unknown"},
-	{CAUSE_CIDinUse, "Caller Identity in Use"},
-	{CAUSE_NoChans, "No Channels available"},
-	{CAUSE_FacNotImpl, "Facility Not Implemented"},
-	{CAUSE_FacNotSubscr, "Facility Not Subscribed"},
-	{CAUSE_OutgoingBarred, "Outgoing calls barred"},
-	{CAUSE_UserAccessBusy, "User Access Busy"},
-	{CAUSE_NegativeGBG, "Negative GBG"},
-	{CAUSE_UnknownGBG, "Unknown  GBG"},
-	{CAUSE_NoSPVknown, "No SPV known"},
-	{CAUSE_DestNotObtain, "Destination not obtainable"},
-	{CAUSE_NumberChanged, "Number changed"},
-	{CAUSE_OutOfOrder, "Out Of Order"},
-	{CAUSE_NoUserResponse, "No User Response"},
-	{CAUSE_UserBusy, "User Busy"},
-	{CAUSE_IncomingBarred, "Incoming Barred"},
-	{CAUSE_CallRejected, "Call Rejected"},
-	{CAUSE_NetworkCongestion, "Network Congestion"},
-	{CAUSE_RemoteUser, "Remote User initiated"},
-	{CAUSE_LocalProcErr, "Local Procedure Error"},
-	{CAUSE_RemoteProcErr, "Remote Procedure Error"},
-	{CAUSE_RemoteUserSuspend, "Remote User Suspend"},
-	{CAUSE_RemoteUserResumed, "Remote User Resumed"},
-	{CAUSE_UserInfoDiscarded, "User Info Discarded"}
-};
-
-int cause_1tr6_len = (sizeof(cause_1tr6) / sizeof(struct MessageType));
-
-static int
-prcause_1tr6(char *dest, byte * p) 
-{
-	char           *dp = dest;
-	int i, cause;
-
-	p++;
-	if (0 == *p) {
-		dp += sprintf(dp, "   OK (cause length=0)\n");
-		return (dp - dest);
-	} else if (*p > 1) {
-		dp += sprintf(dp, "    coding ");
-		dp += prbits(dp, p[2], 7, 2);
-		dp += sprintf(dp, " location ");
-		dp += prbits(dp, p[2], 4, 4);
-		*dp++ = '\n';
-	}
-	p++;
-	cause = 0x7f & *p;
-
-	/* locate cause value */
-	for (i = 0; i < cause_1tr6_len; i++)
-		if (cause_1tr6[i].nr == cause)
-			break;
-
-	/* display cause value if it exists */
-	if (i == cause_1tr6_len)
-		dp += sprintf(dp, "Unknown cause type %x!\n", cause);
-	else
-		dp += sprintf(dp, "  cause value %x : %s \n", cause, cause_1tr6[i].descr);
-
-	return (dp - dest);
-
-}
-
-static int
-prchident(char *dest, byte * p) {
-	char *dp = dest;
-
-	p += 2;
-	dp += sprintf(dp, "    octet 3 ");
-	dp += prbits(dp, *p, 8, 8);
-	*dp++ = '\n';
-	return (dp - dest);
-}
-
-static int
-prcalled(char *dest, byte * p) {
-	int             l;
-	char           *dp = dest;
-
-	p++;
-	l = *p++ - 1;
-	dp += sprintf(dp, "    octet 3 ");
-	dp += prbits(dp, *p++, 8, 8);
-	*dp++ = '\n';
-	dp += sprintf(dp, "    number digits ");
-	while (l--)
-		*dp++ = *p++;
-	*dp++ = '\n';
-	return (dp - dest);
-}
-static int
-prcalling(char *dest, byte * p) {
-	int             l;
-	char           *dp = dest;
-
-	p++;
-	l = *p++ - 1;
-	dp += sprintf(dp, "    octet 3 ");
-	dp += prbits(dp, *p, 8, 8);
-	*dp++ = '\n';
-	if (!(*p & 0x80)) {
-		dp += sprintf(dp, "    octet 3a ");
-		dp += prbits(dp, *++p, 8, 8);
-		*dp++ = '\n';
-		l--;
-	};
-	p++;
-
-	dp += sprintf(dp, "    number digits ");
-	while (l--)
-		*dp++ = *p++;
-	*dp++ = '\n';
-	return (dp - dest);
-}
-
-static
-int
-prbearer(char *dest, byte * p)
-{
-	char           *dp = dest, ch;
-
-	p += 2;
-	dp += sprintf(dp, "    octet 3  ");
-	dp += prbits(dp, *p++, 8, 8);
-	*dp++ = '\n';
-	dp += sprintf(dp, "    octet 4  ");
-	dp += prbits(dp, *p, 8, 8);
-	*dp++ = '\n';
-	if ((*p++ & 0x1f) == 0x18) {
-		dp += sprintf(dp, "    octet 4.1 ");
-		dp += prbits(dp, *p++, 8, 8);
-		*dp++ = '\n';
-	}
-	/* check for user information layer 1 */
-	if ((*p & 0x60) == 0x20) {
-		ch = ' ';
-		do {
-			dp += sprintf(dp, "    octet 5%c ", ch);
-			dp += prbits(dp, *p, 8, 8);
-			*dp++ = '\n';
-			if (ch == ' ')
-				ch = 'a';
-			else
-				ch++;
-		}
-		while (!(*p++ & 0x80));
-	}
-	/* check for user information layer 2 */
-	if ((*p & 0x60) == 0x40) {
-		dp += sprintf(dp, "    octet 6  ");
-		dp += prbits(dp, *p++, 8, 8);
-		*dp++ = '\n';
-	}
-	/* check for user information layer 3 */
-	if ((*p & 0x60) == 0x60) {
-		dp += sprintf(dp, "    octet 7  ");
-		dp += prbits(dp, *p++, 8, 8);
-		*dp++ = '\n';
-	}
-	return (dp - dest);
-}
-
-static int
-general(char *dest, byte * p) {
-	char           *dp = dest;
-	char            ch = ' ';
-	int             l, octet = 3;
-
-	p++;
-	l = *p++;
-	/* Iterate over all octets in the information element */
-	while (l--) {
-		dp += sprintf(dp, "    octet %d%c ", octet, ch);
-		dp += prbits(dp, *p++, 8, 8);
-		*dp++ = '\n';
-
-		/* last octet in group? */
-		if (*p & 0x80) {
-			octet++;
-			ch = ' ';
-		} else if (ch == ' ')
-			ch = 'a';
-		else
-			ch++;
-	}
-	return (dp - dest);
-}
-
-static int
-prcharge(char *dest, byte * p) {
-	char *dp = dest;
-	int l;
-
-	p++;
-	l = *p++ - 1;
-	dp += sprintf(dp, "    GEA ");
-	dp += prbits(dp, *p++, 8, 8);
-	dp += sprintf(dp, "  Anzahl: ");
-	/* Iterate over all octets in the * information element */
-	while (l--)
-		*dp++ = *p++;
-	*dp++ = '\n';
-	return (dp - dest);
-} 
-static int
-prtext(char *dest, byte * p) {
-	char *dp = dest;
-	int l;
-
-	p++;
-	l = *p++;
-	dp += sprintf(dp, "    ");
-	/* Iterate over all octets in the * information element */
-	while (l--)
-		*dp++ = *p++;
-	*dp++ = '\n';
-	return (dp - dest);
-}
-static int
-display(char *dest, byte * p) {
-	char           *dp = dest;
-	char            ch = ' ';
-	int             l, octet = 3;
-
-	p++;
-	l = *p++;
-	/* Iterate over all octets in the * display-information element */
-	dp += sprintf(dp, "   \"");
-	while (l--) {
-		dp += sprintf(dp, "%c", *p++);
-
-		/* last octet in group? */
-		if (*p & 0x80) {
-			octet++;
-			ch = ' ';
-		} else if (ch == ' ')
-			ch = 'a';
-
-		else
-			ch++;
-	}
-	*dp++ = '\"';
-	*dp++ = '\n';
-	return (dp - dest);
-}
-
-int
-prfacility(char *dest, byte * p)
-{
-	char           *dp = dest;
-	int             l, l2;
-
-	p++;
-	l = *p++;
-	dp += sprintf(dp, "    octet 3 ");
-	dp += prbits(dp, *p++, 8, 8);
-	dp += sprintf(dp, "\n");
-	l -= 1;
-
-	while (l > 0) {
-		dp += sprintf(dp, "   octet 4 ");
-		dp += prbits(dp, *p++, 8, 8);
-		dp += sprintf(dp, "\n");
-		dp += sprintf(dp, "   octet 5 %d\n", l2 = *p++ & 0x7f);
-		l -= 2;
-		dp += sprintf(dp, "   contents ");
-		while (l2--) {
-			dp += sprintf(dp, "%2x ", *p++);
-			l--;
-		}
-		dp += sprintf(dp, "\n");
-	}
-
-	return (dp - dest);
-}
-
-static
-struct InformationElement {
-	byte            nr;
-	char           *descr;
-	int             (*f) (char *, byte *);
-} ielist[] = {
-
-	{
-		0x00, "Segmented message", general
-	},
-	{
-		0x04, "Bearer capability", prbearer
-	},
-	{
-		0x08, "Cause", prcause
-	},
-	{
-		0x10, "Call identity", general
-	},
-	{
-		0x14, "Call state", general
-	},
-	{
-		0x18, "Channel identification", prchident
-	},
-	{
-		0x1c, "Facility", prfacility
-	},
-	{
-		0x1e, "Progress indicator", general
-	},
-	{
-		0x20, "Network-specific facilities", general
-	},
-	{
-		0x27, "Notification indicator", general
-	},
-	{
-		0x28, "Display", display
-	},
-	{
-		0x29, "Date/Time", general
-	},
-	{
-		0x2c, "Keypad facility", general
-	},
-	{
-		0x34, "Signal", general
-	},
-	{
-		0x40, "Information rate", general
-	},
-	{
-		0x42, "End-to-end delay", general
-	},
-	{
-		0x43, "Transit delay selection and indication", general
-	},
-	{
-		0x44, "Packet layer binary parameters", general
-	},
-	{
-		0x45, "Packet layer window size", general
-	},
-	{
-		0x46, "Packet size", general
-	},
-	{
-		0x47, "Closed user group", general
-	},
-	{
-		0x4a, "Reverse charge indication", general
-	},
-	{
-		0x6c, "Calling party number", prcalling
-	},
-	{
-		0x6d, "Calling party subaddress", general
-	},
-	{
-		0x70, "Called party number", prcalled
-	},
-	{
-		0x71, "Called party subaddress", general
-	},
-	{
-		0x74, "Redirecting number", general
-	},
-	{
-		0x78, "Transit network selection", general
-	},
-	{
-		0x79, "Restart indicator", general
-	},
-	{
-		0x7c, "Low layer compatibility", general
-	},
-	{
-		0x7d, "High layer compatibility", general
-	},
-	{
-		0x7e, "User-user", general
-	},
-	{
-		0x7f, "Escape for extension", general
-	},
-};
-
-
-#define IESIZE sizeof(ielist)/sizeof(struct InformationElement)
-
-static struct InformationElement we_0[] =
-{
-	{WE0_cause, "Cause", prcause_1tr6},
-	{WE0_connAddr, "Connecting Address", prcalled},
-	{WE0_callID, "Call IDentity", general},
-	{WE0_chanID, "Channel IDentity", general},
-	{WE0_netSpecFac, "Network Specific Facility", general},
-	{WE0_display, "Display", general},
-	{WE0_keypad, "Keypad", general},
-	{WE0_origAddr, "Origination Address", prcalled},
-	{WE0_destAddr, "Destination Address", prcalled},
-	{WE0_userInfo, "User Info", general}
-};
-
-static int we_0_len = (sizeof(we_0) / sizeof(struct InformationElement));
-
-static struct InformationElement we_6[] =
-{
-	{WE6_serviceInd, "Service Indicator", general},
-	{WE6_chargingInfo, "Charging Information", prcharge},
-	{WE6_date, "Date", prtext},
-	{WE6_facSelect, "Facility Select", general},
-	{WE6_facStatus, "Facility Status", general},
-	{WE6_statusCalled, "Status Called", general},
-	{WE6_addTransAttr, "Additional Transmission Attributes", general}
-};
-static int we_6_len = (sizeof(we_6) / sizeof(struct InformationElement));
-
-void
-dlogframe(struct IsdnCardState *sp, byte * buf, int size, char *comment) {
-	byte           *bend = buf + size;
-	char           *dp;
-	int i, cs = 0, cs_old = 0, cs_fest = 0;
-
-	/* display header */
-	dp = sp->dlogspace;
-	dp += sprintf(dp, "%s\n", comment);
-
-	{
-		byte *p = buf;
-		dp += sprintf(dp, "hex: ");
-		while (p < bend)
-		dp += sprintf(dp, "%02x ", *p++);
-		dp += sprintf(dp, "\n");
-		teles_putstatus(sp->dlogspace);
-		dp = sp->dlogspace;
-	} 
-	if ((0xfe & buf[0]) == PROTO_DIS_N0) {	/* 1TR6 */
-		/* locate message type */
-		if (buf[0] == PROTO_DIS_N0) {	/* N0 */
-			for (i = 0; i < mt_n0_len; i++)
-				if (mt_n0[i].nr == buf[3])
-					break;
-			/* display message type iff it exists */
-			if (i == mt_n0_len)
-				dp += sprintf(dp, "Unknown message type N0 %x!\n", buf[3]);
-			else
-				dp += sprintf(dp, "call reference %d size %d message type %s\n",
-					      buf[2], size, mt_n0[i].descr);
-		} else {	/* N1 */
-			for (i = 0; i < mt_n1_len; i++)
-				if (mt_n1[i].nr == buf[3])
-					break;
-			/* display message type iff it exists */
-			if (i == mt_n1_len)
-				dp += sprintf(dp, "Unknown message type N1 %x!\n", buf[3]);
-			else
-				dp += sprintf(dp, "call reference %d size %d message type %s\n",
-					      buf[2], size, mt_n1[i].descr);
-		}
-
-		/* display each information element */
-		buf += 4;
-		while (buf < bend) {
-			/* Is it a single octet information element? */
-			if (*buf & 0x80) {
-				switch ((*buf >> 4) & 7) {
-				  case 1:
-					dp += sprintf(dp, "  Shift %x\n", *buf & 0xf);
-					cs_old = cs;
-					cs = *buf & 7;
-					cs_fest = *buf & 8;
-					break;
-				  case 3:
-					dp += sprintf(dp, "  Congestion level %x\n", *buf & 0xf);
-					break;
-				  case 2:
-					if (*buf == 0xa0) {
-						dp += sprintf(dp, "  More data\n");
-						break;
-					}
-					if (*buf == 0xa1) {
-						dp += sprintf(dp, "  Sending complete\n");
-					}
-					break;
-				  /* fall through */
-				  default:
-					dp += sprintf(dp, "  Reserved %x\n", *buf);
-					break;
-				}
-				buf++;
-				continue;
-			}
-			/* No, locate it in the table */
-			if (cs == 0) {
-				for (i = 0; i < we_0_len; i++)
-					if (*buf == we_0[i].nr)
-						break;
-
-				/* When found, give appropriate msg */
-				if (i != we_0_len) {
-					dp += sprintf(dp, "  %s\n", we_0[i].descr);
-					dp += we_0[i].f(dp, buf);
-				} else
-					dp += sprintf(dp, "  Codeset %d attribute %x attribute size %d\n", cs, *buf, buf[1]);
-			} else if (cs == 6) {
-				for (i = 0; i < we_6_len; i++)
-					if (*buf == we_6[i].nr)
-						break;
-
-				/* When found, give appropriate msg */
-				if (i != we_6_len) {
-					dp += sprintf(dp, "  %s\n", we_6[i].descr);
-					dp += we_6[i].f(dp, buf);
-				} else
-					dp += sprintf(dp, "  Codeset %d attribute %x attribute size %d\n", cs, *buf, buf[1]);
-			} else
-				dp += sprintf(dp, "  Unknown Codeset %d attribute %x attribute size %d\n", cs, *buf, buf[1]);
-			/* Skip to next element */
-			if (cs_fest == 8) {
-				cs = cs_old;
-				cs_old = 0;
-				cs_fest = 0;
-			}
-			buf += buf[1] + 2;
-		}
-	} else if (buf[0]==PROTO_EURO) {	/* EURO */
-		/* locate message type */
-		for (i = 0; i < MTSIZE; i++)
-			if (mtlist[i].nr == buf[3])
-				break;
-
-		/* display message type iff it exists */
-		if (i == MTSIZE)
-			dp += sprintf(dp, "Unknown message type %x!\n", buf[3]);
-		else
-			dp += sprintf(dp, "call reference %d size %d message type %s\n",
-		buf[2], size, mtlist[i].descr);
-
-		/* display each information element */
-		buf += 4;
-		while (buf < bend) {
-			/* Is it a single octet information element? */
-			if (*buf & 0x80) {
-				switch ((*buf >> 4) & 7) {
-				  case 1:
-					dp += sprintf(dp, "  Shift %x\n", *buf & 0xf);
-					break;
-				  case 3:
-					dp += sprintf(dp, "  Congestion level %x\n", *buf & 0xf);
-					break;
-				  case 5:
-					dp += sprintf(dp, "  Repeat indicator %x\n", *buf & 0xf);
-					break;
-				  case 2:
-					if (*buf == 0xa0) {
-						dp += sprintf(dp, "  More data\n");
-						break;
-					}
-					if (*buf == 0xa1) {
-						dp += sprintf(dp, "  Sending complete\n");
-					}
-					break;
-				  /* fall through */
-				  default:
-					dp += sprintf(dp, "  Reserved %x\n", *buf);
-					break;
-				}
-				buf++;
-				continue;
-			}
-			/* No, locate it in the table */
-			for (i = 0; i < IESIZE; i++)
-				if (*buf == ielist[i].nr)
-					break;
-
-			/* When not found, give appropriate msg */
-			if (i != IESIZE) {
-				dp += sprintf(dp, "  %s\n", ielist[i].descr);
-				dp += ielist[i].f(dp, buf);
-			} else
-				dp += sprintf(dp, "  attribute %x attribute size %d\n", *buf, buf[1]);
-
-			/* Skip to next element */
-			buf += buf[1] + 2;
-		}
-	}
-	else dp += sprintf(dp,"Unnown frame type %.2x, ignored\n",buf[0]);
-
-	dp += sprintf(dp, "\n");
-	teles_putstatus(sp->dlogspace);
-}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/tei.c linux/drivers/isdn/teles/tei.c
--- v2.0.30/linux/drivers/isdn/teles/tei.c	Sun Apr 21 01:56:15 1996
+++ linux/drivers/isdn/teles/tei.c	Wed Dec 31 16:00:00 1969
@@ -1,248 +0,0 @@
-/* $Id: tei.c,v 1.1 1996/04/13 10:28:25 fritz Exp $
- *
- * $Log: tei.c,v $
- * Revision 1.1  1996/04/13 10:28:25  fritz
- * Initial revision
- *
- *
- */
-#define __NO_VERSION__
-#include "teles.h"
-
-extern struct IsdnCard cards[];
-extern int      nrcards;
-
-static struct PStack *
-findces(struct PStack *st, int ces)
-{
-	struct PStack  *ptr = *(st->l1.stlistp);
-
-	while (ptr)
-		if (ptr->l2.ces == ces)
-			return (ptr);
-		else
-			ptr = ptr->next;
-	return (NULL);
-}
-
-static struct PStack *
-findtei(struct PStack *st, int tei)
-{
-	struct PStack  *ptr = *(st->l1.stlistp);
-
-	if (tei == 127)
-		return (NULL);
-
-	while (ptr)
-		if (ptr->l2.tei == tei)
-			return (ptr);
-		else
-			ptr = ptr->next;
-	return (NULL);
-}
-
-void 
-tei_handler(struct PStack *st,
-	    byte pr, struct BufHeader *ibh)
-{
-	byte           *bp;
-	unsigned int    tces;
-	struct PStack  *otsp, *ptr;
-	unsigned int    data;
-
-	if (st->l2.debug)
-		printk(KERN_DEBUG "teihandler %d\n", pr);
-
-	switch (pr) {
-	  case (MDL_ASSIGN):
-		  data = (unsigned int) ibh;
-		  BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 6);
-		  if (!ibh)
-			  return;
-		  bp = DATAPTR(ibh);
-		  bp += st->l2.uihsize;
-		  bp[0] = 0xf;
-		  bp[1] = data >> 8;
-		  bp[2] = data & 0xff;
-		  bp[3] = 0x1;
-		  bp[4] = 0xff;
-		  ibh->datasize = 8;
-		  st->l3.l3l2(st, DL_UNIT_DATA, ibh);
-		  break;
-	  case (DL_UNIT_DATA):
-		  bp = DATAPTR(ibh);
-		  bp += 3;
-		  if (bp[0] != 0xf)
-			  break;
-		  switch (bp[3]) {
-		    case (2):
-			    tces = (bp[1] << 8) | bp[2];
-			    BufPoolRelease(ibh);
-			    if (st->l3.debug)
-				    printk(KERN_DEBUG "tei identity assigned for %d=%d\n", tces,
-					   bp[4] >> 1);
-			    if ((otsp = findces(st, tces)))
-				    otsp->ma.teil2(otsp, MDL_ASSIGN,
-						   (void *)(bp[4] >> 1));
-			    break;
-		    case (4):
-			    if (st->l3.debug)
-				    printk(KERN_DEBUG "checking identity for %d\n", bp[4] >> 1);
-			    if (bp[4] >> 1 == 0x7f) {
-				    BufPoolRelease(ibh);
-				    ptr = *(st->l1.stlistp);
-				    while (ptr) {
-					    if ((ptr->l2.tei & 0x7f) != 0x7f) {
-						    if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 7))
-							    break;
-						    bp = DATAPTR(ibh);
-						    bp += 3;
-						    bp[0] = 0xf;
-						    bp[1] = ptr->l2.ces >> 8;
-						    bp[2] = ptr->l2.ces & 0xff;
-						    bp[3] = 0x5;
-						    bp[4] = (ptr->l2.tei << 1) | 1;
-						    ibh->datasize = 8;
-						    st->l3.l3l2(st, DL_UNIT_DATA, ibh);
-					    }
-					    ptr = ptr->next;
-				    }
-			    } else {
-				    otsp = findtei(st, bp[4] >> 1);
-				    BufPoolRelease(ibh);
-				    if (!otsp)
-					    break;
-				    if (st->l3.debug)
-					    printk(KERN_DEBUG "ces is %d\n", otsp->l2.ces);
-				    if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 7))
-					    break;
-				    bp = DATAPTR(ibh);
-				    bp += 3;
-				    bp[0] = 0xf;
-				    bp[1] = otsp->l2.ces >> 8;
-				    bp[2] = otsp->l2.ces & 0xff;
-				    bp[3] = 0x5;
-				    bp[4] = (otsp->l2.tei << 1) | 1;
-				    ibh->datasize = 8;
-				    st->l3.l3l2(st, DL_UNIT_DATA, ibh);
-			    }
-			    break;
-		    default:
-			    BufPoolRelease(ibh);
-			    if (st->l3.debug)
-				    printk(KERN_DEBUG "tei message unknown %d ai %d\n", bp[3], bp[4] >> 1);
-		  }
-		  break;
-	  default:
-		  printk(KERN_WARNING "tei handler unknown primitive %d\n", pr);
-		  break;
-	}
-}
-
-unsigned int 
-randomces(void)
-{
-	int             x = jiffies & 0xffff;
-
-	return (x);
-}
-
-static void 
-tei_man(struct PStack *sp, int i, void *v)
-{
-	printk(KERN_DEBUG "tei_man\n");
-}
-
-static void 
-tei_l2tei(struct PStack *st, int pr, void *arg)
-{
-	struct IsdnCardState *sp = st->l1.hardware;
-
-	tei_handler(sp->teistack, pr, arg);
-}
-
-void 
-setstack_tei(struct PStack *st)
-{
-	st->l2.l2tei = tei_l2tei;
-}
-
-static void 
-init_tei(struct IsdnCardState *sp, int protocol)
-{
-	struct PStack  *st;
-	char            tmp[128];
-
-#define DIRTY_HACK_AGAINST_SIGSEGV
-
-	st = (struct PStack *) Smalloc(sizeof(struct PStack), GFP_KERNEL,
-				       "struct PStack");
-
-#ifdef DIRTY_HACK_AGAINST_SIGSEGV
-	sp->teistack = st;	                /* struct is not initialized yet */
-	sp->teistack->protocol = protocol;	/* struct is not initialized yet */
-#endif	                                        /* DIRTY_HACK_AGAINST_SIGSEGV    */
-
-
-	setstack_teles(st, sp);
-
-	st->l2.extended = !0;
-	st->l2.laptype = LAPD;
-	st->l2.window = 1;
-	st->l2.orig = !0;
-	st->protocol = protocol;
-
-/*
- * the following is not necessary for tei mng. (broadcast only)
- */
-
-	st->l2.t200 = 500;	/* 500 milliseconds */
-	st->l2.n200 = 4;	/* try 4 times */
-
-	st->l2.sap = 63;
-	st->l2.tei = 127;
-
-	sprintf(tmp, "Card %d tei ", sp->cardnr);
-	setstack_isdnl2(st, tmp);
-	st->l2.debug = 0;
-	st->l3.debug = 0;
-
-	st->ma.manl2(st, MDL_NOTEIPROC, NULL);
-
-	st->l2.l2l3 = (void *) tei_handler;
-	st->l1.l1man = tei_man;
-	st->l2.l2man = tei_man;
-	st->l4.l2writewakeup = NULL;
-	
-	teles_addlist(sp, st);
-	sp->teistack = st;
-}
-
-static void 
-release_tei(struct IsdnCardState *sp)
-{
-	struct PStack  *st = sp->teistack;
-
-	teles_rmlist(sp, st);
-	Sfree((void *) st);
-}
-
-void 
-TeiNew(void)
-{
-	int             i;
-
-	for (i = 0; i < nrcards; i++)
-		if (cards[i].sp)
-			init_tei(cards[i].sp, cards[i].protocol);
-}
-
-void 
-TeiFree(void)
-{
-	int             i;
-
-	for (i = 0; i < nrcards; i++)
-		if (cards[i].sp)
-			release_tei(cards[i].sp);
-}
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/teles.h linux/drivers/isdn/teles/teles.h
--- v2.0.30/linux/drivers/isdn/teles/teles.h	Sun Dec  1 10:01:37 1996
+++ linux/drivers/isdn/teles/teles.h	Wed Dec 31 16:00:00 1969
@@ -1,487 +0,0 @@
-/* $Id: teles.h,v 1.2 1996/04/30 21:52:04 isdn4dev Exp $
- *
- * $Log: teles.h,v $
- * Revision 1.2  1996/04/30 21:52:04  isdn4dev
- * SPV for 1TR6 - Karsten
- *
- * Revision 1.1  1996/04/13 10:29:00  fritz
- * Initial revision
- *
- *
- */
-#include <linux/module.h>
-#include <linux/version.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/major.h>
-#include <asm/segment.h>
-#include <asm/io.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/signal.h>
-#include <linux/malloc.h>
-#include <linux/mm.h>
-#include <linux/mman.h>
-#include <linux/ioport.h>
-#include <linux/timer.h>
-#include <linux/wait.h>
-#include <linux/isdnif.h>
-#include <linux/tty.h>
-
-#define PH_ACTIVATE   1
-#define PH_DATA       2
-#define PH_DEACTIVATE 3
-
-#define MDL_ASSIGN       4
-#define DL_UNIT_DATA     5
-#define SC_STARTUP       6
-#define CC_ESTABLISH     7
-#define DL_ESTABLISH     8
-#define DL_DATA          9
-#define CC_S_STATUS_ENQ  10
-
-#define CC_CONNECT       15
-#define CC_CONNECT_ACKNOWLEDGE 16
-#define CO_EOF                 17
-#define SC_DISCONNECT          18
-#define CO_DTMF                19
-#define DL_RELEASE             20
-
-#define CO_ALARM               22
-#define CC_REJECT              23
-
-#define CC_SETUP_REQ           24
-#define CC_SETUP_CNF           25
-#define CC_SETUP_IND           26
-#define CC_SETUP_RSP           27
-#define CC_SETUP_COMPLETE_IND  28
-
-#define CC_DISCONNECT_REQ      29
-#define CC_DISCONNECT_IND      30
-
-#define CC_RELEASE_CNF         31
-#define CC_RELEASE_IND         32
-#define CC_RELEASE_REQ         33
-
-#define CC_REJECT_REQ          34
-
-#define CC_PROCEEDING_IND      35
-
-#define CC_DLRL                36
-#define CC_DLEST               37
-
-#define CC_ALERTING_REQ        38
-#define CC_ALERTING_IND        39
-
-#define DL_STOP                40
-#define DL_START               41
-
-#define MDL_NOTEIPROC          46
-
-#define LC_ESTABLISH           47
-#define LC_RELEASE             48
-
-#define PH_REQUEST_PULL        49
-#define PH_PULL_ACK            50
-#define	PH_DATA_PULLED	       51
-#define CC_INFO_CHARGE         52
-
-/*
- * Message-Types 
- */
-
-#define MT_ALERTING            0x01
-#define MT_CALL_PROCEEDING     0x02
-#define MT_CONNECT             0x07
-#define MT_CONNECT_ACKNOWLEDGE 0x0f
-#define MT_PROGRESS            0x03
-#define MT_SETUP               0x05
-#define MT_SETUP_ACKNOWLEDGE   0x0d
-#define MT_RESUME              0x26
-#define MT_RESUME_ACKNOWLEDGE  0x2e
-#define MT_RESUME_REJECT       0x22
-#define MT_SUSPEND             0x25
-#define MT_SUSPEND_ACKNOWLEDGE 0x2d
-#define MT_SUSPEND_REJECT      0x21
-#define MT_USER_INFORMATION    0x20
-#define MT_DISCONNECT          0x45
-#define MT_RELEASE             0x4d
-#define MT_RELEASE_COMPLETE    0x5a
-#define MT_RESTART             0x46
-#define MT_RESTART_ACKNOWLEDGE 0x4e
-#define MT_SEGMENT             0x60
-#define MT_CONGESTION_CONTROL  0x79
-#define MT_INFORMATION         0x7b
-#define MT_FACILITY            0x62
-#define MT_NOTIFY              0x6e
-#define MT_STATUS              0x7d
-#define MT_STATUS_ENQUIRY      0x75
-
-#define IE_CAUSE               0x08
-
-struct HscxIoctlArg {
-	int             channel;
-	int             mode;
-	int             transbufsize;
-};
-
-#ifdef __KERNEL__
-
-#undef DEBUG_MAGIC
-
-#define HSCX_SBUF_ORDER     1
-#define HSCX_SBUF_BPPS      2
-#define HSCX_SBUF_MAXPAGES  3
-
-#define HSCX_RBUF_ORDER     1
-#define HSCX_RBUF_BPPS      2
-#define HSCX_RBUF_MAXPAGES  3
-
-#define HSCX_SMALLBUF_ORDER     0
-#define HSCX_SMALLBUF_BPPS      40
-#define HSCX_SMALLBUF_MAXPAGES  1
-
-#define ISAC_SBUF_ORDER     0
-#define ISAC_SBUF_BPPS      16
-#define ISAC_SBUF_MAXPAGES  1
-
-#define ISAC_RBUF_ORDER     0
-#define ISAC_RBUF_BPPS      16
-#define ISAC_RBUF_MAXPAGES  1
-
-#define ISAC_SMALLBUF_ORDER     0
-#define ISAC_SMALLBUF_BPPS      40
-#define ISAC_SMALLBUF_MAXPAGES  1
-
-#define byte unsigned char
-
-#define MAX_WINDOW 8
-
-byte           *Smalloc(int size, int pr, char *why);
-void            Sfree(byte * ptr);
-
-/*
- * Statemachine 
- */
-struct Fsm {
-	int            *jumpmatrix;
-	int             state_count, event_count;
-	char          **strEvent, **strState;
-};
-
-struct FsmInst {
-	struct Fsm     *fsm;
-	int             state;
-	int             debug;
-	void           *userdata;
-	int             userint;
-	void            (*printdebug) (struct FsmInst *, char *);
-};
-
-struct FsmNode {
-	int             state, event;
-	void            (*routine) (struct FsmInst *, int, void *);
-};
-
-struct FsmTimer {
-	struct FsmInst *fi;
-	struct timer_list tl;
-	int             event;
-	void           *arg;
-};
-
-struct BufHeader {
-#ifdef DEBUG_MAGIC
-	int             magic;
-#endif
-	struct BufHeader *next;
-	struct BufPool *bp;
-	int             datasize;
-	byte            primitive, where;
-	void           *heldby;
-};
-
-struct Pages {
-	struct Pages   *next;
-};
-
-struct BufPool {
-#ifdef DEBUG_MAGIC
-	int             magic;
-#endif
-	struct BufHeader *freelist;
-	struct Pages   *pageslist;
-	int             pageorder;
-	int             pagescount;
-	int             bpps;
-	int             bufsize;
-	int             maxpages;
-};
-
-struct BufQueue {
-#ifdef DEBUG_MAGIC
-	int             magic;
-#endif
-	struct BufHeader *head, *tail;
-};
-
-struct Layer1 {
-	void           *hardware;
-	int             hscx;
-	struct BufPool *sbufpool, *rbufpool, *smallpool;
-	struct PStack **stlistp;
-	int             act_state;
-	void            (*l1l2) (struct PStack *, int, struct BufHeader *);
-        void            (*l1man) (struct PStack *, int, void *);
-	int             hscxmode, hscxchannel, requestpull;
-};
-
-struct Layer2 {
-	int             sap, tei, ces;
-	int             extended, laptype;
-	int             uihsize, ihsize;
-	int             vs, va, vr;
-	struct BufQueue i_queue;
-	int             window, orig;
-	int             rejexp;
-	int             debug;
-	struct BufHeader *windowar[MAX_WINDOW];
-	int             sow;
-	struct FsmInst  l2m;
-	void            (*l2l1) (struct PStack *, int, struct BufHeader *);
-        void            (*l2l1discardq) (struct PStack *, int, void *, int);
-        void            (*l2man) (struct PStack *, int, void *);
-	void            (*l2l3) (struct PStack *, int, void *);
-	void            (*l2tei) (struct PStack *, int, void *);
-	struct FsmTimer t200_timer, t203_timer;
-	int             t200, n200, t203;
-	int             rc, t200_running;
-	char            debug_id[32];
-};
-
-struct Layer3 {
-	void            (*l3l4) (struct PStack *, int, struct BufHeader *);
-        void            (*l3l2) (struct PStack *, int, void *);
-	int             state, callref;
-	int             debug;
-};
-
-struct Layer4 {
-	void            (*l4l3) (struct PStack *, int, void *);
-	void           *userdata;
-	void            (*l1writewakeup) (struct PStack *);
-	void		(*l2writewakeup) (struct PStack *);
-};
-
-struct Management {
-	void            (*manl1) (struct PStack *, int, void *);
-        void            (*manl2) (struct PStack *, int, void *);
-	void            (*teil2) (struct PStack *, int, void *);
-};
-
-struct Param {
-	int             cause;
-	int             bchannel;
-	int             callref;     /* TEI-Number                      */
-	int             itc;
-	int             info;	     /* Service-Indicator               */
-	int             info2;	     /* Service-Indicator, second octet */
-	char            calling[40]; /* Called Id                       */
-	char            called[40];  /* Caller Id                       */
-	int             chargeinfo;  /* Charge Info - only for 1tr6 in
-				      * the moment 
-				      */
-	int		spv;	     /* SPV Flag */
-};
-
-struct PStack {
-	struct PStack  *next;
-	struct Layer1   l1;
-	struct Layer2   l2;
-	struct Layer3   l3;
-	struct Layer4   l4;
-	struct Management ma;
-	struct Param   *pa;
-	int             protocol;     /* EDSS1 or 1TR6 */
-};
-
-struct HscxState {
-	byte           *membase;
-	int             iobase;
-	int             inuse, init, active;
-	struct BufPool  sbufpool, rbufpool, smallpool;
-	struct IsdnCardState *sp;
-	int             hscx, mode;
-	int             transbufsize, receive;
-	struct BufHeader *rcvibh, *xmtibh;
-	int             rcvptr, sendptr;
-	struct PStack  *st;
-	struct tq_struct tqueue;
-	int             event;
-	struct BufQueue rq, sq;
-	int             releasebuf;
-#ifdef DEBUG_MAGIC
-	int             magic;	/* 301270 */
-#endif
-};
-
-struct IsdnCardState {
-#ifdef DEBUG_MAGIC
-	int             magic;
-#endif
-	byte           *membase;
-	int             iobase;
-	struct BufPool  sbufpool, rbufpool, smallpool;
-	struct PStack  *stlist;
-	struct BufHeader *xmtibh, *rcvibh;
-	int             rcvptr, sendptr;
-	int             event;
-	struct tq_struct tqueue;
-	int             ph_active;
-	struct BufQueue rq, sq;
-
-	int             cardnr, ph_state;
-	struct PStack  *teistack;
-	struct HscxState hs[2];
-
-	int             dlogflag;
-	char           *dlogspace;
-	int             debug;
-	int             releasebuf;
-};
-
-struct IsdnCard {
-	byte           *membase;
-	int             interrupt;
-	unsigned int    iobase;
-	int             protocol;	/* EDSS1 or 1TR6 */
-	struct IsdnCardState *sp;
-};
-
-#define DATAPTR(x) ((byte *)x+sizeof(struct BufHeader))
-
-#define LAPD 0
-#define LAPB 1
-
-void            BufPoolInit(struct BufPool *bp, int order, int bpps,
-			    int maxpages);
-int             BufPoolAdd(struct BufPool *bp, int priority);
-void            BufPoolFree(struct BufPool *bp);
-int             BufPoolGet(struct BufHeader **bh,
-	      struct BufPool *bp, int priority, void *heldby, int where);
-void            BufPoolRelease(struct BufHeader *bh);
-void            BufQueueLink(struct BufQueue *bq,
-			     struct BufHeader *bh);
-int             BufQueueUnlink(struct BufHeader **bh, struct BufQueue *bq);
-void            BufQueueInit(struct BufQueue *bq);
-void            BufQueueRelease(struct BufQueue *bq);
-void            BufQueueDiscard(struct BufQueue *q, int pr, void *heldby,
-				int releasetoo);
-int             BufQueueLength(struct BufQueue *bq);
-void            BufQueueLinkFront(struct BufQueue *bq,
-				  struct BufHeader *bh);
-
-void            l2down(struct PStack *st,
-		       byte pr, struct BufHeader *ibh);
-void            l2up(struct PStack *st,
-		     byte pr, struct BufHeader *ibh);
-void            acceptph(struct PStack *st,
-			 struct BufHeader *ibh);
-void            setstack_isdnl2(struct PStack *st, char *debug_id);
-int             teles_inithardware(void);
-void            teles_closehardware(void);
-
-void            setstack_teles(struct PStack *st, struct IsdnCardState *sp);
-unsigned int    randomces(void);
-void            setstack_isdnl3(struct PStack *st);
-void            teles_addlist(struct IsdnCardState *sp,
-			      struct PStack *st);
-void            releasestack_isdnl2(struct PStack *st);
-void            teles_rmlist(struct IsdnCardState *sp,
-			     struct PStack *st);
-void            newcallref(struct PStack *st);
-
-int             ll_init(void);
-void            ll_stop(void), ll_unload(void);
-int             setstack_hscx(struct PStack *st, struct HscxState *hs);
-void            modehscx(struct HscxState *hs, int mode, int ichan);
-byte           *findie(byte * p, int size, byte ie, int wanted_set);
-int             getcallref(byte * p);
-
-void            FsmNew(struct Fsm *fsm,
-		       struct FsmNode *fnlist, int fncount);
-void            FsmFree(struct Fsm *fsm);
-int             FsmEvent(struct FsmInst *fi,
-			 int event, void *arg);
-void            FsmChangeState(struct FsmInst *fi,
-			       int newstate);
-void            FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft);
-int             FsmAddTimer(struct FsmTimer *ft,
-			  int millisec, int event, void *arg, int where);
-void            FsmDelTimer(struct FsmTimer *ft, int where);
-int             FsmTimerRunning(struct FsmTimer *ft);
-void            jiftime(char *s, long mark);
-
-void            CallcNew(void);
-void            CallcFree(void);
-int             CallcNewChan(void);
-void            CallcFreeChan(void);
-int             teles_command(isdn_ctrl * ic);
-int             teles_writebuf(int id, int chan, const u_char * buf, int count, int user);
-void            teles_putstatus(char *buf);
-void            teles_reportcard(int cardnr);
-int             ListLength(struct BufHeader *ibh);
-void            dlogframe(struct IsdnCardState *sp, byte * p, int size, char *comment);
-void            iecpy(byte * dest, byte * iestart, int ieoffset);
-void            setstack_transl2(struct PStack *st);
-void            releasestack_transl2(struct PStack *st);
-void            close_hscxstate(struct HscxState *);
-void            setstack_tei(struct PStack *st);
-
-struct LcFsm {
-	struct FsmInst  lcfi;
-	int             type;
-	struct Channel *ch;
-	void            (*lccall) (struct LcFsm *, int, void *);
-	struct PStack  *st;
-	int             l2_establish;
-	int             l2_start;
-	struct FsmTimer act_timer;
-	char            debug_id[32];
-};
-
-struct Channel {
-	struct PStack   ds, is;
-	struct IsdnCardState *sp;
-	int             hscx;
-	int             chan;
-	int             incoming;
-	struct FsmInst  fi;
-	struct LcFsm    lc_d, lc_b;
-	struct Param    para;
-	int             debug;
-#ifdef DEBUG_MAGIC
-	int             magic;	/* 301272 */
-#endif
-	int             l2_protocol, l2_active_protocol;
-	int             l2_primitive, l2_headersize;
-	int             data_open;
-	int             outcallref;
-	int             impair;
-};
-
-#define PART_SIZE(order,bpps) (( (PAGE_SIZE<<order) -\
-  sizeof(void *))/bpps)
-#define BUFFER_SIZE(order,bpps) (PART_SIZE(order,bpps)-\
-  sizeof(struct BufHeader))
-
-#endif
-
-void            Isdnl2New(void);
-void            Isdnl2Free(void);
-void            TeiNew(void);
-void            TeiFree(void);
-
-
-
-
diff -u --recursive --new-file v2.0.30/linux/drivers/net/3c509.c linux/drivers/net/3c509.c
--- v2.0.30/linux/drivers/net/3c509.c	Tue Oct 29 17:42:40 1996
+++ linux/drivers/net/3c509.c	Fri Sep  5 11:20:20 1997
@@ -1,8 +1,8 @@
 /* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */
 /*
-	Written 1993-1995 by Donald Becker.
+	Written 1993-1997 by Donald Becker.
 
-	Copyright 1994,1995 by Donald Becker.
+	Copyright 1994-1997 by Donald Becker.
 	Copyright 1993 United States Government as represented by the
 	Director, National Security Agency.	 This software may be used and
 	distributed according to the terms of the GNU Public License,
@@ -31,9 +31,17 @@
 				what the docs say some people definitely
 				get problems with lower (but in card spec)
 				delays
+		v1.10 4/21/97 Fixed module code so that multiple cards may be detected,
+				other cleanups.  -djb
 */
 
-static char *version = "3c509.c:1.07 6/15/95 becker@cesdis.gsfc.nasa.gov\n";
+static char *version = "3c509.c:1.12 6/4/97 becker@cesdis.gsfc.nasa.gov\n";
+/* A few values that may be tweaked. */
+
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  (400*HZ/1000)
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+#define INTR_WORK      10
 
 #include <linux/module.h>
 
@@ -64,7 +72,7 @@
 
 /* To minimize the size of the driver source I only define operating
    constants if they are used several times.  You'll need the manual
-   if you want to understand driver details. */
+   anyway if you want to understand driver details. */
 /* Offsets from base I/O address. */
 #define EL3_DATA 0x00
 #define EL3_CMD 0x0e
@@ -115,11 +123,13 @@
 
 struct el3_private {
 	struct enet_statistics stats;
+	struct device *next_dev;
 	/* skb send-queue */
 	int head, size;
 	struct sk_buff *queue[SKB_QUEUE_SIZE];
 };
 static int id_port = 0x100;
+static struct device *el3_root_dev = NULL;
 
 static ushort id_read_eeprom(int index);
 static ushort read_eeprom(short ioaddr, int index);
@@ -130,9 +140,7 @@
 static struct enet_statistics *el3_get_stats(struct device *dev);
 static int el3_rx(struct device *dev);
 static int el3_close(struct device *dev);
-#ifdef HAVE_MULTICAST
 static void set_multicast_list(struct device *dev);
-#endif
 
 
 
@@ -140,7 +148,7 @@
 {
 	short lrs_state = 0xff, i;
 	ushort ioaddr, irq, if_port;
-	short *phys_addr = (short *)dev->dev_addr;
+	short phys_addr[3];
 	static int current_tag = 0;
 
 	/* First check all slots of the EISA bus.  The next slot address to
@@ -195,6 +203,8 @@
 	outb(0x02, 0xA79);           /* Return to WaitForKey state. */
 	/* Select an open I/O location at 0x1*0 to do contention select. */
 	for (id_port = 0x100; id_port < 0x200; id_port += 0x10) {
+		if (check_region(id_port, 1))
+			continue;
 		outb(0x00, id_port);
 		outb(0xff, id_port);
 		if (inb(id_port) & 0x01)
@@ -210,13 +220,6 @@
 	   on cards as they are found.  Cards with their tag set will not
 	   respond to subsequent ID sequences. */
 
-	if (check_region(id_port,1)) {
-	  static int once = 1;
-	  if (once) printk("3c509: Somebody has reserved 0x%x, can't do ID_PORT lookup, nor card auto-probing\n",id_port);
-          once = 0;
-          return -ENODEV;
-        }
-
 	outb(0x00, id_port);
 	outb(0x00, id_port);
 	for(i = 0; i < 255; i++) {
@@ -247,13 +250,13 @@
 		if_port = iobase >> 14;
 		ioaddr = 0x200 + ((iobase & 0x1f) << 4);
 	}
-	if (dev->irq > 1  &&  dev->irq < 16)
+	if (dev  &&  dev->irq > 1  &&  dev->irq < 16)
 		irq = dev->irq;
 	else
 		irq = id_read_eeprom(9) >> 12;
 
-	if (dev->base_addr != 0
-		&&      dev->base_addr != (unsigned short)ioaddr) {
+	if (dev  &&  dev->base_addr != 0
+		&&  dev->base_addr != (unsigned short)ioaddr) {
 		return -ENODEV;
 	}
 
@@ -270,9 +273,14 @@
 	/* Free the interrupt so that some other card can use it. */
 	outw(0x0f00, ioaddr + WN0_IRQ);
  found:
+	if (dev == NULL) {
+		dev = init_etherdev(dev, sizeof(struct el3_private));
+	}
+	memcpy(dev->dev_addr, phys_addr, sizeof(phys_addr));
 	dev->base_addr = ioaddr;
 	dev->irq = irq;
-	dev->if_port = if_port;
+	dev->if_port = (dev->mem_start & 0x1f) ? dev->mem_start & 3 : if_port;
+
 	request_region(dev->base_addr, EL3_IO_EXTENT, "3c509");
 
 	{
@@ -287,11 +295,15 @@
 	printk(", IRQ %d.\n", dev->irq);
 
 	/* Make up a EL3-specific-data structure. */
-	dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL);
+	if (dev->priv == NULL)
+		dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL);
 	if (dev->priv == NULL)
 		return -ENOMEM;
 	memset(dev->priv, 0, sizeof(struct el3_private));
 
+	((struct el3_private *)dev->priv)->next_dev = el3_root_dev;
+	el3_root_dev = dev;
+
 	if (el3_debug > 0)
 		printk(version);
 
@@ -300,9 +312,7 @@
 	dev->hard_start_xmit = &el3_start_xmit;
 	dev->stop = &el3_close;
 	dev->get_stats = &el3_get_stats;
-#ifdef HAVE_MULTICAST
-		dev->set_multicast_list = &set_multicast_list;
-#endif
+	dev->set_multicast_list = &set_multicast_list;
 
 	/* Fill in the generic fields of the device structure. */
 	ether_setup(dev);
@@ -354,7 +364,6 @@
 	outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
 
 	if (request_irq(dev->irq, &el3_interrupt, 0, "3c509", dev)) {
-		irq2dev_map[dev->irq] = NULL;
 		return -EAGAIN;
 	}
 
@@ -430,7 +439,7 @@
 	/* Transmitter timeout, serious problems. */
 	if (dev->tbusy) {
 		int tickssofar = jiffies - dev->trans_start;
-		if (tickssofar < 40*HZ/100)
+		if (tickssofar < TX_TIMEOUT)
 			return 1;
 		printk("%s: transmit timed out, Tx_status %2.2x status %4.4x "
 			   "Tx FIFO room %d.\n",
@@ -444,14 +453,6 @@
 		dev->tbusy = 0;
 	}
 
-	if (skb == NULL) {
-		dev_tint(dev);
-		return 0;
-	}
-
-	if (skb->len <= 0)
-		return 0;
-
 	if (el3_debug > 4) {
 		printk("%s: el3_start_xmit(length = %ld) called, status %4.4x.\n",
 			   dev->name, skb->len, inw(ioaddr + EL3_STATUS));
@@ -483,7 +484,11 @@
 		outw(skb->len, ioaddr + TX_FIFO);
 		outw(0x00, ioaddr + TX_FIFO);
 		/* ... and the packet rounded to a doubleword. */
+#ifdef  __powerpc__
+		outsl_unswapped(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+#else
 		outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+#endif
 
 		dev->trans_start = jiffies;
 		if (inw(ioaddr + TX_FREE) > 1536) {
@@ -516,7 +521,7 @@
 {
 	struct device *dev = (struct device *)dev_id;
 	int ioaddr, status;
-	int i = 0;
+	int i = INTR_WORK;
 
 	if (dev == NULL) {
 		printk ("el3_interrupt(): irq %d for unknown device.\n", irq);
@@ -568,7 +573,7 @@
 			}
 		}
 
-		if (++i > 10) {
+		if (--i < 0) {
 			printk("%s: Infinite loop in interrupt, status %4.4x.\n",
 				   dev->name, status);
 			/* Clear all interrupts. */
@@ -667,13 +672,18 @@
 					   pkt_len, rx_status);
 			if (skb != NULL) {
 				skb->dev = dev;
-				skb_reserve(skb,2);     /* Align IP on 16 byte */
+				skb_reserve(skb, 2);     /* Align IP on 16 byte */
 
 				/* 'skb->data' points to the start of sk_buff data area. */
-				insl(ioaddr+RX_FIFO, skb_put(skb,pkt_len),
-							(pkt_len + 3) >> 2);
+#ifdef  __powerpc__
+				insl_unswapped(ioaddr+RX_FIFO, skb_put(skb,pkt_len),
+							   (pkt_len + 3) >> 2);
+#else
+				insl(ioaddr + RX_FIFO, skb_put(skb,pkt_len),
+					 (pkt_len + 3) >> 2);
+#endif
 
-				skb->protocol=eth_type_trans(skb,dev);
+				skb->protocol = eth_type_trans(skb,dev);
 				netif_rx(skb);
 				outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
 				lp->stats.rx_packets++;
@@ -692,7 +702,6 @@
 	return 0;
 }
 
-#ifdef HAVE_MULTICAST
 /*
  *     Set or clear the multicast filter for this adaptor.
  */
@@ -717,7 +726,6 @@
 	else
                 outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
 }
-#endif
 
 static int
 el3_close(struct device *dev)
@@ -758,43 +766,50 @@
 }
 
 #ifdef MODULE
-static char devicename[9] = { 0, };
-static struct device dev_3c509 = {
-        devicename, /* device name is inserted by linux/drivers/net/net_init.c */
-        0, 0, 0, 0,
-        0, 0,
-        0, 0, 0, NULL, el3_probe };
-
-static int io  = 0;
-static int irq = 0;
+/* Parameter that may be passed into the module. */
+static int debug = -1;
+static int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int xcvr[] = {-1, -1, -1, -1, -1, -1, -1, -1};
 
 int
 init_module(void)
 {
-	dev_3c509.base_addr = io;
-        dev_3c509.irq       = irq;
-        if (!EISA_bus && !io) {
-		printk("3c509: WARNING! Module load-time probing works reliably only for EISA bus!!\n");
+	int el3_cards = 0;
+
+	if (debug >= 0)
+		el3_debug = debug;
+
+	el3_root_dev = NULL;
+	while (el3_probe(0) == 0) {
+		if (irq[el3_cards] > 1)
+			el3_root_dev->irq = irq[el3_cards];
+		if (xcvr[el3_cards] >= 0)
+			el3_root_dev->if_port = xcvr[el3_cards];
+		el3_cards++;
 	}
-	if (register_netdev(&dev_3c509) != 0)
-		return -EIO;
-	return 0;
+
+	return el3_cards ? 0 : -ENODEV;
 }
 
 void
 cleanup_module(void)
 {
-	unregister_netdev(&dev_3c509);
-	kfree_s(dev_3c509.priv,sizeof(struct el3_private));
-	dev_3c509.priv=NULL;
-	/* If we don't do this, we can't re-insmod it later. */
-	release_region(dev_3c509.base_addr, EL3_IO_EXTENT);
+	struct device *next_dev;
+
+	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+	while (el3_root_dev) {
+		next_dev = ((struct el3_private *)el3_root_dev->priv)->next_dev;
+		unregister_netdev(el3_root_dev);
+		release_region(el3_root_dev->base_addr, EL3_IO_EXTENT);
+		kfree(el3_root_dev);
+		el3_root_dev = next_dev;
+	}
 }
 #endif /* MODULE */
 
 /*
  * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 3c509.c"
+ *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c509.c"
  *  version-control: t
  *  kept-new-versions: 5
  *  tab-width: 4
diff -u --recursive --new-file v2.0.30/linux/drivers/net/3c59x.c linux/drivers/net/3c59x.c
--- v2.0.30/linux/drivers/net/3c59x.c	Tue Apr  8 08:47:45 1997
+++ linux/drivers/net/3c59x.c	Fri Sep  5 11:20:20 1997
@@ -1,31 +1,58 @@
-/* 3c59x.c: A 3Com 3c590/3c595 "Vortex" ethernet driver for linux. */
+/* EtherLink.c: A 3Com EtherLink PCI III/XL ethernet driver for linux. */
 /*
-	Written 1995 by Donald Becker.
+	Written 1996-1997 by Donald Becker.
 
 	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 3Com "Vortex" series ethercards.  Members of
-	the series include the 3c590 PCI EtherLink III and 3c595-Tx PCI Fast
-	EtherLink.
+	This driver is for the 3Com "Vortex" and "Boomerang" series ethercards.
+	Members of the series include Fast EtherLink 3c590/3c592/3c595/3c597
+	and the EtherLink XL 3c900 and 3c905 cards.
 
 	The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
 	Center of Excellence in Space Data and Information Sciences
 	   Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
 */
 
-static char *version = "3c59x.c:v0.25 5/17/96 becker@cesdis.gsfc.nasa.gov\n";
+static char *version =
+"3c59x.c:v0.43 9/2/97 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n";
 
 /* "Knobs" that turn on special features. */
-/* Enable the experimental automatic media selection code. */
+/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
+   Setting to > 1512 effectively disables this feature. */
+static const rx_copybreak = 200;
+/* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */
+static const mtu = 1500;
+
+/* Enable the automatic media selection code -- usually set. */
 #define AUTOMEDIA 1
 
-/* Allow the use of bus master transfers instead of programmed-I/O for the
-   Tx process.  Bus master transfers are always disabled by default, but
-   iff this is set they may be turned on using 'options'. */
+/* 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)
+
+/* Keep the ring sizes a power of two for efficiency. */
+#define TX_RING_SIZE	16
+#define RX_RING_SIZE	32
+#define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
+
+#include <linux/config.h>
+#ifdef MODULE
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
 #include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
 
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -39,6 +66,7 @@
 #include <linux/pci.h>
 #include <linux/bios32.h>
 #include <linux/timer.h>
+#include <asm/irq.h>			/* For NR_IRQS only. */
 #include <asm/bitops.h>
 #include <asm/io.h>
 
@@ -46,27 +74,60 @@
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 
+/* Kernel compatibility defines, common to David Hind's PCMCIA package.
+   This is only in the support-all-kernels source code. */
+#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)
+#else  /* 1.3.0 and later */
 #define RUN_AT(x) (jiffies + (x))
 #define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
+#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
+
+#if (LINUX_VERSION_CODE >= 0x10344)
+#define NEW_MULTICAST
+#include <linux/delay.h>
+#else
+#define udelay(microsec)	do { int _i = 4*microsec; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
+#endif
+
+#if (LINUX_VERSION_CODE < 0x20123)
+#define test_and_set_bit(val, addr) set_bit(val, addr)
+#endif
 
 /* "Knobs" for adjusting internal parameters. */
 /* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */
-#define VORTEX_DEBUG 2
-
-/* Number of times to check to see if the Tx FIFO has space, used in some
-   limited cases. */
-#define WAIT_TX_AVAIL 200
+#define VORTEX_DEBUG 1
+/* Some values here only for performance evaluation and path-coverage
+   debugging. */
+static int rx_nocopy = 0, rx_copy = 0, queued_packet = 0;
 
 /* Operational parameter that usually are not changed. */
-#define TX_TIMEOUT  40		/* Time in jiffies before concluding Tx hung */
 
-/* The total size is twice that of the original EtherLinkIII series: the
-   runtime register window, window 1, is now always mapped in. */
+/* The Vortex size is twice that of the original EtherLinkIII series: the
+   runtime register window, window 1, is now always mapped in.
+   The Boomerang size is twice as large as the Vortex -- it has additional
+   bus master control registers. */
 #define VORTEX_TOTAL_SIZE 0x20
+#define BOOMERANG_TOTAL_SIZE 0x40
 
 #ifdef HAVE_DEVLIST
 struct netdev_entry tc59x_drv =
@@ -79,24 +140,37 @@
 int vortex_debug = 1;
 #endif
 
-static int product_ids[] = {0x5900, 0x5950, 0x5951, 0x5952, 0, 0};
+/* Caution!  These entries must be consistent, with the EISA ones last. */
+static int product_ids[] = {0x5900, 0x5950, 0x5951, 0x5952, 0x9000, 0x9001,
+								0x9050, 0x9051, 0, 0};
 static const char *product_names[] = {
 	"3c590 Vortex 10Mbps",
 	"3c595 Vortex 100baseTX",
 	"3c595 Vortex 100baseT4",
 	"3c595 Vortex 100base-MII",
-	"EISA Vortex 3c597",
+	"3c900 Boomerang 10baseT",
+	"3c900 Boomerang 10Mbps/Combo",
+	"3c905 Boomerang 100baseTx",
+	"3c905 Boomerang 100baseT4",
+	"3c592 EISA 10mbps Demon/Vortex",
+	"3c597 EISA Fast Demon/Vortex",
 };
-#define DEMON_INDEX 5			/* Caution!  Must be consistent with above! */
+#define DEMON10_INDEX 8
+#define DEMON100_INDEX 9
 
 /*
 				Theory of Operation
 
 I. Board Compatibility
 
-This device driver is designed for the 3Com FastEtherLink, 3Com's PCI to
-10/100baseT adapter.  It also works with the 3c590, a similar product
-with only a 10Mbs interface.
+This device driver is designed for the 3Com FastEtherLink and FastEtherLink
+XL, 3Com's PCI to 10/100baseT adapters.  It also works with the 10Mbs
+versions of the FastEtherLink cards.  The supported product IDs are
+  3c590, 3c592, 3c595, 3c597, 3c900, 3c905
+
+The ISA 3c515 is supported with a seperate driver, 3c515.c, included with
+the kernel source or available from
+    cesdis.gsfc.nasa.gov:/pub/linux/drivers/3c515.html
 
 II. Board-specific settings
 
@@ -112,12 +186,33 @@
 series.  The primary interface is two programmed-I/O FIFOs, with an
 alternate single-contiguous-region bus-master transfer (see next).
 
+The 3c900 "Boomerang" series uses a full-bus-master interface with seperate
+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
+revisions.
+
 One extension that is advertised in a very large font is that the adapters
-are capable of being bus masters.  Unfortunately this capability is only for
-a single contiguous region making it less useful than the list of transfer
-regions available with the DEC Tulip or AMD PCnet.  Given the significant
-performance impact of taking an extra interrupt for each transfer, using
-DMA transfers is a win only with large blocks.
+are capable of being bus masters.  On the Vortex chip this capability was
+only for a single contiguous region making it far less useful than the full
+bus master capability.  There is a significant performance impact of taking
+an extra interrupt or polling for the completion of each transfer, as well
+as difficulty sharing the single transfer engine between the transmit and
+receive threads.  Using DMA transfers is a win only with large blocks or
+with the flawed versions of the Intel Orion motherboard PCI controller.
+
+The Boomerang chip's full-bus-master interface is useful, and has the
+currently-unused advantages over other similar chips that queued transmit
+packets may be reordered and receive buffer groups are associated with a
+single frame.
+
+With full-bus-master support, this driver uses a "RX_COPYBREAK" scheme.
+Tather 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
+copying cost of copying a frame to a correctly-sized skbuff.
+
 
 IIIC. Synchronization
 The driver runs as two independent, single-threaded flows of control.  One
@@ -127,8 +222,8 @@
 
 IV. Notes
 
-Thanks to Cameron Spitzer and Terry Murphy of 3Com for providing both
-3c590 and 3c595 boards.
+Thanks to Cameron Spitzer and Terry Murphy of 3Com for providing development
+3c590, 3c595, and 3c900 boards.
 The name "Vortex" is the internal 3Com project name for the PCI ASIC, and
 the EISA version is called "Demon".  According to Terry these names come
 from rides at the local amusement park.
@@ -159,8 +254,10 @@
 
 enum vortex_cmd {
 	TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
-	RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
-	TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
+	RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11,
+	UpStall = 6<<11, UpUnstall = (6<<11)+1,
+	DownStall = (6<<11)+2, DownUnstall = (6<<11)+3,
+	RxDiscard = 8<<11, TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
 	FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11,
 	SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
 	SetTxThreshold = 18<<11, SetTxStart = 19<<11,
@@ -175,7 +272,8 @@
 enum vortex_status {
 	IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
 	TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
-	IntReq = 0x0040, StatsFull = 0x0080, DMADone = 1<<8,
+	IntReq = 0x0040, StatsFull = 0x0080,
+	DMADone = 1<<8, DownComplete = 1<<9, UpComplete = 1<<10,
 	DMAInProgress = 1<<11,			/* DMA controller is still busy.*/
 	CmdInProgress = 1<<12,			/* EL3_CMD is still busy.*/
 };
@@ -190,6 +288,7 @@
 enum Window0 {
 	Wn0EepromCmd = 10,		/* Window 0: EEPROM command register. */
 	Wn0EepromData = 12,		/* Window 0: EEPROM results register. */
+	IntrStatus=0x0E,		/* Valid in all windows. */
 };
 enum Win0_EEPROM_bits {
 	EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0,
@@ -212,12 +311,12 @@
 		unsigned int ram_size:3, ram_width:1, ram_speed:2, rom_size:2;
 		int pad8:8;
 		unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1, autoselect:1;
-		int pad24:8;
+		int pad24:7;
 	} u;
 };
 
-enum Window4 {
-	Wn4_Media = 0x0A,		/* Window 4: Various transcvr/media bits. */
+enum Window4 {		/* Window 4: Xcvr/media bits. */
+	Wn4_NetDiag = 6, Wn4_PhysicalMgmt=8, Wn4_Media = 10,
 };
 enum Win4_Media_bits {
 	Media_SQE = 0x0008,		/* Enable SQE error counting for AUI. */
@@ -228,11 +327,53 @@
 enum Window7 {					/* Window 7: Bus Master control. */
 	Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12,
 };
+/* Boomerang bus master control registers. */
+enum MasterCtrl {
+	PktStatus = 0x20, DownListPtr = 0x24, FragAddr = 0x28, FragLen = 0x2c,
+	TxFreeThreshold = 0x2f, UpPktStatus = 0x30, UpListPtr = 0x38,
+};
+
+/* The Rx and Tx descriptor lists.
+   Caution Alpha hackers: these types are 32 bits!  Note also the 8 byte
+   alignment contraint on tx_ring[] and rx_ring[]. */
+#define LAST_FRAG  0x80000000			/* Last Addr/Len pair in descriptor. */
+struct boom_rx_desc {
+	u32 next;					/* Last entry points to 0.   */
+	s32 status;
+	u32 addr;					/* Up to addr/len possible.. */
+	s32 length;					/* set high bit to indicate last pair. */
+};
+/* Values for the Rx status entry. */
+enum rx_desc_status {
+	RxDComplete=0x00008000, RxDError=0x4000,
+	/* See boomerang_rx() for actual error bits */
+};
+
+struct boom_tx_desc {
+	u32 next;					/* Last entry points to 0.   */
+	s32 status;					/* bits 0:12 length, others see below.  */
+	u32 addr;
+	s32 length;
+};
+
+/* Values for the Tx status entry. */
+enum tx_desc_status {
+	CRCDisable=0x2000, TxDComplete=0x8000,
+	TxIntrUploaded=0x80000000,		/* IRQ when in FIFO, but maybe not sent. */
+};
 
 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. */
+	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];
+	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 sk_buff *tx_skb;		/* Packet being eaten by bus master ctrl.  */
 	struct timer_list timer;	/* Media selection timer. */
@@ -241,12 +382,23 @@
 	unsigned int available_media:8,	/* From Wn3_Options */
 	  media_override:3, 			/* Passed-in media type. */
 	  default_media:3,			/* Read from the EEPROM. */
-	  full_duplex:1, bus_master:1, autoselect:1;
+	  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  */
+      tx_full:1;
+	u16 capabilities;			/* Adapter capabilities word. */
+	u16 info1, info2;			/* Software information information. */
+	unsigned char phys[2];		/* MII device addresses. */
 };
 
 /* The action to take with a media selection timer tick.
    Note that we deviate from the 3Com order by checking 10base2 before AUI.
  */
+enum xcvr_types {
+	XCVR_10baseT=0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx,
+	XCVR_100baseFx, XCVR_MII=6, XCVR_Default=8,
+};
+
 static struct media_table {
   char *name;
   unsigned int media_bits:16,		/* Bits to set in Wn4_Media register. */
@@ -254,30 +406,37 @@
 	next:8;				/* The media type to try next. */
   short wait;			/* Time before we check media status. */
 } media_tbl[] = {
-  {	"10baseT",   Media_10TP,0x08, 3 /* 10baseT->10base2 */, (14*HZ)/10},
-  { "10Mbs AUI", Media_SQE, 0x20, 8 /* AUI->default */, (1*HZ)/10},
-  { "undefined", 0,			0x80, 0 /* Undefined */, 0},
-  { "10base2",   0,			0x10, 1 /* 10base2->AUI. */, (1*HZ)/10},
-  { "100baseTX", Media_Lnk, 0x02, 5 /* 100baseTX->100baseFX */, (14*HZ)/10},
-  { "100baseFX", Media_Lnk, 0x04, 6 /* 100baseFX->MII */, (14*HZ)/10},
-  { "MII",		 0,			0x40, 0 /* MII->10baseT */, (14*HZ)/10},
-  { "undefined", 0,			0x01, 0 /* Undefined/100baseT4 */, 0},
-  { "Default",	 0,			0xFF, 0 /* Use default */, 0},
+  {	"10baseT",   Media_10TP,0x08, XCVR_10base2, (14*HZ)/10},
+  { "10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1*HZ)/10},
+  { "undefined", 0,			0x80, XCVR_10baseT, 10000},
+  { "10base2",   0,			0x10, XCVR_AUI,		(1*HZ)/10},
+  { "100baseTX", Media_Lnk, 0x02, XCVR_100baseFx, (14*HZ)/10},
+  { "100baseFX", Media_Lnk, 0x04, XCVR_MII,		(14*HZ)/10},
+  { "MII",		 0,			0x40, XCVR_10baseT, 3*HZ },
+  { "undefined", 0,			0x01, XCVR_10baseT, 10000},
+  { "Default",	 0,			0xFF, XCVR_10baseT, 10000},
 };
 
 static int vortex_scan(struct device *dev);
-static int vortex_found_device(struct device *dev, int ioaddr, int irq,
-							   int product_index, int options);
+static struct device *vortex_found_device(struct device *dev, int ioaddr,
+										  int irq, int product_index,
+										  int options, int card_idx);
 static int vortex_probe1(struct device *dev);
 static int vortex_open(struct device *dev);
+static int mdio_read(int ioaddr, int phy_id, int location);
 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 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 set_rx_mode(struct device *dev);
+#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
@@ -297,11 +456,15 @@
 /* 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};
+/* A list of all installed Vortex devices, for removing the driver module. */
+static struct device *root_vortex_dev = NULL;
+
+/* Variables to work-around the Compaq PCI BIOS32 problem. */
+static int compaq_ioaddr = 0, compaq_irq = 0, compaq_prod_id = 0;
 
 #ifdef MODULE
 static int debug = -1;
-/* A list of all installed Vortex devices, for removing the driver module. */
-static struct device *root_vortex_dev = NULL;
 
 int
 init_module(void)
@@ -336,94 +499,144 @@
 {
 	int cards_found = 0;
 
+#ifndef NO_PCI					/* Allow an EISA-only driver. */
+	/* Ideally we would detect all 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 3Com cards
+	   in slot order. */
 	if (pcibios_present()) {
 		static int pci_index = 0;
-		static int board_index = 0;
-		for (; product_ids[board_index]; board_index++, pci_index = 0) {
-			for (; pci_index < 16; pci_index++) {
-				unsigned char pci_bus, pci_device_fn, pci_irq_line;
-				unsigned char pci_latency;
-				unsigned int pci_ioaddr;
-				unsigned short pci_command;
-
-				if (pcibios_find_device(TCOM_VENDOR_ID,
-										product_ids[board_index], pci_index,
-										&pci_bus, &pci_device_fn))
+		unsigned char pci_bus, pci_device_fn;
+
+		for (;pci_index < 0xff; pci_index++) {
+			unsigned char pci_irq_line, pci_latency;
+			unsigned short pci_command, vendor, device;
+			unsigned int pci_ioaddr;
+
+			int board_index = 0;
+			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_byte(pci_bus, pci_device_fn,
+									 PCI_INTERRUPT_LINE, &pci_irq_line);
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+									  PCI_BASE_ADDRESS_0, &pci_ioaddr);
+			/* Remove I/O space marker in bit 0. */
+			pci_ioaddr &= ~3;
+
+			if (vendor != TCOM_VENDOR_ID)
+				continue;
+
+			for (board_index = 0; product_ids[board_index]; board_index++) {
+				if (device == product_ids[board_index])
 					break;
-				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 (product_ids[board_index] == 0) {
+				printk("Unknown 3Com PCI ethernet adapter type %4.4x detected:"
+					   " not configured.\n", device);
+				continue;
+			}
+			if (check_region(pci_ioaddr, VORTEX_TOTAL_SIZE))
+				continue;
 
-#ifdef VORTEX_BUS_MASTER
+			dev = vortex_found_device(dev, pci_ioaddr, pci_irq_line,
+									  board_index, dev && dev->mem_start
+									  ? dev->mem_start : options[cards_found],
+									  cards_found);
+
+			if (dev) {
 				/* Get and check the bus-master and latency values.
 				   Some PCI BIOSes fail to set the master-enable bit, and
 				   the latency timer must be set to the maximum value to avoid
 				   data corruption that occurs when the timer expires during
-				   a transfer.  Yes, it's a bug. */
+				   a transfer -- a bug in the Vortex chip. */
 				pcibios_read_config_word(pci_bus, pci_device_fn,
 										 PCI_COMMAND, &pci_command);
 				if ( ! (pci_command & PCI_COMMAND_MASTER)) {
-					printk("  PCI Master Bit has not been set! Setting...\n");
+					printk("%s:  PCI Master Bit has not been set! "
+						   " Setting...\n", dev->name);
 					pci_command |= PCI_COMMAND_MASTER;
 					pcibios_write_config_word(pci_bus, pci_device_fn,
 											  PCI_COMMAND, pci_command);
 				}
 				pcibios_read_config_byte(pci_bus, pci_device_fn,
 										 PCI_LATENCY_TIMER, &pci_latency);
-				if (pci_latency != 255) {
-					printk("  Overriding PCI latency timer (CFLT) setting of"
-						   " %d, new value is 255.\n", pci_latency);
+				if (pci_latency != 248) {
+					printk("%s: Overriding PCI latency"
+						   " timer (CFLT) setting of %d, new value is 248.\n",
+						   dev->name, pci_latency);
 					pcibios_write_config_byte(pci_bus, pci_device_fn,
-											  PCI_LATENCY_TIMER, 255);
+											  PCI_LATENCY_TIMER, 248);
 				}
-#endif  /* VORTEX_BUS_MASTER */
-				vortex_found_device(dev, pci_ioaddr, pci_irq_line, board_index,
-									dev && dev->mem_start ? dev->mem_start
-									: options[cards_found]);
 				dev = 0;
 				cards_found++;
 			}
 		}
 	}
+#endif /* NO_PCI */
 
 	/* Now check all slots of the EISA bus. */
 	if (EISA_bus) {
 		static int ioaddr = 0x1000;
 		for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
+			int product_id, product_index;
+			if (check_region(ioaddr, VORTEX_TOTAL_SIZE))
+				continue;
 			/* Check the standard EISA ID register for an encoded '3Com'. */
 			if (inw(ioaddr + 0xC80) != 0x6d50)
 				continue;
 			/* Check for a product that we support, 3c59{2,7} any rev. */
-			if ((inw(ioaddr + 0xC82) & 0xF0FF) != 0x7059 		/* 597 */
-				&& (inw(ioaddr + 0xC82) & 0xF0FF) != 0x2059)	/* 592 */
+			product_id = inw(ioaddr + 0xC82) & 0xF0FF;
+			if (product_id == 0x7059) 		/* 597 */
+				product_index = DEMON100_INDEX;
+			else if (product_id == 0x2059)	/* 592 */
+				product_index = DEMON10_INDEX;
+			else
 				continue;
 			vortex_found_device(dev, ioaddr, inw(ioaddr + 0xC88) >> 12,
-								DEMON_INDEX,  dev && dev->mem_start
-								? dev->mem_start : options[cards_found]);
+								product_index,  dev && dev->mem_start
+								? dev->mem_start : options[cards_found],
+								cards_found);
 			dev = 0;
 			cards_found++;
 		}
 	}
 
+	/* Special code to work-around the Compaq PCI BIOS32 problem. */
+	if (compaq_ioaddr) {
+		vortex_found_device(dev, compaq_ioaddr, compaq_irq, compaq_prod_id,
+							dev && dev->mem_start ? dev->mem_start
+							: options[cards_found], cards_found);
+		cards_found++;
+		dev = 0;
+	}
+
+	/* Finally check for a 3c515 on the ISA bus. */
+	/* (3c515 support omitted on this version.) */
+
 	return cards_found;
 }
 
-static int vortex_found_device(struct device *dev, int ioaddr, int irq,
-							   int product_index, int options)
+static struct device *
+vortex_found_device(struct device *dev, int ioaddr, int irq,
+					int product_index, int options, int card_idx)
 {
 	struct vortex_private *vp;
 
 #ifdef MODULE
 	/* Allocate and fill new device structure. */
 	int dev_size = sizeof(struct device) +
-		sizeof(struct vortex_private);
-	
+		sizeof(struct vortex_private) + 15;		/* Pad for alignment */
+
 	dev = (struct device *) kmalloc(dev_size, GFP_KERNEL);
 	memset(dev, 0, dev_size);
-	dev->priv = ((void *)dev) + sizeof(struct device);
+	/* 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;
@@ -431,28 +644,36 @@
 	dev->init = vortex_probe1;
 	vp->product_name = product_names[product_index];
 	vp->options = options;
+	if (card_idx >= 0) {
+		if (full_duplex[card_idx] >= 0)
+			vp->full_duplex = full_duplex[card_idx];
+	} else
+		vp->full_duplex = (options >= 0 && (options & 0x10) ? 1 : 0);
+
 	if (options >= 0) {
-		vp->media_override = ((options & 7) == 2)  ?  0  :  options & 7;
-		vp->full_duplex = (options & 8) ? 1 : 0;
+		vp->media_override = ((options & 7) == XCVR_10baseTOnly) ?
+			XCVR_10baseT  :  options & 7;
 		vp->bus_master = (options & 16) ? 1 : 0;
 	} else {
 		vp->media_override = 7;
-		vp->full_duplex = 0;
 		vp->bus_master = 0;
 	}
 	ether_setup(dev);
 	vp->next_module = root_vortex_dev;
 	root_vortex_dev = dev;
 	if (register_netdev(dev) != 0)
-		return -EIO;
+		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_names[product_index];
 	vp->options = options;
@@ -468,13 +689,14 @@
 
 	vortex_probe1(dev);
 #endif /* MODULE */
-	return 0;
+	return dev;
 }
 
 static int vortex_probe1(struct device *dev)
 {
 	int ioaddr = dev->base_addr;
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
+	unsigned int eeprom[0x40], checksum = 0;		/* EEPROM contents */
 	int i;
 
 	printk("%s: 3Com %s at %#3x,", dev->name,
@@ -482,27 +704,33 @@
 
 	/* Read the station address from the EEPROM. */
 	EL3WINDOW(0);
-	for (i = 0; i < 3; i++) {
+	for (i = 0; i < 0x18; i++) {
 		short *phys_addr = (short *)dev->dev_addr;
 		int timer;
-		outw(EEPROM_Read + PhysAddr01 + i, ioaddr + Wn0EepromCmd);
+		outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd);
 		/* Pause for at least 162 us. for the read to take place. */
-		for (timer = 162*4 + 400; timer >= 0; timer--) {
-			SLOW_DOWN_IO;
+		for (timer = 4; timer >= 0; timer--) {
+			udelay(162);
 			if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0)
 				break;
 		}
-		phys_addr[i] = htons(inw(ioaddr + Wn0EepromData));
+		eeprom[i] = inw(ioaddr + Wn0EepromData);
+		checksum ^= eeprom[i];
+		if (i >= 10 && i < 13)
+			phys_addr[i - 10] = htons(inw(ioaddr + Wn0EepromData));
 	}
+	checksum = (checksum ^ (checksum >> 8)) & 0xff;
+	if (checksum != 0x00)
+		printk(" ***INVALID CHECKSUM %4.4x*** ", checksum);
 	for (i = 0; i < 6; i++)
 		printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i]);
 	printk(", IRQ %d\n", dev->irq);
 	/* Tell them about an invalid IRQ. */
-	if (vortex_debug && (dev->irq <= 0 || dev->irq > 15))
-		printk(" *** Warning: this IRQ is unlikely to work!\n");
+	if (vortex_debug && (dev->irq <= 0 || dev->irq >= NR_IRQS))
+		printk(" *** Warning: this IRQ is unlikely to work! ***\n");
 
 	{
-		char *ram_split[] = {"5:3", "3:1", "1:1", "invalid"};
+		char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
 		union wn3_config config;
 		EL3WINDOW(3);
 		vp->available_media = inw(ioaddr + Wn3_Options);
@@ -520,8 +748,41 @@
 		vp->default_media = config.u.xcvr;
 		vp->autoselect = config.u.autoselect;
 	}
+	if (vp->media_override != 7) {
+		printk("  Media override to transceiver type %d (%s).\n",
+			   vp->media_override, media_tbl[vp->media_override].name);
+		dev->if_port = vp->media_override;
+	}
 
-	/* We do a request_region() only to register /proc/ioports info. */
+	if (dev->if_port == XCVR_MII) {
+		int phy, phy_idx = 0;
+		EL3WINDOW(4);
+		for (phy = 0; phy < 32 && phy_idx < sizeof(vp->phys); phy++) {
+			int mii_status = mdio_read(ioaddr, phy, 0);
+			if (mii_status != 0xffff  && mii_status != 0x0000) {
+				vp->phys[phy_idx++] = phy;
+				printk("%s: MII transceiver found at address %d.\n",
+					   dev->name, phy);
+			}
+		}
+		if (phy_idx == 0) {
+			printk("%s: ***WARNING*** No MII transceivers found!\n",
+				   dev->name);
+			vp->phys[0] = 0;
+		}
+	}
+
+	vp->info1 = eeprom[13];
+	vp->info2 = eeprom[15];
+	vp->capabilities = eeprom[16];
+	if (vp->capabilities & 0x20) {
+	  vp->full_bus_master_tx = 1;
+	  printk("  Enabling bus-master transmits and %s receives.\n",
+			 (vp->info2 & 1) ? "early" : "whole-frame" );
+	  vp->full_bus_master_rx = (vp->info2 & 1) ? 1 : 2;
+	}
+
+	/* We do a request_region() to register /proc/ioports info. */
 	request_region(ioaddr, VORTEX_TOTAL_SIZE, vp->product_name);
 
 	/* The 3c59x-specific entries in the device structure. */
@@ -529,10 +790,62 @@
 	dev->hard_start_xmit = &vortex_start_xmit;
 	dev->stop = &vortex_close;
 	dev->get_stats = &vortex_get_stats;
+#ifdef NEW_MULTICAST
 	dev->set_multicast_list = &set_rx_mode;
+#else
+	dev->set_multicast_list = &set_multicast_list;
+#endif
 
 	return 0;
 }
+
+/* Read and write the MII registers using software-generated serial
+   MDIO protocol.  The maxium data clock rate is 2.5 Mhz. */
+#define mdio_delay(microsecs) udelay(microsecs)
+
+#define MDIO_SHIFT_CLK	0x01
+#define MDIO_DIR_WRITE	0x04
+#define MDIO_DATA_WRITE0 (0x00 | MDIO_DIR_WRITE)
+#define MDIO_DATA_WRITE1 (0x02 | MDIO_DIR_WRITE)
+#define MDIO_DATA_READ	0x02
+#define MDIO_ENB_IN		0x00
+
+static int mdio_read(int ioaddr, int phy_id, int location)
+{
+	int i;
+	int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+	unsigned short retval = 0;
+	int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+
+	/* Shift the read command bits out. */
+	for (i = 17; i >= 0; i--) {
+		int dataval = (read_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+		outw(dataval, mdio_addr);
+		mdio_delay(1);
+		outw(dataval | MDIO_SHIFT_CLK, mdio_addr);
+		mdio_delay(1);
+		outw(dataval, mdio_addr);
+		mdio_delay(1);
+	}
+	outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+	outw(MDIO_ENB_IN, mdio_addr);
+
+	for (i = 16; i > 0; i--) {
+		outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+		mdio_delay(1);
+		retval = (retval << 1) | ((inw(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
+		outw(MDIO_ENB_IN, mdio_addr);
+		mdio_delay(1);
+	}
+	/* Clear out extra bits.  Needed? */
+	for (i = 16; i > 0; i--) {
+		outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+		mdio_delay(1);
+		outw(MDIO_ENB_IN, mdio_addr);
+		mdio_delay(1);
+	}
+	return retval;
+}
 
 
 static int
@@ -545,8 +858,6 @@
 
 	/* Before initializing select the active media port. */
 	EL3WINDOW(3);
-	if (vp->full_duplex)
-		outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */
 	config.i = inl(ioaddr + Wn3_Config);
 
 	if (vp->media_override != 7) {
@@ -557,7 +868,7 @@
 		dev->if_port = vp->media_override;
 	} else if (vp->autoselect) {
 		/* Find first available media type, starting with 100baseTx. */
-		dev->if_port = 4;
+		dev->if_port = XCVR_100baseTx;
 		while (! (vp->available_media & media_tbl[dev->if_port].mask))
 			dev->if_port = media_tbl[dev->if_port].next;
 
@@ -576,6 +887,27 @@
 	config.u.xcvr = dev->if_port;
 	outl(config.i, ioaddr + Wn3_Config);
 
+	if (dev->if_port == XCVR_MII) {
+		int mii_reg1, mii_reg25;
+		/* We cheat here: we know that we are using the 83840 transceiver
+		   which summarizes the FD status in an extended register. */
+		EL3WINDOW(4);
+		/* Read BMSR (reg1) only to clear old status. */
+		mii_reg1 = mdio_read(ioaddr, vp->phys[0], 1);
+		mii_reg25 = mdio_read(ioaddr, vp->phys[0], 0x19);
+		if (vortex_debug > 1)
+			printk("%s: MII #%d status %4.4x, duplex report %4.4x,"
+				   " setting %s-duplex.\n", dev->name, vp->phys[0],
+				   mii_reg1, mii_reg25, mii_reg25 & 0x0080 ? "full" : "half");
+		if (mii_reg25 & 0x0080)
+			vp->full_duplex = 1;
+		EL3WINDOW(3);
+	}
+
+	/* Set the full-duplex bit. */
+	outb(((vp->info1 & 0x8000) || vp->full_duplex ? 0x20 : 0) |
+		 (dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl);
+
 	if (vortex_debug > 1) {
 		printk("%s: vortex_open() InternalConfig %8.8x.\n",
 			dev->name, config.i);
@@ -583,22 +915,32 @@
 
 	outw(TxReset, ioaddr + EL3_CMD);
 	for (i = 20; i >= 0 ; i--)
-		if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+		if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
 			break;
 
 	outw(RxReset, ioaddr + EL3_CMD);
 	/* Wait a few ticks for the RxReset command to complete. */
 	for (i = 20; i >= 0 ; i--)
-		if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+		if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
 			break;
 
 	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,
 					vp->product_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);
@@ -613,7 +955,7 @@
 	for (; i < 12; i+=2)
 		outw(0, ioaddr + i);
 
-	if (dev->if_port == 3)
+	if (dev->if_port == XCVR_10base2)
 		/* Start the thinnet transceiver. We should really wait 50ms...*/
 		outw(StartCoax, ioaddr + EL3_CMD);
 	EL3WINDOW(4);
@@ -623,18 +965,52 @@
 	/* Switch to the stats window, and clear all stats by reading. */
 	outw(StatsDisable, ioaddr + EL3_CMD);
 	EL3WINDOW(6);
-	for (i = 0; i < 10; i++)	
+	for (i = 0; i < 10; i++)
 		inb(ioaddr + i);
 	inw(ioaddr + 10);
 	inw(ioaddr + 12);
 	/* New: On the Vortex we must also clear the BadSSD counter. */
 	EL3WINDOW(4);
 	inb(ioaddr + 12);
+	/* ..and on the Boomerang we enable the extra statistics bits. */
+	outw(0x0040, ioaddr + Wn4_NetDiag);
 
 	/* Switch to register set 7 for normal use. */
 	EL3WINDOW(7);
 
-	/* Set receiver mode: presumably accept b-case and phys addr only. */
+	if (vp->full_bus_master_rx) { /* Boomerang bus master. */
+		vp->cur_rx = vp->dirty_rx = 0;
+		/* Initialize the RxEarly register as recommended. */
+		outw(SetRxThreshold + (1536>>2), ioaddr + EL3_CMD);
+		outl(0x0020, ioaddr + PktStatus);
+		if (vortex_debug > 2)
+			printk("%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].status = 0;	/* Clear complete bit. */
+			vp->rx_ring[i].length = 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. */
+			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
+			vp->rx_ring[i].addr = virt_to_bus(skb->tail);
+		}
+		vp->rx_ring[i-1].next = virt_to_bus(&vp->rx_ring[0]); /* Wrap the ring. */
+		outl(virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr);
+	}
+	if (vp->full_bus_master_tx) { 		/* Boomerang bus master Tx. */
+		dev->hard_start_xmit = &boomerang_start_xmit;
+		vp->cur_tx = vp->dirty_tx = 0;
+		outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold); /* Room for a packet. */
+		/* Clear the Tx ring. */
+		for (i = 0; i < TX_RING_SIZE; i++)
+			vp->tx_skbuff[i] = 0;
+		outl(0, ioaddr + DownListPtr);
+	}
+	/* Set reciever mode: presumably accept b-case and phys addr only. */
 	set_rx_mode(dev);
 	outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
 
@@ -645,12 +1021,17 @@
 	outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
 	outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
 	/* Allow status bits to be seen. */
-	outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
+	outw(SetStatusEnb | AdapterFailure|IntReq|StatsFull |
+		 (vp->full_bus_master_tx ? DownComplete : TxAvailable) |
+		 (vp->full_bus_master_rx ? UpComplete : RxComplete) |
+		 (vp->bus_master ? DMADone : 0),
+		 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
-		 | DMADone, ioaddr + EL3_CMD);
+		 | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete,
+			ioaddr + EL3_CMD);
 
 	MOD_INC_USE_COUNT;
 
@@ -676,7 +1057,7 @@
 	  EL3WINDOW(4);
 	  media_status = inw(ioaddr + Wn4_Media);
 	  switch (dev->if_port) {
-	  case 0:  case 4:  case 5:		/* 10baseT, 100baseTX, 100baseFX  */
+	  case XCVR_10baseT:  case XCVR_100baseTx:  case XCVR_100baseFx:
 		if (media_status & Media_LnkBeat) {
 		  ok = 1;
 		  if (vortex_debug > 1)
@@ -685,8 +1066,18 @@
 		} else if (vortex_debug > 1)
 		  printk("%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);
+			  if (vortex_debug > 1)
+				  printk("%s: MII #%d status register is %4.4x.\n",
+						 dev->name, vp->phys[0], mii_reg1);
+			  if (mii_reg1 & 0x0004)
+				  ok = 1;
+			  break;
+		  }
 	  default:					/* Other media types handled by Tx timeouts. */
 		if (vortex_debug > 1)
 		  printk("%s: Media %s is has no indication, %x.\n",
@@ -699,7 +1090,7 @@
 		do {
 			dev->if_port = media_tbl[dev->if_port].next;
 		} while ( ! (vp->available_media & media_tbl[dev->if_port].mask));
-		if (dev->if_port == 8) { /* Go back to default. */
+		if (dev->if_port == XCVR_Default) { /* Go back to default. */
 		  dev->if_port = vp->default_media;
 		  if (vortex_debug > 1)
 			printk("%s: Media selection failing, using default %s port.\n",
@@ -719,7 +1110,8 @@
 		config.u.xcvr = dev->if_port;
 		outl(config.i, ioaddr + Wn3_Config);
 
-		outw(dev->if_port == 3 ? StartCoax : StopCoax, ioaddr + EL3_CMD);
+		outw(dev->if_port == XCVR_10base2 ? StartCoax : StopCoax,
+			 ioaddr + EL3_CMD);
 	  }
 	  EL3WINDOW(old_window);
 	}   restore_flags(flags);
@@ -731,72 +1123,113 @@
 	return;
 }
 
-static int
-vortex_start_xmit(struct sk_buff *skb, struct device *dev)
+static void vortex_tx_timeout(struct device *dev)
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
 	int ioaddr = dev->base_addr;
+	int i;
 
-	/* Part of the following code is inspired by code from Giuseppe Ciaccio,
-	   ciaccio@disi.unige.it.
-	   It works around a ?bug? in the 8K Vortex that only occurs on some
-	   systems: the TxAvailable interrupt seems to be lost.
-	   The ugly work-around is to busy-wait for room available in the Tx
-	   buffer before deciding the transmitter is actually hung.
-	   This busy-wait should never really occur, since the problem is that
-	   there actually *is*  room in the Tx FIFO.
-
-	   This pointed out an optimization -- we can ignore dev->tbusy if
-	   we actually have room for this packet.
-	   */
-
-#if 0
-	/* unstable optimization */
-	if (inw(ioaddr + TxFree) > skb->len) /* We actually have free room. */
-	  dev->tbusy = 0;			/* Fake out the check below. */
-	else
-#endif
-	if (dev->tbusy) {
-		/* Transmitter timeout, serious problems. */
-		int tickssofar = jiffies - dev->trans_start;
-		int i;
-
-		if (tickssofar < 2)		/* We probably aren't empty. */
-			return 1;
-		/* Wait a while to see if there really is room. */
-		for (i = WAIT_TX_AVAIL; i >= 0; i--)
-			if (inw(ioaddr + TxFree) > skb->len)
-			  break;
-		if ( i < 0) {
-			if (tickssofar < TX_TIMEOUT)
-				return 1;
-			printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
-				   dev->name, inb(ioaddr + TxStatus), inw(ioaddr + EL3_STATUS));
-			/* Issue TX_RESET and TX_START commands. */
-			outw(TxReset, ioaddr + EL3_CMD);
-			for (i = 20; i >= 0 ; i--)
-				if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
-						break;
-			outw(TxEnable, ioaddr + EL3_CMD);
-			dev->trans_start = jiffies;
-			dev->tbusy = 0;
-			vp->stats.tx_errors++;
-			vp->stats.tx_dropped++;
-			dev_kfree_skb(skb, FREE_WRITE);
-			return 0;			/* Yes, silently *drop* the packet! */
+	printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
+		   dev->name, inb(ioaddr + TxStatus),
+		   inw(ioaddr + EL3_STATUS));
+	/* Slight code bloat to be user friendly. */
+	if ((inb(ioaddr + TxStatus) & 0x88) == 0x88)
+	  printk("%s: Transmitter encountered 16 collisions -- network"
+			 " network cable problem?\n", dev->name);
+#ifndef final_version
+	printk("  Flags; bus-master %d, full %d; dirty %d current %d.\n",
+		   vp->full_bus_master_tx, vp->tx_full, vp->dirty_tx, vp->cur_tx);
+	printk("  Down list %8.8x vs. %p.\n", inl(ioaddr + DownListPtr),
+		   &vp->tx_ring[0]);
+	for (i = 0; i < TX_RING_SIZE; i++) {
+	  printk("  %d: %p  length %8.8x status %8.8x\n", i,
+			 &vp->tx_ring[i],
+			 vp->tx_ring[i].length,
+			 vp->tx_ring[i].status);
+	}
+	if (vp->full_bus_master_rx) {
+		printk("  Switching to non-bus-master receives.\n");
+		outw(SetStatusEnb | AdapterFailure|IntReq|StatsFull |
+			 (vp->full_bus_master_tx ? DownComplete : TxAvailable) |
+			 RxComplete | (vp->bus_master ? DMADone : 0),
+			 ioaddr + EL3_CMD);
+	}
+#endif
+	/* Issue TX_RESET and TX_START commands. */
+	outw(TxReset, ioaddr + EL3_CMD);
+	for (i = 20; i >= 0 ; i--)
+	  if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
+		break;
+	if (vp->full_bus_master_tx) {
+	  /*  Change 6/25/97 Michael Sievers sieversm@mail.desy.de
+              The card has been resetted, but the Tx Ring is still full.
+		 Since the card won't know where to resume, 'update' the
+		 Tx Ring. Probably, we'll lose 16 packets this way, these
+		 will be accounted for as 'dropped'. The code to update the
+		 Tx Ring is taken from the Interrupt handler. */
+   
+	  unsigned int dirty_tx = vp->dirty_tx;
+	  
+	  while (vp->cur_tx - dirty_tx > 0) {
+		int entry = dirty_tx % TX_RING_SIZE;
+		if (inl(ioaddr + DownListPtr) ==
+			virt_to_bus(&vp->tx_ring[entry]))
+		  break;			/* It still hasn't been processed. */
+		if (vp->tx_skbuff[entry]) {
+		  if (vortex_debug > 0)
+			printk("%s: Freeing Tx ring entry %d\n",dev->name,entry);
+		  dev_kfree_skb(vp->tx_skbuff[entry], FREE_WRITE);
+		  vp->tx_skbuff[entry] = 0;
 		}
+		vp->stats.tx_errors++;
+		vp->stats.tx_dropped++;
+		dirty_tx++;
+	  }
+	  vp->dirty_tx = dirty_tx;
+	  vp->tx_full= 0;
+	} else {     /* not bus-master, no Tx ring to clear */
+	  vp->stats.tx_errors++;
+	  vp->stats.tx_dropped++;
 	}
+	
+	/* Issue Tx Enable */
+	outw(TxEnable, ioaddr + EL3_CMD);
+	dev->trans_start = jiffies;
+	
+	/* Switch to register set 7 for normal use. */
+	EL3WINDOW(7);
+	
+	/* The TxFreeThreshold has to be set again after a reset! */
+	if (vp->full_bus_master_tx) {  
+	  outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold);
+	  /* This is to be sure that all bus-master Tx features
+		 are correctly re-initialized after a reset, although
+		 the DownListPtr should already be 0 at this point. */
+	  outl(0, ioaddr + DownListPtr);
+	}
+	/* finally, allow new Transmits */
+	dev->tbusy = 0;
+	/* End of Michael Sievers <sieversm@mail.desy.de> changes. */
+}
 
-	if(skb == NULL) {
+static int
+vortex_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct vortex_private *vp = (struct vortex_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+
+#ifndef final_version
+	if (skb == NULL || skb->len <= 0) {
+		printk("%s: Obsolete driver layer request made: skbuff==NULL.\n",
+			   dev->name);
 		dev_tint(dev);
-		return NULL;
+		return 0;
 	}
+#endif
 
-	/* Block a timer-based transmit from overlapping.  This could better be
-	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
-	   If this ever occurs the queue layer is doing something evil! */
-	if (set_bit(0, (void*)&dev->tbusy) != 0) {
-		printk("%s: Transmitter access conflict.\n", dev->name);
+	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
+		if (jiffies - dev->trans_start >= TX_TIMEOUT)
+			vortex_tx_timeout(dev);
 		return 1;
 	}
 
@@ -849,7 +1282,7 @@
 					int j;
 					outw(TxReset, ioaddr + EL3_CMD);
 					for (j = 20; j >= 0 ; j--)
-						if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+						if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
 							break;
 				}
 				outw(TxEnable, ioaddr + EL3_CMD);
@@ -860,12 +1293,84 @@
 	return 0;
 }
 
+static int
+boomerang_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct vortex_private *vp = (struct vortex_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+
+#ifndef final_version
+	if (skb == NULL || skb->len <= 0) {
+		printk("%s: Obsolete driver layer request made: skbuff==NULL.\n",
+			   dev->name);
+		dev_tint(dev);
+		return 0;
+	}
+#endif
+
+	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
+		if (jiffies - dev->trans_start >= TX_TIMEOUT)
+			vortex_tx_timeout(dev);
+		return 1;
+	} else {
+		/* Calculate the next Tx descriptor entry. */
+		int entry = vp->cur_tx % TX_RING_SIZE;
+		struct boom_tx_desc *prev_entry =
+			&vp->tx_ring[(vp->cur_tx-1) % TX_RING_SIZE];
+		unsigned long flags, i;
+
+		if (vortex_debug > 3)
+			printk("%s: Trying to send a packet, Tx index %d.\n",
+				   dev->name, vp->cur_tx);
+		if (vp->tx_full) {
+		  if (vortex_debug >0)
+			printk("%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;
+
+		save_flags(flags);
+		cli();
+		outw(DownStall, ioaddr + EL3_CMD);
+		/* Wait for the stall to complete. */
+		for (i = 20; i >= 0 ; i--)
+			if ( (inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0)
+				break;
+		prev_entry->next = virt_to_bus(&vp->tx_ring[entry]);
+		if (inl(ioaddr + DownListPtr) == 0) {
+			outl(virt_to_bus(&vp->tx_ring[entry]), ioaddr + DownListPtr);
+			queued_packet++;
+		}
+		outw(DownUnstall, ioaddr + EL3_CMD);
+		restore_flags(flags);
+
+		vp->cur_tx++;
+		if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1)
+			vp->tx_full = 1;
+		else {					/* Clear previous interrupt enable. */
+			prev_entry->status &= ~TxIntrUploaded;
+			dev->tbusy = 0;
+		}
+		dev->trans_start = jiffies;
+		return 0;
+	}
+}
+
 /* 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)
 {
-	/* Use the now-standard shared IRQ implementation. */
+#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 *lp;
 	int ioaddr, status;
 	int latency;
@@ -911,13 +1416,41 @@
 			dev->tbusy = 0;
 			mark_bh(NET_BH);
 		}
+		if (status & DownComplete) {
+			unsigned int dirty_tx = lp->dirty_tx;
+
+			while (lp->cur_tx - dirty_tx > 0) {
+				int entry = dirty_tx % TX_RING_SIZE;
+				if (inl(ioaddr + DownListPtr) ==
+					virt_to_bus(&lp->tx_ring[entry]))
+					break;			/* It still hasn't been processed. */
+				if (lp->tx_skbuff[entry]) {
+					dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE);
+					lp->tx_skbuff[entry] = 0;
+				}
+				/* lp->stats.tx_packets++;  Counted below. */
+				dirty_tx++;
+			}
+			outw(AckIntr | DownComplete, ioaddr + EL3_CMD);
+			if (lp->tx_full) {
+				lp->tx_full= 0;
+				dev->tbusy = 0;
+				mark_bh(NET_BH);
+			}
+			lp->dirty_tx = dirty_tx;
+		}
 #ifdef VORTEX_BUS_MASTER
 		if (status & DMADone) {
 			outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */
 			dev->tbusy = 0;
+			dev_kfree_skb (lp->tx_skb, FREE_WRITE); /* Release the transfered buffer */
 			mark_bh(NET_BH);
 		}
 #endif
+		if (status & UpComplete) {
+			boomerang_rx(dev);
+			outw(AckIntr | UpComplete, ioaddr + EL3_CMD);
+		}
 		if (status & (AdapterFailure | RxEarly | StatsFull)) {
 			/* Handle all uncommon interrupts at once. */
 			if (status & RxEarly) {				/* Rx early is unused. */
@@ -943,7 +1476,8 @@
 							printk(" %2.2x", inb(ioaddr+reg));
 					}
 					EL3WINDOW(7);
-					outw(SetIntrEnb | 0x18, ioaddr + EL3_CMD);
+					outw(SetIntrEnb | TxAvailable | RxComplete
+						 | UpComplete | DownComplete, ioaddr + EL3_CMD);
 					DoneDidThat++;
 				}
 			}
@@ -960,10 +1494,10 @@
 		if (++i > 10) {
 			printk("%s: Infinite loop in interrupt, status %4.4x.  "
 				   "Disabling functions (%4.4x).\n",
-				   dev->name, status, SetStatusEnb | ((~status) & 0xFE));
+				   dev->name, status, SetStatusEnb | ((~status) & 0x7FE));
 			/* Disable all pending interrupts. */
-			outw(SetStatusEnb | ((~status) & 0xFE), ioaddr + EL3_CMD);
-			outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
+			outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD);
+			outw(AckIntr | 0x7FF, ioaddr + EL3_CMD);
 			break;
 		}
 		/* Acknowledge the IRQ. */
@@ -992,7 +1526,7 @@
 	while ((rx_status = inw(ioaddr + RxStatus)) > 0) {
 		if (rx_status & 0x4000) { /* Error, update stats. */
 			unsigned char rx_error = inb(ioaddr + RxErrors);
-			if (vortex_debug > 4)
+			if (vortex_debug > 2)
 				printk(" Rx error: status %2.2x.\n", rx_error);
 			vp->stats.rx_errors++;
 			if (rx_error & 0x01)  vp->stats.rx_over_errors++;
@@ -1011,28 +1545,36 @@
 					   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);
+				outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
 				skb->protocol = eth_type_trans(skb, dev);
-				netif_rx(skb);
+#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++;
 				/* Wait a limited time to go to next packet. */
 				for (i = 200; i >= 0; i--)
-					if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+					if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
 						break;
-				vp->stats.rx_packets++;
 				continue;
 			} else if (vortex_debug)
 				printk("%s: Couldn't allocate a sk_buff of size %d.\n",
 					   dev->name, pkt_len);
 		}
-		vp->stats.rx_dropped++;
 		outw(RxDiscard, ioaddr + EL3_CMD);
+		vp->stats.rx_dropped++;
 		/* Wait a limited time to skip this packet. */
 		for (i = 200; i >= 0; i--)
-			if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+			if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
 				break;
 	}
 
@@ -1040,17 +1582,111 @@
 }
 
 static int
+boomerang_rx(struct device *dev)
+{
+	struct vortex_private *vp = (struct vortex_private *)dev->priv;
+	int entry = vp->cur_rx % RX_RING_SIZE;
+	int ioaddr = dev->base_addr;
+	int rx_status;
+
+	if (vortex_debug > 5)
+		printk("   In boomerang_rx(), status %4.4x, rx_status %4.4x.\n",
+			   inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus));
+	while ((rx_status = vp->rx_ring[entry].status) & RxDComplete) {
+		if (rx_status & RxDError) { /* Error, update stats. */
+			unsigned char rx_error = rx_status >> 16;
+			if (vortex_debug > 4)
+				printk(" Rx error: status %2.2x.\n", rx_error);
+			vp->stats.rx_errors++;
+			if (rx_error & 0x01)  vp->stats.rx_over_errors++;
+			if (rx_error & 0x02)  vp->stats.rx_length_errors++;
+			if (rx_error & 0x04)  vp->stats.rx_frame_errors++;
+			if (rx_error & 0x08)  vp->stats.rx_crc_errors++;
+			if (rx_error & 0x10)  vp->stats.rx_length_errors++;
+		} else {
+			/* The packet length: up to 4.5K!. */
+			short pkt_len = rx_status & 0x1fff;
+			struct sk_buff *skb;
+
+			if (vortex_debug > 4)
+				printk("Receiving packet size %d status %4.4x.\n",
+					   pkt_len, rx_status);
+
+			/* 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 = dev;
+				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),
+					   pkt_len);
+				rx_copy++;
+			} else{
+				void *temp;
+				/* Pass up the skbuff already on the Rx ring. */
+				skb = vp->rx_skbuff[entry];
+				vp->rx_skbuff[entry] = NULL;
+				temp = skb_put(skb, pkt_len);
+				/* Remove this checking code for final release. */
+				if (bus_to_virt(vp->rx_ring[entry].addr) != temp)
+					printk("%s: Warning -- the skbuff addresses do not match"
+						   " in boomerang_rx: %p vs. %p / %p.\n", dev->name,
+						   bus_to_virt(vp->rx_ring[entry].addr),
+						   skb->head, temp);
+				rx_nocopy++;
+			}
+#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;
+			vp->stats.rx_packets++;
+		}
+		entry = (++vp->cur_rx) % RX_RING_SIZE;
+	}
+	/* Refill the Rx ring buffers. */
+	for (; vp->dirty_rx < vp->cur_rx; vp->dirty_rx++) {
+		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)
+				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_skbuff[entry] = skb;
+		}
+		vp->rx_ring[entry].status = 0;	/* Clear complete bit. */
+	}
+	return 0;
+}
+
+static int
 vortex_close(struct device *dev)
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
 	int ioaddr = dev->base_addr;
+	int i;
 
 	dev->start = 0;
 	dev->tbusy = 1;
 
-	if (vortex_debug > 1)
+	if (vortex_debug > 1) {
 		printk("%s: vortex_close() status %4.4x, Tx status %2.2x.\n",
 			   dev->name, inw(ioaddr + EL3_STATUS), inb(ioaddr + TxStatus));
+		printk("%s: vortex close stats: rx_nocopy %d rx_copy %d"
+			   " tx_queued %d.\n",
+			   dev->name, rx_nocopy, rx_copy, queued_packet);
+	}
 
 	del_timer(&vp->timer);
 
@@ -1061,13 +1697,40 @@
 	outw(RxDisable, ioaddr + EL3_CMD);
 	outw(TxDisable, ioaddr + EL3_CMD);
 
-	if (dev->if_port == 3)
+	if (dev->if_port == XCVR_10base2)
 		/* Turn off thinnet power.  Green! */
 		outw(StopCoax, ioaddr + EL3_CMD);
 
-	FREE_IRQ(dev->irq, dev);
+#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);
 
 	update_stats(ioaddr, dev);
+	if (vp->full_bus_master_rx) { /* Free Boomerang bus master Rx buffers. */
+		outl(0, ioaddr + UpListPtr);
+		for (i = 0; i < RX_RING_SIZE; i++)
+			if (vp->rx_skbuff[i]) {
+#if LINUX_VERSION_CODE < 0x20100
+				vp->rx_skbuff[i]->free = 1;
+#endif
+				dev_kfree_skb (vp->rx_skbuff[i], FREE_WRITE);
+				vp->rx_skbuff[i] = 0;
+			}
+	}
+	if (vp->full_bus_master_tx) { /* Free Boomerang bus master Tx buffers. */
+		outl(0, ioaddr + DownListPtr);
+		for (i = 0; i < TX_RING_SIZE; i++)
+			if (vp->tx_skbuff[i]) {
+				dev_kfree_skb(vp->tx_skbuff[i], FREE_WRITE);
+				vp->tx_skbuff[i] = 0;
+			}
+	}
+
 	MOD_DEC_USE_COUNT;
 
 	return 0;
@@ -1079,10 +1742,12 @@
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
-	update_stats(dev->base_addr, dev);
-	restore_flags(flags);
+	if (dev->start) {
+		save_flags(flags);
+		cli();
+		update_stats(dev->base_addr, dev);
+		restore_flags(flags);
+	}
 	return &vp->stats;
 }
 
@@ -1140,11 +1805,19 @@
 		new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast|RxProm;
 	} else	if ((dev->mc_list)  ||  (dev->flags & IFF_ALLMULTI)) {
 		new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast;
-	} else 
+	} else
 		new_mode = SetRxFilter | RxStation | RxBroadcast;
 
 	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
 
 #ifdef MODULE
 void
@@ -1162,12 +1835,14 @@
 		root_vortex_dev = next_dev;
 	}
 }
-#endif /* MODULE */
+
+#endif  /* MODULE */
 
 /*
  * Local variables:
- *  compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 3c59x.c -o ../../modules/3c59x.o"
+ *  compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c"
  *  c-indent-level: 4
+ *  c-basic-offset: 4
  *  tab-width: 4
  * End:
  */
diff -u --recursive --new-file v2.0.30/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.0.30/linux/drivers/net/Config.in	Thu Mar  6 10:03:51 1997
+++ linux/drivers/net/Config.in	Tue Aug 12 20:29:26 1997
@@ -50,7 +50,7 @@
       tristate '3c507 support' CONFIG_EL16
     fi
     tristate '3c509/3c579 support' CONFIG_EL3
-    tristate '3c590 series (592/595/597) "Vortex" support' CONFIG_VORTEX
+    tristate '3c590/3c900 series (592/595/597/900/905) "Vortex/Boomerang" support' CONFIG_VORTEX
   fi
   bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE
   if [ "$CONFIG_LANCE" = "y" ]; then
@@ -60,6 +60,7 @@
   if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then
     tristate 'WD80*3 support' CONFIG_WD80x3
     tristate 'SMC Ultra support' CONFIG_ULTRA
+    tristate 'SMC Ultra32 support' CONFIG_ULTRA32
     tristate 'SMC 9194 support' CONFIG_SMC9194
   fi
   bool 'Other ISA cards' CONFIG_NET_ISA
@@ -97,6 +98,7 @@
       tristate 'Ansel Communications EISA 3200 support (EXPERIMENTAL)' CONFIG_AC3200
     fi
     tristate 'Apricot Xen-II on board ethernet' CONFIG_APRICOT
+    tristate 'Intel EtherExpress/Pro 100B support' CONFIG_EEXPRESS_PRO100B
     tristate 'DE425, DE434, DE435, DE450, DE500 support' CONFIG_DE4X5
     tristate 'DECchip Tulip (dc21x4x) PCI support' CONFIG_DEC_ELCP
     tristate 'Digi Intl. RightSwitch SE-X support' CONFIG_DGRS
diff -u --recursive --new-file v2.0.30/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.0.30/linux/drivers/net/Makefile	Tue Oct 29 17:42:40 1996
+++ linux/drivers/net/Makefile	Tue Aug 12 20:29:26 1997
@@ -144,6 +144,16 @@
   endif
 endif
 
+ifeq ($(CONFIG_ULTRA32),y)
+L_OBJS += smc-ultra32.o
+CONFIG_8390_BUILTIN = y
+else
+  ifeq ($(CONFIG_ULTRA),m)
+  CONFIG_8390_MODULE = y
+  M_OBJS += smc-ultra32.o
+  endif
+endif
+
 ifeq ($(CONFIG_E2100),y)
 L_OBJS += e2100.o
 CONFIG_8390_BUILTIN = y
@@ -309,6 +319,15 @@
   M_OBJS += eepro.o
   endif
 endif
+
+ifeq ($(CONFIG_EEXPRESS_PRO100B),y)
+L_OBJS += eepro100.o
+else
+  ifeq ($(CONFIG_EEXPRESS_PRO100B),m)
+  M_OBJS += eepro100.o
+  endif
+endif
+
 
 ifeq ($(CONFIG_WAVELAN),y)
 L_OBJS += wavelan.o
diff -u --recursive --new-file v2.0.30/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v2.0.30/linux/drivers/net/Space.c	Tue Apr  8 08:47:45 1997
+++ linux/drivers/net/Space.c	Tue Aug 12 20:29:26 1997
@@ -41,6 +41,7 @@
 extern int tulip_probe(struct device *dev);
 extern int hp100_probe(struct device *dev);
 extern int ultra_probe(struct device *dev);
+extern int ultra32_probe(struct device *dev);
 extern int wd_probe(struct device *dev);
 extern int el2_probe(struct device *dev);
 extern int ne_probe(struct device *dev);
@@ -49,6 +50,7 @@
 extern int znet_probe(struct device *);
 extern int express_probe(struct device *);
 extern int eepro_probe(struct device *);
+extern int eepro100_probe(struct device *);
 extern int el3_probe(struct device *);
 extern int at1500_probe(struct device *);
 extern int at1700_probe(struct device *);
@@ -111,6 +113,9 @@
 #if defined(CONFIG_ULTRA)
 	&& ultra_probe(dev)
 #endif
+#if defined(CONFIG_ULTRA32)
+	&& ultra32_probe(dev)
+#endif
 #if defined(CONFIG_SMC9194)
 	&& smc_init(dev)
 #endif
@@ -158,6 +163,9 @@
 #endif
 #ifdef CONFIG_EEXPRESS_PRO	/* Intel EtherExpress Pro/10 */
 	&& eepro_probe(dev)
+#endif
+#ifdef CONFIG_EEXPRESS_PRO100B	/* Intel EtherExpress Pro100B */
+	&& eepro100_probe(dev)
 #endif
 #ifdef CONFIG_DEPCA		/* DEC DEPCA */
 	&& depca_probe(dev)
diff -u --recursive --new-file v2.0.30/linux/drivers/net/atp.c linux/drivers/net/atp.c
--- v2.0.30/linux/drivers/net/atp.c	Thu Feb 29 21:50:43 1996
+++ linux/drivers/net/atp.c	Tue Aug 12 13:21:12 1997
@@ -1,9 +1,9 @@
 /* atp.c: Attached (pocket) ethernet adapter driver for linux. */
 /*
-	This is a driver for a commonly OEMed pocket (parallel port)
-	ethernet adapter.  
+	This is a driver for commonly OEMed pocket (parallel port)
+	ethernet adapters based on the Realtek RTL8002 and RTL8012 chips.
 
-	Written 1993,1994,1995 by Donald Becker.
+	Written 1993-95,1997 by Donald Becker.
 
 	Copyright 1993 United States Government as represented by the
 	Director, National Security Agency.
@@ -19,7 +19,11 @@
 */
 
 static const char *version =
-	"atp.c:v1.01 1/18/95 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+	"atp.c:v1.08 4/1/97 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+/* Operational parameters that may be safely changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  ((400*HZ)/1000)
 
 /*
 	This file is a device driver for the RealTek (aka AT-Lan-Tec) pocket
@@ -33,6 +37,10 @@
 	description is written based on guesses and writing lots of special-purpose
 	code to test my theorized operation.
 
+	In 1997 Realtek made available the documentation for the second generation
+	RTL8012 chip, which has lead to several driver improvements.
+	  http://www.realtek.com.tw/cn/cn.html
+
 					Theory of Operation
 	
 	The RTL8002 adapter seems to be built around a custom spin of the SEEQ
@@ -58,6 +66,10 @@
 	timing and control bits.  The data is then read from status port or written
 	to the data port.
 
+	Correction: the controller has two banks of 16 registers.  The second
+	bank contains only the multicast filter table (now used) and the EEPROM
+	access registers.
+
 	Since the bulk data transfer of the actual packets through the slow
 	parallel port dominates the driver's running time, four distinct data
 	(non-register) transfer modes are provided by the adapter, two in each
@@ -80,6 +92,14 @@
 	generate reasonable object code.  This header file also documents my
 	interpretations of the device registers.
 */
+#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
 
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -103,6 +123,49 @@
 
 #include "atp.h"
 
+/* Kernel compatibility defines, common to David Hind's PCMCIA package.
+   This is only in the support-all-kernels source code. */
+#include <linux/version.h>		/* Evil, but neccessary */
+
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10300
+#define RUN_AT(x) (x)			/* What to put in timer->expires.  */
+#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC)
+#define virt_to_bus(addr)  ((unsigned long)addr)
+#define bus_to_virt(addr) ((void*)addr)
+
+#else  /* 1.3.0 and later */
+#define RUN_AT(x) (jiffies + (x))
+#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
+#endif
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10338
+#ifdef MODULE
+#if !defined(CONFIG_MODVERSIONS) && !defined(__NO_VERSION__)
+char kernel_version[] = UTS_RELEASE;
+#endif
+#else
+#undef MOD_INC_USE_COUNT
+#define MOD_INC_USE_COUNT
+#undef MOD_DEC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+#endif /* 1.3.38 */
+
+#if (LINUX_VERSION_CODE >= 0x10344)
+#define NEW_MULTICAST
+#include <linux/delay.h>
+#endif
+
+#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
+/* End of kernel compatibility defines. */
+
 /* use 0 for production, 1 for verification, >2 for debug */
 #ifndef NET_DEBUG
 #define NET_DEBUG 1
@@ -112,6 +175,9 @@
 /* The number of low I/O ports used by the ethercard. */
 #define ETHERCARD_TOTAL_SIZE	3
 
+/* Sequence to switch an 8012 from printer mux to ethernet mode. */
+static char mux_8012[] = { 0xff, 0xf7, 0xff, 0xfb, 0xf3, 0xfb, 0xff, 0xf7,};
+
 /* This code, written by wwc@super.org, resets the adapter every
    TIMED_CHECKER ticks.  This recovers from an unknown error which
    hangs the device. */
@@ -119,13 +185,11 @@
 #ifdef TIMED_CHECKER
 #include <linux/timer.h>
 static void atp_timed_checker(unsigned long ignored);
-static struct device *atp_timed_dev;
-static struct timer_list atp_timer = {NULL, NULL, 0, 0, atp_timed_checker};
 #endif
 
 /* Index to functions, as function prototypes. */
 
-extern int atp_probe(struct device *dev);
+extern int atp_init(struct device *dev);
 
 static int atp_probe1(struct device *dev, short ioaddr);
 static void get_node_ID(struct device *dev);
@@ -135,14 +199,23 @@
 static void write_packet(short ioaddr, int length, unsigned char *packet, int mode);
 static void trigger_send(short ioaddr, int length);
 static int	net_send_packet(struct sk_buff *skb, struct device *dev);
-static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void net_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs);
 static void net_rx(struct device *dev);
 static void read_block(short ioaddr, int length, unsigned char *buffer, int data_mode);
 static int net_close(struct device *dev);
 static struct enet_statistics *net_get_stats(struct device *dev);
-static void set_multicast_list(struct device *dev);
+#ifdef NEW_MULTICAST
+static void set_rx_mode_8002(struct device *dev);
+static void set_rx_mode_8012(struct device *dev);
+#else
+static void set_rx_mode_8002(struct device *dev, int num_addrs, void *addrs);
+static void set_rx_mode_8012(struct device *dev, int num_addrs, void *addrs);
+#endif
 
 
+/* A list of all installed ATP devices, for removing the driver module. */
+static struct device *root_atp_dev = NULL;
+
 /* Check for a network adapter of this type, and return '0' iff one exists.
    If dev->base_addr == 0, probe all likely locations.
    If dev->base_addr == 1, always return failure.
@@ -153,7 +226,7 @@
 atp_init(struct device *dev)
 {
 	int *port, ports[] = {0x378, 0x278, 0x3bc, 0};
-	int base_addr = dev->base_addr;
+	int base_addr = dev ? dev->base_addr : 0;
 
 	if (base_addr > 0x1ff)		/* Check a single specified location. */
 		return atp_probe1(dev, base_addr);
@@ -174,7 +247,8 @@
 
 static int atp_probe1(struct device *dev, short ioaddr)
 {
-	int saved_ctrl_reg, status;
+	struct net_local *lp;
+	int saved_ctrl_reg, status, i;
 
 	outb(0xff, ioaddr + PAR_DATA);
 	/* Save the original value of the Control register, in case we guessed
@@ -182,6 +256,9 @@
 	saved_ctrl_reg = inb(ioaddr + PAR_CONTROL);
 	/* IRQEN=0, SLCTB=high INITB=high, AUTOFDB=high, STBB=high. */
 	outb(0x04, ioaddr + PAR_CONTROL);
+	/* Turn off the printer multiplexer on the 8012. */
+	for (i = 0; i < 8; i++)
+		outb(mux_8012[i], ioaddr + PAR_DATA);
 	write_reg_high(ioaddr, CMR1, CMR1h_RESET);
 	eeprom_delay(2048);
 	status = read_nibble(ioaddr, CMR1);
@@ -196,6 +273,9 @@
 		outb(saved_ctrl_reg, ioaddr + PAR_CONTROL);
 		return 1;
 	}
+
+	dev = init_etherdev(dev, sizeof(struct net_local));
+
 	/* Find the IRQ used by triggering an interrupt. */
 	write_reg_byte(ioaddr, CMR2, 0x01);			/* No accept mode, IRQ out. */
 	write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE);	/* Enable Tx and Rx. */
@@ -218,27 +298,29 @@
 		   dev->irq, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
 		   dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
 
-	/* Leave the hardware in a reset state. */
-    write_reg_high(ioaddr, CMR1, CMR1h_RESET);
+	/* Reset the ethernet hardware and activate the printer pass-through. */
+    write_reg_high(ioaddr, CMR1, CMR1h_RESET | CMR1h_MUX);
 
 	if (net_debug)
 		printk(version);
 
 	/* Initialize the device structure. */
 	ether_setup(dev);
-	dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+	if (dev->priv == NULL)
+		dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
 	if (dev->priv == NULL)
 		return -ENOMEM;
 	memset(dev->priv, 0, sizeof(struct net_local));
 
+	lp = (struct net_local *)dev->priv;
+	lp->chip_type = RTL8002;
+	lp->addr_mode = CMR2h_Normal;
 
-	{
-		struct net_local *lp = (struct net_local *)dev->priv;
-		lp->addr_mode = CMR2h_Normal;
-	}
+	lp->next_module = root_atp_dev;
+	root_atp_dev = dev;
 
 	/* For the ATP adapter the "if_port" is really the data transfer mode. */
-	dev->if_port = (dev->mem_start & 0xf) ? dev->mem_start & 0x7 : 4;
+	dev->if_port = (dev->mem_start & 0xf) ? (dev->mem_start & 0x7) : 4;
 	if (dev->mem_end & 0xf)
 		net_debug = dev->mem_end & 7;
 
@@ -246,14 +328,9 @@
 	dev->stop		= net_close;
 	dev->hard_start_xmit = net_send_packet;
 	dev->get_stats	= net_get_stats;
-	dev->set_multicast_list = &set_multicast_list;
+	dev->set_multicast_list =
+	  lp->chip_type == RTL8002 ? &set_rx_mode_8002 : &set_rx_mode_8012;
 
-#ifdef TIMED_CHECKER
-	del_timer(&atp_timer);
-	atp_timer.expires = jiffies + TIMED_CHECKER;
-	atp_timed_dev = dev;
-	add_timer(&atp_timer);
-#endif
 	return 0;
 }
 
@@ -322,18 +399,32 @@
    */
 static int net_open(struct device *dev)
 {
+	struct net_local *lp = (struct net_local *)dev->priv;
 
 	/* The interrupt line is turned off (tri-stated) when the device isn't in
 	   use.  That's especially important for "attached" interfaces where the
 	   port or interrupt may be shared. */
+#ifndef SA_SHIRQ
 	if (irq2dev_map[dev->irq] != 0
 		|| (irq2dev_map[dev->irq] = dev) == 0
-		|| request_irq(dev->irq, &net_interrupt, 0, "ATP", NULL)) {
+		|| REQUEST_IRQ(dev->irq, &net_interrupt, 0, "ATP", dev)) {
 		return -EAGAIN;
 	}
+#else
+	if (request_irq(dev->irq, &net_interrupt, 0, "ATP Ethernet", dev))
+		return -EAGAIN;
+#endif
 
+	MOD_INC_USE_COUNT;
 	hardware_init(dev);
 	dev->start = 1;
+
+	init_timer(&lp->timer);
+	lp->timer.expires = RUN_AT(TIMED_CHECKER);
+	lp->timer.data = (unsigned long)dev;
+	lp->timer.function = &atp_timed_checker;    /* timer handler */
+	add_timer(&lp->timer);
+
 	return 0;
 }
 
@@ -345,6 +436,9 @@
 	int ioaddr = dev->base_addr;
     int i;
 
+	/* Turn off the printer multiplexer on the 8012. */
+	for (i = 0; i < 8; i++)
+		outb(mux_8012[i], ioaddr + PAR_DATA);
 	write_reg_high(ioaddr, CMR1, CMR1h_RESET);
 	
     for (i = 0; i < 6; i++)
@@ -418,11 +512,18 @@
 	struct net_local *lp = (struct net_local *)dev->priv;
 	int ioaddr = dev->base_addr;
 
-	if (dev->tbusy) {
-		/* If we get here, some higher level has decided we are broken.
-		   There should really be a "kick me" function call instead. */
-		int tickssofar = jiffies - dev->trans_start;
-		if (tickssofar < 5)
+#ifndef final_version
+	if (skb == NULL || skb->len <= 0) {
+		printk("%s: Obsolete driver layer request made: skbuff==NULL.\n",
+			   dev->name);
+		dev_tint(dev);
+		return 0;
+	}
+#endif
+
+	/* Use transmit-while-tbusy as a crude error timer. */
+	if (set_bit(0, (void*)&dev->tbusy) != 0) {
+		if (jiffies - dev->trans_start < TX_TIMEOUT)
 			return 1;
 		printk("%s: transmit timed out, %s?\n", dev->name,
 			   inb(ioaddr + PAR_CONTROL) & 0x10 ? "network cable problem"
@@ -432,21 +533,8 @@
 		hardware_init(dev);
 		dev->tbusy=0;
 		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. */
-	if (set_bit(0, (void*)&dev->tbusy) != 0)
-		printk("%s: Transmitter access conflict.\n", dev->name);
-	else {
+		return 1;
+	} else {
 		short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
 		unsigned char *buf = skb->data;
 		int flags;
@@ -484,9 +572,13 @@
 /* The typical workload of the driver:
    Handle the network interface interrupts. */
 static void
-net_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+net_interrupt IRQ(int irq, void *dev_instance, struct pt_regs * regs)
 {
+#ifdef SA_SHIRQ
+	struct device *dev = (struct device *)dev_instance;
+#else
 	struct device *dev = (struct device *)(irq2dev_map[irq]);
+#endif
 	struct net_local *lp;
 	int ioaddr, status, boguscount = 20;
 	static int num_tx_since_rx = 0;
@@ -585,11 +677,6 @@
 		int i;
 		for (i = 0; i < 6; i++)
 			write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]);
-#ifdef TIMED_CHECKER
-		del_timer(&atp_timer);
-		atp_timer.expires = jiffies + TIMED_CHECKER;
-		add_timer(&atp_timer);
-#endif
 	}
 
 	/* Tell the adapter that it can go back to using the output line as IRQ. */
@@ -610,17 +697,23 @@
 #ifdef TIMED_CHECKER
 /* This following code fixes a rare (and very difficult to track down)
    problem where the adapter forgets its ethernet address. */
-static void atp_timed_checker(unsigned long ignored)
+static void atp_timed_checker(unsigned long data)
 {
-  int i;
-  int ioaddr = atp_timed_dev->base_addr;
+	struct device *dev = (struct device *)data;
+	int ioaddr = dev->base_addr;
+	struct net_local *lp = (struct net_local *)dev->priv;
+	int tickssofar = jiffies - lp->last_rx_time;
+	int i;
 
-  if (!atp_timed_dev->interrupt)
-	{
-	  for (i = 0; i < 6; i++)
-#if 0
-		if (read_cmd_byte(ioaddr, PAR0 + i) != atp_timed_dev->dev_addr[i])
-		  {
+	if (tickssofar > 2*HZ  &&  dev->interrupt == 0) {
+#if 1
+		for (i = 0; i < 6; i++)
+			write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]);
+		lp->last_rx_time = jiffies;
+#else
+		for (i = 0; i < 6; i++)
+			if (read_cmd_byte(ioaddr, PAR0 + i) != atp_timed_dev->dev_addr[i])
+				{
 			struct net_local *lp = (struct net_local *)atp_timed_dev->priv;
 			write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]);
 			if (i == 2)
@@ -632,13 +725,10 @@
 			else
 			  lp->stats.rx_errors++;
 		  }
-#else
-	  write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]);
 #endif
 	}
-  del_timer(&atp_timer);
-  atp_timer.expires = jiffies + TIMED_CHECKER;
-  add_timer(&atp_timer);
+	lp->timer.expires = RUN_AT(TIMED_CHECKER);
+	add_timer(&lp->timer);
 }
 #endif
 
@@ -647,11 +737,7 @@
 {
 	struct net_local *lp = (struct net_local *)dev->priv;
 	int ioaddr = dev->base_addr;
-#ifdef notdef
-	ushort header[4];
-#else
 	struct rx_header rx_head;
-#endif
 
 	/* Process the received packet. */
 	outb(EOC+MAR, ioaddr + PAR_DATA);
@@ -661,18 +747,23 @@
 			   rx_head.rx_count, rx_head.rx_status, rx_head.cur_addr);
 	if ((rx_head.rx_status & 0x77) != 0x01) {
 		lp->stats.rx_errors++;
-		/* Ackkk!  I don't have any documentation on what the error bits mean!
-		   The best I can do is slap the device around a bit. */
+		if (rx_head.rx_status & 0x0004) lp->stats.rx_frame_errors++;
+		else if (rx_head.rx_status & 0x0002) lp->stats.rx_crc_errors++;
 		if (net_debug > 3) printk("%s: Unknown ATP Rx error %04x.\n",
 								  dev->name, rx_head.rx_status);
-		hardware_init(dev);
+		if  (rx_head.rx_status & 0x0020) {
+			lp->stats.rx_fifo_errors++;
+			write_reg_high(ioaddr, CMR1, CMR1h_TxENABLE);
+			write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE);
+		} else if (rx_head.rx_status & 0x0050)
+			hardware_init(dev);
 		return;
 	} else {
-		/* Malloc up new buffer. */
-		int pkt_len = (rx_head.rx_count & 0x7ff) - 4; 		/* The "-4" is omits the FCS (CRC). */
+		/* Malloc up new buffer. The "-4" is omits the FCS (CRC). */
+		int pkt_len = (rx_head.rx_count & 0x7ff) - 4;
 		struct sk_buff *skb;
 		
-		skb = dev_alloc_skb(pkt_len);
+		skb = DEV_ALLOC_SKB(pkt_len + 2);
 		if (skb == NULL) {
 			printk("%s: Memory squeeze, dropping packet.\n", dev->name);
 			lp->stats.rx_dropped++;
@@ -680,23 +771,20 @@
 		}
 		skb->dev = dev;
 		
+#if LINUX_VERSION_CODE >= 0x10300
+		skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
 		read_block(ioaddr, pkt_len, skb_put(skb,pkt_len), dev->if_port);
-
-		if (net_debug > 6) {
-			unsigned char *data = skb->data;
-			printk(" data %02x%02x%02x %02x%02x%02x %02x%02x%02x"
-				   "%02x%02x%02x %02x%02x..",
-				   data[0], data[1], data[2], data[3], data[4], data[5],
-				   data[6], data[7], data[8], data[9], data[10], data[11],
-				   data[12], data[13]);
-		}
-		
-		skb->protocol=eth_type_trans(skb,dev);
+		skb->protocol = eth_type_trans(skb, dev);
+#else
+		read_block(ioaddr, pkt_len, skb->data, dev->if_port);
+		skb->len = pkt_len;
+#endif
 		netif_rx(skb);
 		lp->stats.rx_packets++;
 	}
  done:
 	write_reg(ioaddr, CMR1, CMR1_NextPkt);
+	lp->last_rx_time = jiffies;
 	return;
 }
 
@@ -730,17 +818,23 @@
 	dev->tbusy = 1;
 	dev->start = 0;
 
+	del_timer(&lp->timer);
+
 	/* Flush the Tx and disable Rx here. */
 	lp->addr_mode = CMR2h_OFF;
 	write_reg_high(ioaddr, CMR2, CMR2h_OFF);
 
 	/* Free the IRQ line. */
 	outb(0x00, ioaddr + PAR_CONTROL);
-	free_irq(dev->irq, NULL);
+	FREE_IRQ(dev->irq, dev);
+#ifndef SA_SHIRQ
 	irq2dev_map[dev->irq] = 0;
+#endif
+
+	/* Reset the ethernet hardware and activate the printer pass-through. */
+    write_reg_high(ioaddr, CMR1, CMR1h_RESET | CMR1h_MUX);
 
-	/* Leave the hardware in a reset state. */
-    write_reg_high(ioaddr, CMR1, CMR1h_RESET);
+	MOD_DEC_USE_COUNT;
 
 	return 0;
 }
@@ -757,29 +851,125 @@
 /*
  *	Set or clear the multicast filter for this adapter.
  */
- 
-static void set_multicast_list(struct device *dev)
+
+/* The little-endian AUTODIN32 ethernet CRC calculation.
+   This is common code and should be moved to net/core/crc.c */
+static unsigned const ethernet_polynomial_le = 0xedb88320U;
+static inline unsigned ether_crc_le(int length, unsigned char *data)
+{
+    unsigned int crc = 0xffffffff;	/* Initial value. */
+    while(--length >= 0) {
+		unsigned char current_octet = *data++;
+		int bit;
+		for (bit = 8; --bit >= 0; current_octet >>= 1) {
+			if ((crc ^ current_octet) & 1) {
+				crc >>= 1;
+				crc ^= ethernet_polynomial_le;
+			} else
+				crc >>= 1;
+		}
+    }
+    return crc;
+}
+
+static void
+#ifdef NEW_MULTICAST
+set_rx_mode_8002(struct device *dev)
+#else
+static void set_rx_mode_8002(struct device *dev, int num_addrs, void *addrs);
+#endif
 {
 	struct net_local *lp = (struct net_local *)dev->priv;
 	short ioaddr = dev->base_addr;
-	int num_addrs=dev->mc_list;
-	
-	if(dev->flags&(IFF_ALLMULTI|IFF_PROMISC))
-		num_addrs=1;
-	/*
-	 *	We must make the kernel realise we had to move
-	 *	into promisc mode or we start all out war on
-	 *	the cable. - AC
-	 */
-	if(num_addrs)
-		dev->flags|=IFF_PROMISC;		
-	lp->addr_mode = num_addrs ? CMR2h_PROMISC : CMR2h_Normal;
+
+	if ( dev->mc_count > 0 || (dev->flags & (IFF_ALLMULTI|IFF_PROMISC))) {
+		/* We must make the kernel realise we had to move
+		 *	into promisc mode or we start all out war on
+		 *	the cable. - AC
+		 */
+		dev->flags|=IFF_PROMISC;
+		lp->addr_mode = CMR2h_PROMISC;
+	} else
+		lp->addr_mode = CMR2h_Normal;
 	write_reg_high(ioaddr, CMR2, lp->addr_mode);
 }
+
+static void
+#ifdef NEW_MULTICAST
+set_rx_mode_8012(struct device *dev)
+#else
+static void set_rx_mode_8012(struct device *dev, int num_addrs, void *addrs);
+#endif
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+	short ioaddr = dev->base_addr;
+	unsigned char new_mode, mc_filter[8]; /* Multicast hash filter */
+	int i;
+
+	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
+		new_mode = CMR2h_PROMISC;
+	} else if ((dev->mc_count > 1000)  ||  (dev->flags & IFF_ALLMULTI)) {
+		/* Too many to filter perfectly -- accept all multicasts. */
+		memset(mc_filter, 0xff, sizeof(mc_filter));
+		new_mode = CMR2h_Normal;
+	} else {
+		struct dev_mc_list *mclist;
+
+		memset(mc_filter, 0, sizeof(mc_filter));
+		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);
+		new_mode = CMR2h_Normal;
+	}
+	lp->addr_mode = new_mode;
+    write_reg(ioaddr, CMR2, CMR2_IRQOUT | 0x04); /* Switch to page 1. */
+    for (i = 0; i < 8; i++)
+		write_reg_byte(ioaddr, i, mc_filter[i]);
+	if (net_debug > 2 || 1) {
+		lp->addr_mode = 1;
+		printk("%s: Mode %d, setting multicast filter to",
+			   dev->name, lp->addr_mode);
+		for (i = 0; i < 8; i++)
+			printk(" %2.2x", mc_filter[i]);
+		printk(".\n");
+	}
+
+	write_reg_high(ioaddr, CMR2, lp->addr_mode);
+    write_reg(ioaddr, CMR2, CMR2_IRQOUT); /* Switch back to page 0 */
+}
+
+#ifdef MODULE
+static int debug = 1;
+int
+init_module(void)
+{
+	net_debug = debug;
+	root_atp_dev = NULL;
+	atp_init(0);
+	return 0;
+}
+
+void
+cleanup_module(void)
+{
+	struct device *next_dev;
+
+	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+	/* No need to release_region(), since we never snarf it. */
+	while (root_atp_dev) {
+		next_dev = ((struct net_local *)root_atp_dev->priv)->next_module;
+		unregister_netdev(root_atp_dev);
+		kfree(root_atp_dev);
+		root_atp_dev = next_dev;
+	}
+}
+#endif /* MODULE */
+
 
 /*
  * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c atp.c"
+ *  compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c atp.c"
  *  version-control: t
  *  kept-new-versions: 5
  *  tab-width: 4
diff -u --recursive --new-file v2.0.30/linux/drivers/net/atp.h linux/drivers/net/atp.h
--- v2.0.30/linux/drivers/net/atp.h	Tue Oct 10 06:03:24 1995
+++ linux/drivers/net/atp.h	Tue Aug 12 13:21:12 1997
@@ -1,3 +1,6 @@
+/* Linux header file for the ATP pocket ethernet adapter. */
+/* v1.04 4/1/97 becker@cesdis.gsfc.nasa.gov. */
+
 #include <linux/if_ether.h>
 #include <linux/types.h>
 #include <asm/io.h>
@@ -6,16 +9,19 @@
 #ifdef __KERNEL__
     struct enet_statistics stats;
 #endif
+    struct device *next_module;
+    struct timer_list timer;	/* Media selection timer. */
+    long last_rx_time;		/* Last Rx, in jiffies, to handle Rx hang. */
     ushort saved_tx_size;
-    unsigned char
-	re_tx,			/* Number of packet retransmissions. */
-	tx_unit_busy,
-	addr_mode,		/* Current Rx filter e.g. promiscuous, etc. */
-	pac_cnt_in_tx_buf;
+    unsigned tx_unit_busy:1;
+    unsigned char re_tx,	/* Number of packet retransmissions. */
+      addr_mode,		/* Current Rx filter e.g. promiscuous, etc. */
+      pac_cnt_in_tx_buf,
+      chip_type;
 };
 
 struct rx_header {
-    ushort pad;			/* The first read is always corrupted. */
+    ushort pad;			/* Pad. */
     ushort rx_count;
     ushort rx_status;		/* Unknown bit assignments :-<.  */
     ushort cur_addr;		/* Apparently the current buffer address(?) */
@@ -25,6 +31,8 @@
 #define PAR_STATUS	1
 #define PAR_CONTROL 2
 
+enum chip_type { RTL8002, RTL8012 };
+
 #define Ctrl_LNibRead	0x08	/* LP_PSELECP */
 #define Ctrl_HNibRead	0
 #define Ctrl_LNibWrite	0x08	/* LP_PSELECP */
@@ -47,7 +55,8 @@
     ISR = 10, IMR = 11,			/* Interrupt status and mask. */
     CMR1 = 12,				/* Command register 1. */
     CMR2 = 13,				/* Command register 2. */
-    MAR = 14,				/* Memory address register. */
+    MODSEL = 14,			/* Mode select register. */
+    MAR = 14,				/* Memory address register (?). */
     CMR2_h = 0x1d, };
 
 enum eepage_regs
@@ -59,6 +68,7 @@
 #define ISR_TxErr	0x02
 #define ISRh_RxErr	0x11	/* ISR, high nibble */
 
+#define CMR1h_MUX	0x08	/* Select printer multiplexor on 8012. */
 #define CMR1h_RESET	0x04	/* Reset. */
 #define CMR1h_RxENABLE	0x02	/* Rx unit enable.  */
 #define CMR1h_TxENABLE	0x01	/* Tx unit enable.  */
diff -u --recursive --new-file v2.0.30/linux/drivers/net/auto_irq.c linux/drivers/net/auto_irq.c
--- v2.0.30/linux/drivers/net/auto_irq.c	Thu Feb 29 21:50:43 1996
+++ linux/drivers/net/auto_irq.c	Mon Aug  4 12:10:18 1997
@@ -54,17 +54,23 @@
 {
 	irq_number = irq;
 	set_bit(irq, (void *)&irq_bitmap);	/* irq_bitmap |= 1 << irq; */
-	disable_irq(irq);
+	/* This code used to disable the irq. However, the interrupt stub
+	 * would then re-enable the interrupt with (potentially) disastrous
+	 * consequences
+	 */
+	free_irq(irq, dev_id);
 	return;
 }
 
 int autoirq_setup(int waittime)
 {
-	int i, mask;
+	int i;
 	int timeout = jiffies + waittime;
 	int boguscount = (waittime*loops_per_sec) / 100;
 
 	irq_handled = 0;
+	irq_bitmap = 0;
+
 	for (i = 0; i < 16; i++) {
 		if (test_bit(i, &irqs_busy) == 0
 			&& request_irq(i, autoirq_probe, SA_INTERRUPT, "irq probe", NULL) == 0)
@@ -72,22 +78,15 @@
 	}
 	/* Update our USED lists. */
 	irqs_used |= ~irq_handled;
-	irq_number = 0;
-	irq_bitmap = 0;
 
 	/* Hang out at least <waittime> jiffies waiting for bogus IRQ hits. */
 	while (timeout > jiffies  &&  --boguscount > 0)
 		;
 
-	for (i = 0, mask = 0x01; i < 16; i++, mask <<= 1) {
-		if (irq_bitmap & irq_handled & mask) {
-			irq_handled &= ~mask;
-#ifdef notdef
-			printk(" Spurious interrupt on IRQ %d\n", i);
-#endif
-			free_irq(i, NULL);
-		}
-	}
+	irq_handled &= ~irq_bitmap;
+
+	irq_number = 0;	/* We are interested in new interrupts from now on */
+
 	return irq_handled;
 }
 
@@ -102,6 +101,8 @@
 	while (timeout > jiffies  &&  --boguscount > 0)
 		if (irq_number)
 			break;
+
+	irq_handled &= ~irq_bitmap;	/* This eliminates the already reset handlers */
 
 	/* Retract the irq handlers that we installed. */
 	for (i = 0; i < 16; i++) {
diff -u --recursive --new-file v2.0.30/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
--- v2.0.30/linux/drivers/net/de4x5.c	Sat Nov  9 09:31:38 1996
+++ linux/drivers/net/de4x5.c	Tue Aug 12 13:21:12 1997
@@ -1,12 +1,35 @@
-/*  de4x5.c: A DIGITAL DE425/DE434/DE435/DE450/DE500 ethernet driver for Linux.
+/*  de4x5.c: A DIGITAL DC21x4x DECchip and DE425/DE434/DE435/DE450/DE500
+             ethernet driver for Linux.
 
     Copyright 1994, 1995 Digital Equipment Corporation.
 
-    This software may be used and distributed according to the terms of
-    the GNU Public License, incorporated herein by reference.
+    Testing resources for this driver have been made available
+    in part by NASA Ames Research Center (mjacob@nas.nasa.gov).
 
-    This driver is written for the Digital Equipment Corporation series
-    of EtherWORKS ethernet cards:
+    The author may be reached at davies@maniac.ultranet.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 of the  License, or (at your
+    option) any later version.
+
+    THIS  SOFTWARE  IS PROVIDED   ``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  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.
+
+    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.
+
+    Originally,   this  driver  was    written  for the  Digital   Equipment
+    Corporation series of EtherWORKS ethernet cards:
 
         DE425 TP/COAX EISA
 	DE434 TP PCI
@@ -14,6 +37,25 @@
 	DE450 TP/COAX/AUI PCI
 	DE500 10/100 PCI Fasternet
 
+    but it  will  now attempt  to  support all  cards which   conform to the
+    Digital Semiconductor   SROM   Specification.    The  driver   currently
+    recognises the following chips:
+
+        DC21040     (no SROM)
+	DC21041[A]
+	DC21140[A]
+
+    I plan to  add DC2114[23]  support ASAP,  time permitting.   So far  the
+    driver is known to work with the following cards:
+
+        KINGSTON
+	Linksys
+	ZNYX342
+	SMC8432
+	SMC9332 (w/new SROM)
+	ZNYX31[45]
+	ZNYX346 10/100 4 port (can act as a 10/100 bridge!) 
+
     The driver has been tested on a relatively busy network using the DE425,
     DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred
     16M of data to a DECstation 5000/200 as follows:
@@ -29,12 +71,12 @@
     measurement. Their error is +/-20k on a quiet (private) network and also
     depend on what load the CPU has.
 
-    The author may be reached at davies@maniac.ultranet.com.
-
     =========================================================================
-    This driver has been written  substantially  from scratch, although  its
+    This driver  has been written substantially  from  scratch, although its
     inheritance of style and stack interface from 'ewrk3.c' and in turn from
-    Donald Becker's 'lance.c' should be obvious.
+    Donald Becker's 'lance.c' should be obvious. With the module autoload of
+    every  usable DECchip board,  I  pinched Donald's 'next_module' field to
+    link my modules together.
 
     Upto 15 EISA cards can be supported under this driver, limited primarily
     by the available IRQ lines.  I have  checked different configurations of
@@ -46,43 +88,37 @@
     to the differences in the EISA and PCI CSR address offsets from the base
     address.
 
-    The ability to load  this driver as a  loadable module has been included
-    and used  extensively during the  driver development (to save those long
+    The ability to load this  driver as a loadable  module has been included
+    and used extensively  during the driver development  (to save those long
     reboot sequences).  Loadable module support  under PCI and EISA has been
     achieved by letting the driver autoprobe as if it were compiled into the
-    kernel, except that there is no autoprobing of the IRQ lines. This is of
-    no great  consequence except do make sure  you're not sharing interrupts
-    with  anything that cannot  accommodate  interrupt sharing! The existing
-    register_netdevice() code will only allow one device to be registered at
-    a time. 
-
-    ************************************************************************
-    For now, please only use the 'io=??' assignment (see  2. below, ?? != 0)
-    when loading a module.
-    ************************************************************************
-
-    Essentially, the I/O address and IRQ information  are ignored and filled
-    in later by  the PCI BIOS   during the PCI  probe.  Note  that the board
-    should be in the system at boot time so that its I/O address and IRQ are
-    allocated by the PCI BIOS automatically. 
+    kernel. Do make sure  you're not sharing  interrupts with anything  that
+    cannot accommodate  interrupt  sharing!
 
     To utilise this ability, you have to do 8 things:
 
     0) have a copy of the loadable modules code installed on your system.
     1) copy de4x5.c from the  /linux/drivers/net directory to your favourite
     temporary directory.
-    2) edit the source code near line 3779 to reflect the I/O address you're
-    using (only  if you want to manually  load the module),  or assign these
-    when loading by:
+    2) for fixed  autoprobes (not  recommended),  edit the source code  near
+    line 5005 to reflect the I/O address  you're using, or assign these when
+    loading by:
 
-                   insmod de4x5.o io=0xghh         where g = bus number
+                   insmod de4x5 io=0xghh           where g = bus number
 		                                        hh = device number   
 
+       NB: autoprobing for modules is now supported by default. You may just
+           use:
+
+                   insmod de4x5
+
+           to load all available boards. For a specific board, still use
+	   the 'io=?' above.
     3) compile  de4x5.c, but include -DMODULE in  the command line to ensure
     that the correct bits are compiled (see end of source code).
     4) if you are wanting to add a new  card, goto 5. Otherwise, recompile a
     kernel with the de4x5 configuration turned off and reboot.
-    5) insmod de4x5.o [io=0xghh]
+    5) insmod de4x5 [io=0xghh]
     6) run the net startup bits for your new eth?? interface(s) manually 
     (usually /etc/rc.inet[12] at boot time). 
     7) enjoy!
@@ -98,13 +134,7 @@
     By  default,  the driver will  now   autodetect any  DECchip based card.
     Should you have a need to restrict the driver to DIGITAL only cards, you
     can compile with a  DEC_ONLY define, or if  loading as a module, use the
-    'dec_only=1'  parameter. However, this  "feature" is in no way supported
-    nor  tested in this  driver  and the user  may use  it  at his/her  sole
-    discretion.   I have had  2 conflicting reports that  my  driver will or
-    won't work  with Znyx.   Try  Donald Becker's 'tulip.c' if   this driver
-    doesn't work for you. I will not be supporting Znyx  and SMC cards since
-    I have  no information on  them and  can't test  them in  a system (this
-    applies most particularly to the DC21140 based cards).
+    'dec_only=1'  parameter. 
 
     I've changed the timing routines to  use the kernel timer and scheduling
     functions  so that the  hangs  and other assorted problems that occurred
@@ -125,6 +155,30 @@
     aligned  DMA transfers  and  the  Alphas get   alignment traps with  non
     longword aligned data copies (which makes them really slow). No comment.
 
+    I  have added SROM decoding  routines to make this  driver work with any
+    card that  supports the Digital  Semiconductor SROM spec. This will help
+    all  cards running the dc2114x  series chips in particular.  Cards using
+    the dc2104x  chips should run correctly with  the basic  driver.  I'm in
+    debt to <mjacob@feral.com> for the  testing and feedback that helped get
+    this feature working.  So far we have  tested KINGSTON, SMC8432, SMC9332
+    (with the latest SROM complying  with the SROM spec  V3: their first was
+    broken), ZNYX342  and  LinkSys. ZYNX314 (dual  21041  MAC) and  ZNYX 315
+    (quad 21041 MAC)  cards also  appear  to work despite their  incorrectly
+    wired IRQs.
+
+    I have added a temporary fix for interrupt problems when some SCSI cards
+    share the same interrupt as the DECchip based  cards. The problem occurs
+    because  the SCSI card wants to  grab the interrupt  as a fast interrupt
+    (runs the   service routine with interrupts turned   off) vs.  this card
+    which really needs to run the service routine with interrupts turned on.
+    This driver will  now   add the interrupt service   routine  as  a  fast
+    interrupt if it   is bounced from the   slow interrupt.  THIS IS NOT   A
+    RECOMMENDED WAY TO RUN THE DRIVER  and has been done  for a limited time
+    until  people   sort  out their  compatibility    issues and the  kernel
+    interrupt  service code  is  fixed.   YOU  SHOULD SEPARATE OUT  THE FAST
+    INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not
+    run on the same interrupt. PCMCIA/CardBus is another can of worms...
+
     TO DO:
     ------
 
@@ -208,16 +262,49 @@
                           with <csd@microplex.com>
       0.44   13-Aug-96    Fix RX overflow bug in 2114[023] chips.
                           Fix EISA probe bugs reported by <os2@kpi.kharkov.ua>
-			  and <michael@compurex.com>
+			  and <michael@compurex.com>.
       0.441   9-Sep-96    Change dc21041_autoconf() to probe quiet BNC media
                            with a loopback packet.
       0.442   9-Sep-96    Include AUI in dc21041 media printout. Bug reported
                            by <bhat@mundook.cs.mu.OZ.AU>
+      0.45    8-Dec-96    Include endian functions for PPC use, from work 
+                           by <cort@cs.nmt.edu> and <g.thomas@opengroup.org>.
+      0.451  28-Dec-96    Added fix to allow autoprobe for modules after
+                           suggestion from <mjacob@feral.com>.
+      0.5    30-Jan-97    Added SROM decoding functions.
+                          Updated debug flags.
+			  Fix sleep/wakeup calls for PCI cards, bug reported
+			   by <cross@gweep.lkg.dec.com>.
+			  Added multi-MAC, one SROM feature from discussion
+			   with <mjacob@feral.com>.
+			  Added full module autoprobe capability.
+			  Added attempt to use an SMC9332 with broken SROM.
+			  Added fix for ZYNX multi-mac cards that didn't
+			   get their IRQs wired correctly.
+      0.51   13-Feb-97    Added endian fixes for the SROM accesses from
+			   <paubert@iram.es>
+			  Fix init_connection() to remove extra device reset.
+			  Fix MAC/PHY reset ordering in dc21140m_autoconf().
+			  Fix initialisation problem with lp->timeout in
+			   typeX_infoblock() from <paubert@iram.es>.
+			  Fix MII PHY reset problem from work done by
+			   <paubert@iram.es>.
+      0.52   26-Apr-97    Some changes may not credit the right people -
+                           a disk crash meant I lost some mail.
+			  Change RX interrupt routine to drop rather than 
+			   defer packets to avoid hang reported by 
+			   <g.thomas@opengroup.org>.
+			  Fix srom_exec() to return for COMPACT and type 1
+			   infoblocks.
+			  Added DC21142 and DC21143 functions.
+			  Added byte counters from <phil@tazenda.demon.co.uk>
+			  Added SA_INTERRUPT temporary fix from 
+			   <mjacob@feral.com>.
 
     =========================================================================
 */
 
-static const char *version = "de4x5.c:v0.442 96/11/7 davies@maniac.ultranet.com\n";
+static const char *version = "de4x5.c:V0.52 1997/4/26 davies@maniac.ultranet.com\n";
 
 #include <linux/module.h>
 
@@ -235,7 +322,8 @@
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
-#include <asm/segment.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -250,14 +338,37 @@
 
 #define c_char const char
 
+#include <linux/version.h>
+#if	LINUX_VERSION_CODE < ((2 << 16) | (1 << 8))
+#define        net_device_stats      enet_statistics
+#define copy_to_user(a,b,c)   memcpy_tofs(a,b,c)
+#define copy_from_user(a,b,c) memcpy_fromfs(a,b,c)
+#define le16_to_cpu(a) cpu_to_le16(a) 
+#define le32_to_cpu(a) cpu_to_le32(a) 
+#ifdef __powerpc__
+#define cpu_to_le16(a) ((((a) & 0x00ffU) << 8) | (((a) & 0xff00U) >> 8))
+#define cpu_to_le32(a) ((((a) & 0x000000ffU) << 24) |\
+		        (((a) & 0x0000ff00U) <<  8) |\
+		        (((a) & 0x00ff0000U) >>  8) |\
+		        (((a) & 0xff000000U) >> 24))
+#else
+#define cpu_to_le16(a) (a)
+#define cpu_to_le32(a) (a)
+#endif  /* __powerpc__ */
+#include	<asm/segment.h>
+#else
+#include	<asm/uaccess.h>
+#endif  /* LINUX_VERSION_CODE */
+#define TWIDDLE(a) (u_short)le16_to_cpu(get_unaligned((u_short *)(a)))
+
 /*
 ** MII Information
 */
 struct phy_table {
-    int reset;              /* Hard reset required? */
-    int id;                 /* IEEE OUI */
+    int reset;              /* Hard reset required?                         */
+    int id;                 /* IEEE OUI                                     */
     int ta;                 /* One cycle TA time - 802.3u is confusing here */
-    struct {                /* Non autonegotiation (parallel) speed det. */
+    struct {                /* Non autonegotiation (parallel) speed det.    */
 	int reg;
 	int mask;
 	int value;
@@ -265,19 +376,35 @@
 };
 
 struct mii_phy {
-    int reset;              /* Hard reset required? */
-    int id;                 /* IEEE OUI */
-    int ta;                 /* One cycle TA time */
+    int reset;              /* Hard reset required?                      */
+    int id;                 /* IEEE OUI                                  */
+    int ta;                 /* One cycle TA time                         */
     struct {                /* Non autonegotiation (parallel) speed det. */
 	int reg;
 	int mask;
 	int value;
     } spd;
-    int addr;               /* MII address for the PHY */
+    int addr;               /* MII address for the PHY                   */
+    u_char  *gep;           /* Start of GEP sequence block in SROM       */
+    u_char  *rst;           /* Start of reset sequence in SROM           */
+    u_int mc;               /* Media Capabilities                        */
+    u_int ana;              /* NWay Advertisement                        */
+    u_int fdx;              /* Full DupleX capabilites for each media    */
+    u_int ttm;              /* Transmit Threshold Mode for each media    */
 };
 
 #define DE4X5_MAX_PHY 8     /* Allow upto 8 attached PHY devices per board */
 
+struct sia_phy {
+    u_char mc;              /* Media Code                                */
+    u_char ext;             /* csr13-15 valid when set                   */
+    int csr13;              /* SIA Connectivity Register                 */
+    int csr14;              /* SIA TX/RX Register                        */
+    int csr15;              /* SIA General Register                      */
+    int gepc;               /* SIA GEP Control Information               */
+    int gep;                /* SIA GEP Data                              */
+};
+
 /*
 ** Define the know universe of PHY devices that can be
 ** recognised by this driver
@@ -285,8 +412,8 @@
 static struct phy_table phy_info[] = {
     {0, NATIONAL_TX, 1, {0x19, 0x40, 0x00}},   /* National TX */
     {1, BROADCOM_T4, 1, {0x10, 0x02, 0x02}},   /* Broadcom T4 */
-    {0, SEEQ_T4    , 1, {0x12, 0x10, 0x10}},   /* SEEQ T4 */
-    {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}}    /* Cypress T4 */
+    {0, SEEQ_T4    , 1, {0x12, 0x10, 0x10}},   /* SEEQ T4     */
+    {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}}    /* Cypress T4  */
 };
 
 /*
@@ -300,11 +427,23 @@
 #define SMC    1
 #define ACCTON 2
 
+/*
+** SROM Repair definitions. If a broken SROM is detected a card may
+** use this information to help figure out what to do. This is a
+** "stab in the dark" and so far for SMC9332's only.
+*/
+static c_char srom_repair_info[][100] = {
+    {0x00,0x1e,0x00,0x00,0x00,0x08,             /* SMC9332 */
+     0x1f,0x01,0x8f,0x01,0x00,0x01,0x00,0x02,
+     0x01,0x00,0x00,0x78,0xe0,0x01,0x00,0x50,
+     0x00,0x18,}
+};
+
 
 #ifdef DE4X5_DEBUG
 static int de4x5_debug = DE4X5_DEBUG;
 #else
-static int de4x5_debug = 1;
+static int de4x5_debug = (0);
 #endif
 
 #ifdef DE4X5_AUTOSENSE              /* Should be done on a per adapter basis */
@@ -449,7 +588,7 @@
     char id_block_crc;
     char reserved2;
     char version;
-    char num_adapters;
+    char num_controllers;
     char ieee_addr[6];
     char info[100];
     short chksum;
@@ -494,7 +633,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 enet_statistics stats;           /* Public stats                 */
+    struct net_device_stats stats;           /* Public stats                 */
     struct {
 	u_int bins[DE4X5_PKT_STAT_SZ];      /* Private stats counters       */
 	u_int unicast;
@@ -512,19 +651,21 @@
     char txRingSize;
     int  bus;                               /* EISA or PCI                  */
     int  bus_num;                           /* PCI Bus number               */
+    int  device;                            /* Device number on PCI bus     */
     int  state;                             /* Adapter OPENED or CLOSED     */
     int  chipset;                           /* DC21040, DC21041 or DC21140  */
     s32  irq_mask;                          /* Interrupt Mask (Enable) bits */
     s32  irq_en;                            /* Summary interrupt bits       */
     int  media;                             /* Media (eg TP), mode (eg 100B)*/
     int  c_media;                           /* Remember the last media conn */
+    int  fdx;                               /* media full duplex flag       */
     int  linkOK;                            /* Link is OK                   */
     int  autosense;                         /* Allow/disallow autosensing   */
     int  tx_enable;                         /* Enable descriptor polling    */
-    int  lostMedia;                         /* Possibly lost media          */
     int  setup_f;                           /* Setup frame filtering type   */
     int  local_state;                       /* State within a 'media' state */
     struct mii_phy phy[DE4X5_MAX_PHY];      /* List of attached PHY devices */
+    struct sia_phy sia;                     /* SIA PHY Information          */
     int  active;                            /* Index to active PHY device   */
     int  mii_cnt;                           /* Number of attached PHY's     */
     int  timeout;                           /* Scheduling counter           */
@@ -537,13 +678,31 @@
 	s32 csr0;                           /* Saved Bus Mode Register      */
 	s32 csr6;                           /* Saved Operating Mode Reg.    */
 	s32 csr7;                           /* Saved IRQ Mask Register      */
+	s32 gep;                            /* Saved General Purpose Reg.   */
+	s32 gepc;                           /* Control info for GEP         */
 	s32 csr13;                          /* Saved SIA Connectivity Reg.  */
 	s32 csr14;                          /* Saved SIA TX/RX Register     */
 	s32 csr15;                          /* Saved SIA General Register   */
 	int save_cnt;                       /* Flag if state already saved  */
 	struct sk_buff *skb;                /* Save the (re-ordered) skb's  */
     } cache;
+    struct de4x5_srom srom;                 /* A copy of the SROM           */
+    struct device *next_module;             /* Link to the next module      */
     int rx_ovf;                             /* Check for 'RX overflow' tag  */
+    int useSROM;                            /* For non-DEC card use SROM    */
+    int useMII;                             /* Infoblock using the MII      */
+    int asBitValid;                         /* Autosense bits in GEP?       */
+    int asPolarity;                         /* 0 => asserted high           */
+    int asBit;                              /* Autosense bit number in GEP  */
+    int defMedium;                          /* SROM default medium          */
+    int tcount;                             /* Last infoblock number        */
+    int infoblock_init;                     /* Initialised this infoblock?  */
+    int infoleaf_offset;                    /* SROM infoleaf for controller */
+    s32 infoblock_csr6;                     /* csr6 value in SROM infoblock */
+    int infoblock_media;                    /* infoblock media              */
+    int (*infoleaf_fn)(struct device *);    /* Pointer to infoleaf function */
+    u_char *rst;                            /* Pointer to Type 5 reset info */
+    u_char  ibn;                            /* Infoblock number             */
 };
 
 /*
@@ -558,9 +717,29 @@
     int chipset;
     struct de4x5_srom srom;
     int autosense;
+    int useSROM;
 } bus;
 
 /*
+** To get around certain poxy cards that don't provide an SROM
+** for the second and more DECchip, I have to key off the first
+** chip's address. I'll assume there's not a bad SROM iff:
+**
+**      o the chipset is the same
+**      o the bus number is the same and > 0
+**      o the sum of all the returned hw address bytes is 0 or 0x5fa
+**
+** Also have to save the irq for those cards whose hardware designers
+** can't follow the PCI to PCI Bridge Architecture spec.
+*/
+static struct {
+    int chipset;
+    int bus;
+    int irq;
+    u_char addr[ETH_ALEN];
+} last = {0,};
+
+/*
 ** The transmit ring full condition is described by the tx_old and tx_new
 ** pointers by:
 **    tx_old            = tx_new    Empty ring
@@ -580,7 +759,7 @@
 static int     de4x5_queue_pkt(struct sk_buff *skb, struct device *dev);
 static void    de4x5_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 static int     de4x5_close(struct device *dev);
-static struct  enet_statistics *de4x5_get_stats(struct device *dev);
+static struct  net_device_stats *de4x5_get_stats(struct device *dev);
 static void    de4x5_local_stats(struct device *dev, char *buf, int pkt_len);
 static void    set_multicast_list(struct device *dev);
 static int     de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd);
@@ -605,6 +784,7 @@
 static int     dc21040_autoconf(struct device *dev);
 static int     dc21041_autoconf(struct device *dev);
 static int     dc21140m_autoconf(struct device *dev);
+static int     srom_autoconf(struct device *dev);
 static int     de4x5_suspect_state(struct device *dev, int timeout, int prev_state, int (*fn)(struct device *, int), int (*asfn)(struct device *));
 static int     dc21040_state(struct device *dev, int csr13, int csr14, int csr15, int timeout, int next_state, int suspect_state, int (*fn)(struct device *, int));
 static int     test_media(struct device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec);
@@ -642,6 +822,10 @@
 /*static void    srom_busy(u_int command, u_long address);*/
 static void    sendto_srom(u_int command, u_long addr);
 static int     getfrom_srom(u_long addr);
+static void    srom_map_media(struct device *dev);
+static int     srom_infoleaf_info(struct device *dev);
+static void    srom_init(struct device *dev);
+static void    srom_exec(struct device *dev, u_char *p);
 static int     mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr);
 static void    mii_wr(int data, u_char phyreg, u_char phyaddr, u_long ioaddr);
 static int     mii_rdata(u_long ioaddr);
@@ -655,6 +839,8 @@
 static int     mii_get_phy(struct device *dev);
 static void    SetMulticastFilter(struct device *dev);
 static int     get_hw_addr(struct device *dev);
+static void    srom_repair(struct device *dev, int card);
+static int     test_bad_enet(struct device *dev, int status);
 
 static void    eisa_probe(struct device *dev, u_long iobase);
 static void    pci_probe(struct device *dev, u_long iobase);
@@ -664,20 +850,33 @@
 static char    *build_setup_frame(struct device *dev, int mode);
 static void    disable_ast(struct device *dev);
 static void    enable_ast(struct device *dev, u32 time_out);
-static long    de4x5_switch_to_srl(struct device *dev);
-static long    de4x5_switch_to_mii(struct device *dev);
+static long    de4x5_switch_mac_port(struct device *dev);
 static void    timeout(struct device *dev, void (*fn)(u_long data), u_long data, u_long msec);
+static void    yawn(struct device *dev, int state);
 static int     de4x5_dev_index(char *s);
+static void    link_modules(struct device *dev, struct device *tmp);
 static void    de4x5_dbg_open(struct device *dev);
 static void    de4x5_dbg_mii(struct device *dev, int k);
 static void    de4x5_dbg_media(struct device *dev);
 static void    de4x5_dbg_srom(struct de4x5_srom *p);
 static void    de4x5_dbg_rx(struct sk_buff *skb, int len);
 static int     de4x5_strncmp(char *a, char *b, int n);
+static int     dc21041_infoleaf(struct device *dev);
+static int     dc21140_infoleaf(struct device *dev);
+static int     dc21142_infoleaf(struct device *dev);
+static int     dc21143_infoleaf(struct device *dev);
+static int     type0_infoblock(struct device *dev, u_char count, u_char *p);
+static int     type1_infoblock(struct device *dev, u_char count, u_char *p);
+static int     type2_infoblock(struct device *dev, u_char count, u_char *p);
+static int     type3_infoblock(struct device *dev, u_char count, u_char *p);
+static int     type4_infoblock(struct device *dev, u_char count, u_char *p);
+static int     type5_infoblock(struct device *dev, u_char count, u_char *p);
+static int     compact_infoblock(struct device *dev, u_char count, u_char *p);
 
 #ifdef MODULE
 int  init_module(void);
 void cleanup_module(void);
+static struct  device *unlink_modules(struct device *p);
 static int autoprobed = 0, loading_module = 1;
 # else
 static int autoprobed = 0, loading_module = 0;
@@ -686,7 +885,37 @@
 static char name[DE4X5_NAME_LENGTH + 1];
 static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST;
 static int num_de4x5s = 0, num_eth = 0;
-static int cfrv = 0;
+static int cfrv = 0, useSROM = 0;
+
+/*
+** List the SROM infoleaf functions and chipsets
+*/
+struct InfoLeaf {
+    int chipset;
+    int (*fn)(struct device *);
+};
+static struct InfoLeaf infoleaf_array[] = {
+    {DC21041, dc21041_infoleaf},
+    {DC21140, dc21140_infoleaf},
+    {DC21142, dc21142_infoleaf},
+    {DC21143, dc21143_infoleaf}
+};
+#define INFOLEAF_SIZE (sizeof(infoleaf_array)/(sizeof(int)+sizeof(int *)))
+
+/*
+** List the SROM info block functions
+*/
+static int (*dc_infoblock[])(struct device *dev, u_char, u_char *) = {
+    type0_infoblock,
+    type1_infoblock,
+    type2_infoblock,
+    type3_infoblock,
+    type4_infoblock,
+    type5_infoblock,
+    compact_infoblock
+};
+
+#define COMPACT (sizeof(dc_infoblock)/sizeof(int *) - 1)
 
 /*
 ** Miscellaneous defines...
@@ -703,6 +932,13 @@
     de4x5_ms_delay(1);\
 }
 
+#define PHY_HARD_RESET {\
+    outl(GEP_HRST, DE4X5_GEP);           /* Hard RESET the PHY dev. */\
+    udelay(1000);                        /* Assert for 1ms */\
+    outl(0x00, DE4X5_GEP);\
+    udelay(2000);                        /* Wait for 2ms */\
+}
+
 
 /*
 ** Autoprobing in modules is allowed here. See the top of the file for
@@ -712,17 +948,12 @@
 int
 de4x5_probe(struct device *dev)
 {
-    int tmp = num_de4x5s, status = -ENODEV;
+    int status = -ENODEV;
     u_long iobase = dev->base_addr;
 
     eisa_probe(dev, iobase);
     pci_probe(dev, iobase);
     
-    if ((tmp == num_de4x5s) && (iobase != 0) && loading_module) {
-	printk("%s: de4x5_probe() cannot find device at 0x%04lx.\n", dev->name, 
-	       iobase);
-    }
-
     /*
     ** Walk the device list to check that at least one device
     ** initialised OK
@@ -731,7 +962,7 @@
     
     if (dev->priv) status = 0;
     if (iobase == 0) autoprobed = 1;
-    
+
     return status;
 }
 
@@ -739,16 +970,18 @@
 de4x5_hw_init(struct device *dev, u_long iobase)
 {
     struct bus_type *lp = &bus;
-    int tmpbus, tmpchs, status=0;
-    int i, media = *((char *)&(lp->srom) + *((char *)&(lp->srom) + 19) * 3);
+    int i, status=0;
     char *tmp;
     
     /* Ensure we're not sleeping */
-    if (lp->chipset == DC21041) {
-	outl(0, PCI_CFDA);
-	de4x5_ms_delay(10);
+    if (lp->bus == EISA) {
+	outb(WAKEUP, PCI_CFPM);
+    } else {
+	pcibios_write_config_byte(lp->bus_num, lp->device << 3, 
+				  PCI_CFDA_PSM, WAKEUP);
     }
-    
+    de4x5_ms_delay(10);
+
     RESET_DE4X5;
     
     if ((inl(DE4X5_STS) & (STS_TS | STS_RS)) != 0) {
@@ -758,6 +991,7 @@
     /* 
     ** Now find out what kind of DC21040/DC21041/DC21140 board we have.
     */
+    useSROM = FALSE;
     if (lp->bus == PCI) {
 	PCI_signature(name, lp);
     } else {
@@ -784,9 +1018,6 @@
     }
     printk("%2.2x,\n", dev->dev_addr[i]);
     
-    tmpbus = lp->bus;
-    tmpchs = lp->chipset;
-    
     if (status != 0) {
 	printk("      which has an Ethernet PROM CRC error.\n");
 	return -ENXIO;
@@ -810,26 +1041,23 @@
 	dev->priv = (void *)(((u_long)dev->priv + ALIGN) & ~ALIGN);
 	lp = (struct de4x5_private *)dev->priv;
 	memset(dev->priv, 0, sizeof(struct de4x5_private));
-	lp->bus = tmpbus;
-	lp->chipset = tmpchs;
+	lp->bus = bus.bus;
+	lp->bus_num = bus.bus_num;
+	lp->device = bus.device;
+	lp->chipset = bus.chipset;
 	lp->cache.priv = tmp;
+	lp->cache.gepc = GEP_INIT;
+	lp->asBit = GEP_SLNK;
+	lp->asPolarity = GEP_SLNK;
+	lp->asBitValid = TRUE;
+	lp->timeout = -1;
+	lp->useSROM = useSROM;
+	memcpy((char *)&lp->srom,(char *)&bus.srom,sizeof(struct de4x5_srom));
 
 	/*
-	** Check for an MII interface
-	*/
-	if (media & MEDIA_MII) {                   /* MII interface? */
-	    if (!mii_get_phy(dev)) {
-		printk("%s: MII search failed, no device found when one was expected\n", dev->name);
-		return -ENXIO;
-	    }
-	} else {
-	    mii_get_phy(dev);                      /* Search the MII anyway! */
-	}
-	
-	/*
 	** Choose correct autosensing in case someone messed up
 	*/
-	if (de4x5_autosense & AUTO) {
+	if ((de4x5_autosense & AUTO) || lp->useSROM) {
 	    lp->autosense = AUTO;
 	} else {
 	    if (lp->chipset != DC21140) {
@@ -844,14 +1072,14 @@
 		lp->autosense = de4x5_autosense & 0x00c0;
 	    }
 	}
-	
+	lp->fdx = de4x5_full_duplex;
 	sprintf(lp->adapter_name,"%s (%s)", name, dev->name);
 	
 	/*
 	** Set up the RX descriptor ring (Intels)
 	** Allocate contiguous receive buffers, long word aligned (Alphas) 
 	*/
-#if !defined(__alpha__) && !defined(DE4X5_DO_MEMCPY)
+#if !defined(__alpha__) && !defined(__powerpc__) && !defined(DE4X5_DO_MEMCPY)
 	for (i=0; i<NUM_RX_DESC; i++) {
 	    lp->rx_ring[i].status = 0;
 	    lp->rx_ring[i].des1 = RX_BUFF_SZ;
@@ -871,8 +1099,8 @@
 	tmp = (char *)(((u_long) tmp + ALIGN) & ~ALIGN);
 	for (i=0; i<NUM_RX_DESC; i++) {
 	    lp->rx_ring[i].status = 0;
-	    lp->rx_ring[i].des1 = RX_BUFF_SZ;
-	    lp->rx_ring[i].buf = virt_to_bus(tmp + i * RX_BUFF_SZ);
+	    lp->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ);
+	    lp->rx_ring[i].buf = cpu_to_le32(virt_to_bus(tmp+i*RX_BUFF_SZ));
 	    lp->rx_ring[i].next = 0;
 	    lp->rx_skb[i] = (struct sk_buff *) 1;     /* Dummy entry */
 	}
@@ -888,8 +1116,8 @@
 	lp->txRingSize = NUM_TX_DESC;
 	    
 	/* Write the end of list marker to the descriptor lists */
-	lp->rx_ring[lp->rxRingSize - 1].des1 |= RD_RER;
-	lp->tx_ring[lp->txRingSize - 1].des1 |= TD_TER;
+	lp->rx_ring[lp->rxRingSize - 1].des1 |= cpu_to_le32(RD_RER);
+	lp->tx_ring[lp->txRingSize - 1].des1 |= cpu_to_le32(TD_TER);
 	    
 	/* Tell the adapter where the TX/RX rings are located. */
 	outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA);
@@ -903,20 +1131,35 @@
 	create_packet(dev, lp->frame, sizeof(lp->frame));
 
 	/* Check if the RX overflow bug needs testing for */
-	tmpchs = cfrv & 0x000000fe;
-	if ((lp->chipset == DC21140) && (tmpchs == 0x20)) {
+	i = cfrv & 0x000000fe;
+	if ((lp->chipset == DC21140) && (i == 0x20)) {
 	    lp->rx_ovf = 1;
 	}
 
-	/* Initialise the adapter state */
+	/* Initialise the SROM pointers if possible */
+	if (lp->useSROM) {
+	    lp->state = INITIALISED;
+	    de4x5_dbg_srom((struct de4x5_srom *)&lp->srom);
+	    if (srom_infoleaf_info(dev)) {
+		return -ENXIO;
+	    }
+	    srom_init(dev);
+	}
+
 	lp->state = CLOSED;
 
+	/*
+	** Check for an MII interface
+	*/
+	if ((lp->chipset != DC21040) && (lp->chipset != DC21041)) {
+	    mii_get_phy(dev);
+	}
+	
 	printk("      and requires IRQ%d (provided by %s).\n", dev->irq,
 	       ((lp->bus == PCI) ? "PCI BIOS" : "EISA CNFG"));
-
     }
     
-    if (de4x5_debug > 1) {
+    if (de4x5_debug & DEBUG_VERSION) {
 	printk(version);
     }
     
@@ -930,14 +1173,11 @@
     
     dev->mem_start = 0;
     
-    /* Fill in the generic field of the device structure. */
+    /* Fill in the generic fields of the device structure. */
     ether_setup(dev);
     
     /* Let the adapter sleep to save power */
-    if (lp->chipset == DC21041) {
-	outl(0, DE4X5_SICR);
-	outl(CFDA_PSM, PCI_CFDA);
-    }
+    yawn(dev, SLEEP);
     
     return status;
 }
@@ -962,10 +1202,7 @@
     /*
     ** Wake up the adapter
     */
-    if (lp->chipset == DC21041) {
-	outl(0, PCI_CFDA);
-	de4x5_ms_delay(10);
-    }
+    yawn(dev, WAKEUP);
 
     /* 
     ** Re-initialize the DE4X5... 
@@ -977,20 +1214,31 @@
     
     if (request_irq(dev->irq, (void *)de4x5_interrupt, SA_SHIRQ, 
 		                                     lp->adapter_name, dev)) {
-	printk("de4x5_open(): Requested IRQ%d is busy\n",dev->irq);
-	status = -EAGAIN;
-    } else {
-	dev->tbusy = 0;                         
-	dev->start = 1;
-	dev->interrupt = UNMASK_INTERRUPTS;
-	dev->trans_start = jiffies;
-	
-	START_DE4X5;
-	
-	de4x5_setup_intr(dev);
+	printk("de4x5_open(): Requested IRQ%d is busy - attemping FAST/SHARE...", dev->irq);
+	if (request_irq(dev->irq, de4x5_interrupt, SA_INTERRUPT | SA_SHIRQ,
+			                             lp->adapter_name, dev)) {
+	    printk("\n              Cannot get IRQ- reconfigure your hardware.\n");
+	    disable_ast(dev);
+	    de4x5_free_rx_buffs(dev);
+	    de4x5_free_tx_buffs(dev);
+	    yawn(dev, SLEEP);
+	    lp->state = CLOSED;
+	    return -EAGAIN;
+	} else {
+	    printk("\n              Succeeded, but you should reconfigure your hardware to avoid this.\n");
+	    printk("WARNING: there may be IRQ related problems in heavily loaded systems.\n");
+	}
     }
+    dev->tbusy = 0;                         
+    dev->start = 1;
+    dev->interrupt = UNMASK_INTERRUPTS;
+    dev->trans_start = jiffies;
+    
+    START_DE4X5;
+	
+    de4x5_setup_intr(dev);
     
-    if (de4x5_debug > 1) {
+    if (de4x5_debug & DEBUG_OPEN) {
 	printk("\tsts:  0x%08x\n", inl(DE4X5_STS));
 	printk("\tbmr:  0x%08x\n", inl(DE4X5_BMR));
 	printk("\timr:  0x%08x\n", inl(DE4X5_IMR));
@@ -1037,12 +1285,15 @@
     s32 bmr, omr;
     
     /* Select the MII or SRL port now and RESET the MAC */
-    if (lp->phy[lp->active].id == 0) {
-	de4x5_switch_to_srl(dev);
-    } else {
-	de4x5_switch_to_mii(dev);
+    if (!lp->useSROM) {
+	if (lp->phy[lp->active].id != 0) {
+	    lp->infoblock_csr6 = OMR_PS | OMR_HBD;
+	} else {
+	    lp->infoblock_csr6 = OMR_TTM;
+	}
+	de4x5_switch_mac_port(dev);
     }
-    
+
     /* 
     ** Set the programmable burst length to 8 longwords for all the DC21140
     ** Fasternet chips and 4 longwords for all others: DMA errors result
@@ -1063,11 +1314,11 @@
     lp->tx_new = lp->tx_old = 0;
     
     for (i = 0; i < lp->rxRingSize; i++) {
-	lp->rx_ring[i].status = R_OWN;
+	lp->rx_ring[i].status = cpu_to_le32(R_OWN);
     }
     
     for (i = 0; i < lp->txRingSize; i++) {
-	lp->tx_ring[i].status = 0;
+	lp->tx_ring[i].status = cpu_to_le32(0);
     }
     
     barrier();
@@ -1082,7 +1333,7 @@
     sti();                                       /* Ensure timer interrupts */
     for (j=0, i=0;(i<500) && (j==0);i++) {       /* Upto 500ms delay */
 	udelay(1000);
-	if (lp->tx_ring[lp->tx_new].status >= 0) j=1;
+	if ((s32)le32_to_cpu(lp->tx_ring[lp->tx_new].status) >= 0) j=1;
     }
     outl(omr, DE4X5_OMR);                        /* Stop everything! */
     
@@ -1137,8 +1388,8 @@
 	} else {
 	    de4x5_put_cache(dev, skb);
 	}
-	if (de4x5_debug > 1) {
-	    printk("%s: transmit busy, lost media or stale skb found:\n  STS:%08x\n  tbusy:%ld\n  lostMedia:%d\n  IMR:%08x\n  OMR:%08x\n Stale skb: %s\n",dev->name, inl(DE4X5_STS), dev->tbusy, lp->lostMedia, inl(DE4X5_IMR), inl(DE4X5_OMR), (lp->tx_skb[lp->tx_new] ? "YES" : "NO"));
+	if (de4x5_debug & DEBUG_TX) {
+	    printk("%s: transmit busy, lost media or stale skb found:\n  STS:%08x\n  tbusy:%ld\n  IMR:%08x\n  OMR:%08x\n Stale skb: %s\n",dev->name, inl(DE4X5_STS), dev->tbusy, inl(DE4X5_IMR), inl(DE4X5_OMR), (lp->tx_skb[lp->tx_new] ? "YES" : "NO"));
 	}
     } else if (skb->len > 0) {
 	/* If we already have stuff queued locally, use that first */
@@ -1151,6 +1402,9 @@
 	    cli();
 	    set_bit(0, (void*)&dev->tbusy);
 	    load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb);
+#if	LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8))
+ 	    lp->stats.tx_bytes += skb->len;
+#endif
 	    outl(POLL_DEMAND, DE4X5_TPD);/* Start the TX */
 		
 	    lp->tx_new = (++lp->tx_new) % lp->txRingSize;
@@ -1200,6 +1454,9 @@
       printk("%s: Re-entering the interrupt handler.\n", dev->name);
 	
     DISABLE_IRQs;                        /* Ensure non re-entrancy */
+#if	LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8))
+    synchronize_irq();
+#endif
     dev->interrupt = MASK_INTERRUPTS;
 	
     for (limit=0; limit<8; limit++) {
@@ -1215,7 +1472,6 @@
 	  de4x5_tx(dev); 
 	    
 	if (sts & STS_LNF) {             /* TP Link has failed */
-	    lp->lostMedia = LOST_MEDIA_THRESHOLD + 1;
 	    lp->irq_mask &= ~IMR_LFM;
 	}
 	    
@@ -1253,8 +1509,9 @@
     int entry;
     s32 status;
     
-    for (entry=lp->rx_new; lp->rx_ring[entry].status>=0;entry=lp->rx_new) {
-	status = lp->rx_ring[entry].status;
+    for (entry=lp->rx_new; (s32)le32_to_cpu(lp->rx_ring[entry].status)>=0;
+	                                                    entry=lp->rx_new) {
+	status = (s32)le32_to_cpu(lp->rx_ring[entry].status);
 	
 	if (lp->rx_ovf) {
 	    if (inl(DE4X5_MFC) & MFC_FOCM) {
@@ -1268,7 +1525,7 @@
 	}
 	
 	if (status & RD_LS) {                 /* Valid frame status */
-	    lp->linkOK++;
+	    if (lp->tx_enable) lp->linkOK++;
 	    if (status & RD_ES) {	      /* There was an error. */
 		lp->stats.rx_errors++;        /* Update the error stats. */
 		if (status & (RD_RF | RD_TL)) lp->stats.rx_frame_errors++;
@@ -1281,31 +1538,35 @@
 		if (status & RD_OF)           lp->pktStats.rx_overflow++;
 	    } else {                          /* A valid frame received */
 		struct sk_buff *skb;
-		short pkt_len = (short)(lp->rx_ring[entry].status >> 16) - 4;
+		short pkt_len = (short)(le32_to_cpu(lp->rx_ring[entry].status)
+					                            >> 16) - 4;
 		
 		if ((skb = de4x5_alloc_rx_buff(dev, entry, pkt_len)) == NULL) {
 		    printk("%s: Insufficient memory; nuking packet.\n", 
 			                                            dev->name);
-		    lp->stats.rx_dropped++;   /* Really, deferred. */
-		    break;
-		}
-		de4x5_dbg_rx(skb, pkt_len);
+		    lp->stats.rx_dropped++;
+		} else {
+		    de4x5_dbg_rx(skb, pkt_len);
 
-		/* Push up the protocol stack */
-		skb->protocol=eth_type_trans(skb,dev);
-		netif_rx(skb);
+		    /* Push up the protocol stack */
+		    skb->protocol=eth_type_trans(skb,dev);
+		    netif_rx(skb);
 		    
-		/* Update stats */
-		lp->stats.rx_packets++;
-		de4x5_local_stats(dev, skb->data, pkt_len);
+		    /* Update stats */
+		    lp->stats.rx_packets++;
+#if	LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8))
+ 		    lp->stats.rx_bytes += pkt_len;
+#endif
+		    de4x5_local_stats(dev, skb->data, pkt_len);
+		}
 	    }
 	    
 	    /* Change buffer ownership for this frame, back to the adapter */
 	    for (;lp->rx_old!=entry;lp->rx_old=(++lp->rx_old)%lp->rxRingSize) {
-		lp->rx_ring[lp->rx_old].status = R_OWN;
+		lp->rx_ring[lp->rx_old].status = cpu_to_le32(R_OWN);
 		barrier();
 	    }
-	    lp->rx_ring[entry].status = R_OWN;
+	    lp->rx_ring[entry].status = cpu_to_le32(R_OWN);
 	    barrier();
 	}
 	
@@ -1330,7 +1591,7 @@
     s32 status;
     
     for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) {
-	status = lp->tx_ring[entry].status;
+	status = (s32)le32_to_cpu(lp->tx_ring[entry].status);
 	if (status < 0) {                     /* Buffer not sent yet */
 	    break;
 	} else if (status != 0x7fffffff) {    /* Not setup frame */
@@ -1342,16 +1603,12 @@
 		if (status & TD_EC) lp->pktStats.excessive_collisions++;
 		if (status & TD_DE) lp->stats.tx_aborted_errors++;
 	    
-		if (status & (TD_LO | TD_NC | TD_EC | TD_LF)) {
-		    lp->lostMedia++;
-		}
 		if (TX_PKT_PENDING) {
 		    outl(POLL_DEMAND, DE4X5_TPD);/* Restart a stalled TX */
 		}
 	    } else {                      /* Packet sent */
 		lp->stats.tx_packets++;
-		lp->lostMedia = 0;        /* Remove transient problem */
-		lp->linkOK++;
+		if (lp->tx_enable) lp->linkOK++;
 	    }
 	    /* Update the collision counter */
 	    lp->stats.collisions += ((status & TD_EC) ? 16 : 
@@ -1384,7 +1641,9 @@
     
     disable_ast(dev);
     
-    if (lp->chipset == DC21140) {
+    if (lp->useSROM) {
+	next_tick = srom_autoconf(dev);
+    } else if (lp->chipset == DC21140) {
 	next_tick = dc21140m_autoconf(dev);
     } else if (lp->chipset == DC21041) {
 	next_tick = dc21041_autoconf(dev);
@@ -1431,8 +1690,8 @@
     outl(omr & ~OMR_SR, DE4X5_OMR);
     while (inl(DE4X5_STS) & STS_RS);
 
-    for (; lp->rx_ring[lp->rx_new].status>=0;) {
-	lp->rx_ring[lp->rx_new].status = R_OWN;
+    for (; (s32)le32_to_cpu(lp->rx_ring[lp->rx_new].status)>=0;) {
+	lp->rx_ring[lp->rx_new].status = cpu_to_le32(R_OWN);
 	lp->rx_new = (++lp->rx_new % lp->rxRingSize);
     }
 
@@ -1452,7 +1711,7 @@
     dev->start = 0;
     dev->tbusy = 1;
     
-    if (de4x5_debug > 1) {
+    if (de4x5_debug & DEBUG_CLOSE) {
 	printk("%s: Shutting down ethercard, status was %8.8x.\n",
 	       dev->name, inl(DE4X5_STS));
     }
@@ -1474,15 +1733,12 @@
     MOD_DEC_USE_COUNT;
     
     /* Put the adapter to sleep to save power */
-    if (lp->chipset == DC21041) {
-	outl(0, DE4X5_SICR);
-	outl(CFDA_PSM, PCI_CFDA);
-    }
+    yawn(dev, SLEEP);
     
     return 0;
 }
 
-static struct enet_statistics *
+static struct net_device_stats *
 de4x5_get_stats(struct device *dev)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
@@ -1529,12 +1785,12 @@
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     
-    lp->tx_ring[lp->tx_new].buf = virt_to_bus(buf);
-    lp->tx_ring[lp->tx_new].des1 &= TD_TER;
-    lp->tx_ring[lp->tx_new].des1 |= flags;
+    lp->tx_ring[lp->tx_new].buf = cpu_to_le32(virt_to_bus(buf));
+    lp->tx_ring[lp->tx_new].des1 &= cpu_to_le32(TD_TER);
+    lp->tx_ring[lp->tx_new].des1 |= cpu_to_le32(flags);
     lp->tx_skb[lp->tx_new] = skb;
     barrier();
-    lp->tx_ring[lp->tx_new].status = T_OWN;
+    lp->tx_ring[lp->tx_new].status = cpu_to_le32(T_OWN);
     barrier();
     
     return;
@@ -1548,7 +1804,7 @@
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
-    
+
     /* First, double check that the adapter is open */
     if (lp->state == OPEN) {
 	if (dev->flags & IFF_PROMISC) {         /* set promiscuous mode */
@@ -1586,7 +1842,7 @@
     u32 omr, crc, poly = CRC_POLYNOMIAL_LE;
     char *pa;
     unsigned char *addrs;
-    
+
     omr = inl(DE4X5_OMR);
     omr &= ~(OMR_PR | OMR_PM);
     pa = build_setup_frame(dev, ALL);        /* Build the basic frame */
@@ -1639,15 +1895,15 @@
 static void
 eisa_probe(struct device *dev, u_long ioaddr)
 {
-    int i, maxSlots, status;
-    u_short vendor, device;
-    s32 cfid;
+    int i, maxSlots, status, device;
+    u_short vendor;
+    u32 cfid;
     u_long iobase;
     struct bus_type *lp = &bus;
     char name[DE4X5_STRLEN];
     struct device *tmp;
     
-    if (!ioaddr && autoprobed) return;     /* Been here before ! */
+    if (autoprobed) return;                /* Been here before ! */
     
     lp->bus = EISA;
     
@@ -1661,17 +1917,18 @@
 	maxSlots = i + 1;
     }
     
-    for (status = -ENODEV; (i<maxSlots) && (dev!=NULL); i++, iobase+=EISA_SLOT_INC) {
+    for (status= -ENODEV;(i<maxSlots)&&(dev!=NULL);i++,iobase+=EISA_SLOT_INC) {
 	if (EISA_signature(name, EISA_ID)) {
-	    cfid = inl(PCI_CFID);
-	    cfrv = inl(PCI_CFRV);
-	    device = (u_short)(cfid >> 16);
+	    cfid = (u32) inl(PCI_CFID);
+	    cfrv = (u_short) inl(PCI_CFRV);
+	    device = (cfid >> 8) & 0x00ffff00;
 	    vendor = (u_short) cfid;
 	    
 	    /* Read the EISA Configuration Registers */
 	    dev->irq = inb(EISA_REG0);
 	    dev->irq = de4x5_irq[(dev->irq >> 1) & 0x03];
 
+	    if (is_DC2114x) device |= (cfrv & 0x00f0);
 	    lp->chipset = device;
 	    DevicePresent(DE4X5_APROM);
 	    /* Write the PCI Configuration Registers */
@@ -1683,6 +1940,9 @@
 		if ((tmp = alloc_device(dev, iobase)) != NULL) {
 		    if ((status = de4x5_hw_init(tmp, iobase)) == 0) {
 			num_de4x5s++;
+			if (loading_module) link_modules(dev, tmp);
+		    } else if (loading_module && (tmp != dev)) {
+			kfree(tmp);
 		    }
 		}
 	    } else if (autoprobed) {
@@ -1714,13 +1974,13 @@
 {
     u_char irq;
     u_char pb, pbus, dev_num, dnum, dev_fn;
-    u_short vendor, device, index, status;
+    u_short dev_id, vendor, index, status;
     u_int class = DE4X5_CLASS_CODE;
-    u_int iobase;
+    u_int device, iobase;
     struct bus_type *lp = &bus;
     struct device *tmp;
 
-    if ((!ioaddr || !loading_module) && autoprobed) return;
+    if (autoprobed) return;
     
     if (!pcibios_present()) return;          /* No PCI bus in this machine! */
     
@@ -1738,21 +1998,32 @@
 	 (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND);
 	 index++) {
 	dev_num = PCI_SLOT(dev_fn);
-	
+
 	if ((!pbus && !dnum) || ((pbus == pb) && (dnum == dev_num))) {
+	    device = 0;
 	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
-	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &device);
-	    if (!(is_DC21040 || is_DC21041 || is_DC21140)) continue;
+	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id);
+	    device = dev_id;
+	    device <<= 8;
+	    if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) {
+		continue;
+	    }
+
+	    /* Get the chip configuration revision register */
+	    pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv);
 
 	    /* Set the device number information */
 	    lp->device = dev_num;
 	    lp->bus_num = pb;
 	    
 	    /* Set the chipset information */
+	    if (is_DC2114x) device |= (cfrv & 0x00f0);
 	    lp->chipset = device;
-	    
-	    /* Get the chip configuration revision register */
-	    pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv);
+
+	    if (is_DC21142 || is_DC21143) {
+		printk("de4x5: Detected a %s chip. Currently this is unsupported in this driver.\nPlease email the author to request its inclusion!\n", (is_DC21142?"DC21142":"DC21143"));
+		continue;
+	    }
 
 	    /* Get the board I/O address */
 	    pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &iobase);
@@ -1778,6 +2049,9 @@
 		    tmp->irq = irq;
 		    if ((status = de4x5_hw_init(tmp, iobase)) == 0) {
 			num_de4x5s++;
+			if (loading_module) link_modules(dev, tmp);
+		    } else if (loading_module && (tmp != dev)) {
+			kfree(tmp);
 		    }
 		}
 	    } else if (autoprobed) {
@@ -1802,9 +2076,17 @@
     struct device *adev = NULL;
     int fixed = 0, new_dev = 0;
 
+    if (!dev) return dev;
     num_eth = de4x5_dev_index(dev->name);
-    if (loading_module) return dev;
-    
+
+    if (loading_module) {
+	if (dev->priv) {
+	    dev = insert_device(dev, iobase, de4x5_probe);
+	}
+	num_eth++;
+	return dev;
+    }
+
     while (1) {
 	if (((dev->base_addr == DE4X5_NDA) || (dev->base_addr==0)) && !adev) {
 	    adev=dev;
@@ -1851,20 +2133,22 @@
 	printk("eth%d: Device not initialised, insufficient memory\n",num_eth);
 	return NULL;
     } else {
-	new->next = dev->next;
-	dev->next = new;
-	dev = dev->next;               /* point to the new device */
-	dev->name = (char *)(dev + 1);
-	if (num_eth > 9999) {
-	    sprintf(dev->name,"eth????");/* New device name */
-	} else {
-	    sprintf(dev->name,"eth%d", num_eth);/* New device name */
+	memset((char *)new, 0, sizeof(struct device)+8);
+	new->name = (char *)(new + 1);
+	new->base_addr = iobase;       /* assign the io address */
+	new->init = init;              /* initialisation routine */
+	if (!loading_module) {
+	    new->next = dev->next;
+	    dev->next = new;
+	    if (num_eth > 9999) {
+		sprintf(new->name,"eth????");/* New device name */
+	    } else {
+		sprintf(new->name,"eth%d", num_eth);/* New device name */
+	    }
 	}
-	dev->base_addr = iobase;       /* assign the io address */
-	dev->init = init;              /* initialisation routine */
     }
 
-    return dev;
+    return new;
 }
 
 static int
@@ -1882,6 +2166,26 @@
     return i;
 }
 
+static void
+link_modules(struct device *dev, struct device *tmp)
+{
+    struct device *p=dev;
+
+    if (p) {
+	while (((struct de4x5_private *)(p->priv))->next_module) {
+	    p = ((struct de4x5_private *)(p->priv))->next_module;
+	}
+
+	if (dev != tmp) {
+	    ((struct de4x5_private *)(p->priv))->next_module = tmp;
+	} else {
+	    ((struct de4x5_private *)(p->priv))->next_module = NULL;
+	}
+    }
+
+    return;
+}
+
 /*
 ** Auto configure the media here rather than setting the port at compile
 ** time. This routine is called by de4x5_init() and when a loss of media is
@@ -1901,13 +2205,16 @@
     disable_ast(dev);
     inl(DE4X5_MFC);                         /* Zero the lost frames counter */
     lp->media = INIT;
-    if (lp->chipset == DC21040) {
+    if (lp->useSROM) {
+	next_tick = srom_autoconf(dev);
+    } else if (lp->chipset == DC21040) {
 	next_tick = dc21040_autoconf(dev);
     } else if (lp->chipset == DC21041) {
 	next_tick = dc21041_autoconf(dev);
     } else if (lp->chipset == DC21140) {
 	next_tick = dc21140m_autoconf(dev);
     }
+
     enable_ast(dev, next_tick);
     
     return (lp->media);
@@ -2046,7 +2353,7 @@
 
     switch (lp->local_state) {
       case 1:
-	if (lp->linkOK && !LOST_MEDIA) {
+	if (lp->linkOK) {
 	    lp->media = prev_state;
 	} else {
 	    lp->local_state++;
@@ -2063,6 +2370,7 @@
 	    lp->media = prev_state;
 	} else {
 	    lp->media = INIT;
+	    lp->tcount++;
 	}
     }
 
@@ -2110,7 +2418,7 @@
       case TP_NW:
 	if (lp->timeout < 0) {
 	    omr = inl(DE4X5_OMR);/* Set up full duplex for the autonegotiate */
-	    outl(omr | OMR_FD, DE4X5_OMR);
+	    outl(omr | OMR_FDX, DE4X5_OMR);
 	}
 	irqs = STS_LNF | STS_LNP;
 	irq_mask = IMR_LFM | IMR_LPM;
@@ -2157,7 +2465,7 @@
 	if (!lp->tx_enable) {
 	    if (lp->timeout < 0) {
 		omr = inl(DE4X5_OMR);          /* Set up half duplex for TP */
-		outl(omr & ~OMR_FD, DE4X5_OMR);
+		outl(omr & ~OMR_FDX, DE4X5_OMR);
 	    }
 	    irqs = STS_LNF | STS_LNP;
 	    irq_mask = IMR_LFM | IMR_LPM;
@@ -2191,7 +2499,7 @@
 	if (!lp->tx_enable) {
 	    if (lp->timeout < 0) {
 		omr = inl(DE4X5_OMR);          /* Set up half duplex for AUI */
-		outl(omr & ~OMR_FD, DE4X5_OMR);
+		outl(omr & ~OMR_FDX, DE4X5_OMR);
 	    }
 	    irqs = 0;
 	    irq_mask = 0;
@@ -2222,7 +2530,7 @@
 	  case 0:
 	    if (lp->timeout < 0) {
 		omr = inl(DE4X5_OMR);          /* Set up half duplex for BNC */
-		outl(omr & ~OMR_FD, DE4X5_OMR);
+		outl(omr & ~OMR_FDX, DE4X5_OMR);
 	    }
 	    irqs = 0;
 	    irq_mask = 0;
@@ -2261,7 +2569,7 @@
 	
       case NC:
 	omr = inl(DE4X5_OMR);    /* Set up full duplex for the autonegotiate */
-	outl(omr | OMR_FD, DE4X5_OMR);
+	outl(omr | OMR_FDX, DE4X5_OMR);
 	reset_init_sia(dev, 0xef01, 0xffff, 0x0008);/* Initialise the SIA */
 	if (lp->media != lp->c_media) {
 	    de4x5_dbg_media(dev);
@@ -2287,34 +2595,45 @@
     int ana, anlpa, cap, cr, slnk, sr, iobase = dev->base_addr;
     int next_tick = DE4X5_AUTOSENSE_MS;
     u_long imr, omr;
-    
+
     switch(lp->media) {
       case INIT: 
-	DISABLE_IRQs;
-	lp->tx_enable = FALSE;
-	lp->timeout = -1;
+        if (lp->timeout < 0) {
+	    DISABLE_IRQs;
+	    lp->tx_enable = FALSE;
+	    lp->linkOK = 0;
+	    de4x5_save_skbs(dev);          /* Save non transmitted skb's */
+	}
 	if ((next_tick = de4x5_reset_phy(dev)) < 0) {
 	    next_tick &= ~TIMER_CB;
 	} else {
-	    de4x5_save_skbs(dev);          /* Save non transmitted skb's */
-	    lp->tmp = MII_SR_ASSC;         /* Fake out the MII speed set */
-	    SET_10Mb;
-	    if (lp->autosense == _100Mb) {
-		lp->media = _100Mb;
-	    } else if (lp->autosense == _10Mb) {
-		lp->media = _10Mb;
-	    } else if ((lp->autosense == AUTO) && 
-		                     ((sr=is_anc_capable(dev)) & MII_SR_ANC)) {
-		ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA);
-		ana &= (de4x5_full_duplex ? ~0 : ~MII_ANA_FDAM);
-		mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII);
-		lp->media = ANS;
-	    } else if (lp->autosense == AUTO) {
-		lp->media = SPD_DET;
-	    } else if (is_spd_100(dev) && is_100_up(dev)) {
-		lp->media = _100Mb;
+	    if (lp->useSROM) {
+		srom_map_media(dev);
+		srom_exec(dev, lp->phy[lp->active].gep);
+		if (lp->infoblock_media == ANS) {
+		    ana = lp->phy[lp->active].ana | MII_ANA_CSMA;
+		    mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII);
+		}
 	    } else {
-		lp->media = NC;
+		lp->tmp = MII_SR_ASSC;     /* Fake out the MII speed set */
+		SET_10Mb;
+		if (lp->autosense == _100Mb) {
+		    lp->media = _100Mb;
+		} else if (lp->autosense == _10Mb) {
+		    lp->media = _10Mb;
+		} else if ((lp->autosense == AUTO) && 
+			            ((sr=is_anc_capable(dev)) & MII_SR_ANC)) {
+		    ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA);
+		    ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM);
+		    mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII);
+		    lp->media = ANS;
+		} else if (lp->autosense == AUTO) {
+		    lp->media = SPD_DET;
+		} else if (is_spd_100(dev) && is_100_up(dev)) {
+		    lp->media = _100Mb;
+		} else {
+		    lp->media = NC;
+		}
 	    }
 	    lp->local_state = 0;
 	    next_tick = dc21140m_autoconf(dev);
@@ -2354,10 +2673,10 @@
 		    if (!(anlpa & MII_ANLPA_RF) && 
 			 (cap = anlpa & MII_ANLPA_TAF & ana)) {
 			if (cap & MII_ANA_100M) {
-			    de4x5_full_duplex = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE);
+			    lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE);
 			    lp->media = _100Mb;
 			} else if (cap & MII_ANA_10M) {
-			    de4x5_full_duplex = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE);
+			    lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE);
 
 			    lp->media = _10Mb;
 			}
@@ -2390,7 +2709,7 @@
 	break;
 	
       case _100Mb:                               /* Set 100Mb/s */
-	next_tick = 3000;
+        next_tick = 3000;
 	if (!lp->tx_enable) {
 	    SET_100Mb;
 	    de4x5_init_connection(dev);
@@ -2398,6 +2717,7 @@
 	    if (!lp->linkOK && (lp->autosense == AUTO)) {
 		if (!(is_spd_100(dev) && is_100_up(dev))) {
 		    lp->media = INIT;
+		    lp->tcount++;
 		    next_tick = DE4X5_AUTOSENSE_MS;
 		}
 	    }
@@ -2405,7 +2725,7 @@
 	break;
 	
       case _10Mb:                                /* Set 10Mb/s */
-	next_tick = 3000;
+        next_tick = 3000;
 	if (!lp->tx_enable) {
 	    SET_10Mb;
 	    de4x5_init_connection(dev);
@@ -2413,6 +2733,7 @@
 	    if (!lp->linkOK && (lp->autosense == AUTO)) {
 		if (!(!is_spd_100(dev) && is_10_up(dev))) {
 		    lp->media = INIT;
+		    lp->tcount++;
 		    next_tick = DE4X5_AUTOSENSE_MS;
 		}
 	    }
@@ -2432,6 +2753,72 @@
     return next_tick;
 }
 
+static int
+srom_autoconf(struct device *dev)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+
+    return lp->infoleaf_fn(dev);
+}
+
+/*
+** This mapping keeps the original media codes and FDX flag unchanged.
+** While it isn't strictly necessary, it helps me for the moment...
+*/
+static void
+srom_map_media(struct device *dev)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+
+    lp->fdx = 0;
+    switch(lp->infoblock_media) {
+      case SROM_10BASETF:
+	lp->fdx = TRUE;
+      case SROM_10BASET:
+	if (lp->chipset == DC21140) {
+	    lp->media = _10Mb;
+	} else {
+	    lp->media = TP;
+	}
+	break;
+
+      case SROM_10BASE2:
+	lp->media = BNC;
+	break;
+
+      case SROM_10BASE5:
+	lp->media = AUI;
+	break;
+
+      case SROM_100BASETF:
+	lp->fdx = TRUE;
+      case SROM_100BASET:
+	lp->media = _100Mb;
+	break;
+
+      case SROM_100BASET4:
+	lp->media = _100Mb;
+	break;
+
+      case SROM_100BASEFF:
+	lp->fdx = TRUE;
+      case SROM_100BASEF: 
+	lp->media = _100Mb;
+	break;
+
+      case ANS:
+	lp->media = ANS;
+	break;
+
+      default: 
+	printk("%s: Bad media code [%d] detected in SROM!\n", dev->name, 
+	                                                  lp->infoblock_media);
+	break;
+    }
+
+    return;
+}
+
 static void
 de4x5_init_connection(struct device *dev)
 {
@@ -2442,11 +2829,10 @@
         de4x5_dbg_media(dev);
 	lp->c_media = lp->media;          /* Stop scrolling media messages */
     }
-    de4x5_restore_skbs(dev);
+
     cli();
-    de4x5_rx(dev);
+    de4x5_restore_skbs(dev);
     de4x5_setup_intr(dev);
-    lp->lostMedia = 0;
     lp->tx_enable = YES;
     dev->tbusy = 0;
     sti();
@@ -2456,6 +2842,11 @@
     return;
 }
 
+/*
+** General PHY reset function. Some MII devices don't reset correctly
+** since their MII address pins can float at voltages that are dependent
+** on the signal pin use. Do a double reset to ensure a reset.
+*/
 static int
 de4x5_reset_phy(struct device *dev)
 {
@@ -2463,17 +2854,30 @@
     u_long iobase = dev->base_addr;
     int next_tick = 0;
 
-    if (lp->phy[lp->active].id) {
+    if ((lp->useSROM) || (lp->phy[lp->active].id)) {
 	if (lp->timeout < 0) {
-	    outl(GEP_HRST, DE4X5_GEP);           /* Hard RESET the PHY dev. */
-	    udelay(1000);                        /* Assert for 1ms */
-	    outl(0x00, DE4X5_GEP);
-	    udelay(2000);                        /* Wait for 2ms */
-	    mii_wr(MII_CR_RST, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);
+	    if (lp->useSROM) {
+		if (lp->phy[lp->active].rst) { /* MII device specific reset */
+		    srom_exec(dev, lp->phy[lp->active].rst);
+		    srom_exec(dev, lp->phy[lp->active].rst);
+		} else if (lp->rst) {          /* Type 5 infoblock reset */
+		    srom_exec(dev, lp->rst);
+		    srom_exec(dev, lp->rst);
+		}
+	    } else {
+		PHY_HARD_RESET;
+	    }
+	    if (lp->useMII) {
+	        mii_wr(MII_CR_RST, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);
+            }
+        }
+	if (lp->useMII) {
+	    next_tick = test_mii_reg(dev, MII_CR, MII_CR_RST, FALSE, 500);
 	}
-	next_tick = test_mii_reg(dev, MII_CR, MII_CR_RST, FALSE, 500);
+    } else if (lp->chipset == DC21140) {
+	PHY_HARD_RESET;
     }
-    
+
     return next_tick;
 }
 
@@ -2546,7 +2950,7 @@
 	lp->timeout = msec/100;
     }
     
-    if (lp->phy[lp->active].id) {
+    if (lp->phy[lp->active].id || lp->useSROM) {
 	gep = ((is_100_up(dev) && is_spd_100(dev)) ? GEP_SLNK : 0);
     } else {
 	gep = (~inl(DE4X5_GEP) & (GEP_SLNK | GEP_LNP));
@@ -2594,7 +2998,11 @@
     u_long iobase = dev->base_addr;
     int spd;
     
-    if (lp->phy[lp->active].id) {
+    if (lp->useSROM && !lp->useMII) {
+	spd = (lp->asBitValid & 
+	       (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) |
+ 		 (lp->linkOK & ~lp->asBitValid);
+    } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
 	spd = mii_rd(lp->phy[lp->active].spd.reg, lp->phy[lp->active].addr, DE4X5_MII);
 	spd = ~(spd ^ lp->phy[lp->active].spd.value);
 	spd &= lp->phy[lp->active].spd.mask;
@@ -2611,7 +3019,11 @@
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
     
-    if (lp->phy[lp->active].id) {
+    if (lp->useSROM && !lp->useMII) {
+	return ((lp->asBitValid & 
+		 (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) |
+		(lp->linkOK & ~lp->asBitValid));
+    } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
 	/* Double read for sticky bits & temporary drops */
 	mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII);
 	return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS);
@@ -2626,7 +3038,11 @@
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
     
-    if (lp->phy[lp->active].id) {
+    if (lp->useSROM && !lp->useMII) {
+	return ((lp->asBitValid & 
+		 (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) |
+		(lp->linkOK & ~lp->asBitValid));
+    } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
 	/* Double read for sticky bits & temporary drops */
 	mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII);
 	return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS);
@@ -2641,7 +3057,7 @@
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
     
-    if (lp->phy[lp->active].id) {
+    if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
 	return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII));
     } else {
 	return 0;
@@ -2670,11 +3086,14 @@
     
     sisr = inl(DE4X5_SISR);
 
-    if ((!(sisr & SISR_NCR)) && (lp->tx_ring[lp->tmp].status < 0) && (--lp->timeout)) {
+    if ((!(sisr & SISR_NCR)) && 
+	((s32)le32_to_cpu(lp->tx_ring[lp->tmp].status) < 0) && 
+	 (--lp->timeout)) {
 	sisr = 100 | TIMER_CB;
     } else {
 	if ((!(sisr & SISR_NCR)) && 
-	    !(lp->tx_ring[lp->tmp].status & (T_OWN | TD_ES)) && lp->timeout) {
+	    !(le32_to_cpu(lp->tx_ring[lp->tmp].status) & (T_OWN | TD_ES)) &&
+	    lp->timeout) {
 	    sisr = 0;
 	} else {
 	    sisr = 1;
@@ -2696,7 +3115,7 @@
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     struct sk_buff *p;
 
-#if !defined(__alpha__) && !defined(DE4X5_DO_MEMCPY)
+#if !defined(__alpha__) && !defined(__powerpc__) && !defined(DE4X5_DO_MEMCPY)
     struct sk_buff *ret;
     u_long i=0, tmp;
 
@@ -2728,10 +3147,13 @@
     skb_reserve(p, 2);	                               /* Align */
     if (index < lp->rx_old) {                          /* Wrapped buffer */
 	short tlen = (lp->rxRingSize - lp->rx_old) * RX_BUFF_SZ;
-	memcpy(skb_put(p,tlen), bus_to_virt(lp->rx_ring[lp->rx_old].buf),tlen);
-	memcpy(skb_put(p,len-tlen), bus_to_virt(lp->rx_ring[0].buf), len-tlen);
+	memcpy(skb_put(p,tlen), 
+	       bus_to_virt(le32_to_cpu(lp->rx_ring[lp->rx_old].buf)),tlen);
+	memcpy(skb_put(p,len-tlen), 
+	       bus_to_virt(le32_to_cpu(lp->rx_ring[0].buf)), len-tlen);
     } else {                                           /* Linear buffer */
-	memcpy(skb_put(p,len), bus_to_virt(lp->rx_ring[lp->rx_old].buf),len);
+	memcpy(skb_put(p,len), 
+	       bus_to_virt(le32_to_cpu(lp->rx_ring[lp->rx_old].buf)),len);
     }
 		    
     return p;
@@ -2810,13 +3232,26 @@
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
+    int i;
     s32 omr;
 
     if (lp->cache.save_cnt) {
 	STOP_DE4X5;
-	de4x5_cache_state(dev, DE4X5_SAVE_STATE);
-	de4x5_sw_reset(dev);
-	de4x5_cache_state(dev, DE4X5_RESTORE_STATE);
+	outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA);
+	outl(virt_to_bus(lp->tx_ring), DE4X5_TRBA);
+    
+	lp->rx_new = lp->rx_old = 0;
+	lp->tx_new = lp->tx_old = 0;
+    
+	for (i = 0; i < lp->rxRingSize; i++) {
+	    lp->rx_ring[i].status = cpu_to_le32(R_OWN);
+	}
+    
+	for (i = 0; i < lp->txRingSize; i++) {
+	    lp->tx_ring[i].status = cpu_to_le32(0);
+	}
+    
+	barrier();
 	lp->cache.save_cnt--;
 	START_DE4X5;
     }
@@ -2829,7 +3264,6 @@
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
-    s32 gep;
 
     switch(flag) {
       case DE4X5_SAVE_STATE:
@@ -2848,12 +3282,8 @@
 	outl(lp->cache.csr6, DE4X5_OMR);
 	outl(lp->cache.csr7, DE4X5_IMR);
 	if (lp->chipset == DC21140) {
-	    outl(GEP_INIT, DE4X5_GEP);
-	    gep = (lp->media == _100Mb ? GEP_MODE : 0);
-	    if (!lp->phy[lp->active].id && !de4x5_full_duplex) {
-		gep |= GEP_FDXD;
-	    }
-	    outl(gep, DE4X5_GEP);
+	    outl(lp->cache.gepc, DE4X5_GEP);
+	    outl(lp->cache.gep, DE4X5_GEP);
 	} else {
 	    reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14, 
 			                                      lp->cache.csr15);
@@ -2975,7 +3405,7 @@
 }
 
 /*
-** Create a loopback ethernet packet with an invalid CRC
+** Create a loopback ethernet packet
 */
 static void
 create_packet(struct device *dev, char *frame, int len)
@@ -3069,13 +3499,10 @@
     
     if (lp->chipset == DC21040) {
 	strcpy(name, "DE434/5");
-    } else {
+	return status;
+    } else {                           /* Search for a DEC name in the SROM */
 	int i = *((char *)&lp->srom + 19) * 3;
-	if (lp->chipset == DC21041) {
-	    strncpy(name, (char *)&lp->srom + 26 + i, 8);
-	} else if (lp->chipset == DC21140) {
-	    strncpy(name, (char *)&lp->srom + 26 + i, 8);
-	}
+	strncpy(name, (char *)&lp->srom + 26 + i, 8);
     }
     name[8] = '\0';
     for (i=0; i<siglen; i++) {
@@ -3087,8 +3514,13 @@
 	} else {                        /* Use chip name to avoid confusion */
 	    strcpy(name, (((lp->chipset == DC21040) ? "DC21040" :
 			   ((lp->chipset == DC21041) ? "DC21041" :
-			    ((lp->chipset == DC21140) ? "DC21140" : "UNKNOWN"
-			     )))));
+			    ((lp->chipset == DC21140) ? "DC21140" :
+			     ((lp->chipset == DC21142) ? "DC21142" :
+			      ((lp->chipset == DC21143) ? "DC21143" : "UNKNOWN"
+			     )))))));
+	}
+	if (lp->chipset != DC21041) {
+	    useSROM = TRUE;             /* card is not recognisably DEC */
 	}
     }
     
@@ -3108,9 +3540,10 @@
     if (lp->chipset == DC21040) {
 	outl(0, aprom_addr);           /* Reset Ethernet Address ROM Pointer */
     } else {                           /* Read new srom */
-	short *p = (short *)&lp->srom;
+	u_short tmp, *p = (short *)&lp->srom;
 	for (i=0; i<(sizeof(struct de4x5_srom)>>1); i++) {
-	    *p++ = srom_rd(aprom_addr, i);
+	    tmp = srom_rd(aprom_addr, i);
+	    *p++ = le16_to_cpu(tmp);
 	}
 	de4x5_dbg_srom((struct de4x5_srom *)&lp->srom);
     }
@@ -3118,6 +3551,12 @@
     return;
 }
 
+/*
+** For the bad status case and no SROM, then add one to the previous
+** address. However, need to add one backwards in case we have 0xff
+** as one or more of the bytes. Only the last 3 bytes should be checked
+** as the first three are invariant - assigned to an organisation.
+*/
 static int
 get_hw_addr(struct device *dev)
 {
@@ -3171,6 +3610,12 @@
 	if ((k != chksum) && (dec_only)) status = -1;
     }
 
+    /* If possible, try to fix a broken card - SMC only so far */
+    srom_repair(dev, broken);
+
+    /* Test for a bad enet address */
+    status = test_bad_enet(dev, status);
+
     return status;
 }
 
@@ -3210,6 +3655,55 @@
     return ret;
 }
 
+static void
+srom_repair(struct device *dev, int card)
+{
+    struct bus_type *lp = &bus;
+
+    switch(card) {
+      case SMC:
+	memset((char *)&bus.srom, 0, sizeof(struct de4x5_srom));
+	memcpy(lp->srom.ieee_addr, (char *)dev->dev_addr, ETH_ALEN);
+	memcpy(lp->srom.info, (char *)&srom_repair_info[SMC-1], 100);
+	useSROM = TRUE;
+	break;
+    }
+
+    return;
+}
+
+static int
+test_bad_enet(struct device *dev, int status)
+{
+    struct bus_type *lp = &bus;
+    int i, tmp;
+
+    for (tmp=0,i=0; i<ETH_ALEN; i++) tmp += (u_char)dev->dev_addr[i];
+    if ((tmp == 0) || (tmp == 0x5fa)) {
+	if ((lp->chipset == last.chipset) && 
+	    (lp->bus_num == last.bus) && (lp->bus_num > 0)) {
+	    for (i=0; i<ETH_ALEN; i++) dev->dev_addr[i] = last.addr[i];
+	    for (i=ETH_ALEN-1; i>2; --i) {
+		dev->dev_addr[i] += 1;
+		if (dev->dev_addr[i] != 0) break;
+	    }
+	    for (i=0; i<ETH_ALEN; i++) last.addr[i] = dev->dev_addr[i];
+	    if (((*((int *)dev->dev_addr) & 0x00ffffff) == 0x95c000) &&
+		(lp->chipset == DC21040)) {
+		dev->irq = last.irq;
+	    }
+	    status = 0;
+	}
+    } else if (!status) {
+	last.chipset = lp->chipset;
+	last.bus = lp->bus_num;
+	last.irq = dev->irq;
+	for (i=0; i<ETH_ALEN; i++) last.addr[i] = dev->dev_addr[i];
+    }
+
+    return status;
+}
+
 /*
 ** SROM Read
 */
@@ -3321,16 +3815,401 @@
     return tmp;
 }
 
-/*
-** MII Read/Write
-*/
-
 static int
-mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr)
+srom_infoleaf_info(struct device *dev)
 {
-    mii_wdata(MII_PREAMBLE,  2, ioaddr);   /* Start of 34 bit preamble...    */
-    mii_wdata(MII_PREAMBLE, 32, ioaddr);   /* ...continued                   */
-    mii_wdata(MII_STRD, 4, ioaddr);        /* SFD and Read operation         */
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    int i, count;
+    u_char *p;
+
+    /* Find the infoleaf decoder function that matches this chipset */
+    for (i=0; i<INFOLEAF_SIZE; i++) {
+	if (lp->chipset == infoleaf_array[i].chipset) break;
+    }
+    if (i == INFOLEAF_SIZE) {
+	lp->useSROM = FALSE;
+	printk("%s: Cannot find correct chipset for SROM decoding!\n", 
+	                                                          dev->name);
+	return -ENXIO;
+    }
+
+    lp->infoleaf_fn = infoleaf_array[i].fn;
+
+    /* Find the information offset that this function should use */
+    count = *((u_char *)&lp->srom + 19);
+    p  = (u_char *)&lp->srom + 26;
+
+    if (count > 1) {
+	for (i=count; i; --i, p+=3) {
+	    if (lp->device == *p) break;
+	}
+	if (i == 0) {
+	    lp->useSROM = FALSE;
+	    printk("%s: Cannot find correct PCI device [%d] for SROM decoding!\n", 
+	                                               dev->name, lp->device);
+	    return -ENXIO;
+	}
+    }
+
+    lp->infoleaf_offset = TWIDDLE(p+1);
+
+    return 0;
+}
+
+/*
+** This routine loads any type 1 or 3 MII info into the mii device
+** struct and executes any type 5 code to reset PHY devices for this
+** controller.
+** The info for the MII devices will be valid since the index used
+** will follow the discovery process from MII address 1-31 then 0.
+*/
+static void
+srom_init(struct device *dev)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    u_long iobase = dev->base_addr;
+    u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset;
+    u_char count;
+
+    p+=2;
+    if (lp->chipset == DC21140) {
+	lp->cache.gepc = (*p++ | GEP_CTRL);
+	outl(lp->cache.gepc, DE4X5_GEP);
+    }
+
+    /* Block count */
+    count = *p++;
+
+    /* Jump the infoblocks to find types */
+    for (;count; --count) {
+	if (*p < 128) {
+	    p += COMPACT_LEN;
+	} else if (*(p+1) == 5) {
+	    type5_infoblock(dev, 1, p);
+	    p += ((*p & BLOCK_LEN) + 1);
+	} else if (*(p+1) == 3) {
+	    type3_infoblock(dev, 1, p);
+	    p += ((*p & BLOCK_LEN) + 1);
+	} else if (*(p+1) == 1) {
+	    type1_infoblock(dev, 1, p);
+	    p += ((*p & BLOCK_LEN) + 1);
+	} else {
+	    p += ((*p & BLOCK_LEN) + 1);
+	}
+    }
+
+    return;
+}
+
+static void
+srom_exec(struct device *dev, u_char *p)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    u_long iobase = dev->base_addr;
+    u_char count = (p ? *p++ : 0);
+
+    while (count--) {
+	if (lp->chipset == DC21140) {
+	    outl(*p++, DE4X5_GEP);
+	}
+	udelay(2000);                       /* 2ms per action */
+    }
+
+    return;
+}
+
+/*
+** Basically this function is a NOP since it will never be called,
+** unless I implement the DC21041 SROM functions. There's no need
+** since the existing code will be satisfactory for all boards.
+*/
+static int 
+dc21041_infoleaf(struct device *dev)
+{
+    return DE4X5_AUTOSENSE_MS;
+}
+
+static int 
+dc21140_infoleaf(struct device *dev)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    u_char count = 0;
+    u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset;
+    int next_tick = DE4X5_AUTOSENSE_MS;
+
+    /* Read the connection type */
+    p+=2;
+
+    /* GEP control */
+    lp->cache.gepc = (*p++ | GEP_CTRL);
+
+    /* Block count */
+    count = *p++;
+
+    /* Recursively figure out the info blocks */
+    if (*p < 128) {
+	next_tick = dc_infoblock[COMPACT](dev, count, p);
+    } else {
+	next_tick = dc_infoblock[*(p+1)](dev, count, p);
+    }
+
+    if (lp->tcount == count) {
+	lp->media = NC;
+        if (lp->media != lp->c_media) {
+	    de4x5_dbg_media(dev);
+	    lp->c_media = lp->media;
+	}
+	lp->media = INIT;
+	lp->tcount = 0;
+	lp->tx_enable = FALSE;
+    }
+
+    return next_tick & ~TIMER_CB;
+}
+
+static int 
+dc21142_infoleaf(struct device *dev)
+{
+printk("dc21142_infoleaf()\n");
+    return DE4X5_AUTOSENSE_MS;
+}
+
+static int 
+dc21143_infoleaf(struct device *dev)
+{
+printk("dc21143_infoleaf()\n");
+    return DE4X5_AUTOSENSE_MS;
+}
+
+/*
+** The compact infoblock is only designed for DC21140[A] chips, so
+** we'll reuse the dc21140m_autoconf function. Non MII media only.
+*/
+static int 
+compact_infoblock(struct device *dev, u_char count, u_char *p)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    u_long iobase = dev->base_addr;
+    u_char flags, csr6;
+
+    /* Recursively figure out the info blocks */
+    if (--count > lp->tcount) {
+	if (*(p+COMPACT_LEN) < 128) {
+	    return dc_infoblock[COMPACT](dev, count, p+COMPACT_LEN);
+	} else {
+	    return dc_infoblock[*(p+COMPACT_LEN+1)](dev, count, p+COMPACT_LEN);
+	}
+    }
+
+    if ((lp->media == INIT) && (lp->timeout < 0)) {
+        outl(lp->cache.gepc, DE4X5_GEP);
+	lp->infoblock_media = (*p++) & COMPACT_MC;
+	lp->cache.gep = *p++;
+	csr6 = *p++;
+	flags = *p++;
+
+	lp->asBitValid = (flags & 0x80) ? 0 : -1;
+	lp->defMedium = (flags & 0x40) ? -1 : 0;
+	lp->asBit = 1 << ((csr6 >> 1) & 0x07);
+	lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit;
+	lp->infoblock_csr6 = (csr6 & 0x71) << 18;
+	lp->useMII = FALSE;
+
+	de4x5_switch_mac_port(dev);
+    }
+
+    return dc21140m_autoconf(dev);
+}
+
+/*
+** This block describes non MII media for the DC21140[A] only.
+ */
+static int 
+type0_infoblock(struct device *dev, u_char count, u_char *p)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    u_long iobase = dev->base_addr;
+    u_char flags, csr6, len = (*p & BLOCK_LEN)+1;
+
+    /* Recursively figure out the info blocks */
+    if (--count > lp->tcount) {
+	if (*(p+len) < 128) {
+	    return dc_infoblock[COMPACT](dev, count, p+len);
+	} else {
+	    return dc_infoblock[*(p+len+1)](dev, count, p+len);
+	}
+    }
+
+    if ((lp->media == INIT) && (lp->timeout < 0)) {
+        outl(lp->cache.gepc, DE4X5_GEP);
+	p+=2;
+	lp->infoblock_media = (*p++) & BLOCK0_MC;
+	lp->cache.gep = *p++;
+	csr6 = *p++;
+	flags = *p++;
+
+	lp->asBitValid = (flags & 0x80) ? 0 : -1;
+	lp->defMedium = (flags & 0x40) ? -1 : 0;
+	lp->asBit = 1 << ((csr6 >> 1) & 0x07);
+	lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit;
+	lp->infoblock_csr6 = (csr6 & 0x71) << 18;
+	lp->useMII = FALSE;
+
+	de4x5_switch_mac_port(dev);
+    }
+
+    return dc21140m_autoconf(dev);
+}
+
+/* These functions are under construction! */
+
+static int 
+type1_infoblock(struct device *dev, u_char count, u_char *p)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    u_char len = (*p & BLOCK_LEN)+1;
+
+    /* Recursively figure out the info blocks */
+    if (--count > lp->tcount) {
+	if (*(p+len) < 128) {
+	    return dc_infoblock[COMPACT](dev, count, p+len);
+	} else {
+	    return dc_infoblock[*(p+len+1)](dev, count, p+len);
+	}
+    }
+
+    p += 2;
+    if (lp->state == INITIALISED) {
+	lp->ibn = 1;
+	lp->active = *p++;
+	lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1);
+	lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1);
+	lp->phy[lp->active].mc  = TWIDDLE(p); p += 2;
+	lp->phy[lp->active].ana = TWIDDLE(p); p += 2;
+	lp->phy[lp->active].fdx = TWIDDLE(p); p += 2;
+	lp->phy[lp->active].ttm = TWIDDLE(p);
+	return 0;
+    } else if ((lp->media == INIT) && (lp->timeout < 0)) {
+        lp->ibn = 1;
+        lp->active = *p;
+	lp->infoblock_csr6 = OMR_PS | OMR_HBD;
+	lp->useMII = TRUE;
+	lp->infoblock_media = ANS;
+	de4x5_switch_mac_port(dev);
+    }
+
+    return dc21140m_autoconf(dev);
+}
+
+static int 
+type2_infoblock(struct device *dev, u_char count, u_char *p)
+{
+    return DE4X5_AUTOSENSE_MS;
+}
+
+static int 
+type3_infoblock(struct device *dev, u_char count, u_char *p)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    u_char flags, csr6, len = (*p & BLOCK_LEN)+1;
+
+    /* Recursively figure out the info blocks */
+    if (--count > lp->tcount) {
+	if (*(p+len) < 128) {
+	    return dc_infoblock[COMPACT](dev, count, p+len);
+	} else {
+	    return dc_infoblock[*(p+len+1)](dev, count, p+len);
+	}
+    }
+
+    if (lp->state == INITIALISED) {
+	lp->ibn = 3; p += 2;
+        lp->active = *p++;
+	lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1);
+	lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1);
+	lp->phy[lp->active].mc  = TWIDDLE(p); p += 2;
+	lp->phy[lp->active].ana = TWIDDLE(p); p += 2;
+	lp->phy[lp->active].fdx = TWIDDLE(p); p += 2;
+	lp->phy[lp->active].ttm = TWIDDLE(p);
+	return 0;
+    } else if (lp->media == INIT) {
+	p+=2;
+	lp->infoblock_media = (*p++) & BLOCK0_MC;
+	lp->cache.gep = *p++;
+	csr6 = *p++;
+	flags = *p++;
+
+	lp->asBitValid = (flags & 0x80) ? 0 : -1;
+	lp->defMedium = (flags & 0x40) ? -1 : 0;
+	lp->asBit = 1 << ((csr6 >> 1) & 0x07);
+	lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit;
+	lp->infoblock_csr6 = (csr6 & 0x71) << 18;
+	lp->useMII = TRUE;
+
+	de4x5_switch_mac_port(dev);
+    }
+
+    return dc21140m_autoconf(dev);
+}
+
+static int 
+type4_infoblock(struct device *dev, u_char count, u_char *p)
+{
+    return DE4X5_AUTOSENSE_MS;
+}
+
+/*
+** This block type provides information for resetting external devices
+** (chips) through the General Purpose Register.
+*/
+static int 
+type5_infoblock(struct device *dev, u_char count, u_char *p)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    u_long iobase = dev->base_addr;
+    u_char i, j, len = (*p & BLOCK_LEN)+1;
+
+    /* Recursively figure out the info blocks */
+    if (--count > lp->tcount) {
+	if (*(p+len) < 128) {
+	    return dc_infoblock[COMPACT](dev, count, p+len);
+	} else {
+	    return dc_infoblock[*(p+len+1)](dev, count, p+len);
+	}
+    }
+
+    /* Must be initializing to run this code */
+    if ((lp->state == INITIALISED) || (lp->media == INIT)) {
+	p+=2;
+        lp->rst = p;
+	i = *p++;
+	for (j=0;i;--i) {
+	    if (lp->chipset == DC21140) {
+		if (!j) {
+		    outl(*p++ | GEP_CTRL, DE4X5_GEP);
+		    j++;
+		}
+		outl(*p++, DE4X5_GEP);
+	    } else if (lp->chipset == DC21142) {
+	    } else if (lp->chipset == DC21143) {
+	    }
+	}
+	    
+    }
+
+    return DE4X5_AUTOSENSE_MS;
+}
+
+/*
+** MII Read/Write
+*/
+
+static int
+mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr)
+{
+    mii_wdata(MII_PREAMBLE,  2, ioaddr);   /* Start of 34 bit preamble...    */
+    mii_wdata(MII_PREAMBLE, 32, ioaddr);   /* ...continued                   */
+    mii_wdata(MII_STRD, 4, ioaddr);        /* SFD and Read operation         */
     mii_address(phyaddr, ioaddr);          /* PHY address to be accessed     */
     mii_address(phyreg, ioaddr);           /* PHY Register to read           */
     mii_ta(MII_STRD, ioaddr);              /* Turn around time - 2 MDC       */
@@ -3494,23 +4373,25 @@
     return r2;                                  /* (I did it) My way */
 }
 
+/*
+** The SROM spec forces us to search addresses [1-31 0]. Bummer.
+*/
 static int
 mii_get_phy(struct device *dev)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     int iobase = dev->base_addr;
-    int i, j, k, limit=sizeof(phy_info)/sizeof(struct phy_table);
+    int i, j, k, n, limit=sizeof(phy_info)/sizeof(struct phy_table);
     int id;
     
-    /* Issue a hard PHY reset - Broadcom is screwed up otherwise */
-    outl(GEP_HRST, DE4X5_GEP);
-    udelay(1000);                                  /* Assert for 1ms */
-    outl(0x00, DE4X5_GEP);
-    udelay(2000);                                  /* Wait for 2ms */
-    
-    /* Search the MII address space for possible PHY devices */
     lp->active = 0;
-    for (lp->mii_cnt=0, i=1; i<DE4X5_MAX_MII; i++) {
+    lp->useMII = TRUE;
+
+    /* Search the MII address space for possible PHY devices */
+    for (n=0, lp->mii_cnt=0, i=1; !((i==1) && (n==1)); i=(++i)%DE4X5_MAX_MII) {
+	lp->phy[lp->active].addr = i;
+	if (i==0) n++;                             /* Count cycles */
+	while (de4x5_reset_phy(dev)<0) udelay(100);/* Wait for reset */
 	id = mii_get_oui(i, DE4X5_MII); 
 	if ((id == 0) || (id == -1)) continue;     /* Valid ID? */
 	for (j=0; j<limit; j++) {                  /* Search PHY table */
@@ -3521,13 +4402,15 @@
 		       (char *)&phy_info[j], sizeof(struct phy_table));
 		lp->phy[k].addr = i;
 		lp->mii_cnt++;
+		lp->active++;
 	    } else {
 		i = DE4X5_MAX_MII;                 /* Stop the search */
 		j = limit;
 	    }
 	}
     }
-    if (lp->phy[lp->active].id) {                  /* Reset the PHY devices */
+    lp->active = 0;
+    if (lp->phy[0].id) {                           /* Reset the PHY devices */
 	for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++) { /*For each PHY*/
 	    mii_wr(MII_CR_RST, MII_CR, lp->phy[k].addr, DE4X5_MII);
 	    while (mii_rd(MII_CR, lp->phy[k].addr, DE4X5_MII) & MII_CR_RST);
@@ -3590,15 +4473,17 @@
 }
 
 static long
-de4x5_switch_to_mii(struct device *dev)
+de4x5_switch_mac_port(struct device *dev)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     int iobase = dev->base_addr;
-    long omr;
-    
+    s32 omr;
+
     /* Assert the OMR_PS bit in CSR6 */
-    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR));
-    omr |= (OMR_PS | OMR_HBD);
+    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR |
+			                                             OMR_FDX));
+    omr |= lp->infoblock_csr6;
+    if (omr & OMR_PS) omr |= OMR_HBD;
     outl(omr, DE4X5_OMR);
     
     /* Soft Reset */
@@ -3606,42 +4491,13 @@
     
     /* Restore the GEP */
     if (lp->chipset == DC21140) {
-	outl(GEP_INIT, DE4X5_GEP);
-	outl(0, DE4X5_GEP);
+	outl(lp->cache.gepc, DE4X5_GEP);
+	outl(lp->cache.gep, DE4X5_GEP);
     }
-    
-    /* Restore CSR6 */
-    outl(omr, DE4X5_OMR);
 
-    /* Reset CSR8 */
-    inl(DE4X5_MFC);
-
-    return omr;
-}
-
-static long
-de4x5_switch_to_srl(struct device *dev)
-{
-    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-    int iobase = dev->base_addr;
-    long omr;
-    
-    /* Deassert the OMR_PS bit in CSR6 */
-    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR));
-    outl(omr, DE4X5_OMR);
-    
-    /* Soft Reset */
-    RESET_DE4X5;
-    
-    /* Restore the GEP */
-    if (lp->chipset == DC21140) {
-	outl(GEP_INIT, DE4X5_GEP);
-	outl(0, DE4X5_GEP);
-    }
-    
     /* Restore CSR6 */
     outl(omr, DE4X5_OMR);
-    
+
     /* Reset CSR8 */
     inl(DE4X5_MFC);
 
@@ -3671,12 +4527,60 @@
 }
 
 static void
+yawn(struct device *dev, int state)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    int iobase = dev->base_addr;
+
+    if ((lp->chipset == DC21040) || (lp->chipset == DC21140)) return;
+
+    if(lp->bus == EISA) {
+	switch(state) {
+	  case WAKEUP:
+	    outb(WAKEUP, PCI_CFPM);
+	    de4x5_ms_delay(10);
+	    break;
+
+	  case SNOOZE:
+	    outb(SNOOZE, PCI_CFPM);
+	    break;
+
+	  case SLEEP:
+	    outl(0, DE4X5_SICR);
+	    outb(SLEEP, PCI_CFPM);
+	    break;
+	}
+    } else {
+	switch(state) {
+	  case WAKEUP:
+	    pcibios_write_config_byte(lp->bus_num, lp->device << 3, 
+				      PCI_CFDA_PSM, WAKEUP);
+	    de4x5_ms_delay(10);
+	    break;
+
+	  case SNOOZE:
+	    pcibios_write_config_byte(lp->bus_num, lp->device << 3, 
+				      PCI_CFDA_PSM, SNOOZE);
+	    break;
+
+	  case SLEEP:
+	    outl(0, DE4X5_SICR);
+	    pcibios_write_config_byte(lp->bus_num, lp->device << 3, 
+				      PCI_CFDA_PSM, SLEEP);
+	    break;
+	}
+    }
+
+    return;
+}
+
+static void
 de4x5_dbg_open(struct device *dev)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     int i;
     
-    if (de4x5_debug > 1) {
+    if (de4x5_debug & DEBUG_OPEN) {
 	printk("%s: de4x5 opening with irq %d\n",dev->name,dev->irq);
 	printk("\tphysical address: ");
 	for (i=0;i<6;i++) {
@@ -3702,17 +4606,17 @@
 	printk("Descriptor buffers:\nRX: ");
 	for (i=0;i<lp->rxRingSize-1;i++){
 	    if (i < 3) {
-		printk("0x%8.8x  ",lp->rx_ring[i].buf);
+		printk("0x%8.8x  ",le32_to_cpu(lp->rx_ring[i].buf));
 	    }
 	}
-	printk("...0x%8.8x\n",lp->rx_ring[i].buf);
+	printk("...0x%8.8x\n",le32_to_cpu(lp->rx_ring[i].buf));
 	printk("TX: ");
 	for (i=0;i<lp->txRingSize-1;i++){
 	    if (i < 3) {
-		printk("0x%8.8x  ", lp->tx_ring[i].buf);
+		printk("0x%8.8x  ", le32_to_cpu(lp->tx_ring[i].buf));
 	    }
 	}
-	printk("...0x%8.8x\n", lp->tx_ring[i].buf);
+	printk("...0x%8.8x\n", le32_to_cpu(lp->tx_ring[i].buf));
 	printk("Ring size: \nRX: %d\nTX: %d\n", 
 	       (short)lp->rxRingSize, 
 	       (short)lp->txRingSize); 
@@ -3727,7 +4631,7 @@
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     int iobase = dev->base_addr;
     
-    if (de4x5_debug > 2) {
+    if (de4x5_debug & DEBUG_MII) {
 	printk("\nMII CR:  %x\n",mii_rd(MII_CR,lp->phy[k].addr,DE4X5_MII));
 	printk("MII SR:  %x\n",mii_rd(MII_SR,lp->phy[k].addr,DE4X5_MII));
 	printk("MII ID0: %x\n",mii_rd(MII_ID0,lp->phy[k].addr,DE4X5_MII));
@@ -3754,7 +4658,7 @@
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     
     if (lp->media != lp->c_media) {
-	if (de4x5_debug > 0) {
+	if (de4x5_debug & DEBUG_MEDIA) {
 	    if (lp->chipset != DC21140) {
 		printk("%s: media is %s\n", dev->name,
 		       (lp->media == NC  ? "unconnected!" :
@@ -3786,10 +4690,12 @@
 {
     int i;
 
-    if (de4x5_debug > 1) {
-	printk("Sub-system Vendor ID: %04x\n", (u_short)*(p->sub_vendor_id));
-	printk("Sub-system ID:        %04x\n", (u_short)*(p->sub_system_id));
+    if (de4x5_debug & DEBUG_SROM) {
+	printk("Sub-system Vendor ID: %04x\n", *((u_short *)p->sub_vendor_id));
+	printk("Sub-system ID:        %04x\n", *((u_short *)p->sub_system_id));
 	printk("ID Block CRC:         %02x\n", (u_char)(p->id_block_crc));
+	printk("SROM version:         %02x\n", (u_char)(p->version));
+	printk("# controllers:         %02x\n", (u_char)(p->num_controllers));
 
 	printk("Hardware Address:     ");
 	for (i=0;i<ETH_ALEN-1;i++) {
@@ -3810,7 +4716,7 @@
 {
     int i, j;
 
-    if (de4x5_debug > 2) {
+    if (de4x5_debug & DEBUG_RX) {
 	printk("R: %02x:%02x:%02x:%02x:%02x:%02x <- %02x:%02x:%02x:%02x:%02x:%02x len/SAP:%02x%02x [%d]\n",
 	       (u_char)skb->data[0],
 	       (u_char)skb->data[1],
@@ -3827,7 +4733,7 @@
 	       (u_char)skb->data[12],
 	       (u_char)skb->data[13],
 	       len);
-	if (de4x5_debug > 3) {
+	if (de4x5_debug & DEBUG_RX) {
 	    for (j=0; len>0;j+=16, len-=16) {
 		printk("    %03x: ",j);
 		for (i=0; i<16 && i<len; i++) {
@@ -3854,9 +4760,9 @@
     int i, j, status = 0;
     s32 omr;
     union {
-	u8  addr[(HASH_TABLE_LEN * ETH_ALEN)];
-	u16 sval[(HASH_TABLE_LEN * ETH_ALEN) >> 1];
-	u32 lval[(HASH_TABLE_LEN * ETH_ALEN) >> 2];
+	u8  addr[144];
+	u16 sval[72];
+	u32 lval[36];
     } tmp;
     
     switch(ioc->cmd) {
@@ -3868,7 +4774,7 @@
 	for (i=0; i<ETH_ALEN; i++) {
 	    tmp.addr[i] = dev->dev_addr[i];
 	}
-	memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+	copy_to_user(ioc->data, tmp.addr, ioc->len);
 	
 	break;
       case DE4X5_SET_HWADDR:           /* Set the hardware address */
@@ -3879,7 +4785,7 @@
 	if (!suser())
 	  break;
 	status = 0;
-	memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN);
+	copy_from_user(tmp.addr, ioc->data, ETH_ALEN);
 	for (i=0; i<ETH_ALEN; i++) {
 	    dev->dev_addr[i] = tmp.addr[i];
 	}
@@ -3918,39 +4824,12 @@
 	
 	break;
       case DE4X5_GET_MCA:              /* Get the multicast address table */
-	ioc->len = (HASH_TABLE_LEN >> 3);
-	status = verify_area(VERIFY_WRITE, ioc->data, ioc->len);
-	if (!status) {
-	    memcpy_tofs(ioc->data, lp->setup_frame, ioc->len); 
-	}
-	
 	break;
       case DE4X5_SET_MCA:              /* Set a multicast address */
-	if (suser()) {
-	    /******* FIX ME! ********/
-	    if (ioc->len != HASH_TABLE_LEN) {         /* MCA changes */
-		if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN * ioc->len))) {
-		    memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN * ioc->len);
-		    set_multicast_list(dev);
-		}
-	    } else {
-		set_multicast_list(dev);
-	    }
-	} else {
-	    status = -EPERM;
-	}
-	
 	break;
       case DE4X5_CLR_MCA:              /* Clear all multicast addresses */
-	if (suser()) {
-	    /******* FIX ME! ********/
-	    set_multicast_list(dev);
-	} else {
-	    status = -EPERM;
-	}
-	
 	break;
-      case DE4X5_MCA_EN:               /* Enable pass all multicast addressing */
+      case DE4X5_MCA_EN:               /* Enable pass all multicast addresses*/
 	if (suser()) {
 	    omr = inl(DE4X5_OMR);
 	    omr |= OMR_PM;
@@ -3967,7 +4846,7 @@
 	  break;
 	
 	cli();
-	memcpy_tofs(ioc->data, &lp->pktStats, ioc->len); 
+	copy_to_user(ioc->data, &lp->pktStats, ioc->len); 
 	sti();
 	
 	break;
@@ -3984,14 +4863,14 @@
       case DE4X5_GET_OMR:              /* Get the OMR Register contents */
 	tmp.addr[0] = inl(DE4X5_OMR);
 	if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, 1))) {
-	    memcpy_tofs(ioc->data, tmp.addr, 1);
+	    copy_to_user(ioc->data, tmp.addr, 1);
 	}
 	
 	break;
       case DE4X5_SET_OMR:              /* Set the OMR Register contents */
 	if (suser()) {
 	    if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, 1))) {
-		memcpy_fromfs(tmp.addr, ioc->data, 1);
+		copy_from_user(tmp.addr, ioc->data, 1);
 		outl(tmp.addr[0], DE4X5_OMR);
 	    }
 	} else {
@@ -4011,11 +4890,12 @@
 	tmp.lval[7] = inl(DE4X5_SIGR); j+=4;
 	ioc->len = j;
 	if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) {
-	    memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+	    copy_to_user(ioc->data, tmp.addr, ioc->len);
 	}
 	break;
 	
-#define DE4X5_DUMP              0x0f /* Dump the DE4X5 Status */
+/*
+#define DE4X5_DUMP              0x0f / * Dump the DE4X5 Status * /
 	
       case DE4X5_DUMP:
 	j = 0;
@@ -4042,22 +4922,22 @@
 	
 	for (i=0;i<lp->rxRingSize-1;i++){
 	    if (i < 3) {
-		tmp.lval[j>>2] = (s32)lp->rx_ring[i].buf; j+=4;
+		tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4;
 	    }
 	}
-	tmp.lval[j>>2] = (s32)lp->rx_ring[i].buf; j+=4;
+	tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4;
 	for (i=0;i<lp->txRingSize-1;i++){
 	    if (i < 3) {
-		tmp.lval[j>>2] = (s32)lp->tx_ring[i].buf; j+=4;
+		tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4;
 	    }
 	}
-	tmp.lval[j>>2] = (s32)lp->tx_ring[i].buf; j+=4;
+	tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4;
 	
 	for (i=0;i<lp->rxRingSize;i++){
-	    tmp.lval[j>>2] = lp->rx_ring[i].status; j+=4;
+	    tmp.lval[j>>2] = le32_to_cpu(lp->rx_ring[i].status); j+=4;
 	}
 	for (i=0;i<lp->txRingSize;i++){
-	    tmp.lval[j>>2] = lp->tx_ring[i].status; j+=4;
+	    tmp.lval[j>>2] = le32_to_cpu(lp->tx_ring[i].status); j+=4;
 	}
 	
 	tmp.lval[j>>2] = inl(DE4X5_BMR);  j+=4;
@@ -4078,7 +4958,7 @@
 	    tmp.lval[j>>2] = inl(DE4X5_SIGR); j+=4; 
 	}
 	tmp.lval[j>>2] = lp->phy[lp->active].id; j+=4; 
-	if (lp->phy[lp->active].id) {
+	if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
 	    tmp.lval[j>>2] = lp->active; j+=4; 
 	    tmp.lval[j>>2]=mii_rd(MII_CR,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
 	    tmp.lval[j>>2]=mii_rd(MII_SR,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
@@ -4102,10 +4982,11 @@
 	
 	ioc->len = j;
 	if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) {
-	    memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+	    copy_to_user(ioc->data, tmp.addr, ioc->len);
 	}
 	
 	break;
+*/
       default:
 	status = -EOPNOTSUPP;
     }
@@ -4118,85 +4999,60 @@
 ** Note now that module autoprobing is allowed under EISA and PCI. The
 ** IRQ lines will not be auto-detected; instead I'll rely on the BIOSes
 ** to "do the right thing".
-**
-** NB: Current register_netdevice() code does not permit assigning io=0 as
-** this driver will autoprobe all instances of acceptable DECchips. The
-** cleanup_module() code needs work still....(just to unload modules owned
-** by this driver).
-*/
-static char devicename[9] = { 0, };
-static struct device thisDE4X5 = {
-    devicename,   /* device name inserted by /linux/drivers/net/net_init.c  */
-    0, 0, 0, 0,
-    0, 0,         /* I/O address, IRQ                                       */
-    0, 0, 0, NULL, de4x5_probe };
-
-static int io=0x0b; /* EDIT THESE LINES FOR YOUR CONFIGURATION              */
+*/
+#define LP(a) ((struct de4x5_private *)(a))
+static struct device *mdev = NULL;
+static int io=0x0;/* EDIT THIS LINE FOR YOUR CONFIGURATION IF NEEDED        */
 
 int
 init_module(void)
 {
-    struct device *p  = (struct device *)&thisDE4X5;
-    
-    thisDE4X5.base_addr = io;                   /* Now autoprobe the module */
-    thisDE4X5.irq = 0;
-    
-    for (; p!=NULL; p=p->next) {
+    struct device *p;
+
+    if ((mdev = insert_device(NULL, io, de4x5_probe)) == NULL) 
+      return -ENOMEM;
+
+    for (p = mdev; p != NULL; p = LP(p->priv)->next_module) {
 	if (register_netdev(p) != 0)
 	  return -EIO;
     }
-    io=0;
+
     return 0;
 }
 
 void
 cleanup_module(void)
 {
-    struct de4x5_private *lp = (struct de4x5_private *) thisDE4X5.priv;
-    struct device *p  = (struct device *)&thisDE4X5;
-    int keep_loaded = 0;
-    
-    for (; p!=NULL; p=p->next) {
-	keep_loaded += (p->flags & IFF_UP);     /* Is an interface up?       */
-    }
-    
-    if (keep_loaded) {
-	printk("de4x5: Cannot unload modules - %d interface%s%s still active.\n",
-	       keep_loaded, (keep_loaded>1 ? "s ": " "),
-	       (keep_loaded>1 ? "are": "is"));
-	return;
+    while (mdev != NULL) {
+	mdev = unlink_modules(mdev);
     }
-    
-    for (p=thisDE4X5.next; p!=NULL; p=p->next) {
-	if (p->priv) {                          /* Private area allocated?   */
-	    struct de4x5_private *lp = (struct de4x5_private *)p->priv;
-	    if (lp->cache.buf) {                /* MAC buffers allocated?    */
-		kfree(lp->cache.buf);           /* Free the MAC buffers      */
-	    }
-	    release_region(p->base_addr, (lp->bus == PCI ? 
-					  DE4X5_PCI_TOTAL_SIZE :
-					  DE4X5_EISA_TOTAL_SIZE));
-	    kfree(lp->cache.priv);              /* Free the private area     */
-	}
-	unregister_netdev(p);
-	kfree(p);                               /* Free the device structure */
-    }
-
-    if (thisDE4X5.priv) {
-	if (lp->cache.buf) {                    /* Are MAC buffers allocated */
-            kfree(lp->cache.buf);
-	}
-	release_region(thisDE4X5.base_addr,
-		      (lp->bus == PCI ? 
-		       DE4X5_PCI_TOTAL_SIZE :
-		       DE4X5_EISA_TOTAL_SIZE));
-	kfree(lp->cache.priv);
-	thisDE4X5.priv = NULL;
-    }
-    unregister_netdev(&thisDE4X5);
 
     return;
 }
+
+static struct device *
+unlink_modules(struct device *p)
+{
+    struct device *next = NULL;
+
+    if (p->priv) {                          /* Private areas allocated?  */
+	struct de4x5_private *lp = (struct de4x5_private *)p->priv;
+
+	next = lp->next_module;
+	if (lp->cache.buf) {                /* MAC buffers allocated?    */
+	    kfree(lp->cache.buf);           /* Free the MAC buffers      */
+	}
+	kfree(lp->cache.priv);              /* Free the private area     */
+	release_region(p->base_addr, (lp->bus == PCI ? 
+				      DE4X5_PCI_TOTAL_SIZE :
+				      DE4X5_EISA_TOTAL_SIZE));
+    }
+    unregister_netdev(p);
+    kfree(p);                               /* Free the device structure */
+    
+    return next;
+}
+
 #endif /* MODULE */
 
 
diff -u --recursive --new-file v2.0.30/linux/drivers/net/de4x5.h linux/drivers/net/de4x5.h
--- v2.0.30/linux/drivers/net/de4x5.h	Tue Aug 20 23:18:08 1996
+++ linux/drivers/net/de4x5.h	Tue Aug 12 13:21:12 1997
@@ -62,6 +62,8 @@
 #define PCI_CBER     iobase+0x0030   /* PCI Expansion ROM Base Address Reg. */
 #define PCI_CFIT     iobase+0x003c   /* PCI Configuration Interrupt Register */
 #define PCI_CFDA     iobase+0x0040   /* PCI Driver Area Register */
+#define PCI_CFDD     iobase+0x0041   /* PCI Driver Dependent Area Register */
+#define PCI_CFPM     iobase+0x0043   /* PCI Power Management Area Register */
 
 /*
 ** EISA Configuration Register 0 bit definitions
@@ -95,16 +97,20 @@
 #define ER3_LSR       0x02           /* Local Software Reset */
 
 /*
-** PCI Configuration ID Register (PCI_CFID)
+** PCI Configuration ID Register (PCI_CFID). The Device IDs are left
+** shifted 8 bits to allow detection of DC21142 and DC21143 variants with
+** the configuration revision register step number.
 */
 #define CFID_DID    0xff00           /* Device ID */
 #define CFID_VID    0x00ff           /* Vendor ID */
-#define DC21040_DID 0x0002           /* Unique Device ID # */
+#define DC21040_DID 0x0200           /* Unique Device ID # */
 #define DC21040_VID 0x1011           /* DC21040 Manufacturer */
-#define DC21041_DID 0x0014           /* Unique Device ID # */
+#define DC21041_DID 0x1400           /* Unique Device ID # */
 #define DC21041_VID 0x1011           /* DC21041 Manufacturer */
-#define DC21140_DID 0x0009           /* Unique Device ID # */
+#define DC21140_DID 0x0900           /* Unique Device ID # */
 #define DC21140_VID 0x1011           /* DC21140 Manufacturer */
+#define DC2114x_DID 0x1900           /* Unique Device ID # */
+#define DC2114x_VID 0x1011           /* DC2114[23] Manufacturer */
 
 /*
 ** Chipset defines
@@ -112,10 +118,16 @@
 #define DC21040     DC21040_DID
 #define DC21041     DC21041_DID
 #define DC21140     DC21140_DID
+#define DC2114x     DC2114x_DID
+#define DC21142     (DC2114x_DID | 0x0010)
+#define DC21143     (DC2114x_DID | 0x0020)
 
 #define is_DC21040 ((vendor == DC21040_VID) && (device == DC21040_DID))
 #define is_DC21041 ((vendor == DC21041_VID) && (device == DC21041_DID))
 #define is_DC21140 ((vendor == DC21140_VID) && (device == DC21140_DID))
+#define is_DC2114x ((vendor == DC2114x_VID) && (device == DC2114x_DID))
+#define is_DC21142 ((vendor == DC2114x_VID) && (device == DC21142))
+#define is_DC21143 ((vendor == DC2114x_VID) && (device == DC21143))
 
 /*
 ** PCI Configuration Command/Status Register (PCI_CFCS)
@@ -127,7 +139,7 @@
 #define CFCS_DST    0x06000000       /* DEVSEL Timing         (S) */
 #define CFCS_DPR    0x01000000       /* Data Parity Report    (S) */
 #define CFCS_FBB    0x00800000       /* Fast Back-To-Back     (S) */
-#define CFCS_SLE    0x00000100       /* System Error Enable   (C) */
+#define CFCS_SEE    0x00000100       /* System Error Enable   (C) */
 #define CFCS_PER    0x00000040       /* Parity Error Response (C) */
 #define CFCS_MO     0x00000004       /* Master Operation      (C) */
 #define CFCS_MSA    0x00000002       /* Memory Space Access   (C) */
@@ -138,8 +150,8 @@
 */
 #define CFRV_BC     0xff000000       /* Base Class */
 #define CFRV_SC     0x00ff0000       /* Subclass */
-#define CFRV_SN     0x000000f0       /* Step Number */
-#define CFRV_RN     0x0000000f       /* Revision Number */
+#define CFRV_RN     0x000000f0       /* Revision Number */
+#define CFRV_SN     0x0000000f       /* Step Number */
 #define BASE_CLASS  0x02000000       /* Indicates Network Controller */
 #define SUB_CLASS   0x00000000       /* Indicates Ethernet Controller */
 #define STEP_NUMBER 0x00000020       /* Increments for future chips */
@@ -158,19 +170,46 @@
 #define CBIO_IOSI   0x00000001       /* I/O Space Indicator (RO, value is 1) */
 
 /*
+** PCI Configuration Card Information Structure Register (PCI_CCIS)
+*/
+#define CCIS_ROMI   0xf0000000       /* ROM Image */
+#define CCIS_ASO    0x0ffffff8       /* Address Space Offset */
+#define CCIS_ASI    0x00000007       /* Address Space Indicator */
+
+/*
+** PCI Configuration Subsystem ID Register (PCI_SSID)
+*/
+#define SSID_SSID   0xffff0000       /* Subsystem ID */
+#define SSID_SVID   0x0000ffff       /* Subsystem Vendor ID */
+
+/*
 ** PCI Configuration Expansion ROM Base Address Register (PCI_CBER)
 */
 #define CBER_MASK   0xfffffc00       /* Expansion ROM Base Address Mask */
 #define CBER_ROME   0x00000001       /* ROM Enable */
 
 /*
-** PCI Configuration Driver Area Register (PCI_CFDA)
+** PCI Configuration Interrupt Register (PCI_CFIT)
 */
-#define CFDA_PSM    0x80000000       /* Power Saving Mode */
+#define CFIT_MXLT   0xff000000       /* MAX_LAT Value (0.25us periods) */
+#define CFIT_MNGT   0x00ff0000       /* MIN_GNT Value (0.25us periods) */
+#define CFIT_IRQP   0x0000ff00       /* Interrupt Pin */
+#define CFIT_IRQL   0x000000ff       /* Interrupt Line */
+
+/*
+** PCI Configuration Power Management Area Register (PCI_CFPM)
+*/
+#define SLEEP       0x80             /* Power Saving Sleep Mode */
+#define SNOOZE      0x40             /* Power Saving Snooze Mode */
+#define WAKEUP      0x00             /* Power Saving Wakeup */
+
+#define PCI_CFDA_DSU 0x41            /* 8 bit Configuration Space Address */
+#define PCI_CFDA_PSM 0x43            /* 8 bit Configuration Space Address */
 
 /*
 ** DC21040 Bus Mode Register (DE4X5_BMR)
 */
+#define BMR_RML    0x00200000       /* [Memory] Read Multiple */
 #define BMR_DBO    0x00100000       /* Descriptor Byte Ordering (Endian) */
 #define BMR_TAP    0x000e0000       /* Transmit Automatic Polling */
 #define BMR_DAS    0x00010000       /* Diagnostic Address Space */
@@ -181,6 +220,7 @@
 #define BMR_BAR    0x00000002       /* Bus ARbitration */
 #define BMR_SWR    0x00000001       /* Software Reset */
 
+                                    /* Timings here are for 10BASE-T/AUI only*/
 #define TAP_NOPOLL 0x00000000       /* No automatic polling */
 #define TAP_200US  0x00020000       /* TX automatic polling every 200us */
 #define TAP_800US  0x00040000       /* TX automatic polling every 800us */
@@ -232,18 +272,21 @@
 #define TRBA       0xfffffffc       /* TX Descriptor List Start Address */
 
 /*
-** DC21040 Status Register (DE4X5_STS)
+** Status Register (DE4X5_STS)
 */
+#define STS_GPI    0x04000000       /* General Purpose Port Interrupt */
 #define STS_BE     0x03800000       /* Bus Error Bits */
 #define STS_TS     0x00700000       /* Transmit Process State */
 #define STS_RS     0x000e0000       /* Receive Process State */
 #define STS_NIS    0x00010000       /* Normal Interrupt Summary */
 #define STS_AIS    0x00008000       /* Abnormal Interrupt Summary */
 #define STS_ER     0x00004000       /* Early Receive */
+#define STS_FBE    0x00002000       /* Fatal Bus Error */
 #define STS_SE     0x00002000       /* System Error */
 #define STS_LNF    0x00001000       /* Link Fail */
 #define STS_FD     0x00000800       /* Full-Duplex Short Frame Received */
 #define STS_TM     0x00000800       /* Timer Expired (DC21041) */
+#define STS_ETI    0x00000400       /* Early Transmit Interupt */
 #define STS_AT     0x00000400       /* AUI/TP Pin */
 #define STS_RWT    0x00000200       /* Receive Watchdog Time-Out */
 #define STS_RPS    0x00000100       /* Receive Process Stopped */
@@ -251,6 +294,7 @@
 #define STS_RI     0x00000040       /* Receive Interrupt */
 #define STS_UNF    0x00000020       /* Transmit Underflow */
 #define STS_LNP    0x00000010       /* Link Pass */
+#define STS_ANC    0x00000010       /* Autonegotiation Complete */
 #define STS_TJT    0x00000008       /* Transmit Jabber Time-Out */
 #define STS_TU     0x00000004       /* Transmit Buffer Unavailable */
 #define STS_TPS    0x00000002       /* Transmit Process Stopped */
@@ -283,8 +327,10 @@
 #define INT_CANCEL 0x0001ffff       /* For zeroing all interrupt sources */
 
 /*
-** DC21040 Operation Mode Register (DE4X5_OMR)
+** Operation Mode Register (DE4X5_OMR)
 */
+#define OMR_SC     0x80000000       /* Special Capture Effect Enable */
+#define OMR_RA     0x40000000       /* Receive All */
 #define OMR_SDP    0x02000000       /* SD Polarity - MUST BE ASSERTED */
 #define OMR_SCR    0x01000000       /* Scrambler Mode */
 #define OMR_PCS    0x00800000       /* PCS Function */
@@ -298,7 +344,7 @@
 #define OMR_ST     0x00002000       /* Start/Stop Transmission Command */
 #define OMR_FC     0x00001000       /* Force Collision Mode */
 #define OMR_OM     0x00000c00       /* Operating Mode */
-#define OMR_FD     0x00000200       /* Full Duplex Mode */
+#define OMR_FDX    0x00000200       /* Full Duplex Mode */
 #define OMR_FKD    0x00000100       /* Flaky Oscillator Disable */
 #define OMR_PM     0x00000080       /* Pass All Multicast */
 #define OMR_PR     0x00000040       /* Promiscuous Mode */
@@ -309,27 +355,31 @@
 #define OMR_SR     0x00000002       /* Start/Stop Receive */
 #define OMR_HP     0x00000001       /* Hash/Perfect Receive Filtering Mode */
 
-#define TR_72      0x00000000       /* Threshold set to 72 bytes */
-#define TR_96      0x00004000       /* Threshold set to 96 bytes */
-#define TR_128     0x00008000       /* Threshold set to 128 bytes */
-#define TR_160     0x0000c000       /* Threshold set to 160 bytes */
+#define TR_72      0x00000000       /* Threshold set to 72 (128) bytes */
+#define TR_96      0x00004000       /* Threshold set to 96 (256) bytes */
+#define TR_128     0x00008000       /* Threshold set to 128 (512) bytes */
+#define TR_160     0x0000c000       /* Threshold set to 160 (1024) bytes */
 
 /*
 ** DC21040 Interrupt Mask Register (DE4X5_IMR)
 */
+#define IMR_GPM    0x04000000       /* General Purpose Port Mask */
 #define IMR_NIM    0x00010000       /* Normal Interrupt Summary Mask */
 #define IMR_AIM    0x00008000       /* Abnormal Interrupt Summary Mask */
 #define IMR_ERM    0x00004000       /* Early Receive Mask */
+#define IMR_FBM    0x00002000       /* Fatal Bus Error Mask */
 #define IMR_SEM    0x00002000       /* System Error Mask */
 #define IMR_LFM    0x00001000       /* Link Fail Mask */
 #define IMR_FDM    0x00000800       /* Full-Duplex (Short Frame) Mask */
 #define IMR_TMM    0x00000800       /* Timer Expired Mask (DC21041) */
+#define IMR_ETM    0x00000400       /* Early Transmit Interrupt Mask */
 #define IMR_ATM    0x00000400       /* AUI/TP Switch Mask */
 #define IMR_RWM    0x00000200       /* Receive Watchdog Time-Out Mask */
 #define IMR_RSM    0x00000100       /* Receive Stopped Mask */
 #define IMR_RUM    0x00000080       /* Receive Buffer Unavailable Mask */
 #define IMR_RIM    0x00000040       /* Receive Interrupt Mask */
 #define IMR_UNM    0x00000020       /* Underflow Interrupt Mask */
+#define IMR_ANM    0x00000010       /* Autonegotiation Complete Mask */
 #define IMR_LPM    0x00000010       /* Link Pass */
 #define IMR_TJM    0x00000008       /* Transmit Time-Out Jabber Mask */
 #define IMR_TUM    0x00000004       /* Transmit Buffer Unavailable Mask */
@@ -337,13 +387,7 @@
 #define IMR_TIM    0x00000001       /* Transmit Interrupt Mask */
 
 /*
-** DC21040 Missed Frames Counter (DE4X5_MFC)
-*/
-#define MFC_OVFL   0x00010000       /* Missed Frames Counter Overflow Bit */
-#define MFC_CNTR   0x0000ffff       /* Missed Frames Counter Bits */
-
-/*
-** DC21140 Missed Frames and FIFO Overflow Counters (DE4X5_MFC)
+** Missed Frames and FIFO Overflow Counters (DE4X5_MFC)
 */
 #define MFC_FOCO   0x10000000       /* FIFO Overflow Counter Overflow Bit */
 #define MFC_FOC    0x0ffe0000       /* FIFO Overflow Counter Bits */
@@ -444,7 +488,7 @@
 ** MII Management Auto Negotiation Advertisement Register
 */
 #define MII_ANA_TAF  0x03e0        /* Technology Ability Field */
-#define MII_ANA_T4AM 0x0400        /* T4 Technology Ability Mask */
+#define MII_ANA_T4AM 0x0200        /* T4 Technology Ability Mask */
 #define MII_ANA_TXAM 0x0180        /* TX Technology Ability Mask */
 #define MII_ANA_FDAM 0x0140        /* Full Duplex Technology Ability Mask */
 #define MII_ANA_HDAM 0x02a0        /* Half Duplex Technology Ability Mask */
@@ -459,7 +503,7 @@
 #define MII_ANLPA_ACK  0x4000      /* Remote Acknowledge */
 #define MII_ANLPA_RF   0x2000      /* Remote Fault */
 #define MII_ANLPA_TAF  0x03e0      /* Technology Ability Field */
-#define MII_ANLPA_T4AM 0x0400      /* T4 Technology Ability Mask */
+#define MII_ANLPA_T4AM 0x0200      /* T4 Technology Ability Mask */
 #define MII_ANLPA_TXAM 0x0180      /* TX Technology Ability Mask */
 #define MII_ANLPA_FDAM 0x0140      /* Full Duplex Technology Ability Mask */
 #define MII_ANLPA_HDAM 0x02a0      /* Half Duplex Technology Ability Mask */
@@ -478,6 +522,76 @@
 #define MEDIA_BNC      0x0001      /* BNC Media present */
 
 /*
+** SROM Definitions (Digital Semiconductor Format)
+*/
+#define SROM_SSVID     0x0000      /* Sub-system Vendor ID offset */
+#define SROM_SSID      0x0002      /* Sub-system ID offset */
+#define SROM_CISPL     0x0004      /* CardBus CIS Pointer low offset */
+#define SROM_CISPH     0x0006      /* CardBus CIS Pointer high offset */
+#define SROM_IDCRC     0x0010      /* ID Block CRC offset*/
+#define SROM_RSVD2     0x0011      /* ID Reserved 2 offset */
+#define SROM_SFV       0x0012      /* SROM Format Version offset */
+#define SROM_CCNT      0x0013      /* Controller Count offset */
+#define SROM_HWADD     0x0014      /* Hardware Address offset */
+#define SROM_MRSVD     0x007c      /* Manufacturer Reserved offset*/
+#define SROM_CRC       0x007e      /* SROM CRC offset */
+
+/*
+** SROM Media Connection Definitions
+*/
+#define SROM_10BT      0x0000      /*  10BASE-T half duplex */
+#define SROM_10BTN     0x0100      /*  10BASE-T with Nway */
+#define SROM_10BTF     0x0204      /*  10BASE-T full duplex */
+#define SROM_10BTNLP   0x0400      /*  10BASE-T without Link Pass test */
+#define SROM_10B2      0x0001      /*  10BASE-2 (BNC) */
+#define SROM_10B5      0x0002      /*  10BASE-5 (AUI) */
+#define SROM_100BTH    0x0003      /*  100BASE-T half duplex */
+#define SROM_100BTF    0x0205      /*  100BASE-T full duplex */
+#define SROM_100BT4    0x0006      /*  100BASE-T4 */
+#define SROM_100BFX    0x0007      /*  100BASE-FX half duplex (Fiber) */
+#define SROM_M10BT     0x0009      /*  MII 10BASE-T half duplex */
+#define SROM_M10BTF    0x020a      /*  MII 10BASE-T full duplex */
+#define SROM_M100BT    0x000d      /*  MII 100BASE-T half duplex */
+#define SROM_M100BTF   0x020e      /*  MII 100BASE-T full duplex */
+#define SROM_M100BT4   0x000f      /*  MII 100BASE-T4 */
+#define SROM_M100BF    0x0010      /*  MII 100BASE-FX half duplex */
+#define SROM_M100BFF   0x0211      /*  MII 100BASE-FX full duplex */
+#define SROM_PDA       0x0800      /*  Powerup & Dynamic Autosense */
+#define SROM_PAO       0x8800      /*  Powerup Autosense Only */
+#define SROM_NSMI      0xffff      /*  No Selected Media Information */
+
+/*
+** SROM Media Definitions
+*/
+#define SROM_10BASET   0x0000      /*  10BASE-T half duplex */
+#define SROM_10BASE2   0x0001      /*  10BASE-2 (BNC) */
+#define SROM_10BASE5   0x0002      /*  10BASE-5 (AUI) */
+#define SROM_100BASET  0x0003      /*  100BASE-T half duplex */
+#define SROM_10BASETF  0x0004      /*  10BASE-T full duplex */
+#define SROM_100BASETF 0x0005      /*  100BASE-T full duplex */
+#define SROM_100BASET4 0x0006      /*  100BASE-T4 */
+#define SROM_100BASEF  0x0007      /*  100BASE-FX half duplex */
+#define SROM_100BASEFF 0x0008      /*  100BASE-FX full duplex */
+
+#define BLOCK_LEN      0x7f        /* Extended blocks length mask */
+#define EXT_FIELD      0x40        /* Extended blocks extension field bit */
+#define MEDIA_CODE     0x3f        /* Extended blocks media code mask */
+
+/*
+** SROM Compact Format Block Masks
+*/
+#define COMPACT_FI      0x80       /* Format Indicator */
+#define COMPACT_LEN     0x04       /* Length */
+#define COMPACT_MC      0x3f       /* Media Code */
+
+/*
+** SROM Extended Format Block Type 0 Masks
+*/
+#define BLOCK0_FI      0x80        /* Format Indicator */
+#define BLOCK0_MCS     0x80        /* Media Code byte Sign */
+#define BLOCK0_MC      0x3f        /* Media Code */
+
+/*
 ** DC21040 Full Duplex Register (DE4X5_FDR)
 */
 #define FDR_FDACV  0x0000ffff      /* Full Duplex Auto Configuration Value */
@@ -501,17 +615,21 @@
 #define GEP_FLED 0x00000002        /* Force Activity LED on   (output) */
 #define GEP_MODE 0x00000001        /* 0: 10Mb/s,  1: 100Mb/s           */
 #define GEP_INIT 0x0000011f        /* Setup inputs (0) and outputs (1) */
-
+#define GEP_CTRL 0x00000100        /* GEP control bit                  */
 
 /*
-** DC21040 SIA Status Register (DE4X5_SISR)
+** SIA Status Register (DE4X5_SISR)
 */
 #define SISR_LPC   0xffff0000      /* Link Partner's Code Word */
 #define SISR_LPN   0x00008000      /* Link Partner Negotiable */
 #define SISR_ANS   0x00007000      /* Auto Negotiation Arbitration State */
-#define SISR_NSN   0x00000800      /* Non Stable NLPs Detected */
+#define SISR_NSN   0x00000800      /* Non Stable NLPs Detected (DC21041) */
+#define SISR_TRF   0x00000800      /* Transmit Remote Fault */
+#define SISR_NSND  0x00000400      /* Non Stable NLPs Detected (DC21142) */
 #define SISR_ANR_FDS 0x00000400    /* Auto Negotiate Restart/Full Duplex Sel.*/
+#define SISR_TRA   0x00000200      /* 10BASE-T Receive Port Activity */
 #define SISR_NRA   0x00000200      /* Non Selected Port Receive Activity */
+#define SISR_ARA   0x00000100      /* AUI Receive Port Activity */
 #define SISR_SRA   0x00000100      /* Selected Port Receive Activity */
 #define SISR_DAO   0x00000080      /* PLL All One */
 #define SISR_DAZ   0x00000040      /* PLL All Zero */
@@ -521,7 +639,7 @@
 #define SISR_LKF   0x00000004      /* Link Fail Status */
 #define SISR_NCR   0x00000002      /* Network Connection Error */
 #define SISR_PAUI  0x00000001      /* AUI_TP Indication */
-#define SIA_RESET  0x00000000      /* SIA Reset */
+#define SISR_MRA   0x00000001      /* MII Receive Port Activity */
 
 #define ANS_NDIS   0x00000000      /* Nway disable */
 #define ANS_TDIS   0x00001000      /* Transmit Disable */
@@ -532,7 +650,7 @@
 #define ANS_LCHK   0x00006000      /* Link Check */
 
 /*
-** DC21040 SIA Connectivity Register (DE4X5_SICR)
+** SIA Connectivity Register (DE4X5_SICR)
 */
 #define SICR_SDM   0xffff0000       /* SIA Diagnostics Mode */
 #define SICR_OE57  0x00008000       /* Output Enable 5 6 7 */
@@ -551,14 +669,14 @@
 #define SICR_SIM   0x00000040       /* Serial Interface Input Multiplexer */
 #define SICR_ENI   0x00000020       /* Encoder Input Multiplexer */
 #define SICR_EDP   0x00000010       /* SIA PLL External Input Enable */
-#define SICR_AUI   0x00000008       /* 10Base-T or AUI */
+#define SICR_AUI   0x00000008       /* 10Base-T (0) or AUI (1) */
 #define SICR_CAC   0x00000004       /* CSR Auto Configuration */
 #define SICR_PS    0x00000002       /* Pin AUI/TP Selection */
 #define SICR_SRL   0x00000001       /* SIA Reset */
-#define SICR_RESET 0xffff0000       /* Reset value for SICR */
+#define SIA_RESET  0x00000000       /* SIA Reset Value */
 
 /*
-** DC21040 SIA Transmit and Receive Register (DE4X5_STRR)
+** SIA Transmit and Receive Register (DE4X5_STRR)
 */
 #define STRR_TAS   0x00008000       /* 10Base-T/AUI Autosensing Enable */
 #define STRR_SPP   0x00004000       /* Set Polarity Plus */
@@ -578,8 +696,20 @@
 #define STRR_RESET 0xffffffff       /* Reset value for STRR */
 
 /*
-** DC21040 SIA General Register (DE4X5_SIGR)
+** SIA General Register (DE4X5_SIGR)
 */
+#define SIGR_RMI   0x40000000       /* Receive Match Interrupt */
+#define SIGR_GI1   0x20000000       /* General Port Interrupt 1 */
+#define SIGR_GI0   0x10000000       /* General Port Interrupt 0 */
+#define SIGR_CWE   0x08000000       /* Control Write Enable */
+#define SIGR_RME   0x04000000       /* Receive Match Enable */
+#define SIGR_GEI1  0x02000000       /* GEP Interrupt Enable on Port 1 */
+#define SIGR_GEI0  0x01000000       /* GEP Interrupt Enable on Port 0 */
+#define SIGR_LGS3  0x00800000       /* LED/GEP3 Select */
+#define SIGR_LGS2  0x00400000       /* LED/GEP2 Select */
+#define SIGR_LGS1  0x00200000       /* LED/GEP1 Select */
+#define SIGR_LGS0  0x00100000       /* LED/GEP0 Select */
+#define SIGR_MD    0x000f0000       /* General Purpose Mode and Data */
 #define SIGR_LV2   0x00008000       /* General Purpose LED2 value */
 #define SIGR_LE2   0x00004000       /* General Purpose LED2 enable */
 #define SIGR_FRL   0x00002000       /* Force Receiver Low */
@@ -602,7 +732,8 @@
 ** Receive Descriptor Bit Summary
 */
 #define R_OWN      0x80000000       /* Own Bit */
-#define RD_FL      0x7fff0000       /* Frame Length */
+#define RD_FF      0x40000000       /* Filtering Fail */
+#define RD_FL      0x3fff0000       /* Frame Length */
 #define RD_ES      0x00008000       /* Error Summary */
 #define RD_LE      0x00004000       /* Length Error */
 #define RD_DT      0x00003000       /* Data Type */
@@ -614,6 +745,7 @@
 #define RD_CS      0x00000040       /* Collision Seen */
 #define RD_FT      0x00000020       /* Frame Type */
 #define RD_RJ      0x00000010       /* Receive Watchdog */
+#define RD_RE      0x00000008       /* Report on MII Error */
 #define RD_DB      0x00000004       /* Dribbling Bit */
 #define RD_CE      0x00000002       /* CRC Error */
 #define RD_OF      0x00000001       /* Overflow */
@@ -649,40 +781,53 @@
 #define TD_TCH     0x01000000       /* Second Address Chained */
 #define TD_DPD     0x00800000       /* Disabled Padding */
 #define TD_FT0     0x00400000       /* Filtering Type */
-#define TD_RBS2    0x003ff800       /* Buffer 2 Size */
-#define TD_RBS1    0x000007ff       /* Buffer 1 Size */
+#define TD_TBS2    0x003ff800       /* Buffer 2 Size */
+#define TD_TBS1    0x000007ff       /* Buffer 1 Size */
 
 #define PERFECT_F  0x00000000
 #define HASH_F     TD_FT0
 #define INVERSE_F  TD_FT1
-#define HASH_O_F   TD_FT1| TD_F0
+#define HASH_O_F   (TD_FT1 | TD_F0)
 
 /*
 ** Media / mode state machine definitions
 */
-#define NC              0x0000     /* No Connection */
-#define TP              0x0001     /* 10Base-T */
-#define TP_NW           0x0002     /* 10Base-T with Nway */
-#define BNC             0x0004     /* Thinwire */
-#define AUI             0x0008     /* Thickwire */
+#define NC              0x0000     /* No Connection                        */
+#define TP              0x0001     /* 10Base-T                             */
+#define TP_NW           0x0002     /* 10Base-T with Nway                   */
+#define BNC             0x0004     /* Thinwire                             */
+#define AUI             0x0008     /* Thickwire                            */
 #define BNC_AUI         0x0010     /* BNC/AUI on DC21040 indistinguishable */
-#define ANS             0x0020     /* Intermediate AutoNegotiation State */
-#define ANS_1           0x0021     /* Intermediate AutoNegotiation State */
-
-#define _10Mb           0x0040     /* 10Mb/s Ethernet */
-#define _100Mb          0x0080     /* 100Mb/s Ethernet */
-#define SPD_DET         0x0100     /* Parallel speed detection */
-#define INIT            0x0200     /* Initial state */
-#define EXT_SIA         0x0400     /* External SIA for motherboard chip */
-#define ANS_SUSPECT     0x0802     /* Suspect the ANS (TP) port is down */
-#define TP_SUSPECT      0x0803     /* Suspect the TP port is down */
-#define BNC_AUI_SUSPECT 0x0804     /* Suspect the BNC or AUI port is down */
-#define EXT_SIA_SUSPECT 0x0805     /* Suspect the EXT SIA port is down */
-#define BNC_SUSPECT     0x0806     /* Suspect the BNC port is down */
-#define AUI_SUSPECT     0x0807     /* Suspect the AUI port is down */
-
-#define AUTO            0x4000     /* Auto sense the media or speed */
-#define TIMER_CB        0x80000000 /* Timer callback detection */
+#define ANS             0x0020     /* Intermediate AutoNegotiation State   */
+#define _10Mb           0x0040     /* 10Mb/s Ethernet                      */
+#define _100Mb          0x0080     /* 100Mb/s Ethernet                     */
+#define SPD_DET         0x0100     /* Parallel speed detection             */
+#define INIT            0x0200     /* Initial state                        */
+#define EXT_SIA         0x0400     /* External SIA for motherboard chip    */
+#define ANS_SUSPECT     0x0802     /* Suspect the ANS (TP) port is down    */
+#define TP_SUSPECT      0x0803     /* Suspect the TP port is down          */
+#define BNC_AUI_SUSPECT 0x0804     /* Suspect the BNC or AUI port is down  */
+#define EXT_SIA_SUSPECT 0x0805     /* Suspect the EXT SIA port is down     */
+#define BNC_SUSPECT     0x0806     /* Suspect the BNC port is down         */
+#define AUI_SUSPECT     0x0807     /* Suspect the AUI port is down         */
+#define MII             0x1000     /* MII on the 21143                     */
+
+#define AUTO            0x4000     /* Auto sense the media or speed        */
+#define TIMER_CB        0x80000000 /* Timer callback detection             */
+
+/*
+** DE4X5 DEBUG Options
+*/
+#define DEBUG_NONE      0x0000     /* No DEBUG messages */
+#define DEBUG_VERSION   0x0001     /* Print version message */
+#define DEBUG_MEDIA     0x0002     /* Print media messages */
+#define DEBUG_TX        0x0004     /* Print TX (queue_pkt) messages */
+#define DEBUG_RX        0x0008     /* Print RX (de4x5_rx) messages */
+#define DEBUG_SROM      0x0010     /* Print SROM messages */
+#define DEBUG_MII       0x0020     /* Print MII messages */
+#define DEBUG_OPEN      0x0040     /* Print de4x5_open() messages */
+#define DEBUG_CLOSE     0x0080     /* Print de4x5_close() messages */
+#define DEBUG_PCICFG    0x0100
 
 /*
 ** Miscellaneous
@@ -699,7 +844,6 @@
 #define POLL_DEMAND          1
 
 #define LOST_MEDIA_THRESHOLD 3
-#define LOST_MEDIA           (lp->lostMedia > LOST_MEDIA_THRESHOLD)
 
 #define MASK_INTERRUPTS      1
 #define UNMASK_INTERRUPTS    0
@@ -728,11 +872,16 @@
 */
 #define NO                   0
 #define FALSE                0
-#define CLOSED               0
 
 #define YES                  ~0
 #define TRUE                 ~0
-#define OPEN                 ~0
+
+/*
+** Adapter state
+*/
+#define INITIALISED          0     /* After h/w initialised and mem alloc'd */
+#define CLOSED               1     /* Ready for opening */
+#define OPEN                 2     /* Running */
 
 /*
 ** IEEE OUIs for various PHY vendor/chip combos - Reg 2 values only. Since
@@ -748,55 +897,67 @@
 ** Speed Selection stuff
 */
 #define SET_10Mb {\
-  if (lp->phy[lp->active].id) {\
-    omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FD);\
+  if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\
+    omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX);\
     if ((lp->tmp != MII_SR_ASSC) || (lp->autosense != AUTO)) {\
-      mii_wr(MII_CR_10|(de4x5_full_duplex?MII_CR_FDM:0), MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\
+      mii_wr(MII_CR_10|(lp->fdx?MII_CR_FDM:0), MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\
     }\
-    omr |= ((de4x5_full_duplex ? OMR_FD : 0) | OMR_TTM);\
+    omr |= ((lp->fdx ? OMR_FDX : 0) | OMR_TTM);\
     outl(omr, DE4X5_OMR);\
-    outl(0, DE4X5_GEP);\
+    if (!lp->useSROM) lp->cache.gep = 0;\
+  } else if (lp->useSROM && !lp->useMII) {\
+    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\
+    omr |= (lp->fdx ? OMR_FDX : 0);\
+    outl(omr | lp->infoblock_csr6, DE4X5_OMR);\
   } else {\
-    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FD));\
-    omr |= (de4x5_full_duplex ? OMR_FD : 0);\
+    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\
+    omr |= (lp->fdx ? OMR_FDX : 0);\
     outl(omr | OMR_TTM, DE4X5_OMR);\
-    outl((de4x5_full_duplex ? 0 : GEP_FDXD), DE4X5_GEP);\
+    lp->cache.gep = (lp->fdx ? 0 : GEP_FDXD);\
   }\
 }
 
 #define SET_100Mb {\
-  if (lp->phy[lp->active].id) {\
+  if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\
     int fdx=0;\
     if (lp->phy[lp->active].id == NATIONAL_TX) {\
         mii_wr(mii_rd(0x18, lp->phy[lp->active].addr, DE4X5_MII) & ~0x2000,\
                       0x18, lp->phy[lp->active].addr, DE4X5_MII);\
     }\
-    omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FD);\
+    omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX);\
     sr = mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII);\
-    if (!(sr & MII_ANA_T4AM) && de4x5_full_duplex) fdx=1;\
+    if (!(sr & MII_ANA_T4AM) && lp->fdx) fdx=1;\
     if ((lp->tmp != MII_SR_ASSC) || (lp->autosense != AUTO)) {\
       mii_wr(MII_CR_100|(fdx?MII_CR_FDM:0), MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\
     }\
-    if (fdx) omr |= OMR_FD;\
+    if (fdx) omr |= OMR_FDX;\
     outl(omr, DE4X5_OMR);\
+    if (!lp->useSROM) lp->cache.gep = 0;\
+  } else if (lp->useSROM && !lp->useMII) {\
+    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\
+    omr |= (lp->fdx ? OMR_FDX : 0);\
+    outl(omr | lp->infoblock_csr6 | OMR_HBD, DE4X5_OMR);\
   } else {\
-    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FD));\
-    omr |= (de4x5_full_duplex ? OMR_FD : 0);\
+    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\
+    omr |= (lp->fdx ? OMR_FDX : 0);\
     outl(omr | OMR_PS | OMR_HBD | OMR_PCS | OMR_SCR, DE4X5_OMR);\
-    outl((de4x5_full_duplex ? 0 : GEP_FDXD) | GEP_MODE, DE4X5_GEP);\
+    lp->cache.gep = (lp->fdx ? 0 : GEP_FDXD) | GEP_MODE;\
   }\
 }
 
 /* FIX ME so I don't jam 10Mb networks */
 #define SET_100Mb_PDET {\
-  if (lp->phy[lp->active].id) {\
+  if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\
     mii_wr(MII_CR_100|MII_CR_ASSE, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\
-    omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FD));\
+    omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\
+    outl(omr, DE4X5_OMR);\
+  } else if (lp->useSROM && !lp->useMII) {\
+    omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\
     outl(omr, DE4X5_OMR);\
   } else {\
-    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FD));\
+    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\
     outl(omr | OMR_PS | OMR_HBD | OMR_PCS | OMR_SCR, DE4X5_OMR);\
-    outl(GEP_FDXD | GEP_MODE, DE4X5_GEP);\
+    lp->cache.gep = (GEP_FDXD | GEP_MODE);\
   }\
 }
 
@@ -817,7 +978,7 @@
 ** Recognised commands for the driver 
 */
 #define DE4X5_GET_HWADDR	0x01 /* Get the hardware address */
-#define DE4X5_SET_HWADDR	0x02 /* Get the hardware address */
+#define DE4X5_SET_HWADDR	0x02 /* Set the hardware address */
 #define DE4X5_SET_PROM  	0x03 /* Set Promiscuous Mode */
 #define DE4X5_CLR_PROM  	0x04 /* Clear Promiscuous Mode */
 #define DE4X5_SAY_BOO	        0x05 /* Say "Boo!" to the kernel log file */
diff -u --recursive --new-file v2.0.30/linux/drivers/net/dgrs.c linux/drivers/net/dgrs.c
--- v2.0.30/linux/drivers/net/dgrs.c	Sat Dec 21 07:22:33 1996
+++ linux/drivers/net/dgrs.c	Tue Aug 12 16:05:23 1997
@@ -97,7 +97,6 @@
 #include <linux/skbuff.h>
 
 #include <linux/types.h>
-#include <linux/config.h>	/* for CONFIG_PCI */
 
 /*
  *	API changed at linux version 2.1.0
diff -u --recursive --new-file v2.0.30/linux/drivers/net/eepro100.c linux/drivers/net/eepro100.c
--- v2.0.30/linux/drivers/net/eepro100.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/eepro100.c	Fri Sep  5 11:20:20 1997
@@ -0,0 +1,1754 @@
+/* drivers/net/eepro100.c: An Intel i82557 ethernet driver for linux. */
+/*
+   NOTICE: this version tested with kernels 1.3.72 and later only!
+	Written 1996-1997 by Donald Becker.
+
+	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 Intel EtherExpress Pro 100B boards.
+	It should work with other i82557 boards (if any others exist).
+	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.
+
+	The author may be reached as becker@CESDIS.usra.edu, or C/O
+	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">
+*/
+
+static const char *version =
+"eepro100.c:v0.34 8/30/97 Donald Becker linux-eepro100@cesdis.gsfc.nasa.gov\n";
+
+/* A few user-configurable values that apply to all boards.
+   First set are undocumented and spelled per Intel recommendations. */
+
+static int congenb = 0;		/* Enable congestion control in the DP83840. */
+static int txfifo = 8;		/* Tx FIFO threshold in 4 byte units, 0-15 */
+static int rxfifo = 8;		/* Rx FIFO threshold, default 32 bytes. */
+static int txdmacount = 0;	/* Tx DMA burst length, 0-127, default 0. */
+static int rxdmacount = 0;	/* Rx DMA length, 0 means no preemption. */
+
+/* If defined use the copy-only-tiny-buffer scheme for higher performance.
+   The value sets the copy breakpoint.  Lower uses more memory, but is
+   faster. */
+#define SKBUFF_RX_COPYBREAK 256
+
+#include <linux/config.h>
+#ifdef MODULE
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
+#include <linux/module.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+#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. */
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+/* A nominally proper method to handle version dependencies is to use
+   LINUX_VERSION_CODE in version.h, but that triggers recompiles w/'make'. */
+#define VERSION(v,p,s) (((v)<<16)+(p<<8)+s)
+#ifdef MODULE
+#if (LINUX_VERSION_CODE < VERSION(1,3,0))
+#define KERNEL_1_2
+#else /* 1.3.0 */
+#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
+#define NEW_MULTICAST
+#define LINUX_1_4
+#else
+#warning "This driver is tested for 1.3.44 and later development kernels only."
+#endif /* 1.3.44 */
+#endif
+#else
+
+#if (LINUX_VERSION_CODE >= 0x10344)
+#define NEW_MULTICAST
+#include <linux/delay.h>
+#endif
+
+#ifdef HAVE_HEADER_CACHE
+#define LINUX_1_4
+#define NEW_MULTICAST
+#else
+#ifdef ETH_P_DDCMP				/* Warning: Bogus!  This means IS_LINUX_1_3. */
+#define KERNEL_1_3
+#else
+#define KERNEL_1_2
+#endif
+#endif
+
+#endif
+/* This should be in a header file. */
+#if (LINUX_VERSION_CODE < VERSION(1,3,44))
+struct device *init_etherdev(struct device *dev, int sizeof_priv,
+							 unsigned long *mem_startp);
+#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)
+#define virt_to_bus(addr)  ((unsigned long)addr)
+#define bus_to_virt(addr) ((void*)addr)
+#else  /* 1.3.0 and later */
+#define RUN_AT(x) (jiffies + (x))
+#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
+#endif
+
+#if (LINUX_VERSION_CODE < 0x20123)
+#define test_and_set_bit(val, addr) set_bit(val, addr)
+#endif
+
+/* The total I/O port extent of the board.  Nominally 0x18, but rounded up
+   for PCI allocation. */
+#define SPEEDO3_TOTAL_SIZE 0x20
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry eepro100_drv =
+{"EEPro-100", eepro100_init, SPEEDO3_TOTAL_SIZE, NULL};
+#endif
+
+#ifdef SPEEDO3_DEBUG
+int speedo_debug = SPEEDO3_DEBUG;
+#else
+int speedo_debug = 3;
+#endif
+
+/*
+				Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the Intel i82557 "Speedo3" chip, Intel's
+single-chip fast ethernet controller for PCI, as used on the Intel
+EtherExpress Pro 100 adapter.
+
+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
+possible to share PCI interrupt lines, it negatively impacts performance and
+only recent kernels support it.
+
+III. Driver operation
+
+IIIA. General
+The Speedo3 is very similar to other Intel network chips, that is to say
+"apparently designed on a different planet".  This chips retains the complex
+Rx and Tx descriptors and multiple buffers pointers as previous chips, but
+also has simplified Tx and Rx buffer modes.  This driver uses the "flexible"
+Tx mode, but in a simplified lower-overhead manner: it associates only a
+single buffer descriptor with each frame descriptor.
+
+Despite the extra space overhead in each recieve 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
+
+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
+pointer fields are absolute addresses.
+
+The System Control Block (SCB) of some previous Intel chips exists on the
+chip in both PCI I/O and memory space.  This driver uses the I/O space
+registers, but might switch to memory mapped mode to better support non-x86
+processors.
+
+IIIB. Transmit structure
+
+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
+(TxBD).  A fixed ring of these TxCB+TxBD pairs are kept as part of the
+speedo_private data structure for each adapter instance.
+
+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
+already loaded the link address in the previous descriptor.  So for these
+commands we convert the next free descriptor on the ring to a NoOp, and point
+that descriptor's link to the complex command.
+
+An additional complexity of these non-transmit commands are that they may be
+added asynchronous to the normal transmit queue, so we disable interrupts
+whenever the Tx descriptor ring is manipulated.
+
+A notable aspect of the these special configure commands is that they do
+work with the normal Tx ring entry scavenge method.  The Tx ring scavenge
+is done at interrupt time using the 'dirty_tx' index, and checking for the
+command-complete bit.  While the setup frames may have the NoOp command on the
+Tx ring marked as complete, but not have completed the setup command, this
+is not a problem.  The tx_ring entry can be still safely reused, as the
+tx_skbuff[] entry is always empty for config_cmd and mc_setup frames.
+
+Commands may have bits set e.g. CmdSuspend in the command word to either
+suspend or stop the transmit/command unit.  This driver always flags the last
+command with CmdSuspend, erases the CmdSuspend in the previous command, and
+then issues a CU_RESUME.
+Note: Watch out for the potential race condition here: imagine
+	erasing the previous suspend
+		the chip processes the previous command
+		the chip processes the final command, and suspends
+	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
+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.
+
+IIIC. Receive structure
+
+Because of the bus-master support on the Speedo3 this driver uses the new
+SKBUFF_RX_COPYBREAK scheme, rather than a fixed intermediate receive buffer.
+This scheme allocates full-sized skbuffs as receive buffers.  The value
+SKBUFF_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 copying cost of copying a frame to a
+correctly-sized skbuff.
+
+For small frames the copying cost is negligible (esp. considering that we
+are pre-loading the cache with immediately useful header information), so we
+allocate a new, minimally-sized skbuff.  For large frames the copying cost
+is non-trivial, and the larger copy might flush the cache of useful data, so
+we pass up the skbuff the packet was received into.
+
+IIID. Synchronization
+The driver runs as two independent, single-threaded flows of control.  One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag.  The other thread is the interrupt handler, which is single
+threaded by the hardware and other software.
+
+The send packet thread has partial control over the Tx ring and 'dev->tbusy'
+flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next
+queue slot is empty, it clears the tbusy flag when finished otherwise it sets
+the 'sp->tx_full' flag.
+
+The interrupt handler has exclusive control over the Rx ring and records stats
+from the Tx ring.  (The Tx-done interrupt can't be selectively turned off, so
+we can't avoid the interrupt overhead by having the Tx routine reap the Tx
+stats.)	 After reaping the stats, it marks the queue entry as empty by setting
+the 'base' to zero.	 Iff the 'sp->tx_full' flag is set, it clears both the
+tx_full and tbusy flags.
+
+IV. Notes
+
+Thanks to Steve Williams of Intel for arranging the non-disclosure agreement
+that stated that I could disclose the information.  But I still resent
+having to sign an Intel NDA when I'm helping Intel sell their own product!
+
+*/
+
+/* A few values that may be tweaked. */
+/* The ring sizes should be a power of two for efficiency. */
+#define TX_RING_SIZE	16		/* Effectively 2 entries fewer. */
+#define RX_RING_SIZE	16
+/* Size of an pre-allocated Rx buffer: <Ethernet MTU> + slack.*/
+#define PKT_BUF_SZ		1536
+
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  ((400*HZ)/1000)
+
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+#define INTR_WORK	16
+
+/* 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)
+{
+  short wait = 100;
+  do   ;
+  while(inb(cmd_ioaddr) && --wait >= 0);
+}
+
+/* Operational parameter that usually are not changed. */
+
+#ifndef PCI_VENDOR_ID_INTEL		/* Now defined in linux/pci.h */
+#define PCI_VENDOR_ID_INTEL		0x8086 /* Hmmmm, how did they pick that? */
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82557
+#define PCI_DEVICE_ID_INTEL_82557	0x1229
+#endif
+
+/* The rest of these values should never change. */
+
+/* Offsets to the various registers.
+   All accesses need not be longword aligned. */
+enum speedo_offsets {
+	SCBStatus = 0, SCBCmd = 2,	/* Rx/Command Unit command and status. */
+	SCBPointer = 4,				/* General purpose pointer. */
+	SCBPort = 8,				/* Misc. commands and operands.  */
+	SCBflash = 12, SCBeeprom = 14, /* EEPROM and flash memory control. */
+	SCBCtrlMDI = 16,			/* MDI interface control. */
+	SCBEarlyRx = 20,			/* Early receive byte count. */
+};
+/* Commands that can be put in a command list entry. */
+enum commands {
+	CmdNOp = 0, CmdIASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
+	CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7,
+	CmdSuspend = 0x4000,		/* Suspend after completion. */
+	CmdIntr = 0x2000,			/* Interrupt after completion. */
+	CmdTxFlex = 0x0008,			/* Use "Flexible mode" for CmdTx command. */
+};
+
+/* The SCB accepts the following controls for the Tx and Rx units: */
+#define	 CU_START		0x0010
+#define	 CU_RESUME		0x0020
+#define	 CU_STATSADDR	0x0040
+#define	 CU_SHOWSTATS	0x0050	/* Dump statistics counters. */
+#define	 CU_CMD_BASE	0x0060	/* Base address to add to add CU commands. */
+#define	 CU_DUMPSTATS	0x0070	/* Dump then reset stats counters. */
+
+#define	 RX_START	0x0001
+#define	 RX_RESUME	0x0002
+#define	 RX_ABORT	0x0004
+#define	 RX_ADDR_LOAD	0x0006
+#define	 RX_RESUMENR	0x0007
+#define INT_MASK	0x0100
+#define DRVR_INT	0x0200		/* Driver generated interrupt. */
+
+/* The Speedo3 Rx and Tx frame/buffer descriptors. */
+struct descriptor {			/* A generic descriptor. */
+	s16 status;		/* Offset 0. */
+	s16 command;		/* Offset 2. */
+	u32 link;					/* struct descriptor *  */
+	unsigned char params[0];
+};
+
+/* The Speedo3 Rx and Tx buffer descriptors. */
+struct RxFD {					/* Receive frame descriptor. */
+	s32 status;
+	u32 link;					/* struct RxFD * */
+	u32 rx_buf_addr;			/* void * */
+	u16 count;
+	u16 size;
+};
+
+/* Elements of the RxFD.status word. */
+#define RX_COMPLETE 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. */
+};
+
+/* Elements of the dump_statistics block. This block must be lword aligned. */
+struct speedo_stats {
+	u32 tx_good_frames;
+	u32 tx_coll16_errs;
+	u32 tx_late_colls;
+	u32 tx_underruns;
+	u32 tx_lost_carrier;
+	u32 tx_deferred;
+	u32 tx_one_colls;
+	u32 tx_multi_colls;
+	u32 tx_total_colls;
+	u32 rx_good_frames;
+	u32 rx_crc_errs;
+	u32 rx_align_errs;
+	u32 rx_resource_errs;
+	u32 rx_overrun_errs;
+	u32 rx_colls_errs;
+	u32 rx_runt_errs;
+	u32 done_marker;
+};
+
+struct speedo_private {
+	char devname[8];			/* Used only for kernel debugging. */
+	const char *product_name;
+	struct device *next_module;
+	struct TxFD	tx_ring[TX_RING_SIZE];	/* Commands (usually CmdTxPacket). */
+	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
+	struct sk_buff* tx_skbuff[TX_RING_SIZE];
+	struct descriptor  *last_cmd;	/* Last command sent. */
+	/* Rx descriptor ring & addresses of receive-in-place skbuffs. */
+	struct RxFD *rx_ringp[RX_RING_SIZE];
+	struct sk_buff* rx_skbuff[RX_RING_SIZE];
+#if (LINUX_VERSION_CODE < 0x10300)	/* Kernel v1.2.*. */
+	struct RxFD saved_skhead[RX_RING_SIZE];	/* Saved skbuff header chunk. */
+#endif
+	struct RxFD *last_rxf;	/* Last command sent. */
+	struct enet_statistics stats;
+	struct speedo_stats lstats;
+	struct timer_list timer;	/* Media selection timer. */
+	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. */
+	char rx_mode;						/* Current PROMISC/ALLMULTI setting. */
+	unsigned int tx_full:1;				/* The Tx queue is full. */
+	unsigned int full_duplex:1;			/* Full-duplex operation requested. */
+	unsigned int default_port:1;		/* Last dev->if_port value. */
+	unsigned int rx_bug:1;				/* Work around receiver hang errata. */
+	unsigned int rx_bug10:1;			/* Receiver might hang at 10mbps. */
+	unsigned int rx_bug100:1;			/* Receiver might hang at 100mbps. */
+	unsigned short phy[2];				/* PHY media interfaces available. */
+};
+
+/* 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] = {
+	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, };
+
+/* PHY media interface chips. */
+static const char *phys[] = {
+	"None", "i82553-A/B", "i82553-C", "i82503",
+	"DP83840", "80c240", "80c24", "i82555",
+	"unknown-8", "unknown-9", "DP83840A", "unknown-11",
+	"unknown-12", "unknown-13", "unknown-14", "unknown-15", };
+enum phy_chips { NonSuchPhy=0, I82553AB, I82553C, I82503, DP83840, S80C240,
+					 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,
+						  int options, 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 speedo_open(struct device *dev);
+static void speedo_timer(unsigned long data);
+static void speedo_init_rx_ring(struct device *dev);
+static int speedo_start_xmit(struct sk_buff *skb, struct device *dev);
+static int speedo_rx(struct device *dev);
+#ifdef SA_SHIRQ
+static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
+#else
+static void speedo_interrupt(int irq, struct pt_regs *regs);
+#endif
+static int speedo_close(struct device *dev);
+static struct enet_statistics *speedo_get_stats(struct device *dev);
+static void set_rx_mode(struct device *dev);
+
+
+
+/* The parameters that may be passed in... */
+/* 'options' is used to pass a transceiver override or full-duplex flag
+   e.g. "options=16" for FD, "options=32" for 100mbps-only. */
+static int full_duplex[] = {-1, -1, -1, -1, -1, -1, -1, -1};
+#ifdef MODULE
+static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int debug = -1;			/* The debug level */
+#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;
+
+	if (pcibios_present()) {
+		int pci_index;
+		for (pci_index = 0; pci_index < 8; pci_index++) {
+			unsigned char pci_bus, pci_device_fn, pci_irq_line, pci_latency;
+#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
+			int pci_ioaddr;
+#else
+			long pci_ioaddr;
+#endif
+			unsigned short pci_command;
+
+			if (pcibios_find_device(PCI_VENDOR_ID_INTEL,
+									PCI_DEVICE_ID_INTEL_82557,
+									pci_index, &pci_bus,
+									&pci_device_fn))
+			  break;
+			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);
+			if ( ! (pci_command & PCI_COMMAND_MASTER)) {
+				printk("  PCI Master Bit has not been set! Setting...\n");
+				pci_command |= PCI_COMMAND_MASTER;
+				pcibios_write_config_word(pci_bus, pci_device_fn,
+										  PCI_COMMAND, pci_command);
+			}
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+										 PCI_LATENCY_TIMER, &pci_latency);
+			if (pci_latency < 10) {
+				printk("  PCI latency timer (CFLT) is unreasonably low at %d."
+					   "  Setting to 255 clocks.\n", pci_latency);
+				pcibios_write_config_byte(pci_bus, pci_device_fn,
+										  PCI_LATENCY_TIMER, 255);
+			} else if (speedo_debug > 1)
+				printk("  PCI latency timer (CFLT) is %#x.\n", pci_latency);
+
+#ifdef MODULE
+			speedo_found1(dev, pci_ioaddr, pci_irq_line, options[cards_found],
+						  cards_found);
+#else
+			speedo_found1(dev, pci_ioaddr, pci_irq_line,
+						  dev ? dev->mem_start : 0, -1);
+#endif
+			dev = NULL;
+			cards_found++;
+		}
+	}
+
+	return cards_found;
+}
+
+static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
+						  int card_idx)
+{
+	static int did_version = 0;			/* Already printed version info. */
+	struct speedo_private *sp;
+	int i;
+	u16 eeprom[0x40];
+
+	if (speedo_debug > 0  &&  did_version++ == 0)
+		printk(version);
+
+#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
+	dev = init_etherdev(dev, sizeof(struct speedo_private));
+#else
+	dev = init_etherdev(dev, sizeof(struct speedo_private), 0);
+#endif
+
+	/* Read the station address EEPROM before doing the reset.
+	   Perhaps this should even be done before accepting the device,
+	   then we wouldn't have a device name with which to report the error. */
+	{
+		u16 sum = 0;
+		int j;
+		for (j = 0, i = 0; i < 0x40; i++) {
+			unsigned short value = read_eeprom(ioaddr, i);
+			eeprom[i] = value;
+			sum += value;
+			if (i < 3) {
+				dev->dev_addr[j++] = value;
+				dev->dev_addr[j++] = value >> 8;
+			}
+		}
+		if (sum != 0xBABA)
+			printk(KERN_WARNING "%s: Invalid EEPROM checksum %#4.4x, "
+				   "check settings before activating this device!\n",
+				   dev->name, sum);
+		/* Don't  unregister_netdev(dev);  as the EEPro may actually be
+		   usable, especially if the MAC address is set later. */
+	}
+
+	/* Reset the chip: stop Tx and Rx processes and clear counters.
+	   This takes less than 10usec and will easily finish before the next
+	   action. */
+	outl(0, ioaddr + SCBPort);
+
+	printk(KERN_INFO "%s: Intel EtherExpress Pro 10/100 at %#3x, ",
+		   dev->name, ioaddr);
+	for (i = 0; i < 5; i++)
+		printk("%2.2X:", dev->dev_addr[i]);
+	printk("%2.2X, IRQ %d.\n", dev->dev_addr[i], irq);
+
+#ifndef kernel_bloat
+	/* OK, this is pure kernel bloat.  I don't like it when other drivers
+	   waste non-pageable kernel space to emit similar messages, but I need
+	   them for bug reports. */
+	{
+		const char *connectors[] = {" RJ45", " BNC", " AUI", " MII"};
+		/* The self-test results must be paragraph aligned. */
+		int str[6], *volatile self_test_results;
+		int boguscnt = 16000;	/* Timeout for set-test. */
+		if (eeprom[3] & 0x03)
+			printk(KERN_INFO "  Receiver lock-up bug exists -- enabling"
+				   " work-around.\n");
+		printk(KERN_INFO "  Board assembly %4.4x%2.2x-%3.3d, Physical"
+			   " connectors present:",
+			   eeprom[8], eeprom[9]>>8, eeprom[9] & 0xff);
+		for (i = 0; i < 4; i++)
+			if (eeprom[5] & (1<<i))
+				printk(connectors[i]);
+		printk("\n"KERN_INFO"  Primary interface chip %s PHY #%d.\n",
+			   phys[(eeprom[6]>>8)&15], eeprom[6] & 0x1f);
+		if (eeprom[7] & 0x0700)
+			printk(KERN_INFO "    Secondary interface chip %s.\n",
+				   phys[(eeprom[7]>>8)&7]);
+#if defined(notdef)
+		/* ToDo: Read and set PHY registers through MDIO port. */
+		for (i = 0; i < 2; i++)
+			printk(KERN_INFO"  MDIO register %d is %4.4x.\n",
+				   i, mdio_read(ioaddr, eeprom[6] & 0x1f, i));
+		for (i = 5; i < 7; i++)
+			printk(KERN_INFO"  MDIO register %d is %4.4x.\n",
+				   i, mdio_read(ioaddr, eeprom[6] & 0x1f, i));
+		printk(KERN_INFO"  MDIO register %d is %4.4x.\n",
+			   25, mdio_read(ioaddr, eeprom[6] & 0x1f, 25));
+#endif
+		if (((eeprom[6]>>8) & 0x3f) == DP83840
+			||  ((eeprom[6]>>8) & 0x3f) == DP83840A) {
+			int mdi_reg23 = mdio_read(ioaddr, eeprom[6] & 0x1f, 23) | 0x0422;
+			if (congenb)
+			  mdi_reg23 |= 0x0100;
+			printk(KERN_INFO"  DP83840 specific setup, setting register 23 to %4.4x.\n",
+				   mdi_reg23);
+			mdio_write(ioaddr, eeprom[6] & 0x1f, 23, mdi_reg23);
+		}
+		if ((options >= 0) && (options & 0x60)) {
+			printk(KERN_INFO "  Forcing %dMbs %s-duplex operation.\n",
+				   (options & 0x20 ? 100 : 10),
+				   (options & 0x10 ? "full" : "half"));
+			mdio_write(ioaddr, eeprom[6] & 0x1f, 0,
+					   ((options & 0x20) ? 0x2000 : 0) | 	/* 100mbps? */
+					   ((options & 0x10) ? 0x0100 : 0)); /* Full duplex? */
+		}
+
+		/* Perform a system self-test. */
+		self_test_results = (int*) ((((int) str) + 15) & ~0xf);
+		self_test_results[0] = 0;
+		self_test_results[1] = -1;
+		outl(virt_to_bus(self_test_results) | 1, ioaddr + SCBPort);
+		do {
+#ifdef _LINUX_DELAY_H
+			udelay(10);
+#else
+			SLOW_DOWN_IO;
+#endif
+		} while (self_test_results[1] == -1  &&  --boguscnt >= 0);
+
+		if (boguscnt < 0) {		/* Test optimized out. */
+			printk(KERN_ERR "Self test failed, status %8.8x:\n"
+				   KERN_ERR " Failure to initialize the i82557.\n"
+				   KERN_ERR " Verify that the card is a bus-master"
+				   " capable slot.\n",
+				   self_test_results[1]);
+		} else
+			printk(KERN_INFO "  General self-test: %s.\n"
+				   KERN_INFO "  Serial sub-system self-test: %s.\n"
+				   KERN_INFO "  Internal registers self-test: %s.\n"
+				   KERN_INFO "  ROM checksum self-test: %s (%#8.8x).\n",
+				   self_test_results[1] & 0x1000 ? "failed" : "passed",
+				   self_test_results[1] & 0x0020 ? "failed" : "passed",
+				   self_test_results[1] & 0x0008 ? "failed" : "passed",
+				   self_test_results[1] & 0x0004 ? "failed" : "passed",
+				   self_test_results[0]);
+	}
+#endif  /* kernel_bloat */
+
+	/* We do a request_region() only to register /proc/ioports info. */
+	request_region(ioaddr, SPEEDO3_TOTAL_SIZE, "Intel Speedo3 Ethernet");
+
+	dev->base_addr = ioaddr;
+	dev->irq = irq;
+
+	if (dev->priv == NULL)
+		dev->priv = kmalloc(sizeof(*sp), GFP_KERNEL);
+	sp = dev->priv;
+	memset(sp, 0, sizeof(*sp));
+#ifdef MODULE
+	sp->next_module = root_speedo_dev;
+	root_speedo_dev = dev;
+#endif
+
+	if (card_idx >= 0) {
+		if (full_duplex[card_idx] >= 0)
+			sp->full_duplex = full_duplex[card_idx];
+	} else
+		sp->full_duplex = options >= 0 && (options & 0x10) ? 1 : 0;
+	sp->default_port = options >= 0 ? (options & 0x0f) : 0;
+
+	sp->phy[0] = eeprom[6];
+	sp->phy[1] = eeprom[7];
+	sp->rx_bug = (eeprom[3] & 0x03) == 3 ? 0 : 1;
+
+	printk(KERN_INFO "  Operating in %s duplex mode.\n",
+		   sp->full_duplex ? "full" : "half");
+	if (sp->rx_bug)
+	  printk(KERN_INFO "  Reciever lock-up workaround activated.\n");
+
+	/* The Speedo-specific entries in the device structure. */
+	dev->open = &speedo_open;
+	dev->hard_start_xmit = &speedo_start_xmit;
+	dev->stop = &speedo_close;
+	dev->get_stats = &speedo_get_stats;
+#ifdef NEW_MULTICAST
+	dev->set_multicast_list = &set_rx_mode;
+#endif
+
+	return;
+}
+
+/* Serial EEPROM section.
+   A "bit" grungy, but we work our way through bit-by-bit :->. */
+/*  EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK	0x01	/* EEPROM shift clock. */
+#define EE_CS			0x02	/* EEPROM chip select. */
+#define EE_DATA_WRITE	0x04	/* EEPROM chip data in. */
+#define EE_WRITE_0		0x01
+#define EE_WRITE_1		0x05
+#define EE_DATA_READ	0x08	/* EEPROM chip data out. */
+#define EE_ENB			(0x4800 | EE_CS)
+
+/* Delay between EEPROM clock transitions.
+   This is a "nasty" timing loop, but PC compatible machines are defined
+   to delay an ISA compatible period for the SLOW_DOWN_IO macro.  */
+#define eeprom_delay(nanosec)	do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
+
+/* 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)
+{
+	int i;
+	unsigned short retval = 0;
+	int ee_addr = ioaddr + SCBeeprom;
+	int read_cmd = location | EE_READ_CMD;
+
+	outw(EE_ENB & ~EE_CS, ee_addr);
+	outw(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;
+		outw(EE_ENB | dataval, ee_addr);
+		eeprom_delay(100);
+		outw(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
+		eeprom_delay(150);
+		outw(EE_ENB | dataval, ee_addr);	/* Finish EEPROM a clock tick. */
+		eeprom_delay(250);
+	}
+	outw(EE_ENB, ee_addr);
+
+	for (i = 15; i >= 0; i--) {
+		outw(EE_ENB | EE_SHIFT_CLK, ee_addr);
+		eeprom_delay(100);
+		retval = (retval << 1) | ((inw(ee_addr) & EE_DATA_READ) ? 1 : 0);
+		outw(EE_ENB, ee_addr);
+		eeprom_delay(100);
+	}
+
+	/* Terminate the EEPROM access. */
+	outw(EE_ENB & ~EE_CS, ee_addr);
+	return retval;
+}
+
+static int mdio_read(int ioaddr, int phy_id, int location)
+{
+	int val, boguscnt = 64*4;		/* <64 usec. to complete, typ 27 ticks */
+	outl(0x08000000 | (location<<16) | (phy_id<<21), ioaddr + SCBCtrlMDI);
+	do {
+#ifdef _LINUX_DELAY_H
+		udelay(16);
+#else
+		SLOW_DOWN_IO;
+#endif
+		val = inl(ioaddr + SCBCtrlMDI);
+		if (--boguscnt < 0) {
+			printk(KERN_ERR " mdio_read() timed out with val = %8.8x.\n", val);
+		}
+	} while (! (val & 0x10000000));
+	return val & 0xffff;
+}
+
+static int mdio_write(int ioaddr, int phy_id, int location, int value)
+{
+	int val, boguscnt = 64*4;		/* <64 usec. to complete, typ 27 ticks */
+	outl(0x04000000 | (location<<16) | (phy_id<<21) | value,
+		 ioaddr + SCBCtrlMDI);
+	do {
+#ifdef _LINUX_DELAY_H
+		udelay(16);
+#else
+		SLOW_DOWN_IO;
+#endif
+		val = inl(ioaddr + SCBCtrlMDI);
+		if (--boguscnt < 0) {
+			printk(KERN_ERR" mdio_write() timed out with val = %8.8x.\n", val);
+		}
+	} while (! (val & 0x10000000));
+	return val & 0xffff;
+}
+
+
+static int
+speedo_open(struct device *dev)
+{
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+
+#ifdef notdef
+	/* We could reset the chip, but should not need to. */
+	outl(0, ioaddr + SCBPort);
+	for (i = 40; i >= 0; i--)
+		SLOW_DOWN_IO;			/* At least 250ns */
+#endif
+
+#ifdef SA_SHIRQ
+	if (request_irq(dev->irq, &speedo_interrupt, SA_SHIRQ,
+					"Intel EtherExpress Pro 10/100 Ethernet", dev)) {
+		return -EAGAIN;
+	}
+#else
+#ifdef USE_SHARED_IRQ
+	if (request_shared_irq(dev->irq, &speedo_interrupt, dev,
+						   "Intel EtherExpress Pro 10/100 Ethernet"))
+		return -EAGAIN;
+#else
+	if (dev->irq < 2  ||  dev->irq > 15  ||  irq2dev_map[dev->irq] != NULL)
+		return -EAGAIN;
+	irq2dev_map[dev->irq] = dev;
+	if (request_irq(dev->irq, &speedo_interrupt, 0, "Intel EtherExpress Pro 10/100 Ethernet")) {
+		irq2dev_map[dev->irq] = NULL;
+		return -EAGAIN;
+	}
+#endif
+#endif
+
+	if (speedo_debug > 1)
+		printk(KERN_DEBUG "%s: speedo_open() irq %d.\n", dev->name, dev->irq);
+
+	MOD_INC_USE_COUNT;
+
+	/* Load the statistics block address. */
+	outl(virt_to_bus(&sp->lstats), ioaddr + SCBPointer);
+	outw(INT_MASK | CU_STATSADDR, ioaddr + SCBCmd);
+	sp->lstats.done_marker = 0;
+
+	speedo_init_rx_ring(dev);
+	outl(0, ioaddr + SCBPointer);
+	outw(INT_MASK | RX_ADDR_LOAD, ioaddr + SCBCmd);
+
+	/* Todo: verify that we must wait for previous command completion. */
+	wait_for_cmd_done(ioaddr + SCBCmd);
+	outl(virt_to_bus(sp->rx_ringp[0]), ioaddr + SCBPointer);
+	outw(INT_MASK | RX_START, ioaddr + SCBCmd);
+
+	/* Fill the first command with our physical address. */
+	{
+		unsigned short *eaddrs = (unsigned short *)dev->dev_addr;
+		unsigned short *setup_frm = (short *)&(sp->tx_ring[0].tx_desc_addr);
+
+		/* Avoid a bug(?!) here by marking the command already completed. */
+		sp->tx_ring[0].status = ((CmdSuspend | CmdIASetup) << 16) | 0xa000;
+		sp->tx_ring[0].link = virt_to_bus(&(sp->tx_ring[1]));
+		*setup_frm++ = eaddrs[0];
+		*setup_frm++ = eaddrs[1];
+		*setup_frm++ = eaddrs[2];
+	}
+	sp->last_cmd = (struct descriptor *)&sp->tx_ring[0];
+	sp->cur_tx = 1;
+	sp->dirty_tx = 0;
+	sp->tx_full = 0;
+
+	outl(0, ioaddr + SCBPointer);
+	outw(INT_MASK | CU_CMD_BASE, ioaddr + SCBCmd);
+
+	dev->if_port = sp->default_port;
+
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+
+	/* Start the chip's Tx process and unmask interrupts. */
+	/* Todo: verify that we must wait for previous command completion. */
+	wait_for_cmd_done(ioaddr + SCBCmd);
+	outl(virt_to_bus(&sp->tx_ring[0]), ioaddr + SCBPointer);
+	outw(CU_START, ioaddr + SCBCmd);
+
+	/* Setup the chip and configure the multicast list. */
+	sp->mc_setup_frm = NULL;
+	sp->mc_setup_frm_len = 0;
+	sp->rx_mode = -1;			/* Invalid -> always reset the mode. */
+	set_rx_mode(dev);
+
+	if (speedo_debug > 2) {
+		printk(KERN_DEBUG "%s: Done speedo_open(), status %8.8x.\n",
+			   dev->name, inw(ioaddr + SCBStatus));
+	}
+	/* Set the timer.  The timer serves a dual purpose:
+	   1) to monitor the media interface (e.g. link beat) and perhaps switch
+	   to an alternate media type
+	   2) to monitor Rx activity, and restart the Rx process if the receiver
+	   hangs. */
+	init_timer(&sp->timer);
+	sp->timer.expires = RUN_AT((24*HZ)/10); 			/* 2.4 sec. */
+	sp->timer.data = (unsigned long)dev;
+	sp->timer.function = &speedo_timer;					/* timer handler */
+	add_timer(&sp->timer);
+
+	outw(CU_DUMPSTATS, ioaddr + SCBCmd);
+	return 0;
+}
+
+/* Media monitoring and control. */
+static void speedo_timer(unsigned long data)
+{
+	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",
+			   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);
+	}
+}
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void
+speedo_init_rx_ring(struct device *dev)
+{
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	struct RxFD *rxf, *last_rxf = NULL;
+	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;
+#ifndef KERNEL_1_2
+		skb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD));
+#else
+		skb = alloc_skb(PKT_BUF_SZ, GFP_ATOMIC);
+#endif
+		sp->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
+		rxf = (struct RxFD *)skb->tail;
+		skb_reserve(skb, sizeof(struct RxFD));
+#else
+		/* Save the data in the header region -- it's restored later. */
+		rxf = (struct RxFD *)(skb->data - sizeof(struct RxFD));
+		memcpy(&sp->saved_skhead[i], rxf, sizeof(struct RxFD));
+#endif
+		sp->rx_ringp[i] = rxf;
+		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. */
+#if LINUX_VERSION_CODE < 0x10300
+		/* This field unused by i82557, we use it as a consistency check. */
+		rxf->rx_buf_addr = virt_to_bus(skb->data);
+#else
+		rxf->rx_buf_addr = virt_to_bus(skb->tail);
+#endif
+		rxf->count = 0;
+		rxf->size = PKT_BUF_SZ;
+	}
+	/* Mark the last entry as end-of-list. */
+	last_rxf->status = 0xC0000002; 			/* '2' is flag value only. */
+	sp->last_rxf = last_rxf;
+}
+
+static void speedo_tx_timeout(struct device *dev)
+{
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+	int i;
+
+	printk(KERN_WARNING "%s: Transmit timed out: status %4.4x "
+		   "command %4.4x.\n",
+		   dev->name, inw(ioaddr + SCBStatus), inw(ioaddr + SCBCmd));
+#ifndef final_version
+	printk(KERN_WARNING "%s:  Tx timeout  fill index %d  scavenge index %d.\n",
+		   dev->name, sp->cur_tx, sp->dirty_tx);
+	printk(KERN_WARNING "    Tx queue ");
+	for (i = 0; i < TX_RING_SIZE; i++)
+	  printk(" %8.8x", (int)sp->tx_ring[i].status);
+	printk(".\n" KERN_WARNING "    Rx ring ");
+	for (i = 0; i < RX_RING_SIZE; i++)
+	  printk(" %8.8x", (int)sp->rx_ringp[i]->status);
+	printk(".\n");
+
+#else
+	dev->if_port ^= 1;
+	printk(KERN_WARNING "  (Media type switching not yet implemented.)\n");
+	/* Do not do 'dev->tbusy = 0;' there -- it is incorrect. */
+#endif
+	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);
+	} else {
+	  outw(DRVR_INT, ioaddr + SCBCmd);
+	}
+	/* Reset the MII transceiver. */
+	if ((sp->phy[0] & 0x8000) == 0)
+		mdio_write(ioaddr, sp->phy[0] & 0x1f, 0, 0x8000);
+	sp->stats.tx_errors++;
+	dev->trans_start = jiffies;
+	return;
+}
+
+static int
+speedo_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+	int entry;
+
+	if (skb == NULL || skb->len <= 0) {
+		printk(KERN_ERR "%s: Obsolete driver layer request made: skbuff==NULL.\n",
+			   dev->name);
+		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.
+	   If this ever occurs the queue layer is doing something evil! */
+	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
+		int tickssofar = jiffies - dev->trans_start;
+		if (tickssofar < TX_TIMEOUT - 2)
+			return 1;
+		if (tickssofar < TX_TIMEOUT) {
+			/* Reap sent packets from the full Tx queue. */
+			outw(DRVR_INT, ioaddr + SCBCmd);
+			return 1;
+		}
+		speedo_tx_timeout(dev);
+		return 0;
+	}
+
+	/* Caution: the write order is important here, set the base address
+	   with the "ownership" bits last. */
+
+	{	/* Prevent interrupts from changing the Tx ring from underneath us. */
+		unsigned long flags;
+
+		save_flags(flags);
+		cli();
+		/* Calculate the Tx descriptor entry. */
+		entry = sp->cur_tx++ % TX_RING_SIZE;
+
+		sp->tx_skbuff[entry] = skb;
+		/* Todo: be a little more clever about setting the interrupt bit. */
+		sp->tx_ring[entry].status =
+			(CmdSuspend | CmdTx | CmdTxFlex) << 16;
+		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);
+		/* 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;
+		/* 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];
+		/* Trigger the command unit resume. */
+		outw(CU_RESUME, ioaddr + SCBCmd);
+		restore_flags(flags);
+	}
+
+	/* Leave room for set_rx_mode() to fill two entries. */
+	if (sp->cur_tx - sp->dirty_tx > TX_RING_SIZE - 3)
+		sp->tx_full = 1;
+	else
+		dev->tbusy = 0;
+
+	dev->trans_start = jiffies;
+
+	return 0;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+   after the Tx thread. */
+#ifdef SA_SHIRQ
+static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
+#else
+static void speedo_interrupt(int irq, struct pt_regs *regs)
+#endif
+{
+#ifdef SA_SHIRQ
+	struct device *dev = (struct device *)dev_instance;
+#else
+#ifdef USE_SHARED_IRQ
+	struct device *dev = (struct device *)(irq == 0 ? regs : irq2dev_map[irq]);
+#else
+	struct device *dev = (struct device *)(irq2dev_map[irq]);
+#endif
+#endif
+	struct speedo_private *sp;
+	int ioaddr, boguscnt = INTR_WORK;
+	unsigned short status;
+
+#ifndef final_version
+	if (dev == NULL) {
+		printk(KERN_ERR "speedo_interrupt(): irq %d for unknown device.\n", irq);
+		return;
+	}
+#endif
+
+	ioaddr = dev->base_addr;
+	sp = (struct speedo_private *)dev->priv;
+#ifndef final_version
+	if (dev->interrupt) {
+		printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
+		return;
+	}
+	dev->interrupt = 1;
+#endif
+
+	do {
+		status = inw(ioaddr + SCBStatus);
+		/* Acknowledge all of the current interrupt sources ASAP. */
+		outw(status & 0xfc00, ioaddr + SCBStatus);
+
+		if (speedo_debug > 4)
+			printk(KERN_DEBUG "%s: interrupt  status=%#4.4x.\n",
+				   dev->name, status);
+
+		if ((status & 0xfc00) == 0)
+			break;
+
+		if (status & 0x4000)	 /* Packet received. */
+			speedo_rx(dev);
+
+		if (status & 0x1000) {
+#ifdef notdef
+		  int i;
+		  printk(KERN_WARNING"%s: The EEPro100 receiver left the ready"
+				 " state -- %4.4x!  Index %d (%d).\n", dev->name, status,
+				 sp->cur_rx, sp->cur_rx % RX_RING_SIZE);
+		  printk(KERN_WARNING "   Rx ring:\n ");
+		  for (i = 0; i < RX_RING_SIZE; i++)
+			printk("   %d %8.8x %8.8x %8.8x %d %d.\n",
+				   i, sp->rx_ringp[i]->status, sp->rx_ringp[i]->link,
+				   sp->rx_ringp[i]->rx_buf_addr, sp->rx_ringp[i]->count,
+				   sp->rx_ringp[i]->size);
+#endif
+
+		  if ((status & 0x003c) == 0x0028) /* No more Rx buffers. */
+			outw(RX_RESUMENR, ioaddr + SCBCmd);
+		  else if ((status & 0x003c) == 0x0008) { /* No resources (why?!) */
+			/* No idea of what went wrong.  Restart the receiver. */
+			outl(virt_to_bus(sp->rx_ringp[sp->cur_rx % RX_RING_SIZE]),
+				 ioaddr + SCBPointer);
+			outw(RX_START, ioaddr + SCBCmd);
+		  }
+		  sp->stats.rx_errors++;
+		}
+
+		/* User interrupt, Command/Tx unit interrupt or CU not active. */
+		if (status & 0xA400) {
+			unsigned int dirty_tx = sp->dirty_tx;
+
+			while (sp->cur_tx - dirty_tx > 0) {
+				int entry = dirty_tx % TX_RING_SIZE;
+				int status = sp->tx_ring[entry].status;
+
+				if (speedo_debug > 5)
+					printk(KERN_DEBUG " scavenge canidate %d status %4.4x.\n",
+						   entry, status);
+				if ((status & 0x8000) == 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);
+					sp->tx_skbuff[entry] = 0;
+				}
+				dirty_tx++;
+			}
+
+#ifndef final_version
+			if (sp->cur_tx - dirty_tx > TX_RING_SIZE) {
+				printk(KERN_ERR "out-of-sync dirty pointer, %d vs. %d,"
+					   " full=%d.\n",
+					   dirty_tx, sp->cur_tx, sp->tx_full);
+				dirty_tx += TX_RING_SIZE;
+			}
+#endif
+
+			if (sp->tx_full && dev->tbusy
+				&& dirty_tx > sp->cur_tx - TX_RING_SIZE + 2) {
+				/* The ring is no longer full, clear tbusy. */
+				sp->tx_full = 0;
+				dev->tbusy = 0;
+				mark_bh(NET_BH);
+			}
+
+			sp->dirty_tx = dirty_tx;
+		}
+
+		if (--boguscnt < 0) {
+			printk(KERN_ERR "%s: Too much work at interrupt, status=0x%4.4x.\n",
+				   dev->name, status);
+			/* Clear all interrupt sources. */
+			outl(0xfc00, ioaddr + SCBStatus);
+			break;
+		}
+	} while (1);
+
+	if (speedo_debug > 3)
+		printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
+			   dev->name, inw(ioaddr + SCBStatus));
+
+#ifndef final_version
+	/* Special code for testing *only*. */
+	{
+		static int stopit = 100;
+		if (dev->start == 0  &&  --stopit < 0) {
+			printk(KERN_ALERT "%s: Emergency stop, interrupt is stuck.\n",
+				   dev->name);
+#ifdef SA_SHIRQ
+			free_irq(irq, dev);
+#else
+			free_irq(irq);
+#endif
+		}
+	}
+#endif
+
+	dev->interrupt = 0;
+	return;
+}
+
+static int
+speedo_rx(struct device *dev)
+{
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	int entry = sp->cur_rx % RX_RING_SIZE;
+	int status;
+
+	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) {
+
+		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);
+		} else {
+			/* Malloc up new buffer, compatible with net-2e. */
+			short 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 > SKBUFF_RX_COPYBREAK) {
+				struct sk_buff *newskb;
+				char *temp;
+
+				/* Pass up the skb already on the Rx ring. */
+				skb = sp->rx_skbuff[entry];
+#ifdef KERNEL_1_2
+				temp = skb->data;
+				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: %p vs. %p / %p.\n", dev->name,
+						   bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr),
+						   temp, skb->data);
+				/* Get a fresh skbuff to replace the filled one. */
+				newskb = alloc_skb(PKT_BUF_SZ, GFP_ATOMIC);
+#else
+				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));
+#endif
+				if (newskb) {
+					struct RxFD *rxf;
+					rx_in_place = 1;
+					sp->rx_skbuff[entry] = newskb;
+					newskb->dev = dev;
+#ifdef KERNEL_1_2
+					/* Restore the data in the old header region. */
+					memcpy(skb->data - sizeof(struct RxFD),
+						   &sp->saved_skhead[entry], sizeof(struct RxFD));
+					/* Save the data in this header region. */
+					rxf = (struct RxFD *)(newskb->data - sizeof(struct RxFD));
+					sp->rx_ringp[entry] = rxf;
+					memcpy(&sp->saved_skhead[entry], rxf, sizeof(struct RxFD));
+					rxf->rx_buf_addr = virt_to_bus(newskb->data);
+#else
+					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);
+#endif
+					rxf->status = 0x00000001;
+				} else			/* No memory, drop the packet. */
+				  skb = 0;
+			} else
+#ifdef KERNEL_1_2
+				skb = alloc_skb(pkt_len, GFP_ATOMIC);
+#else
+				skb = dev_alloc_skb(pkt_len + 2);
+#endif
+			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 (LINUX_VERSION_CODE >= VERSION(1,3,44))
+			if (! rx_in_place) {
+				skb_reserve(skb, 2);	/* 16 byte align the data fields */
+				memcpy(skb_put(skb, pkt_len),
+					   bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), pkt_len);
+			}
+			skb->protocol = eth_type_trans(skb, dev);
+#else
+#ifdef KERNEL_1_3
+#warning This code has only been tested with later 1.3.* kernels.
+			skb->len = pkt_len;
+			memcpy(skb->data, bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr),
+				   pkt_len);
+			/* Needed for 1.3.*. */
+			skb->protocol = eth_type_trans(skb, dev);
+#else	/* KERNEL_1_2 */
+			skb->len = pkt_len;
+			if (! rx_in_place) {
+				memcpy(skb->data,
+					   bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), pkt_len);
+			}
+#endif
+#endif
+			netif_rx(skb);
+			sp->stats.rx_packets++;
+		}
+
+		/*	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;
+		}
+	}
+
+	sp->last_rx_time = jiffies;
+	return 0;
+}
+
+static int
+speedo_close(struct device *dev)
+{
+	int ioaddr = dev->base_addr;
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	int i;
+
+	dev->start = 0;
+	dev->tbusy = 1;
+
+	if (speedo_debug > 1)
+		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n",
+			   dev->name, inw(ioaddr + SCBStatus));
+
+	/* Shut off the media monitoring timer. */
+	del_timer(&sp->timer);
+
+	/* Disable interrupts, and stop the chip's Rx process. */
+	outw(INT_MASK, ioaddr + SCBCmd);
+	outw(INT_MASK | RX_ABORT, ioaddr + SCBCmd);
+
+#ifdef SA_SHIRQ
+	free_irq(dev->irq, dev);
+#else
+	free_irq(dev->irq);
+	irq2dev_map[dev->irq] = 0;
+#endif
+
+	/* Free all the skbuffs in the Rx and Tx queues. */
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		struct sk_buff *skb = sp->rx_skbuff[i];
+		sp->rx_skbuff[i] = 0;
+		/* Clear the Rx descriptors. */
+		if (skb)
+			dev_kfree_skb(skb, FREE_WRITE);
+	}
+
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		struct sk_buff *skb = sp->tx_skbuff[i];
+		sp->tx_skbuff[i] = 0;
+		/* Clear the Tx descriptors. */
+		if (skb)
+			dev_kfree_skb(skb, FREE_WRITE);
+	}
+	if (sp->mc_setup_frm) {
+		kfree(sp->mc_setup_frm);
+		sp->mc_setup_frm_len = 0;
+	}
+
+	/* Print a few items for debugging. */
+	if (speedo_debug > 3) {
+		int phy_num = sp->phy[0] & 0x1f;
+		printk(KERN_DEBUG "%s:Printing Rx ring (next to receive into %d).\n",
+			   dev->name, sp->cur_rx);
+
+		for (i = 0; i < RX_RING_SIZE; i++)
+			printk(KERN_DEBUG "  Rx ring entry %d  %8.8x.\n",
+				   i, (int)sp->rx_ringp[i]->status);
+
+		for (i = 0; i < 5; i++)
+			printk(KERN_DEBUG "  PHY index %d register %d is %4.4x.\n",
+				   phy_num, i, mdio_read(ioaddr, phy_num, i));
+		for (i = 21; i < 26; i++)
+			printk(KERN_DEBUG "  PHY index %d register %d is %4.4x.\n",
+				   phy_num, i, mdio_read(ioaddr, phy_num, i));
+	}
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+/* The Speedo-3 has an especially awkward and unusable method of getting
+   statistics out of the chip.  It takes an unpredictable length of time
+   for the dump-stats command to complete.  To avoid a busy-wait loop we
+   update the stats with the previous dump results, and then trigger a
+   new dump.
+
+   These problems are mitigated by the current /proc implementation, which
+   calls this routine first to judge the output length, and then to emit the
+   output.
+
+   Oh, and incoming frames are dropped while executing dump-stats!
+   */
+static struct enet_statistics *
+speedo_get_stats(struct device *dev)
+{
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+
+	if (sp->lstats.done_marker == 0xA007) {	/* Previous dump finished */
+		sp->stats.tx_aborted_errors += sp->lstats.tx_coll16_errs;
+		sp->stats.tx_window_errors += sp->lstats.tx_late_colls;
+		sp->stats.tx_fifo_errors += sp->lstats.tx_underruns;
+		sp->stats.tx_fifo_errors += sp->lstats.tx_lost_carrier;
+		/*sp->stats.tx_deferred += sp->lstats.tx_deferred;*/
+		sp->stats.collisions += sp->lstats.tx_total_colls;
+		sp->stats.rx_crc_errors += sp->lstats.rx_crc_errs;
+		sp->stats.rx_frame_errors += sp->lstats.rx_align_errs;
+		sp->stats.rx_over_errors += sp->lstats.rx_resource_errs;
+		sp->stats.rx_fifo_errors += sp->lstats.rx_overrun_errs;
+		sp->stats.rx_length_errors += sp->lstats.rx_runt_errs;
+		sp->lstats.done_marker = 0x0000;
+		if (dev->start)
+			outw(CU_DUMPSTATS, ioaddr + SCBCmd);
+	}
+	return &sp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+   This is very ugly with Intel chips -- we usually have to execute an
+   entire configuration command, plus process a multicast command.
+   This is complicated.  We must put a large configuration command and
+   an arbitrarily-sized multicast command in the transmit list.
+   To minimize the disruption -- the previous command might have already
+   loaded the link -- we convert the current command block, normally a Tx
+   command, into a no-op and link it to the new command.
+*/
+static void
+set_rx_mode(struct device *dev)
+{
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+	char new_rx_mode;
+	unsigned long flags;
+	int entry, i;
+
+	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
+		new_rx_mode = 3;
+	} else if (dev->flags & IFF_ALLMULTI) {
+		new_rx_mode = 1;
+	} else
+		new_rx_mode = 0;
+
+	if (sp->cur_tx - sp->dirty_tx >= TX_RING_SIZE - 1) {
+	  /* The Tx ring is full -- don't add anything!  Presumably the new mode
+		 is in config_cmd_data and will be added anyway. */
+		sp->rx_mode = -1;
+		return;
+	}
+
+	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);
+		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. */
+		outw(CU_RESUME, ioaddr + SCBCmd);
+		sp->last_cmd = &sp->config_cmd;
+		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");
+		}
+	}
+
+	if (new_rx_mode == 0  &&  dev->mc_count < 3) {
+		/* The simple case of 0-2 multicast list entries occurs often, and
+		   fits within one tx_ring[] entry. */
+		u16 *setup_params;
+		unsigned short *eaddrs;
+		struct dev_mc_list *mclist;
+
+		save_flags(flags);
+		cli();
+		entry = sp->cur_tx++ % TX_RING_SIZE;
+		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]);
+		sp->tx_ring[entry].tx_desc_addr = 0; /* Really MC list count. */
+		setup_params = (short *)&sp->tx_ring[entry].tx_desc_addr;
+		*setup_params++ = dev->mc_count*6;
+		/* Fill in the multicast addresses. */
+		for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
+			 i++, mclist = mclist->next) {
+			eaddrs = (unsigned short *)mclist->dmi_addr;
+			*setup_params++ = *eaddrs++;
+			*setup_params++ = *eaddrs++;
+			*setup_params++ = *eaddrs++;
+		}
+
+		sp->last_cmd->command &= ~CmdSuspend;
+		/* Immediately trigger the command unit resume. */
+		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;
+		unsigned short *eaddrs;
+		struct descriptor *mc_setup_frm = sp->mc_setup_frm;
+		u16 *setup_params = (short *)mc_setup_frm->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. */
+			if (sp->mc_setup_frm)
+				kfree(sp->mc_setup_frm);
+			sp->mc_setup_frm_len = 10 + dev->mc_count*6 + 24;
+			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);
+				sp->rx_mode = -1; /* We failed, try again. */
+				return;
+			}
+		}
+		mc_setup_frm = sp->mc_setup_frm;
+		/* Construct the new setup frame. */
+		if (speedo_debug > 1)
+			printk(KERN_DEBUG "%s: Constructing a setup frame at %p, "
+				   "%d bytes.\n",
+				   dev->name, sp->mc_setup_frm, sp->mc_setup_frm_len);
+		mc_setup_frm->status = 0;
+		mc_setup_frm->command = CmdSuspend | CmdIntr | CmdMulticastList;
+		/* Link set below. */
+		setup_params = (short *)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;
+			 i++, mclist = mclist->next) {
+			eaddrs = (unsigned short *)mclist->dmi_addr;
+			*setup_params++ = *eaddrs++;
+			*setup_params++ = *eaddrs++;
+			*setup_params++ = *eaddrs++;
+		}
+
+		/* Disable interrupts while playing with the Tx Cmd list. */
+		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);
+
+		/* Change the command to a NoOp, pointing to the CmdMulti command. */
+		sp->tx_skbuff[entry] = 0;
+		sp->tx_ring[entry].status = CmdNOp << 16;
+		sp->tx_ring[entry].link = virt_to_bus(mc_setup_frm);
+
+		/* Set the link in the setup frame. */
+		mc_setup_frm->link =
+		  virt_to_bus(&(sp->tx_ring[sp->cur_tx % TX_RING_SIZE]));
+
+		sp->last_cmd->command &= ~CmdSuspend;
+		/* Immediately trigger the command unit resume. */
+		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);
+	}
+
+	sp->rx_mode = new_rx_mode;
+}
+
+#ifdef MODULE
+#if (LINUX_VERSION_CODE < VERSION(1,3,38))	/* 1.3.38 and later */
+char kernel_version[] = UTS_RELEASE;
+#endif
+
+int
+init_module(void)
+{
+	int cards_found;
+
+	if (debug >= 0)
+		speedo_debug = debug;
+	if (speedo_debug)
+		printk(KERN_INFO "%s", version);
+
+	root_speedo_dev = NULL;
+	cards_found = eepro100_init(NULL);
+	return cards_found ? 0 : -ENODEV;
+}
+
+void
+cleanup_module(void)
+{
+	struct device *next_dev;
+
+	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+	while (root_speedo_dev) {
+		next_dev = ((struct speedo_private *)root_speedo_dev->priv)->next_module;
+		unregister_netdev(root_speedo_dev);
+		release_region(root_speedo_dev->base_addr, SPEEDO3_TOTAL_SIZE);
+		kfree(root_speedo_dev);
+		root_speedo_dev = next_dev;
+	}
+}
+#else   /* not MODULE */
+int eepro100_probe(struct device *dev)
+{
+	int cards_found = 0;
+
+	cards_found = eepro100_init(dev);
+
+	if (speedo_debug > 0  &&  cards_found)
+		printk(version);
+
+	return cards_found ? 0 : -ENODEV;
+}
+#endif  /* MODULE */
+
+/*
+ * Local variables:
+ *  compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c eepro100.c"
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff -u --recursive --new-file v2.0.30/linux/drivers/net/eql.c linux/drivers/net/eql.c
--- v2.0.30/linux/drivers/net/eql.c	Sun Sep 22 11:17:08 1996
+++ linux/drivers/net/eql.c	Tue Aug 12 14:15:56 1997
@@ -17,7 +17,7 @@
  */
 
 static const char *version = 
-	"Equalizer1996: $Revision: 1.2.1 $ $Date: 1996/09/22 13:52:00 $ Simon Janes (simon@ncm.com)\n";
+	"Equalizer1996: $Revision: 1.9 $ $Date: 1996/10/12 11:14:37 $ Simon Janes (simon@ncm.com)\n";
 
 /*
  * Sources:
@@ -31,6 +31,9 @@
 
 /*
  * $Log: eql.c,v $
+ * Revision 1.9  1996/10/12 11:14:37  davem
+ * Quick merge to 2.0.20
+ *
  * Revision 1.2  1996/04/11 17:51:52  guru
  * Added one-line eql_remove_slave patch.
  *
@@ -262,6 +265,7 @@
 
 	dev->hard_header	= eql_header; 
 	dev->rebuild_header	= eql_rebuild_header;
+	dev->hard_header_len	= MAX_HEADER; /* enough space for any slave */
 
 	/*
 	 *	Now we undo some of the things that eth_setup does
@@ -371,10 +375,19 @@
 	equalizer_t *eql = (equalizer_t *) dev->priv;
 	struct device *slave_dev = 0;
 	slave_t *slave;
+	struct sk_buff *skb2;
 
 	if (skb == NULL)
 		return 0;
 
+#if 0
+	/* Make a copy we can free so we don't mess up the skb->dev pointer */
+	skb2 = skb_clone(skb, GFP_ATOMIC);
+
+	if (skb2 == NULL)
+		return 1;
+#endif
+
 	eql_schedule_slaves (eql->queue);
   
 	slave_dev = eql_best_slave_dev (eql->queue);
@@ -388,20 +401,48 @@
 				dev->name, eql_number_slaves (eql->queue), skb->len,
 				slave_dev->name);
 #endif
-		dev_queue_xmit (skb, slave_dev, 1);
-		eql->stats->tx_packets++;
-		slave->bytes_queued += skb->len; 
-	}
-	else
-	{
-		/*
-		 *	The alternative for this is the return 1 and have
-		 *	dev_queue_xmit just queue it up on the eql's queue. 
+
+		/* Rip off the fake header */
+		skb_pull(skb,MAX_HEADER);
+
+		/* The original code had no hard header constructed.
+		 * If a frame is fragmented on EQL and then passed to PPP,
+		 * or ISDN, the result will be a panic.
+		 * The solution is to call the hard_header constructor
+		 * for the device we point to just before we send the packet.
+		 * If this fails we drop the packet.
+		 * We don't know any special parameters for the hard_header
+		 * constructor at this point, so we pass in made up values
+		 * that will cause the constructor to fail on every device
+		 * except those that we are allowed to use EQL on:
+		 * PPP, SLIP and ISDN (in some cases!).
+		 * The worst thing that happens is if some fool
+		 * configures EQL to enslave something that needs
+		 * these parameters it throws out packets.
+		 * This is not an issue for the things EQL is intended for
+		 * anyway, and probably would have crashed the kernel
+		 * or sent garbage down the wire previously.
 		 */
 
-		eql->stats->tx_dropped++;
-		dev_kfree_skb(skb, FREE_WRITE);
-	}	  
+		if (slave_dev->hard_header == NULL
+		|| slave_dev->hard_header(skb,slave_dev,
+			ETH_P_IP,NULL,NULL,skb->len) >= 0) {
+			dev_queue_xmit (skb, slave_dev, 1);
+			eql->stats->tx_packets++;
+			slave->bytes_queued += skb->len; 
+			/* dev_kfree_skb(skb, FREE_WRITE); */
+			return 0;
+		}
+	}
+
+	/*
+	 *	The alternative for this is the return 1 and have
+	 *	dev_queue_xmit just queue it up on the eql's queue. 
+	 */
+
+	eql->stats->tx_dropped++;
+	/* dev_kfree_skb(skb2, FREE_WRITE); */
+	dev_kfree_skb(skb, FREE_WRITE);
 	return 0;
 }
 
@@ -417,7 +458,9 @@
 	   unsigned short type, void *daddr, void *saddr, 
 	   unsigned len)
 {
-	return 0;
+	/* Fake header to keep space during buggy IP fragmentation.  */
+	skb_push(skb,MAX_HEADER);
+	return MAX_HEADER;
 }
 
 
diff -u --recursive --new-file v2.0.30/linux/drivers/net/eth16i.c linux/drivers/net/eth16i.c
--- v2.0.30/linux/drivers/net/eth16i.c	Tue Apr  8 08:47:45 1997
+++ linux/drivers/net/eth16i.c	Fri Aug 15 15:35:39 1997
@@ -61,6 +61,11 @@
    0.21         17.10-95        Removed the unnecessary extern 
 				init_etherdev() declaration. Some
 				other cleanups.
+
+   0.21a	15.08-97	Made signature check less restrictive to
+				detect card that have been used for booting
+				with a bootprom.
+				Kurt Huwig <kurt@huwig.de>
    Bugs:
 	In some cases the interface autoprobing code doesn't find 
 	the correct interface type. In this case you can 
@@ -72,7 +77,7 @@
 */
 
 static char *version = 
-	"eth16i.c: v0.21 17-10-95 Mika Kuoppala (miku@elt.icl.fi)\n";
+	"eth16i.c: v0.21a 15-08-97 Mika Kuoppala (miku@elt.icl.fi)/Kurt Huwig (kurt@huwig.de)\n";
 
 #include <linux/module.h>
 
@@ -722,10 +727,10 @@
 #endif
 
   if( !( (creg[2] == 0x36) && (creg[3] == 0xE0)) ) {
-      creg[2] &= 0x42;
+      creg[2] &= 0x40;
       creg[3] &= 0x03;
 
-      if( !( (creg[2] == 0x42) && (creg[3] == 0x00)) )
+      if( !( (creg[2] == 0x40) && (creg[3] == 0x00)) )
 	return -1;
   }
 
diff -u --recursive --new-file v2.0.30/linux/drivers/net/hp100.c linux/drivers/net/hp100.c
--- v2.0.30/linux/drivers/net/hp100.c	Thu Apr 11 23:49:38 1996
+++ linux/drivers/net/hp100.c	Tue Aug  5 09:11:35 1997
@@ -1,89 +1,66 @@
 /*
- * hp100.c: Hewlett Packard HP10/100VG ANY LAN ethernet driver for Linux.
- *
- * Author:  Jaroslav Kysela, <perex@pf.jcu.cz>
- *
- * Supports only the following Hewlett Packard cards:
- *
- *  	HP J2577	10/100 EISA card with REVA Cascade chip
- *	HP J2573	10/100 ISA card with REVA Cascade chip
- *	HP 27248B	10 only EISA card with Cascade chip
- *	HP J2577	10/100 EISA card with Cascade chip
- *	HP J2573	10/100 ISA card with Cascade chip
- *	HP J2585	10/100 PCI card
- *
- * Other ATT2MD01 Chip based boards might be supported in the future
- * (there are some minor changes needed).
- *
- * This driver is based on the 'hpfepkt' crynwr packet driver.
- *
- * This source/code is public free; you can distribute it and/or modify 
- * it under terms of the GNU General Public License (published by the
- * Free Software Foundation) either version two of this License, or any 
- * later version.
- * ----------------------------------------------------------------------------
- *
- * Note: Some routines (interrupt handling, transmit) assumes that  
- *       there is the PERFORMANCE page selected...
- *
- * ----------------------------------------------------------------------------
- *
- * If you are going to use the module version of this driver, you may
- * change this values at the "insert time" :
- *
- *   Variable                   Description
- *
- *   hp100_rx_ratio		Range 1-99 - onboard memory used for RX
- *                              packets in %.
- *   hp100_priority_tx		If this variable is nonzero - all outgoing
- *                              packets will be transmitted as priority.
- *   hp100_port			Adapter port (for example 0x380).
- *
- * ----------------------------------------------------------------------------
- * MY BEST REGARDS GOING TO:
- *
- * IPEX s.r.o which lend me two HP J2573 cards and
- * the HP AdvanceStack 100VG Hub-15 for debugging.
- *
- * Russel Nellson <nelson@crynwr.com> for help with obtaining sources
- * of the 'hpfepkt' packet driver.
- *
- * Also thanks to Abacus Electric s.r.o which let me to use their 
- * motherboard for my second computer.
- *
- * ----------------------------------------------------------------------------
- *
- * TO DO:
- * ======
- *       - ioctl handling - some runtime setup things
- *       - 100Mb/s Voice Grade AnyLAN network adapter/hub services support
- *		- 802.5 frames
- *		- promiscuous mode
- *		- bridge mode
- *		- cascaded repeater mode
- *		- 100Mbit MAC
- *
- * Revision history:
- * =================
- * 
- *    Version   Date	    Description
- *
- *	0.1	14-May-95   Initial writing. ALPHA code was released.
- *                          Only HP J2573 on 10Mb/s (two machines) tested.
- *      0.11    14-Jun-95   Reset interface bug fixed?
- *			    Little bug in hp100_close function fixed.
- *                          100Mb/s connection debugged.
- *      0.12    14-Jul-95   Link down is now handled better.
- *      0.20    01-Aug-95   Added PCI support for HP J2585A card.
- *                          Statistics bug fixed.
- *      0.21    04-Aug-95   Memory mapped access support for PCI card.
- *                          Added priority transmit support for 100Mb/s
- *                          Voice Grade AnyLAN network.
- *
- */
+** hp100.c 
+** HP CASCADE Architecture Driver for 100VG-AnyLan Network Adapters
+**
+** $Id: hp100.c,v 1.54 1997/06/12 10:37:07 perex Exp perex $
+**
+** Based on the HP100 driver written by Jaroslav Kysela <perex@jcu.cz>
+** Extended for new busmaster capable chipsets by 
+** Siegfried "Frieder" Loeffler (dg1sek) <floeff@mathematik.uni-stuttgart.de>
+**
+** Maintained by: Jaroslav Kysela <perex@jcu.cz>
+** 
+** This driver has only been tested with
+** -- HP J2585B 10/100 Mbit/s PCI Busmaster
+** -- HP J2585A 10/100 Mbit/s PCI 
+** -- HP J2970  10 Mbit/s PCI Combo 10base-T/BNC
+** -- HP J2973  10 Mbit/s PCI 10base-T
+** -- HP J2573  10/100 ISA
+** -- Compex ReadyLink ENET100-VG4  10/100 Mbit/s PCI / EISA
+** 
+** but it should also work with the other CASCADE based adapters.
+**
+** TODO:
+**       -  J2573 seems to hang sometimes when in shared memory mode.
+**       -  Mode for Priority TX
+**       -  Check PCI registers, performance might be improved?
+**       -  To reduce interrupt load in busmaster, one could switch off
+**          the interrupts that are used to refill the queues whenever the
+**          queues are filled up to more than a certain threshold.
+**
+**
+** This source/code is public free; you can distribute it and/or modify 
+** it under terms of the GNU General Public License (published by the
+** Free Software Foundation) either version two of this License, or any 
+** later version.
+**
+** 1.53 -> 1.54
+**   - added hardware multicast filter support (doesn't work)
+**   - little changes in hp100_sense_lan routine 
+**     - added support for Coax and AUI (J2970)
+**   - fix for multiple cards and hp100_mode parameter (insmod)
+**   - fix for shared IRQ 
+**
+** 1.52 -> 1.53
+**   - fixed bug in multicast support
+**
+*/
+
+#define HP100_DEFAULT_PRIORITY_TX 0 
+
+#undef HP100_DEBUG
+#undef HP100_DEBUG_B           /* Trace  */
+#undef HP100_DEBUG_BM          /* Debug busmaster code (PDL stuff) */
+
+#undef HP100_DEBUG_TRAINING    /* Debug login-to-hub procedure */
+#undef HP100_DEBUG_TX   
+#undef HP100_DEBUG_IRQ 
+#undef HP100_DEBUG_RX 
 
-#include <linux/module.h>
+#undef HP100_MULTICAST_FILTER  /* Need to be debugged... */
 
+#include <linux/version.h>
+#include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/string.h>
@@ -101,7 +78,23 @@
 #include <linux/skbuff.h>
 
 #include <linux/types.h>
-#include <linux/config.h>	/* for CONFIG_PCI */
+#include <linux/config.h>  /* for CONFIG_PCI */
+#include <linux/delay.h>
+
+#if LINUX_VERSION_CODE < 0x020100
+#define ioremap vremap
+#define iounmap vfree
+typedef struct enet_statistics hp100_stats_t;
+#else
+#define LINUX_2_1
+typedef struct net_device_stats hp100_stats_t;
+#endif
+
+#ifndef __initfunc
+#define __initfunc(__initarg) __initarg
+#else
+#include <linux/init.h>
+#endif
 
 #include "hp100.h"
 
@@ -109,18 +102,28 @@
  *  defines
  */
 
-#define HP100_BUS_ISA		0
-#define HP100_BUS_EISA		1
-#define HP100_BUS_PCI		2
+#define HP100_BUS_ISA     0
+#define HP100_BUS_EISA    1
+#define HP100_BUS_PCI     2
+
+#ifndef PCI_DEVICE_ID_HP_J2585B
+#define PCI_DEVICE_ID_HP_J2585B 0x1031
+#endif
+#ifndef PCI_VENDOR_ID_COMPEX
+#define PCI_VENDOR_ID_COMPEX 0x11f6
+#endif
+#ifndef PCI_DEVICE_ID_COMPEX_ENET100VG4
+#define PCI_DEVICE_ID_COMPEX_ENET100VG4 0x0112
+#endif
 
-#define HP100_REGION_SIZE	0x20
+#define HP100_REGION_SIZE  0x20 /* for ioports */
 
-#define HP100_MAX_PACKET_SIZE	(1536+4)
-#define HP100_MIN_PACKET_SIZE	60
+#define HP100_MAX_PACKET_SIZE  (1536+4)
+#define HP100_MIN_PACKET_SIZE  60
 
 #ifndef HP100_DEFAULT_RX_RATIO
-/* default - 65% onboard memory on the card are used for RX packets */
-#define HP100_DEFAULT_RX_RATIO	65
+/* default - 75% onboard memory on the card are used for RX packets */
+#define HP100_DEFAULT_RX_RATIO  75
 #endif
 
 #ifndef HP100_DEFAULT_PRIORITY_TX
@@ -140,193 +143,289 @@
 
 struct hp100_private {
   struct hp100_eisa_id *id;
+  u_short chip;
   u_short soft_model;
-  u_int memory_size;
-  u_short rx_ratio;		    /* 1 - 99 */
-  u_short priority_tx;	            /* != 0 - priority tx */
-  short mem_mapped;		    /* memory mapped access */
-  u_char *mem_ptr_virt;		    /* virtual memory mapped area, maybe NULL */
-  u_char *mem_ptr_phys;		    /* physical memory mapped area */
-  short lan_type;		    /* 10Mb/s, 100Mb/s or -1 (error) */
-  int hub_status;		    /* login to hub was successful? */
+  u_int memory_size; 
+  u_short rx_ratio;       /* 1 - 99 */
+  u_short priority_tx;    /* != 0 - priority tx */
+  u_short mode;           /* PIO, Shared Mem or Busmaster */
+  u_char bus;
+  u_char pci_bus;
+  u_char pci_device_fn;
+  short mem_mapped;	  /* memory mapped access */
+  u_int *mem_ptr_virt;    /* virtual memory mapped area, maybe NULL */
+  u_int *mem_ptr_phys;	  /* physical memory mapped area */
+  short lan_type;	  /* 10Mb/s, 100Mb/s or -1 (error) */
+  int hub_status;	  /* was login to hub successful? */
   u_char mac1_mode;
   u_char mac2_mode;
-  struct enet_statistics stats;
+  u_char hash_bytes[ 8 ];
+  hp100_stats_t stats;
+
+  /* Rings for busmaster mode: */
+  hp100_ring_t *rxrhead;  /* Head (oldest) index into rxring */
+  hp100_ring_t *rxrtail;  /* Tail (newest) index into rxring */
+  hp100_ring_t *txrhead;  /* Head (oldest) index into txring */
+  hp100_ring_t *txrtail;  /* Tail (newest) index into txring */
+
+  hp100_ring_t rxring[ MAX_RX_PDL ];
+  hp100_ring_t txring[ MAX_TX_PDL ];
+
+  u_int *page_vaddr;      /* Virtual address of allocated page */
+  u_int *page_vaddr_algn; /* Aligned virtual address of allocated page */
+  int rxrcommit;          /* # Rx PDLs commited to adapter */
+  int txrcommit;          /* # Tx PDLs commited to adapter */
 };
 
 /*
  *  variables
  */
- 
+
 static struct hp100_eisa_id hp100_eisa_ids[] = {
 
-  /* 10/100 EISA card with REVA Cascade chip */
-  { 0x080F1F022, "HP J2577 rev A", HP100_BUS_EISA }, 
+  /* 10/100 EISA card with revision A Cascade chip */
+  { 0x80F1F022, "HP J2577 rev A", HP100_BUS_EISA },
 
-  /* 10/100 ISA card with REVA Cascade chip */
-  { 0x050F1F022, "HP J2573 rev A", HP100_BUS_ISA },
+  /* 10/100 ISA card with revision A Cascade chip */
+  { 0x50F1F022, "HP J2573 rev A", HP100_BUS_ISA },
 
   /* 10 only EISA card with Cascade chip */
-  { 0x02019F022, "HP 27248B",      HP100_BUS_EISA }, 
+  { 0x2019F022, "HP 27248B",      HP100_BUS_EISA },
 
   /* 10/100 EISA card with Cascade chip */
-  { 0x04019F022, "HP J2577",       HP100_BUS_EISA },
+  { 0x4019F022, "HP J2577",       HP100_BUS_EISA },
 
   /* 10/100 ISA card with Cascade chip */
-  { 0x05019F022, "HP J2573",       HP100_BUS_ISA },
+  { 0x5019F022, "HP J2573",       HP100_BUS_ISA },
+
+  /* 10/100 PCI card - old J2585A */
+  { 0x1030103c, "HP J2585A", 	    HP100_BUS_PCI },
+
+  /* 10/100 PCI card - new J2585B - master capable */
+  { 0x1041103c, "HP J2585B",      HP100_BUS_PCI },
+
+  /* 10 Mbit Combo Adapter */
+  { 0x1042103c, "HP J2970",       HP100_BUS_PCI },
 
-  /* 10/100 PCI card */
-  /* Note: ID for this card is same as PCI vendor/device numbers. */
-  { 0x01030103c, "HP J2585", 	   HP100_BUS_PCI },
+  /* 10 Mbit 10baseT Adapter */
+  { 0x1040103c, "HP J2973",       HP100_BUS_PCI },
+
+  /* 10/100 EISA card from Compex */
+  { 0x0103180e, "ReadyLink ENET100-VG4", HP100_BUS_EISA },
+
+  /* 10/100 PCI card from Compex (J2585A compatible) */
+  { 0x011211f6, "ReadyLink ENET100-VG4", HP100_BUS_PCI }
 };
 
-int hp100_rx_ratio = HP100_DEFAULT_RX_RATIO;
-int hp100_priority_tx = HP100_DEFAULT_PRIORITY_TX;
+static int hp100_rx_ratio = HP100_DEFAULT_RX_RATIO;
+static int hp100_priority_tx = HP100_DEFAULT_PRIORITY_TX;
+static int hp100_mode = 1;
+
+#ifdef LINUX_2_1
+MODULE_PARM( hp100_rx_ratio, "1i" );
+MODULE_PARM( hp100_priority_tx, "1i" );
+MODULE_PARM( hp100_mode, "1i" );
+#endif
 
 /*
  *  prototypes
  */
 
-static int hp100_probe1( struct device *dev, int ioaddr, int bus );
-static int hp100_open( struct device *dev );
-static int hp100_close( struct device *dev );
-static int hp100_start_xmit( struct sk_buff *skb, struct device *dev );
+static int  hp100_probe1( struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn  );
+static int  hp100_open( struct device *dev );
+static int  hp100_close( struct device *dev );
+static int  hp100_start_xmit( struct sk_buff *skb, struct device *dev );
+static int  hp100_start_xmit_bm (struct sk_buff *skb, struct device *dev );
 static void hp100_rx( struct device *dev );
-static struct enet_statistics *hp100_get_stats( struct device *dev );
+static hp100_stats_t *hp100_get_stats( struct device *dev );
 static void hp100_update_stats( struct device *dev );
 static void hp100_clear_stats( int ioaddr );
 static void hp100_set_multicast_list( struct device *dev);
 static void hp100_interrupt( int irq, void *dev_id, struct pt_regs *regs );
-
 static void hp100_start_interface( struct device *dev );
 static void hp100_stop_interface( struct device *dev );
 static void hp100_load_eeprom( struct device *dev );
-static int hp100_sense_lan( struct device *dev );
-static int hp100_login_to_vg_hub( struct device *dev );
-static int hp100_down_vg_link( struct device *dev );
+static int  hp100_sense_lan( struct device *dev );
+static int  hp100_login_to_vg_hub( struct device *dev, u_short force_relogin );
+static int  hp100_down_vg_link( struct device *dev );
+static void hp100_cascade_reset( struct device *dev, u_short enable );
+static void hp100_BM_shutdown( struct device *dev );
+static void hp100_mmuinit( struct device *dev );
+static void hp100_init_pdls( struct device *dev );
+static int  hp100_init_rxpdl( struct device *dev, register hp100_ring_t *ringptr, register u_int *pdlptr);
+static int  hp100_init_txpdl( struct device *dev, register hp100_ring_t *ringptr, register u_int *pdlptr);
+static void hp100_rxfill( struct device *dev );
+static void hp100_hwinit( struct device *dev );
+static void hp100_clean_txring( struct device *dev );
+#ifdef HP100_DEBUG
+static void hp100_RegisterDump( struct device *dev );
+#endif
+
+/* TODO: This function should not really be needed in a good design... */
+static void wait( void )
+{
+  udelay( 1000 );
+}
 
 /*
  *  probe functions
+ *  These functions should - if possible - avoid doing write operations
+ *  since this could cause problems when the card is not installed.
  */
- 
-int hp100_probe( struct device *dev )
+
+__initfunc(int hp100_probe( struct device *dev ))
 {
   int base_addr = dev ? dev -> base_addr : 0;
-  int ioaddr;
+  int ioaddr = 0;
 #ifdef CONFIG_PCI
   int pci_start_index = 0;
 #endif
 
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4200, TRACE );
+  printk( "hp100: %s: probe\n", dev->name );
+#endif
+
   if ( base_addr > 0xff )	/* Check a single specified location. */
     {
       if ( check_region( base_addr, HP100_REGION_SIZE ) ) return -EINVAL;
       if ( base_addr < 0x400 )
-        return hp100_probe1( dev, base_addr, HP100_BUS_ISA );
-       else
-        return hp100_probe1( dev, base_addr, HP100_BUS_EISA );
+        return hp100_probe1( dev, base_addr, HP100_BUS_ISA, 0, 0 );
+      if ( EISA_bus && base_addr >= 0x1c38 && ( (base_addr - 0x1c38) & 0x3ff ) == 0 )
+        return hp100_probe1( dev, base_addr, HP100_BUS_EISA, 0, 0 );
+#ifdef CONFIG_PCI
+      printk( "hp100: %s: You may specify card # in i/o address parameter for PCI bus...", dev->name );
+      return hp100_probe1( dev, base_addr, HP100_BUS_PCI, 0, 0 );
+#else
+      return -ENODEV;
+#endif
     }
-   else 
+  else
 #ifdef CONFIG_PCI
-  if ( base_addr > 0 && base_addr < 8 + 1 )
-    pci_start_index = 0x100 | ( base_addr - 1 );
-   else
+    if ( base_addr > 0 && base_addr < 8 + 1 )
+      pci_start_index = 0x100 | ( base_addr - 1 );
+    else
 #endif
-    if ( base_addr != 0 ) return -ENXIO;
+      if ( base_addr != 0 ) return -ENXIO;
 
   /* at first - scan PCI bus(es) */
-  
+
 #ifdef CONFIG_PCI
   if ( pcibios_present() )
     {
       int pci_index;
-      
+
 #ifdef HP100_DEBUG_PCI
-      printk( "hp100: PCI BIOS is present, checking for devices..\n" );
+      printk( "hp100: %s: PCI BIOS is present, checking for devices..\n", dev->name );
 #endif
       for ( pci_index = pci_start_index & 7; pci_index < 8; pci_index++ )
         {
           u_char pci_bus, pci_device_fn;
           u_short pci_command;
-          
-          if ( pcibios_find_device( PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585A,
-          			    pci_index, &pci_bus,
-          			    &pci_device_fn ) != 0 ) break;
+
+          if ((pcibios_find_device( PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585A,
+                                    pci_index, &pci_bus,
+                                    &pci_device_fn ) != 0 ) && 
+              (pcibios_find_device( PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585B,
+                                    pci_index, &pci_bus,
+                                    &pci_device_fn ) != 0 ) &&
+              (pcibios_find_device( PCI_VENDOR_ID_COMPEX, PCI_DEVICE_ID_COMPEX_ENET100VG4,
+                                    pci_index, &pci_bus,
+                                    &pci_device_fn ) != 0 ) ) break; 
+
           pcibios_read_config_dword( pci_bus, pci_device_fn,
-              		             PCI_BASE_ADDRESS_0, &ioaddr );
-              				 
-          ioaddr &= ~3;		/* remove I/O space marker in bit 0. */
-              
+                                     PCI_BASE_ADDRESS_0, &ioaddr );
+
+          ioaddr &= ~3;    /* remove I/O space marker in bit 0. */
+
           if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
-              
+
           pcibios_read_config_word( pci_bus, pci_device_fn,
-              			    PCI_COMMAND, &pci_command );
+                                    PCI_COMMAND, &pci_command );
           if ( !( pci_command & PCI_COMMAND_MASTER ) )
             {
-#ifdef HP100_DEBUG_PCI
-              printk( "hp100: PCI Master Bit has not been set. Setting...\n" );
+#ifdef HP100_DEBUG
+              printk( "hp100: %s: PCI Master Bit has not been set. Setting...\n", dev->name );
 #endif
               pci_command |= PCI_COMMAND_MASTER;
               pcibios_write_config_word( pci_bus, pci_device_fn,
-                  			 PCI_COMMAND, pci_command );
+                                         PCI_COMMAND, pci_command );
             }
-#ifdef HP100_DEBUG_PCI
-          printk( "hp100: PCI adapter found at 0x%x\n", ioaddr );
+#ifdef HP100_DEBUG
+          printk( "hp100: %s: PCI adapter found at 0x%x\n", dev->name, ioaddr );
 #endif
-       	  if ( hp100_probe1( dev, ioaddr, HP100_BUS_PCI ) == 0 ) return 0;
+	  if ( hp100_probe1( dev, ioaddr, HP100_BUS_PCI, pci_bus, pci_device_fn ) == 0 )
+	    return 0;
         }
     }
   if ( pci_start_index > 0 ) return -ENODEV;
 #endif /* CONFIG_PCI */
-         
-  /* at second - probe all EISA possible port regions (if EISA bus present) */
-  
+
+  /* Second: Probe all EISA possible port regions (if EISA bus present) */
   for ( ioaddr = 0x1c38; EISA_bus && ioaddr < 0x10000; ioaddr += 0x400 )
     {
       if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
-      if ( hp100_probe1( dev, ioaddr, HP100_BUS_EISA ) == 0 ) return 0;
+      if ( hp100_probe1( dev, ioaddr, HP100_BUS_EISA, 0, 0 ) == 0 ) return 0;
     }
-         
-  /* at third - probe all ISA possible port regions */
-         
+
+  /* Third Probe all ISA possible port regions */
   for ( ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20 )
     {
       if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
-      if ( hp100_probe1( dev, ioaddr, HP100_BUS_ISA ) == 0 ) return 0;
+      if ( hp100_probe1( dev, ioaddr, HP100_BUS_ISA, 0, 0 ) == 0 ) return 0;
     }
-                                                                            
+
   return -ENODEV;
 }
 
-static int hp100_probe1( struct device *dev, int ioaddr, int bus )
+
+__initfunc(static int hp100_probe1( struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn ))
 {
   int i;
+
   u_char uc, uc_1;
   u_int eisa_id;
+  u_int chip;
+  u_int memory_size = 0;
+  u_short local_mode, lsw;
   short mem_mapped;
-  u_char *mem_ptr_phys, *mem_ptr_virt;
+  u_int *mem_ptr_phys, *mem_ptr_virt;
   struct hp100_private *lp;
   struct hp100_eisa_id *eid;
 
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4201, TRACE );
+  printk("hp100: %s: probe1\n",dev->name);
+#endif
+
   if ( dev == NULL )
     {
 #ifdef HP100_DEBUG
-      printk( "hp100_probe1: dev == NULL ?\n" );
+      printk( "hp100_probe1: %s: dev == NULL ?\n", dev->name );
 #endif
       return EIO;
     }
+  
+  if ( hp100_inw( HW_ID ) != HP100_HW_ID_CASCADE ) 
+    {
+      return -ENODEV;
+    }
+  else
+    {
+      chip = hp100_inw( PAGING ) & HP100_CHIPID_MASK;
+#ifdef HP100_DEBUG
+      if ( chip == HP100_CHIPID_SHASTA )
+        printk("hp100: %s: Shasta Chip detected. (This is a pre 802.12 chip)\n", dev->name);
+      else if ( chip == HP100_CHIPID_RAINIER )
+        printk("hp100: %s: Rainier Chip detected. (This is a pre 802.12 chip)\n", dev->name);
+      else if ( chip == HP100_CHIPID_LASSEN )
+        printk("hp100: %s: Lassen Chip detected.\n", dev->name);
+      else
+        printk("hp100: %s: Warning: Unknown CASCADE chip (id=0x%.4x).\n",dev->name,chip);
+#endif 
+    }
 
-  if ( bus != HP100_BUS_PCI )		/* don't check PCI cards again */
-    if ( inb( ioaddr + 0 ) != HP100_HW_ID_0 ||
-         inb( ioaddr + 1 ) != HP100_HW_ID_1 ||
-         ( inb( ioaddr + 2 ) & 0xf0 ) != HP100_HW_ID_2_REVA ||
-         inb( ioaddr + 3 ) != HP100_HW_ID_3 ) 
-       return -ENODEV;
-
-  dev -> base_addr = ioaddr;
-
-#ifdef HP100_DEBUG_PROBE1
-  printk( "hp100_probe1: card found at port 0x%x\n", ioaddr );
-#endif
+  dev->base_addr = ioaddr;
 
   hp100_page( ID_MAC_ADDR );
   for ( i = uc = eisa_id = 0; i < 4; i++ )
@@ -338,29 +437,25 @@
     }
   uc += hp100_inb( BOARD_ID + 4 );
 
-#ifdef HP100_DEBUG_PROBE1
-  printk( "hp100_probe1: EISA ID = 0x%08x  checksum = 0x%02x\n", eisa_id, uc );
-#endif
-
-  if ( uc != 0xff )		/* bad checksum? */
+  if ( uc != 0xff )    /* bad checksum? */
     {
-      printk( "hp100_probe: bad EISA ID checksum at base port 0x%x\n", ioaddr );
+      printk("hp100_probe: %s: bad EISA ID checksum at base port 0x%x\n", dev->name, ioaddr );
       return -ENODEV;
-    }  
+    }
 
-  for ( i = 0; i < sizeof( hp100_eisa_ids ) / sizeof( struct hp100_eisa_id ); i++ )
+  for ( i=0; i<sizeof(hp100_eisa_ids)/sizeof(struct hp100_eisa_id); i++)
     if ( ( hp100_eisa_ids[ i ].id & 0xf0ffffff ) == ( eisa_id & 0xf0ffffff ) )
       break;
   if ( i >= sizeof( hp100_eisa_ids ) / sizeof( struct hp100_eisa_id ) )
     {
-      printk( "hp100_probe1: card at port 0x%x isn't known (id = 0x%x)\n", ioaddr, eisa_id );
+      printk( "hp100_probe: %s: card at port 0x%x isn't known (id = 0x%x)\n", dev -> name, ioaddr, eisa_id );
       return -ENODEV;
     }
   eid = &hp100_eisa_ids[ i ];
-  if ( ( eid -> id & 0x0f000000 ) < ( eisa_id & 0x0f000000 ) )
+  if ( ( eid->id & 0x0f000000 ) < ( eisa_id & 0x0f000000 ) )
     {
-      printk( "hp100_probe1: newer version of card %s at port 0x%x - unsupported\n", 
-	eid -> name, ioaddr );
+      printk( "hp100_probe: %s: newer version of card %s at port 0x%x - unsupported\n",
+	      dev->name, eid->name, ioaddr );
       return -ENODEV;
     }
 
@@ -368,469 +463,1607 @@
     uc += hp100_inb( LAN_ADDR + i );
   if ( uc != 0xff )
     {
-      printk( "hp100_probe1: bad lan address checksum (card %s at port 0x%x)\n", 
-	eid -> name, ioaddr );
+      printk("hp100_probe: %s: bad lan address checksum (card %s at port 0x%x)\n",
+	     dev->name, eid->name, ioaddr );
       return -EIO;
     }
 
-#ifndef HP100_IO_MAPPED
+  /* Determine driver operation mode
+   *
+   * Use the variable "hp100_mode" upon insmod or as kernel parameter to
+   * force driver modes:
+   * hp100_mode=1 -> default, use busmaster mode if configured.
+   * hp100_mode=2 -> enable shared memory mode 
+   * hp100_mode=3 -> force use of i/o mapped mode.
+   * hp100_mode=4 -> same as 1, but re-set the enable bit on the card.
+   */
+
+  /* hp100_mode value maybe used in future by another card */
+  local_mode=hp100_mode;
+  if ( local_mode < 1 || local_mode > 4 )
+    local_mode = 1;		/* default */
+#ifdef HP100_DEBUG
+  printk( "hp100: %s: original LSW = 0x%x\n", dev->name, hp100_inw(OPTION_LSW) );
+#endif
+
+  if(local_mode==3)
+    {
+      hp100_outw(HP100_MEM_EN|HP100_RESET_LB, OPTION_LSW);
+      hp100_outw(HP100_IO_EN|HP100_SET_LB, OPTION_LSW);
+      hp100_outw(HP100_BM_WRITE|HP100_BM_READ|HP100_RESET_HB, OPTION_LSW);
+      printk("hp100: %s: IO mapped mode forced.\n", dev->name);
+    }
+  else if(local_mode==2)
+    {
+      hp100_outw(HP100_MEM_EN|HP100_SET_LB, OPTION_LSW);
+      hp100_outw(HP100_IO_EN |HP100_SET_LB, OPTION_LSW);
+      hp100_outw(HP100_BM_WRITE|HP100_BM_READ|HP100_RESET_HB, OPTION_LSW);
+      printk("hp100: %s: Shared memory mode requested.\n", dev->name);
+    } 
+  else if(local_mode==4)
+    {
+      if(chip==HP100_CHIPID_LASSEN)
+	{
+	  hp100_outw(HP100_BM_WRITE|
+		     HP100_BM_READ | HP100_SET_HB, OPTION_LSW);
+	  hp100_outw(HP100_IO_EN   | 
+		     HP100_MEM_EN  | HP100_RESET_LB, OPTION_LSW);
+	  printk("hp100: %s: Busmaster mode requested.\n",dev->name);
+	}
+      local_mode=1;
+    }
+		
+  if(local_mode==1) /* default behaviour */
+    {
+      lsw = hp100_inw(OPTION_LSW);
+    
+      if ( (lsw & HP100_IO_EN) &&
+	   (~lsw & HP100_MEM_EN) &&
+	   (~lsw & (HP100_BM_WRITE|HP100_BM_READ)) )
+	{
+#ifdef HP100_DEBUG
+	  printk("hp100: %s: IO_EN bit is set on card.\n",dev->name);
+#endif
+	  local_mode=3;
+	}
+      else if ( chip == HP100_CHIPID_LASSEN &&
+	        ( lsw & (HP100_BM_WRITE|HP100_BM_READ) ) ==
+	                (HP100_BM_WRITE|HP100_BM_READ) )
+	{
+	  printk("hp100: %s: Busmaster mode enabled.\n",dev->name);
+	  hp100_outw(HP100_MEM_EN|HP100_IO_EN|HP100_RESET_LB, OPTION_LSW);
+	}
+      else
+	{
+#ifdef HP100_DEBUG
+	  printk("hp100: %s: Card not configured for BM or BM not supported with this card. Trying shared memory mode.\n", dev->name);
+#endif
+	  /* In this case, try shared memory mode */
+	  local_mode=2;
+	  hp100_outw(HP100_MEM_EN|HP100_SET_LB, OPTION_LSW);
+	  /* hp100_outw(HP100_IO_EN|HP100_RESET_LB, OPTION_LSW); */
+	}
+    }
+
+#ifdef HP100_DEBUG
+  printk( "hp100: %s: new LSW = 0x%x\n", dev->name, hp100_inw(OPTION_LSW) );
+#endif
+
+  /* Check for shared memory on the card, eventually remap it */
   hp100_page( HW_MAP );
-  mem_mapped = ( hp100_inw( OPTION_LSW ) & 
-                 ( HP100_MEM_EN | HP100_BM_WRITE | HP100_BM_READ ) ) != 0;
+  mem_mapped = (( hp100_inw( OPTION_LSW ) & ( HP100_MEM_EN ) ) != 0);
   mem_ptr_phys = mem_ptr_virt = NULL;
-  if ( mem_mapped )
+  memory_size = (8192<<( (hp100_inb(SRAM)>>5)&0x07));
+
+  /* For memory mapped or busmaster mode, we want the memory address */
+  if ( mem_mapped || (local_mode==1))
     {
-      mem_ptr_phys = (u_char *)( hp100_inw( MEM_MAP_LSW ) | 
-                               ( hp100_inw( MEM_MAP_MSW ) << 16 ) );
-      (u_int)mem_ptr_phys &= ~0x1fff;	/* 8k alignment */
+      mem_ptr_phys = (u_int *)( hp100_inw( MEM_MAP_LSW ) | 
+				( hp100_inw( MEM_MAP_MSW ) << 16 ) );
+      (u_int)mem_ptr_phys &= ~0x1fff;  /* 8k alignment */
+
       if ( bus == HP100_BUS_ISA && ( (u_long)mem_ptr_phys & ~0xfffff ) != 0 )
         {
+	  printk("hp100: %s: Can only use programmed i/o mode.\n", dev->name);
           mem_ptr_phys = NULL;
           mem_mapped = 0;
+	  local_mode=3; /* Use programmed i/o */
         }
-      if ( mem_mapped && bus == HP100_BUS_PCI )
-        {
-          if ( ( mem_ptr_virt = vremap( (u_long)mem_ptr_phys, 0x2000 ) ) == NULL )
-            {
-              printk( "hp100: vremap for high PCI memory at 0x%lx failed\n", (u_long)mem_ptr_phys );
-              mem_ptr_phys = NULL;
-              mem_mapped = 0;
-            }
-        }
-    }
-#else
-  mem_mapped = 0;
-  mem_ptr_phys = mem_ptr_virt = NULL;
+						
+      /* We do not need access to shared memory in busmaster mode */
+      /* However in slave mode we need to remap high (>1GB) card memory  */
+      if(local_mode!=1) /* = not busmaster */
+	{
+	  if ( bus == HP100_BUS_PCI )
+	    {
+	      /* We try with smaller memory sizes, if ioremap fails */
+	      for(; memory_size>16383; memory_size=memory_size/2)
+		{
+		  if((mem_ptr_virt=ioremap((u_long)mem_ptr_phys,memory_size))==NULL)
+		    {
+#ifdef HP100_DEBUG
+		      printk( "hp100: %s: ioremap for 0x%x bytes high PCI memory at 0x%lx failed\n", dev->name, memory_size, (u_long)mem_ptr_phys );
+#endif
+		    }	
+		  else
+		    {
+#ifdef HP100_DEBUG
+		      printk( "hp100: %s: remapped 0x%x bytes high PCI memory at 0x%lx to 0x%lx.\n", dev->name, memory_size, (u_long)mem_ptr_phys, (u_long)mem_ptr_virt);
 #endif
+		      break;
+		    }
+		}
+														
+	      if(mem_ptr_virt==NULL) /* all ioremap tries failed */
+		{
+		  printk("hp100: %s: Failed to ioremap the PCI card memory. Will have to use i/o mapped mode.\n", dev->name);
+		  local_mode=3;
+		  memory_size = (8192<<( (hp100_inb(SRAM)>>5)&0x07) );
+		}
+	    }
+	}
+						
+    }
+
+  if(local_mode==3) /* io mapped forced */
+    {
+      mem_mapped = 0;
+      mem_ptr_phys = mem_ptr_virt = NULL;
+      printk("hp100: %s: Using (slow) programmed i/o mode.\n", dev->name);
+    }
 
-  if ( ( dev -> priv = kmalloc( sizeof( struct hp100_private ), GFP_KERNEL ) ) == NULL )
+  /* Initialise the "private" data structure for this card. */
+  if ( (dev->priv=kmalloc(sizeof(struct hp100_private), GFP_KERNEL)) == NULL)
     return -ENOMEM;
-  memset( dev -> priv, 0, sizeof( struct hp100_private ) );
+  memset( dev->priv, 0, sizeof(struct hp100_private) );
 
-  lp = (struct hp100_private *)dev -> priv;
-  lp -> id = eid;
-  lp -> mem_mapped = mem_mapped;
-  lp -> mem_ptr_phys = mem_ptr_phys;
-  lp -> mem_ptr_virt = mem_ptr_virt;
+  lp = (struct hp100_private *)dev->priv;
+  lp->id = eid;
+  lp->chip = chip;
+  lp->mode = local_mode;
+  lp->pci_bus = pci_bus;
+  lp->bus = bus;
+  lp->pci_device_fn = pci_device_fn;
+  lp->priority_tx = hp100_priority_tx;
+  lp->rx_ratio = hp100_rx_ratio;
+  lp->mem_ptr_phys = mem_ptr_phys;
+  lp->mem_ptr_virt = mem_ptr_virt;
   hp100_page( ID_MAC_ADDR );
-  lp -> soft_model = hp100_inb( SOFT_MODEL );
-  lp -> mac1_mode = HP100_MAC1MODE3;
-  lp -> mac2_mode = HP100_MAC2MODE3;
-  
-  dev -> base_addr = ioaddr;
+  lp->soft_model = hp100_inb( SOFT_MODEL );
+  lp->mac1_mode = HP100_MAC1MODE3;
+  lp->mac2_mode = HP100_MAC2MODE3;
+  memset( &lp->hash_bytes, 0x00, 8 );
+
+  dev->base_addr = ioaddr;
+
+  lp->memory_size = memory_size;
+  lp->rx_ratio = hp100_rx_ratio; /* can be conf'd with insmod */
+
+  /* memory region for programmed i/o */
+  request_region( dev->base_addr, HP100_REGION_SIZE, eid->name );
+
+  dev->open = hp100_open;
+  dev->stop = hp100_close;
+
+  if (lp->mode==1) /* busmaster */
+    dev->hard_start_xmit = hp100_start_xmit_bm;
+  else
+    dev->hard_start_xmit = hp100_start_xmit;
+
+  dev->get_stats = hp100_get_stats;
+  dev->set_multicast_list = &hp100_set_multicast_list;
+
+  /* Ask the card for which IRQ line it is configured */
   hp100_page( HW_MAP );
-  dev -> irq = hp100_inb( IRQ_CHANNEL ) & HP100_IRQ_MASK;
-  if ( dev -> irq == 2 ) dev -> irq = 9;
-  lp -> memory_size = 0x200 << ( ( hp100_inb( SRAM ) & 0xe0 ) >> 5 );
-  lp -> rx_ratio = hp100_rx_ratio;
-
-  dev -> open = hp100_open;
-  dev -> stop = hp100_close;
-  dev -> hard_start_xmit = hp100_start_xmit;
-  dev -> get_stats = hp100_get_stats;
-  dev -> set_multicast_list = &hp100_set_multicast_list;
+  dev->irq = hp100_inb( IRQ_CHANNEL ) & HP100_IRQMASK;
+  if ( dev->irq == 2 )
+    dev->irq = 9;
 
-  request_region( dev -> base_addr, HP100_REGION_SIZE, eid -> name );
+  if(lp->mode==1) /* busmaster */
+    dev->dma=4; 
 
+  /* Ask the card for its MAC address and store it for later use. */
   hp100_page( ID_MAC_ADDR );
   for ( i = uc = 0; i < 6; i++ )
-    dev -> dev_addr[ i ] = hp100_inb( LAN_ADDR + i );
+    dev->dev_addr[ i ] = hp100_inb( LAN_ADDR + i );
 
+  /* Reset statistics (counters) */
   hp100_clear_stats( ioaddr );
 
   ether_setup( dev );
 
-  lp -> lan_type = hp100_sense_lan( dev );
+  /* If busmaster mode is wanted, a dma-capable memory area is needed for
+   * the rx and tx PDLs 
+   * PCI cards can access the whole PC memory. Therefore GFP_DMA is not
+   * needed for the allocation of the memory area. 
+   */
+
+  /* TODO: We do not need this with old cards, where PDLs are stored
+   * in the cards shared memory area. But currently, busmaster has been
+   * implemented/tested only with the lassen chip anyway... */
+  if(lp->mode==1) /* busmaster */
+    {
+      /* Get physically continous memory for TX & RX PDLs    */
+      if ( (lp->page_vaddr=kmalloc(MAX_RINGSIZE+0x0f,GFP_KERNEL) ) == NULL)
+        return -ENOMEM;
+      lp->page_vaddr_algn=((u_int *) ( ((u_int)(lp->page_vaddr)+0x0f) &~0x0f));
+      memset(lp->page_vaddr, 0, MAX_RINGSIZE+0x0f);
+
+#ifdef HP100_DEBUG_BM
+      printk("hp100: %s: Reserved DMA memory from 0x%x to 0x%x\n",
+      	     dev->name,
+             (u_int)lp->page_vaddr_algn,
+             (u_int)lp->page_vaddr_algn+MAX_RINGSIZE);
+#endif
+      lp->rxrcommit  = lp->txrcommit = 0;
+      lp->rxrhead    = lp->rxrtail   = &(lp->rxring[0]);
+      lp->txrhead    = lp->txrtail   = &(lp->txring[0]); 
+    }
+
+  /* Initialise the card. */
+  /* (I'm not really sure if it's a good idea to do this during probing, but 
+   * like this it's assured that the lan connection type can be sensed
+   * correctly)
+   */
+  hp100_hwinit( dev );
+
+  /* Try to find out which kind of LAN the card is connected to. */
+  lp->lan_type = hp100_sense_lan( dev );
      
-  printk( "%s: %s at 0x%x, IRQ %d, ",
-    dev -> name, lp -> id -> name, ioaddr, dev -> irq );
+  /* Print out a message what about what we think we have probed. */
+  printk( "hp100: %s: %s at 0x%x, IRQ %d, ",
+          dev->name, lp->id->name, ioaddr, dev->irq );
   switch ( bus ) {
-    case HP100_BUS_EISA: printk( "EISA" ); break;
-    case HP100_BUS_PCI:  printk( "PCI" );  break;
-    default:		 printk( "ISA" );  break;
+  case HP100_BUS_EISA: printk( "EISA" ); break;
+  case HP100_BUS_PCI:  printk( "PCI" );  break;
+  default:     printk( "ISA" );  break;
   }
   printk( " bus, %dk SRAM (rx/tx %d%%).\n",
-    lp -> memory_size >> ( 10 - 4 ), lp -> rx_ratio );
-  if ( mem_mapped )
+          lp->memory_size >> 10, lp->rx_ratio );
+
+  if ( lp->mode==2 ) /* memory mapped */ 
     {
-      printk( "%s: Memory area at 0x%lx-0x%lx",
-		dev -> name, (u_long)mem_ptr_phys, (u_long)mem_ptr_phys + 0x1fff );
+      printk( "hp100: %s: Memory area at 0x%lx-0x%lx",
+              dev->name,(u_long)mem_ptr_phys,(u_long)mem_ptr_phys+(u_long)lp->memory_size );
       if ( mem_ptr_virt )
-        printk( " (virtual base 0x%lx)", (u_long)mem_ptr_virt );
+	printk( " (virtual base 0x%lx)", (u_long)mem_ptr_virt );
       printk( ".\n" );
+
+      /* Set for info when doing ifconfig */
+      dev->mem_start = (u_long)mem_ptr_phys;
+      dev->mem_end = (u_long)mem_ptr_phys+(u_long)lp->memory_size;
     }
-  printk( "%s: ", dev -> name );
-  if ( lp -> lan_type != HP100_LAN_ERR )
+  printk( "hp100: %s: ", dev->name );
+  if ( lp->lan_type != HP100_LAN_ERR )
     printk( "Adapter is attached to " );
-  switch ( lp -> lan_type ) {
-    case HP100_LAN_100:
-      printk( "100Mb/s Voice Grade AnyLAN network.\n" );
-      break;
-    case HP100_LAN_10:
-      printk( "10Mb/s network.\n" );
-      break;
-    default:
-      printk( "Warning! Link down.\n" );
+  switch ( lp->lan_type ) {
+  case HP100_LAN_100:
+    printk( "100Mb/s Voice Grade AnyLAN network.\n" );
+    break;
+  case HP100_LAN_10:
+    printk( "10Mb/s network.\n" );
+    break;
+  default:
+    printk( "Warning! Link down.\n" );
   }
+
+  return 0;
+}
+
+
+/* This procedure puts the card into a stable init state */
+static void hp100_hwinit( struct device *dev )
+{
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4202, TRACE );
+  printk("hp100: %s: hwinit\n", dev->name);
+#endif
+
+  /* Initialise the card. -------------------------------------------- */
+
+  /* Clear all pending Ints and disable Ints */
+  hp100_page( PERFORMANCE );
+  hp100_outw( 0xfefe, IRQ_MASK );    /* mask off all ints */
+  hp100_outw( 0xffff, IRQ_STATUS );  /* clear all pending ints */
+
+  hp100_outw( HP100_INT_EN | HP100_RESET_LB, OPTION_LSW );
+  hp100_outw( HP100_TRI_INT | HP100_SET_HB, OPTION_LSW );
+
+  if(lp->mode==1)
+    { 
+      hp100_BM_shutdown( dev ); /* disables BM, puts cascade in reset */
+      wait();
+    }
+  else
+    {
+      hp100_outw( HP100_INT_EN | HP100_RESET_LB, OPTION_LSW );
+      hp100_cascade_reset( dev, TRUE );
+      hp100_page( MAC_CTRL );
+      hp100_andb( ~(HP100_RX_EN|HP100_TX_EN), MAC_CFG_1); 
+    }
 		
-  hp100_stop_interface( dev );
+  /* Initiate EEPROM reload */
+  hp100_load_eeprom( dev );
+		
+  wait();
+		
+  /* Go into reset again. */
+  hp100_cascade_reset( dev, TRUE );
+		
+  /* Set Option Registers to a safe state  */
+  hp100_outw( HP100_DEBUG_EN |
+              HP100_RX_HDR   |
+              HP100_EE_EN    |
+              HP100_BM_WRITE |
+              HP100_BM_READ  | HP100_RESET_HB |
+              HP100_FAKE_INT |
+              HP100_INT_EN   |
+	      HP100_MEM_EN   |
+              HP100_IO_EN    | HP100_RESET_LB, OPTION_LSW);
+		
+  hp100_outw( HP100_TRI_INT  |
+              HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW );
+
+  hp100_outb( HP100_PRIORITY_TX |
+              HP100_ADV_NXT_PKT |
+              HP100_TX_CMD      | HP100_RESET_LB, OPTION_MSW );
+
+  /* TODO: Configure MMU for Ram Test. */
+  /* TODO: Ram Test. */
+
+  /* Re-check if adapter is still at same i/o location      */
+  /* (If the base i/o in eeprom has been changed but the    */
+  /* registers had not been changed, a reload of the eeprom */
+  /* would move the adapter to the address stored in eeprom */
   
-  return 0;
+  /* TODO: Code to implement. */
+		
+  /* Until here it was code from HWdiscover procedure. */
+  /* Next comes code from mmuinit procedure of SCO BM driver which is
+   * called from HWconfigure in the SCO driver.  */
+
+  /* Initialise MMU, eventually switch on Busmaster Mode, initialise 
+   * multicast filter...
+   */
+  hp100_mmuinit( dev );
+
+  /* We don't turn the interrupts on here - this is done by start_interface. */
+  wait(); /* TODO: Do we really need this? */
+
+  /* Enable Hardware (e.g. unreset) */
+  hp100_cascade_reset( dev, FALSE );
+
+  /* ------- initialisation complete ----------- */
+
+  /* Finally try to log in the Hub if there may be a VG connection. */
+  if( lp->lan_type != HP100_LAN_10 )
+    hp100_login_to_vg_hub( dev, FALSE ); /* relogin */
 }
 
-/*
- *  open/close functions
+
+/* 
+ * mmuinit - Reinitialise Cascade MMU and MAC settings.
+ * Note: Must already be in reset and leaves card in reset. 
  */
-
-static int hp100_open( struct device *dev )
+static void hp100_mmuinit( struct device *dev )
 {
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
   int i;
-  int ioaddr = dev -> base_addr;
-  struct hp100_private *lp = (struct hp100_private *)dev -> priv;
 
-  if ( request_irq( dev -> irq, hp100_interrupt, SA_INTERRUPT, lp -> id -> name, NULL ) )
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4203, TRACE );
+  printk("hp100: %s: mmuinit\n",dev->name);
+#endif
+
+#ifdef HP100_DEBUG
+  if( 0!=(hp100_inw(OPTION_LSW)&HP100_HW_RST) )
     {
-      printk( "%s: unable to get IRQ %d\n", dev -> name, dev -> irq );
-      return -EAGAIN;
+      printk("hp100: %s: Not in reset when entering mmuinit. Fix me.\n",dev->name);
+      return;
     }
-  irq2dev_map[ dev -> irq ] = dev;
+#endif
 
-  MOD_INC_USE_COUNT;
-  
-  dev -> tbusy = 0;
-  dev -> trans_start = jiffies;
-  dev -> interrupt = 0;
-  dev -> start = 1;
-
-  lp -> lan_type = hp100_sense_lan( dev );
-  lp -> mac1_mode = HP100_MAC1MODE3;
-  lp -> mac2_mode = HP100_MAC2MODE3;
+  /* Make sure IRQs are masked off and ack'ed. */
+  hp100_page( PERFORMANCE );
+  hp100_outw( 0xfefe, IRQ_MASK );  /* mask off all ints */
+  hp100_outw( 0xffff, IRQ_STATUS );  /* ack IRQ */
+
+  /*
+   * Enable Hardware 
+   * - Clear Debug En, Rx Hdr Pipe, EE En, I/O En, Fake Int and Intr En
+   * - Set Tri-State Int, Bus Master Rd/Wr, and Mem Map Disable
+   * - Clear Priority, Advance Pkt and Xmit Cmd
+   */
+
+  hp100_outw( HP100_DEBUG_EN |
+              HP100_RX_HDR   |
+              HP100_EE_EN    | HP100_RESET_HB |
+              HP100_IO_EN    |
+              HP100_FAKE_INT |
+              HP100_INT_EN   | HP100_RESET_LB, OPTION_LSW );
+  	
+  hp100_outw( HP100_TRI_INT | HP100_SET_HB, OPTION_LSW);
+		
+  if(lp->mode==1) /* busmaster */
+    {
+      hp100_outw( HP100_BM_WRITE | 
+		  HP100_BM_READ  |
+		  HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW );
+    }
+  else if(lp->mode==2) /* memory mapped */
+    {
+      hp100_outw( HP100_BM_WRITE |
+		  HP100_BM_READ  | HP100_RESET_HB, OPTION_LSW );
+      hp100_outw( HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW );
+      hp100_outw( HP100_MEM_EN | HP100_SET_LB, OPTION_LSW );
+      hp100_outw( HP100_IO_EN | HP100_SET_LB, OPTION_LSW );
+    }
+  else if( lp->mode==3 ) /* i/o mapped mode */
+    {
+      hp100_outw( HP100_MMAP_DIS | HP100_SET_HB | 
+                  HP100_IO_EN    | HP100_SET_LB, OPTION_LSW );
+    }
+
+  hp100_page( HW_MAP );
+  hp100_outb( 0, EARLYRXCFG );
+  hp100_outw( 0, EARLYTXCFG );
   
-  hp100_page( MAC_CTRL );
-  hp100_orw( HP100_LINK_BEAT_DIS | HP100_RESET_LB, LAN_CFG_10 );
+  /*
+   * Enable Bus Master mode
+   */
+  if(lp->mode==1) /* busmaster */
+    {
+      /* Experimental: Set some PCI configuration bits */
+      hp100_page( HW_MAP );
+      hp100_andb( ~HP100_PDL_USE3, MODECTRL1 ); /* BM engine read maximum */
+      hp100_andb( ~HP100_TX_DUALQ, MODECTRL1 ); /* No Queue for Priority TX */
+
+      /* PCI Bus failures should result in a Misc. Interrupt */
+      hp100_orb( HP100_EN_BUS_FAIL, MODECTRL2);
+					
+      hp100_outw( HP100_BM_READ | HP100_BM_WRITE | HP100_SET_HB, OPTION_LSW );
+      hp100_page( HW_MAP );					
+      /* Use Burst Mode and switch on PAGE_CK */
+      hp100_orb( HP100_BM_BURST_RD |
+                 HP100_BM_BURST_WR, BM);
+      if((lp->chip==HP100_CHIPID_RAINIER)||(lp->chip==HP100_CHIPID_SHASTA))
+	hp100_orb( HP100_BM_PAGE_CK, BM );
+      hp100_orb( HP100_BM_MASTER, BM );		
+    }
+  else /* not busmaster */
+    {
+      hp100_page(HW_MAP);
+      hp100_andb(~HP100_BM_MASTER, BM );
+    }
+
+  /*
+   * Divide card memory into regions for Rx, Tx and, if non-ETR chip, PDLs
+   */  
+  hp100_page( MMU_CFG );
+  if(lp->mode==1) /* only needed for Busmaster */
+    {
+      int xmit_stop, recv_stop;
 
-  hp100_stop_interface( dev );
-  hp100_load_eeprom( dev );
+      if((lp->chip==HP100_CHIPID_RAINIER)||(lp->chip==HP100_CHIPID_SHASTA))
+        {
+          int pdl_stop;
+          
+          /*
+           * Each pdl is 508 bytes long. (63 frags * 4 bytes for address and
+           * 4 bytes for for header). We will leave NUM_RXPDLS * 508 (rounded
+           * to the next higher 1k boundary) bytes for the rx-pdl's
+	   * Note: For non-etr chips the transmit stop register must be
+	   * programmed on a 1k boundary, i.e. bits 9:0 must be zero. 
+	   */
+          pdl_stop  = lp->memory_size;
+          xmit_stop = ( pdl_stop-508*(MAX_RX_PDL)-16 )& ~(0x03ff);
+          recv_stop = ( xmit_stop * (lp->rx_ratio)/100 ) &~(0x03ff);
+          hp100_outw( (pdl_stop>>4)-1, PDL_MEM_STOP );
+#ifdef HP100_DEBUG_BM
+          printk("hp100: %s: PDL_STOP = 0x%x\n", dev->name, pdl_stop);
+#endif
+        }
+      else /* ETR chip (Lassen) in busmaster mode */
+        {
+          xmit_stop = ( lp->memory_size ) - 1;
+          recv_stop = ( ( lp->memory_size * lp->rx_ratio ) / 100 ) & ~(0x03ff);
+        }
+
+      hp100_outw( xmit_stop>>4 , TX_MEM_STOP );
+      hp100_outw( recv_stop>>4 , RX_MEM_STOP );
+#ifdef HP100_DEBUG_BM
+      printk("hp100: %s: TX_STOP  = 0x%x\n",dev->name,xmit_stop>>4);
+      printk("hp100: %s: RX_STOP  = 0x%x\n",dev->name,recv_stop>>4);
+#endif
+    }  
+  else /* Slave modes (memory mapped and programmed io)  */
+    {
+      hp100_outw( (((lp->memory_size*lp->rx_ratio)/100)>>4), RX_MEM_STOP );
+      hp100_outw( ((lp->memory_size - 1 )>>4), TX_MEM_STOP );  
+#ifdef HP100_DEBUG
+      printk("hp100: %s: TX_MEM_STOP: 0x%x\n", dev->name,hp100_inw(TX_MEM_STOP));
+      printk("hp100: %s: RX_MEM_STOP: 0x%x\n", dev->name,hp100_inw(RX_MEM_STOP));
+#endif
+    }
 
-  hp100_outw( HP100_MMAP_DIS | HP100_SET_HB | 
-              HP100_IO_EN | HP100_SET_LB, OPTION_LSW );
-  hp100_outw( HP100_DEBUG_EN | HP100_RX_HDR | HP100_EE_EN | HP100_RESET_HB |
-              HP100_FAKE_INT | HP100_RESET_LB, OPTION_LSW );
-  hp100_outw( HP100_ADV_NXT_PKT | HP100_TX_CMD | HP100_RESET_LB |
-                HP100_PRIORITY_TX | ( hp100_priority_tx ? HP100_SET_HB : HP100_RESET_HB ),
-              OPTION_MSW );
-              				
+  /* Write MAC address into page 1 */
   hp100_page( MAC_ADDRESS );
   for ( i = 0; i < 6; i++ )
-    hp100_outb( dev -> dev_addr[ i ], MAC_ADDR + i );
-  for ( i = 0; i < 8; i++ )		/* setup multicast filter to receive all */
-    hp100_outb( 0xff, HASH_BYTE0 + i );
+    hp100_outb( dev->dev_addr[ i ], MAC_ADDR + i );
+  
+  /* Zero the multicast hash registers */
+  for ( i = 0; i < 8; i++ )
+    hp100_outb( 0x0, HASH_BYTE0 + i );  
+  
+  /* Set up MAC defaults */
+  hp100_page( MAC_CTRL );
+ 
+  /* Go to LAN Page and zero all filter bits */
+  /* Zero accept error, accept multicast, accept broadcast and accept */
+  /* all directed packet bits */
+  hp100_andb( ~(HP100_RX_EN|
+		HP100_TX_EN|
+		HP100_ACC_ERRORED|
+		HP100_ACC_MC|
+		HP100_ACC_BC|
+		HP100_ACC_PHY),   MAC_CFG_1 );
+
+  hp100_outb( 0x00, MAC_CFG_2 );
+
+  /* Zero the frame format bit. This works around a training bug in the */
+  /* new hubs. */
+  hp100_outb( 0x00, VG_LAN_CFG_2); /* (use 802.3) */	
+
+  if(lp->priority_tx)
+    hp100_outb( HP100_PRIORITY_TX | HP100_SET_LB, OPTION_MSW );
+  else
+    hp100_outb( HP100_PRIORITY_TX | HP100_RESET_LB, OPTION_MSW );
+	
+  hp100_outb( HP100_ADV_NXT_PKT |
+	      HP100_TX_CMD      | HP100_RESET_LB, OPTION_MSW );
+	
+  /* If busmaster, initialize the PDLs */
+  if(lp->mode==1)
+    hp100_init_pdls( dev );
+
+  /* Go to performance page and initalize isr and imr registers */
   hp100_page( PERFORMANCE );
-  hp100_outw( 0xfefe, IRQ_MASK );	/* mask off all ints */
-  hp100_outw( 0xffff, IRQ_STATUS );	/* ack IRQ */
-  hp100_outw( (HP100_RX_PACKET | HP100_RX_ERROR | HP100_SET_HB) |
-              (HP100_TX_ERROR | HP100_SET_LB ), IRQ_MASK );
-              				/* and enable few */
-  hp100_reset_card();
-  hp100_page( MMU_CFG );
-  hp100_outw( ( lp -> memory_size * lp -> rx_ratio ) / 100, RX_MEM_STOP );
-  hp100_outw( lp -> memory_size - 1, TX_MEM_STOP );
-  hp100_unreset_card();
+  hp100_outw( 0xfefe, IRQ_MASK );  /* mask off all ints */
+  hp100_outw( 0xffff, IRQ_STATUS );  /* ack IRQ */
+}
+
+
+/*
+ *  open/close functions
+ */
+
+static int hp100_open( struct device *dev )
+{
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+#ifdef HP100_DEBUG_B
+  int ioaddr=dev->base_addr;
+#endif
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4204, TRACE );
+  printk("hp100: %s: open\n",dev->name);
+#endif
+		
+  /* New: if bus is PCI or EISA, interrupts might be shared interrupts */
+  if ( request_irq(dev->irq, hp100_interrupt,
+  		   lp->bus==HP100_BUS_PCI||lp->bus==HP100_BUS_EISA?SA_SHIRQ:SA_INTERRUPT,
+  		   lp->id->name, dev))
+    {
+      printk( "hp100: %s: unable to get IRQ %d\n", dev->name, dev->irq );
+      return -EAGAIN;
+    }
 
-  if ( lp -> lan_type == HP100_LAN_100 )
-    lp -> hub_status = hp100_login_to_vg_hub( dev );
+  MOD_INC_USE_COUNT;
+
+  dev->tbusy = 0;
+  dev->trans_start = jiffies;
+  dev->interrupt = 0;
+  dev->start = 1;
+
+  lp->lan_type = hp100_sense_lan( dev );
+  lp->mac1_mode = HP100_MAC1MODE3;
+  lp->mac2_mode = HP100_MAC2MODE3;
+  memset( &lp->hash_bytes, 0x00, 8 );
+
+  hp100_stop_interface( dev );
+ 
+  hp100_hwinit( dev );
 
-  hp100_start_interface( dev );
+  hp100_start_interface( dev ); /* sets mac modes, enables interrupts */
 
   return 0;
 }
 
+
+/* The close function is called when the interface is to be brought down */
 static int hp100_close( struct device *dev )
 {
-  int ioaddr = dev -> base_addr;
-  struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4205, TRACE );
+  printk("hp100: %s: close\n", dev->name);
+#endif
 
   hp100_page( PERFORMANCE );
-  hp100_outw( 0xfefe, IRQ_MASK );		/* mask off all IRQs */
+  hp100_outw( 0xfefe, IRQ_MASK );    /* mask off all IRQs */
 
   hp100_stop_interface( dev );
 
-  if ( lp -> lan_type == HP100_LAN_100 )	/* relogin */
-    hp100_login_to_vg_hub( dev );
+  if ( lp->lan_type == HP100_LAN_100 ) 
+    lp->hub_status=hp100_login_to_vg_hub( dev, FALSE );
+
+  dev->tbusy = 1;
+  dev->start = 0;
 
-  dev -> tbusy = 1;
-  dev -> start = 0;
+  free_irq( dev->irq, dev );
+
+#ifdef HP100_DEBUG
+  printk( "hp100: %s: close LSW = 0x%x\n", dev->name, hp100_inw(OPTION_LSW) );
+#endif
 
-  free_irq( dev -> irq, NULL );
-  irq2dev_map[ dev -> irq ] = NULL;
   MOD_DEC_USE_COUNT;
   return 0;
 }
 
-/* 
- *  transmit
+
+/*
+ * Configure the PDL Rx rings and LAN 
  */
-
-static int hp100_start_xmit( struct sk_buff *skb, struct device *dev )
+static void hp100_init_pdls( struct device *dev )
 {
-  int i, ok_flag;
-  int ioaddr = dev -> base_addr;
-  u_short val;
-  struct hp100_private *lp = (struct hp100_private *)dev -> priv;
-
-  if ( lp -> lan_type < 0 )
-    {
-      hp100_stop_interface( dev );
-      if ( ( lp -> lan_type = hp100_sense_lan( dev ) ) < 0 )
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  hp100_ring_t         *ringptr;
+  u_int                *pageptr;
+  int                  i;
+
+#ifdef HP100_DEBUG_B
+  int ioaddr = dev->base_addr;
+#endif
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4206, TRACE );
+  printk("hp100: %s: init pdls\n", dev->name);
+#endif
+
+  if(0==lp->page_vaddr_algn)
+    printk("hp100: %s: Warning: lp->page_vaddr_algn not initialised!\n",dev->name);
+  else
+    {
+      /* pageptr shall point into the DMA accessible memory region  */
+      /* we use this pointer to status the upper limit of allocated */
+      /* memory in the allocated page. */
+      /* note: align the pointers to the pci cache line size */
+      memset(lp->page_vaddr_algn, 0, MAX_RINGSIZE); /* Zero  Rx/Tx ring page */
+      pageptr=lp->page_vaddr_algn;
+						
+      lp->rxrcommit =0;
+      ringptr = lp->rxrhead = lp-> rxrtail = &(lp->rxring[0]);
+      
+      /* Initialise Rx Ring */
+      for (i=MAX_RX_PDL-1; i>=0; i--)
         {
-          printk( "%s: no connection found - check wire\n", dev -> name );
-          hp100_start_interface( dev );	/* 10Mb/s RX packets maybe handled */
-          return -EIO;
+          lp->rxring[i].next = ringptr;
+          ringptr=&(lp->rxring[i]);
+          pageptr+=hp100_init_rxpdl(dev, ringptr, pageptr);
         }
-      if ( lp -> lan_type == HP100_LAN_100 )
-        lp -> hub_status = hp100_login_to_vg_hub( dev );
-      hp100_start_interface( dev );
-    }
-  
-  if ( ( i = ( hp100_inl( TX_MEM_FREE ) & ~0x7fffffff ) ) < skb -> len + 16 )
-    {
-#ifdef HP100_DEBUG
-      printk( "hp100_start_xmit: rx free mem = 0x%x\n", i );
-#endif
-      if ( jiffies - dev -> trans_start < 2 * HZ ) return -EAGAIN;
-      if ( lp -> lan_type == HP100_LAN_100 && lp -> hub_status < 0 )
- 				/* 100Mb/s adapter isn't connected to hub */
+      
+      /* Initialise Tx Ring */
+      lp->txrcommit = 0;
+      ringptr = lp->txrhead = lp->txrtail = &(lp->txring[0]);
+      for (i=MAX_TX_PDL-1; i>=0; i--)
         {
-          printk( "%s: login to 100Mb/s hub retry\n", dev -> name );
-          hp100_stop_interface( dev );
-          lp -> hub_status = hp100_login_to_vg_hub( dev );
-          hp100_start_interface( dev );
+          lp->txring[i].next = ringptr;
+          ringptr=&(lp->txring[i]);
+          pageptr+=hp100_init_txpdl(dev, ringptr, pageptr);
         }
-       else
+    }
+}
+
+
+/* These functions "format" the entries in the pdl structure   */
+/* They return how much memory the fragments need.            */
+static int hp100_init_rxpdl( struct device *dev, register hp100_ring_t *ringptr, register u32 *pdlptr )
+{
+  /* pdlptr is starting adress for this pdl */
+
+  if( 0!=( ((unsigned)pdlptr) & 0xf) )
+    printk("hp100: %s: Init rxpdl: Unaligned pdlptr 0x%x.\n",dev->name,(unsigned)pdlptr);
+
+  ringptr->pdl       = pdlptr+1; 
+  ringptr->pdl_paddr = virt_to_bus(pdlptr+1);
+  ringptr->skb       = (void *) NULL; 
+
+  /* 
+   * Write address and length of first PDL Fragment (which is used for
+   * storing the RX-Header
+   * We use the 4 bytes _before_ the PDH in the pdl memory area to 
+   * store this information. (PDH is at offset 0x04)
+   */
+  /* Note that pdlptr+1 and not pdlptr is the pointer to the PDH */
+
+  *(pdlptr+2) =(u_int) virt_to_bus(pdlptr);  /* Address Frag 1 */ 
+  *(pdlptr+3) = 4;                           /* Length  Frag 1 */
+
+  return( ( ((MAX_RX_FRAG*2+2)+3) /4)*4 );
+}
+
+
+static int hp100_init_txpdl( struct device *dev, register hp100_ring_t *ringptr, register u32 *pdlptr )
+{
+  if( 0!=( ((unsigned)pdlptr) & 0xf) )
+    printk("hp100: %s: Init txpdl: Unaligned pdlptr 0x%x.\n",dev->name,(unsigned) pdlptr);
+
+  ringptr->pdl       = pdlptr; /* +1; */  
+  ringptr->pdl_paddr = virt_to_bus(pdlptr); /* +1 */
+  ringptr->skb = (void *) NULL;
+  
+  return((((MAX_TX_FRAG*2+2)+3)/4)*4);
+}
+
+
+/*
+ * hp100_build_rx_pdl allocates an skb_buff of maximum size plus two bytes 
+ * for possible odd word alignment rounding up to next dword and set PDL
+ * address for fragment#2 
+ * Returns: 0 if unable to allocate skb_buff
+ *          1 if successful
+ */
+int hp100_build_rx_pdl( hp100_ring_t *ringptr, struct device *dev )
+{
+#ifdef HP100_DEBUG_B
+  int ioaddr = dev->base_addr;
+#endif
+#ifdef HP100_DEBUG_BM
+  u_int *p;
+#endif
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4207, TRACE );
+  printk("hp100: %s: build rx pdl\n", dev->name);
+#endif
+
+  /* Allocate skb buffer of maximum size */
+  /* Note: This depends on the alloc_skb functions allocating more 
+   * space than requested, i.e. aligning to 16bytes */
+
+  ringptr->skb = dev_alloc_skb( ((MAX_ETHER_SIZE+2+3)/4)*4 );
+		
+  if(NULL!=ringptr->skb)
+    {
+      /* 
+       * Reserve 2 bytes at the head of the buffer to land the IP header
+       * on a long word boundary (According to the Network Driver section
+       * in the Linux KHG, this should help to increase performance.)
+       */
+      skb_reserve(ringptr->skb, 2);
+
+      ringptr->skb->dev=dev; 
+      ringptr->skb->data=(u_char *)skb_put(ringptr->skb, MAX_ETHER_SIZE );
+						
+      /* ringptr->pdl points to the beginning of the PDL, i.e. the PDH */
+      /* Note: 1st Fragment is used for the 4 byte packet status
+       * (receive header). Its PDL entries are set up by init_rxpdl. So 
+       * here we only have to set up the PDL fragment entries for the data
+       * part. Those 4 bytes will be stored in the DMA memory region 
+       * directly before the PDL. 
+       */
+#ifdef HP100_DEBUG_BM
+      printk("hp100: %s: build_rx_pdl: PDH@0x%x, skb->data (len %d) at 0x%x\n",
+      	     dev->name,
+	     (u_int) ringptr->pdl,
+	     ((MAX_ETHER_SIZE+2+3)/4)*4,
+	     (unsigned int) ringptr->skb->data);
+#endif
+
+      ringptr->pdl[0] = 0x00020000;                          /* Write PDH */
+      ringptr->pdl[3] = ((u_int)virt_to_bus(ringptr->skb->data)); 
+      ringptr->pdl[4] = MAX_ETHER_SIZE;                 /* Length of Data */
+						
+#ifdef HP100_DEBUG_BM
+      for(p=(ringptr->pdl); p<(ringptr->pdl+5); p++)
+        printk("hp100: %s: Adr 0x%.8x = 0x%.8x\n",dev->name,(u_int) p,(u_int) *p );
+#endif
+      return(1);
+    }
+  /* else: */
+  /* alloc_skb failed (no memory) -> still can receive the header
+   * fragment into PDL memory. make PDL safe by clearing msgptr and
+   * making the PDL only 1 fragment (i.e. the 4 byte packet status)
+   */
+#ifdef HP100_DEBUG_BM
+  printk("hp100: %s: build_rx_pdl: PDH@0x%x, No space for skb.\n",
+  	 dev->name,
+	 (u_int) ringptr->pdl);
+#endif
+
+  ringptr->pdl[0]=0x00010000;   /* PDH: Count=1 Fragment */
+
+  return(0);
+}
+
+
+/*
+ *  hp100_rxfill - attempt to fill the Rx Ring will empty skb's
+ *
+ * Makes assumption that skb's are always contiguous memory areas and
+ * therefore PDLs contain only 2 physical fragments.
+ * -  While the number of Rx PDLs with buffers is less than maximum
+ *      a.  Get a maximum packet size skb
+ *      b.  Put the physical address of the buffer into the PDL.
+ *      c.  Output physical address of PDL to adapter.
+ */
+static void hp100_rxfill( struct device *dev )
+{
+  int ioaddr=dev->base_addr; 
+
+  struct hp100_private  *lp      = (struct hp100_private *)dev->priv;
+  hp100_ring_t    *ringptr;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4208, TRACE );
+  printk("hp100: %s: rxfill\n",dev->name);
+#endif
+		
+  hp100_page( PERFORMANCE );
+
+  while (lp->rxrcommit < MAX_RX_PDL)
+    {
+      /*
+      ** Attempt to get a buffer and build a Rx PDL.
+      */
+      ringptr = lp->rxrtail;
+      if (0 == hp100_build_rx_pdl( ringptr, dev ))
         {
-          hp100_ints_off();
-          i = hp100_sense_lan( dev );
-          hp100_page( PERFORMANCE );
-          hp100_ints_on();
-          if ( i == HP100_LAN_ERR )
-            printk( "%s: link down detected\n", dev -> name );
-           else
-          if ( lp -> lan_type != i )
+          return;      /* None available, return */
+        }
+      
+      /* Hand this PDL over to the card */
+      /* Note: This needs performance page selected! */
+#ifdef HP100_DEBUG_BM
+      printk("hp100: %s: rxfill: Hand to card: pdl #%d @0x%x phys:0x%x, buffer: 0x%x\n",
+      	     dev->name,
+             lp->rxrcommit,
+             (u_int)ringptr->pdl,
+             (u_int)ringptr->pdl_paddr,
+             (u_int)ringptr->pdl[3]);
+#endif
+
+      hp100_outl( (u32)ringptr->pdl_paddr, RX_PDA); 
+      
+      lp->rxrcommit += 1;
+      lp->rxrtail = ringptr->next;
+    }
+}
+
+
+/*
+ * BM_shutdown - shutdown bus mastering and leave chip in reset state
+ */
+
+static void hp100_BM_shutdown( struct device *dev )
+{
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  unsigned long time;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4209, TRACE );
+  printk("hp100: %s: bm shutdown\n",dev->name);
+#endif
+
+  hp100_page( PERFORMANCE );
+  hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */
+  hp100_outw( 0xffff, IRQ_STATUS ); /* Ack all ints */
+
+  /* Ensure Interrupts are off */
+  hp100_outw( HP100_INT_EN | HP100_RESET_LB , OPTION_LSW );
+
+  /* Disable all MAC activity */
+  hp100_page( MAC_CTRL );
+  hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 );  /* stop rx/tx */
+
+  /* If cascade MMU is not already in reset */
+  if (0 != (hp100_inw(OPTION_LSW)&HP100_HW_RST) )
+    {
+      /* Wait 1.3ms (10Mb max packet time) to ensure MAC is idle so
+       * MMU pointers will not be reset out from underneath
+       */
+      hp100_page( MAC_CTRL );
+      for(time=0; time<5000; time++)
+        {
+          if( (hp100_inb(MAC_CFG_1)&(HP100_TX_IDLE|HP100_RX_IDLE))==
+              (HP100_TX_IDLE|HP100_RX_IDLE) ) break;
+        }
+      
+      /* Shutdown algorithm depends on the generation of Cascade */
+      if( lp->chip==HP100_CHIPID_LASSEN )
+        { /* ETR shutdown/reset */
+          /* Disable Busmaster mode and wait for bit to go to zero. */
+          hp100_page(HW_MAP);
+          hp100_andb( ~HP100_BM_MASTER, BM );
+          /* 100 ms timeout */
+          for(time=0; time<32000; time++)
             {
-              /* it's very heavy - all network setting must be changed!!! */
-              printk( "%s: cable change 10Mb/s <-> 100Mb/s detected\n", dev -> name );
-              lp -> lan_type = i;
-              hp100_stop_interface( dev );
-              if ( lp -> lan_type == HP100_LAN_100 )
-                lp -> hub_status = hp100_login_to_vg_hub( dev );
-              hp100_start_interface( dev );
+              if ( 0 == (hp100_inb( BM ) & HP100_BM_MASTER) ) break;
             }
-           else
+        }
+      else
+        { /* Shasta or Rainier Shutdown/Reset */
+          /* To ensure all bus master inloading activity has ceased,
+           * wait for no Rx PDAs or no Rx packets on card. 
+           */
+          hp100_page( PERFORMANCE );
+          /* 100 ms timeout */
+          for(time=0; time<10000; time++)
             {
-              printk( "%s: interface reset\n", dev -> name );
-              hp100_stop_interface( dev );
-              hp100_start_interface( dev );
+              /* RX_PDL: PDLs not executed. */
+              /* RX_PKT_CNT: RX'd packets on card. */
+              if ( (hp100_inb( RX_PDL ) == 0) &&
+                   (hp100_inb( RX_PKT_CNT ) == 0) ) break;
             }
-        }
-      dev -> trans_start = jiffies;
-      return -EAGAIN;
+          
+          if(time>=10000)
+            printk("hp100: %s: BM shutdown error.\n", dev->name);
+          
+          /* To ensure all bus master outloading activity has ceased,
+           * wait until the Tx PDA count goes to zero or no more Tx space
+           * available in the Tx region of the card. 
+           */
+          /* 100 ms timeout */
+          for(time=0; time<10000; time++) {
+            if ( (0 == hp100_inb( TX_PKT_CNT )) &&
+                 (0 != (hp100_inb( TX_MEM_FREE )&HP100_AUTO_COMPARE))) break;
+          } 
+          
+          /* Disable Busmaster mode */
+          hp100_page(HW_MAP);
+          hp100_andb( ~HP100_BM_MASTER, BM );
+        } /* end of shutdown procedure for non-etr parts */  
+						
+      hp100_cascade_reset( dev, TRUE );
     }
-    
-  if ( skb == NULL )
+  hp100_page( PERFORMANCE );
+  /* hp100_outw( HP100_BM_READ | HP100_BM_WRITE | HP100_RESET_HB, OPTION_LSW ); */
+  /* Busmaster mode should be shut down now. */
+}
+
+
+
+/* 
+ *  transmit functions
+ */
+
+/* tx function for busmaster mode */
+static int hp100_start_xmit_bm( struct sk_buff *skb, struct device *dev )
+{
+  int i, ok_flag;
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  hp100_ring_t *ringptr;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4210, TRACE );
+  printk("hp100: %s: start_xmit_bm\n",dev->name);
+#endif
+
+  if ( skb==NULL )
     {
       dev_tint( dev );
       return 0;
     }
-    
-  if ( skb -> len <= 0 ) return 0;
+	
+  if ( skb->len <= 0 ) return 0;
+	
+  /* Get Tx ring tail pointer */
+  if( lp->txrtail->next==lp->txrhead )
+    {
+      /* No memory. */
+#ifdef HP100_DEBUG
+      printk("hp100: %s: start_xmit_bm: No TX PDL available.\n", dev->name);
+#endif
+      /* not waited long enough since last tx? */
+      if ( jiffies - dev->trans_start < HZ/10 ) return -EAGAIN;
+
+      if ( lp->lan_type < 0 ) /* no LAN type detected yet? */
+	{
+	  hp100_stop_interface( dev );
+	  if ( ( lp->lan_type = hp100_sense_lan( dev ) ) < 0 )
+	    {
+	      printk( "hp100: %s: no connection found - check wire\n", dev->name );
+	      hp100_start_interface( dev );  /* 10Mb/s RX pkts maybe handled */
+	      return -EIO;
+	    }
+	  if ( lp->lan_type == HP100_LAN_100 )
+	    lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); /* relogin */
+	  hp100_start_interface( dev );
+	}
+				
+      if ( lp->lan_type == HP100_LAN_100 && lp->hub_status < 0 )
+	/* we have a 100Mb/s adapter but it isn't connected to hub */
+	{
+	  printk( "hp100: %s: login to 100Mb/s hub retry\n", dev->name );
+	  hp100_stop_interface( dev );
+	  lp->hub_status = hp100_login_to_vg_hub( dev, FALSE );
+	  hp100_start_interface( dev );
+	}
+      else
+	{
+	  hp100_ints_off();
+	  i = hp100_sense_lan( dev );
+	  hp100_ints_on();
+	  if ( i == HP100_LAN_ERR )
+	    printk( "hp100: %s: link down detected\n", dev->name );
+	  else
+	    if ( lp->lan_type != i ) /* cable change! */
+	      {
+		/* it's very hard - all network setting must be changed!!! */
+		printk( "hp100: %s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name );
+		lp->lan_type = i;
+		hp100_stop_interface( dev );
+		if ( lp->lan_type == HP100_LAN_100 )
+		  lp->hub_status = hp100_login_to_vg_hub( dev, FALSE );
+		hp100_start_interface( dev );
+	      }
+	    else
+	      {
+		printk( "hp100: %s: interface reset\n", dev->name );
+		hp100_stop_interface( dev );
+		hp100_start_interface( dev );
+	      }
+	}
+
+      dev->trans_start = jiffies;
+      return -EAGAIN;
+    }
+	
+  /*
+   * we have to turn int's off before modifying this, otherwise
+   * a tx_pdl_cleanup could occur at the same time
+   */
+  cli();
+  ringptr=lp->txrtail;
+  lp->txrtail=ringptr->next;
+	
+  /* Check whether packet has minimal packet size */
+  ok_flag = skb->len >= HP100_MIN_PACKET_SIZE;
+  i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE;
+					
+  ringptr->skb=skb;
+  ringptr->pdl[0]=((1<<16) | i);                /* PDH: 1 Fragment & length */
+  ringptr->pdl[1]=(u32)virt_to_bus(skb->data);  /* 1st Frag: Adr. of data */
+  if(lp->chip==HP100_CHIPID_SHASTA)
+    {
+      /* TODO:Could someone who has the EISA card please check if this works? */
+      ringptr->pdl[2]=i;
+    }
+  else /* Lassen */
+    {
+      /* In the PDL, don't use the padded size but the real packet size: */
+      ringptr->pdl[2]=skb->len;              /* 1st Frag: Length of frag */
+    }
+
+  /* Hand this PDL to the card. */
+  hp100_outl( ringptr->pdl_paddr, TX_PDA_L ); /* Low Prio. Queue */
+	
+  lp->txrcommit++;
+  sti();
+	
+  /* Update statistics */	
+  lp->stats.tx_packets++;
+#ifdef LINUX_2_1
+  lp->stats.tx_bytes += skb->len;
+#endif
+  dev->trans_start = jiffies;
+	
+  return 0;
+}
+
+
+/* clean_txring checks if packets have been sent by the card by reading
+ * the TX_PDL register from the performance page and comparing it to the
+ * number of commited packets. It then frees the skb's of the packets that
+ * obviously have been sent to the network.
+ *
+ * Needs the PERFORMANCE page selected. 
+ */
+static void hp100_clean_txring( struct device *dev )
+{
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  int    ioaddr = dev->base_addr;
+  int    donecount;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4211, TRACE );
+  printk("hp100: %s: clean txring\n", dev->name);
+#endif
 
-  for ( i = 0; i < 6000 && ( hp100_inw( OPTION_MSW ) & HP100_TX_CMD ); i++ )
+  /* How many PDLs have been transmitted? */
+  donecount=(lp->txrcommit)-hp100_inb(TX_PDL);
+
+#ifdef HP100_DEBUG
+  if(donecount>MAX_TX_PDL)
+    printk("hp100: %s: Warning: More PDLs transmitted than commited to card???\n",dev->name);
+#endif
+
+  for( ; 0!=donecount; donecount-- )
     {
-#ifdef HP100_DEBUG_TX
-      printk( "hp100_start_xmit: busy\n" );
-#endif    
+#ifdef HP100_DEBUG_BM
+      printk("hp100: %s: Free skb: data @0x%.8x txrcommit=0x%x TXPDL=0x%x, done=0x%x\n",
+             dev->name,
+	     (u_int) lp->txrhead->skb->data,
+	     lp->txrcommit,
+	     hp100_inb(TX_PDL),
+	     donecount);
+#endif
+      dev_kfree_skb( lp->txrhead->skb, FREE_WRITE );
+      lp->txrhead->skb=(void *)NULL;
+      lp->txrhead=lp->txrhead->next;
+      lp->txrcommit--;
     }
+}
 
-  hp100_ints_off();
-  val = hp100_inw( IRQ_STATUS );
-  hp100_outw( val & HP100_TX_COMPLETE, IRQ_STATUS );
-#ifdef HP100_DEBUG_TX
-  printk( "hp100_start_xmit: irq_status = 0x%x, len = %d\n", val, (int)skb -> len );
+
+/* tx function for slave modes */
+static int hp100_start_xmit( struct sk_buff *skb, struct device *dev )
+{
+  int i, ok_flag;
+  int ioaddr = dev->base_addr;
+  u_short val;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4212, TRACE );
+  printk("hp100: %s: start_xmit\n", dev->name);
 #endif
-  ok_flag = skb -> len >= HP100_MIN_PACKET_SIZE;
-  i = ok_flag ? skb -> len : HP100_MIN_PACKET_SIZE;
-  hp100_outw( i, DATA32 );		/* length to memory manager */
-  hp100_outw( i, FRAGMENT_LEN );
-  if ( lp -> mem_mapped )
-    {
-      if ( lp -> mem_ptr_virt )
-        {
-          memcpy( lp -> mem_ptr_virt, skb -> data, skb -> len );
-          if ( !ok_flag )
-            memset( lp -> mem_ptr_virt, 0, HP100_MIN_PACKET_SIZE - skb -> len );
+
+  if ( lp->lan_type < 0 ) /* no LAN type detected yet? */
+    {
+      hp100_stop_interface( dev );
+      if ( ( lp->lan_type = hp100_sense_lan( dev ) ) < 0 )
+        {
+          printk( "hp100: %s: no connection found - check wire\n", dev->name );
+          hp100_start_interface( dev );  /* 10Mb/s RX packets maybe handled */
+          return -EIO;
         }
-       else
+      if ( lp->lan_type == HP100_LAN_100 )
+        lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); /* relogin */
+      hp100_start_interface( dev );
+    }
+
+  /* If there is not enough free memory on the card... */
+  i=hp100_inl(TX_MEM_FREE)&0x7fffffff;
+  if ( !(((i/2)-539)>(skb->len+16) && (hp100_inb(TX_PKT_CNT)<255)) )
+    {
+#ifdef HP100_DEBUG
+      printk( "hp100: %s: start_xmit: tx free mem = 0x%x\n", dev->name, i );
+#endif
+      /* not waited long enough since last failed tx try? */
+      if ( jiffies - dev->trans_start < HZ/2 ) 
+	{
+#ifdef HP100_DEBUG
+	  printk("hp100: %s: trans_start timing problem\n", dev->name);
+#endif
+	  return -EAGAIN;
+	}
+      if ( lp->lan_type == HP100_LAN_100 && lp->hub_status < 0 )
+	/* we have a 100Mb/s adapter but it isn't connected to hub */
         {
-          memcpy_toio( lp -> mem_ptr_phys, skb -> data, skb -> len );
-          if ( !ok_flag )
-            memset_io( lp -> mem_ptr_phys, 0, HP100_MIN_PACKET_SIZE - skb -> len );
+          printk( "hp100: %s: login to 100Mb/s hub retry\n", dev->name );
+          hp100_stop_interface( dev );
+          lp->hub_status = hp100_login_to_vg_hub( dev, FALSE );
+          hp100_start_interface( dev );
         }
+      else
+        {
+          hp100_ints_off();
+          i = hp100_sense_lan( dev );
+          hp100_ints_on();
+          if ( i == HP100_LAN_ERR )
+            printk( "hp100: %s: link down detected\n", dev->name );
+	  else
+	    if ( lp->lan_type != i ) /* cable change! */
+	      {
+		/* it's very hard - all network setting must be changed!!! */
+		printk( "hp100: %s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name );
+		lp->lan_type = i;
+		hp100_stop_interface( dev );
+		if ( lp->lan_type == HP100_LAN_100 )
+		  lp->hub_status = hp100_login_to_vg_hub( dev, FALSE );
+		hp100_start_interface( dev );
+	      }
+	    else
+	      {
+		printk( "hp100: %s: interface reset\n", dev->name );
+		hp100_stop_interface( dev );
+		hp100_start_interface( dev );
+		udelay(1000);
+	      }
+        }
+      dev->trans_start = jiffies;
+      return -EAGAIN;
     }
-   else
+
+  for ( i=0; i<6000 && ( hp100_inb( OPTION_MSW ) & HP100_TX_CMD ); i++ )
+    {
+#ifdef HP100_DEBUG_TX
+      printk( "hp100: %s: start_xmit: busy\n", dev->name );
+#endif
+    }
+	
+  hp100_ints_off();
+  val = hp100_inw( IRQ_STATUS );
+  /* Ack / clear the interrupt TX_COMPLETE interrupt - this interrupt is set
+   * when the current packet being transmitted on the wire is completed. */
+  hp100_outw( HP100_TX_COMPLETE, IRQ_STATUS ); 
+#ifdef HP100_DEBUG_TX
+  printk("hp100: %s: start_xmit: irq_status=0x%.4x, irqmask=0x%.4x, len=%d\n",dev->name,val,hp100_inw(IRQ_MASK),(int)skb->len );
+#endif
+
+  ok_flag = skb->len >= HP100_MIN_PACKET_SIZE;
+  i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE;
+
+  hp100_outw( i, DATA32 );       /* tell card the total packet length */
+  hp100_outw( i, FRAGMENT_LEN ); /* and first/only fragment length    */
+	
+  if ( lp->mode==2 ) /* memory mapped */
+    {
+      if ( lp->mem_ptr_virt ) /* high pci memory was remapped */
+	{
+	  /* Note: The J2585B needs alignment to 32bits here!  */
+	  memcpy( lp->mem_ptr_virt, skb->data, ( skb->len +3 ) & ~3 );
+	  if ( !ok_flag )
+	    memset( lp->mem_ptr_virt, 0, HP100_MIN_PACKET_SIZE - skb->len );
+	}
+      else
+	{
+	  memcpy_toio( lp->mem_ptr_phys, skb->data, skb->len );
+	  if ( !ok_flag )
+	    memset_io( lp->mem_ptr_phys, 0, HP100_MIN_PACKET_SIZE - skb->len );
+	}
+    }
+  else /* programmed i/o */
     {
-      outsl( ioaddr + HP100_REG_DATA32, skb -> data, ( skb -> len + 3 ) >> 2 );
+      outsl( ioaddr + HP100_REG_DATA32, skb->data, ( skb->len + 3 ) >> 2 );
       if ( !ok_flag )
-        for ( i = ( skb -> len + 3 ) & ~3; i < HP100_MIN_PACKET_SIZE; i += 4 )
-          hp100_outl( 0, DATA32 );
+	for ( i = ( skb->len + 3 ) & ~3; i < HP100_MIN_PACKET_SIZE; i += 4 )
+	  hp100_outl( 0, DATA32 );
     }
-  hp100_outw( HP100_TX_CMD | HP100_SET_LB, OPTION_MSW ); /* send packet */
-  lp -> stats.tx_packets++;
-  dev -> trans_start = jiffies;
+	
+  hp100_outb( HP100_TX_CMD | HP100_SET_LB, OPTION_MSW ); /* send packet */
+	
+  lp->stats.tx_packets++;
+#ifdef LINUX_2_1
+  lp->stats.tx_bytes += skb->len;
+#endif
+  dev->trans_start=jiffies;
   hp100_ints_on();
-
+	
   dev_kfree_skb( skb, FREE_WRITE );
-
+	
 #ifdef HP100_DEBUG_TX
-  printk( "hp100_start_xmit: end\n" );
+  printk( "hp100: %s: start_xmit: end\n", dev->name );
 #endif
-
+	
   return 0;
 }
 
+
 /*
- *  receive - called from interrupt handler
+ * Receive Function (Non-Busmaster mode)
+ * Called when an "Receive Packet" interrupt occurs, i.e. the receive 
+ * packet counter is non-zero.
+ * For non-busmaster, this function does the whole work of transfering
+ * the packet to the host memory and then up to higher layers via skb
+ * and netif_rx. 
  */
 
 static void hp100_rx( struct device *dev )
 {
   int packets, pkt_len;
-  int ioaddr = dev -> base_addr;
-  struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
   u_int header;
   struct sk_buff *skb;
 
-#if 0
-  if ( lp -> lan_type < 0 )
-    {
-      if ( ( lp -> lan_type = hp100_sense_lan( dev ) ) == HP100_LAN_100 )
-        lp -> hub_status = hp100_login_to_vg_hub( dev );
-      hp100_page( PERFORMANCE );
-    }
+#ifdef DEBUG_B
+  hp100_outw( 0x4213, TRACE );
+  printk("hp100: %s: rx\n", dev->name);
 #endif
-  
+
+  /* First get indication of received lan packet */
+  /* RX_PKT_CND indicates the number of packets which have been fully */
+  /* received onto the card but have not been fully transfered of the card */
   packets = hp100_inb( RX_PKT_CNT );
-#ifdef HP100_DEBUG
+#ifdef HP100_DEBUG_RX
   if ( packets > 1 )
-    printk( "hp100_rx: waiting packets = %d\n", packets );
+    printk( "hp100: %s: rx: waiting packets = %d\n", dev->name,packets );
 #endif
+
   while ( packets-- > 0 )
     {
-      for ( pkt_len = 0; pkt_len < 6000 && ( hp100_inw( OPTION_MSW ) & HP100_ADV_NXT_PKT ); pkt_len++ )
+      /* If ADV_NXT_PKT is still set, we have to wait until the card has */
+      /* really advanced to the next packet. */
+      for (pkt_len=0; pkt_len<6000 &&(hp100_inb(OPTION_MSW)&HP100_ADV_NXT_PKT);
+	   pkt_len++ )
         {
-#ifdef HP100_DEBUG_TX
-          printk( "hp100_rx: busy, remaining packets = %d\n", packets );
+#ifdef HP100_DEBUG_RX
+          printk( "hp100: %s: rx: busy, remaining packets = %d\n", dev->name, packets );
 #endif    
         }
-      if ( lp -> mem_mapped )
+
+      /* First we get the header, which contains information about the */
+      /* actual length of the received packet. */
+      if( lp->mode==2 ) /* memory mapped mode */
         {
-          if ( lp -> mem_ptr_virt )
-            header = *(__u32 *)lp -> mem_ptr_virt;
-           else
-            header = readl( lp -> mem_ptr_phys );
+          if ( lp->mem_ptr_virt )    /* if memory was remapped */
+            header = *(__u32 *)lp->mem_ptr_virt;
+          else
+            header = readl( lp->mem_ptr_phys );
         }
-       else
+      else /* programmed i/o */
         header = hp100_inl( DATA32 );
+      
       pkt_len = header & HP100_PKT_LEN_MASK;
+
 #ifdef HP100_DEBUG_RX
-      printk( "hp100_rx: new packet - length = %d, errors = 0x%x, dest = 0x%x\n",
-      	header & HP100_PKT_LEN_MASK, ( header >> 16 ) & 0xfff8, ( header >> 16 ) & 7 );
+      printk( "hp100: %s: rx: new packet - length=%d, errors=0x%x, dest=0x%x\n",
+      	      dev->name,
+              header & HP100_PKT_LEN_MASK, (header>>16)&0xfff8,
+              (header>>16)&7);
 #endif
-      /*
-       * NOTE! This (and the skb_put() below) depends on the skb-functions
+    
+      /* Now we allocate the skb and transfer the data into it. */  
+      /* NOTE! This (and the skb_put() below) depends on the skb-functions
        * allocating more than asked (notably, aligning the request up to
        * the next 16-byte length).
        */
       skb = dev_alloc_skb( pkt_len );
-      if ( skb == NULL )
-        {
+      if ( skb == NULL ) /* Not enough memory->drop packet */
+	{
 #ifdef HP100_DEBUG
-          printk( "hp100_rx: couldn't allocate a sk_buff of size %d\n", pkt_len );
+	  printk( "hp100: %s: rx: couldn't allocate a sk_buff of size %d\n", dev->name, pkt_len );
 #endif
-          lp -> stats.rx_dropped++;
-        }
-       else
-        {
-          u_char *ptr;
-        
-          skb -> dev = dev;
-          ptr = (u_char *)skb_put( skb, pkt_len );
-          if ( lp -> mem_mapped )
+	  lp->stats.rx_dropped++;
+	}
+      else /* skb successfully allocated */
+	{
+	  u_char *ptr;
+      
+	  skb->dev = dev;
+      
+	  /* ptr to start of the sk_buff data area */
+	  ptr = (u_char *)skb_put( skb, pkt_len );
+      
+	  /* Now transfer the data from the card into that area */
+	  if ( lp->mode==2 )
             {
-              if ( lp -> mem_ptr_virt )
-                memcpy( ptr, lp -> mem_ptr_virt, ( pkt_len + 3 ) & ~3 );
-               else
-                memcpy_fromio( ptr, lp -> mem_ptr_phys, ( pkt_len + 3 ) & ~3 );
+              if ( lp->mem_ptr_virt )
+                memcpy( ptr, lp->mem_ptr_virt, ( pkt_len + 3 ) & ~3 );
+              /* Note alignment to 32bit transfers */
+              else
+                memcpy_fromio( ptr, lp->mem_ptr_phys, ( pkt_len + 3 ) & ~3 );
             }
-           else
-            insl( ioaddr + HP100_REG_DATA32, ptr, ( pkt_len + 3 ) >> 2 );
-          skb -> protocol = eth_type_trans( skb, dev );
-          netif_rx( skb );
-          lp -> stats.rx_packets++;
+	  else /* io mapped */
+	    insl( ioaddr + HP100_REG_DATA32, ptr, ( pkt_len + 3 ) >> 2 );
+      
+	  skb->protocol = eth_type_trans( skb, dev );
+
+	  netif_rx( skb );
+	  lp->stats.rx_packets++;
+#ifdef LINUX_2_1
+	  lp->stats.rx_bytes += skb->len;
+#endif
+      
 #ifdef HP100_DEBUG_RX
-          printk( "rx: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
-		ptr[ 0 ], ptr[ 1 ], ptr[ 2 ], ptr[ 3 ], ptr[ 4 ], ptr[ 5 ],
-		ptr[ 6 ], ptr[ 7 ], ptr[ 8 ], ptr[ 9 ], ptr[ 10 ], ptr[ 11 ] );
+	  printk( "hp100: %s: rx: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+	  	  dev->name,
+		  ptr[ 0 ], ptr[ 1 ], ptr[ 2 ], ptr[ 3 ], ptr[ 4 ], ptr[ 5 ],
+		  ptr[ 6 ], ptr[ 7 ], ptr[ 8 ], ptr[ 9 ], ptr[ 10 ], ptr[ 11 ] );
 #endif
-        }
-      hp100_outw( HP100_ADV_NXT_PKT | HP100_SET_LB, OPTION_MSW );
+	}
+  
+      /* Indicate the card that we have got the packet */
+      hp100_outb( HP100_ADV_NXT_PKT | HP100_SET_LB, OPTION_MSW );
+
       switch ( header & 0x00070000 ) {
-        case (HP100_MULTI_ADDR_HASH<<16):
-        case (HP100_MULTI_ADDR_NO_HASH<<16):
-          lp -> stats.multicast++; break;
+      case (HP100_MULTI_ADDR_HASH<<16):
+      case (HP100_MULTI_ADDR_NO_HASH<<16):
+	lp->stats.multicast++; break;
       }
-    }
+    } /* end of while(there are packets) loop */
 #ifdef HP100_DEBUG_RX
-   printk( "hp100_rx: end\n" );
+  printk( "hp100_rx: %s: end\n", dev->name );
+#endif
+}
+
+
+/* 
+ * Receive Function for Busmaster Mode
+ */
+static void hp100_rx_bm( struct device *dev )
+{
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  hp100_ring_t *ptr;
+  u_int header;
+  int pkt_len;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4214, TRACE );
+  printk("hp100: %s: rx_bm\n", dev->name);
+#endif
+
+#ifdef HP100_DEBUG
+  if(0==lp->rxrcommit)
+    {
+      printk("hp100: %s: rx_bm called although no PDLs were committed to adapter?\n", dev->name); 
+      return;
+    }
+  else
+
+    /* RX_PKT_CNT states how many PDLs are currently formatted and available to 
+     * the cards BM engine */
+    if( (hp100_inw(RX_PKT_CNT)&0x00ff) >= lp->rxrcommit)
+      {
+	printk("hp100: %s: More packets received than commited? RX_PKT_CNT=0x%x, commit=0x%x\n", dev->name, hp100_inw(RX_PKT_CNT)&0x00ff, lp->rxrcommit);
+	return;
+      }
 #endif
+
+  while( (lp->rxrcommit > hp100_inb(RX_PDL)) )
+    {
+      /*
+       * The packet was received into the pdl pointed to by lp->rxrhead (
+       * the oldest pdl in the ring 
+       */
+						
+      /* First we get the header, which contains information about the */
+      /* actual length of the received packet. */
+      
+      ptr=lp->rxrhead;
+      
+      header = *(ptr->pdl-1);
+      pkt_len = (header & HP100_PKT_LEN_MASK);
+
+#ifdef HP100_DEBUG_BM
+      printk( "hp100: %s: rx_bm: header@0x%x=0x%x length=%d, errors=0x%x, dest=0x%x\n",
+      	      dev->name,
+              (u_int) (ptr->pdl-1),(u_int) header,
+              pkt_len, 
+              (header>>16)&0xfff8,
+              (header>>16)&7);
+      printk( "hp100: %s: RX_PDL_COUNT:0x%x TX_PDL_COUNT:0x%x, RX_PKT_CNT=0x%x PDH=0x%x, Data@0x%x len=0x%x\n",
+      	      dev->name,
+	      hp100_inb( RX_PDL ),
+	      hp100_inb( TX_PDL ),
+	      hp100_inb( RX_PKT_CNT ),
+	      (u_int) *(ptr->pdl),
+	      (u_int) *(ptr->pdl+3),
+	      (u_int) *(ptr->pdl+4));
+#endif
+      
+      if( (pkt_len>=MIN_ETHER_SIZE) &&
+          (pkt_len<=MAX_ETHER_SIZE) )  
+        {
+	  if(ptr->skb==NULL)
+	    {
+	      printk("hp100: %s: rx_bm: skb null\n", dev->name);
+	      /* can happen if we only allocated room for the pdh due to memory shortage. */
+	      lp->stats.rx_dropped++;
+	    }
+	  else
+	    {
+	      skb_trim( ptr->skb, pkt_len );     /* Shorten it */
+	      ptr->skb->protocol = eth_type_trans( ptr->skb, dev );
+														
+	      netif_rx( ptr->skb );              /* Up and away... */
+
+	      lp->stats.rx_packets++;
+#ifdef LINUX_2_1
+	      lp->stats.rx_bytes += ptr->skb->len;
+#endif
+	    }
+
+          switch ( header & 0x00070000 ) {
+          case (HP100_MULTI_ADDR_HASH<<16):
+          case (HP100_MULTI_ADDR_NO_HASH<<16):
+            lp->stats.multicast++; break;
+          }
+        }
+      else
+        {
+#ifdef HP100_DEBUG
+          printk("hp100: %s: rx_bm: Received bad packet (length=%d)\n",dev->name,pkt_len);
+#endif
+	  if(ptr->skb!=NULL)
+	    dev_kfree_skb( ptr->skb, FREE_READ );					
+          lp->stats.rx_errors++;
+        }
+						
+      lp->rxrhead=lp->rxrhead->next;
+
+      /* Allocate a new rx PDL (so lp->rxrcommit stays the same) */
+      if (0 == hp100_build_rx_pdl( lp->rxrtail, dev ))
+        {
+	  /* No space for skb, header can still be received. */
+#ifdef HP100_DEBUG
+          printk("hp100: %s: rx_bm: No space for new PDL.\n", dev->name);
+#endif
+	  return;
+        } 
+      else
+        { /* successfully allocated new PDL - put it in ringlist at tail. */
+	  hp100_outl((u32)lp->rxrtail->pdl_paddr, RX_PDA);
+          lp->rxrtail=lp->rxrtail->next;
+	}
+						
+    }
 }
 
+
+
 /*
  *  statistics
  */
- 
-static struct enet_statistics *hp100_get_stats( struct device *dev )
+static hp100_stats_t *hp100_get_stats( struct device *dev )
 {
-  int ioaddr = dev -> base_addr;
+  int ioaddr = dev->base_addr;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4215, TRACE );
+#endif
 
   hp100_ints_off();
   hp100_update_stats( dev );
   hp100_ints_on();
-  return &((struct hp100_private *)dev -> priv) -> stats;
+  return &((struct hp100_private *)dev->priv)->stats;
 }
 
 static void hp100_update_stats( struct device *dev )
 {
-  int ioaddr = dev -> base_addr;
+  int ioaddr = dev->base_addr;
   u_short val;
-  struct hp100_private *lp = (struct hp100_private *)dev -> priv;
-         
-  hp100_page( MAC_CTRL );		/* get all statistics bytes */
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4216, TRACE );
+  printk("hp100: %s: update-stats\n", dev->name);
+#endif
+
+  /* Note: Statistics counters clear when read. */
+  hp100_page( MAC_CTRL ); 
   val = hp100_inw( DROPPED ) & 0x0fff;
-  lp -> stats.rx_errors += val;
-  lp -> stats.rx_over_errors += val;
+  lp->stats.rx_errors += val;
+  lp->stats.rx_over_errors += val;
   val = hp100_inb( CRC );
-  lp -> stats.rx_errors += val;
-  lp -> stats.rx_crc_errors += val;
+  lp->stats.rx_errors += val;
+  lp->stats.rx_crc_errors += val;
   val = hp100_inb( ABORT );
-  lp -> stats.tx_errors += val;
-  lp -> stats.tx_aborted_errors += val;
+  lp->stats.tx_errors += val;
+  lp->stats.tx_aborted_errors += val;
   hp100_page( PERFORMANCE );
 }
 
 static void hp100_clear_stats( int ioaddr )
 {
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4217, TRACE );
+  printk("hp100: %s: clear_stats\n", dev->name);
+#endif
+
   cli();
-  hp100_page( MAC_CTRL );		/* get all statistics bytes */
+  hp100_page( MAC_CTRL );    /* get all statistics bytes */
   hp100_inw( DROPPED );
   hp100_inb( CRC );
   hp100_inb( ABORT );
@@ -838,6 +2071,7 @@
   sti();
 }
 
+
 /*
  *  multicast setup
  */
@@ -848,293 +2082,899 @@
                                                           
 static void hp100_set_multicast_list( struct device *dev)
 {
-  int ioaddr = dev -> base_addr;
-  struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
 
-#ifdef HP100_DEBUG_MULTI
-  printk( "hp100_set_multicast_list: num_addrs = %d\n", dev->mc_count);
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4218, TRACE );
+  printk("hp100: %s: set_mc_list\n", dev->name);
 #endif
+
   cli();
   hp100_ints_off();
   hp100_page( MAC_CTRL );
-  hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 );	/* stop rx/tx */
+  hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 );  /* stop rx/tx */
 
-  if ( dev->flags&IFF_PROMISC)
+  if ( dev->flags & IFF_PROMISC )
     {
-      lp -> mac2_mode = HP100_MAC2MODE6;  /* promiscuous mode, all good */
-      lp -> mac1_mode = HP100_MAC1MODE6;  /* packets on the net */
+      lp->mac2_mode = HP100_MAC2MODE6;  /* promiscuous mode = get all good */
+      lp->mac1_mode = HP100_MAC1MODE6;  /* packets on the net */
+      memset( &lp->hash_bytes, 0xff, 8 );
     }
-   else
-  if ( dev->mc_count || dev->flags&IFF_ALLMULTI )
+  else if ( dev->mc_count || (dev->flags&IFF_ALLMULTI) )
+    {
+      lp->mac2_mode = HP100_MAC2MODE5;  /* multicast mode = get packets for */
+      lp->mac1_mode = HP100_MAC1MODE5;  /* me, broadcasts and all multicasts */
+#ifdef HP100_MULTICAST_FILTER		/* doesn't work!!! */
+      if ( dev -> flags & IFF_ALLMULTI )
+        {
+          /* set hash filter to receive all multicast packets */
+          memset( &lp->hash_bytes, 0xff, 8 );
+        }
+       else
+        {
+          int i, j, idx;
+          u_char *addrs;
+          struct dev_mc_list *dmi;
+
+          memset( &lp->hash_bytes, 0x00, 8 );
+#ifdef HP100_DEBUG
+          printk("hp100: %s: computing hash filter - mc_count = %i\n", dev -> name, dev -> mc_count );
+#endif 
+          for ( i = 0, dmi = dev -> mc_list; i < dev -> mc_count; i++, dmi = dmi -> next )
+            {
+              addrs = dmi -> dmi_addr;
+              if ( ( *addrs & 0x01 ) == 0x01 )	/* multicast address? */
+                {
+#ifdef HP100_DEBUG
+                  printk("hp100: %s: multicast = %02x:%02x:%02x:%02x:%02x:%02x, ",
+                  	dev -> name,
+        		addrs[ 0 ], addrs[ 1 ], addrs[ 2 ],
+        		addrs[ 3 ], addrs[ 4 ], addrs[ 5 ] );
+#endif 
+                  for ( j = idx = 0; j < 6; j++ )
+                    {
+                      idx ^= *addrs++ & 0x3f;
+                      printk( ":%02x:", idx );
+                    }
+#ifdef HP100_DEBUG
+                  printk("idx = %i\n", idx );
+#endif
+		  lp->hash_bytes[ idx >> 3 ] |= ( 1 << ( idx & 7 ) );
+                }
+            }
+        }
+#else
+      memset( &lp->hash_bytes, 0xff, 8 );
+#endif
+    }
+  else
+    {
+      lp->mac2_mode = HP100_MAC2MODE3;  /* normal mode = get packets for me */
+      lp->mac1_mode = HP100_MAC1MODE3;  /* and broadcasts */
+      memset( &lp->hash_bytes, 0x00, 8 );
+    }
+
+  if ( ( (hp100_inb(MAC_CFG_1) & 0x0f)!=lp->mac1_mode ) ||
+        ( hp100_inb(MAC_CFG_2)!=lp->mac2_mode ) ) 
     {
-      lp -> mac2_mode = HP100_MAC2MODE5;  /* multicast mode, packets for me */
-      lp -> mac1_mode = HP100_MAC1MODE5;  /* broadcasts and all multicasts */
+      int i;
+    
+      hp100_outb( lp->mac2_mode, MAC_CFG_2 );
+      hp100_andb( HP100_MAC1MODEMASK, MAC_CFG_1 ); /* clear mac1 mode bits */
+      hp100_orb( lp->mac1_mode, MAC_CFG_1 );       /* and set the new mode */
+
+      hp100_page( MAC_ADDRESS );
+      for ( i = 0; i < 8; i++ )
+        hp100_outb( lp->hash_bytes[ i ], HASH_BYTE0 + i );
+#ifdef HP100_DEBUG
+      printk("hp100: %s: mac1 = 0x%x, mac2 = 0x%x, multicast hash = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", 
+      		dev->name, lp->mac1_mode, lp->mac2_mode,
+      		lp->hash_bytes[ 0 ], lp->hash_bytes[ 1 ],
+      		lp->hash_bytes[ 2 ], lp->hash_bytes[ 3 ],
+      		lp->hash_bytes[ 4 ], lp->hash_bytes[ 5 ],
+      		lp->hash_bytes[ 6 ], lp->hash_bytes[ 7 ]
+      		);
+#endif 
+
+      if(lp->lan_type==HP100_LAN_100)
+        {
+#ifdef HP100_DEBUG
+  	  printk("hp100: %s: 100VG MAC settings have changed - relogin.\n", dev->name);
+#endif 
+	  lp->hub_status=hp100_login_to_vg_hub( dev, TRUE );  /* force a relogin to the hub */
+        }
     }
    else
     {
-      lp -> mac2_mode = HP100_MAC2MODE3;  /* normal mode, packets for me */
-      lp -> mac1_mode = HP100_MAC1MODE3;  /* and broadcasts */
+      int i;
+      u_char old_hash_bytes[ 8 ];
+
+      hp100_page( MAC_ADDRESS );
+      for ( i = 0; i < 8; i++ )
+        old_hash_bytes[ i ] = hp100_inb( HASH_BYTE0 + i );
+      if ( memcmp( old_hash_bytes, &lp->hash_bytes, 8 ) )
+        {
+          for ( i = 0; i < 8; i++ )
+            hp100_outb( lp->hash_bytes[ i ], HASH_BYTE0 + i );
+#ifdef HP100_DEBUG
+          printk("hp100: %s: multicast hash = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", 
+          	dev->name,
+      		lp->hash_bytes[ 0 ], lp->hash_bytes[ 1 ],
+      		lp->hash_bytes[ 2 ], lp->hash_bytes[ 3 ],
+      		lp->hash_bytes[ 4 ], lp->hash_bytes[ 5 ],
+      		lp->hash_bytes[ 6 ], lp->hash_bytes[ 7 ]
+      		);
+#endif 
+
+          if(lp->lan_type==HP100_LAN_100)
+            {
+#ifdef HP100_DEBUG
+  	      printk("hp100: %s: 100VG MAC settings have changed - relogin.\n", dev->name);
+#endif 
+   	      lp->hub_status=hp100_login_to_vg_hub( dev, TRUE );  /* force a relogin to the hub */
+            }
+        }
     }
 
-  hp100_outb( lp -> mac2_mode, MAC_CFG_2 );
-  hp100_andb( HP100_MAC1MODEMASK, MAC_CFG_1 );
-  hp100_orb( lp -> mac1_mode |
-  	     HP100_RX_EN | HP100_RX_IDLE |		/* enable rx */
-  	     HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1 );	/* enable tx */
+  hp100_page( MAC_CTRL );
+  hp100_orb( HP100_RX_EN | HP100_RX_IDLE |              /* enable rx */
+	     HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1 );  /* enable tx */
+
   hp100_page( PERFORMANCE );
   hp100_ints_on();
   sti();
 }
 
+
 /*
  *  hardware interrupt handling
  */
 
 static void hp100_interrupt( int irq, void *dev_id, struct pt_regs *regs )
 {
-  struct device *dev = (struct device *)irq2dev_map[ irq ];
-  struct hp100_private *lp;
+  struct device *dev = (struct device *)dev_id;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+
   int ioaddr;
-  u_short val;
+  u_int val;
 
   if ( dev == NULL ) return;
-  ioaddr = dev -> base_addr;
-  if ( dev -> interrupt )
-    printk( "%s: re-entering the interrupt handler\n", dev -> name );
+  ioaddr = dev->base_addr;
+
+  if ( dev->interrupt )
+    printk( "hp100: %s: re-entering the interrupt handler\n", dev->name );
   hp100_ints_off();
-  dev -> interrupt = 1;
-  hp100_page( PERFORMANCE );
+  dev->interrupt = 1;          /* mark that we are inside the handler */
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4219, TRACE );
+#endif
+
+  /*  hp100_page( PERFORMANCE ); */
   val = hp100_inw( IRQ_STATUS );
 #ifdef HP100_DEBUG_IRQ
-  printk( "hp100_interrupt: irq_status = 0x%x\n", val );
+  printk( "hp100: %s: mode=%x,IRQ_STAT=0x%.4x,RXPKTCNT=0x%.2x RXPDL=0x%.2x TXPKTCNT=0x%.2x TXPDL=0x%.2x\n",
+  	  dev->name,
+          lp->mode,
+	  (u_int)val,
+          hp100_inb( RX_PKT_CNT ),
+          hp100_inb( RX_PDL ),
+	  hp100_inb( TX_PKT_CNT ),
+	  hp100_inb( TX_PDL )
+	  );
 #endif
-  if ( val & HP100_RX_PACKET )
+
+  if(val==0) /* might be a shared interrupt */ 
     {
-      hp100_rx( dev );
-      hp100_outw( HP100_RX_PACKET, IRQ_STATUS );
+      dev->interrupt=0;
+      hp100_ints_on();
+      return;
     }
-  if ( val & (HP100_TX_SPACE_AVAIL | HP100_TX_COMPLETE) )
-    {
-      hp100_outw( val & (HP100_TX_SPACE_AVAIL | HP100_TX_COMPLETE), IRQ_STATUS );
+  /* We're only interested in those interrupts we really enabled. */
+  /* val &= hp100_inw( IRQ_MASK ); */
+
+  /* 
+   * RX_PDL_FILL_COMPL is set whenever a RX_PDL has been executed. A RX_PDL 
+   * is considered executed whenever the RX_PDL data structure is no longer 
+   * needed.
+   */
+  if ( val & HP100_RX_PDL_FILL_COMPL )
+    {
+      if(lp->mode==1)
+	hp100_rx_bm( dev );
+      else
+	{
+	  printk("hp100: %s: rx_pdl_fill_compl interrupt although not busmaster?\n", dev->name);
+	}
     }
+		
+  /* 
+   * The RX_PACKET interrupt is set, when the receive packet counter is
+   * non zero. We use this interrupt for receiving in slave mode. In
+   * busmaster mode, we use it to make sure we did not miss any rx_pdl_fill
+   * interrupts. If rx_pdl_fill_compl is not set and rx_packet is set, then
+   * we somehow have missed a rx_pdl_fill_compl interrupt.
+   */
+
+  if ( val & HP100_RX_PACKET  ) /* Receive Packet Counter is non zero */
+    {
+      if(lp->mode!=1) /* non busmaster */
+        hp100_rx( dev );
+      else if ( !(val & HP100_RX_PDL_FILL_COMPL ))
+	{
+	  /* Shouldnt happen - maybe we missed a RX_PDL_FILL Interrupt?  */
+	  hp100_rx_bm( dev );
+	}
+    }
+
+  /*
+   * Ack. that we have noticed the interrupt and thereby allow next one.
+   * Note that this is now done after the slave rx function, since first
+   * acknowledging and then setting ADV_NXT_PKT caused an extra interrupt
+   * on the J2573.
+   */
+  hp100_outw( val, IRQ_STATUS );
+
+  /*
+   * RX_ERROR is set when a packet is dropped due to no memory resources on 
+   * the card or when a RCV_ERR occurs. 
+   * TX_ERROR is set when a TX_ABORT condition occurs in the MAC->exists  
+   * only in the 802.3 MAC and happens when 16 collisions occur during a TX 
+   */
   if ( val & ( HP100_TX_ERROR | HP100_RX_ERROR ) )
     {
-      lp = (struct hp100_private *)dev -> priv;
-      hp100_update_stats( dev );
-      hp100_outw( val & (HP100_TX_ERROR | HP100_RX_ERROR), IRQ_STATUS );
-    }
 #ifdef HP100_DEBUG_IRQ
-  printk( "hp100_interrupt: end\n" );
+      printk("hp100: %s: TX/RX Error IRQ\n", dev->name);
 #endif
-  dev -> interrupt = 0;
+      hp100_update_stats( dev );
+      if(lp->mode==1)
+	{
+	  hp100_rxfill( dev );
+	  hp100_clean_txring( dev );
+	}
+    }
+				
+  /* 
+   * RX_PDA_ZERO is set when the PDA count goes from non-zero to zero. 
+   */
+  if ( (lp->mode==1)&&(val &(HP100_RX_PDA_ZERO)) )
+    hp100_rxfill( dev );
+		
+  /* 
+   * HP100_TX_COMPLETE interrupt occurs when packet transmitted on wire 
+   * is completed 
+   */
+  if ( (lp->mode==1) && ( val & ( HP100_TX_COMPLETE )) )
+    hp100_clean_txring( dev );
+
+  /* 
+   * MISC_ERROR is set when either the LAN link goes down or a detected
+   * bus error occurs.
+   */
+  if ( val & HP100_MISC_ERROR ) /* New for J2585B */
+    {
+      printk("hp100: %s: Misc. Error Interrupt - Check cabling.\n", dev->name);
+      if(lp->mode==1)
+	{
+	  hp100_clean_txring( dev );
+	  hp100_rxfill( dev );
+	}
+    }
+	
+  dev->interrupt = 0;
   hp100_ints_on();
 }
 
+
 /*
  *  some misc functions
  */
 
 static void hp100_start_interface( struct device *dev )
 {
-  int ioaddr = dev -> base_addr;
-  struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4220, TRACE );
+  printk("hp100: %s: hp100_start_interface\n",dev->name);
+#endif
 
   cli();
-  hp100_unreset_card();
-  hp100_page( MAC_CTRL );
-  hp100_outb( lp -> mac2_mode, MAC_CFG_2 );
-  hp100_andb( HP100_MAC1MODEMASK, MAC_CFG_1 );
-  hp100_orb( lp -> mac1_mode |
-             HP100_RX_EN | HP100_RX_IDLE |
-             HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1 );
+
+  /* Ensure the adapter does not want to request an interrupt when */
+  /* enabling the IRQ line to be active on the bus (i.e. not tri-stated) */
   hp100_page( PERFORMANCE );
-  hp100_outw( HP100_INT_EN | HP100_SET_LB, OPTION_LSW );
-  hp100_outw( HP100_TRI_INT | HP100_RESET_HB, OPTION_LSW );
-  if ( lp -> mem_mapped )
+  hp100_outw( 0xfefe, IRQ_MASK );  /* mask off all ints */
+  hp100_outw( 0xffff, IRQ_STATUS );  /* ack all IRQs */
+  hp100_outw( HP100_FAKE_INT|HP100_INT_EN|HP100_RESET_LB, OPTION_LSW);
+  /* Un Tri-state int. TODO: Check if shared interrupts can be realised? */
+  hp100_outw( HP100_TRI_INT | HP100_RESET_HB, OPTION_LSW ); 
+
+  if(lp->mode==1)
+    {
+      /* Make sure BM bit is set... */
+      hp100_page(HW_MAP);
+      hp100_orb( HP100_BM_MASTER, BM );
+      hp100_rxfill( dev );
+    }
+  else if(lp->mode==2)
     {
-      /* enable memory mapping */
+      /* Enable memory mapping. Note: Don't do this when busmaster. */
       hp100_outw( HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW );
     }
-  sti();
-} 
 
+  hp100_page(PERFORMANCE);
+  hp100_outw( 0xfefe, IRQ_MASK );  /* mask off all ints */
+  hp100_outw( 0xffff, IRQ_STATUS );  /* ack IRQ */
+
+  /* enable a few interrupts: */
+  if(lp->mode==1) /* busmaster mode */
+    {
+      hp100_outw( HP100_RX_PDL_FILL_COMPL |
+		  HP100_RX_PDA_ZERO  |  
+		  HP100_RX_ERROR     |  
+		  /* HP100_RX_PACKET    | */    
+		  /* HP100_RX_EARLY_INT |  */     HP100_SET_HB  |  
+		  /* HP100_TX_PDA_ZERO  |  */
+		  HP100_TX_COMPLETE  |  
+		  /* HP100_MISC_ERROR   |  */
+		  HP100_TX_ERROR     | HP100_SET_LB, IRQ_MASK );
+    } 
+  else
+    {  
+      hp100_outw( HP100_RX_PACKET  |
+		  HP100_RX_ERROR   | HP100_SET_HB |
+                  HP100_TX_ERROR   | HP100_SET_LB , IRQ_MASK );
+    }
+	
+  /* Enable MAC Tx and RX, set MAC modes, ... */
+  /* Note: This function also turns on the interrupts. */
+  hp100_set_multicast_list( dev );
+}
+
+
 static void hp100_stop_interface( struct device *dev )
 {
-  int ioaddr = dev -> base_addr;
-  u_short val;
-
-  hp100_outw( HP100_INT_EN | HP100_RESET_LB | 
-              HP100_TRI_INT | HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW );
-  val = hp100_inw( OPTION_LSW );
-  hp100_page( HW_MAP );
-  hp100_andb( HP100_BM_SLAVE, BM );
-  hp100_page( MAC_CTRL );
-  hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 );
-  if ( !(val & HP100_HW_RST) ) return;
-  for ( val = 0; val < 6000; val++ )
-    if ( ( hp100_inb( MAC_CFG_1 ) & (HP100_TX_IDLE | HP100_RX_IDLE) ) ==
-                                    (HP100_TX_IDLE | HP100_RX_IDLE) )
-      return;
-  printk( "%s: hp100_stop_interface - timeout\n", dev -> name );
+  struct hp100_private *lp = (struct hp100_private *)dev->priv; 
+  int ioaddr = dev->base_addr;
+  u_int val;
+
+#ifdef HP100_DEBUG_B
+  printk("hp100: %s: hp100_stop_interface\n",dev->name);
+  hp100_outw( 0x4221, TRACE );
+#endif
+
+  if (lp->mode==1) 
+    hp100_BM_shutdown( dev );
+  else
+    {
+      /* Note: MMAP_DIS will be reenabled by start_interface */
+      hp100_outw( HP100_INT_EN | HP100_RESET_LB | 
+                  HP100_TRI_INT | HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW );
+      val = hp100_inw( OPTION_LSW );
+   
+      hp100_page( MAC_CTRL );
+      hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 );
+
+      if ( !(val & HP100_HW_RST) ) return; /* If reset, imm. return ... */
+      /* ... else: busy wait until idle */
+      for ( val = 0; val < 6000; val++ )
+	if ( ( hp100_inb( MAC_CFG_1 ) & (HP100_TX_IDLE | HP100_RX_IDLE) ) ==
+	     (HP100_TX_IDLE | HP100_RX_IDLE) )
+	  {
+	    hp100_page(PERFORMANCE);
+	    return;
+	  }
+      printk( "hp100: %s: hp100_stop_interface - timeout\n", dev->name );
+      hp100_page(PERFORMANCE);
+    }
 }
 
+
 static void hp100_load_eeprom( struct device *dev )
 {
   int i;
-  int ioaddr = dev -> base_addr;
+  int ioaddr = dev->base_addr;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4222, TRACE );
+#endif
 
   hp100_page( EEPROM_CTRL );
   hp100_andw( ~HP100_EEPROM_LOAD, EEPROM_CTRL );
   hp100_orw( HP100_EEPROM_LOAD, EEPROM_CTRL );
-  for ( i = 0; i < 6000; i++ )
-    if ( !( hp100_inw( OPTION_MSW ) & HP100_EE_LOAD ) ) return;
-  printk( "%s: hp100_load_eeprom - timeout\n", dev -> name );
+  for ( i = 0; i < 10000; i++ )
+    if ( !( hp100_inb( OPTION_MSW ) & HP100_EE_LOAD ) ) return;
+  printk( "hp100: %s: hp100_load_eeprom - timeout\n", dev->name );
 }
 
-/* return values: LAN_10, LAN_100 or LAN_ERR (not connected or hub is down)... */
-
+
+/*  Sense connection status.
+ *  return values: LAN_10  - Connected to 10Mbit/s network
+ *                 LAN_100 - Connected to 100Mbit/s network
+ *                 LAN_ERR - not connected or 100Mbit/s Hub down
+ */
 static int hp100_sense_lan( struct device *dev )
 {
-  int i;
-  int ioaddr = dev -> base_addr;
+  int ioaddr = dev->base_addr;
   u_short val_VG, val_10;
-  struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4223, TRACE );
+#endif
 
   hp100_page( MAC_CTRL );
-  hp100_orw( HP100_VG_RESET, LAN_CFG_VG );
-  val_10 = hp100_inw( LAN_CFG_10 );
-  val_VG = hp100_inw( LAN_CFG_VG );
-#ifdef HP100_DEBUG_SENSE
-  printk( "hp100_sense_lan: val_VG = 0x%04x, val_10 = 0x%04x\n", val_VG, val_10 );
-#endif
-  if ( val_10 & HP100_LINK_BEAT_ST ) return HP100_LAN_10;
-  if ( lp -> id -> id == 0x02019F022 ) /* HP J27248B doesn't have 100Mb/s interface */
-    return HP100_LAN_ERR;
-  for ( i = 0; i < 2500; i++ )
+  val_10 = hp100_inb( 10_LAN_CFG_1 );
+  val_VG = hp100_inb( VG_LAN_CFG_1 );
+  hp100_page( PERFORMANCE );
+#ifdef HP100_DEBUG
+  printk( "hp100: %s: sense_lan: val_VG = 0x%04x, val_10 = 0x%04x\n", dev->name, val_VG, val_10 );
+#endif
+
+  if ( val_10 & HP100_LINK_BEAT_ST )	/* 10Mb connection is active */
+    return HP100_LAN_10;
+
+  if ( val_10 & HP100_AUI_ST )		/* have we BNC or AUI onboard? */
     {
-      val_VG = hp100_inw( LAN_CFG_VG );
-      if ( val_VG & HP100_LINK_CABLE_ST ) return HP100_LAN_100;
+      val_10 |= HP100_AUI_SEL | HP100_LOW_TH;
+      hp100_page( MAC_CTRL );
+      hp100_outb( val_10, 10_LAN_CFG_1 );
+      hp100_page( PERFORMANCE );
+      return HP100_LAN_10;
     }
+
+  if ( (lp->id->id == 0x02019F022) || 
+       (lp->id->id == 0x01042103c) ||
+       (lp->id->id == 0x01040103c) )
+    return HP100_LAN_ERR; /* Those cards don't have a 100 Mbit connector */
+
+  if ( val_VG & HP100_LINK_CABLE_ST ) /* Can hear the HUBs tone. */ 
+    return HP100_LAN_100;
   return HP100_LAN_ERR;
 }
 
+
+
 static int hp100_down_vg_link( struct device *dev )
 {
-  int ioaddr = dev -> base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  int ioaddr = dev->base_addr;
   unsigned long time;
-  int i;
+  long savelan, newlan;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4224, TRACE );
+  printk("hp100: %s: down_vg_link\n", dev->name);
+#endif
 
   hp100_page( MAC_CTRL );
-  for ( i = 2500; i > 0; i-- )
-    if ( hp100_inw( LAN_CFG_VG ) & HP100_LINK_CABLE_ST ) break;
-  if ( i <= 0 )				/* not signal - not logout */
+  time=jiffies+(HZ/4);
+  do{
+    if ( hp100_inb( VG_LAN_CFG_1 ) & HP100_LINK_CABLE_ST ) break;
+  } while (time>jiffies);
+
+  if ( jiffies >= time )       /* no signal->no logout */
     return 0;
-  hp100_andw( ~HP100_LINK_CMD, LAN_CFG_VG );
-  time = jiffies + 10; 
-  while ( time > jiffies )
-    if ( !( hp100_inw( LAN_CFG_VG ) & ( HP100_LINK_UP_ST | 
-                                        HP100_LINK_CABLE_ST | 
-                                        HP100_LINK_GOOD_ST ) ) )
-      return 0;
+
+  /* Drop the VG Link by clearing the link up cmd and load addr.*/
+
+  hp100_andb( ~( HP100_LOAD_ADDR| HP100_LINK_CMD), VG_LAN_CFG_1); 
+  hp100_orb( HP100_VG_SEL, VG_LAN_CFG_1); 
+
+  /* Conditionally stall for >250ms on Link-Up Status (to go down) */
+  time=jiffies+(HZ/2);
+  do{
+    if ( !(hp100_inb( VG_LAN_CFG_1) & HP100_LINK_UP_ST) ) break;
+  } while(time>jiffies);
+
 #ifdef HP100_DEBUG
-  printk( "hp100_down_vg_link: timeout\n" );
+  if (jiffies>=time)
+    printk("hp100: %s: down_vg_link: Link does not go down?\n", dev->name);
 #endif
-  return -EIO;
-}
 
-static int hp100_login_to_vg_hub( struct device *dev )
-{
-  int i;
-  int ioaddr = dev -> base_addr;
-  u_short val;
-  unsigned long time;  
+  /* To prevent condition where Rev 1 VG MAC and old hubs do not complete */
+  /* logout under traffic (even though all the status bits are cleared),  */
+  /* do this workaround to get the Rev 1 MAC in its idle state */
+  if ( lp->chip==HP100_CHIPID_LASSEN )
+    {
+      /* Reset VG MAC to insure it leaves the logoff state even if */
+      /* the Hub is still emitting tones */
+      hp100_andb(~HP100_VG_RESET, VG_LAN_CFG_1);
+      udelay(1500); /* wait for >1ms */
+      hp100_orb(HP100_VG_RESET, VG_LAN_CFG_1); /* Release Reset */
+      udelay(1500);
+    }
+
+  /* New: For lassen, switch to 10 Mbps mac briefly to clear training ACK */
+  /* to get the VG mac to full reset. This is not req.d with later chips */
+  /* Note: It will take the between 1 and 2 seconds for the VG mac to be */
+  /* selected again! This will be left to the connect hub function to */
+  /* perform if desired.  */
+  if (lp->chip==HP100_CHIPID_LASSEN)
+    {
+      /* Have to write to 10 and 100VG control registers simultaneously */
+      savelan=newlan=hp100_inl(10_LAN_CFG_1); /* read 10+100 LAN_CFG regs */
+      newlan &= ~(HP100_VG_SEL<<16);
+      newlan |= (HP100_DOT3_MAC)<<8;
+      hp100_andb( ~HP100_AUTO_MODE, MAC_CFG_3); /* Autosel off */
+      hp100_outl(newlan, 10_LAN_CFG_1);
+
+      /* Conditionally stall for 5sec on VG selected. */
+      time=jiffies+(HZ*5);
+      do{
+        if( !(hp100_inb(MAC_CFG_4) & HP100_MAC_SEL_ST) ) break;
+      } while(time>jiffies);
 
-  hp100_page( MAC_CTRL );
-  hp100_orw( HP100_VG_RESET, LAN_CFG_VG );
-  time = jiffies + ( HZ / 2 );
+      hp100_orb( HP100_AUTO_MODE, MAC_CFG_3); /* Autosel back on */
+      hp100_outl(savelan, 10_LAN_CFG_1);
+    }
+
+  time=jiffies+(3*HZ); /* Timeout 3s */
   do {
-    if ( hp100_inw( LAN_CFG_VG ) & HP100_LINK_CABLE_ST ) break;
-  } while ( time > jiffies );
-  if ( time <= jiffies )
+    if ( (hp100_inb( VG_LAN_CFG_1 )&HP100_LINK_CABLE_ST) == 0) break;
+  } while (time>jiffies);
+  
+  if(time<=jiffies)
     {
 #ifdef HP100_DEBUG
-      printk( "hp100_login_to_vg_hub: timeout for link\n" );
+      printk( "hp100: %s: down_vg_link: timeout\n", dev->name );
 #endif
       return -EIO;
     }
-    
-  if ( hp100_down_vg_link( dev ) < 0 )	/* if fail, try reset VG link */
-    {
-      hp100_andw( ~HP100_VG_RESET, LAN_CFG_VG );
-      hp100_orw( HP100_VG_RESET, LAN_CFG_VG );
-    }
-  /* bring up link */
-  hp100_orw( HP100_LOAD_ADDR | HP100_LINK_CMD, LAN_CFG_VG );
-  for ( i = 2500; i > 0; i-- )
-    if ( hp100_inw( LAN_CFG_VG ) & HP100_LINK_CABLE_ST ) break;
-  if ( i <= 0 )
+  
+  time=jiffies+(2*HZ); /* This seems to take a while.... */
+  do {} while (time>jiffies);
+  
+  return 0;
+}
+
+
+static int hp100_login_to_vg_hub( struct device *dev, u_short force_relogin )
+{
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  u_short val=0;
+  unsigned long time;
+  int startst;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4225, TRACE );
+  printk("hp100: %s: login_to_vg_hub\n", dev->name);
+#endif
+
+  /* Initiate a login sequence iff VG MAC is enabled and either Load Address
+   * bit is zero or the force relogin flag is set (e.g. due to MAC address or
+   * promiscuous mode change)
+   */
+  hp100_page( MAC_CTRL );
+  startst=hp100_inb( VG_LAN_CFG_1 );
+  if((force_relogin==TRUE)||(hp100_inb( MAC_CFG_4 )&HP100_MAC_SEL_ST))
     {
-#ifdef HP100_DEBUG
-      printk( "hp100_login_to_vg_hub: timeout for link (bring up)\n" );
+#ifdef HP100_DEBUG_TRAINING
+      printk("hp100: %s: Start training\n", dev->name);
 #endif
-      goto down_link;
-    }
 
-  time = jiffies + ( HZ / 2 );
-  do {   
-    val = hp100_inw( LAN_CFG_VG );
-    if ( ( val & ( HP100_LINK_UP_ST | HP100_LINK_GOOD_ST ) ) == 
-                 ( HP100_LINK_UP_ST | HP100_LINK_GOOD_ST ) )
-      return 0;	/* success */
-  } while ( time > jiffies );
-  if ( val & HP100_LINK_GOOD_ST )
-    printk( "%s: 100Mb cable training failed, check cable.\n", dev -> name );
-   else
-    printk( "%s: 100Mb node not accepted by hub, check frame type or security.\n", dev -> name );
+      /* Ensure VG Reset bit is 1 (i.e., do not reset)*/
+      hp100_orb( HP100_VG_RESET , VG_LAN_CFG_1 );
+
+      /* If Lassen AND auto-select-mode AND VG tones were sensed on */
+      /* entry then temporarily put them into force 100Mbit mode */
+      if((lp->chip==HP100_CHIPID_LASSEN)&&( startst & HP100_LINK_CABLE_ST ) )
+        hp100_andb( ~HP100_DOT3_MAC, 10_LAN_CFG_2 );
+						
+      /* Drop the VG link by zeroing Link Up Command and Load Address  */
+      hp100_andb( ~(HP100_LINK_CMD/* |HP100_LOAD_ADDR */), VG_LAN_CFG_1);
+
+#ifdef HP100_DEBUG_TRAINING
+      printk("hp100: %s: Bring down the link\n", dev->name);
+#endif
+
+      /* Wait for link to drop */
+      time = jiffies + (HZ/10); 
+      do {
+        if (~(hp100_inb( VG_LAN_CFG_1 )& HP100_LINK_UP_ST) ) break;
+      } while (time>jiffies);
+
+      /* Start an addressed training and optionally request promiscuous port */
+      if ( (dev->flags) & IFF_PROMISC )
+	{
+	  hp100_orb( HP100_PROM_MODE, VG_LAN_CFG_2);
+	  if(lp->chip==HP100_CHIPID_LASSEN)
+	    hp100_orw( HP100_MACRQ_PROMSC, TRAIN_REQUEST );
+	}
+      else
+	{
+	  hp100_andb( ~HP100_PROM_MODE, VG_LAN_CFG_2);
+	  /* For ETR parts we need to reset the prom. bit in the training
+	   * register, otherwise promiscious mode won't be disabled.
+	   */
+	  if(lp->chip==HP100_CHIPID_LASSEN)
+	    {
+	      hp100_andw( ~HP100_MACRQ_PROMSC, TRAIN_REQUEST );
+	    }
+	}
+
+      /* With ETR parts, frame format request bits can be set. */
+      if(lp->chip==HP100_CHIPID_LASSEN)
+        hp100_orb( HP100_MACRQ_FRAMEFMT_EITHER, TRAIN_REQUEST);
+
+      hp100_orb( HP100_LINK_CMD|HP100_LOAD_ADDR|HP100_VG_RESET, VG_LAN_CFG_1);
+
+      /* Note: Next wait could be omitted for Hood and earlier chips under */
+      /* certain circumstances */
+      /* TODO: check if hood/earlier and skip wait. */
+
+      /* Wait for either short timeout for VG tones or long for login    */
+      /* Wait for the card hardware to signalise link cable status ok... */
+      hp100_page( MAC_CTRL );
+      time = jiffies + ( 1*HZ ); /* 1 sec timeout for cable st */
+      do {
+        if ( hp100_inb( VG_LAN_CFG_1 ) & HP100_LINK_CABLE_ST ) break;
+      } while ( jiffies < time );
+      
+      if ( jiffies >= time )
+	{
+#ifdef HP100_DEBUG_TRAINING
+	  printk( "hp100: %s: Link cable status not ok? Training aborted.\n", dev->name );
+#endif  
+	}
+      else
+	{
+#ifdef HP100_DEBUG_TRAINING
+	  printk( "hp100: %s: HUB tones detected. Trying to train.\n", dev->name);
+#endif
+
+	  time = jiffies + ( 2*HZ ); /* again a timeout */
+	  do {
+	    val = hp100_inb( VG_LAN_CFG_1 );
+	    if ( (val & ( HP100_LINK_UP_ST )) )
+	      {
+#ifdef HP100_DEBUG_TRAINING
+		printk( "hp100: %s: Passed training.\n", dev->name);
+#endif
+		break;
+	      }
+	  } while ( time > jiffies );
+	}
+      
+      /* If LINK_UP_ST is set, then we are logged into the hub. */
+      if ( (jiffies<=time) && (val & HP100_LINK_UP_ST) )
+        {
+#ifdef HP100_DEBUG_TRAINING
+          printk( "hp100: %s: Successfully logged into the HUB.\n", dev->name);
+          if(lp->chip==HP100_CHIPID_LASSEN)
+            {
+	      val = hp100_inw(TRAIN_ALLOW);
+              printk( "hp100: %s: Card supports 100VG MAC Version \"%s\" ",
+              	      dev->name,(hp100_inw(TRAIN_REQUEST)&HP100_CARD_MACVER) ? "802.12" : "Pre");
+	      printk( "Driver will use MAC Version \"%s\"\n",
+                      ( val & HP100_HUB_MACVER) ? "802.12" : "Pre" ); 
+              printk( "hp100: %s: Frame format is %s.\n",dev->name,(val&HP100_MALLOW_FRAMEFMT)?"802.5":"802.3");
+            }
+#endif
+        }
+      else
+        {
+          /* If LINK_UP_ST is not set, login was not successful */
+          printk("hp100: %s: Problem logging into the HUB.\n",dev->name);
+          if(lp->chip==HP100_CHIPID_LASSEN)
+            {
+              /* Check allowed Register to find out why there is a problem. */
+              val = hp100_inw( TRAIN_ALLOW ); /* wont work on non-ETR card */
+#ifdef HP100_DEBUG_TRAINING
+              printk("hp100: %s: MAC Configuration requested: 0x%04x, HUB allowed: 0x%04x\n", dev->name, hp100_inw(TRAIN_REQUEST), val);
+#endif
+              if ( val & HP100_MALLOW_ACCDENIED )
+                printk("hp100: %s: HUB access denied.\n", dev->name);
+              if ( val & HP100_MALLOW_CONFIGURE )
+                printk("hp100: %s: MAC Configuration is incompatible with the Network.\n", dev->name);
+              if ( val & HP100_MALLOW_DUPADDR )
+                printk("hp100: %s: Duplicate MAC Address on the Network.\n", dev->name);
+            }
+        }
+      
+      /* If we have put the chip into forced 100 Mbit mode earlier, go back */
+      /* to auto-select mode */
+      
+      if( (lp->chip==HP100_CHIPID_LASSEN)&&(startst & HP100_LINK_CABLE_ST) )
+        {
+          hp100_page( MAC_CTRL );
+          hp100_orb( HP100_DOT3_MAC, 10_LAN_CFG_2 );
+        }
+     
+      val=hp100_inb(VG_LAN_CFG_1);
 
-down_link:
-  hp100_down_vg_link( dev );
-  hp100_page( MAC_CTRL );
-  hp100_andw( ~( HP100_LOAD_ADDR | HP100_PROM_MODE ), LAN_CFG_VG );
-  hp100_orw( HP100_LINK_CMD, LAN_CFG_VG );
+      /* Clear the MISC_ERROR Interrupt, which might be generated when doing the relogin */
+      hp100_page(PERFORMANCE);
+      hp100_outw( HP100_MISC_ERROR, IRQ_STATUS);
+					
+      if (val&HP100_LINK_UP_ST)
+        return(0); /* login was ok */
+      else
+        {
+          printk("hp100: %s: Training failed.\n", dev->name);
+          hp100_down_vg_link( dev );
+          return -EIO;
+        }
+    }
+  /* no forced relogin & already link there->no training. */
   return -EIO;
 }
 
+
+static void hp100_cascade_reset( struct device *dev, u_short enable )
+{
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  int i;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4226, TRACE );
+  printk("hp100: %s: cascade_reset\n", dev->name);
+#endif
+
+  if (enable==TRUE) 
+    {
+      hp100_outw( HP100_HW_RST | HP100_RESET_LB, OPTION_LSW );
+      if(lp->chip==HP100_CHIPID_LASSEN)
+	{
+	  /* Lassen requires a PCI transmit fifo reset */
+	  hp100_page( HW_MAP );
+	  hp100_andb( ~HP100_PCI_RESET, PCICTRL2 );
+	  hp100_orb( HP100_PCI_RESET, PCICTRL2 );
+	  /* Wait for min. 300 ns */
+	  /* we cant use jiffies here, because it may be */
+	  /* that we have disabled the timer... */
+	  for (i=0; i<0xffff; i++);
+	  hp100_andb( ~HP100_PCI_RESET, PCICTRL2 );
+	  hp100_page( PERFORMANCE );
+	}	
+    }
+  else
+    { /* bring out of reset */
+      hp100_outw(HP100_HW_RST|HP100_SET_LB, OPTION_LSW);
+      for (i=0; i<0xffff; i++ );
+      hp100_page(PERFORMANCE);
+    }
+}
+
+#ifdef HP100_DEBUG		
+void hp100_RegisterDump( struct device *dev )
+{
+  int ioaddr=dev->base_addr;
+  int Page;
+  int Register;
+
+  /* Dump common registers */
+  printk("hp100: %s: Cascade Register Dump\n", dev->name);
+  printk("hardware id #1: 0x%.2x\n",hp100_inb(HW_ID));
+  printk("hardware id #2/paging: 0x%.2x\n",hp100_inb(PAGING));
+  printk("option #1: 0x%.4x\n",hp100_inw(OPTION_LSW));
+  printk("option #2: 0x%.4x\n",hp100_inw(OPTION_MSW));
+
+  /* Dump paged registers */
+  for (Page = 0; Page < 8; Page++) 
+    {
+      /* Dump registers */
+      printk("page: 0x%.2x\n",Page);
+      outw( Page, ioaddr+0x02);
+      for (Register = 0x8; Register < 0x22; Register += 2)
+	{
+	  /* Display Register contents except data port */
+	  if (((Register != 0x10) && (Register != 0x12)) || (Page > 0))
+	    {
+	      printk("0x%.2x = 0x%.4x\n",Register,inw(ioaddr+Register));
+	    }
+	}
+    }
+  hp100_page(PERFORMANCE);
+}
+#endif
+
+
+
 /*
  *  module section
  */
  
 #ifdef MODULE
 
-static int hp100_port = -1;
+/* Parameters set by insmod */
+int hp100_port[5] = { 0, -1, -1, -1, -1 };
+#ifdef LINUX_2_1
+MODULE_PARM(hp100_port, "1-5i");
+#endif
 
-static char devicename[9] = { 0, };
-static struct device dev_hp100 = {
-	devicename, /* device name is inserted by linux/drivers/net/net_init.c */
-	0, 0, 0, 0,
-	0, 0,
-	0, 0, 0, NULL, hp100_probe
-};
+#ifdef LINUX_2_1
+char hp100_name[5][IFNAMSIZ] = { "", "", "", "", "" };
+MODULE_PARM(hp100_name, "1-5c" __MODULE_STRING(IFNAMSIZ));
+#else
+static char devname[5][IFNAMSIZ] = { "", "", "", "", "" };
+static char *hp100_name[5] = { devname[0], devname[1],
+                               devname[2], devname[3],
+                               devname[4] };
+#endif
+
+/* List of devices */
+static struct device *hp100_devlist[5] = { NULL, NULL, NULL, NULL, NULL };
+
+/*
+ * Note: if you have more than five 100vg cards in your pc, feel free to
+ * increase this value 
+ */
+
+/*
+ * Note: to register three eisa or pci devices, use:
+ * option hp100 hp100_port=0,0,0
+ *        to register one card at io 0x280 as eth239, use:
+ * option hp100 hp100_port=0x280 hp100_name=eth239
+ */
 
 int init_module( void )
 {
-  if (hp100_port == 0 && !EISA_bus)
-    printk("HP100: You should not use auto-probing with insmod!\n");
-  if ( hp100_port > 0 )
-    dev_hp100.base_addr = hp100_port;
-  if ( register_netdev( &dev_hp100 ) != 0 )
-    return -EIO;
-  return 0;
-}         
+  int	i;
+  int	ret = 0;
+
+  if (hp100_port == 0 && !EISA_bus && !pcibios_present())
+    printk("hp100: You should not use auto-probing with insmod!\n");
+
+  /* Loop on all possible base addresses */
+  i = -1;
+  while((hp100_port[++i] != -1) && (i < 5))
+    {
+      /* Create device and set basics args */
+      hp100_devlist[i] = kmalloc(sizeof(struct device), GFP_KERNEL);
+      memset(hp100_devlist[i], 0x00, sizeof(struct device));
+      hp100_devlist[i]->name = hp100_name[i];
+      hp100_devlist[i]->base_addr = hp100_port[i];
+      hp100_devlist[i]->init = &hp100_probe;
+
+      /* Try to create the device */
+      if(register_netdev(hp100_devlist[i]) != 0)
+	{
+	  /* DeAllocate everything */
+	  /* Note: if dev->priv is mallocated, there is no way to fail */
+	  kfree_s(hp100_devlist[i], sizeof(struct device));
+	  hp100_devlist[i] = (struct device *) NULL;
+	  ret = -EIO;
+	}
+    }			/* Loop over all devices */
+
+  return ret;
+}
 
 void cleanup_module( void )
 {
-  unregister_netdev( &dev_hp100 );
-  release_region( dev_hp100.base_addr, HP100_REGION_SIZE );
-  if ( ((struct hp100_private *)dev_hp100.priv) -> mem_ptr_virt )
-    vfree( ((struct hp100_private *)dev_hp100.priv) -> mem_ptr_virt );
-  kfree_s( dev_hp100.priv, sizeof( struct hp100_private ) );
-  dev_hp100.priv = NULL;
+  int		i;
+
+  /* TODO: Check if all skb's are released/freed. */
+  for(i = 0; i < 5; i++)
+    if(hp100_devlist[i] != (struct device *) NULL)
+      {
+	unregister_netdev( hp100_devlist[i] );
+	release_region( hp100_devlist[i]->base_addr, HP100_REGION_SIZE );
+	if( ((struct hp100_private *)hp100_devlist[i]->priv)->mode==1 ) /* busmaster */
+	  kfree_s( ((struct hp100_private *)hp100_devlist[i]->priv)->page_vaddr, MAX_RINGSIZE+0x0f); 
+	if ( ((struct hp100_private *)hp100_devlist[i]->priv) -> mem_ptr_virt )
+	  iounmap( ((struct hp100_private *)hp100_devlist[i]->priv) -> mem_ptr_virt );
+	kfree_s( hp100_devlist[i]->priv, sizeof( struct hp100_private ) );
+	hp100_devlist[i]->priv = NULL;
+	kfree_s(hp100_devlist[i], sizeof(struct device));
+	hp100_devlist[i] = (struct device *) NULL;
+      }
 }
 
-#endif
+#endif		/* MODULE */
+
+
+
+/*
+ * Local variables:
+ *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c hp100.c"
+ *  c-indent-level: 2
+ *  tab-width: 8
+ * End:
+ */
diff -u --recursive --new-file v2.0.30/linux/drivers/net/hp100.h linux/drivers/net/hp100.h
--- v2.0.30/linux/drivers/net/hp100.h	Mon May  6 02:26:08 1996
+++ linux/drivers/net/hp100.h	Tue Aug  5 09:11:35 1997
@@ -1,9 +1,10 @@
 /*
  * hp100.h: Hewlett Packard HP10/100VG ANY LAN ethernet driver for Linux.
  *
- * Author:  Jaroslav Kysela, <perex@pf.jcu.cz>
+ * $Id: hp100.h,v 1.51 1997/04/08 14:26:42 floeff Exp floeff $
  *
- * Header file...
+ * Authors:  Jaroslav Kysela, <perex@pf.jcu.cz>
+ *           Siegfried Loeffler <floeff@tunix.mathematik.uni-stuttgart.de>
  *
  * This driver is based on the 'hpfepkt' crynwr packet driver.
  *
@@ -16,9 +17,10 @@
 /****************************************************************************
  *  Hardware Constants
  ****************************************************************************/
- 
-/*
- *  ATT2MD01 Register Page Constants
+
+/* 
+ * Page Identifiers
+ * (Swap Paging Register, PAGING, bits 3:0, Offset 0x02)
  */
 
 #define HP100_PAGE_PERFORMANCE	0x0	/* Page 0 */
@@ -30,11 +32,8 @@
 #define HP100_PAGE_ID_MAC_ADDR	0x6	/* Page 6 */
 #define HP100_PAGE_MMU_POINTER	0x7	/* Page 7 */
 
-/*
- *  ATT2MD01 Register Addresses
- */
 
-/*  Present on all pages  */
+/* Registers that are present on all pages  */
 
 #define HP100_REG_HW_ID		0x00	/* R:  (16) Unique card ID           */
 #define HP100_REG_TRACE		0x00	/* W:  (16) Used for debug output    */
@@ -47,14 +46,29 @@
 
 #define HP100_REG_IRQ_STATUS	0x08	/* RW: (16) Which ints are pending   */
 #define HP100_REG_IRQ_MASK	0x0a	/* RW: (16) Select ints to allow     */
-#define HP100_REG_FRAGMENT_LEN	0x0c	/* RW: (16)12:0 Current fragment len */
+#define HP100_REG_FRAGMENT_LEN	0x0c	/* W: (16)12:0 Current fragment len */
+/* Note: For 32 bit systems, fragment len and offset registers are available */
+/*       at offset 0x28 and 0x2c, where they can be written as 32bit values. */
 #define HP100_REG_OFFSET	0x0e	/* RW: (16)12:0 Offset to start read */
 #define HP100_REG_DATA32	0x10	/* RW: (32) I/O mode data port       */
 #define HP100_REG_DATA16	0x12	/* RW: WORDs must be read from here  */
 #define HP100_REG_TX_MEM_FREE	0x14	/* RD: (32) Amount of free Tx mem    */
+#define HP100_REG_TX_PDA_L      0x14    /* W: (32) BM: Ptr to PDL, Low Pri  */
+#define HP100_REG_TX_PDA_H      0x1c    /* W: (32) BM: Ptr to PDL, High Pri */
 #define HP100_REG_RX_PKT_CNT	0x18	/* RD: (8) Rx count of pkts on card  */
 #define HP100_REG_TX_PKT_CNT	0x19	/* RD: (8) Tx count of pkts on card  */
-                                        
+#define HP100_REG_RX_PDL        0x1a    /* R: (8) BM: # rx pdl not executed */
+#define HP100_REG_TX_PDL        0x1b    /* R: (8) BM: # tx pdl not executed */
+#define HP100_REG_RX_PDA        0x18    /* W: (32) BM: Up to 31 addresses */
+                                        /*             which point to a PDL */
+#define HP100_REG_SL_EARLY      0x1c    /*    (32) Enhanced Slave Early Rx */
+#define HP100_REG_STAT_DROPPED  0x20    /* R (12) Dropped Packet Counter */
+#define HP100_REG_STAT_ERRORED  0x22    /* R (8) Errored Packet Counter */
+#define HP100_REG_STAT_ABORT    0x23    /* R (8) Abort Counter/OW Coll. Flag */
+#define HP100_REG_RX_RING       0x24    /* W (32) Slave: RX Ring Pointers */
+#define HP100_REG_32_FRAGMENT_LEN 0x28  /* W (13) Slave: Fragment Length Reg */
+#define HP100_REG_32_OFFSET     0x2c    /* W (16) Slave: Offset Register */
+
 /*  Page 1 - MAC Address/Hash Table  */
 
 #define HP100_REG_MAC_ADDR	0x08	/* RW: (8) Cards MAC address	     */
@@ -68,27 +82,46 @@
 #define HP100_REG_IRQ_CHANNEL	0x0d	/* RW: (8) IRQ and edge/level int    */
 #define HP100_REG_SRAM		0x0e	/* RW: (8) How much RAM on card      */
 #define HP100_REG_BM		0x0f	/* RW: (8) Controls BM functions     */
+
+/* New on Page 2 for ETR chips: */
+#define HP100_REG_MODECTRL1     0x10    /* RW: (8) Mode Control 1 */
+#define HP100_REG_MODECTRL2     0x11    /* RW: (8) Mode Control 2 */
+#define HP100_REG_PCICTRL1      0x12    /* RW: (8) PCI Cfg 1 */
+#define HP100_REG_PCICTRL2      0x13    /* RW: (8) PCI Cfg 2 */
+#define HP100_REG_PCIBUSMLAT    0x15    /* RW: (8) PCI Bus Master Latency */
+#define HP100_REG_EARLYTXCFG    0x16    /* RW: (16) Early TX Cfg/Cntrl Reg */
+#define HP100_REG_EARLYRXCFG    0x18    /* RW: (8) Early RX Cfg/Cntrl Reg */
+#define HP100_REG_ISAPNPCFG1    0x1a    /* RW: (8) ISA PnP Cfg/Cntrl Reg 1 */
+#define HP100_REG_ISAPNPCFG2    0x1b    /* RW: (8) ISA PnP Cfg/Cntrl Reg 2 */
                                         
 /*  Page 3 - EEPROM/Boot ROM  */
 
 #define HP100_REG_EEPROM_CTRL	0x08	/* RW: (16) Used to load EEPROM      */
+#define HP100_REG_BOOTROM_CTRL  0x0a
                                         
-/*  Page 4 - LAN Configuration  */
+/*  Page 4 - LAN Configuration  (MAC_CTRL) */
 
-#define HP100_REG_LAN_CFG_10	0x08	/* RW: (16) Set 10M XCVR functions   */
-#define HP100_REG_LAN_CFG_VG	0x0a	/* RW: (16) Set 100M XCVR functions  */
+#define HP100_REG_10_LAN_CFG_1	0x08	/* RW: (8) Set 10M XCVR functions   */
+#define HP100_REG_10_LAN_CFG_2  0x09    /* RW: (8)     10M XCVR functions   */
+#define HP100_REG_VG_LAN_CFG_1	0x0a	/* RW: (8) Set 100M XCVR functions  */
+#define HP100_REG_VG_LAN_CFG_2  0x0b    /* RW: (8) 100M LAN Training cfgregs */
 #define HP100_REG_MAC_CFG_1	0x0c	/* RW: (8) Types of pkts to accept   */
 #define HP100_REG_MAC_CFG_2	0x0d	/* RW: (8) Misc MAC functions        */
-/* The follow clear when read: */
+#define HP100_REG_MAC_CFG_3     0x0e    /* RW: (8) Misc MAC functions */
+#define HP100_REG_MAC_CFG_4     0x0f    /* R:  (8) Misc MAC states */
 #define HP100_REG_DROPPED	0x10	/* R:  (16),11:0 Pkts cant fit in mem*/
 #define HP100_REG_CRC		0x12	/* R:  (8) Pkts with CRC             */
 #define HP100_REG_ABORT		0x13	/* R:  (8) Aborted Tx pkts           */
-                                        
+#define HP100_REG_TRAIN_REQUEST 0x14    /* RW: (16) Endnode MAC register.*/
+#define HP100_REG_TRAIN_ALLOW   0x16    /* R:  (16) Hub allowed register */
+                    
 /*  Page 5 - MMU  */
 
 #define HP100_REG_RX_MEM_STOP	0x0c	/* RW: (16) End of Rx ring addr      */
 #define HP100_REG_TX_MEM_STOP	0x0e	/* RW: (16) End of Tx ring addr      */
-                                        
+#define HP100_REG_PDL_MEM_STOP  0x10    /* Not used by 802.12 devices */
+#define HP100_REG_ECB_MEM_STOP  0x14    /* I've no idea what this is */
+          
 /*  Page 6 - Card ID/Physical LAN Address  */
 
 #define HP100_REG_BOARD_ID	0x08	/* R:  (8) EISA/ISA card ID          */
@@ -99,32 +132,46 @@
                                         
 /*  Page 7 - MMU Current Pointers  */
 
-#define HP100_REG_RX_MEM_BR	0x08	/* R:  (16) Current begin of Rx ring */
-#define HP100_REG_RX_MEM_ER	0x0a	/* R:  (16) Current end of Rx ring   */
-#define HP100_REG_TX_MEM_BR	0x0c	/* R:  (16) Current begin of Tx ring */
-#define HP100_REG_TX_MEM_ER	0x0e	/* R:  (16) Current end of Rx ring   */
-#define HP100_REG_MEM_DEBUG	0x1a	/* RW: (16) Used for memory tests    */
-                                                
-/*
- *  HardwareIDReg bits/masks
- */
-
-#define HP100_HW_ID_0		0x50	/* Hardware ID bytes.                */
-#define HP100_HW_ID_1		0x48
-#define HP100_HW_ID_2_REVA	0x50	/* Rev. A ID. NOTE: lower nibble not used */
-#define HP100_HW_ID_3		0x53
-
-/*
- *  OptionLSWReg bits/masks
- */
-
-#define HP100_DEBUG_EN		0x8000	/* 0:Disable, 1:Enable Debug Dump Pointer */
-#define HP100_RX_HDR		0x4000	/* 0:Disable, 1:Enable putting pkt into */
-                                        /*   system memory before Rx interrupt */
-#define HP100_MMAP_DIS		0x2000	/* 0:Enable, 1:Disable memory mapping. */
-                                        /*   MMAP_DIS must be 0 and MEM_EN must */
-                                        /*   be 1 for memory-mapped mode to be */
-                                        /*   enabled */
+#define HP100_REG_PTR_RXSTART	0x08	/* R:  (16) Current begin of Rx ring */
+#define HP100_REG_PTR_RXEND	0x0a	/* R:  (16) Current end of Rx ring   */
+#define HP100_REG_PTR_TXSTART	0x0c	/* R:  (16) Current begin of Tx ring */
+#define HP100_REG_PTR_TXEND	0x0e	/* R:  (16) Current end of Rx ring   */
+#define HP100_REG_PTR_RPDLSTART 0x10
+#define HP100_REG_PTR_RPDLEND   0x12
+#define HP100_REG_PTR_RINGPTRS  0x14
+#define HP100_REG_PTR_MEMDEBUG  0x1a
+/* ------------------------------------------------------------------------ */
+
+
+/* 
+ * Hardware ID Register I (Always available, HW_ID, Offset 0x00)
+ */
+#define HP100_HW_ID_CASCADE     0x4850  /* Identifies Cascade Chip */
+
+/* 
+ * Hardware ID Register 2 & Paging Register
+ * (Always available, PAGING, Offset 0x02)
+ * Bits 15:4 are for the Chip ID 
+ */
+#define HP100_CHIPID_MASK        0xFFF0
+#define HP100_CHIPID_SHASTA      0x5350  /* Not 802.12 compliant */
+                                         /* EISA BM/SL, MCA16/32 SL, ISA SL */
+#define HP100_CHIPID_RAINIER     0x5360  /* Not 802.12 compliant EISA BM,*/
+                                         /* PCI SL, MCA16/32 SL, ISA SL */
+#define HP100_CHIPID_LASSEN      0x5370  /* 802.12 compliant PCI BM, PCI SL */
+                                         /* LRF supported */
+
+/*
+ *  Option Registers I and II 
+ * (Always available, OPTION_LSW, Offset 0x04-0x05)
+ */
+#define HP100_DEBUG_EN		0x8000	/* 0:Dis., 1:Enable Debug Dump Ptr. */
+#define HP100_RX_HDR		0x4000	/* 0:Dis., 1:Enable putting pkt into */
+                                        /*   system mem. before Rx interrupt */
+#define HP100_MMAP_DIS		0x2000	/* 0:Enable, 1:Disable mem.mapping. */
+                                        /*   MMAP_DIS must be 0 and MEM_EN */
+                                        /*   must be 1 for memory-mapped */
+                                        /*   mode to be enabled */
 #define HP100_EE_EN		0x1000	/* 0:Disable,1:Enable EEPROM writing */
 #define HP100_BM_WRITE		0x0800	/* 0:Slave, 1:Bus Master for Tx data */
 #define HP100_BM_READ		0x0400	/* 0:Slave, 1:Bus Master for Rx data */
@@ -132,121 +179,236 @@
 #define HP100_MEM_EN		0x0040	/* Config program set this to */
                                         /*   0:Disable, 1:Enable mem map. */
                                         /*   See MMAP_DIS. */
-#define HP100_IO_EN		0x0020	/* 0:Disable, 1:Enable I/O transfers */
-#define HP100_BOOT_EN		0x0010	/* 0:Disable, 1:Enable boot ROM access */
-#define HP100_FAKE_INT		0x0008	/* 0:No int, 1:int */
-#define HP100_INT_EN		0x0004	/* 0:Disable, 1:Enable ints from card */
+#define HP100_IO_EN		0x0020	/* 1:Enable I/O transfers */
+#define HP100_BOOT_EN		0x0010	/* 1:Enable boot ROM access */
+#define HP100_FAKE_INT		0x0008	/* 1:int */
+#define HP100_INT_EN		0x0004	/* 1:Enable ints from card */
 #define HP100_HW_RST		0x0002	/* 0:Reset, 1:Out of reset */
+                                        /* NIC reset on 0 to 1 transition */
 
 /*
- *  OptionMSWReg bits/masks
+ *  Option Register III 
+ * (Always available, OPTION_MSW, Offset 0x06)
  */
-#define HP100_PRIORITY_TX	0x0080	/* 0:Don't, 1:Do all Tx pkts as priority */
+#define HP100_PRIORITY_TX	0x0080	/* 1:Do all Tx pkts as priority */
 #define HP100_EE_LOAD		0x0040	/* 1:EEPROM loading, 0 when done */
-#define HP100_ADV_NXT_PKT	0x0004	/* 1:Advance to next pkt in Rx queue, */
+#define HP100_ADV_NXT_PKT	0x0004	/* 1:Advance to next pkt in Rx queue */
                                         /*   h/w will set to 0 when done */
-#define HP100_TX_CMD		0x0002	/* 1:Tell h/w download done, h/w will set */
-                                        /*   to 0 when done */
+#define HP100_TX_CMD		0x0002	/* 1:Tell h/w download done, h/w */
+                                        /*   will set to 0 when done */
 
 /*
- *  InterruptStatusReg/InterruptMaskReg bits/masks.  These bits will 0 when a 1 
- *  is written to them.
- */
+ * Interrupt Status Registers I and II
+ * (Page PERFORMANCE, IRQ_STATUS, Offset 0x08-0x09)
+ * Note: With old chips, these Registers will clear when 1 is written to them
+ *       with new chips this depends on setting of CLR_ISMODE
+ */
+#define HP100_RX_EARLY_INT      0x2000
+#define HP100_RX_PDA_ZERO       0x1000
+#define HP100_RX_PDL_FILL_COMPL 0x0800
 #define HP100_RX_PACKET		0x0400	/* 0:No, 1:Yes pkt has been Rx */
 #define HP100_RX_ERROR		0x0200	/* 0:No, 1:Yes Rx pkt had error */
+#define HP100_TX_PDA_ZERO       0x0020  /* 1 when PDA count goes to zero */
 #define HP100_TX_SPACE_AVAIL	0x0010	/* 0:<8192, 1:>=8192 Tx free bytes */
 #define HP100_TX_COMPLETE	0x0008	/* 0:No, 1:Yes a Tx has completed */
+#define HP100_MISC_ERROR        0x0004  /* 0:No, 1:Lan Link down or bus error*/
 #define HP100_TX_ERROR		0x0002	/* 0:No, 1:Yes Tx pkt had error */
-                                        
+
 /*
- *  TxMemoryFreeCountReg bits/masks.
+ * Xmit Memory Free Count
+ * (Page PERFORMANCE, TX_MEM_FREE, Offset 0x14) (Read only, 32bit)
  */
-#define HP100_AUTO_COMPARE	0x8000	/* Says at least 8k is available for Tx. */
-                                        /*   NOTE: This mask is for the upper */
-                                        /*   word of the register. */
+#define HP100_AUTO_COMPARE	0x80000000	/* Tx Space avail & pkts<255 */
+#define HP100_FREE_SPACE	0x7fffffe0	/* Tx free memory */
 
 /*
- *  IRQChannelReg bits/masks.
+ *  IRQ Channel
+ * (Page HW_MAP, IRQ_CHANNEL, Offset 0x0d)
  */
 #define HP100_ZERO_WAIT_EN	0x80	/* 0:No, 1:Yes asserts NOWS signal */
+#define HP100_IRQ_SCRAMBLE      0x40
+#define HP100_BOND_HP           0x20
 #define HP100_LEVEL_IRQ		0x10	/* 0:Edge, 1:Level type interrupts. */
-                                        /*   Only valid on EISA cards. */
-#define HP100_IRQ_MASK		0x0F	/* Isolate the IRQ bits */
+                                        /* (Only valid on EISA cards) */
+#define HP100_IRQMASK		0x0F	/* Isolate the IRQ bits */
 
 /*
- *  SRAMReg bits/masks.
+ * SRAM Parameters
+ * (Page HW_MAP, SRAM, Offset 0x0e)
  */
 #define HP100_RAM_SIZE_MASK	0xe0	/* AND to get SRAM size index */
-#define HP100_RAM_SIZE_SHIFT	0x05	/* Shift count to put index in lower bits */
+#define HP100_RAM_SIZE_SHIFT	0x05	/* Shift count(put index in lwr bits)*/
+
+/*
+ * Bus Master Register
+ * (Page HW_MAP, BM, Offset 0x0f)
+ */
+#define HP100_BM_BURST_RD       0x01    /* EISA only: 1=Use burst trans. fm system */
+                                        /* memory to chip (tx) */
+#define HP100_BM_BURST_WR       0x02    /* EISA only: 1=Use burst trans. fm system */
+                                        /* memory to chip (rx) */
+#define HP100_BM_MASTER		0x04	/* 0:Slave, 1:BM mode */
+#define HP100_BM_PAGE_CK        0x08    /* This bit should be set whenever in*/
+                                        /* an EISA system */
+#define HP100_BM_PCI_8CLK       0x40    /* ... cycles 8 clocks apart */
+
+
+/* 
+ * Mode Control Register I
+ * (Page HW_MAP, MODECTRL1, Offset0x10)
+ */
+#define HP100_TX_DUALQ          0x10
+   /* If set and BM -> dual tx pda queues*/
+#define HP100_ISR_CLRMODE       0x02   /* If set ISR will clear all pending */
+                                       /* interrupts on read (etr only?) */
+#define HP100_EE_NOLOAD         0x04   /* Status whether res will be loaded */
+                                       /* from the eeprom */
+#define HP100_TX_CNT_FLG        0x08   /* Controls Early TX Reg Cnt Field */
+#define HP100_PDL_USE3          0x10   /* If set BM engine will read only */
+                                       /* first three data elements of a PDL */
+                                       /* on the first access. */
+#define HP100_BUSTYPE_MASK      0xe0   /* Three bit bus type info */
+
+/*
+ * Mode Control Register II
+ * (Page HW_MAP, MODECTRL2, Offset0x11)
+ */
+#define HP100_EE_MASK           0x0f   /* Tell EEPROM circuit not to load */ 
+                                       /* certain resources */
+#define HP100_DIS_CANCEL        0x20   /* For tx dualq mode operation */
+#define HP100_EN_PDL_WB         0x40   /* 1: Status of PDL completion may be */
+                                       /* written back to system mem */
+#define HP100_EN_BUS_FAIL       0x80   /* Enables bus-fail portion of misc */
+                                       /* interrupt */
+
+/* 
+ * PCI Configuration and Control Register I
+ * (Page HW_MAP, PCICTRL1, Offset 0x12)
+ */
+#define HP100_LO_MEM            0x01   /* 1: Mapped Mem requested below 1MB */
+#define HP100_NO_MEM            0x02   /* 1: Disables Req for sysmem to PCI */ 
+                                       /* bios */
+#define HP100_USE_ISA           0x04   /* 1: isa type decodes will occur */
+                                       /* simultaneously with PCI decodes */
+#define HP100_IRQ_HI_MASK       0xf0   /* pgmed by pci bios */
+#define HP100_PCI_IRQ_HI_MASK   0x78    /* Isolate 4 bits for PCI IRQ  */
+ 
+/*
+ * PCI Configuration and Control Register II
+ * (Page HW_MAP, PCICTRL2, Offset 0x13)
+ */
+#define HP100_RD_LINE_PDL       0x01   /* 1: PCI command Memory Read Line en */
+#define HP100_RD_TX_DATA_MASK   0x06   /* choose PCI memread cmds for TX */ 
+#define HP100_MWI               0x08   /* 1: en. PCI memory write invalidate */
+#define HP100_ARB_MODE          0x10   /* Select PCI arbitor type */
+#define HP100_STOP_EN           0x20   /* Enables PCI state machine to issue */
+                                       /* pci stop if cascade not ready */
+#define HP100_IGNORE_PAR        0x40   /* 1: PCI state machine ignores parity*/
+#define HP100_PCI_RESET         0x80   /* 0->1: Reset PCI block */
+
+/*
+ * Early TX Configuration and Control Register
+ * (Page HW_MAP, EARLYTXCFG, Offset 0x16)
+ */
+#define HP100_EN_EARLY_TX       0x8000  /* 1=Enable Early TX */
+#define HP100_EN_ADAPTIVE       0x4000  /* 1=Enable adaptive mode */
+#define HP100_EN_TX_UR_IRQ      0x2000  /* reserved, must be 0 */
+#define HP100_EN_LOW_TX         0x1000  /* reserved, must be 0 */
+#define HP100_ET_CNT_MASK       0x0fff  /* bits 11..0: ET counters */
 
 /*
- *  BMReg bits/masks.
+ * Early RX Configuration and Control Register
+ * (Page HW_MAP, EARLYRXCFG, Offset 0x18)
  */
-#define HP100_BM_SLAVE		0x04	/* 0:Slave, 1:BM mode */
+#define HP100_EN_EARLY_RX       0x80     /* 1=Enable Early RX */
+#define HP100_EN_LOW_RX         0x40     /* reserved, must be 0 */
+#define HP100_RX_TRIP_MASK      0x1f     /* bits 4..0: threshold at which the
+					  * early rx circuit will start the
+					  * dma of received packet into system
+					  * memory for BM */
 
 /*
- *  EEPROMControlReg bits/masks.
+ *  Serial Devices Control Register
+ * (Page EEPROM_CTRL, EEPROM_CTRL, Offset 0x08)
  */
-#define HP100_EEPROM_LOAD	0x0001	/* 0->1 loads the EEPROM into registers. */
-                                        /*   When it goes back to 0, load is  */
-                                        /*   complete.  This should take ~600us. */
+#define HP100_EEPROM_LOAD	0x0001	/* 0->1 loads EEPROM into registers. */
+                                        /* When it goes back to 0, load is   */
+                                        /* complete. This should take ~600us.*/
 
 /*
- *  LANCntrCfg10Reg bits/masks.
+ * 10MB LAN Control and Configuration Register I
+ * (Page MAC_CTRL, 10_LAN_CFG_1, Offset 0x08)
  */
-#define HP100_SQU_ST		0x0100	/* 0:No, 1:Yes collision signal sent */
-                                        /*   after Tx.  Only used for AUI. */
-#define HP100_MAC10_SEL		0x00c0	/* Get bits to indicate MAC */
-#define HP100_AUI_SEL		0x0020	/* Status of AUI selection */
-#define HP100_LOW_TH		0x0010	/* 0:No, 1:Yes allow better cabling */
-#define HP100_LINK_BEAT_DIS	0x0008	/* 0:Enable, 1:Disable link beat */
-#define HP100_LINK_BEAT_ST	0x0004	/* 0:No, 1:Yes link beat being Rx */
-#define HP100_R_ROL_ST		0x0002	/* 0:No, 1:Yes Rx twisted pair has been */
-                                        /*   reversed */
-#define HP100_AUI_ST		0x0001	/* 0:No, 1:Yes use AUI on TP card */
+#define HP100_MAC10_SEL		0xc0	/* Get bits to indicate MAC */
+#define HP100_AUI_SEL		0x20	/* Status of AUI selection */
+#define HP100_LOW_TH		0x10	/* 0:No, 1:Yes allow better cabling */
+#define HP100_LINK_BEAT_DIS	0x08	/* 0:Enable, 1:Disable link beat */
+#define HP100_LINK_BEAT_ST	0x04	/* 0:No, 1:Yes link beat being Rx */
+#define HP100_R_ROL_ST		0x02	/* 0:No, 1:Yes Rx twisted pair has */
+                                        /*             been reversed */
+#define HP100_AUI_ST		0x01	/* 0:No, 1:Yes use AUI on TP card */
 
-/* MAC Selection, use with MAC10_SEL bits */
+/*
+ * 10 MB LAN Control and Configuration Register II
+ * (Page MAC_CTRL, 10_LAN_CFG_2, Offset 0x09)
+ */
+#define HP100_SQU_ST		0x01	/* 0:No, 1:Yes collision signal sent */
+                                        /*       after Tx.Only used for AUI. */
+#define HP100_FULLDUP           0x02    /* 1: LXT901 XCVR fullduplx enabled */
+#define HP100_DOT3_MAC          0x04    /* 1: DOT 3 Mac sel. unless Autosel */
+
+/*
+ * MAC Selection, use with MAC10_SEL bits
+ */
 #define HP100_AUTO_SEL_10	0x0	/* Auto select */
 #define HP100_XCVR_LXT901_10	0x1	/* LXT901 10BaseT transceiver */
 #define HP100_XCVR_7213		0x2	/* 7213 transceiver */
 #define HP100_XCVR_82503	0x3	/* 82503 transceiver */
 
-
 /*
- *  LANCntrCfgVGReg bits/masks.
+ *  100MB LAN Training Register
+ * (Page MAC_CTRL, VG_LAN_CFG_2, Offset 0x0b) (old, pre 802.12)
  */
-#define HP100_FRAME_FORMAT	0x0800	/* 0:802.3, 1:802.5 frames */
-#define HP100_BRIDGE		0x0400	/* 0:No, 1:Yes tell hub it's a bridge */
-#define HP100_PROM_MODE		0x0200	/* 0:No, 1:Yes tell hub card is */
-                                        /*   promiscuous */
-#define HP100_REPEATER		0x0100	/* 0:No, 1:Yes tell hub MAC wants to be */
-                                        /*   a cascaded repeater */
-#define HP100_MAC100_SEL	0x0080	/* 0:No, 1:Yes use 100 Mbit MAC */
-#define HP100_LINK_UP_ST	0x0040	/* 0:No, 1:Yes endnode logged in */
-#define HP100_LINK_CABLE_ST	0x0020	/* 0:No, 1:Yes cable can hear tones from */
-                                        /*   hub */
-#define HP100_LOAD_ADDR		0x0010	/* 0->1 card addr will be sent to hub. */
-                                        /*   100ms later the link status bits are */
-                                        /*   valid */
-#define HP100_LINK_CMD		0x0008	/* 0->1 link will attempt to log in. */
-                                        /*   100ms later the link status bits are */
-                                        /*   valid */
-#define HP100_LINK_GOOD_ST	0x0002	/* 0:No, 1:Yes cable passed training */
-#define HP100_VG_RESET		0x0001	/* 0:Yes, 1:No reset the 100VG MAC */
+#define HP100_FRAME_FORMAT	0x08	/* 0:802.3, 1:802.5 frames */
+#define HP100_BRIDGE		0x04	/* 0:No, 1:Yes tell hub i am a bridge */
+#define HP100_PROM_MODE		0x02	/* 0:No, 1:Yes tell hub card is */
+                                        /*         promiscuous */
+#define HP100_REPEATER		0x01	/* 0:No, 1:Yes tell hub MAC wants to */
+                                        /*         be a cascaded repeater */
+
+/*
+ * 100MB LAN Control and Configuration Register
+ * (Page MAC_CTRL, VG_LAN_CFG_1, Offset 0x0a) 
+ */
+#define HP100_VG_SEL	        0x80	/* 0:No, 1:Yes use 100 Mbit MAC */
+#define HP100_LINK_UP_ST	0x40	/* 0:No, 1:Yes endnode logged in */
+#define HP100_LINK_CABLE_ST	0x20	/* 0:No, 1:Yes cable can hear tones */
+                                        /*         from  hub */
+#define HP100_LOAD_ADDR		0x10	/* 0->1 card addr will be sent  */
+                                        /* 100ms later the link status  */
+                                        /* bits are valid */
+#define HP100_LINK_CMD		0x08	/* 0->1 link will attempt to log in. */
+                                        /* 100ms later the link status */
+                                        /* bits are valid */
+#define HP100_TRN_DONE          0x04    /* NEW ETR-Chips only: Will be reset */
+                                        /* after LinkUp Cmd is given and set */
+                                        /* when training has completed. */
+#define HP100_LINK_GOOD_ST	0x02	/* 0:No, 1:Yes cable passed training */
+#define HP100_VG_RESET		0x01	/* 0:Yes, 1:No reset the 100VG MAC */
 
 
 /*
- *  MACConfiguration1Reg bits/masks.
+ *  MAC Configuration Register I
+ * (Page MAC_CTRL, MAC_CFG_1, Offset 0x0c)
  */
 #define HP100_RX_IDLE		0x80	/* 0:Yes, 1:No currently receiving pkts */
 #define HP100_TX_IDLE		0x40	/* 0:Yes, 1:No currently Txing pkts */
-#define HP100_RX_EN		0x20	/* 0:No, 1:Yes allow receiving of pkts */
-#define HP100_TX_EN		0x10	/* 0:No, 1:Yes allow transmitting of pkts */
+#define HP100_RX_EN		0x20	/* 1: allow receiving of pkts */
+#define HP100_TX_EN		0x10	/* 1: allow transmitting of pkts */
 #define HP100_ACC_ERRORED	0x08	/* 0:No, 1:Yes allow Rx of errored pkts */
 #define HP100_ACC_MC		0x04	/* 0:No, 1:Yes allow Rx of multicast pkts */
 #define HP100_ACC_BC		0x02	/* 0:No, 1:Yes allow Rx of broadcast pkts */
-#define HP100_ACC_PHY		0x01	/* 0:No, 1:Yes allow Rx of ALL physical pkts */
-
+#define HP100_ACC_PHY		0x01	/* 0:No, 1:Yes allow Rx of ALL phys. pkts */
 #define HP100_MAC1MODEMASK	0xf0	/* Hide ACC bits */
 #define HP100_MAC1MODE1		0x00	/* Receive nothing, must also disable RX */
 #define HP100_MAC1MODE2		0x00
@@ -254,15 +416,14 @@
 #define HP100_MAC1MODE4		HP100_MAC1MODE3 | HP100_ACC_MC
 #define HP100_MAC1MODE5		HP100_MAC1MODE4 /* set mc hash to all ones also */
 #define HP100_MAC1MODE6		HP100_MAC1MODE5 | HP100_ACC_PHY	/* Promiscuous */
-
 /* Note MODE6 will receive all GOOD packets on the LAN. This really needs
    a mode 7 defined to be LAN Analyzer mode, which will receive errored and
    runt packets, and keep the CRC bytes. */
-
-#define HP100_MAC1MODE7		MAC1MODE6 OR ACC_ERRORED
+#define HP100_MAC1MODE7		HP100_MAC1MODE6 | HP100_ACC_ERRORED
 
 /*
- *  MACConfiguration2Reg bits/masks.
+ *  MAC Configuration Register II 
+ * (Page MAC_CTRL, MAC_CFG_2, Offset 0x0d)
  */
 #define HP100_TR_MODE		0x80	/* 0:No, 1:Yes support Token Ring formats */
 #define HP100_TX_SAME		0x40	/* 0:No, 1:Yes Tx same packet continuous */
@@ -270,9 +431,12 @@
                                         /*   transceiver */
 #define HP100_LBK_MAC		0x10	/* 0:No, 1:Yes loopback through MAC */
 #define HP100_CRC_I		0x08	/* 0:No, 1:Yes inhibit CRC on Tx packets */
+#define HP100_ACCNA             0x04    /* 1: For 802.5: Accept only token ring
+					 * group addr that maches NA mask */
 #define HP100_KEEP_CRC		0x02	/* 0:No, 1:Yes keep CRC on Rx packets. */
                                         /*   The length will reflect this. */
-
+#define HP100_ACCFA             0x01    /* 1: For 802.5: Accept only functional
+					 * addrs that match FA mask (page1) */
 #define HP100_MAC2MODEMASK	0x02
 #define HP100_MAC2MODE1		0x00
 #define HP100_MAC2MODE2		0x00
@@ -283,6 +447,65 @@
 #define HP100_MAC2MODE7		KEEP_CRC
 
 /*
+ * MAC Configuration Register III 
+ * (Page MAC_CTRL, MAC_CFG_3, Offset 0x0e) 
+ */
+#define HP100_PACKET_PACE       0x03     /* Packet Pacing:
+					  * 00: No packet pacing
+                                          * 01: 8 to 16 uS delay
+                                          * 10: 16 to 32 uS delay
+                                          * 11: 32 to 64 uS delay
+					  */
+#define HP100_LRF_EN            0x04     /* 1: External LAN Rcv Filter and
+					  * TCP/IP Checksumming enabled. */
+#define HP100_AUTO_MODE         0x10     /* 1: AutoSelect between 10/100 */
+
+/*
+ * MAC Configuration Register IV 
+ * (Page MAC_CTRL, MAC_CFG_4, Offset 0x0f)
+ */
+#define HP100_MAC_SEL_ST        0x01      /* (R): Status of external VGSEL
+					   * Signal, 1=100VG, 0=10Mbit sel. */
+#define HP100_LINK_FAIL_ST      0x02      /* (R): Status of Link Fail portion
+                                           * of the Misc. Interrupt */
+
+/* 
+ *  100 MB LAN Training Request/Allowed Registers 
+ * (Page MAC_CTRL, TRAIN_REQUEST and TRAIN_ALLOW, Offset 0x14-0x16)(ETR parts only)
+ */
+#define HP100_MACRQ_REPEATER         0x0001  /* 1: MAC tells HUB it wants to be 
+				              *    a cascaded repeater
+					      * 0: ... wants to be a DTE */
+#define HP100_MACRQ_PROMSC           0x0006  /* 2 bits: Promiscious mode
+					      * 00: Rcv only unicast packets
+					      *     specifically addr to this
+					      *     endnode
+					      * 10: Rcv all pckts fwded by 
+					      *     the local repeater */
+#define HP100_MACRQ_FRAMEFMT_EITHER  0x0018  /* 11: either format allowed */
+#define HP100_MACRQ_FRAMEFMT_802_3   0x0000  /* 00: 802.3 is requested */
+#define HP100_MACRQ_FRAMEFMT_802_5   0x0010  /* 10: 802.5 format is requested */
+#define HP100_CARD_MACVER            0xe000  /* R: 3 bit Cards 100VG MAC version */
+#define HP100_MALLOW_REPEATER        0x0001  /* If reset, requested access as an
+					      * end node is allowed */
+#define HP100_MALLOW_PROMSC          0x0004  /* 2 bits: Promiscious mode
+					      * 00: Rcv only unicast packets
+					      *     specifically addr to this
+					      *     endnode
+					      * 10: Rcv all pckts fwded by 
+					      *     the local repeater */
+#define HP100_MALLOW_FRAMEFMT        0x00e0  /* 2 bits: Frame Format
+					      * 00: 802.3 format will be used
+					      * 10: 802.5 format will be used */
+#define HP100_MALLOW_ACCDENIED       0x0400  /* N bit */
+#define HP100_MALLOW_CONFIGURE       0x0f00  /* C bit */
+#define HP100_MALLOW_DUPADDR         0x1000  /* D bit */
+#define HP100_HUB_MACVER             0xe000  /* R: 3 bit 802.12 MAC/RMAC training */
+                                             /*    protocol of repeater */
+
+/* ****************************************************************************** */
+
+/*
  *  Set/Reset bits
  */
 #define HP100_SET_HB		0x0100	/* 0:Set fields to 0 whose mask is 1 */
@@ -297,20 +520,45 @@
 #define HP100_LAN_10		10	/* lan_type value for 10BaseT */
 #define HP100_LAN_ERR		(-1)	/* lan_type value for link down */
 
-/*
- *  Receive Header Definition.
+#define TRUE 1
+#define FALSE 0
+
+
+/* 
+ * Bus Master Data Structures  ----------------------------------------------
  */
 
-struct hp100_rx_header {
-  u_short rx_length;			/* Pkt length is bits 12:0 */
-  u_short rx_status;			/* status of the packet */
-};
+#define MAX_RX_PDL              30   /* Card limit = 31 */
+#define MAX_RX_FRAG             2    /* Dont need more... */
+#define MAX_TX_PDL              29
+#define MAX_TX_FRAG             2   /* Limit = 31 */
+
+/* Define total PDL area size in bytes (should be 4096) */
+/* This is the size of kernel (dma) memory that will be allocated. */
+#define MAX_RINGSIZE ((MAX_RX_FRAG*8+4+4)*MAX_RX_PDL+(MAX_TX_FRAG*8+4+4)*MAX_TX_PDL)+16
+
+/* Ethernet Packet Sizes */
+#define MIN_ETHER_SIZE          60
+#define MAX_ETHER_SIZE          1514    /* Needed for preallocation of */
+                                        /* skb buffer when busmastering */
+
+/* Tx or Rx Ring Entry */
+typedef struct hp100_ring {
+  u_int                     *pdl;       /* Address of PDLs PDH, dword before
+					 * this address is used for rx hdr */
+  u_int                     pdl_paddr;  /* Physical address of PDL */
+  struct sk_buff            *skb;
+  struct hp100_ring         *next;      
+} hp100_ring_t;
+
+
+
+/* Mask for Header Descriptor */
+#define HP100_PKT_LEN_MASK	0x1FFF	/* AND with RxLength to get length */
 
-#define HP100_PKT_LEN_MASK	0x1FFF	/* AND with RxLength to get length bits */
 
 /* Receive Packet Status.  Note, the error bits are only valid if ACC_ERRORED 
    bit in the MAC Configuration Register 1 is set. */
-   
 #define HP100_RX_PRI		0x8000	/* 0:No, 1:Yes packet is priority */
 #define HP100_SDF_ERR		0x4000	/* 0:No, 1:Yes start of frame error */
 #define HP100_SKEW_ERR		0x2000	/* 0:No, 1:Yes skew out of range */
@@ -368,7 +616,11 @@
 	outw( HP100_MMAP_DIS | HP100_RESET_HB, ioaddr + HP100_REG_OPTION_LSW )
 #define hp100_mem_map_disable() \
 	outw( HP100_MMAP_DIS | HP100_SET_HB, ioaddr + HP100_REG_OPTION_LSW )
-#define hp100_reset_card() \
-	outw( HP100_HW_RST | HP100_RESET_LB, ioaddr + HP100_REG_OPTION_LSW )
-#define hp100_unreset_card() \
-	outw( HP100_HW_RST | HP100_SET_LB, ioaddr + HP100_REG_OPTION_LSW )
+
+
+/*
+ * Local variables:
+ *  c-indent-level: 2
+ *  tab-width: 8
+ * End:
+*/
diff -u --recursive --new-file v2.0.30/linux/drivers/net/ibmtr.c linux/drivers/net/ibmtr.c
--- v2.0.30/linux/drivers/net/ibmtr.c	Tue Apr  8 08:47:46 1997
+++ linux/drivers/net/ibmtr.c	Tue Aug 12 13:21:12 1997
@@ -971,14 +971,15 @@
 			} /* ARB response */
 			
 			if (status & SSB_RESP_INT) { /* SSB response */
-						
+				unsigned char retcode;		
 				switch (readb(ti->ssb)) { /* SSB command check */
 					
 				      case XMIT_DIR_FRAME:
 				      case XMIT_UI_FRAME:
-					if (readb(ti->ssb+2)) /* checks ret_code */
+					retcode = readb(ti->ssb+2);
+					if (retcode && (retcode != 0x22)) /* checks ret_code */
 						DPRINTK("xmit ret_code: %02X xmit error code: %02X\n", 
-							(int)readb(ti->ssb+2), (int)readb(ti->ssb+6));		
+							(int)retcode, (int)readb(ti->ssb+6));		
 					else ti->tr_stats.tx_packets++;
 					break;
 					
diff -u --recursive --new-file v2.0.30/linux/drivers/net/lance32.c linux/drivers/net/lance32.c
--- v2.0.30/linux/drivers/net/lance32.c	Tue Apr  8 08:47:46 1997
+++ linux/drivers/net/lance32.c	Mon Aug  4 12:11:05 1997
@@ -273,6 +273,9 @@
         outw(0x0002, ioaddr+LANCE_ADDR);
 	outw(0x0002, ioaddr+LANCE_BUS_IF);
 
+	/* Reset the LANCE - this should prevent any more interrupts from arriving */
+	inw(ioaddr+LANCE_RESET);
+
 	if (lance32_debug > 0)
 		printk(version);
 
@@ -451,7 +454,7 @@
 	/* Transmitter timeout, serious problems. */
 	if (dev->tbusy) {
 		int tickssofar = jiffies - dev->trans_start;
-		if (tickssofar < 20)
+		if (tickssofar < 60)		/* It can take this long to run through the 16 retries */
 			return 1;
 		outw(0, ioaddr+LANCE_ADDR);
 		printk("%s: transmit timed out, status %4.4x, resetting.\n",
@@ -469,8 +472,9 @@
 					   lp->rx_ring[i].base, -lp->rx_ring[i].buf_length,
 					   lp->rx_ring[i].msg_length);
 			for (i = 0 ; i < TX_RING_SIZE; i++)
-				printk("%s %08x %04x %04x", i & 0x3 ? "" : "\n ",
+				printk("%s %08x %04x %04x %08x  ", i & 0x1 ? "" : "\n ",
 					   lp->tx_ring[i].base, -lp->tx_ring[i].length,
+					   lp->tx_ring[i].status,
 					   lp->tx_ring[i].misc);
 			printk("\n");
 		}
@@ -633,6 +637,9 @@
 				dirty_tx += TX_RING_SIZE;
 			}
 #endif
+
+			if (dev->tbusy)
+				dev->trans_start = jiffies; /* We are just starting the next transmit */
 
 			if (lp->tx_full && dev->tbusy
 				&& dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
diff -u --recursive --new-file v2.0.30/linux/drivers/net/ne.c linux/drivers/net/ne.c
--- v2.0.30/linux/drivers/net/ne.c	Sat Mar  1 18:09:39 1997
+++ linux/drivers/net/ne.c	Sun Aug 17 09:44:56 1997
@@ -24,6 +24,7 @@
     Paul Gortmaker	: multiple card support for module users.
     Paul Gortmaker	: Support for PCI ne2k clones, similar to lance.c
     Paul Gortmaker	: Allow users with bad cards to avoid full probe.
+    Paul Gortmaker	: PCI probe changes, more PCI cards supported.
 
 */
 
@@ -61,11 +62,24 @@
 /* Do we have a non std. amount of memory? (in units of 256 byte pages) */
 /* #define PACKETBUF_MEMSIZE	0x40 */
 
-/* ---- No user-serviceable parts below ---- */
-
+#if defined(HAVE_DEVLIST) || !defined(MODULE)
 /* A zero-terminated list of I/O addresses to be probed. */
 static unsigned int netcard_portlist[] =
 { 0x300, 0x280, 0x320, 0x340, 0x360, 0};
+#endif /* defined(HAVE_DEVLIST) || !defined(MODULE) */
+
+#ifdef CONFIG_PCI
+/* Ack! People are making PCI ne2000 clones! Oh the horror, the horror... */
+static struct { unsigned short vendor, dev_id;}
+pci_clone_list[] = {
+	{PCI_VENDOR_ID_REALTEK,		PCI_DEVICE_ID_REALTEK_8029},
+	{PCI_VENDOR_ID_WINBOND2,	PCI_DEVICE_ID_WINBOND2_89C940},
+	{PCI_VENDOR_ID_COMPEX,		PCI_DEVICE_ID_COMPEX_RL2000},
+	{PCI_VENDOR_ID_KTI,		PCI_DEVICE_ID_KTI_ET32P2},
+	{PCI_VENDOR_ID_NETVIN,		PCI_DEVICE_ID_NETVIN_NV5000SC},
+	{0,}
+};
+#endif
 
 #ifdef SUPPORT_NE_BAD_CLONES
 /* A list of bad clones that we none-the-less recognize. */
@@ -80,10 +94,14 @@
     {"4-DIM8","4-DIM16", {0x00,0x00,0x4d,}},  /* Outlaw 4-Dimension cards. */
     {"Con-Intl_8", "Con-Intl_16", {0x00, 0x00, 0x24}}, /* Connect Int'nl */
     {"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 */
     {0,}
 };
 #endif
 
+/* ---- No user-serviceable parts below ---- */
+
 #define NE_BASE	 (dev->base_addr)
 #define NE_CMD	 	0x00
 #define NE_DATAPORT	0x10	/* NatSemi-defined port window offset. */
@@ -99,6 +117,7 @@
 static unsigned char pci_irq_line = 0;
 
 int ne_probe(struct device *dev);
+static int ne_probe_pci(struct device *dev);
 static int ne_probe1(struct device *dev, int ioaddr);
 
 static int ne_open(struct device *dev);
@@ -145,7 +164,9 @@
 
 int ne_probe(struct device *dev)
 {
+#ifndef MODULE
     int i;
+#endif /* MODULE */
     int base_addr = dev ? dev->base_addr : 0;
 
     /* First check any supplied i/o locations. User knows best. <cough> */
@@ -154,42 +175,13 @@
     else if (base_addr != 0)	/* Don't probe at all. */
 	return ENXIO;
 
+#ifdef CONFIG_PCI
     /* Then look for any installed PCI clones */
-#if defined(CONFIG_PCI)
-    if (pcibios_present()) {
-	int pci_index;
-	for (pci_index = 0; pci_index < 8; pci_index++) {
-		unsigned char pci_bus, pci_device_fn;
-		unsigned int pci_ioaddr;
-
-		/* Currently only Realtek are making PCI ne2k clones. */
-		if (pcibios_find_device (PCI_VENDOR_ID_REALTEK,
-				PCI_DEVICE_ID_REALTEK_8029, pci_index,
-				&pci_bus, &pci_device_fn) != 0)
-			break;	/* OK, now try to probe for std. ISA card */
-		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);
-		/* Strip the I/O address out of the returned value */
-		pci_ioaddr &= PCI_BASE_ADDRESS_IO_MASK;
-		/* Avoid already found cards from previous ne_probe() calls */
-		if (check_region(pci_ioaddr, NE_IO_EXTENT)) {
-			pci_irq_line=0;
-			continue;
-		}
-		printk("ne.c: PCI BIOS reports ne2000 clone at i/o %#x, irq %d.\n",
-				pci_ioaddr, pci_irq_line);
-		if (ne_probe1(dev, pci_ioaddr) != 0) {	/* Shouldn't happen. */
-			printk(KERN_ERR "ne.c: Probe of PCI card at %#x failed.\n", pci_ioaddr);
-			break;	/* Hrmm, try to probe for ISA card... */
-		}
-		pci_irq_line = 0;
-		return 0;
-	}
-    }
-#endif  /* defined(CONFIG_PCI) */
+    if (pcibios_present() && (ne_probe_pci(dev) == 0))
+  return 0;
+#endif
 
+#ifndef MODULE
     /* Last resort. The semi-risky ISA auto-probe. */
     for (i = 0; netcard_portlist[i]; i++) {
 	int ioaddr = netcard_portlist[i];
@@ -198,11 +190,53 @@
 	if (ne_probe1(dev, ioaddr) == 0)
 	    return 0;
     }
+#endif
 
     return ENODEV;
 }
 #endif
 
+#ifdef CONFIG_PCI
+static int ne_probe_pci(struct device *dev)
+{
+	int i;
+
+	for (i = 0; pci_clone_list[i].vendor != 0; i++) {
+		unsigned char pci_bus, pci_device_fn;
+		unsigned int pci_ioaddr;
+		int pci_index;
+		
+		for (pci_index = 0; pci_index < 8; pci_index++) {
+			if (pcibios_find_device (pci_clone_list[i].vendor,
+					pci_clone_list[i].dev_id, pci_index,
+					&pci_bus, &pci_device_fn) != 0)
+				break;	/* No more of these type of cards */
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+					PCI_BASE_ADDRESS_0, &pci_ioaddr);
+			/* Strip the I/O address out of the returned value */
+			pci_ioaddr &= PCI_BASE_ADDRESS_IO_MASK;
+			/* Avoid already found cards from previous calls */
+			if (check_region(pci_ioaddr, NE_IO_EXTENT))
+				continue;
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+					PCI_INTERRUPT_LINE, &pci_irq_line);
+			break;	/* Beauty -- got a valid card. */
+		}
+		if (pci_irq_line == 0) continue;	/* Try next PCI ID */
+		printk("ne.c: PCI BIOS reports NE 2000 clone at i/o %#x, irq %d.\n",
+				pci_ioaddr, pci_irq_line);
+		if (ne_probe1(dev, pci_ioaddr) != 0) {	/* Shouldn't happen. */
+			printk(KERN_ERR "ne.c: Probe of PCI card at %#x failed.\n", pci_ioaddr);
+			pci_irq_line = 0;
+			return -ENXIO;
+		}
+		pci_irq_line = 0;
+		return 0;
+	}
+	return -ENODEV;
+}
+#endif  /* CONFIG_PCI */
+
 static int ne_probe1(struct device *dev, int ioaddr)
 {
     int i;
@@ -310,8 +344,8 @@
 	for (i = 0; i < 16; i++)
 		SA_prom[i] = SA_prom[i+i];
     
-    if (pci_irq_line)
-	wordlength = 2;		/* Catch broken cards mentioned above. */
+    if (pci_irq_line || ioaddr >= 0x400)
+	wordlength = 2;		/* Catch broken PCI cards mentioned above. */
 
     if (wordlength == 2) {
 	/* We must set the 8390 for word mode. */
@@ -361,9 +395,8 @@
 
     }
 
-    if (pci_irq_line) {
+    if (pci_irq_line)
 	dev->irq = pci_irq_line;
-    }
 
     if (dev->irq < 2) {
 	autoirq_setup(0);
@@ -713,17 +746,17 @@
 		dev->irq = irq[this_dev];
 		dev->base_addr = io[this_dev];
 		dev->init = ne_probe;
-		if (io[this_dev] == 0)  {
-			if (this_dev != 0) break; /* only complain once */
-			printk(KERN_NOTICE "ne.c: Module autoprobing not allowed. Append \"io=0xNNN\" value(s).\n");
-			return -EPERM;
-		}
-		if (register_netdev(dev) != 0) {
-			printk(KERN_WARNING "ne.c: No NE*000 card found (i/o = 0x%x).\n", io[this_dev]);
-			if (found != 0) return 0;	/* Got at least one. */
-			return -ENXIO;
+		if (register_netdev(dev) == 0) {
+			found++;
+			continue;
 		}
-		found++;
+		if (found != 0) 	/* Got at least one. */
+			return 0;
+		if (io[this_dev] != 0)
+			printk(KERN_WARNING "ne.c: No NE*000 card found at i/o = %#x\n", io[this_dev]);
+		else
+			printk(KERN_NOTICE "ne.c: No PCI cards found. Use \"io=0xNNN\" value(s) for ISA cards.\n");
+		return -ENXIO;
 	}
 
 	return 0;
diff -u --recursive --new-file v2.0.30/linux/drivers/net/new_tunnel.c linux/drivers/net/new_tunnel.c
--- v2.0.30/linux/drivers/net/new_tunnel.c	Tue Apr  8 08:47:46 1997
+++ linux/drivers/net/new_tunnel.c	Tue Aug 12 13:21:12 1997
@@ -161,7 +161,7 @@
 	 *  routing tables
 	 */
 	iph = (struct iphdr *) skb->data;
-	if ((rt = ip_rt_route(iph->daddr, 0)) == NULL)
+	if ((rt = ip_rt_route(iph->daddr, 0, skb->sk?skb->sk->bound_device:NULL)) == NULL)
 	{ 
 		/* No route to host */
 		/* Where did the packet come from? */
@@ -194,7 +194,7 @@
 	}
 	ip_rt_put(rt);
 
-	if ((rt = ip_rt_route(target, 0)) == NULL)
+	if ((rt = ip_rt_route(target, 0, skb->sk?skb->sk->bound_device:NULL)) == NULL)
 	{ 
 		/* No route to host */
 		/* Where did the packet come from? */
@@ -267,6 +267,7 @@
 
 		/* Tack on our header */
 		new_skb->h.iph = (struct iphdr *) skb_push(new_skb, tunnel_hlen);
+		new_skb->mac.raw = new_skb->ip_hdr;
 
 		/* Free the old packet, we no longer need it */
 		dev_kfree_skb(skb, FREE_WRITE);
diff -u --recursive --new-file v2.0.30/linux/drivers/net/ppp.c linux/drivers/net/ppp.c
--- v2.0.30/linux/drivers/net/ppp.c	Tue Oct  8 11:21:03 1996
+++ linux/drivers/net/ppp.c	Tue Aug 12 14:15:56 1997
@@ -6,7 +6,7 @@
  *  Dynamic PPP devices by Jim Freeman <jfree@caldera.com>.
  *  ppp_tty_receive ``noisy-raise-bug'' fixed by Ove Ewerlid <ewerlid@syscon.uu.se>
  *
- *  ==FILEVERSION 960528==
+ *  ==FILEVERSION 970703==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, please set the number above to the
@@ -406,7 +406,14 @@
 		skb_queue_head_init (&dev->buffs[indx]);
 
 	/* New-style flags */
+#ifdef IFF_SOFTHEADERS
+	/* Needed to make SOCK_PACKET work correctly in
+	 * memory fussy kernels.
+	 */
+	dev->flags	= IFF_POINTOPOINT|IFF_SOFTHEADERS;
+#else
 	dev->flags	= IFF_POINTOPOINT;
+#endif
 	dev->family	= AF_INET;
 	dev->pa_addr	= 0;
 	dev->pa_brdaddr = 0;
@@ -697,7 +704,7 @@
 
 	if (dev && dev->flags & IFF_UP) {
 		dev_close (dev); /* close the device properly */
-		dev->flags = 0;	 /* prevent recursion */
+		dev->flags &= ~IFF_UP;	 /* prevent recursion */
 	}
 
 	ppp_free_buf (ppp->rbuf);
@@ -3079,7 +3086,7 @@
  * Fetch the pointer to the data
  */
 	len   = skb->len;
-	data  = skb_data(skb);
+	data  = skb_data(skb) + PPP_HARD_HDR_LEN;
 /*
  * Bug trap for null data. Release the skb and bail out.
  */
@@ -3152,7 +3159,12 @@
 			   __u16 type, void *daddr,
 			   void *saddr, unsigned int len)
 {
-	return (0);
+	/* On the PPP device the hard header must be ignored
+	 * by the SOCK_PACKET layer. (Backward compatability).
+	 */
+	skb->mac.raw = skb->data;
+	skb_push(skb,PPP_HARD_HDR_LEN);
+	return PPP_HARD_HDR_LEN;
 }
 
 static int
diff -u --recursive --new-file v2.0.30/linux/drivers/net/smc-ultra32.c linux/drivers/net/smc-ultra32.c
--- v2.0.30/linux/drivers/net/smc-ultra32.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/smc-ultra32.c	Tue Aug 12 16:04:57 1997
@@ -0,0 +1,413 @@
+/* 	smc-ultra32.c: An SMC Ultra32 EISA ethernet driver for linux.
+
+Sources:
+
+	This driver is based on (cloned from) the ISA SMC Ultra driver
+	written by Donald Becker. Modifications to support the EISA
+	version of the card by Paul Gortmaker and Leonard N. Zubkoff.
+
+	This software may be used and distributed according to the terms
+	of the GNU Public License, incorporated herein by reference.
+
+Theory of Operation:
+
+	The SMC Ultra32C card uses the SMC 83c790 chip which is also
+	found on the ISA SMC Ultra cards. It has a shared memory mode of
+	operation that makes it similar to the ISA version of the card.
+	The main difference is that the EISA card has 32KB of RAM, but
+	only an 8KB window into that memory. The EISA card also can be
+	set for a bus-mastering mode of operation via the ECU, but that
+	is not (and probably will never be) supported by this driver.
+	The ECU should be run to enable shared memory and to disable the
+	bus-mastering feature for use with linux.
+
+	By programming the 8390 to use only 8KB RAM, the modifications
+	to the ISA driver can be limited to the probe and initialization
+	code. This allows easy integration of EISA support into the ISA
+	driver. However, the driver development kit from SMC provided the
+	register information for sliding the 8KB window, and hence the 8390
+	is programmed to use the full 32KB RAM.
+
+	Unfortunately this required code changes outside the probe/init
+	routines, and thus we decided to separate the EISA driver from
+	the ISA one. In this way, ISA users don't end up with a larger
+	driver due to the EISA code, and EISA users don't end up with a
+	larger driver due to the ISA EtherEZ PIO code. The driver is
+	similar to the 3c503/16 driver, in that the window must be set
+	back to the 1st 8KB of space for access to the two 8390 Tx slots.
+
+	In testing, using only 8KB RAM (3 Tx / 5 Rx) didn't appear to
+	be a limiting factor, since the EISA bus could get packets off
+	the card fast enough, but having the use of lots of RAM as Rx
+	space is extra insurance if interrupt latencies become excessive.
+
+*/
+
+static const char *version = "smc-ultra32.c: 06/97 v1.00\n";
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include "8390.h"
+
+int ultra32_probe(struct device *dev);
+int ultra32_probe1(struct device *dev, int ioaddr);
+static int ultra32_open(struct device *dev);
+static void ultra32_reset_8390(struct device *dev);
+static void ultra32_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+				 int ring_page);
+static void ultra32_block_input(struct device *dev, int count,
+				struct sk_buff *skb, int ring_offset);
+static void ultra32_block_output(struct device *dev, int count,
+				 const unsigned char *buf, const start_page);
+static int ultra32_close(struct device *dev);
+
+#define ULTRA32_CMDREG	0	/* Offset to ASIC command register. */
+#define	 ULTRA32_RESET	0x80	/* Board reset, in ULTRA32_CMDREG. */
+#define	 ULTRA32_MEMENB	0x40	/* Enable the shared memory. */
+#define ULTRA32_NIC_OFFSET 16	/* NIC register offset from the base_addr. */
+#define ULTRA32_IO_EXTENT 32
+#define EN0_ERWCNT		0x08	/* Early receive warning count. */
+
+/*
+ * Defines that apply only to the Ultra32 EISA card. Note that
+ * "smc" = 10011 01101 00011 = 0x4da3, and hence !smc8010.cfg translates
+ * into an EISA ID of 0x1080A34D
+ */
+#define ULTRA32_BASE	0xca0
+#define ULTRA32_ID	0x1080a34d
+#define ULTRA32_IDPORT	(-0x20)	/* 0xc80 */
+/* Config regs 1->7 from the EISA !SMC8010.CFG file. */
+#define ULTRA32_CFG1	0x04	/* 0xca4 */
+#define ULTRA32_CFG2	0x05	/* 0xca5 */
+#define ULTRA32_CFG3	(-0x18)	/* 0xc88 */
+#define ULTRA32_CFG4	(-0x17)	/* 0xc89 */
+#define ULTRA32_CFG5	(-0x16)	/* 0xc8a */
+#define ULTRA32_CFG6	(-0x15)	/* 0xc8b */
+#define ULTRA32_CFG7	0x0d	/* 0xcad */
+
+
+/*	Probe for the Ultra32.  This looks like a 8013 with the station
+	address PROM at I/O ports <base>+8 to <base>+13, with a checksum
+	following.
+*/
+
+int ultra32_probe(struct device *dev)
+{
+	const char *ifmap[] = {"UTP No Link", "", "UTP/AUI", "UTP/BNC"};
+	int ioaddr, edge, media;
+
+	if (!EISA_bus) return ENODEV;
+
+	/* EISA spec allows for up to 16 slots, but 8 is typical. */
+	for (ioaddr = 0x1000 + ULTRA32_BASE; ioaddr < 0x9000; ioaddr += 0x1000)
+	if (check_region(ioaddr, ULTRA32_IO_EXTENT) == 0 &&
+	    inb(ioaddr + ULTRA32_IDPORT) != 0xff &&
+	    inl(ioaddr + ULTRA32_IDPORT) == ULTRA32_ID) {
+		media = inb(ioaddr + ULTRA32_CFG7) & 0x03;
+		edge = inb(ioaddr + ULTRA32_CFG5) & 0x08;
+		printk("SMC Ultra32 in EISA Slot %d, Media: %s, %s IRQs.\n",
+		       ioaddr >> 12, ifmap[media],
+		       (edge ? "Edge Triggered" : "Level Sensitive"));
+		if (ultra32_probe1(dev, ioaddr) == 0)
+		  return 0;
+	}
+	return ENODEV;
+}
+
+int ultra32_probe1(struct device *dev, int ioaddr)
+{
+	int i;
+	int checksum = 0;
+	const char *model_name;
+	static unsigned version_printed = 0;
+	/* Values from various config regs. */
+	unsigned char idreg = inb(ioaddr + 7);
+	unsigned char reg4 = inb(ioaddr + 4) & 0x7f;
+
+	/* Check the ID nibble. */
+	if ((idreg & 0xf0) != 0x20) 			/* SMC Ultra */
+		return ENODEV;
+
+	/* Select the station address register set. */
+	outb(reg4, ioaddr + 4);
+
+	for (i = 0; i < 8; i++)
+		checksum += inb(ioaddr + 8 + i);
+	if ((checksum & 0xff) != 0xff)
+		return ENODEV;
+
+	/* We should have a "dev" from Space.c or the static module table. */
+	if (dev == NULL) {
+		printk("smc-ultra32.c: Passed a NULL device.\n");
+		dev = init_etherdev(0, 0);
+	}
+
+	if (ei_debug  &&  version_printed++ == 0)
+		printk(version);
+
+	model_name = "SMC Ultra32";
+
+	printk("%s: %s at 0x%X,", dev->name, model_name, ioaddr);
+
+	for (i = 0; i < 6; i++)
+		printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i));
+
+	/* Switch from the station address to the alternate register set and
+	   read the useful registers there. */
+	outb(0x80 | reg4, ioaddr + 4);
+
+	/* Enable FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */
+	outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c);
+
+	/* Reset RAM addr. */
+	outb(0x00, ioaddr + 0x0b);
+
+	/* Switch back to the station address register set so that the
+	   MS-DOS driver can find the card after a warm boot. */
+	outb(reg4, ioaddr + 4);
+
+	if ((inb(ioaddr + ULTRA32_CFG5) & 0x40) == 0) {
+		printk("\nsmc-ultra32: Card RAM is disabled!  "
+		       "Run EISA config utility.\n");
+		return ENODEV;
+	}
+	if ((inb(ioaddr + ULTRA32_CFG2) & 0x04) == 0)
+		printk("\nsmc-ultra32: Ignoring Bus-Master enable bit.  "
+		       "Run EISA config utility.\n");
+
+	if (dev->irq < 2) {
+		unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15};
+		int irq = irqmap[inb(ioaddr + ULTRA32_CFG5) & 0x07];
+		if (irq == 0) {
+			printk(", failed to detect IRQ line.\n");
+			return -EAGAIN;
+		}
+		dev->irq = irq;
+	}
+
+	/* Allocate dev->priv and fill in 8390 specific dev fields. */
+	if (ethdev_init(dev)) {
+		printk (", no memory for dev->priv.\n");
+                return -ENOMEM;
+        }
+
+	/* OK, we are certain this is going to work.  Setup the device. */
+	request_region(ioaddr, ULTRA32_IO_EXTENT, model_name);
+
+	/* The 8390 isn't at the base address, so fake the offset */
+	dev->base_addr = ioaddr + ULTRA32_NIC_OFFSET;
+
+	/* Save RAM address in the unused reg0 to avoid excess inb's. */
+	ei_status.reg0 = inb(ioaddr + ULTRA32_CFG3) & 0xfc;
+
+	dev->mem_start =  0xc0000 + ((ei_status.reg0 & 0x7c) << 11);
+
+	ei_status.name = model_name;
+	ei_status.word16 = 1;
+	ei_status.tx_start_page = 0;
+	ei_status.rx_start_page = TX_PAGES;
+	/* All Ultra32 cards have 32KB memory with an 8KB window. */
+	ei_status.stop_page = 128;
+
+	dev->rmem_start = dev->mem_start + TX_PAGES*256;
+	dev->mem_end = dev->rmem_end = dev->mem_start + 0x1fff;
+
+	printk(", IRQ %d, 32KB memory, 8KB window at 0x%lx-0x%lx.\n",
+	       dev->irq, dev->mem_start, dev->mem_end);
+	ei_status.block_input = &ultra32_block_input;
+	ei_status.block_output = &ultra32_block_output;
+	ei_status.get_8390_hdr = &ultra32_get_8390_hdr;
+	ei_status.reset_8390 = &ultra32_reset_8390;
+	dev->open = &ultra32_open;
+	dev->stop = &ultra32_close;
+	NS8390_init(dev, 0);
+
+	return 0;
+}
+
+static int ultra32_open(struct device *dev)
+{
+	int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* ASIC addr */
+
+	if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name, NULL))
+		return -EAGAIN;
+
+	outb(ULTRA32_MEMENB, ioaddr); /* Enable Shared Memory. */
+	outb(0x80, ioaddr + ULTRA32_CFG6); /* Enable Interrupts. */
+	outb(0x84, ioaddr + 5);	/* Enable MEM16 & Disable Bus Master. */
+	outb(0x01, ioaddr + 6);	/* Enable Interrupts. */
+	/* Set the early receive warning level in window 0 high enough not
+	   to receive ERW interrupts. */
+	outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr);
+	outb(0xff, dev->base_addr + EN0_ERWCNT);
+	ei_open(dev);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int ultra32_close(struct device *dev)
+{
+	int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* CMDREG */
+
+	dev->start = 0;
+	dev->tbusy = 1;
+
+	if (ei_debug > 1)
+		printk("%s: Shutting down ethercard.\n", dev->name);
+
+	outb(0x00, ioaddr + ULTRA32_CFG6); /* Disable Interrupts. */
+	outb(0x00, ioaddr + 6);		/* Disable interrupts. */
+	free_irq(dev->irq, NULL);
+	irq2dev_map[dev->irq] = 0;
+
+	NS8390_init(dev, 0);
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+static void ultra32_reset_8390(struct device *dev)
+{
+	int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* ASIC base addr */
+
+	outb(ULTRA32_RESET, ioaddr);
+	if (ei_debug > 1) printk("resetting Ultra32, t=%ld...", jiffies);
+	ei_status.txing = 0;
+
+	outb(ULTRA32_MEMENB, ioaddr); /* Enable Shared Memory. */
+	outb(0x80, ioaddr + ULTRA32_CFG6); /* Enable Interrupts. */
+	outb(0x84, ioaddr + 5);	/* Enable MEM16 & Disable Bus Master. */
+	outb(0x01, ioaddr + 6);	/* Enable Interrupts. */
+	if (ei_debug > 1) printk("reset done\n");
+	return;
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+   we don't need to be concerned with ring wrap as the header will be at
+   the start of a page, so we optimize accordingly. */
+
+static void ultra32_get_8390_hdr(struct device *dev,
+				 struct e8390_pkt_hdr *hdr,
+				 int ring_page)
+{
+	unsigned long hdr_start = dev->mem_start + ((ring_page & 0x1f) << 8);
+	unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3;
+
+	/* Select correct 8KB Window. */
+	outb(ei_status.reg0 | ((ring_page & 0x60) >> 5), RamReg);
+
+#ifdef notdef
+	/* Officially this is what we are doing, but the readl() is faster */
+	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+#else
+	((unsigned int*)hdr)[0] = readl(hdr_start);
+#endif
+}
+
+/* Block input and output are easy on shared memory ethercards, the only
+   complication is when the ring buffer wraps, or in this case, when a
+   packet spans an 8KB boundary. Note that the current 8KB segment is
+   already set by the get_8390_hdr routine. */
+
+static void ultra32_block_input(struct device *dev,
+				int count,
+				struct sk_buff *skb,
+				int ring_offset)
+{
+	unsigned long xfer_start = dev->mem_start + (ring_offset & 0x1fff);
+	unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3;
+
+	if ((ring_offset & ~0x1fff) != ((ring_offset + count - 1) & ~0x1fff)) {
+		int semi_count = 8192 - (ring_offset & 0x1FFF);
+		memcpy_fromio(skb->data, xfer_start, semi_count);
+		count -= semi_count;
+		if (ring_offset < 96*256) {
+			/* Select next 8KB Window. */
+			ring_offset += semi_count;
+			outb(ei_status.reg0 | ((ring_offset & 0x6000) >> 13), RamReg);
+			memcpy_fromio(skb->data + semi_count, dev->mem_start, count);
+		} else {
+			/* Select first 8KB Window. */
+			outb(ei_status.reg0, RamReg);
+			memcpy_fromio(skb->data + semi_count, dev->rmem_start, count);
+		}
+	} else {
+		/* Packet is in one chunk -- we can copy + cksum. */
+		eth_io_copy_and_sum(skb, xfer_start, count, 0);
+	}
+}
+
+static void ultra32_block_output(struct device *dev,
+				 int count,
+				 const unsigned char *buf,
+				 int start_page)
+{
+	unsigned long xfer_start = dev->mem_start + (start_page<<8);
+	unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3;
+
+	/* Select first 8KB Window. */
+	outb(ei_status.reg0, RamReg);
+
+	memcpy_toio(xfer_start, buf, count);
+}
+
+#ifdef MODULE
+#define MAX_ULTRA32_CARDS   4	/* Max number of Ultra cards per module */
+#define NAMELEN		    8	/* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_ULTRA32_CARDS] = { 0, };
+static struct device dev_ultra[MAX_ULTRA32_CARDS] = {
+	{
+		NULL,		/* assign a chunk of namelist[] below */
+		0, 0, 0, 0,
+		0, 0,
+		0, 0, 0, NULL, NULL
+	},
+};
+
+int init_module(void)
+{
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < MAX_ULTRA32_CARDS; this_dev++) {
+		struct device *dev = &dev_ultra[this_dev];
+		dev->name = namelist+(NAMELEN*this_dev);
+		dev->init = ultra32_probe;
+		if (register_netdev(dev) != 0) {
+			if (found > 0) return 0; /* Got at least one. */
+			printk(KERN_WARNING "smc-ultra32.c: No SMC Ultra32 found.\n");
+			return -ENXIO;
+		}
+		found++;
+	}
+
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	int this_dev;
+
+	for (this_dev = 0; this_dev < MAX_ULTRA32_CARDS; this_dev++) {
+		struct device *dev = &dev_ultra[this_dev];
+		if (dev->priv != NULL) {
+			/* NB: ultra32_close_card() does free_irq + irq2dev */
+			int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET;
+			kfree(dev->priv);
+			dev->priv = NULL;
+			release_region(ioaddr, ULTRA32_IO_EXTENT);
+			unregister_netdev(dev);
+		}
+	}
+}
+#endif /* MODULE */
diff -u --recursive --new-file v2.0.30/linux/drivers/net/tulip.c linux/drivers/net/tulip.c
--- v2.0.30/linux/drivers/net/tulip.c	Tue Apr  8 08:47:46 1997
+++ linux/drivers/net/tulip.c	Fri Sep  5 11:20:20 1997
@@ -1,7 +1,7 @@
-/* tulip.c: A DEC 21040 ethernet driver for linux. */
+/* tulip.c: A DEC 21040-family ethernet driver for linux. */
 /*
-   NOTICE: this version works with kernels 1.1.82 and later only!
-	Written 1994,1995 by Donald Becker.
+   NOTICE: THIS IS THE ALPHA TEST VERSION!
+	Written 1994-1997 by Donald Becker.
 
 	This software may be used and distributed according to the terms
 	of the GNU Public License, incorporated herein by reference.
@@ -13,44 +13,60 @@
 	Center of Excellence in Space Data and Information Sciences
 	   Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
 
-	Subscribe to linux-tulip@cesdis.gsfc.nasa.gov and linux-tulip-bugs@cesdis.gsfc.nasa.gov
-	for late breaking news and exciting develovements.
+	Support and updates available at
+	http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html
 */
 
-static char *version =
-"tulip.c:v0.10 8/11/95 becker@cesdis.gsfc.nasa.gov\n"
-"        +0.72 4/17/96 "
-"http://www.dsl.tutics.tut.ac.jp/~linux/tulip\n"
-"        +0.02 12/15/96 mjacob@feral.com (2.0.27)\n";
+static const char *version = "tulip.c:v0.79 9/3/97 becker@cesdis.gsfc.nasa.gov\n";
 
 /* A few user-configurable values. */
 
-/* Default to using 10baseT (i.e. AUI/10base2/100baseT port) port. */
-#define	TULIP_10TP_PORT		0
-#define	TULIP_100TP_PORT	1
-#define	TULIP_AUI_PORT		1
-#define	TULIP_BNC_PORT		2
-#define	TULIP_MAX_PORT		3
-#define	TULIP_AUTO_PORT		-1
+/* Set if the PCI BIOS detects the chips on a multiport board backwards. */
+#ifdef REVERSE_PROBE_ORDER
+static int reverse_probe = 1;
+#else
+static int reverse_probe = 0;
+#endif
 
-#ifndef	TULIP_PORT
-#define	TULIP_PORT			TULIP_10TP_PORT
+/* Keep the ring sizes a power of two for efficiency.
+   Making the Tx ring too large decreases the effectiveness of channel
+   bonding and packet priority.
+   There are no ill effects from too-large receive rings. */
+#define TX_RING_SIZE	16
+#define RX_RING_SIZE	16
+
+/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */
+static const rx_copybreak = 200;
+
+/* The following example shows how to always use the 10base2 port. */
+#ifdef notdef
+#define TULIP_DEFAULT_MEDIA 1		/* 1 == 10base2 */
+#define TULIP_NO_MEDIA_SWITCH		/* Don't switch from this port */
 #endif
 
 /* Define to force full-duplex operation on all Tulip interfaces. */
 /* #define  TULIP_FULL_DUPLEX 1 */
 
-/* Define to fix port. */
-/* #define  TULIP_FIX_PORT 1 */
-
-/* Define to probe only first detected device */
-/*#define	TULIP_MAX_CARDS 1*/
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  ((2000*HZ)/1000)
 
+#include <linux/config.h>
+#ifdef MODULE
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
 #include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
 
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/string.h>
+#include <linux/timer.h>
 #include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
@@ -58,8 +74,7 @@
 #include <linux/interrupt.h>
 #include <linux/pci.h>
 #include <linux/bios32.h>
-#include <linux/delay.h>
-#include <asm/byteorder.h>
+#include <asm/processor.h>		/* Processor type for cache alignment. */
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
@@ -68,35 +83,116 @@
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 
+/* Kernel compatibility defines, common to David Hind's PCMCIA package.
+   This is only in the support-all-kernels source code. */
+#include <linux/version.h>		/* Evil, but neccessary */
+
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10300
+#define RUN_AT(x) (x)			/* What to put in timer->expires.  */
+#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC)
+#define virt_to_bus(addr)  ((unsigned long)addr)
+#define bus_to_virt(addr) ((void*)addr)
+
+#else  /* 1.3.0 and later */
+#define RUN_AT(x) (jiffies + (x))
+#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
+#endif
+
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10338
+#ifdef MODULE
+#if !defined(CONFIG_MODVERSIONS) && !defined(__NO_VERSION__)
+char kernel_version[] = UTS_RELEASE;
+#endif
+#else
+#undef MOD_INC_USE_COUNT
+#define MOD_INC_USE_COUNT
+#undef MOD_DEC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+#endif /* 1.3.38 */
+
+#if (LINUX_VERSION_CODE >= 0x10344)
+#define NEW_MULTICAST
+#include <linux/delay.h>
+#endif
+#if (LINUX_VERSION_CODE >= 0x20100)
+char kernel_version[] = UTS_RELEASE;
+#endif
+#ifdef SA_SHIRQ
+#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs)
+#else
+#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs)
+#endif
+
+#if (LINUX_VERSION_CODE < 0x20123)
+#define test_and_set_bit(val, addr) set_bit(val, addr)
+#endif
+
+/* This my implementation of shared IRQs, now only used for 1.2.13. */
+#ifdef HAVE_SHARED_IRQ
+#define USE_SHARED_IRQ
+#include <linux/shared_irq.h>
+#endif
+
 /* The total size is unusually large: The 21040 aligns each of its 16
    longword-wide registers on a quadword boundary. */
 #define TULIP_TOTAL_SIZE 0x80
 
+#ifdef HAVE_DEVLIST
+struct netdev_entry tulip_drv =
+{"Tulip", tulip_pci_probe, TULIP_TOTAL_SIZE, NULL};
+#endif
+
+#ifdef TULIP_DEBUG
+int tulip_debug = TULIP_DEBUG;
+#else
+int tulip_debug = 1;
+#endif
+
 /*
 				Theory of Operation
 
 I. Board Compatibility
 
-This device driver is designed for the DECchip 21040 "Tulip", Digital's
-single-chip ethernet controller for PCI, as used on the SMC EtherPower
-ethernet adapter.  It also works with boards based the 21041 (new/experimental)
-and 21140 (10/100mbps).
+This device driver is designed for the DECchip "Tulip", Digital's
+single-chip ethernet controllers for PCI.  Supported members of the family
+are the 21040, 21041, 21140, 21140A and 21142.  These chips are used on
+many PCI boards including the SMC EtherPower series.
 
 
 II. Board-specific settings
 
 PCI bus devices are configured by the system at boot time, so no jumpers
-need to be set on the board.  The system BIOS should be set to assign the
-PCI INTA signal to an otherwise unused system IRQ line.  While it's
-physically possible to shared PCI interrupt lines, the kernel doesn't
-support it. 
+need to be set on the board.  The system BIOS preferably should assign the
+PCI INTA signal to an otherwise unused system IRQ line.
+Note: Kernel versions earlier than 1.3.73 do not support shared PCI
+interrupt lines.
 
 III. Driver operation
 
 IIIa. Ring buffers
+
 The Tulip can use either ring buffers or lists of Tx and Rx descriptors.
-The current driver uses a statically allocated Rx ring of descriptors and
-buffers, and a list of the Tx buffers.
+This driver uses statically allocated rings of Rx and Tx descriptors, set at
+compile time by RX/TX_RING_SIZE.  This version of the driver allocates skbuffs
+for the Rx ring buffers at open() time and passes the skb->data field to the
+Tulip as receive data buffers.  When an incoming frame is less than
+RX_COPYBREAK bytes long, a fresh skbuff is allocated and the frame is
+copied to the new skbuff.  When the incoming frame is larger, the skbuff is
+passed directly up the protocol stack and replaced by a newly allocated
+skbuff.
+
+The RX_COPYBREAK value is chosen to trade-off the memory wasted by
+using a full-sized skbuff for small frames vs. the copying costs of larger
+frames.  For small frames the copying cost is negligible (esp. considering
+that we are pre-loading the cache with immediately useful header
+information).  For large frames the copying cost is non-trivial, and the
+larger copy might flush the cache of useful data.  A subtle aspect of this
+choice is that the Tulip only receives into longword aligned buffers, thus
+the IP header at offset 14 isn't longword aligned for further processing.
+Copied frames are put into the new skbuff at an offset of "+2", thus copying
+has the beneficial effect of aligning the IP header and preloading the
+cache.
 
 IIIC. Synchronization
 The driver runs as two independent, single-threaded flows of control.  One
@@ -120,592 +216,863 @@
 
 Thanks to Duke Kamstra of SMC for providing an EtherPower board.
 
+IVb. References
+
+http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
+http://www.digital.com  (search for current 21*4* datasheets and "21X4 SROM")
+http://www.national.com/pf/DP/DP83840.html
+
+IVc. Errata
+
 The DEC databook doesn't document which Rx filter settings accept broadcast
 packets.  Nor does it document how to configure the part to configure the
 serial subsystem for normal (vs. loopback) operation or how to have it
 autoswitch between internal 10baseT, SIA and AUI transceivers.
 
-The databook claims that CSR13, CSR14, and CSR15 should each be the last
-register of the set CSR12-15 written.   Hmmm, now how is that possible?
-*/
+The 21040 databook claims that CSR13, CSR14, and CSR15 should each be the last
+register of the set CSR12-15 written.  Hmmm, now how is that possible?  */
+
 
 /* A few values that may be tweaked. */
-/* Keep the ring sizes a power of two for efficiency. */
-#define TX_RING_SIZE	4
-#define RX_RING_SIZE	4
 #define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
 
-/* This is a mysterious value that can be written to CSR11 in the 21040
-   to detect a full-duplex frame.  No one knows what it should be, but if
-   left at its default value some 10base2(!) packets trigger a
-   full-duplex-request interrupt. */
+/* This is a mysterious value that can be written to CSR11 in the 21040 (only)
+   to support a pre-NWay full-duplex signaling mechanism using short frames.
+   No one knows what it should be, but if left at its default value some
+   10base2(!) packets trigger a full-duplex-request interrupt. */
 #define FULL_DUPLEX_MAGIC	0x6969
 
+#ifndef PCI_VENDOR_ID_DEC		/* Now defined in linux/pci.h */
+#define PCI_VENDOR_ID_DEC			0x1011
+#define PCI_DEVICE_ID_TULIP			0x0002		/* 21040. */
+#define PCI_DEVICE_ID_TULIP_FAST	0x0009		/* 21140. */
+#endif
+
+#ifndef PCI_DEVICE_ID_DEC_TULIP_PLUS
+#define PCI_DEVICE_ID_DEC_TULIP_PLUS	0x0014		/* 21041. */
+#endif
+
+#ifndef PCI_DEVICE_ID_DEC_TULIP_21142
+#define PCI_DEVICE_ID_DEC_TULIP_21142	0x0019
+#endif
+
 /* The rest of these values should never change. */
 
-#define PCI_DEVICE_ID_NONE	0xFFFF
-#define	ETHNAMSIZ	8
-#define	ROUND_UP(size, n)	((size + n - 1) & ~(n - 1))
+static void tulip_timer(unsigned long data);
+
+/* A table describing the chip types. */
+static struct tulip_chip_table {
+  int device_id;
+  char *chip_name;
+  int flags;
+  void (*media_timer)(unsigned long data);
+} tulip_tbl[] = {
+  { PCI_DEVICE_ID_DEC_TULIP, "DS21040 Tulip", 0, tulip_timer },
+  { PCI_DEVICE_ID_DEC_TULIP_PLUS, "DS21041 Tulip", 0, tulip_timer },
+  { PCI_DEVICE_ID_DEC_TULIP_FAST, "DS21140 Tulip", 0, tulip_timer }, /* + 21140A*/
+  { PCI_DEVICE_ID_DEC_TULIP_21142, "DS21142 Tulip", 0, tulip_timer }, /* + 21143 */
+  {0, 0, 0, 0},
+};
+/* This matches the table above. */
+enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, };
+
+static const char * const medianame[] = {
+  "10baseT", "10base2", "AUI", "100baseTx",
+  "10baseT-FD", "100baseTx-FD", "100baseT4", "100baseFx",
+  "100baseFx-FD", "MII 10baseT", "MII 10baseT-FD", "MII",
+  "10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FD", "MII 100baseT4",
+};
+/* A full-duplex map for above. */
+static const char media_fd[] =
+{0,0,0,0,  0xff,0xff,0,0,  0xff,0,0xff,0x01, 0,0,0xff,0 };
+/* 21041 transceiver register settings: 10-T, 10-2, AUI, 10-T, 10T-FD*/
+static u16 t21041_csr13[] = { 0xEF05, 0xEF09, 0xEF09, 0xEF01, 0xEF09, };
+static u16 t21041_csr14[] = { 0x7F3F, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, };
+static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
+
+static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0001, 0x0001, };
+static u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x7F3F, 0x7F3D, };
+static u16 t21142_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
 
 /* Offsets to the Command and Status Registers, "CSRs".  All accesses
    must be longword instructions and quadword aligned. */
 enum tulip_offsets {
-				/* 21040		21041		21140		*/
-	CSR0=0,		/* BUS mode								*/
-    CSR1=0x08,	/* TX poll demand						*/
-	CSR2=0x10,	/* RX poll demand						*/
-	CSR3=0x18,	/* RX ring base addr					*/
-	CSR4=0x20,	/* TX ring base addr					*/
-	CSR5=0x28,	/* Status								*/
-	CSR6=0x30,	/* Command mode							*/
-	CSR7=0x38,	/* Interrupt Mask						*/
-	CSR8=0x40,	/* Missed frame counter					*/
-	CSR9=0x48,	/* Eth.addrROM	SROM mii	SROM mii	*/
-	CSR10=0x50,	/* Diagn.		boot ROM	-			*/
-	CSR11=0x58,	/* Full duplex	G.P. timer	G.P. timer	*/
-	CSR12=0x60,	/* SIA status				G.P.		*/
-	CSR13=0x68,	/* SIA connectivity			-			*/
-	CSR14=0x70,	/* SIA TX/RX				-			*/
-	CSR15=0x78	/* SIA general				watchdog	*/
+	CSR0=0,    CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,
+	CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,
+	CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 };
+
+/* The bits in the CSR5 status registers, mostly interrupt sources. */
+enum status_bits {
+	TimerInt=0x800, TPLnkFail=0x1000, TPLnkPass=0x10,
+	RxJabber=0x200, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40,
+	TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02,
+	TxIntr=0x01,
 };
 
-/* description of CSR0 bus mode register */
-#define	TBMOD_RESERVED		0xfff80000	/* I don't know */
-#define	TBMOD_RESET			0x00000001
-#define	TBMOD_BIGENDIAN		0x00000080
-/*
-	   Cache alignment bits 15:14	     Burst length 13:8
-    	0000	No alignment  0x00000000 unlimited		0800 8 longwords
-		4000	8  longwords		0100 1 longword		1000 16 longwords
-		8000	16 longwords		0200 2 longwords	2000 32 longwords
-		C000	32  longwords		0400 4 longwords
-*/
-#define	TBMOD_ALIGN0		0x00000000	/* no cache alignment */
-#define	TBMOD_ALIGN8		0x00004000	/* 8 longwords */
-#define	TBMOD_ALIGN16		0x00008000
-#define	TBMOD_ALIGN32		(TBMOD_ALIGN8|TBMOD_ALIGN16)
-#define	TBMOD_BURST0		0x00000000	/* unlimited=rx buffer size */
-#define	TBMOD_BURST1		0x00000100	/* 1 longwords */
-#define	TBMOD_BURST2		0x00000200
-#define	TBMOD_BURST4		0x00000400
-#define	TBMOD_BURST8		0x00000800
-#define	TBMOD_BURST16		0x00001000
-#define	TBMOD_BURST32		0x00002000
-
-/* description of CSR1 Tx poll demand register */
-/* description of CSR2 Rx poll demand register */
-#define	TPOLL_START			0x00000001	/* ? */
-#define	TPOLL_TRIGGER		0x00000000	/* ? */
-
-/* description of CSR5 status register from de4x5.h */
-#define	TSTAT_BUSERROR		0x03800000
-#define	TSTAT_SYSERROR		0x00002000
-#define	TSTAT_TxSTAT		0x00700000
-#define	TSTAT_RxSTAT		0x000e0000
-#define	TSTAT_LKFAIL		0x00001000
-#define	TSTAT_NORINTR		0x00010000	/* Normal interrupt */
-#define	TSTAT_ABNINTR		0x00008000	/* Abnormal interrupt */
-#define	TSTAT_RxMISSED		0x00000100	/* Rx frame missed */
-#define	TSTAT_RxUNABL		0x00000080
-#define	TSTAT_RxINTR		0x00000040
-#define	TSTAT_LKPASS		0x00000010
-#define	TSTAT_TEXPIRED		0x00000800	/* Timer Expired */
-#define	TSTAT_TxTOUT		0x00000008
-#define	TSTAT_TxUNABL		0x00000004
-#define	TSTAT_TxINTR		0x00000001
-#define	TSTAT_CLEARINTR		0x0001ffff	/* clear all interrupt sources */
-
-/* description of CSR6 command mode register */
-#define	TCMOD_SCRM			0x01000000	/* scrambler mode */
-#define	TCMOD_PCS			0x00800000	/* PCS function */
-#define	TCMOD_TxTHMODE		0x00400000	/* Tx threshold mode */
-#define	TCMOD_SW100TP		0x00040000  /* 21140: 100MB */
-#define	TCMOD_CAPTURE		0x00020000	/* capture effect */
-#define	TCMOD_FULLDUPLEX	0x00000200
-#define TCMOD_TH128			0x00008000	/* 10 - 128 bytes threshold */
-#define	TCMOD_TxSTART		0x00002000
-#define	TCMOD_RxSTART		0x00000002
-#define	TCMOD_ALLMCAST		0x00000080	/* pass all multicast */
-#define	TCMOD_PROMISC		0x00000040	/* promisc */
-#define	TCMOD_BOFFCOUNTER	0x00000020	/* backoff counter */
-#define	TCMOD_INVFILTER		0x00000010	/* invert filtering */
-#define	TCMOD_HONLYFILTER	0x00000004	/* hash only filtering */
-#define TCMOD_HPFILTER		0x00000001	/* hash/perfect Rx filtering */
-#define	TCMOD_MODEMASK		(TCMOD_ALLMCAST|TCMOD_PROMISC)
-#define	TCMOD_FILTERMASK	(TCMOD_HONLYFILTER|TCMOD_HPFILTER|TCMOD_INVFILTER)
-#define	TCMOD_TRxSTART		(TCMOD_TxSTART|TCMOD_RxSTART)
-#define	TCMOD_BASE			(TCMOD_CAPTURE|TCMOD_BOFFCOUNTER)
-#define	TCMOD_10TP			(TCMOD_TxTHMODE|TCMOD_BASE)
-#define	TCMOD_100TP			(TCMOD_SCRM|TCMOD_PCS|TCMOD_SW100TP|TCMOD_BASE)
-#define	TCMOD_AUTO			(TCMOD_SW100TP|TCMOD_TH128|TCMOD_10TP)
-
-/* description of CSR7 interrupt mask register */
-#define	TINTR_ENABLE		0xFFFFFFFF
-#define	TINTR_DISABLE		0x00000000
-
-/* description of CSR11 G.P. timer (21041/21140) register */
-#define	TGEPT_COUNT			0x0001FFFF
-
-/* description of CSR12 SIA status(2104x)/GP(21140) register */
-#define	TSIAS_CONERROR		0x00000002	/* connection error */
-#define	TSIAS_LNKERROR		0x00000004	/* link error */
-#define TSIAS_ACTERROR		0x00000200  /* port Rx activity */
-#define TSIAS_RxACTIVE		0x00000100  /* port Rx activity */
-
-#define	TGEPR_LK10NG		0x00000080	/* 10Mbps N.G. (R) */
-#define	TGEPR_LK100NG		0x00000040	/* 100Mbps N.G. (R) */
-#define	TGEPR_DETECT		0x00000020	/* detect signal (R) */
-#define	TGEPR_HALFDUPLEX	0x00000008	/* half duplex (W) */
-#define	TGEPR_PHYLOOPBACK	0x00000004	/* PHY loopback (W) */
-#define	TGEPR_FORCEALED		0x00000002	/* force activity LED on (W) */
-#define	TGEPR_FORCE100		0x00000001	/* force 100Mbps mode */
-
-/* description of CSR13 SIA connectivity register */
-#define	TSIAC_OUTEN			0x0000e000	/* 21041: Output enable */
-#define TSIAC_SELED			0x00000f00	/* 21041: AUI or TP with LEDs */
-#define	TSIAC_INEN			0x00001000	/* 21041: Input enable */
-#define	TSIAC_NO10TP		0x00000008	/* 10baseT(0) or not(1) */
-#define TSIAC_CONFIG		0x00000004	/* Configuration */
-#define	TSIAC_SWRESET		0x00000001	/* 21041: software reset */
-#define	TSIAC_RESET			0x00000000	/* reset */
-#define	TSIAC_C21041		(TSIAC_OUTEN|TSIAC_SELED|TSIAC_SWRESET)
-#define	TSIAC_C21040		TSIAC_CONFIG
-
-/* description of CSR14 SIA TX/RX register */
-#define	TSIAX_NO10TP		0x0000f73d
-#define	TSIAX_10TP			0x0000ff3f
-
-/* description of CSR15 SIA general register */
-#define	TSIAG_SWBNCAUI		0x00000008 /* BNC(0) or AUI(1) */
-#define	TSIAG_BNC			0x00000006
-#define	TSIAG_AUI			(TSIAG_BNC|TSIAG_SWBNCAUI)
-#define	TSIAG_10TP			0x00000000
-
-/* description of rx_ring.status */
-#define	TRING_OWN			0x80000000	/* Owned by chip */
-#define	TRING_CLEAR			0x00000000	/* clear */
-#define	TRING_ERROR			0x00008000	/* error summary */
-#define	TRING_ETxTO			0x00004000	/* Tx time out */
-#define	TRING_ELCOLL		0x00000200	/* late collision */
-#define	TRING_EFCOLL		0x00000100	/* fatal collision */
-#define	TRING_ELCARR		0x00000800	/* carrier lost */
-#define	TRING_ENCARR		0x00000400	/* no carrier */
-#define	TRING_ENOHB			0x00000080	/* heartbeat fail */
-#define	TRING_ELINK			0x00000004	/* link fail */
-#define	TRING_EUFLOW		0x00000002	/* underflow */
-
-#define	TRING_ELEN			0x00004000	/* length error */
-#define	TRING_FDESC			0x00000200	/* first descriptor */
-#define	TRING_LDESC			0x00000100	/* last descriptor */
-#define	TRING_ERUNT			0x00000800	/* runt frame */
-#define	TRING_ELONG			0x00000080	/* frame too long */
-#define	TRING_EWATCHDOG		0x00000010	/* receive watchdog */
-#define	TRING_EDRBIT		0x00000004	/* dribble bit */
-#define	TRING_ECRC			0x00000002	/* CRC error */
-#define	TRING_EOVERFLOW		0x00000001	/* overflow */
-
-#define	TRING_RxDESCMASK	(TRING_FDESC|TRING_LDESC)
-#define	TRING_RxLENGTH		(TRING_ERUNT|TRING_ELONG|TRING_EWATCHDOG)
-#define	TRING_RxFRAME		(TRING_EDRBIT)
-#define	TRING_RxCRC			(TRING_ECRC)
-#define	TRING_RxFIFO		(TRING_EOVERFLOW)
-#define	TRING_TxABORT		(TRING_ETxTO|TRING_EFCOLL|TRING_ELINK)
-#define	TRING_TxCARR		(TRING_ELCARR|TRING_ENCARR)
-#define	TRING_TxWINDOW		(TRING_ELCOLL)
-#define	TRING_TxFIFO		(TRING_EUFLOW)
-#define	TRING_TxHEARTBEAT	(TRING_ENOHB)
 /* The Tulip Rx and Tx buffer descriptors. */
 struct tulip_rx_desc {
 	s32 status;
 	s32 length;
-	u32 buffer1, buffer2;			/* We use only buffer 1.  */
+	u32 buffer1, buffer2;
 };
 
 struct tulip_tx_desc {
 	s32 status;
 	s32 length;
-	u32 buffer1, buffer2;			/* We use only buffer 1.  */
+	u32 buffer1, buffer2;				/* We use only buffer 1.  */
+};
+
+struct medialeaf {
+	u8 type;
+	u8 media;
+	unsigned char *leafdata;
+};
+
+struct mediatable {
+	u16 defaultmedia;
+	u8 leafcount, csr12dir;				/* General purpose pin directions. */
+	unsigned has_mii:1;
+	struct medialeaf mleaf[0];
+};
+
+struct mediainfo {
+	struct mediainfo *next;
+	int info_type;
+	int index;
+	struct non_mii { char media; unsigned char csr12val; char bitnum, flags;}  non_mii;
+	unsigned char *info;
 };
 
 struct tulip_private {
+	char devname[8];			/* Used only for kernel debugging. */
+	const char *product_name;
+	struct device *next_module;
 	struct tulip_rx_desc rx_ring[RX_RING_SIZE];
 	struct tulip_tx_desc tx_ring[TX_RING_SIZE];
 	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
 	struct sk_buff* tx_skbuff[TX_RING_SIZE];
-	char rx_buffs[RX_RING_SIZE][PKT_BUF_SZ];
-	/* temporary Rx buffers. */
+	/* The addresses of receive-in-place skbuffs. */
+	struct sk_buff* rx_skbuff[RX_RING_SIZE];
+	char *rx_buffs;				/* Address of temporary Rx buffers. */
+	int setup_frame[48];		/* Pseudo-Tx frame to init address table. */
+	int chip_id;
+	int revision;
+#if LINUX_VERSION_CODE > 0x20139
+	struct net_device_stats stats;
+#else
 	struct enet_statistics stats;
-	int setup_frame[48];	/* Pseudo-Tx frame to init address table. */
-	void (*port_select)(struct device *dev);
-	int (*port_fail)(struct device *dev);
-	struct device *next_module;
-	char *signature;
+#endif
+	struct timer_list timer;	/* Media selection timer. */
 	unsigned int cur_rx, cur_tx;		/* The next free ring entry */
 	unsigned int dirty_rx, dirty_tx;	/* The ring entries to be free()ed. */
 	unsigned int tx_full:1;				/* The Tx queue is full. */
 	unsigned int full_duplex:1;			/* Full-duplex operation requested. */
-	unsigned int port_fix:1;			/* Fix if_port to specified port. */
-};
-
-struct eeprom {
-    union {
-		struct { /* broken EEPROM structure */
-			u_char addr[ETH_ALEN];
-		} ng;
-		struct { /* DEC EtherWorks
-					and other cards which have correct eeprom structure */
-			u_char dum1[20];
-			u_char addr[ETH_ALEN];
-		} ok;
-    } hw;
-#define	ng_addr	hw.ng.addr
-#define	ok_addr	hw.ok.addr
-#define	EE_SIGNLEN	14		/* should be 102 ? */
-	u_char sign[EE_SIGNLEN];
+	unsigned int default_port:4;		/* Last dev->if_port value. */
+	unsigned int media2:4;				/* Secondary monitored media port. */
+	unsigned int medialock:1;			/* Don't sense media type. */
+	unsigned int mediasense:1;			/* Media sensing in progress. */
+	unsigned int csr6;					/* Current CSR6 control settings. */
+	unsigned char eeprom[128];			/* Serial EEPROM contents. */
+	signed char phys[4];				/* MII device addresses. */
+	struct mediatable *mtable;
+	int cur_index;						/* Current media index. */
+	unsigned char pci_bus, pci_device_fn;
+	int pad0, pad1;						/* Used for 8-byte alignment */
 };
 
-static int read_eeprom(int ioaddr, struct eeprom *eepp);
+/* Used to pass the full-duplex flag, etc. */
+static int full_duplex[8] = {0, };
+static int options[8] = {0, };
+
+static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq,
+								   int chip_id, int options);
+static void parse_eeprom(struct device *dev);
+static int read_eeprom(int ioaddr, int location);
+static int mdio_read(int ioaddr, int phy_id, int location);
+static void select_media(struct device *dev, int startup);
 static int tulip_open(struct device *dev);
+static void tulip_timer(unsigned long data);
+static void tulip_tx_timeout(struct device *dev);
 static void tulip_init_ring(struct device *dev);
 static int tulip_start_xmit(struct sk_buff *skb, struct device *dev);
 static int tulip_rx(struct device *dev);
-static void tulip_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs);
 static int tulip_close(struct device *dev);
 static struct enet_statistics *tulip_get_stats(struct device *dev);
+#ifdef NEW_MULTICAST
 static void set_multicast_list(struct device *dev);
+#else
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
+#endif
 
-#define	generic21140_fail	NULL
-static void generic21040_select(struct device *dev);
-static void generic21140_select(struct device *dev);
-static void generic21041_select(struct device *dev);
-static void auto21140_select(struct device *dev);
-static void cogent21140_select(struct device *dev);
-static int generic21040_fail(struct device *dev);
-static int generic21041_fail(struct device *dev);
+
 
 #ifdef MODULE
 /* A list of all installed Tulip devices, for removing the driver module. */
 static struct device *root_tulip_dev = NULL;
 #endif
 
-static struct {
-	void (*port_select)(struct device *dev);
-	int (*port_fail)(struct device *dev);
-	unsigned int vendor_id, device_id;
-	char *signature;
-	unsigned int array:1;
-} cardVendor[] = {
-	{generic21140_select, generic21140_fail,
-		 0x0000c000, PCI_DEVICE_ID_DEC_TULIP_FAST, "smc9332", 0},
-	{generic21041_select, generic21041_fail,
-		 0x0000c000, PCI_DEVICE_ID_DEC_TULIP_PLUS, "smc8432", 0},
-	{generic21040_select, generic21040_fail,
-		 0x0000c000, PCI_DEVICE_ID_DEC_TULIP, "old smc8432", 0},
-	{auto21140_select, generic21140_fail,
-		 0x0000f400, PCI_DEVICE_ID_DEC_TULIP_FAST, "LA100PCI", 0},
-	{cogent21140_select, generic21140_fail,
-		 0x00009200, PCI_DEVICE_ID_DEC_TULIP_FAST, "cogent_em110", 0},
-	{generic21040_select, generic21040_fail,
-		0x00009200, PCI_DEVICE_ID_DEC_TULIP, "cogent_em96x", 1},
-	{generic21140_select, generic21140_fail,
-		 0x0000f800, PCI_DEVICE_ID_DEC_TULIP_FAST, "DE500", 0},
-	{generic21041_select, generic21041_fail,
-		 0x0000f800, PCI_DEVICE_ID_DEC_TULIP_PLUS, "DE450", 0},
-	{generic21040_select, generic21040_fail,
-		 0x0000f800, PCI_DEVICE_ID_DEC_TULIP, "DE43x", 0},
-	{generic21040_select, generic21040_fail,
-		 0x0040c700, PCI_DEVICE_ID_DEC_TULIP, "EN9400", 0},
-	{generic21040_select, generic21040_fail,
-		 0x00c09500, PCI_DEVICE_ID_DEC_TULIP, "ZNYX312", 1},
-	{generic21040_select, generic21040_fail,
-		 0x08002b00, PCI_DEVICE_ID_DEC_TULIP, "QSILVER's", 0},
-	{generic21040_select, generic21040_fail,
-		 0, PCI_DEVICE_ID_DEC_TULIP, "21040", 0},
-	{generic21140_select, generic21140_fail,
-		 0, PCI_DEVICE_ID_DEC_TULIP_FAST, "21140", 0},
-	{generic21041_select, generic21041_fail,
-		 0, PCI_DEVICE_ID_DEC_TULIP_PLUS, "21041", 0},
-	{NULL, NULL, 0, 0, "Unknown", 0}
-};
+/* This 21040 probe no longer uses a large fixed contiguous Rx buffer region,
+   but now receives directly into full-sized skbuffs that are allocated
+   at open() time.
+   This allows the probe routine to use the old driver initialization
+   interface. */
 
-
-/* Serial EEPROM section.
-   A "bit" grungy, but we work our way through bit-by-bit :->. */
-/*  EEPROM_Ctrl bits. */
-#define EE_SHIFT_CLK	0x02	/* EEPROM shift clock. */
-#define EE_CS			0x01	/* EEPROM chip select. */
-#define EE_DATA_WRITE	0x04	/* EEPROM chip data in. */
-#define EE_WRITE_0		0x01
-#define EE_WRITE_1		0x05
-#define EE_DATA_READ	0x08	/* EEPROM chip data out. */
-#define EE_ENB			(0x4800 | EE_CS)
+int tulip_probe(struct device *dev)
+{
+	int cards_found = 0;
+	static int pci_index = 0;	/* Static, for multiple probe calls. */
 
-/* The EEPROM commands include the alway-set leading bit. */
-#define EE_WRITE_CMD	(5 << 6)
-#define EE_READ_CMD		(6 << 6)
-#define EE_ERASE_CMD	(7 << 6)
+	/* Ideally we would detect all network cards in slot order.  That would
+	   be best done a central PCI probe dispatch, which wouldn't work
+	   well with the current structure.  So instead we detect just the
+	   Tulip cards in slot order. */
+
+	if (pcibios_present()) {
+		unsigned char pci_bus, pci_device_fn;
+
+		for (;pci_index < 0xff; pci_index++) {
+			unsigned char pci_irq_line, pci_latency;
+			unsigned short pci_command, vendor, device;
+			unsigned int pci_ioaddr, chip_idx = 0;
+
+			if (pcibios_find_class
+				(PCI_CLASS_NETWORK_ETHERNET << 8,
+				 reverse_probe ? 0xfe - pci_index : pci_index,
+				 &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL)
+				if (reverse_probe)
+					continue;
+				else
+					break;
+			pcibios_read_config_word(pci_bus, pci_device_fn,
+									 PCI_VENDOR_ID, &vendor);
+			pcibios_read_config_word(pci_bus, pci_device_fn,
+									 PCI_DEVICE_ID, &device);
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+									 PCI_INTERRUPT_LINE, &pci_irq_line);
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+									  PCI_BASE_ADDRESS_0, &pci_ioaddr);
+			/* Remove I/O space marker in bit 0. */
+			pci_ioaddr &= ~3;
+
+			if (vendor != PCI_VENDOR_ID_DEC)
+				continue;
+
+			for (chip_idx = 0; tulip_tbl[chip_idx].chip_name; chip_idx++)
+				if (device == tulip_tbl[chip_idx].device_id)
+					break;
+			if (tulip_tbl[chip_idx].chip_name == 0) {
+				printk(KERN_INFO "Unknown Digital PCI ethernet chip type"
+					   " %4.4x"" detected: not configured.\n", device);
+				continue;
+			}
+			if (tulip_debug > 2)
+				printk(KERN_DEBUG "Found DEC PCI Tulip at I/O %#x, IRQ %d.\n",
+					   pci_ioaddr, pci_irq_line);
+
+			if (check_region(pci_ioaddr, TULIP_TOTAL_SIZE))
+				continue;
 
 #ifdef MODULE
-static int if_port=TULIP_AUTO_PORT;
-#ifdef TULIP_FULL_DUPLEX
-static int full_duplex=1;
+			dev = tulip_probe1(dev, pci_ioaddr, pci_irq_line, chip_idx,
+						 cards_found);
 #else
-static int full_duplex=0;
-#endif
+			dev = tulip_probe1(dev, pci_ioaddr, pci_irq_line, chip_idx, -1);
 #endif
 
-#define	tio_write(val, port)	outl(val, ioaddr + port)
-#define	tio_read(port)			inl(ioaddr + port)
+			if (dev) {
+			  /* Get and check the bus-master and latency values. */
+			  pcibios_read_config_word(pci_bus, pci_device_fn,
+									   PCI_COMMAND, &pci_command);
+			  if ( ! (pci_command & PCI_COMMAND_MASTER)) {
+				printk(KERN_INFO "  PCI Master Bit has not been set! Setting...\n");
+				pci_command |= PCI_COMMAND_MASTER;
+				pcibios_write_config_word(pci_bus, pci_device_fn,
+										  PCI_COMMAND, pci_command);
+			  }
+			  pcibios_read_config_byte(pci_bus, pci_device_fn,
+									   PCI_LATENCY_TIMER, &pci_latency);
+			  if (pci_latency < 10) {
+				printk(KERN_INFO "  PCI latency timer (CFLT) is unreasonably"
+					   " low at %d.  Setting to 64 clocks.\n", pci_latency);
+				pcibios_write_config_byte(pci_bus, pci_device_fn,
+										  PCI_LATENCY_TIMER, 64);
+			  } else if (tulip_debug > 1)
+				printk(KERN_INFO "  PCI latency timer (CFLT) is %#x.\n",
+					   pci_latency);
+			  /* Bring the 21143 out power-down mode. */
+			  if (device == PCI_DEVICE_ID_DEC_TULIP_21142)
+				pcibios_write_config_dword(pci_bus, pci_device_fn,
+										  	0x40, 0x40000000);
+			  dev = 0;
+			  cards_found++;
+			}
+		}
+	}
 
-static void inline
-tio_sia_write(u32 ioaddr, u32 val13, u32 val14, u32 val15)
-{
-	tio_write(0,CSR13);
-	tio_write(val15,CSR15);
-	tio_write(val14,CSR14);
-	tio_write(val13,CSR13);
+	return cards_found ? 0 : -ENODEV;
 }
 
-/*
-   card_type returns 1 if the card is 'etherarray'
-*/
-
-static int
-card_type(struct tulip_private *tp, int device_id, int vendor_id)
+static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq,
+								   int chip_id, int board_idx)
 {
-	int n;
+	static int did_version = 0;			/* Already printed version info. */
+	struct tulip_private *tp;
+	/* See note below on the multiport cards. */
+	static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'};
+	static int last_irq = 0;
+	int i;
+	unsigned short sum;
 
-	for (n = 0; cardVendor[n].device_id; n ++)
-		if (cardVendor[n].device_id == device_id
-			&& (cardVendor[n].vendor_id == vendor_id
-				|| cardVendor[n].vendor_id == 0)) break;
-	tp->port_select = cardVendor[n].port_select;
-	tp->port_fail = cardVendor[n].port_fail;
-	tp->signature = cardVendor[n].signature;
-	return(cardVendor[n].array ? 1: 0);
-}
+	if (tulip_debug > 0  &&  did_version++ == 0)
+		printk(KERN_INFO "%s", version);
 
-static int
-read_eeprom(int ioaddr, struct eeprom *eepp)
-{
-    int i, n;
-    unsigned short val = 0;
-    int read_cmd = EE_READ_CMD;
-    u_char *p=(u_char *)eepp;
-
-	for (n = 0; n < sizeof(struct eeprom) / 2; n ++, read_cmd ++) {
-		tio_write(EE_ENB & ~EE_CS, CSR9);
-		tio_write(EE_ENB, CSR9);
-
-		/* Shift the read command bits out. */
-		for (i = 10; i >= 0; i--) {
-			short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
-			tio_write(EE_ENB | dataval, CSR9);
-			udelay(100);
-			tio_write(EE_ENB | dataval | EE_SHIFT_CLK, CSR9);
-			udelay(150);
-			tio_write(EE_ENB | dataval, CSR9);
-			udelay(250);
-		}
-		tio_write(EE_ENB, CSR9);
-
-		for (i = 16; i > 0; i--) {
-			tio_write(EE_ENB | EE_SHIFT_CLK, CSR9);
-			udelay(100);
-			val = (val << 1)
-				| ((tio_read(CSR9) & EE_DATA_READ) ? 1 : 0);
-			tio_write(EE_ENB, CSR9);
-			udelay(100);
-		}
-
-		/* Terminate the EEPROM access. */
-		tio_write(EE_ENB & ~EE_CS, CSR9);
-		*p ++ = val;
-		*p ++ = val >> 8;
-    }
-	/* broken eeprom ? */
-	p = (u_char *)eepp;
-	for (i = 0; i < 8; i ++)
-		if (p[i] != p[15 - i] || p[i] != p[16 + i]) return(0);
-	return(-1); /* broken */
-}
+	dev = init_etherdev(dev, 0);
 
-/* Is this required ? */
-static int
-generic21040_fail(struct device *dev)
-{
-	int ioaddr = dev->base_addr;
+	printk(KERN_INFO "%s: DEC %s at %#3x,",
+		   dev->name, tulip_tbl[chip_id].chip_name, ioaddr);
 
-	return(tio_read(CSR12) & TSIAS_CONERROR);
-}
+	/* Stop the chip's Tx and Rx processes. */
+	outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
+	/* Clear the missed-packet counter. */
+	(volatile)inl(ioaddr + CSR8);
 
-static int
-generic21041_fail(struct device *dev)
-{
-	int ioaddr = dev->base_addr;
-	u32 csr12 = tio_read(CSR12);
+	if (chip_id == DC21041) {
+		if (inl(ioaddr + CSR9) & 0x8000) {
+			printk(" 21040 compatible mode,");
+			chip_id = DC21040;
+		} else {
+			printk(" 21041 mode,");
+		}
+	}
 
-	return((!(csr12 & TSIAS_CONERROR)
-			|| !(csr12 & TSIAS_LNKERROR)) ? 0: 1);
-}
+	/* The station address ROM is read byte serially.  The register must
+	   be polled, waiting for the value to be read bit serially from the
+	   EEPROM.
+	   */
+	sum = 0;
+	if (chip_id == DC21040) {
+		outl(0, ioaddr + CSR9);		/* Reset the pointer with a dummy write. */
+		for (i = 0; i < 6; i++) {
+			int value, boguscnt = 100000;
+			do
+				value = inl(ioaddr + CSR9);
+			while (value < 0  && --boguscnt > 0);
+			dev->dev_addr[i] = value;
+			sum += value & 0xff;
+		}
+	} else {	/* Must be a new chip, with a serial EEPROM interface. */
+		/* We read the whole EEPROM, and sort it out later.  DEC has a
+		   specification _Digital Semiconductor 21X4 Serial ROM Format_
+		   but early vendor boards just put the address in the first six
+		   EEPROM locations. */
+		unsigned char ee_data[128];
+		int sa_offset = 0;
+
+		for (i = 0; i < sizeof(ee_data)/2; i++)
+			((u16 *)ee_data)[i] = read_eeprom(ioaddr, i);
+
+		/* Detect the simple EEPROM format by the duplicated station addr. */
+		for (i = 0; i < 8; i ++)
+			if (ee_data[i] != ee_data[16+i])
+				sa_offset = 20;
+		for (i = 0; i < 6; i ++) {
+			dev->dev_addr[i] = ee_data[i + sa_offset];
+			sum += ee_data[i + sa_offset];
+		}
+	}
+	/* On the Zynx 315 Etherarray and other multiport boards only the
+	   first Tulip has an EEPROM.
+	   The addresses of the subsequent ports are derived from the first.
+	   Many PCI BIOSes also incorrectly report the IRQ line, so we correct
+	   that here as well. */
+	if (sum == 0  || sum == 6*0xff) {
+		printk(" EEPROM not present,");
+		for (i = 0; i < 5; i++)
+			dev->dev_addr[i] = last_phys_addr[i];
+		dev->dev_addr[i] = last_phys_addr[i] + 1;
+		irq = last_irq;
+	}
+	for (i = 0; i < 6; i++)
+		printk(" %2.2x", last_phys_addr[i] = dev->dev_addr[i]);
+	printk(", IRQ %d.\n", irq);
+	last_irq = irq;
 
-static void
-generic21040_select(struct device *dev)
-{
-	int ioaddr = dev->base_addr;
-	const char *media;
+	/* We do a request_region() only to register /proc/ioports info. */
+	request_region(ioaddr, TULIP_TOTAL_SIZE, tulip_tbl[chip_id].chip_name);
 
-	dev->if_port &= 3;
-	switch (dev->if_port)
-	{
-	case TULIP_10TP_PORT:
-		media = "10baseT";
-		break;
-	case TULIP_AUI_PORT:
-		media = "AUI";
+	dev->base_addr = ioaddr;
+	dev->irq = irq;
+
+	/* Make certain the data structures are quadword aligned. */
+	tp = (void *)(((long)kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA) + 7) & ~7);
+	memset(tp, 0, sizeof(*tp));
+	dev->priv = tp;
+
+#ifdef MODULE
+	tp->next_module = root_tulip_dev;
+	root_tulip_dev = dev;
+#endif
+
+	tp->chip_id = chip_id;
+
+#ifdef TULIP_FULL_DUPLEX
+	tp->full_duplex = 1;
+#endif
+#ifdef TULIP_DEFAULT_MEDIA
+	tp->default_port = TULIP_DEFAULT_MEDIA;
+#endif
+#ifdef TULIP_NO_MEDIA_SWITCH
+	tp->medialock = 1;
+#endif
+
+	/* The lower four bits are the media type. */
+	if (board_idx >= 0) {
+		tp->full_duplex = (options[board_idx]&16) || full_duplex[board_idx]>0;
+		tp->default_port = options[board_idx] & 15;
+		if (tp->default_port)
+			tp->medialock = 1;
+	}
+
+	/* This is logically part of probe1(), but too complex to write inline. */
+	if (chip_id != DC21040)
+		parse_eeprom(dev);
+
+	if (tp->mtable  &&  tp->mtable->has_mii) {
+		int phy, phy_idx;
+		/* Find the connected MII xcvrs.
+		   Doing this in open() would allow detecting external xcvrs later,
+		   but takes much time. */
+		for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys);
+			 phy++) {
+			int mii_status = mdio_read(ioaddr, phy, 0);
+			if (mii_status != 0xffff  && mii_status != 0x0000) {
+				tp->phys[phy_idx++] = phy;
+				printk(KERN_INFO "%s:  MII transceiver found at MDIO address %d.\n",
+					   dev->name, phy);
+			}
+		}
+		if (phy_idx == 0) {
+			printk(KERN_INFO "%s: ***WARNING***: No MII transceiver found!\n",
+				   dev->name);
+			tp->phys[0] = 1;
+		}
+	}
+
+	/* The Tulip-specific entries in the device structure. */
+	dev->open = &tulip_open;
+	dev->hard_start_xmit = &tulip_start_xmit;
+	dev->stop = &tulip_close;
+	dev->get_stats = &tulip_get_stats;
+#ifdef HAVE_MULTICAST
+	dev->set_multicast_list = &set_multicast_list;
+#endif
+
+	/* Reset the xcvr interface and turn on heartbeat. */
+	switch (chip_id) {
+	case DC21041:
+		outl(0x00000000, ioaddr + CSR13);
+		outl(0xFFFFFFFF, ioaddr + CSR14);
+		outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */
+		outl(inl(ioaddr + CSR6) | 0x200, ioaddr + CSR6);
+		outl(0x0000EF05, ioaddr + CSR13);
 		break;
-	case TULIP_BNC_PORT:
-		media = "BNC";
+	case DC21140:  case DC21142:
+		if (tp->mtable)
+			outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12);
 		break;
-	default:
-		media = "unknown type";
+	case DC21040:
+		outl(0x00000000, ioaddr + CSR13);
+		outl(0x00000004, ioaddr + CSR13);
 		break;
 	}
-	printk("%s: enabling %s port.\n", dev->name, media);
-	/* Set the full duplex match frame. */
-	tio_write(FULL_DUPLEX_MAGIC, CSR11);
-	tio_write(TSIAC_RESET, CSR13);
-	/* Reset the serial interface */
-	tio_write((dev->if_port ? TSIAC_NO10TP: 0) | TSIAC_C21040, CSR13);
-}
 
-#if 0
-static void
-generic_timer(struct device *dev, u32 count)
-{
+	return dev;
+}
+
+/* Serial EEPROM section. */
+/* The main routine to parse the very complicated SROM structure.
+   Search www.digital.com for "21X4 SROM" to get details.
+   This code is very complex, and will require changes to support
+   additional cards, so I'll be verbose about what is going on.
+   */
+
+/* Known cards that have old-style EEPROMs. */
+static struct fixups {
+  char *name;
+  unsigned char addr0, addr1, addr2;
+  u16 newtable[32];				/* Max length below. */
+} eeprom_fixups[] = {
+  {"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c,
+						  0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }},
+  {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x021f,
+							   0x0000, 0x009E, /* 10baseT */
+							   0x0903, 0x006D, /* 100baseTx */ }},
+  {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x013f,
+								 0x0103, 0x006D, /* 100baseTx */ }},
+  {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0313,
+							   0x1001, 0x009E, /* 10base2, CSR12 0x10*/
+							   0x0000, 0x009E, /* 10baseT */
+							   0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ }},
+  {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x031F,
+							0x1B01, 0x0000, /* 10base2,   CSR12 0x1B */
+							0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */ 
+							0x0B00, 0x009E, /* 10baseT,   CSR12 0x0B */
+   }},
+  {0, 0, 0, 0, {}}};
+
+static const char * block_name[] = {"21140 non-MII", "21140 MII PHY",
+ "21142 non-MII PHY", "21142 MII PHY", };
+
+#define EEPROM_SIZE 128
+static void parse_eeprom(struct device *dev)
+{
+	/* The last media info list parsed, for multiport boards.  */
+	static struct mediatable *last_mediatable = NULL;
+	static unsigned char *last_ee_data = NULL;
+	static controller_index = 0;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	int ioaddr = dev->base_addr;
+	unsigned char *ee_data = tp->eeprom;
+	int i;
 
-	tio_write(count, CSR11);
-	while (tio_read(CSR11) & TGEPT_COUNT);
-}
-#endif
+	{
+	  static int done_did_that = 0;
+	  if (done_did_that++ == 0)
+		printk(KERN_INFO"  The following verbose information is emitted for\n"
+			   KERN_INFO"  bug reports on media selection.\n");
+	}
+	tp->mtable = 0;
+	for (i = 0; i < EEPROM_SIZE/2; i++)
+	  ((u16 *)ee_data)[i] = read_eeprom(ioaddr, i);
 
-static void
-generic21041_select(struct device *dev)
-{
-	int ioaddr = dev->base_addr;
-	u32 tsiac = TSIAC_C21041;
-	u32 tsiax = TSIAX_10TP;
-	u32 tsiag = TSIAG_10TP;
-
-	switch(dev->if_port) {
-	case TULIP_AUI_PORT:
-		tsiac |= TSIAC_NO10TP;
-		tsiax = TSIAX_NO10TP;
-		tsiag = TSIAG_AUI;
-		break;
-	case TULIP_BNC_PORT:
-		tsiac |= TSIAC_NO10TP;
-		tsiax = TSIAX_NO10TP;
-		tsiag = TSIAG_BNC;
-		break;
-	default:
-		dev->if_port = TULIP_10TP_PORT;
+	/* Detect an old-style (SA only) EEPROM layout:
+	   memcmp(eedata, eedata+16, 8). */
+	for (i = 0; i < 8; i ++)
+	  if (ee_data[i] != ee_data[16+i])
 		break;
+	if (i >= 8) {
+	  if (ee_data[0] == 0xff) {
+		if (last_mediatable) {
+		  controller_index++;
+		  printk(KERN_INFO "%s:  Controller %d of multiport board.\n",
+				 dev->name, controller_index);
+		  tp->mtable = last_mediatable;
+		  ee_data = last_ee_data;
+		  goto subsequent_board;
+		} else
+		printk(KERN_INFO "%s:  Missing EEPROM, this device may not work correctly!\n",
+			   dev->name);
+		return;
+	  }
+	  /* Do a fix-up based on the vendor half of the station address prefix. */
+	  for (i = 0; eeprom_fixups[i].name; i++) {
+		if (dev->dev_addr[0] == eeprom_fixups[i].addr0
+			&&  dev->dev_addr[1] == eeprom_fixups[i].addr1
+			&&  dev->dev_addr[2] == eeprom_fixups[i].addr2) {
+		  if (dev->dev_addr[2] == 0xE8  &&  ee_data[0x1a] == 0x55)
+			  i++;			/* An Accton EN1207, not an outlaw Maxtech. */
+		  memcpy(ee_data + 26, eeprom_fixups[i].newtable,
+				 sizeof(eeprom_fixups[i].newtable));
+		  printk(KERN_INFO "%s: Old format EEPROM on '%s' board.  Using"
+				 " substitute media control info.\n",
+				 dev->name, eeprom_fixups[i].name);
+		  break;
+		}
+	  }
+	  if (eeprom_fixups[i].name == NULL) { /* No fixup found. */
+		printk(KERN_INFO "%s: Old style EEPROM -- no media selection information.\n",
+			   dev->name);
+		return;
+	  }
+	}
+	if (tulip_debug > 1) {
+	  printk(KERN_DEBUG "\nread_eeprom:");
+	  for (i = 0; i < 64; i++) {
+		printk("%s%4.4x", (i & 7) == 0 ? "\n" KERN_DEBUG : " ",
+			   read_eeprom(ioaddr, i));
+	  }
+	  printk("\n");
+	}
+	
+	controller_index = 0;
+	if (ee_data[19] > 1) {		/* Multiport board. */
+		last_ee_data = ee_data;
+	}
+subsequent_board:
+
+	if (tp->chip_id == DC21041) {
+		unsigned char *p = (void *)ee_data + ee_data[27 + controller_index*3];
+		short media = *(u16 *)p;
+		int count = p[2];
+
+		printk(KERN_INFO "%s:21041 Media information at %d, default media "
+			   "%4.4x (%s).\n", dev->name, ee_data[27], media,
+			   media & 0x0800 ? "Autosense" : medianame[media & 15]);
+		for (i = 0; i < count; i++) {
+			unsigned char media_code = p[3 + i*7];
+			unsigned short *csrvals = (unsigned short *)&p[3 + i*7 + 1];
+			printk(KERN_INFO "%s:  21041 media %2.2x (%s),"
+				   " csr13 %4.4x csr14 %4.4x csr15 %4.4x.\n",
+				   dev->name, media_code & 15, medianame[media_code & 15],
+				   csrvals[0], csrvals[1], csrvals[2]);
+		}
+	} else {
+		unsigned char *p = (void *)ee_data + ee_data[27];
+		unsigned char csr12dir = 0;
+		int count;
+		struct mediatable *mtable;
+		short media = *((u16 *)p)++;
+
+		if (tp->chip_id == DC21140)
+			csr12dir = *p++;
+		count = *p++;
+		mtable = (struct mediatable *)
+			kmalloc(sizeof(struct mediatable) + count*sizeof(struct medialeaf),
+					GFP_KERNEL);
+		if (mtable == NULL)
+			return;				/* Horrible, impossible failure. */
+		last_mediatable = tp->mtable = mtable;
+		mtable->defaultmedia = media;
+		mtable->leafcount = count;
+		mtable->csr12dir = csr12dir;
+		mtable->has_mii = 0;
+
+		printk(KERN_INFO "%s:  EEPROM default media type %s.\n", dev->name,
+			   media & 0x0800 ? "Autosense" : medianame[media & 15]);
+		for (i = 0; i < count; i++) {
+			struct medialeaf *leaf = &mtable->mleaf[i];
+			
+			if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */
+				leaf->type = 0;
+				leaf->media = p[0] & 0x3f;
+				leaf->leafdata = p;
+				p += 4;
+			} else {
+				leaf->type = p[1];
+				if (p[1] & 1) {
+					mtable->has_mii = 1;
+					leaf->media = 11;
+				} else
+					leaf->media = p[2] & 0x0f;
+				leaf->leafdata = p + 2;
+				p += (p[0] & 0x3f) + 1;
+			}
+			if (tulip_debug > 1  &&  leaf->media == 11) {
+				unsigned char *bp = leaf->leafdata;
+				printk(KERN_INFO "%s:  MII interface PHY %d, setup/reset "
+					   "sequences %d/%d long, capabilities %2.2x %2.2x.\n",
+					   dev->name, bp[0], bp[1], bp[1 + bp[1]*2],
+					   bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]);
+				if (tulip_debug > 2) {
+					int mii_reg;
+					printk(KERN_DEBUG "%s:  MII xcvr control registers:",
+						   dev->name);
+					for (mii_reg = 0; mii_reg < 32; mii_reg++)
+						printk(" %4.4x", mdio_read(ioaddr,bp[0], mii_reg));
+					printk(".\n");
+				}
+			}
+
+			printk(KERN_INFO "%s:  Index #%d - Media %s (#%d) described "
+				   "by a %s (%d) block.\n",
+				   dev->name, i, medianame[leaf->media], leaf->media,
+				   block_name[leaf->type], leaf->type);
+		}
 	}
-	tio_sia_write(ioaddr, tsiac, tsiax, tsiag);
-	if (dev->start)
-		printk("%s: enabling %s port.\n", dev->name,
-			   (dev->if_port == TULIP_AUI_PORT) ? "AUI":
-			   (dev->if_port == TULIP_BNC_PORT) ? "BNC": "10TP");
 }
+/* Reading a serial EEPROM is a "bit" grungy, but we work our way through:->.*/
 
-static void
-auto21140_select(struct device *dev)
-{
-	int i, ioaddr = dev->base_addr;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+/*  EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK	0x02	/* EEPROM shift clock. */
+#define EE_CS			0x01	/* EEPROM chip select. */
+#define EE_DATA_WRITE	0x04	/* EEPROM chip data in. */
+#define EE_WRITE_0		0x01
+#define EE_WRITE_1		0x05
+#define EE_DATA_READ	0x08	/* EEPROM chip data out. */
+#define EE_ENB			(0x4800 | EE_CS)
 
-	/* kick port */
-	tio_write(TPOLL_TRIGGER, CSR1);
-	tio_write(TINTR_ENABLE, CSR7);
-	tio_write(TCMOD_AUTO|TCMOD_TRxSTART, CSR6);
-	dev->if_port = !(tio_read(CSR12) & TGEPR_FORCEALED);
-	printk("%s: probed %s port.\n",
-		   dev->name, dev->if_port ? "100TX" : "10TP");
-	tio_write((dev->if_port ? TGEPR_FORCE100: 0)
-			  | (tp->full_duplex ? 0:TGEPR_HALFDUPLEX), CSR12);	
-	tio_write(TINTR_DISABLE, CSR7);
-	i = tio_read(CSR8) & 0xffff;
-	tio_write(TCMOD_AUTO, CSR6);
-}
+/* 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
 
-static void
-cogent21140_select(struct device *dev)
-{
-	int ioaddr = dev->base_addr, csr6;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	dev->if_port &= 1;
-	csr6 = tio_read(CSR6) &
-		~(TCMOD_10TP|TCMOD_100TP|TCMOD_TRxSTART|TCMOD_SCRM);
-	/* Stop the transmit process. */
-	tio_write(csr6 | TCMOD_RxSTART, CSR6);
-	printk("%s: enabling %s port.\n",
-		   dev->name, dev->if_port ? "100baseTx" : "10baseT");
-	/* Turn on the output drivers */
-	tio_write(0x0000013F, CSR12);
-	tio_write((dev->if_port ? TGEPR_FORCE100: 0)
-			  | (tp->full_duplex ? 0:TGEPR_HALFDUPLEX), CSR12);
-	tio_write((dev->if_port ? TCMOD_100TP: TCMOD_10TP)
-			  | TCMOD_TRxSTART | TCMOD_TH128 | csr6, CSR6);
-}
+/* 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 void
-generic21140_select(struct device *dev)
+static int read_eeprom(int ioaddr, int location)
 {
-	int ioaddr = dev->base_addr, csr6;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-
-	dev->if_port &= 1;
-	csr6 = tio_read(CSR6) &
-		~(TCMOD_10TP|TCMOD_100TP|TCMOD_TRxSTART|TCMOD_SCRM);
+	int i;
+	unsigned short retval = 0;
+	int ee_addr = ioaddr + CSR9;
+	int read_cmd = location | EE_READ_CMD;
+	
+	outl(EE_ENB & ~EE_CS, ee_addr);
+	outl(EE_ENB, ee_addr);
+	
+	/* Shift the read command bits out. */
+	for (i = 10; i >= 0; i--) {
+		short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+		outl(EE_ENB | dataval, ee_addr);
+		eeprom_delay(100);
+		outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
+		eeprom_delay(150);
+		outl(EE_ENB | dataval, ee_addr);	/* Finish EEPROM a clock tick. */
+		eeprom_delay(250);
+	}
+	outl(EE_ENB, ee_addr);
+	
+	for (i = 16; i > 0; i--) {
+		outl(EE_ENB | EE_SHIFT_CLK, ee_addr);
+		eeprom_delay(100);
+		retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);
+		outl(EE_ENB, ee_addr);
+		eeprom_delay(100);
+	}
+
+	/* Terminate the EEPROM access. */
+	outl(EE_ENB & ~EE_CS, ee_addr);
+	return retval;
+}
+
+/* Read and write the MII registers using software-generated serial
+   MDIO protocol.  It is just different enough from the EEPROM protocol
+   to not share code.  The maxium data clock rate is 2.5 Mhz. */
+#define MDIO_SHIFT_CLK	0x10000
+#define MDIO_DATA_WRITE0 0x00000
+#define MDIO_DATA_WRITE1 0x20000
+#define MDIO_ENB		0x00000		/* Ignore the 0x02000 databook setting. */
+#define MDIO_ENB_IN		0x40000
+#define MDIO_DATA_READ	0x80000
+#ifdef _LINUX_DELAY_H
+#define mdio_delay()	udelay(1)
+#else
+#define mdio_delay()	__SLOW_DOWN_IO
+#endif
 
-	/* Stop the transmit process. */
-	tio_write(csr6 | TCMOD_RxSTART, CSR6);
-	if (dev->start)
-		printk("%s: enabling %s port.\n",
-			   dev->name, dev->if_port ? "100TX" : "10TP");
-	tio_write((dev->if_port ? TCMOD_100TP: TCMOD_10TP)
-			  | TCMOD_TRxSTART | TCMOD_TH128 | csr6, CSR6);
-	tio_write((dev->if_port ? TGEPR_FORCE100: 0)
-			  | (tp->full_duplex ? 0:TGEPR_HALFDUPLEX), CSR12);
+static int mdio_read(int ioaddr, int phy_id, int location)
+{
+	int i;
+	int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+	unsigned short retval = 0;
+	int mdio_addr = ioaddr + CSR9;
+
+	/* 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();
+		outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
+		mdio_delay();
+	}
+	/* Shift the read command bits out. */
+	for (i = 17; i >= 0; i--) {
+		int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
+
+		outl(dataval, mdio_addr);
+		mdio_delay();
+		outl(dataval | MDIO_SHIFT_CLK, mdio_addr);
+		mdio_delay();
+		outl(dataval, mdio_addr);
+		mdio_delay();
+	}
+	outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+	mdio_delay();
+	outl(MDIO_ENB_IN, mdio_addr);
+
+	for (i = 16; i > 0; i--) {
+		outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+		mdio_delay();
+		retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
+		outl(MDIO_ENB_IN, mdio_addr);
+		mdio_delay();
+	}
+	/* Clear out extra bits. */
+	for (i = 16; i > 0; i--) {
+		outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+		mdio_delay();
+		outl(MDIO_ENB_IN, mdio_addr);
+		mdio_delay();
+	}
+	return retval;
 }
 
+
 static int
 tulip_open(struct device *dev)
 {
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	int ioaddr = dev->base_addr;
-	int i;
+	int i = 0;
 
-	/* Reset the chip, holding bit 0 set at least 10 PCI cycles. */
-	tio_write(tio_read(CSR0)|TBMOD_RESET, CSR0);
-	udelay(1000);
-	/* Deassert reset.  Set 8 longword cache alignment, 8 longword burst.
-	   -> Set 32 longword cache alignment, unlimited longword burst ?
+	/* On some chip revs we must set the MII/SYM port before the reset!? */
+	if (tp->mtable  &&  tp->mtable->has_mii)
+		outl(0x00040000, ioaddr + CSR6);
+
+	/* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
+	outl(0x00000001, ioaddr + CSR0);
+#ifdef _LINUX_DELAY_H
+	udelay(2);
+#else
+	SLOW_DOWN_IO;
+#endif
+	/* Deassert reset.
+	   486: Set 8 longword cache alignment, 8 longword burst.
+	   586: Set 16 longword cache alignment, no burst limit.
+	   Cache alignment bits 15:14	     Burst length 13:8
+		0000	No alignment  0x00000000 unlimited		0800 8 longwords
+		4000	8  longwords		0100 1 longword		1000 16 longwords
+		8000	16 longwords		0200 2 longwords	2000 32 longwords
+		C000	32  longwords		0400 4 longwords
 	   Wait the specified 50 PCI cycles after a reset by initializing
 	   Tx and Rx queues and the address filter list. */
-	tio_write(tio_read(CSR0)|TBMOD_ALIGN32|TBMOD_BURST0, CSR0);
+#if defined(__alpha__)
+	/* ToDo: Alpha setting could be better. */
+	outl(0x00200000 | 0xE000, ioaddr + CSR0);
+#else
+#if defined(MODULE)
+	/* When a module we don't have 'x86' to check. */
+	outl(0x00200000 | 0x4800, ioaddr + CSR0);
+#else
+	outl(0x00200000 | (x86 <= 4 ? 0x4800 : 0x8000), ioaddr + CSR0);
+	if (x86 <= 4)
+	  printk(KERN_INFO "%s: This is a 386/486 PCI system, setting cache "
+			 "alignment to %x.\n", dev->name,
+			 0x00200000 | (x86 <= 4 ? 0x4800 : 0x8000));
+#endif
+#endif
 
-	if (request_irq(dev->irq, (void *)&tulip_interrupt, SA_SHIRQ,
-					tp->signature, dev))
+#ifdef SA_SHIRQ
+	if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ,
+					tulip_tbl[tp->chip_id].chip_name, dev)) {
 		return -EAGAIN;
+	}
+#else
+	if (irq2dev_map[dev->irq] != NULL
+		|| (irq2dev_map[dev->irq] = dev) == NULL
+		|| dev->irq == 0
+		|| request_irq(dev->irq, &tulip_interrupt, 0,
+					   tulip_tbl[tp->chip_id].chip_name)) {
+		return -EAGAIN;
+	}
+#endif
+
+	if (tulip_debug > 1)
+		printk(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. */
 	/* Fill the whole address filter table with our physical address. */
-	{ 
-		unsigned short *eaddrs = (unsigned short *)dev->dev_addr;
+	{
+		u16 *eaddrs = (u16 *)dev->dev_addr;
 		int *setup_frm = tp->setup_frame, i;
 
 		/* You must add the broadcast address when doing perfect filtering! */
@@ -721,57 +1088,481 @@
 		/* Put the setup frame on the Tx list. */
 		tp->tx_ring[0].length = 0x08000000 | 192;
 		tp->tx_ring[0].buffer1 = virt_to_bus(tp->setup_frame);
-		tp->tx_ring[0].buffer2 = 0;
-		tp->tx_ring[0].status = TRING_OWN;
-		barrier();
-		tp->cur_tx++, tp->dirty_tx++;
+		tp->tx_ring[0].status = 0x80000000;
+
+		tp->cur_tx++;
 	}
 
-	tio_write(virt_to_bus(tp->rx_ring), CSR3);
-	tio_write(virt_to_bus(tp->tx_ring), CSR4);
+	outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3);
+	outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4);
+
+	if (dev->if_port == 0)
+	  dev->if_port = tp->default_port;
+	if (tp->chip_id == DC21041  &&  dev->if_port > 4)
+		/* Invalid: Select initial TP, autosense, autonegotiate.  */
+		dev->if_port = 4;
+
+	/* Allow selecting a default media. */
+	if (tp->mtable == NULL)
+		goto media_picked;
+	if (dev->if_port)
+		for (i = 0; i < tp->mtable->leafcount; i++)
+		  if (tp->mtable->mleaf[i].media ==
+			  (dev->if_port == 12 ? 0 : dev->if_port)) {
+			printk(KERN_INFO "%s: Using user-specified media %s.\n",
+				   dev->name, medianame[dev->if_port]);
+			goto media_picked;
+		  }
+	if ((tp->mtable->defaultmedia & 0x0800) == 0)
+		for (i = 0; i < tp->mtable->leafcount; i++)
+		  if (tp->mtable->mleaf[i].media == (tp->mtable->defaultmedia & 15)) {
+			printk(KERN_INFO "%s: Using EEPROM-set media %s.\n",
+				   dev->name, medianame[tp->mtable->mleaf[i].media]);
+			goto media_picked;
+		  }
+	for (i = tp->mtable->leafcount - 1;
+		 (media_fd[tp->mtable->mleaf[i].media] & 2) && i > 0; i--)
+	  ;
+media_picked:
+
+	tp->cur_index = i;
+	tp->csr6 = 0;
+	select_media(dev, 1);
+
+	/* Start the chip's Tx to process setup frame. */
+	outl(tp->csr6, ioaddr + CSR6);
+	outl(tp->csr6 | 0x2000, ioaddr + CSR6);
 
 	dev->tbusy = 0;
 	dev->interrupt = 0;
 	dev->start = 1;
-	/*
-	 * process setup frame completely prior to fiddling with media.
-	 */
-	tio_write((tio_read(CSR6) & ~TCMOD_PROMISC) | TCMOD_TxSTART, CSR6);
-	tio_write(TPOLL_TRIGGER, CSR1);
-	sti();
-	for (i = 0; i < 1000; i++) {
-		if (tp->tx_ring[0].status >= 0) {
+
+
+	/* Enable interrupts by setting the interrupt mask. */
+	outl(0x0001fbff, ioaddr + CSR7);
+	outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+	outl(0, ioaddr + CSR2);		/* Rx poll demand */
+
+	if (tulip_debug > 2) {
+		printk(KERN_DEBUG "%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR13 %8.8x.\n",
+			   dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR5),
+			   inl(ioaddr + CSR13));
+	}
+	/* Set the timer to switch to check for link beat and perhaps switch
+	   to an alternate media type. */
+	init_timer(&tp->timer);
+	tp->timer.expires = RUN_AT((24*HZ)/10);			/* 2.4 sec. */
+	tp->timer.data = (unsigned long)dev;
+	tp->timer.function = &tulip_timer;				/* timer handler */
+	add_timer(&tp->timer);
+
+	return 0;
+}
+
+/* Set up the transceiver control registers for the selected media type. */
+static void select_media(struct device *dev, int startup)
+{
+	int ioaddr = dev->base_addr;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	struct mediatable *mtable = tp->mtable;
+	u32 new_csr6;
+	int check_mii =0, i;
+
+	if (mtable) {
+		struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index];
+		unsigned char *p = mleaf->leafdata;
+		switch (mleaf->type) {
+		case 0:					/* 21140 non-MII xcvr. */
+			if (tulip_debug > 1)
+				printk(KERN_DEBUG "%s: Using a 21140 non-MII transceiver with control"
+					   " setting %2.2x.\n",
+					   dev->name, p[1]);
+			dev->if_port = p[0];
+			if (startup)
+				outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
+			outl(p[1], ioaddr + CSR12);
+			new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18);
+			break;
+		case 1:
+			if (startup) {
+				outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
+				dev->if_port = 11;
+				if (tulip_debug > 2)
+					printk(KERN_DEBUG "%s:  Doing a reset sequence of length %d.\n",
+						   dev->name, p[2 + p[1]]);
+				for (i = 0; i < p[2 + p[1]]; i++)
+					outl(p[3 + p[1] + i], ioaddr + CSR12);
+				if (tulip_debug > 2)
+					printk(KERN_DEBUG "%s  Doing a transceiver setup sequence of length %d.\n",
+						   dev->name, p[1]);
+				for (i = 0; i < p[1]; i++)
+					outl(p[2 + i], ioaddr + CSR12);
+			}
+			check_mii = 1;
+			new_csr6 = 0x020C0000;
 			break;
+		case 2: case 4: {
+			u16 *setup = (u16*)&p[1];
+			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",
+					   dev->name, medianame[dev->if_port], setup[0], setup[1]);
+			if (p[0] & 0x40) {	/* SIA (CSR13-15) setup values are provided. */
+				outl(0, ioaddr + CSR13);
+				outl(setup[1], ioaddr + CSR14);
+				outl(setup[2], ioaddr + CSR15);
+				outl(setup[0], ioaddr + CSR13);
+				setup += 3;
+			} else {
+				outl(0, ioaddr + CSR13);
+				outl(t21142_csr14[dev->if_port], ioaddr + CSR14);
+				outl(t21142_csr15[dev->if_port], ioaddr + CSR15);
+				outl(t21142_csr13[dev->if_port], ioaddr + CSR13);
+			}
+			outl(setup[0]<<16, ioaddr + CSR15);	/* Direction */
+			outl(setup[1]<<16, ioaddr + CSR15);	/* Data */
+			new_csr6 = 0x02000000;
+			break;
+		}
+		case 3: {
+			int init_length = p[1];
+			u16 * init_sequence = (u16*)(p + 2);
+			int reset_length = p[2 + init_length*2];
+			u16 * reset_sequence = (u16*)&p[3 + init_length*2];
+
+			dev->if_port = 11;
+			if (startup) {
+				if (tulip_debug > 2)
+					printk(KERN_DEBUG "%s:  Doing a 21142 reset sequence of length %d.\n",
+						   dev->name, reset_length);
+				for (i = 0; i < reset_length; i++)
+					outl(reset_sequence[i] << 16, ioaddr + CSR15);
+			}
+			if (tulip_debug > 2)
+				printk(KERN_DEBUG "%s: Doing a 21142 xcvr setup sequence of length %d.\n",
+					   dev->name, init_length);
+			for (i = 0; i < init_length; i++)
+				outl(init_sequence[i] << 16, ioaddr + CSR15);
+			check_mii = 1;
+			new_csr6 = 0x020C0000;
+			break;
+		}
+		default:
+		  new_csr6 = 0x020C0000;
+		}
+		if (tulip_debug > 1)
+			printk(KERN_DEBUG "%s: Using media type %s, CSR12 is %2.2x.\n",
+				   dev->name, medianame[dev->if_port],
+				   inl(ioaddr + CSR12) & 0xff);
+	} else if (tp->chip_id == DC21140) {
+		/* Set media type to MII @ 100mbps: 0x020C0000 */
+		new_csr6 = 0x020C0000;
+		dev->if_port = 11;
+		if (tulip_debug > 1) {
+			printk(KERN_DEBUG "%s: Unknown media control, assuming MII, CSR12 %2.2x.\n",
+				   dev->name, inl(ioaddr + CSR12) & 0xff);
 		}
-		udelay(1000);
+	} else if (tp->chip_id == DC21041) {
+		if (tulip_debug > 1)
+			printk(KERN_DEBUG "%s: 21041 using media %s, CSR12 is %4.4x.\n",
+				   dev->name, medianame[dev->if_port & 15],
+				   inl(ioaddr + CSR12) & 0xffff);
+		outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
+		outl(t21041_csr14[dev->if_port], ioaddr + CSR14);
+		outl(t21041_csr15[dev->if_port], ioaddr + CSR15);
+		outl(t21041_csr13[dev->if_port], ioaddr + CSR13);
+		new_csr6 = 0x80020000;
+	} else {					/* 21040 */
+		/* Turn on the xcvr interface. */
+		int csr12 = inl(ioaddr + CSR12);
+		if (tulip_debug > 1)
+			printk(KERN_DEBUG "%s: 21040 media type is %s, CSR12 is %2.2x.\n",
+				   dev->name, dev->if_port ? "AUI" : "10baseT", csr12);
+		new_csr6 = (dev->if_port ? 0x01860000 : 0x00420000);
+		/* Set the full duplux match frame. */
+		outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11);
+		outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
+		outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13);
 	}
-	if (i == 500) {
-		printk("%s: initial setup frame didn't complete.\n", dev->name);
-		dev->start = 0;
-		dev->tbusy = 1;
-		tio_write(TINTR_DISABLE, CSR7);
-		tio_write(tio_read(CSR6) & ~(TCMOD_TRxSTART), CSR6);
-		tio_write(TSIAC_CONFIG, CSR13);
-		tio_write(0, CSR13);
-		free_irq(dev->irq, dev);
-		return (-EIO);
-	}
-	/*
-	 * Whack the chip to stop it and *then* do initial media setup.
-	 */
-	tio_write((tio_read(CSR6) & ~(TCMOD_PROMISC|TCMOD_TxSTART)), CSR6);
-	if (tp->port_select)
-		tp->port_select(dev);
-	/* Start the chip's Tx and Rx processes. */
-	tio_write(tio_read(CSR6) | TCMOD_TRxSTART
-			  | (tp->full_duplex ? TCMOD_FULLDUPLEX:0), CSR6);
-	/* Enable interrupts by setting the interrupt mask. */
-	tio_write(TINTR_ENABLE, CSR7);
 
-	MOD_INC_USE_COUNT;
-	return 0;
+	tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | (tp->full_duplex ? 0x0200 : 0);
+	return;
 }
 
+static void tulip_timer(unsigned long data)
+{
+	struct device *dev = (struct device *)data;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+	u32 csr12 = inl(ioaddr + CSR12);
+	int next_tick = 0;
+
+	if (tulip_debug > 3) {
+		printk(KERN_DEBUG "%s: Media selection tick, status %8.8x mode %8.8x "
+			   "SIA %8.8x %8.8x %8.8x %8.8x.\n",
+			   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR6),
+			   csr12, inl(ioaddr + CSR13),
+			   inl(ioaddr + CSR14), inl(ioaddr + CSR15));
+	}
+	switch (tp->chip_id) {
+	case DC21040:
+		if (csr12 & 0x0002) { /* Network error */
+			printk(KERN_INFO "%s: No 10baseT link beat found, switching to %s media.\n",
+				   dev->name, dev->if_port ? "10baseT" : "AUI");
+			dev->if_port ^= 1;
+			outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13);
+			dev->trans_start = jiffies;
+		}
+		break;
+	case DC21041:
+		if (tulip_debug > 2)
+			printk(KERN_DEBUG "%s: 21041 media tick  CSR12 %8.8x.\n",
+				   dev->name, csr12);
+		switch (dev->if_port) {
+		case 0: case 3: case 4:
+		  if (csr12 & 0x0004) { /*LnkFail */
+			/* 10baseT is dead.  Check for activity on alternate port. */
+			tp->mediasense = 1;
+			if (csr12 & 0x0200)
+				dev->if_port = 2;
+			else
+				dev->if_port = 1;
+			printk(KERN_INFO "%s: No 21041 10baseT link beat, Media switched to %s.\n",
+				   dev->name, medianame[dev->if_port]);
+			outl(0, ioaddr + CSR13); /* Reset */
+			outl(t21041_csr14[dev->if_port], ioaddr + CSR14);
+			outl(t21041_csr15[dev->if_port], ioaddr + CSR15);
+			outl(t21041_csr13[dev->if_port], ioaddr + CSR13);
+			next_tick = 10*HZ;			/* 2.4 sec. */
+		  } else
+			next_tick = 30*HZ;
+		  break;
+		case 1:					/* 10base2 */
+		case 2:					/* AUI */
+		  if (csr12 & 0x0100) {
+			next_tick = (30*HZ);			/* 30 sec. */
+			tp->mediasense = 0;
+		  } else if ((csr12 & 0x0004) == 0) {
+			printk(KERN_INFO "%s: 21041 media switched to 10baseT.\n", dev->name);
+			dev->if_port = 0;
+			select_media(dev, 0);
+			next_tick = (24*HZ)/10;				/* 2.4 sec. */
+		  } else if (tp->mediasense || (csr12 & 0x0002)) {
+			dev->if_port = 3 - dev->if_port; /* Swap ports. */
+			select_media(dev, 0);
+			next_tick = 20*HZ;
+		  } else {
+			next_tick = 20*HZ;
+		  }
+		  break;
+		}
+		break;
+	case DC21140:  case DC21142: {
+		struct medialeaf *mleaf;
+		unsigned char *p;
+		if (tp->mtable == NULL) {	/* No EEPROM info, use generic code. */
+			/* Assume this is like a SMC card, and check its link beat bit. */
+			if ((dev->if_port == 0 && (csr12 & 0x0080)) ||
+				(dev->if_port == 1 && (csr12 & 0x0040) == 0)) {
+				dev->if_port ^= 1;
+				/* Stop the transmit process. */
+				tp->csr6 = (dev->if_port ? 0x03860000 : 0x02420000);
+				outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+				printk(KERN_INFO "%s: link beat timed out, CSR12 is 0x%2.2x, switching to"
+					   " %s media.\n", dev->name,
+					   csr12 & 0xff,
+					   dev->if_port ? "100baseTx" : "10baseT");
+				outl(tp->csr6 | 0xA002, ioaddr + CSR6);
+				dev->trans_start = jiffies;
+				next_tick = (24*HZ)/10;
+			} else {
+				next_tick = 10*HZ;
+				if (tulip_debug > 2)
+					printk(KERN_DEBUG "%s: network media monitor 0x%2.2x, link"
+						   " beat detected as %s.\n", dev->name,
+						   csr12 & 0xff,
+						   dev->if_port ? "100baseTx" : "10baseT");
+			}
+			break;
+		}
+	  mleaf = &tp->mtable->mleaf[tp->cur_index];
+	  p = mleaf->leafdata;
+	  switch (mleaf->type) {
+	  case 0: case 4: {
+		/* Type 0 non-MII or #4 SYM transceiver.  Check the link beat bit. */
+		  s8 bitnum = p[mleaf->type == 4 ? 5 : 2];
+		  if (tulip_debug > 2)
+			  printk(KERN_DEBUG "%s: Transceiver monitor tick: CSR12=%#2.2x bit %d is"
+					 " %d, expecting %d.\n",
+					 dev->name, csr12, (bitnum >> 1) & 7,
+					 (csr12 & (1 << ((bitnum >> 1) & 7))) != 0,
+					 (bitnum >= 0));
+		  /* Check that the specified bit has the proper value. */
+		  if ((bitnum < 0) !=
+			  ((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) {
+			  if (tulip_debug > 1)
+				  printk(KERN_DEBUG "%s: Link beat detected for %s.\n", dev->name,
+						 medianame[mleaf->media]);
+			  break;
+		  }
+		  if (tp->medialock)
+			break;
+	  select_next_media:
+		  if (--tp->cur_index < 0) {
+			/* We start again, but should instead look for default. */
+			tp->cur_index = tp->mtable->leafcount - 1;
+		  }
+		  dev->if_port = tp->mtable->mleaf[tp->cur_index].media;
+		  if (media_fd[dev->if_port])
+			goto select_next_media; /* Skip FD entries. */
+		  if (tulip_debug > 1)
+			  printk(KERN_DEBUG "%s: No link beat on media %s,"
+					 " trying transceiver type %s.\n",
+					 dev->name, medianame[mleaf->media & 15],
+					 medianame[tp->mtable->mleaf[tp->cur_index].media]);
+		  select_media(dev, 0);
+		  /* Restart the transmit process. */
+		  outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+		  outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+		  next_tick = (24*HZ)/10;
+		  break;
+	  }
+	  case 1:
+		  {
+			  int mii_reg5 = mdio_read(ioaddr, tp->phys[0], 5);
+			  printk(KERN_INFO "%s: MII monitoring tick: CSR12 %2.2x, "
+					 "Link partner report %4.4x.\n",
+					 dev->name, csr12, mii_reg5);
+			  if (mii_reg5 != 0xffff
+				  && mdio_read(ioaddr, tp->phys[0], 1) & 0x0020) {
+				  int full_duplex = mii_reg5 & 0x0100 ? 1 : 0;
+				  if (full_duplex != tp->full_duplex) {
+					  tp->full_duplex = full_duplex;
+					  tp->csr6 ^= 0x0200;
+					  outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+					  outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+				  }
+				  if (tulip_debug > 0) /* Gurppp, should be >1 */
+					  printk(KERN_INFO "%s: Setting %s-duplex based on MII"
+							 " Xcvr #%d parter capability of %4.4x.\n",
+							 dev->name, full_duplex ? "full" : "half",
+							 tp->phys[0], mii_reg5);
+			  }
+		  }
+
+		  /* Hack for D-Link: Full duplex indication is on bit 3. */
+		  if (dev->dev_addr[0] == 0  &&  dev->dev_addr[1] == 0x80
+			  && dev->dev_addr[2] == 0xC8) {
+			  if (csr12 & 0x08) {
+				  tp->full_duplex = 0;
+				  tp->csr6 &= ~0x0200;
+				  outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+				  outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+			  } else {
+				  tp->full_duplex = 1;
+				  tp->csr6 |= 0x0200;
+				  outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+				  outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+			  }
+		  }
+		  break;
+	  case 2:					/* 21142 non-MII */
+	  case 3:					/* 21142 MII */
+		  next_tick = (24*HZ)/10;
+		  break;
+	  default:
+		  break;
+	  }
+	}
+	default:					/* Invalid chip type. */
+	  break;
+	}
+	if (next_tick) {
+		tp->timer.expires = RUN_AT(next_tick);
+		add_timer(&tp->timer);
+	}
+}
+
+static void tulip_tx_timeout(struct device *dev)
+{
+  struct tulip_private *tp = (struct tulip_private *)dev->priv;
+  int ioaddr = dev->base_addr;
+
+  if (tp->mtable && tp->mtable->has_mii) {
+	/* 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 (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 == DC21140 || tp->chip_id == DC21142) {
+	/* Stop the transmit process. */
+	outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+	dev->if_port ^= 1;
+	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));
+	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 if (tp->chip_id == DC21041) {
+	u32 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 (dev->if_port == 1 || dev->if_port == 2)
+		if (csr12 & 0x0004) {
+			dev->if_port = 2 - dev->if_port;
+		} else
+			dev->if_port = 0;
+	else
+		dev->if_port = 1;
+	select_media(dev, 0);
+	tp->stats.tx_errors++;
+	dev->trans_start = jiffies;
+	return;
+  } else
+	printk(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)
@@ -784,85 +1575,62 @@
 	tp->dirty_rx = tp->dirty_tx = 0;
 
 	for (i = 0; i < RX_RING_SIZE; i++) {
-		tp->rx_ring[i].status = TRING_OWN;
+		tp->rx_ring[i].status = 0x80000000;	/* Owned by Tulip chip */
 		tp->rx_ring[i].length = PKT_BUF_SZ;
-		tp->rx_ring[i].buffer1 = virt_to_bus(tp->rx_buffs[i]);
+		{
+			/* Note the receive buffer must be longword aligned.
+			   dev_alloc_skb() provides 16 byte alignment.  But do *not*
+			   use skb_reserve() to align the IP header! */
+			struct sk_buff *skb;
+			skb = DEV_ALLOC_SKB(PKT_BUF_SZ);
+			tp->rx_skbuff[i] = skb;
+			if (skb == NULL)
+				break;			/* Bad news!  */
+			skb->dev = dev;			/* Mark as being used by this device. */
+#if LINUX_VERSION_CODE > 0x10300
+			tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail);
+#else
+			tp->rx_ring[i].buffer1 = virt_to_bus(skb->data);
+#endif
+		}
 		tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]);
 	}
-	/* Mark the last entry as wrapping the ring. */ 
+	/* Mark the last entry as wrapping the ring. */
 	tp->rx_ring[i-1].length = PKT_BUF_SZ | 0x02000000;
 	tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]);
 
 	/* The Tx buffer descriptor is filled in as needed, but we
 	   do need to clear the ownership bit. */
 	for (i = 0; i < TX_RING_SIZE; i++) {
+		tp->tx_skbuff[i] = 0;
 		tp->tx_ring[i].status = 0x00000000;
+		tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]);
 	}
+	tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]);
 }
 
 static int
 tulip_start_xmit(struct sk_buff *skb, struct device *dev)
 {
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int ioaddr = dev->base_addr;
-	int entry, len;
-	unsigned long daddr;
+	int entry;
+	u32 flag;
 
-	/* Transmitter timeout, serious problems. */
-	if (dev->tbusy || (tp->port_fail && tp->port_fail(dev))) {
-		int tickssofar = jiffies - dev->trans_start;
-		int i;
-		if (tickssofar < 40) return(1);
-		if (tp->port_select) {
-			if (!tp->port_fix) dev->if_port ++;
-			tp->port_select(dev);
-			dev->trans_start = jiffies;
-			dev_kfree_skb(skb, FREE_WRITE);
-			return(0);
-		}
-		printk("%s: transmit timed out, status %8.8x,"
-			   "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
-			   dev->name, tio_read(CSR5), tio_read(CSR12),
-			   tio_read(CSR13), tio_read(CSR14), tio_read(CSR15));
-#ifndef	__alpha__
-		printk("  Rx ring %8.8x: ", (int)tp->rx_ring);
-#endif
-		for (i = 0; i < RX_RING_SIZE; i++)
-			printk(" %8.8x", (unsigned int)tp->rx_ring[i].status);
-#ifndef	__alpha__
-		printk("\n  Tx ring %8.8x: ", (int)tp->tx_ring);
-#endif
-		for (i = 0; i < TX_RING_SIZE; i++)
-			printk(" %8.8x", (unsigned int)tp->tx_ring[i].status);
-		printk("\n");
-
-		tp->stats.tx_errors++;
-		/* Perhaps we should reinitialize the hardware here. */
-		dev->if_port = 0;
-		tio_write(TSIAC_CONFIG, CSR13);
-		/* Start the chip's Tx and Rx processes . */
-		tio_write(TCMOD_10TP | TCMOD_TRxSTART, CSR6);
-		/* Trigger an immediate transmit demand. */
-		tio_write(TPOLL_TRIGGER, CSR1);
-
-		dev->tbusy=0;
-		dev->trans_start = jiffies;
-		dev_kfree_skb(skb, FREE_WRITE);
-		return(0);
-	}
-
-	if (skb == NULL || (skb != (struct sk_buff *) -1 && skb->len <= 0)) {
-		printk("%s: Obsolete driver layer request made: skbuff==NULL.\n",
+#ifndef final_version
+	if (skb == NULL || skb->len <= 0) {
+		printk(KERN_ERR "%s: Obsolete driver layer request made: skbuff==NULL.\n",
 			   dev->name);
 		dev_tint(dev);
-		return(0);
+		return 0;
 	}
+#endif
 
 	/* Block a timer-based transmit from overlapping.  This could better be
-	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
-	   If this ever occurs the queue layer is doing something evil! */
-	if (set_bit(0, (void*)&dev->tbusy) != 0) {
-		printk("%s: Transmitter access conflict.\n", dev->name);
+	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
+		if (jiffies - dev->trans_start < TX_TIMEOUT)
+			return 1;
+		tulip_tx_timeout(dev);
 		return 1;
 	}
 
@@ -872,43 +1640,49 @@
 	/* Calculate the next Tx descriptor entry. */
 	entry = tp->cur_tx % TX_RING_SIZE;
 
-	tp->tx_full = 1;
-	/*
-	 * If skb is == -1, then this is a funky setup_frame redo.
-	 */
-	if (skb == (struct sk_buff *) -1) {
-		daddr = virt_to_bus((char *)tp->setup_frame);
-		len = 192;
-		skb = NULL;
+	tp->tx_skbuff[entry] = skb;
+	tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data);
+
+	if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */
+	  flag = 0x60000000; /* No interrupt */
+	  dev->tbusy = 0;
+	} else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) {
+	  flag = 0xe0000000; /* Tx-done intr. */
+	  dev->tbusy = 0;
+	} else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) {
+	  flag = 0x60000000; /* No Tx-done intr. */
+	  dev->tbusy = 0;
 	} else {
-		daddr = virt_to_bus(skb->data);
-		len = skb->len;
+	  /* Leave room for set_rx_mode() to fill entries. */
+	  flag = 0xe0000000; /* Tx-done intr. */
+	  tp->tx_full = 1;
 	}
-	tp->tx_skbuff[entry] = skb;
-	tp->tx_ring[entry].length = len |
-		(entry == TX_RING_SIZE-1 ? 0xe2000000 : 0xe0000000);
-	tp->tx_ring[entry].buffer1 = daddr;
-	tp->tx_ring[entry].buffer2 = 0;
-	tp->tx_ring[entry].status = TRING_OWN;	/* Pass ownership to the chip. */
-	barrier();
+	if (entry == TX_RING_SIZE-1)
+		flag |= 0xe2000000;
 
+	tp->tx_ring[entry].length = skb->len | flag;
+	tp->tx_ring[entry].status = 0x80000000;	/* Pass ownership to the chip. */
 	tp->cur_tx++;
-
 	/* Trigger an immediate transmit demand. */
-	tio_write(TPOLL_TRIGGER, CSR1);
+	outl(0, dev->base_addr + CSR1);
 
 	dev->trans_start = jiffies;
 
-	return(0);
+	return 0;
 }
 
 /* The interrupt handler does all of the Rx thread work and cleans up
    after the Tx thread. */
-static void tulip_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs)
 {
-	struct device *dev = (struct device *)dev_id;
+#ifdef SA_SHIRQ		/* Use the now-standard shared IRQ implementation. */
+	struct device *dev = (struct device *)dev_instance;
+#else
+	struct device *dev = (struct device *)(irq2dev_map[irq]);
+#endif
+
 	struct tulip_private *lp;
-	int csr5, ioaddr, boguscnt=10;
+	int csr5, ioaddr, boguscnt = 12;
 
 	if (dev == NULL) {
 		printk ("tulip_interrupt(): irq %d for unknown device.\n", irq);
@@ -918,38 +1692,51 @@
 	ioaddr = dev->base_addr;
 	lp = (struct tulip_private *)dev->priv;
 	if (dev->interrupt)
-		printk("%s: Re-entering the interrupt handler.\n", dev->name);
+		printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
 
 	dev->interrupt = 1;
 
 	do {
-		csr5 = tio_read(CSR5);
+		csr5 = inl(ioaddr + CSR5);
 		/* Acknowledge all of the current interrupt sources ASAP. */
-		tio_write(csr5 & TSTAT_CLEARINTR, CSR5);
-		/* check interrupt ? */
-		if ((csr5 & (TSTAT_NORINTR|TSTAT_ABNINTR)) == 0) break;
+		outl(csr5 & 0x0001ffff, ioaddr + CSR5);
 
-		if (csr5 & TSTAT_RxINTR)			/* Rx interrupt */
+		if (tulip_debug > 4)
+			printk(KERN_DEBUG "%s: interrupt  csr5=%#8.8x new csr5=%#8.8x.\n",
+				   dev->name, csr5, inl(dev->base_addr + CSR5));
+
+		if ((csr5 & 0x00018000) == 0)
+			break;
+
+		if (csr5 & 0x0040)			/* Rx interrupt */
 			tulip_rx(dev);
 
-		if (csr5 & TSTAT_TxINTR) {		/* Tx-done interrupt */
-			int dirty_tx = lp->dirty_tx;
+		if (csr5 & 0x0007) {		/* Tx-done interrupt */
+			int dirty_tx;
 
-			while (dirty_tx < lp->cur_tx) {
+			for (dirty_tx = lp->dirty_tx; dirty_tx < lp->cur_tx; dirty_tx++) {
 				int entry = dirty_tx % TX_RING_SIZE;
 				int status = lp->tx_ring[entry].status;
 
 				if (status < 0)
 					break;			/* It still hasn't been Txed */
+				/* Check for Rx filter setup frames. */
+				if (lp->tx_skbuff[entry] == NULL)
+				  continue;
 
-				if (status & TRING_ERROR) {
+				if (status & 0x8000) {
 					/* There was an major error, log it. */
+#ifndef final_version
+					if (tulip_debug > 1)
+						printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
+							   dev->name, status);
+#endif
 					lp->stats.tx_errors++;
-					if (status & TRING_TxABORT) lp->stats.tx_aborted_errors++;
-					if (status & TRING_TxCARR) lp->stats.tx_carrier_errors++;
-					if (status & TRING_TxWINDOW) lp->stats.tx_window_errors++;
-					if (status & TRING_TxFIFO) lp->stats.tx_fifo_errors++;
-					if ((status & TRING_TxHEARTBEAT) && !lp->full_duplex)
+					if (status & 0x4104) lp->stats.tx_aborted_errors++;
+					if (status & 0x0C00) lp->stats.tx_carrier_errors++;
+					if (status & 0x0200) lp->stats.tx_window_errors++;
+					if (status & 0x0002) lp->stats.tx_fifo_errors++;
+					if ((status & 0x0080) && lp->full_duplex == 0)
 						lp->stats.tx_heartbeat_errors++;
 #ifdef ETHER_STATS
 					if (status & 0x0100) lp->stats.collisions16++;
@@ -963,15 +1750,14 @@
 				}
 
 				/* Free the original skb. */
-				if (lp->tx_skbuff[entry] != NULL)
-					dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE);
-				dirty_tx++;
+				dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE);
+				lp->tx_skbuff[entry] = 0;
 			}
 
 #ifndef final_version
-			if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) {
-				printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
-					   dirty_tx, lp->cur_tx, lp->tx_full);
+			if (lp->cur_tx - dirty_tx > TX_RING_SIZE) {
+				printk(KERN_ERR "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
+					   dev->name, dirty_tx, lp->cur_tx, lp->tx_full);
 				dirty_tx += TX_RING_SIZE;
 			}
 #endif
@@ -988,34 +1774,52 @@
 		}
 
 		/* Log errors. */
-		if (csr5 & TSTAT_ABNINTR) {	/* Abnormal error summary bit. */
-			if (csr5 & TSTAT_TxTOUT) lp->stats.tx_errors++; /* Tx babble. */
-			if (csr5 & TSTAT_RxMISSED) { 		/* Missed a Rx frame. */
+		if (csr5 & 0x8000) {	/* Abnormal error summary bit. */
+			if (csr5 & 0x0008) lp->stats.tx_errors++; /* Tx babble. */
+			if (csr5 & 0x0020) { /* Tx FIFO underflow. */
+			  lp->csr6 |= 0x00200000;  /* Reconfigure to store-n-forward. */
+			  /* Restart the transmit process. */
+			  outl(lp->csr6 | 0x0002, ioaddr + CSR6);
+			  outl(lp->csr6 | 0x2002, ioaddr + CSR6);
+			}
+			if (csr5 & 0x0100) {		/* Missed a Rx frame. */
 				lp->stats.rx_errors++;
-				lp->stats.rx_missed_errors += tio_read(CSR8) & 0xffff;
+				lp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
 			}
-			if (csr5 & TSTAT_TEXPIRED) {
-				printk("%s: Something Wicked happened! %8.8x.\n",
+			if (csr5 & 0x0800) {
+				printk(KERN_ERR "%s: Something Wicked happened! %8.8x.\n",
 					   dev->name, csr5);
 				/* Hmmmmm, it's not clear what to do here. */
 			}
+			/* Clear all error sources, included undocumented ones! */
+			outl(0x000f7ba, ioaddr + CSR5);
 		}
 		if (--boguscnt < 0) {
-			printk("%s: Too much work at interrupt, csr5=0x%8.8x.\n",
+			printk(KERN_WARNING "%s: Too much work at interrupt, csr5=0x%8.8x.\n",
 				   dev->name, csr5);
 			/* Clear all interrupt sources. */
-			tio_write(TSTAT_CLEARINTR, CSR5);
+			outl(0x0001ffff, ioaddr + CSR5);
 			break;
 		}
 	} while (1);
 
-	/* Special code for testing *only*. */
+	if (tulip_debug > 3)
+		printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n",
+			   dev->name, inl(ioaddr + CSR5));
+
+	/* Code that should never be run!  Perhaps remove after testing.. */
 	{
 		static int stopit = 10;
 		if (dev->start == 0  &&  --stopit < 0) {
-			printk("%s: Emergency stop, looping startup interrupt.\n",
-				   dev->name);
+			printk(KERN_ERR "%s: Emergency stop, looping startup interrupt.\n"
+				   KERN_ERR "%s: Disabling interrupt handler %d to avoid "
+				   "locking up the machine.\n",
+				   dev->name, dev->name, dev->irq);
+#ifdef SA_SHIRQ
 			free_irq(irq, dev);
+#else
+			free_irq(irq);
+#endif
 		}
 	}
 
@@ -1028,59 +1832,100 @@
 {
 	struct tulip_private *lp = (struct tulip_private *)dev->priv;
 	int entry = lp->cur_rx % RX_RING_SIZE;
-	int i;
 
+	if (tulip_debug > 4)
+		printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry,
+			   lp->rx_ring[entry].status);
 	/* If we own the next entry, it's a new packet. Send it up. */
 	while (lp->rx_ring[entry].status >= 0) {
 		int status = lp->rx_ring[entry].status;
 
-		if ((status & TRING_RxDESCMASK) != TRING_RxDESCMASK) {
-			printk("%s: Ethernet frame spanned multiple buffers,"
-				   "status %8.8x!\n", dev->name, status);
-		} else if (status & TRING_ERROR) {
+		if ((status & 0x0300) != 0x0300) {
+			if ((status & 0xffff) != 0x7fff) { /* Ingore earlier buffers. */
+			  printk(KERN_WARNING "%s: Oversized Ethernet frame spanned "
+					 "multiple buffers, status %8.8x!\n", dev->name, status);
+			  lp->stats.rx_length_errors++;
+			}
+		} else if (status & 0x8000) {
 			/* There was a fatal error. */
 			lp->stats.rx_errors++; /* end of a packet.*/
-			if (status & TRING_RxLENGTH) lp->stats.rx_length_errors++;
-			if (status & TRING_RxFRAME) lp->stats.rx_frame_errors++;
-			if (status & TRING_RxCRC) lp->stats.rx_crc_errors++;
-			if (status & TRING_RxFIFO) lp->stats.rx_fifo_errors++;
+			if (status & 0x0890) lp->stats.rx_length_errors++;
+			if (status & 0x0004) lp->stats.rx_frame_errors++;
+			if (status & 0x0002) lp->stats.rx_crc_errors++;
+			if (status & 0x0001) lp->stats.rx_fifo_errors++;
 		} else {
 			/* Malloc up new buffer, compatible with net-2e. */
 			/* Omit the four octet CRC from the length. */
 			short pkt_len = (lp->rx_ring[entry].status >> 16) - 4;
 			struct sk_buff *skb;
+			int rx_in_place = 0;
 
-			skb = dev_alloc_skb(pkt_len + 2);
+			/* Check if the packet is long enough to just accept without
+			   copying to a properly sized skbuff. */
+			if (pkt_len > rx_copybreak) {
+				struct sk_buff *newskb;
+				char *temp;
+
+				/* Get a fresh skbuff to replace the filled one. */
+				newskb = DEV_ALLOC_SKB(PKT_BUF_SZ);
+				if (newskb == NULL) {
+					skb = 0;		/* No memory, drop the packet. */
+					goto memory_squeeze;
+				}
+				/* Pass up the skb already on the Rx ring. */
+				skb = lp->rx_skbuff[entry];
+				temp = skb_put(skb, pkt_len);
+				if (bus_to_virt(lp->rx_ring[entry].buffer1) != temp)
+					printk(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(lp->rx_ring[entry].buffer1),
+						   skb->head, temp);
+				rx_in_place = 1;
+				lp->rx_skbuff[entry] = newskb;
+				newskb->dev = dev;
+				/* Longword alignment required: do not skb_reserve(2)! */
+				lp->rx_ring[entry].buffer1 = virt_to_bus(newskb->tail);
+			} else
+				skb = DEV_ALLOC_SKB(pkt_len + 2);
+			memory_squeeze:
 			if (skb == NULL) {
-				printk("%s: Memory squeeze, deferring packet.\n",
+				int i;
+				printk(KERN_WARNING "%s: Memory squeeze, deferring packet.\n",
 					   dev->name);
 				/* Check that at least two ring entries are free.
 				   If not, free one and mark stats->rx_dropped++. */
-				for (i=0; i < RX_RING_SIZE; i++)
+				for (i = 0; i < RX_RING_SIZE; i++)
 					if (lp->rx_ring[(entry+i) % RX_RING_SIZE].status < 0)
 						break;
 
 				if (i > RX_RING_SIZE -2) {
 					lp->stats.rx_dropped++;
-					lp->rx_ring[entry].status = TRING_OWN;
+					lp->rx_ring[entry].status = 0x80000000;
 					lp->cur_rx++;
 				}
 				break;
 			}
 			skb->dev = dev;
-			skb_reserve(skb, 2);
-			memcpy(skb_put(skb, pkt_len),
-				   bus_to_virt(lp->rx_ring[entry].buffer1), pkt_len);
-			/* Needed for 1.3.x */
-			skb->protocol = eth_type_trans(skb,dev);
+			if (! rx_in_place) {
+				skb_reserve(skb, 2);	/* 16 byte align the data fields */
+				memcpy(skb_put(skb, pkt_len),
+					   bus_to_virt(lp->rx_ring[entry].buffer1), pkt_len);
+			}
+#if LINUX_VERSION_CODE > 0x10300
+			skb->protocol = eth_type_trans(skb, dev);
+#else
+			skb->len = pkt_len;
+#endif
 			netif_rx(skb);
 			lp->stats.rx_packets++;
 		}
 
-		lp->rx_ring[entry].status = TRING_OWN;
+		lp->rx_ring[entry].status = 0x80000000;
 		entry = (++lp->cur_rx) % RX_RING_SIZE;
 	}
-	return(0);
+
+	return 0;
 }
 
 static int
@@ -1088,340 +1933,224 @@
 {
 	int ioaddr = dev->base_addr;
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	int i;
 
 	dev->start = 0;
 	dev->tbusy = 1;
 
+	if (tulip_debug > 1)
+		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n",
+			   dev->name, inl(ioaddr + CSR5));
+
 	/* Disable interrupts by clearing the interrupt mask. */
-	tio_write(TINTR_DISABLE, CSR7);
+	outl(0x00000000, ioaddr + CSR7);
 	/* Stop the chip's Tx and Rx processes. */
-	tio_write(tio_read(CSR6) & ~(TCMOD_TRxSTART), CSR6);
-	/* Leave the card in 10baseT state. */
-	tio_write(TSIAC_CONFIG, CSR13);
+	outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
+	/* 21040 -- Leave the card in 10baseT state. */
+	if (tp->chip_id == DC21040)
+		outl(0x00000004, ioaddr + CSR13);
 
-	tp->stats.rx_missed_errors += tio_read(CSR8) & 0xffff;
+	tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
 
-	tio_write(0, CSR13);
-/*	tio_write(0, CSR8);	wake up chip ? */
+	del_timer(&tp->timer);
 
+#ifdef SA_SHIRQ
 	free_irq(dev->irq, dev);
+#else
+	free_irq(dev->irq);
+	irq2dev_map[dev->irq] = 0;
+#endif
 
-	MOD_DEC_USE_COUNT;
-	return(0);
-}
+	/* Free all the skbuffs in the Rx queue. */
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		struct sk_buff *skb = tp->rx_skbuff[i];
+		tp->rx_skbuff[i] = 0;
+		tp->rx_ring[i].status = 0;		/* Not owned by Tulip chip. */
+		tp->rx_ring[i].length = 0;
+		tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */
+		if (skb) {
+#if LINUX_VERSION_CODE < 0x20100
+			skb->free = 1;
+#endif
+			dev_kfree_skb(skb, FREE_WRITE);
+		}
+	}
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		if (tp->tx_skbuff[i])
+			dev_kfree_skb(tp->tx_skbuff[i], FREE_WRITE);
+		tp->tx_skbuff[i] = 0;
+	}
 
-static int
-tulip_config(struct device *dev, struct ifmap *map)
-{
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 
-	if (map->port == 0xff) return(-EINVAL);
-	dev->if_port = map->port;
-	tp->port_fix = 1;
-	if (tp->port_select) tp->port_select(dev);
-	return(0);
+	MOD_DEC_USE_COUNT;
+
+	return 0;
 }
 
 static struct enet_statistics *
 tulip_get_stats(struct device *dev)
 {
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	/*	short ioaddr = dev->base_addr;*/
+	int ioaddr = dev->base_addr;
+
+	if (dev->start)
+		tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
 
-	return(&tp->stats);
+	return &tp->stats;
 }
 
-/*
- *	Set or clear the multicast filter for this adaptor.
- */
+/* Set or clear the multicast filter for this adaptor.
+   Note that we only use exclusion around actually queueing the
+   new frame, not around filling tp->setup_frame.  This is non-deterministic
+   when re-entered but still correct. */
+
+/* The little-endian AUTODIN32 ethernet CRC calculation.
+   N.B. Do not use for bulk data, use a table-based routine instead.
+   This is common code and should be moved to net/core/crc.c */
+static unsigned const ethernet_polynomial_le = 0xedb88320U;
+static inline unsigned ether_crc_le(int length, unsigned char *data)
+{
+	unsigned int crc = 0xffffffff;	/* Initial value. */
+	while(--length >= 0) {
+		unsigned char current_octet = *data++;
+		int bit;
+		for (bit = 8; --bit >= 0; current_octet >>= 1) {
+			if ((crc ^ current_octet) & 1) {
+				crc >>= 1;
+				crc ^= ethernet_polynomial_le;
+			} else
+				crc >>= 1;
+		}
+	}
+	return crc;
+}
 
-static void set_multicast_list(struct device *dev)
+
+static void
+#ifdef NEW_MULTICAST
+set_multicast_list(struct device *dev)
+#else
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
+#endif
 {
-	short ioaddr = dev->base_addr;
-	int csr6 = tio_read(CSR6) & ~(TCMOD_MODEMASK|TCMOD_FILTERMASK);
+	int ioaddr = dev->base_addr;
+	int csr6 = inl(ioaddr + CSR6) & ~0x00D5;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 
-	if (dev->flags&IFF_PROMISC) 
-	{			/* Set promiscuous. why ALLMULTI ? */
-		tio_write(csr6 | TCMOD_PROMISC | TCMOD_ALLMCAST, CSR6);
-		/* Log any net taps. */
-		printk("%s: Promiscuous mode enabled.\n", dev->name);
-	}
-	else if (dev->mc_count > 14 || (dev->flags&IFF_ALLMULTI)) 
-	{
+	tp->csr6 &= ~0x00D5;
+	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
+		outl(csr6 | 0x00C0, ioaddr + CSR6);
+		/* Unconditionally log net taps. */
+		printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name);
+		tp->csr6 |= 0xC0;
+	} else if ((dev->mc_count > 1000)  ||  (dev->flags & IFF_ALLMULTI)) {
 		/* Too many to filter perfectly -- accept all multicasts. */
-		tio_write(csr6 | TCMOD_ALLMCAST, CSR6);
-	}
-	else
-	{
-		struct tulip_private *tp = (struct tulip_private *)dev->priv;
-		struct dev_mc_list *dmi=dev->mc_list;
-		int *setup_frm = tp->setup_frame;
-		unsigned short *eaddrs;
+		outl(csr6 | 0x0080, ioaddr + CSR6);
+		tp->csr6 |= 0x80;
+	} else {
+		u32 *setup_frm = tp->setup_frame;
+		struct dev_mc_list *mclist;
+		u16 *eaddrs;
+		u32 tx_flags;
 		int i;
 
-		/* We have < 15 addresses that we can use the wonderful
-		   16 address perfect filtering of the Tulip.  Note that only
-		   the low shortword of setup_frame[] is valid. */
-		tio_write(csr6 | 0x0000, CSR6);
-		for (i = 0; i < dev->mc_count; i ++) {
-			eaddrs=(unsigned short *)dmi->dmi_addr;
-			dmi=dmi->next;
+		if (dev->mc_count > 14) { /* Must use a multicast hash table. */
+		  u16 hash_table[32];
+		  memset(hash_table, 0, sizeof(hash_table));
+		  for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+			   i++, mclist = mclist->next)
+			set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff,
+					hash_table);
+		  /* Copy the hash table to the setup frame.
+			 NOTE that only the LOW SHORTWORD of setup_frame[] is valid!
+			 This code may require tweaking for non-x86 architectures!  */
+		  for (i = 0; i < 32; i++)
+			*setup_frm++ = hash_table[i];
+		  setup_frm += 7;
+		  tx_flags = 0x08400000 | 192;
+		  /* Too clever: i > 15 for fall-though. */
+		} else {
+		  /* We have <= 15 addresses so we can use the wonderful
+			 16 address perfect filtering of the Tulip. */
+		  for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
+			   i++, mclist = mclist->next) {
+			/* Note that only the low shortword of setup_frame[] is valid!
+			   This code may require tweaking for non-x86 architectures! */
+			eaddrs = (u16 *)mclist->dmi_addr;
 			*setup_frm++ = *eaddrs++;
 			*setup_frm++ = *eaddrs++;
 			*setup_frm++ = *eaddrs++;
+		  }
+		  /* Fill the rest of the table with our physical address.
+			 Once again, only the low shortword or setup_frame[] is valid! */
+		  *setup_frm++ = 0xffff;
+		  *setup_frm++ = 0xffff;
+		  *setup_frm++ = 0xffff;
+		  tx_flags = 0x08000000 | 192;
 		}
-		/* Fill the rest of the table with our physical address. */
-		eaddrs = (unsigned short *)dev->dev_addr;
-		/* Always accept broadcast packets */
-		*setup_frm++ = 0xffff;
-		*setup_frm++ = 0xffff;
-		*setup_frm++ = 0xffff;
+		eaddrs = (u16 *)dev->dev_addr;
 		do {
-			*setup_frm++ = eaddrs[0];
-			*setup_frm++ = eaddrs[1];
-			*setup_frm++ = eaddrs[2];
+		  *setup_frm++ = eaddrs[0];
+		  *setup_frm++ = eaddrs[1];
+		  *setup_frm++ = eaddrs[2];
 		} while (++i < 15);
-
 		/* Now add this frame to the Tx list. */
-		tulip_start_xmit((struct sk_buff *) -1, dev);
-	}
-}
-
-int
-tulip_hwinit(struct device *dev, int ioaddr,
-			 int irq, int device_id)
-{
-	/* See note below on the Znyx 315 etherarray. */
-	static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'};
-	static int last_irq;
-	char detect_mesg[80], *mesgp=detect_mesg;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int i;
-	unsigned short sum, bitsum;
-
-	if (check_region(ioaddr, TULIP_TOTAL_SIZE) != 0) {
-		printk("tulip_hwinit: region already allocated at %#3x.\n",
-			   ioaddr);
-		return(-1);
-	}
-
-	mesgp += sprintf(mesgp, "(DEC 21%d4%d Tulip",
-					 device_id == PCI_DEVICE_ID_DEC_TULIP_FAST,
-					 device_id == PCI_DEVICE_ID_DEC_TULIP_PLUS);
-
-	/* Stop the chip's Tx and Rx processes. */
-	tio_write(tio_read(CSR6) & ~TCMOD_TRxSTART, CSR6);
-	/* Clear the missed-packet counter. */
-	i = tio_read(CSR8) & 0xffff;
-
-	if (device_id == PCI_DEVICE_ID_DEC_TULIP_PLUS
-	    && (tio_read(CSR9) & 0x8000)) {
-		mesgp += sprintf(mesgp, " treat as 21040");
-	    device_id = PCI_DEVICE_ID_DEC_TULIP;
-	}
-	
-	/* The station address ROM is read byte serially.  The register must
-	   be polled, waiting for the value to be read bit serially from the
-	   EEPROM.
-	   */
-	sum = 0;
-	if (device_id == PCI_DEVICE_ID_DEC_TULIP) {
-		tio_write(0, CSR9);
-	    /* Reset the pointer with a dummy write. */
-	    bitsum = 0xff;
-	    for (i = 0; i < 6; i++) {
-			int value, boguscnt = 100000;
-			do
-				value = tio_read(CSR9);
-			while (value < 0  && --boguscnt > 0);
-			dev->dev_addr[i] = value;
-			sum += value & 0xFF;
-			bitsum &= value;
-	    }
-	} else {
-	    /* Must be a 21140/21041, with a serial EEPROM interface. */
-	    struct eeprom eep;
-	    u_char *addr;
-
-	    if (read_eeprom(ioaddr, &eep) < 0) {
-			addr = eep.ng_addr;/* broken EEPROM structure */
-	    } else {
-			addr = eep.ok_addr;/* DEC EtherWorks */
-	    }
-	    for (i = 0; i < ETH_ALEN; i++) {
-			sum += addr[i];
-			dev->dev_addr[i] = addr[i];
-	    }
-	}
-	/* Make certain the data structures are quadword aligned. */
-
-	mesgp += sprintf(mesgp, ") at %#3x, ", ioaddr);
-
-	/* On the Zynx 315 etherarray boards only the first Tulip has an EEPROM.
-	   The addresses of the subsequent ports are derived from the first. */
-	if (sum == 0) {
-		for (i = 0; i < ETH_ALEN - 1; i++)
-			dev->dev_addr[i] = last_phys_addr[i];
-		dev->dev_addr[i] = last_phys_addr[i] + 1;
-		irq = last_irq;
-	}
-	for (i = 0; i < ETH_ALEN - 1; i++)
-		mesgp += sprintf(mesgp, "%2.2x:", dev->dev_addr[i]);
-	mesgp += sprintf(mesgp, "%2.2x, IRQ %d\n",
-					 last_phys_addr[i] = dev->dev_addr[i], irq);
-	last_irq = irq;
-
-	/* copy ethernet address */
-	if (card_type(tp, device_id,
-				  htonl((*(int*)dev->dev_addr) & 0xFFFFFF)))
-		for (i = 0; i < ETH_ALEN - 1; i++)
-			last_phys_addr[i] = dev->dev_addr[i];
-	/* We do a request_region() only to register /proc/ioports info. */
-	request_region(ioaddr, TULIP_TOTAL_SIZE, tp->signature);
-
-	dev->base_addr = ioaddr;
-	dev->irq = irq;
-
-	/* The Tulip-specific entries in the device structure. */
-	dev->open = &tulip_open;
-	dev->hard_start_xmit = &tulip_start_xmit;
-	dev->stop = &tulip_close;
-	dev->get_stats = &tulip_get_stats;
-	dev->set_config = &tulip_config;
-	dev->set_multicast_list = &set_multicast_list;
-
-#ifdef	MODULE
-	if (if_port == TULIP_AUTO_PORT)
-		if_port = TULIP_PORT;
-	else
-		tp->port_fix = 1;
-	dev->if_port = if_port;
-	tp->full_duplex = full_duplex;
-	tp->next_module = root_tulip_dev;
-	root_tulip_dev = dev;
-#else
-#ifdef TULIP_FULL_DUPLEX
-	tp->full_duplex = 1;
-#endif
-	dev->if_port = TULIP_PORT;
-#endif
-#ifdef	TULIP_FIX_PORT
-	tp->port_fix = 1;
-#endif
-
-	printk("%s: %s %s", dev->name, tp->signature, detect_mesg);
-
-	/* Reset the xcvr interface and turn on heartbeat. */
-	tio_write(TSIAC_RESET, CSR13);
-	tio_write(TSIAC_CONFIG, CSR13);
-
-	return(0);
-}
-
-int tulip_probe(struct device *dev)
-{
-	static struct device *tulip_head=NULL;
-	u_char pci_bus, pci_device_fn, pci_latency, pci_irq;
-	u_int pci_ioaddr;
-	u_short pci_command, vendor_id, device_id;
-	u_int pci_chips[] = {
-		PCI_DEVICE_ID_DEC_TULIP,
-		PCI_DEVICE_ID_DEC_TULIP_FAST,
-		PCI_DEVICE_ID_DEC_TULIP_PLUS,
-		PCI_DEVICE_ID_NONE
-	};
-	int num=0, cno;
-	static int pci_index = 0;
-
-    if (!pcibios_present()) return(-ENODEV);
-
-	for (; pci_index < 0xff; pci_index++) {
-		if (pcibios_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, pci_index,
-							   &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL)
-				break;
-
-		/* get vendor id */
-		pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID,
-								 &vendor_id);
-		/* get IRQ */
-		pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_INTERRUPT_LINE,
-								 &pci_irq);
-
-		/* get device id */
-		pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID,
-								 &device_id);
-
-		/* get IO address */
-		pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0,
-								  &pci_ioaddr);
-
-		/* Remove I/O space marker in bit 0. */
-		pci_ioaddr &= ~3;
-		if (vendor_id != PCI_VENDOR_ID_DEC)
-				continue;
-
-		for (cno = 0; pci_chips[cno] != PCI_DEVICE_ID_NONE; cno++)
-				if (device_id == pci_chips[cno])
-						break;
-		if (pci_chips[cno] == PCI_DEVICE_ID_NONE) {
-			printk("Unknown Digital PCI ethernet chip type %4.4x detected:"
-				   " not configured.\n", device_id);
-			continue;
-		}
-		dev = init_etherdev(NULL, ROUND_UP(sizeof(struct device) +
-										   sizeof (struct tulip_private) +
-										   ETHNAMSIZ, 8));
-		if (dev == NULL)
-				break;
+		if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) {
+			/* Same setup recently queued, we need not add it. */
+		} else {
+			unsigned long flags;
+			unsigned int entry;
+			
+			save_flags(flags); cli();
+			entry = tp->cur_tx++ % TX_RING_SIZE;
+
+			if (entry != 0) {
+			  /* Avoid a chip errata by prefixing a dummy entry. */
+			  tp->tx_skbuff[entry] = 0;
+			  tp->tx_ring[entry].length =
+				(entry == TX_RING_SIZE-1) ? 0x02000000 : 0;
+			  tp->tx_ring[entry].buffer1 = 0;
+			  tp->tx_ring[entry].status = 0x80000000;
+			  entry = tp->cur_tx++ % TX_RING_SIZE;
+			}
 
-		if (!tulip_head) {
-			printk(version);
-			tulip_head = dev;
-		}
-
-		/* Get and check the bus-master and latency values. */
-		pcibios_read_config_word(pci_bus, pci_device_fn, PCI_COMMAND,
-								 &pci_command);
-		if ((pci_command & PCI_COMMAND_MASTER) == 0) {
-			printk("  PCI Master Bit has not been set!"
-				   " Setting...\n");
-			pci_command |= PCI_COMMAND_MASTER;
-			pcibios_write_config_word(pci_bus, pci_device_fn, PCI_COMMAND,
-									  pci_command);
-		}
-
-		pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER,
-								 &pci_latency);
-		
-		if (pci_latency < 10) {
-			printk("  PCI latency timer (CFLT) is"
-				   " unreasonably low at %d."
-				   "  Setting to 100 clocks.\n", pci_latency);
-			pcibios_write_config_byte(pci_bus, pci_device_fn,
-									  PCI_LATENCY_TIMER, 100);
-		}
-
-		if (tulip_hwinit(dev, pci_ioaddr, pci_irq, pci_chips[cno]) < 0) {
-			continue;
-		}
-		num++;
-#ifdef	TULIP_MAX_CARDS
-		if (num >= TULIP_MAX_CARDS) return(0);
-#endif
+			tp->tx_skbuff[entry] = 0;
+			/* Put the setup frame on the Tx list. */
+			if (entry == TX_RING_SIZE-1)
+			  tx_flags |= 0x02000000;		/* Wrap ring. */
+			tp->tx_ring[entry].length = tx_flags;
+			tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame);
+			tp->tx_ring[entry].status = 0x80000000;
+			if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) {
+				dev->tbusy = 1;
+				tp->tx_full = 1;
+			}
+			restore_flags(flags);
+			/* Trigger an immediate transmit demand. */
+			outl(0, ioaddr + CSR1);
+		}
+		outl(csr6 | 0x0000, ioaddr + CSR6);
 	}
-	return(num > 0 ? 0: -ENODEV);
 }
-
+
 #ifdef MODULE
+#if LINUX_VERSION_CODE > 0x20118
+MODULE_PARM(debug, "i");
+MODULE_PARM(reverse_probe, "i");
+MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM(options, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i");
+#endif
 
-/* The parameters that may be passed in... */
-/* This driver does nothing with options yet.  It will later be used to
-   pass the full-duplex flag, etc. */
-int debug = -1;
+/* An additional parameter that may be passed in... */
+static int debug = -1;
 
 int
 init_module(void)
 {
+	if (debug >= 0)
+		tulip_debug = debug;
+
 	root_tulip_dev = NULL;
 	return tulip_probe(NULL);
 }
@@ -1433,21 +2162,21 @@
 
 	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
 	while (root_tulip_dev) {
-		next_dev =
-		   ((struct tulip_private *) root_tulip_dev->priv)->next_module;
+		next_dev = ((struct tulip_private *)root_tulip_dev->priv)->next_module;
 		unregister_netdev(root_tulip_dev);
 		release_region(root_tulip_dev->base_addr, TULIP_TOTAL_SIZE);
 		kfree(root_tulip_dev);
 		root_tulip_dev = next_dev;
 	}
 }
-#endif /* MODULE */
 
+#endif  /* MODULE */
 
 /*
  * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c tulip.c"
+ *  compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c"
  *  c-indent-level: 4
+ *  c-basic-offset: 4
  *  tab-width: 4
  * End:
  */
diff -u --recursive --new-file v2.0.30/linux/drivers/net/wavelan.c linux/drivers/net/wavelan.c
--- v2.0.30/linux/drivers/net/wavelan.c	Thu Mar  6 10:03:51 1997
+++ linux/drivers/net/wavelan.c	Sun Aug  3 15:17:45 1997
@@ -112,7 +112,7 @@
  * Read from card's Host Adaptor Status Register.
  */
 static inline u_short
-hasr_read(u_short	ioaddr)
+hasr_read(u_long	ioaddr)
 {
   return(inw(HASR(ioaddr)));
 } /* hasr_read */
@@ -122,7 +122,7 @@
  * Write to card's Host Adapter Command Register.
  */
 static inline void
-hacr_write(u_short	ioaddr,
+hacr_write(u_long	ioaddr,
 	   u_short	hacr)
 {
   outw(hacr, HACR(ioaddr));
@@ -134,7 +134,7 @@
  * those times when it is needed.
  */
 static inline void
-hacr_write_slow(u_short	ioaddr,
+hacr_write_slow(u_long	ioaddr,
 		u_short	hacr)
 {
   hacr_write(ioaddr, hacr);
@@ -147,7 +147,7 @@
  * Set the channel attention bit.
  */
 static inline void
-set_chan_attn(u_short	ioaddr,
+set_chan_attn(u_long	ioaddr,
 	      u_short	hacr)
 {
   hacr_write(ioaddr, hacr | HACR_CA);
@@ -158,7 +158,7 @@
  * Reset, and then set host adaptor into default mode.
  */
 static inline void
-wv_hacr_reset(u_short	ioaddr)
+wv_hacr_reset(u_long	ioaddr)
 {
   hacr_write_slow(ioaddr, HACR_RESET);
   hacr_write(ioaddr, HACR_DEFAULT);
@@ -169,7 +169,7 @@
  * Set the i/o transfer over the ISA bus to 8 bits mode
  */
 static inline void
-wv_16_off(u_short	ioaddr,
+wv_16_off(u_long	ioaddr,
 	  u_short	hacr)
 {
   hacr &= ~HACR_16BITS;
@@ -181,7 +181,7 @@
  * Set the i/o transfer over the ISA bus to 8 bits mode
  */
 static inline void
-wv_16_on(u_short	ioaddr,
+wv_16_on(u_long		ioaddr,
 	 u_short	hacr)
 {
   hacr |= HACR_16BITS;
@@ -196,7 +196,7 @@
 wv_ints_off(device *	dev)
 {
   net_local *	lp = (net_local *)dev->priv;
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
   u_long	x;
 
   x = wv_splhi();
@@ -215,7 +215,7 @@
 wv_ints_on(device *	dev)
 {
   net_local *	lp = (net_local *)dev->priv;
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
   u_long	x;
 
   x = wv_splhi();
@@ -239,7 +239,7 @@
  * Read bytes from the PSA.
  */
 static void
-psa_read(u_short	ioaddr,
+psa_read(u_long		ioaddr,
 	 u_short	hacr,
 	 int		o,	/* offset in PSA */
 	 u_char *	b,	/* buffer to fill */
@@ -262,7 +262,7 @@
  * Write the Paramter Storage Area to the WaveLAN card's memory
  */
 static void
-psa_write(u_short	ioaddr,
+psa_write(u_long	ioaddr,
 	  u_short	hacr,
 	  int		o,	/* Offset in psa */
 	  u_char *	b,	/* Buffer in memory */
@@ -329,7 +329,7 @@
  * Write 1 byte to the MMC.
  */
 static inline void
-mmc_out(u_short		ioaddr,
+mmc_out(u_long		ioaddr,
 	u_short		o,
 	u_char		d)
 {
@@ -347,7 +347,7 @@
  * We start by the end because it is the way it should be !
  */
 static inline void
-mmc_write(u_short	ioaddr,
+mmc_write(u_long	ioaddr,
 	  u_char	o,
 	  u_char *	b,
 	  int		n)
@@ -365,7 +365,7 @@
  * Optimised version for 1 byte, avoid using memory...
  */
 static inline u_char
-mmc_in(u_short	ioaddr,
+mmc_in(u_long	ioaddr,
        u_short	o)
 {
   while(inw(HASR(ioaddr)) & HASR_MMC_BUSY)
@@ -386,7 +386,7 @@
  * We start by the end because it is the way it should be !
  */
 static inline void
-mmc_read(u_short	ioaddr,
+mmc_read(u_long		ioaddr,
 	 u_char		o,
 	 u_char *	b,
 	 int		n)
@@ -400,11 +400,27 @@
 
 /*------------------------------------------------------------------*/
 /*
+ * Get the type of encryption available...
+ */
+static inline int
+mmc_encr(u_long		ioaddr)	/* i/o port of the card */
+{
+  int	temp;
+
+  temp = mmc_in(ioaddr, mmroff(0, mmr_des_avail));
+  if((temp != MMR_DES_AVAIL_DES) && (temp != MMR_DES_AVAIL_AES))
+    return 0;
+  else
+    return temp;
+}
+
+/*------------------------------------------------------------------*/
+/*
  * Wait for the frequency EEprom to complete a command...
  * I hope this one will be optimally inlined...
  */
 static inline void
-fee_wait(u_short	ioaddr,	/* i/o port of the card */
+fee_wait(u_long		ioaddr,	/* i/o port of the card */
 	 int		delay,	/* Base delay to wait for */
 	 int		number)	/* Number of time to wait */
 {
@@ -420,7 +436,7 @@
  * Read bytes from the Frequency EEprom (frequency select cards).
  */
 static void
-fee_read(u_short	ioaddr,	/* i/o port of the card */
+fee_read(u_long		ioaddr,	/* i/o port of the card */
 	 u_short	o,	/* destination offset */
 	 u_short *	b,	/* data buffer */
 	 int		n)	/* number of registers */
@@ -445,6 +461,8 @@
     }
 }
 
+#ifdef WIRELESS_EXT	/* If wireless extension exist in the kernel */
+
 /*------------------------------------------------------------------*/
 /*
  * Write bytes from the Frequency EEprom (frequency select cards).
@@ -453,7 +471,7 @@
  * Jean II
  */
 static void
-fee_write(u_short	ioaddr,	/* i/o port of the card */
+fee_write(u_long	ioaddr,	/* i/o port of the card */
 	  u_short	o,	/* destination offset */
 	  u_short *	b,	/* data buffer */
 	  int		n)	/* number of registers */
@@ -528,6 +546,7 @@
   fee_wait(ioaddr, 10, 100);
 #endif	/* EEPROM_IS_PROTECTED */
 }
+#endif	/* WIRELESS_EXT */
 
 /************************ I82586 SUBROUTINES *************************/
 /*
@@ -540,7 +559,7 @@
  * Why inlining this function make it fail ???
  */
 static /*inline*/ void
-obram_read(u_short	ioaddr,
+obram_read(u_long	ioaddr,
 	   u_short	o,
 	   u_char *	b,
 	   int		n)
@@ -554,7 +573,7 @@
  * Write bytes to the on-board RAM.
  */
 static inline void
-obram_write(u_short	ioaddr,
+obram_write(u_long	ioaddr,
 	    u_short	o,
 	    u_char *	b,
 	    int		n)
@@ -571,7 +590,7 @@
 wv_ack(device *		dev)
 {
   net_local *	lp = (net_local *)dev->priv;
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
   u_short	scb_cs;
   int		i;
 
@@ -614,7 +633,7 @@
 		   const char *	str)
 {
   net_local *	lp = (net_local *)dev->priv;
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
   u_short	scb_cmd;
   ach_t		cb;
   int		i;
@@ -660,7 +679,7 @@
  */
 static inline int
 wv_config_complete(device *	dev,
-		   u_short	ioaddr,
+		   u_long	ioaddr,
 		   net_local *	lp)
 {
   unsigned short	mcs_addr;
@@ -722,7 +741,7 @@
  */
 static int
 wv_complete(device *	dev,
-	    u_short	ioaddr,
+	    u_long	ioaddr,
 	    net_local *	lp)
 {
   int	nreaped = 0;
@@ -992,7 +1011,7 @@
 static void
 wv_mmc_show(device *	dev)
 {
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
   net_local *	lp = (net_local *)dev->priv;
   mmr_t		m;
 
@@ -1077,7 +1096,7 @@
  * Print the last block of the i82586 memory
  */
 static void
-wv_scb_show(unsigned short	ioaddr)
+wv_scb_show(u_long	ioaddr)
 {
   scb_t		scb;
 
@@ -1162,7 +1181,7 @@
 	       int		i,
 	       u_short		p)
 {
-  unsigned short	ioaddr;
+  u_long		ioaddr;
   ac_tx_t		actx;
 
   ioaddr = dev->base_addr;
@@ -1529,7 +1548,7 @@
  * (called in wavelan_ioctl)
  */
 static inline int
-wv_set_frequency(u_short	ioaddr,	/* i/o port of the card */
+wv_set_frequency(u_long		ioaddr,	/* i/o port of the card */
 		 iw_freq *	frequency)
 {
   const int	BAND_NUM = 10;	/* Number of bands */
@@ -1550,7 +1569,7 @@
     }
 
   /* Setting by channel (same as wfreqsel) */
-  /* Warning : each channel is 11MHz wide, so some of the channels
+  /* Warning : each channel is 22MHz wide, so some of the channels
    * will interfere... */
   if((frequency->e == 0) &&
      (frequency->m >= 0) && (frequency->m < BAND_NUM))
@@ -1729,7 +1748,7 @@
  * Give the list of available frequencies
  */
 static inline int
-wv_frequency_list(u_short	ioaddr,	/* i/o port of the card */
+wv_frequency_list(u_long	ioaddr,	/* i/o port of the card */
 		  iw_freq *	list,	/* List of frequency to fill */
 		  int		max)	/* Maximum number of frequencies */
 {
@@ -1826,7 +1845,7 @@
 	      struct ifreq *	rq,	/* Data passed */
 	      int		cmd)	/* Ioctl number */
 {
-  unsigned short	ioaddr = dev->base_addr;
+  u_long		ioaddr = dev->base_addr;
   net_local *		lp = (net_local *)dev->priv;	/* lp is not unused */
   struct iwreq *	wrq = (struct iwreq *) rq;
   psa_t			psa;
@@ -1866,9 +1885,7 @@
 	  m.w.mmw_netw_id_h = (wrq->u.nwid.nwid & 0xFF00) >> 8;
 	  mmc_write(ioaddr, (char *)&m.w.mmw_netw_id_l - (char *)&m,
 		    (unsigned char *)&m.w.mmw_netw_id_l, 2);
-	  m.w.mmw_loopt_sel = 0x00;
-	  mmc_write(ioaddr, (char *)&m.w.mmw_loopt_sel - (char *)&m,
-		    (unsigned char *)&m.w.mmw_loopt_sel, 1);
+	  mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), 0x00);
 	}
       else
 	{
@@ -1878,10 +1895,8 @@
 		    (char *)&psa.psa_nwid_select - (char *)&psa,
 		    (unsigned char *)&psa.psa_nwid_select, 1);
 
-	  /* Disable nwid in the mmc (no check) */
-	  m.w.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID;
-	  mmc_write(ioaddr, (char *)&m.w.mmw_loopt_sel - (char *)&m,
-		    (unsigned char *)&m.w.mmw_loopt_sel, 1);
+	  /* Disable nwid in the mmc (no filtering) */
+	  mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), MMW_LOOPT_SEL_DIS_NWID);
 	}
       break;
 
@@ -1950,6 +1965,82 @@
       wrq->u.sensitivity = psa.psa_thr_pre_set & 0x3F;
       break;
 
+     case SIOCSIWENCODE:
+       /* Set encryption key */
+       if(!mmc_encr(ioaddr))
+	 {
+	   ret = -EOPNOTSUPP;
+	   break;
+	 }
+
+       if(wrq->u.encoding.method)
+	 {	/* enable encryption */
+	   int		i;
+	   long long	key = wrq->u.encoding.code;
+
+	   for(i = 7; i >= 0; i--)
+	     {
+	       psa.psa_encryption_key[i] = key & 0xFF;
+	       key >>= 8;
+	     }
+           psa.psa_encryption_select = 1;
+	   psa_write(ioaddr, lp->hacr,
+		     (char *) &psa.psa_encryption_select - (char *) &psa,
+		     (unsigned char *) &psa.psa_encryption_select, 8+1);
+
+           mmc_out(ioaddr, mmwoff(0, mmw_encr_enable),
+		   MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE);
+           mmc_write(ioaddr, mmwoff(0, mmw_encr_key),
+		     (unsigned char *) &psa.psa_encryption_key, 8);
+	 }
+       else
+	 {	/* disable encryption */
+	   psa.psa_encryption_select = 0;
+	   psa_write(ioaddr, lp->hacr,
+		     (char *) &psa.psa_encryption_select - (char *) &psa,
+		     (unsigned char *) &psa.psa_encryption_select, 1);
+
+	   mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), 0);
+	 }
+       break;
+
+     case SIOCGIWENCODE:
+       /* Read the encryption key */
+       if(!mmc_encr(ioaddr))
+	 {
+	   ret = -EOPNOTSUPP;
+	   break;
+	 }
+
+       /* only super-user can see encryption key */
+       if(!suser())
+	 {
+	   ret = -EPERM;
+	   break;
+	 }
+       else
+	 {
+	   int		i;
+	   long long	key = 0;
+
+	   psa_read(ioaddr, lp->hacr,
+		    (char *) &psa.psa_encryption_select - (char *) &psa,
+		    (unsigned char *) &psa.psa_encryption_select, 1+8);
+	   for(i = 0; i < 8; i++)
+	     {
+	       key <<= 8;
+	       key += psa.psa_encryption_key[i];
+	     }
+	   wrq->u.encoding.code = key;
+
+	   /* encryption is enabled */
+	   if(psa.psa_encryption_select)
+	     wrq->u.encoding.method = mmc_encr(ioaddr);
+	   else
+	     wrq->u.encoding.method = 0;
+	 }
+       break;
+
     case SIOCGIWRANGE:
       /* Basic checking... */
       if(wrq->u.data.pointer != (caddr_t) 0)
@@ -2207,7 +2298,7 @@
 static iw_stats *
 wavelan_get_wireless_stats(device *	dev)
 {
-  unsigned short	ioaddr = dev->base_addr;
+  u_long		ioaddr = dev->base_addr;
   net_local *		lp = (net_local *) dev->priv;
   mmr_t			m;
   iw_stats *		wstats;
@@ -2281,7 +2372,7 @@
 	       int		sksize)
 {
   net_local *		lp = (net_local *) dev->priv;
-  u_short		ioaddr = dev->base_addr;
+  u_long		ioaddr = dev->base_addr;
   struct sk_buff *	skb;
 
 #ifdef DEBUG_RX_TRACE
@@ -2369,7 +2460,7 @@
 static inline void
 wv_receive(device *	dev)
 {
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
   net_local *	lp = (net_local *)dev->priv;
   int		nreaped = 0;
 
@@ -2555,7 +2646,7 @@
 		short	length)
 {
   net_local *		lp = (net_local *) dev->priv;
-  u_short		ioaddr = dev->base_addr;
+  u_long		ioaddr = dev->base_addr;
   unsigned short	txblock;
   unsigned short	txpred;
   unsigned short	tx_addr;
@@ -2760,7 +2851,7 @@
 static inline int
 wv_mmc_init(device *	dev)
 {
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
   net_local *	lp = (net_local *)dev->priv;
   psa_t		psa;
   mmw_t		m;
@@ -2789,6 +2880,9 @@
       /* As NWID is not set : no NWID checking */
       psa.psa_nwid_select = 0;
 
+      /* Disable encryption */
+      psa.psa_encryption_select = 0;
+
       /* Set to standard values
        * 0x04 for AT,
        * 0x01 for MCA,
@@ -2806,7 +2900,7 @@
 #ifdef USE_PSA_CONFIG
       /* Write the psa */
       psa_write(ioaddr, lp->hacr, (char *)psa.psa_nwid - (char *)&psa,
-		(unsigned char *)psa.psa_nwid, 3);
+		(unsigned char *)psa.psa_nwid, 4);
       psa_write(ioaddr, lp->hacr, (char *)&psa.psa_thr_pre_set - (char *)&psa,
 		(unsigned char *)&psa.psa_thr_pre_set, 1);
       psa_write(ioaddr, lp->hacr, (char *)&psa.psa_quality_thr - (char *)&psa,
@@ -2828,6 +2922,14 @@
   else
     m.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID;
 
+  memcpy(&m.mmw_encr_key, &psa.psa_encryption_key, 
+	 sizeof(m.mmw_encr_key));
+
+  if(psa.psa_encryption_select)
+    m.mmw_encr_enable = MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE;
+  else
+    m.mmw_encr_enable = 0;
+
   m.mmw_thr_pre_set = psa.psa_thr_pre_set & 0x3F;
   m.mmw_quality_thr = psa.psa_quality_thr & 0x0F;
 
@@ -2919,7 +3021,7 @@
 wv_ru_start(device *	dev)
 {
   net_local *	lp = (net_local *) dev->priv;
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
   u_short	scb_cs;
   fd_t		fd;
   rbd_t		rbd;
@@ -3013,7 +3115,7 @@
 wv_cu_start(device *	dev)
 {
   net_local *	lp = (net_local *) dev->priv;
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
   int		i;
   u_short	txblock;
   u_short	first_nop;
@@ -3114,7 +3216,7 @@
 wv_82586_start(device *	dev)
 {
   net_local *	lp = (net_local *) dev->priv;
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
   scp_t		scp;		/* system configuration pointer */
   iscp_t	iscp;		/* intermediate scp */
   scb_t		scb;		/* system control block */
@@ -3244,7 +3346,7 @@
 wv_82586_config(device *	dev)
 {
   net_local *		lp = (net_local *) dev->priv;
-  u_short		ioaddr = dev->base_addr;
+  u_long		ioaddr = dev->base_addr;
   unsigned short	txblock;
   unsigned short	txpred;
   unsigned short	tx_addr;
@@ -3440,7 +3542,7 @@
 wv_82586_stop(device *	dev)
 {
   net_local *	lp = (net_local *) dev->priv;
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
   u_short	scb_cmd;
 
 #ifdef DEBUG_CONFIG_TRACE
@@ -3475,7 +3577,7 @@
 wv_hw_reset(device *	dev)
 {
   net_local *	lp = (net_local *)dev->priv;
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
 
 #ifdef DEBUG_CONFIG_TRACE
   printk(KERN_DEBUG "%s: ->wv_hw_reset(dev=0x%x)\n", dev->name,
@@ -3520,7 +3622,7 @@
  * (called in wavelan_probe() and init_module())
  */
 static int
-wv_check_ioaddr(u_short		ioaddr,
+wv_check_ioaddr(u_long		ioaddr,
 		u_char *	mac)
 {
   int		i;		/* Loop counter */
@@ -3567,7 +3669,7 @@
 		  struct pt_regs *	regs)
 {
   device *	dev;
-  u_short	ioaddr;
+  u_long	ioaddr;
   net_local *	lp;
   u_short	hasr;
   u_short	status;
@@ -3714,7 +3816,7 @@
 {
   device *		dev;
   net_local *		lp;
-  unsigned short	ioaddr;
+  u_long		ioaddr;
   unsigned long		x;
   unsigned int		nreaped;
 
@@ -3909,7 +4011,7 @@
 static int
 wavelan_config(device *	dev)
 {
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
   u_char	irq_mask;
   int		irq;
   net_local *	lp;
diff -u --recursive --new-file v2.0.30/linux/drivers/net/wavelan.p.h linux/drivers/net/wavelan.p.h
--- v2.0.30/linux/drivers/net/wavelan.p.h	Thu Mar  6 10:03:51 1997
+++ linux/drivers/net/wavelan.p.h	Sun Aug  3 15:17:45 1997
@@ -35,6 +35,12 @@
 
 /* ------------------------ SPECIFIC NOTES ------------------------ */
 /*
+ * wavelan.o is darn too big
+ * -------------------------
+ *	That's true ! There is a very simple way to reduce the driver
+ *	object by 33% (yes !). Comment out the following line :
+ *		#include <linux/wireless.h>
+ *
  * MAC address and hardware detection :
  * ----------------------------------
  *	The detection code of the wavelan chech that the first 3
@@ -80,14 +86,12 @@
  * caracteristics of the hardware in a standard way and support for
  * applications for taking advantage of it (like Mobile IP).
  *
- * By default, these wireless extensions are disabled, because they
- * need a patch to the Linux Kernel. This simple patch may be found
- * with the driver + some utilities to access those wireless
- * extensions (iwconfig...). Hopefully, those wireless extensions will
- * make their way in the kernel someday.
+ * You will need to enable the CONFIG_NET_RADIO define in the kernel
+ * configuration to enable the wireless extensions (this is the one
+ * giving access to the radio network device choice).
  *
- * You also will need to enable the CONFIG_NET_RADIO in the kernel
- * configuration to enable the wireless extensions.
+ * It might also be a good idea as well to fetch the wireless tools to
+ * configure the device and play a bit.
  */
 
 /* ---------------------------- FILES ---------------------------- */
@@ -161,6 +165,7 @@
  *	Ajay Bakre (bakre@paul.rutgers.edu),
  *	Donald Becker (becker@cesdis.gsfc.nasa.gov),
  *	Loeke Brederveld (Loeke.Brederveld@Utrecht.NCR.com),
+ *	Brent Elphick <belphick@uwaterloo.ca>,
  *	Anders Klemets (klemets@it.kth.se),
  *	Vladimir V. Kolpakov (w@stier.koenig.ru),
  *	Marc Meertens (Marc.Meertens@Utrecht.NCR.com),
@@ -185,6 +190,7 @@
  *	John Rosenberg (johnr@cs.usyd.edu.au),
  *	George Rossi (george@phm.gov.au),
  *	Arthur Scott (arthur@cs.usyd.edu.au),
+ *	Stanislav Sinyagin <stas@isf.ru>
  *	Peter Storey,
  * for their assistance and advice.
  *
@@ -260,9 +266,13 @@
  *	- Remove extern kerword for wavelan_probe()
  *	- Level threshold is now a standard wireless extension (version 4 !)
  *
+ * Changes made for release in 2.1.36 :
+ * ----------------------------------
+ *	- Encryption setting from Brent Elphick (thanks a lot !)
+ *	- 'ioaddr' to 'u_long' for the Alpha (thanks to Stanislav Sinyagin)
+ *
  * Wishes & dreams :
  * ---------------
- *	- Encryption stuff
  *	- Roaming
  */
 
@@ -345,7 +355,7 @@
 /************************ CONSTANTS & MACROS ************************/
 
 #ifdef DEBUG_VERSION_SHOW
-static const char	*version	= "wavelan.c : v15 (wireless extensions) 12/2/97\n";
+static const char	*version	= "wavelan.c : v16 (wireless extensions) 17/4/97\n";
 #endif
 
 /* Watchdog temporisation */
@@ -442,69 +452,65 @@
 	wv_psa_to_irq(u_char);
 /* ------------------- HOST ADAPTER SUBROUTINES ------------------- */
 static inline u_short		/* data */
-	hasr_read(u_short);	/* Read the host interface : base address */
+	hasr_read(u_long);	/* Read the host interface : base address */
 static inline void
-	hacr_write(u_short,	/* Write to host interface : base address */
+	hacr_write(u_long,	/* Write to host interface : base address */
 		   u_short),	/* data */
-	hacr_write_slow(u_short,
+	hacr_write_slow(u_long,
 		   u_short),
-	set_chan_attn(u_short,	/* ioaddr */
+	set_chan_attn(u_long,	/* ioaddr */
 		      u_short),	/* hacr */
-	wv_hacr_reset(u_short),	/* ioaddr */
-	wv_16_off(u_short,	/* ioaddr */
+	wv_hacr_reset(u_long),	/* ioaddr */
+	wv_16_off(u_long,	/* ioaddr */
 		  u_short),	/* hacr */
-	wv_16_on(u_short,	/* ioaddr */
+	wv_16_on(u_long,	/* ioaddr */
 		 u_short),	/* hacr */
 	wv_ints_off(device *),
 	wv_ints_on(device *);
 /* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */
 static void
-	psa_read(u_short,	/* Read the Parameter Storage Area */
+	psa_read(u_long,	/* Read the Parameter Storage Area */
 		 u_short,	/* hacr */
 		 int,		/* offset in PSA */
 		 u_char *,	/* buffer to fill */
 		 int),		/* size to read */
-	psa_write(u_short, 	/* Write to the PSA */
+	psa_write(u_long, 	/* Write to the PSA */
 		  u_short,	/* hacr */
 		  int,		/* Offset in psa */
 		  u_char *,	/* Buffer in memory */
 		  int);		/* Length of buffer */
 static inline void
-	mmc_out(u_short,	/* Write 1 byte to the Modem Manag Control */
+	mmc_out(u_long,		/* Write 1 byte to the Modem Manag Control */
 		u_short,
 		u_char),
-	mmc_write(u_short,	/* Write n bytes to the MMC */
+	mmc_write(u_long,	/* Write n bytes to the MMC */
 		  u_char,
 		  u_char *,
 		  int);
 static inline u_char		/* Read 1 byte from the MMC */
-	mmc_in(u_short,
+	mmc_in(u_long,
 	       u_short);
 static inline void
-	mmc_read(u_short,	/* Read n bytes from the MMC */
+	mmc_read(u_long,	/* Read n bytes from the MMC */
 		 u_char,
 		 u_char *,
 		 int),
-	fee_wait(u_short,	/* Wait for frequency EEprom : base address */
+	fee_wait(u_long,	/* Wait for frequency EEprom : base address */
 		 int,		/* Base delay to wait for */
 		 int);		/* Number of time to wait */
 static void
-	fee_read(u_short,	/* Read the frequency EEprom : base address */
+	fee_read(u_long,	/* Read the frequency EEprom : base address */
 		 u_short,	/* destination offset */
 		 u_short *,	/* data buffer */
-		 int),		/* number of registers */
-	fee_write(u_short,	/* Write to frequency EEprom : base address */
-		  u_short,	/* destination offset */
-		  u_short *,	/* data buffer */
-		  int);		/* number of registers */
+		 int);		/* number of registers */
 /* ---------------------- I82586 SUBROUTINES ----------------------- */
 static /*inline*/ void
-	obram_read(u_short,	/* ioaddr */
+	obram_read(u_long,	/* ioaddr */
 		   u_short,	/* o */
 		   u_char *,	/* b */
 		   int);	/* n */
 static inline void
-	obram_write(u_short,	/* ioaddr */
+	obram_write(u_long,	/* ioaddr */
 		    u_short,	/* o */
 		    u_char *,	/* b */
 		    int);	/* n */
@@ -514,11 +520,11 @@
 	wv_synchronous_cmd(device *,
 			   const char *),
 	wv_config_complete(device *,
-			   u_short,
+			   u_long,
 			   net_local *);
 static int
 	wv_complete(device *,
-		    u_short,
+		    u_long,
 		    net_local *);
 static inline void
 	wv_82586_reconfig(device *);
@@ -560,7 +566,7 @@
 	wv_82586_stop(device *);
 static int
 	wv_hw_reset(device *),		/* Reset the wavelan hardware */
-	wv_check_ioaddr(u_short,	/* ioaddr */
+	wv_check_ioaddr(u_long,		/* ioaddr */
 			u_char *);	/* mac address (read) */
 /* ---------------------- INTERRUPT HANDLING ---------------------- */
 static void
diff -u --recursive --new-file v2.0.30/linux/drivers/pci/pci.c linux/drivers/pci/pci.c
--- v2.0.30/linux/drivers/pci/pci.c	Mon Mar 31 13:23:23 1997
+++ linux/drivers/pci/pci.c	Mon Aug 18 15:19:41 1997
@@ -42,25 +42,37 @@
  */
 struct pci_dev_info dev_info[] = {
 	DEVICE( COMPAQ,		COMPAQ_1280,	"QVision 1280/p"),
+	DEVICE( COMPAQ,		COMPAQ_NETELL100,"Netelligent 10/100"),
+	DEVICE( COMPAQ,		COMPAQ_NETELL10,"Netelligent 10"),
+	DEVICE( COMPAQ,		COMPAQ_NETFLEX3,"NetFlex 3"),
 	DEVICE( COMPAQ,		COMPAQ_THUNDER,	"ThunderLAN"),
 	DEVICE( NCR,		NCR_53C810,	"53c810"),
 	DEVICE( NCR,		NCR_53C820,	"53c820"),
 	DEVICE( NCR,		NCR_53C825,	"53c825"),
 	DEVICE( NCR,		NCR_53C815,	"53c815"),
+	DEVICE( NCR,		NCR_53C860,	"53c860"),
+	DEVICE( NCR,		NCR_53C896,	"53c896"),
+	DEVICE( NCR,		NCR_53C895,	"53c895"),
+	DEVICE( NCR,		NCR_53C885,	"53c885"),
+	DEVICE( NCR,		NCR_53C875,	"53c875"),
 	DEVICE( ATI,		ATI_68800,      "68800AX"),
 	DEVICE( ATI,		ATI_215CT222,   "215CT222"),
 	DEVICE( ATI,		ATI_210888CX,   "210888CX"),
+	DEVICE( ATI,		ATI_215GT,	"Mach64 GT (Rage II)"),
 	DEVICE( ATI,		ATI_210888GX,   "210888GX"),
+	DEVICE( ATI,		ATI_264VT,	"Mach64 VT"),
 	DEVICE( VLSI,		VLSI_82C592,	"82C592-FC1"),
 	DEVICE( VLSI,		VLSI_82C593,	"82C593-FC1"),
 	DEVICE( VLSI,		VLSI_82C594,	"82C594-AFC2"),
 	DEVICE( VLSI,		VLSI_82C597,	"82C597-AFC2"),
+	DEVICE( VLSI,		VLSI_VAS96011,	"VAS96011 PowerPC"),
 	DEVICE( ADL,		ADL_2301,	"2301"),
 	DEVICE( NS,		NS_87410,	"87410"),
 	DEVICE( TSENG,		TSENG_W32P_2,	"ET4000W32P"),
 	DEVICE( TSENG,		TSENG_W32P_b,	"ET4000W32P rev B"),
 	DEVICE( TSENG,		TSENG_W32P_c,	"ET4000W32P rev C"),
 	DEVICE( TSENG,		TSENG_W32P_d,	"ET4000W32P rev D"),
+	DEVICE( TSENG,		TSENG_ET6000,	"ET6000"),
 	DEVICE( WEITEK,		WEITEK_P9000,	"P9000"),
 	DEVICE( WEITEK,		WEITEK_P9100,	"P9100"),
 	BRIDGE( DEC,		DEC_BRD,	"DC21050", 		0x00),
@@ -69,16 +81,26 @@
 	DEVICE( DEC,		DEC_TULIP_FAST,	"DC21140"),
 	DEVICE( DEC,		DEC_FDDI,	"DEFPA"),
 	DEVICE( DEC,		DEC_TULIP_PLUS,	"DC21041"),
+	DEVICE( DEC,		DEC_21142,	"DC21142"),
 	DEVICE( DEC,		DEC_21052,	"DC21052"),
 	DEVICE( DEC,		DEC_21152,	"DC21152"),
+	DEVICE( CIRRUS,		CIRRUS_7548,	"GD 7548"),
 	DEVICE( CIRRUS,		CIRRUS_5430,	"GD 5430"),
 	DEVICE( CIRRUS,		CIRRUS_5434_4,	"GD 5434"),
 	DEVICE( CIRRUS,		CIRRUS_5434_8,	"GD 5434"),
 	DEVICE( CIRRUS,		CIRRUS_5436,	"GD 5436"),
+	DEVICE( CIRRUS,		CIRRUS_5446,	"GD 5446"),
+	DEVICE( CIRRUS,		CIRRUS_5480,	"GD 5480"),
+	DEVICE( CIRRUS,		CIRRUS_5464,	"GD 5464"),
+	DEVICE( CIRRUS,		CIRRUS_5465,	"GD 5465"),
 	DEVICE( CIRRUS,		CIRRUS_6729,	"CL 6729"),
+	DEVICE( CIRRUS,		CIRRUS_6832,	"PD 6832"),
 	DEVICE( CIRRUS,		CIRRUS_7542,	"CL 7542"),
 	DEVICE( CIRRUS,		CIRRUS_7543,	"CL 7543"),
+	DEVICE( CIRRUS,		CIRRUS_7541,	"CL 7541"),
+	DEVICE( IBM,		IBM_FIRE_CORAL,	"Fire Coral"),
 	DEVICE( IBM,		IBM_82G2675,	"82G2675"),
+	DEVICE( IBM,		IBM_82351,	"82351"),
 	DEVICE( WD,		WD_7197,	"WD 7197"),
 	DEVICE( AMD,		AMD_LANCE,	"79C970"),
 	DEVICE( AMD,		AMD_SCSI,	"53C974"),
@@ -89,10 +111,12 @@
 	DEVICE( MATROX,		MATROX_MGA_2,	"Atlas PX2085"),
 	DEVICE( MATROX,		MATROX_MIL,	"Millennium"),
 	DEVICE( MATROX,		MATROX_MYS,	"Mystique"),
+	DEVICE( MATROX,		MATROX_MIL_2,	"Millennium II"),
 	DEVICE( MATROX,		MATROX_MGA_IMP,	"MGA Impression"),
 	DEVICE( CT,		CT_65545,	"65545"),
 	DEVICE( CT,		CT_65548,	"65548"),
 	DEVICE(	CT,		CT_65550,	"65550"),
+	DEVICE( CT,		CT_65554,	"65554"),
 	DEVICE( MIRO,		MIRO_36050,	"ZR36050"),
 	DEVICE( FD,		FD_36C70,	"TMC-18C30"),
 	DEVICE( SI,		SI_6201,	"6201"),
@@ -102,9 +126,13 @@
 	DEVICE( SI,		SI_501,		"85C501"),
 	DEVICE( SI,		SI_496,		"85C496"),
 	DEVICE( SI,		SI_601,		"85C601"),
+	DEVICE( SI,		SI_5107,	"5107"),
 	DEVICE( SI,		SI_5511,		"85C5511"),
 	DEVICE( SI,		SI_5513,		"85C5513"),
+	DEVICE( SI,		SI_5571,	"5571"),
+	DEVICE( SI,		SI_7001,	"7001"),
 	DEVICE( HP,		HP_J2585A,	"J2585A"),
+	DEVICE( HP,		HP_J2585B,	"J2585B (Lassen)"),
 	DEVICE( PCTECH,		PCTECH_RZ1000,  "RZ1000 (buggy)"),
 	DEVICE( PCTECH,		PCTECH_RZ1001,  "RZ1001 (buggy?)"),
 	DEVICE( DPT,		DPT,		"SmartCache/Raid"),
@@ -118,7 +146,13 @@
 	DEVICE( BUSLOGIC,	BUSLOGIC_MULTIMASTER_NC, "MultiMaster NC"),
 	DEVICE( BUSLOGIC,	BUSLOGIC_MULTIMASTER,    "MultiMaster"),
 	DEVICE( BUSLOGIC,	BUSLOGIC_FLASHPOINT,     "FlashPoint"),
+	DEVICE( TI,		TI_PCI1130,	"PCI1130"),
+	DEVICE( TI,		TI_PCI1131,	"PCI1131"),
 	DEVICE( OAK,		OAK_OTI107,	"OTI107"),
+	DEVICE( WINBOND2,	WINBOND2_89C940,"NE2000-PCI"),
+	DEVICE( MOTOROLA,	MOTOROLA_MPC105,"MPC105 Eagle"),
+	DEVICE( MOTOROLA,	MOTOROLA_MPC106,"MPC106 Grackle"),
+	DEVICE( MOTOROLA,	MOTOROLA_RAVEN,	"Raven"),
 	DEVICE( PROMISE,	PROMISE_5300,	"DC5030"),
 	DEVICE( N9,		N9_I128,	"Imagine 128"),
 	DEVICE( N9,		N9_I128_2,	"Imagine 128v2"),
@@ -132,23 +166,40 @@
 	DEVICE( UMC,		UMC_UM8886N,	"UM8886N"),
 	DEVICE( UMC,		UMC_UM8891N,	"UM8891N"),
 	DEVICE( X,		X_AGX016,	"ITT AGX016"),
+	DEVICE( APPLE,		APPLE_BANDIT,	"Bandit"),
+	DEVICE( APPLE,		APPLE_GC,	"Grand Central"),
+	DEVICE( APPLE,		APPLE_HYDRA,	"Hydra"),
 	DEVICE( NEXGEN,		NEXGEN_82C501,	"82C501"),
 	DEVICE( QLOGIC,		QLOGIC_ISP1020,	"ISP1020"),
 	DEVICE( QLOGIC,		QLOGIC_ISP1022,	"ISP1022"),
 	DEVICE( LEADTEK,	LEADTEK_805,	"S3 805"),
 	DEVICE( CONTAQ,		CONTAQ_82C599,	"82C599"),
+	DEVICE( OLICOM,		OLICOM_OC3136,	"OC-3136/3137"),
+	DEVICE( OLICOM,		OLICOM_OC2315,	"OC-2315"),
+	DEVICE( OLICOM,		OLICOM_OC2325,	"OC-2325"),
+	DEVICE( OLICOM,		OLICOM_OC2183,	"OC-2183/2185"),
+	DEVICE( OLICOM,		OLICOM_OC2326,	"OC-2326"),
+	DEVICE( OLICOM,		OLICOM_OC6151,	"OC-6151/6152"),
 	DEVICE( CMD,		CMD_640,	"640 (buggy)"),
+	DEVICE( CMD,		CMD_643,	"643"),
 	DEVICE( CMD,		CMD_646,	"646"),
+	DEVICE( CMD,		CMD_670,	"670"),
 	DEVICE( VISION,		VISION_QD8500,	"QD-8500"),
 	DEVICE( VISION,		VISION_QD8580,	"QD-8580"),
+	DEVICE( BROOKTREE,	BROOKTREE_848,	"Bt848"),
 	DEVICE( SIERRA,		SIERRA_STB,	"STB Horizon 64"),
 	DEVICE( ACC,		ACC_2056,	"2056"),
 	DEVICE( WINBOND,	WINBOND_83769,	"W83769F"),
 	DEVICE( WINBOND,	WINBOND_82C105,	"SL82C105"),
+	DEVICE( WINBOND,	WINBOND_83C553,	"W83C553"),
+	DEVICE( DATABOOK,      	DATABOOK_87144,	"DB87144"),
 	DEVICE( 3COM,		3COM_3C590,	"3C590 10bT"),
 	DEVICE( 3COM,		3COM_3C595TX,	"3C595 100bTX"),
 	DEVICE( 3COM,		3COM_3C595T4,	"3C595 100bT4"),
 	DEVICE( 3COM,		3COM_3C595MII,	"3C595 100b-MII"),
+	DEVICE( 3COM,		3COM_3C900TPO,	"3C900 10bTPO"),
+	DEVICE( 3COM,		3COM_3C900COMBO,"3C900 10b Combo"),
+	DEVICE( 3COM,		3COM_3C905TX,	"3C905 100bTX"),
 	DEVICE( AL,		AL_M1445,	"M1445"),
 	DEVICE( AL,		AL_M1449,	"M1449"),
 	DEVICE( AL,		AL_M1451,	"M1451"),
@@ -157,19 +208,31 @@
 	DEVICE( AL,		AL_M1511,	"M1511"),
 	DEVICE( AL,		AL_M1513,	"M1513"),
 	DEVICE( AL,		AL_M4803,	"M4803"),
+	DEVICE( NEOMAGIC,       NEOMAGIC_MAGICGRAPH_NM2070,     "Magicgraph NM2070"),
+	DEVICE( NEOMAGIC,	NEOMAGIC_MAGICGRAPH_128V, "MagicGraph 128V"),
 	DEVICE( ASP,		ASP_ABP940,	"ABP940"),
+	DEVICE( ASP,		ASP_ABP940U,	"ABP940U"),
 	DEVICE( CERN,		CERN_SPSB_PMC,	"STAR/RD24 SCI-PCI (PMC)"),
 	DEVICE( CERN,		CERN_SPSB_PCI,	"STAR/RD24 SCI-PCI (PMC)"),
 	DEVICE( IMS,		IMS_8849,	"8849"),
 	DEVICE( TEKRAM2,	TEKRAM2_690c,	"DC690c"),
+	DEVICE( TUNDRA,		TUNDRA_CA91C042,"CA91C042 Universe"),
 	DEVICE( AMCC,		AMCC_MYRINET,	"Myrinet PCI (M2-PCI-32)"),
+	DEVICE( AMCC,		AMCC_S5933,	"S5933"),
 	DEVICE( INTERG,		INTERG_1680,	"IGA-1680"),
+	DEVICE( INTERG,         INTERG_1682,    "IGA-1682"),
 	DEVICE( REALTEK,	REALTEK_8029,	"8029"),
+	DEVICE( REALTEK,	REALTEK_8129,	"8129"),
+	DEVICE( TRUEVISION,	TRUEVISION_T1000,"TARGA 1000"),
 	DEVICE( INIT,		INIT_320P,	"320 P"),
 	DEVICE( VIA,		VIA_82C505,	"VT 82C505"),
 	DEVICE( VIA,		VIA_82C561,	"VT 82C561"),
+	DEVICE( VIA,		VIA_82C586_1,	"VT 82C586 Apollo VP-1"),
 	DEVICE( VIA,		VIA_82C576,	"VT 82C576 3V"),
+	DEVICE( VIA,		VIA_82C585,	"VT 82C585VP Apollo VP-1"),
+	DEVICE( VIA,		VIA_82C586_0,	"VT 82C586 Apollo VP-1"),
 	DEVICE( VIA,		VIA_82C416,	"VT 82C416MV"),
+	DEVICE( VIA,		VIA_82C926,	"VT 82C926 Amazon"),
 	DEVICE( VORTEX,		VORTEX_GDT60x0,	"GDT 60x0"),
 	DEVICE( VORTEX,		VORTEX_GDT6000B,"GDT 6000b"),
 	DEVICE( VORTEX,		VORTEX_GDT6x10,	"GDT 6110/6510"),
@@ -186,19 +249,31 @@
 	DEVICE( VORTEX,		VORTEX_GDT6555,	"GDT 6555"),
 	DEVICE( EF,		EF_ATM_FPGA,		"155P-MF1 (FPGA)"),
 	DEVICE( EF,		EF_ATM_ASIC,	"155P-MF1 (ASIC)"),
-	DEVICE( IMAGINGTECH,	IMAGINGTECH_ICPCI, "MVC IC-PCI"),
 	DEVICE( FORE,		FORE_PCA200PC, "PCA-200PC"),
 	DEVICE( FORE,		FORE_PCA200E,	 "PCA-200E"),
+	DEVICE( IMAGINGTECH,	IMAGINGTECH_ICPCI, "MVC IC-PCI"),
+	DEVICE( PHILIPS,	PHILIPS_SAA7146,"SAA7146"),
 	DEVICE( PLX,		PLX_9060,	"PCI9060 i960 bridge"),
 	DEVICE( ALLIANCE,	ALLIANCE_PROMOTIO, "Promotion-6410"),
 	DEVICE( ALLIANCE,	ALLIANCE_PROVIDEO, "Provideo"),
 	DEVICE( VMIC,		VMIC_VME,	"VMIVME-7587"),
  	DEVICE( DIGI,		DIGI_RIGHTSWITCH, "RightSwitch SE-6"),
 	DEVICE( MUTECH,		MUTECH_MV1000,	"MV-1000"),
+	DEVICE( RENDITION,	RENDITION_VERITE,"Verite 1000"),
+	DEVICE( TOSHIBA,	TOSHIBA_601,	"Laptop"),
+	DEVICE( RICOH,		RICOH_RL5C466,	"RL5C466"),
 	DEVICE( ZEITNET,	ZEITNET_1221,	"1221"),
 	DEVICE( ZEITNET,	ZEITNET_1225,	"1225"),
+	DEVICE( OMEGA,		OMEGA_82C092G,	"82C092G"),
+	DEVICE( NP,		NP_PCI_FDDI,	"NP-PCI"),       
 	DEVICE( SPECIALIX,	SPECIALIX_XIO,	"XIO/SIO host"),
 	DEVICE( SPECIALIX,	SPECIALIX_RIO,	"RIO host"),
+	DEVICE( IKON,		IKON_10115,	"10115 Greensheet"),
+	DEVICE( IKON,		IKON_10117,	"10117 Greensheet"),
+	DEVICE( ZORAN,		ZORAN_36057,	"ZR36057"),
+	DEVICE( ZORAN,		ZORAN_36120,	"ZR36120"),
+	DEVICE( COMPEX,		COMPEX_ENET100VG4, "Readylink ENET100-VG4"),
+	DEVICE( COMPEX,		COMPEX_RL2000,	"ReadyLink 2000"),
 	DEVICE( RP,             RP8OCTA,        "RocketPort 8 Oct"),
 	DEVICE( RP,             RP8INTF,        "RocketPort 8 Intf"),
 	DEVICE( RP,             RP16INTF,       "RocketPort 16 Intf"),
@@ -207,10 +282,23 @@
 	DEVICE( CYCLADES,	CYCLOM_Y_Hi,	"Cyclom-Y above 1Mbyte"),
 	DEVICE( CYCLADES,	CYCLOM_Z_Lo,	"Cyclom-Z below 1Mbyte"),
 	DEVICE( CYCLADES,	CYCLOM_Z_Hi,	"Cyclom-Z above 1Mbyte"),
+	DEVICE( 3DFX,		3DFX_VOODOO,	"Voodoo"),
+	DEVICE( SIGMADES,	SIGMADES_6425,	"REALmagic64/GX"),
+	DEVICE( OPTIBASE,	OPTIBASE_FORGE,	"MPEG Forge"),
+	DEVICE( OPTIBASE,	OPTIBASE_FUSION,"MPEG Fusion"),
+	DEVICE( OPTIBASE,	OPTIBASE_VPLEX,	"VideoPlex"),
+	DEVICE( OPTIBASE,	OPTIBASE_VPLEXCC,"VideoPlex CC"),
+	DEVICE( OPTIBASE,	OPTIBASE_VQUEST,"VideoQuest"),
 	DEVICE( SYMPHONY,	SYMPHONY_101,	"82C101"),
 	DEVICE( TEKRAM,		TEKRAM_DC290,	"DC-290"),
 	DEVICE( 3DLABS,		3DLABS_300SX,	"GLINT 300SX"),
+	DEVICE( 3DLABS,		3DLABS_500TX,	"GLINT 500TX"),
+	DEVICE( 3DLABS,		3DLABS_DELTA,	"GLINT Delta"),
+	DEVICE( 3DLABS,		3DLABS_PERMEDIA,"PERMEDIA"),
+	DEVICE( AVANCE,		AVANCE_ALG2064,	"ALG2064i"),
 	DEVICE( AVANCE,		AVANCE_2302,	"ALG-2302"),
+	DEVICE( NETVIN,		NETVIN_NV5000SC,"NV5000"),
+	DEVICE( S3,		S3_PLATO_PXS,	"PLATO/PX (system)"),
 	DEVICE( S3,		S3_ViRGE,	"ViRGE"),
 	DEVICE( S3,		S3_TRIO,	"Trio32/Trio64"),
 	DEVICE( S3,		S3_AURORA64VP,	"Aurora64V+"),
@@ -223,11 +311,16 @@
 	DEVICE( S3,		S3_964_1,	"Vision 964-P"),
 	DEVICE( S3,		S3_964_2,	"Vision 964-P"),
 	DEVICE( S3,		S3_968,		"Vision 968"),
+	DEVICE( S3,		S3_TRIO64V2,	"Trio64V2/DX or /GX"),
+	DEVICE( S3,		S3_PLATO_PXG,	"PLATO/PX (graphics)"),
+	DEVICE( S3,		S3_ViRGE_DXGX,	"ViRGE/DX or /GX"),
 	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),
+	DEVICE( INTEL,		INTEL_82092AA_0,"82092AA PCMCIA bridge"),
+	DEVICE( INTEL,		INTEL_82092AA_1,"82092AA EIDE"),
 	DEVICE( INTEL,		INTEL_7116,	"SAA7116"),
 	DEVICE( INTEL,		INTEL_82596,	"82596"),
 	DEVICE( INTEL,		INTEL_82865,	"82865"),
@@ -235,13 +328,22 @@
 	DEVICE( INTEL,		INTEL_82437,	"82437"),
 	DEVICE( INTEL,		INTEL_82371_0,	"82371 Triton PIIX"),
 	DEVICE( INTEL,		INTEL_82371_1,	"82371 Triton PIIX"),
+	DEVICE( INTEL,		INTEL_82371MX,	"430MX - 82371MX MPIIX"),
+	DEVICE( INTEL,		INTEL_82437MX,	"430MX - 82437MX MTSC"),
 	DEVICE( INTEL,		INTEL_82441,	"82441FX Natoma"),
 	DEVICE( INTEL,		INTEL_82439,	"82439HX Triton II"),
 	DEVICE(	INTEL,		INTEL_82371SB_0,"82371SB Natoma/Triton II PIIX3"),
 	DEVICE(	INTEL,		INTEL_82371SB_1,"82371SB Natoma/Triton II PIIX3"),
 	DEVICE( INTEL,		INTEL_82371SB_2,"82371SB Natoma/Triton II PIIX3"),
 	DEVICE( INTEL,		INTEL_82437VX,	"82437VX Triton II"),
+	DEVICE( INTEL,		INTEL_82439TX,	"82439TX"),
+	DEVICE( INTEL,		INTEL_82371AB_0,"82371AB PIIX4"),
+	DEVICE( INTEL,		INTEL_82371AB,	"82371AB 430TX PIIX4"),
+	DEVICE( INTEL,		INTEL_82371AB_2,"82371AB PIIX4"),
+	DEVICE( INTEL,		INTEL_82371AB_3,"82371AB PIIX4 Power Management"),
 	DEVICE( INTEL,		INTEL_P6,	"Orion P6"),
+ 	DEVICE( INTEL,		INTEL_82450GX,	"82450GX Orion P6"),
+	DEVICE(	KTI,		KTI_ET32P2,	"ET32P2"),
 	DEVICE( ADAPTEC,	ADAPTEC_7850,	"AIC-7850"),
 	DEVICE( ADAPTEC,	ADAPTEC_7855,	"AIC-7855"),
 	DEVICE( ADAPTEC,	ADAPTEC_7860,	"AIC-7860"),
@@ -257,8 +359,9 @@
 	DEVICE( ADAPTEC,	ADAPTEC_7883,	"AIC-7883U"),
 	DEVICE( ADAPTEC,	ADAPTEC_7884,	"AIC-7884U"),
   	DEVICE( ATRONICS,	ATRONICS_2015,	"IDE-2015PL"),
-	DEVICE( HER,		HER_STING,	"Stingray"),
-	DEVICE( HER,		HER_STINGARK,	"Stingray ARK 2000PV")
+	DEVICE( ARK,		ARK_STING,	"Stingray"),
+	DEVICE( ARK,		ARK_STINGARK,	"Stingray ARK 2000PV"),
+	DEVICE( ARK,		ARK_2000MT,	"2000MT")
 };
 
 
@@ -477,11 +580,15 @@
 	      case PCI_VENDOR_ID_HP:		return "Hewlett Packard";
 	      case PCI_VENDOR_ID_PCTECH:	return "PCTECH";
 	      case PCI_VENDOR_ID_DPT:		return "DPT";
-	      case PCI_VENDOR_ID_OPTI:		return "OPTI";
+	      case PCI_VENDOR_ID_OPTI:		return "OPTi";
 	      case PCI_VENDOR_ID_SGS:		return "SGS Thomson";
 	      case PCI_VENDOR_ID_BUSLOGIC:	return "BusLogic";
+	      case PCI_VENDOR_ID_TI:		return "Texas Instruments";
 	      case PCI_VENDOR_ID_OAK: 		return "OAK";
+	      case PCI_VENDOR_ID_WINBOND2:	return "Winbond";
+	      case PCI_VENDOR_ID_MOTOROLA:	return "Motorola";
 	      case PCI_VENDOR_ID_PROMISE:	return "Promise Technology";
+	      case PCI_VENDOR_ID_APPLE:		return "Apple";
 	      case PCI_VENDOR_ID_N9:		return "Number Nine";
 	      case PCI_VENDOR_ID_UMC:		return "UMC";
 	      case PCI_VENDOR_ID_X:		return "X TECHNOLOGY";
@@ -493,43 +600,63 @@
 	      case PCI_VENDOR_ID_OLICOM:	return "Olicom";
 	      case PCI_VENDOR_ID_CMD:		return "CMD";
 	      case PCI_VENDOR_ID_VISION:	return "Vision";
+	      case PCI_VENDOR_ID_BROOKTREE:	return "Brooktree";
 	      case PCI_VENDOR_ID_SIERRA:	return "Sierra";
 	      case PCI_VENDOR_ID_ACC:		return "ACC MICROELECTRONICS";
 	      case PCI_VENDOR_ID_WINBOND:	return "Winbond";
+	      case PCI_VENDOR_ID_DATABOOK:	return "Databook";
 	      case PCI_VENDOR_ID_3COM:		return "3Com";
+	      case PCI_VENDOR_ID_SMC:		return "SMC";
 	      case PCI_VENDOR_ID_AL:		return "Acer Labs";
+	      case PCI_VENDOR_ID_MITSUBISHI:	return "Mitsubishi";
+	      case PCI_VENDOR_ID_NEOMAGIC:	return "Neomagic";
 	      case PCI_VENDOR_ID_ASP:		return "Advanced System Products";
 	      case PCI_VENDOR_ID_CERN:		return "CERN";
 	      case PCI_VENDOR_ID_IMS:		return "IMS";
 	      case PCI_VENDOR_ID_TEKRAM2:	return "Tekram";
+	      case PCI_VENDOR_ID_TUNDRA:	return "Tundra";
 	      case PCI_VENDOR_ID_AMCC:		return "AMCC";
 	      case PCI_VENDOR_ID_INTERG:	return "Intergraphics";
 	      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_VIA:		return "VIA Technologies";
 	      case PCI_VENDOR_ID_VORTEX:	return "VORTEX";
 	      case PCI_VENDOR_ID_EF:		return "Efficient Networks";
 	      case PCI_VENDOR_ID_FORE:		return "Fore Systems";
 	      case PCI_VENDOR_ID_IMAGINGTECH:	return "Imaging Technology";
+	      case PCI_VENDOR_ID_PHILIPS:	return "Philips";
 	      case PCI_VENDOR_ID_PLX:		return "PLX";
 	      case PCI_VENDOR_ID_ALLIANCE:	return "Alliance";
 	      case PCI_VENDOR_ID_VMIC:		return "VMIC";
 	      case PCI_VENDOR_ID_DIGI:		return "Digi Intl.";
 	      case PCI_VENDOR_ID_MUTECH:	return "Mutech";
+	      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_ZEITNET:	return "ZeitNet";
+	      case PCI_VENDOR_ID_OMEGA:		return "Omega Micro";
+	      case PCI_VENDOR_ID_NP:		return "Network Peripherals";
 	      case PCI_VENDOR_ID_SPECIALIX:	return "Specialix";
+	      case PCI_VENDOR_ID_IKON:		return "Ikon";
+	      case PCI_VENDOR_ID_ZORAN:		return "Zoran";
+	      case PCI_VENDOR_ID_COMPEX:	return "Compex";
 	      case PCI_VENDOR_ID_RP:		return "Comtrol";
 	      case PCI_VENDOR_ID_CYCLADES:	return "Cyclades";
+	      case PCI_VENDOR_ID_3DFX:		return "3Dfx";
+	      case PCI_VENDOR_ID_SIGMADES:	return "Sigma Designs";
+	      case PCI_VENDOR_ID_OPTIBASE:	return "Optibase";
 	      case PCI_VENDOR_ID_SYMPHONY:	return "Symphony";
 	      case PCI_VENDOR_ID_TEKRAM:	return "Tekram";
 	      case PCI_VENDOR_ID_3DLABS:	return "3Dlabs";
 	      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_INTEL:		return "Intel";
+	      case PCI_VENDOR_ID_KTI:		return "KTI";
 	      case PCI_VENDOR_ID_ADAPTEC:	return "Adaptec";
 	      case PCI_VENDOR_ID_ATRONICS:	return "Atronics";
-	      case PCI_VENDOR_ID_HER:		return "Hercules";
+	      case PCI_VENDOR_ID_ARK:		return "ARK Logic";
 	      default:				return "Unknown vendor";
 	}
 }
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/BusLogic.c linux/drivers/scsi/BusLogic.c
--- v2.0.30/linux/drivers/scsi/BusLogic.c	Sat Mar 29 09:01:00 1997
+++ linux/drivers/scsi/BusLogic.c	Mon Aug 11 00:10:00 1997
@@ -27,8 +27,8 @@
 */
 
 
-#define BusLogic_DriverVersion		"2.0.9"
-#define BusLogic_DriverDate		"29 March 1997"
+#define BusLogic_DriverVersion		"2.0.10"
+#define BusLogic_DriverDate		"11 August 1997"
 
 
 #include <linux/module.h>
@@ -505,13 +505,14 @@
     Wait for the Host Adapter Ready bit to be set and the Command/Parameter
     Register Busy bit to be reset in the Status Register.
   */
-  TimeoutCounter = loops_per_sec >> 3;
+  TimeoutCounter = 10000;
   while (--TimeoutCounter >= 0)
     {
       StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
       if (StatusRegister.Bits.HostAdapterReady &&
 	  !StatusRegister.Bits.CommandParameterRegisterBusy)
 	break;
+      udelay(100);
     }
   if (TimeoutCounter < 0)
     {
@@ -587,11 +588,11 @@
     case BusLogic_InquireInstalledDevicesID8to15:
     case BusLogic_InquireTargetDevices:
       /* Approximately 60 seconds. */
-      TimeoutCounter = loops_per_sec << 2;
+      TimeoutCounter = 60*10000;
       break;
     default:
       /* Approximately 1 second. */
-      TimeoutCounter = loops_per_sec >> 4;
+      TimeoutCounter = 10000;
       break;
     }
   /*
@@ -611,6 +612,7 @@
 	else BusLogic_ReadDataInRegister(HostAdapter);
       if (OperationCode == BusLogic_FetchHostAdapterLocalRAM &&
 	  StatusRegister.Bits.HostAdapterReady) break;
+      udelay(100);
     }
   if (TimeoutCounter < 0)
     {
@@ -786,7 +788,7 @@
   boolean StandardAddressSeen[BusLogic_ISA_StandardAddressesCount];
   BusLogic_ProbeInfo_T *PrimaryProbeInfo =
     &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount];
-  int NonPrimaryPCIMultiMasterIndex = BusLogic_ProbeInfoCount;
+  int NonPrimaryPCIMultiMasterIndex = BusLogic_ProbeInfoCount + 1;
   int NonPrimaryPCIMultiMasterCount = 0, PCIMultiMasterCount = 0;
   boolean ForceBusDeviceScanningOrder = false;
   boolean ForceBusDeviceScanningOrderChecked = false;
@@ -1240,13 +1242,16 @@
     {
       FlashPoint_Info_T *FlashPointInfo = (FlashPoint_Info_T *)
 	scsi_init_malloc(sizeof(FlashPoint_Info_T), GFP_ATOMIC);
+      int Retries = 10;
       if (FlashPointInfo == NULL)
 	return BusLogic_Failure(HostAdapter, "ALLOCATING FLASHPOINT INFO");
       FlashPointInfo->BaseAddress = HostAdapter->IO_Address;
       FlashPointInfo->IRQ_Channel = HostAdapter->IRQ_Channel;
       FlashPointInfo->Present = false;
-      if (!(FlashPoint_ProbeHostAdapter(FlashPointInfo) == 0 &&
-	    FlashPointInfo->Present))
+      while (!(FlashPoint_ProbeHostAdapter(FlashPointInfo) == 0 &&
+	       FlashPointInfo->Present) &&
+	     --Retries >= 0) ;
+      if (!FlashPointInfo->Present)
 	{
 	  scsi_init_free((char *) FlashPointInfo, sizeof(FlashPoint_Info_T));
 	  BusLogic_Error("BusLogic: FlashPoint Host Adapter detected at "
@@ -1321,7 +1326,7 @@
 					     *HostAdapter)
 {
   BusLogic_StatusRegister_T StatusRegister;
-  int TimeoutCounter = loops_per_sec;
+  int TimeoutCounter;
   /*
     FlashPoint Host Adapters are Hard Reset by the FlashPoint SCCB Manager.
   */
@@ -1344,10 +1349,12 @@
   /*
     Wait until Diagnostic Active is set in the Status Register.
   */
+  TimeoutCounter = 5*10000;
   while (--TimeoutCounter >= 0)
     {
       StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
       if (StatusRegister.Bits.DiagnosticActive) break;
+      udelay(100);
     }
   if (BusLogic_GlobalOptions.Bits.TraceHardReset)
     BusLogic_Notice("BusLogic_HardReset(0x%X): Diagnostic Active, "
@@ -1363,10 +1370,12 @@
   /*
     Wait until Diagnostic Active is reset in the Status Register.
   */
+  TimeoutCounter = 10*10000;
   while (--TimeoutCounter >= 0)
     {
       StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
       if (!StatusRegister.Bits.DiagnosticActive) break;
+      udelay(100);
     }
   if (BusLogic_GlobalOptions.Bits.TraceHardReset)
     BusLogic_Notice("BusLogic_HardReset(0x%X): Diagnostic Completed, "
@@ -1377,6 +1386,7 @@
     Wait until at least one of the Diagnostic Failure, Host Adapter Ready,
     or Data In Register Ready bits is set in the Status Register.
   */
+  TimeoutCounter = 10000;
   while (--TimeoutCounter >= 0)
     {
       StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
@@ -1384,6 +1394,7 @@
 	  StatusRegister.Bits.HostAdapterReady ||
 	  StatusRegister.Bits.DataInRegisterReady)
 	break;
+      udelay(100);
     }
   if (BusLogic_GlobalOptions.Bits.TraceHardReset)
     BusLogic_Notice("BusLogic_HardReset(0x%X): Host Adapter Ready, "
@@ -2998,8 +3009,6 @@
 			     "Incoming Mailbox\n", HostAdapter,
 			     CCB->SerialNumber, CCB->Status);
 	  }
-      else BusLogic_Warning("Aborted CCB #%ld to Target %d Not Found\n",
-			    HostAdapter, CCB->SerialNumber, CCB->TargetID);
       NextIncomingMailbox->CompletionCode = BusLogic_IncomingMailboxFree;
       if (++NextIncomingMailbox > HostAdapter->LastIncomingMailbox)
 	NextIncomingMailbox = HostAdapter->FirstIncomingMailbox;
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/BusLogic.h linux/drivers/scsi/BusLogic.h
--- v2.0.30/linux/drivers/scsi/BusLogic.h	Sat Mar 29 09:01:00 1997
+++ linux/drivers/scsi/BusLogic.h	Mon Aug 11 00:10:00 1997
@@ -27,6 +27,9 @@
 */
 
 
+#include <linux/config.h>
+
+
 /*
   Define types for some of the structures that interface with the rest
   of the Linux Kernel and SCSI Subsystem.
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/ChangeLog.ncr53c8xx linux/drivers/scsi/ChangeLog.ncr53c8xx
--- v2.0.30/linux/drivers/scsi/ChangeLog.ncr53c8xx	Sat Aug 31 23:15:32 1996
+++ linux/drivers/scsi/ChangeLog.ncr53c8xx	Thu Aug 14 10:30:08 1997
@@ -1,3 +1,421 @@
+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.
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v2.0.30/linux/drivers/scsi/Config.in	Fri Feb 28 15:14:18 1997
+++ linux/drivers/scsi/Config.in	Sat Aug 16 11:21:04 1997
@@ -18,7 +18,17 @@
 dep_tristate 'Adaptec AHA152X/2825 support' CONFIG_SCSI_AHA152X $CONFIG_SCSI
 dep_tristate 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 $CONFIG_SCSI
 dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI
-dep_tristate 'Adaptec AHA274X/284X/294X support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
+dep_tristate 'Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
+if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then
+    bool '   Enable tagged command queueing' CONFIG_AIC7XXX_TAGGED_QUEUEING Y
+    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 8
+    fi
+    bool '   Enable SCB paging' CONFIG_AIC7XXX_PAGE_ENABLE N
+    bool '   Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS N
+    int  '   Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 15
+fi
 dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI
 dep_tristate 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 $CONFIG_SCSI
 dep_tristate 'AM53/79C974 PCI SCSI support' CONFIG_SCSI_AM53C974 $CONFIG_SCSI
@@ -55,15 +65,17 @@
 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
-    bool '  force normal IO' CONFIG_SCSI_NCR53C8XX_IOMAPPED
-    bool '  not allow targets to disconnect' CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT
-    bool '  force asynchronous transfer mode' CONFIG_SCSI_NCR53C8XX_FORCE_ASYNCHRONOUS
-    bool '  force synchronous negotiation' CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO
-  fi
-  if [ "$CONFIG_SCSI_NCR53C8XX" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
-    bool '  disable master parity checking' CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK
-    bool '  disable scsi parity checking' CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK
+    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
+      bool '  not allow targets to disconnect' CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT
+    fi
+    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      bool '  assume boards are SYMBIOS compatible' CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT
+    fi
   fi
 fi
 dep_tristate 'IOMEGA Parallel Port ZIP drive SCSI support' CONFIG_SCSI_PPA $CONFIG_SCSI
@@ -73,6 +85,9 @@
   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
+fi
 dep_tristate 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128 $CONFIG_SCSI
 dep_tristate 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F $CONFIG_SCSI
   if [ "$CONFIG_SCSI_U14_34F" != "n" ]; then
@@ -80,5 +95,6 @@
     int  '  maximum number of queued commands' CONFIG_SCSI_U14_34F_MAX_TAGS 8
   fi
 dep_tristate 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR $CONFIG_SCSI
+dep_tristate 'GDT SCSI Disk Array Controller support' CONFIG_SCSI_GDTH $CONFIG_SCSI
 #dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG $CONFIG_SCSI
 endmenu
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/FlashPoint.c linux/drivers/scsi/FlashPoint.c
--- v2.0.30/linux/drivers/scsi/FlashPoint.c	Sat Mar 29 09:01:00 1997
+++ linux/drivers/scsi/FlashPoint.c	Mon Aug 11 00:10:00 1997
@@ -31,6 +31,18 @@
 #endif
 
 
+/*
+  FlashPoint support is only available for the Intel x86 Architecture.
+*/
+
+#ifndef __i386__
+
+#undef CONFIG_SCSI_OMIT_FLASHPOINT
+#define CONFIG_SCSI_OMIT_FLASHPOINT
+
+#endif
+
+
 #ifndef CONFIG_SCSI_OMIT_FLASHPOINT
 
 
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- v2.0.30/linux/drivers/scsi/Makefile	Fri Feb 28 15:14:18 1997
+++ linux/drivers/scsi/Makefile	Thu Aug 14 10:31:20 1997
@@ -19,6 +19,7 @@
 SCSI_SRCS = $(wildcard $(L_OBJS:%.o=%.c))
 
 AHA152X	= -DDEBUG_AHA152X -DAUTOCONF
+GDTH = #-DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS
 
 .SUFFIXES:
 .SUFFIXES: .c .o .h .a
@@ -177,6 +178,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_SCSI_DC390T),y)
+L_OBJS += tmscsim.o
+else
+  ifeq ($(CONFIG_SCSI_DC390T),m)
+  M_OBJS += tmscsim.o
+  endif
+endif
+
 ifeq ($(CONFIG_SCSI_AM53C974),y)
 L_OBJS += AM53C974.o
 else
@@ -225,6 +234,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_SCSI_GDTH),y)
+L_OBJS += gdth.o
+else
+  ifeq ($(CONFIG_SCSI_GDTH),m)
+  M_OBJS += gdth.o
+  endif
+endif
+
 ifeq ($(CONFIG_SCSI_DEBUG),y)
 L_OBJS += scsi_debug.o
 else
@@ -251,10 +268,8 @@
 
 ifeq ($(CONFIG_SCSI_GENERIC_NCR5380),y)
 L_OBJS += g_NCR5380.o
-EXTRA_CFLAGS = -DGENERIC_NCR5380_OVERRIDE="{{(NCR5380_map_type)0x350,5,0, BOARD_NCR53C400}};"
 else
   ifeq ($(CONFIG_SCSI_GENERIC_NCR5380),m)
-  EXTRA_CFLAGS = -DGENERIC_NCR5380_OVERRIDE="{{(NCR5380_map_type)0x350,5,0, BOARD_NCR53C400}};"
   M_OBJS += g_NCR5380.o
   endif
 endif
@@ -348,6 +363,11 @@
   endif
 endif
 
+
+ifeq ($(CONFIG_BLK_DEV_IDESCSI),y)
+L_OBJS += ide-scsi.o
+endif
+
 include $(TOPDIR)/Rules.make
 
 BusLogic.o: BusLogic.c FlashPoint.c
@@ -359,19 +379,17 @@
 aha152x.o: aha152x.c
 	$(CC) $(CFLAGS) $(AHA152X) -c aha152x.c 
 
-aic7xxx_asm:	aic7xxx_asm.c
-	$(HOSTCC) -o $@ aic7xxx_asm.c
+gdth.o: gdth.c gdth.h gdth_proc.c gdth_proc.h
+	$(CC) $(CFLAGS) $(GDTH) -c gdth.c 
 
-aic7xxx.c: aic7xxx_seq.h
-aic7xxx_seq.h:	aic7xxx_asm aic7xxx.seq
-	./aic7xxx_asm -o $@ aic7xxx.seq
+aic7xxx.o: aic7xxx.c aic7xxx_seq.h aic7xxx_reg.h
+	$(CC) $(CFLAGS) -c -o $@ aic7xxx.c
 
 seagate.o: seagate.c
 	$(CC) $(CFLAGS) -DARBITRATE -DSLOW_HANDSHAKE -DFAST32 -c seagate.c 
 
-# For debugging, use the -g flag
-53c7,8xx.o : 53c7,8xx.c
-	$(CC) $(CFLAGS) -g -c 53c7,8xx.c
+tmscsim.o : tmscsim.c
+	$(CC) $(CFLAGS) -c tmscsim.c
 
 53c8xx_d.h 53c8xx_u.h : 53c7,8xx.scr script_asm.pl
 	ln -sf 53c7,8xx.scr fake.c
@@ -380,8 +398,8 @@
 	mv scriptu.h 53c8xx_u.h
 	rm fake.c
 
-ncr53c8xx.o : ncr53c8xx.c
-	$(CC) $(CFLAGS) -c ncr53c8xx.c
+g_NCR5380.o: g_NCR5380.c
+	$(CC) $(CFLAGS) -DGENERIC_NCR5380_OVERRIDE="{{(NCR5380_map_type)0x350,5,0, BOARD_NCR53C400}};" -c g_NCR5380.c
 
 scsi_mod.o: $(MX_OBJS) hosts.o scsi.o scsi_ioctl.o constants.o \
 		scsicam.o scsi_proc.o
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/README.BusLogic linux/drivers/scsi/README.BusLogic
--- v2.0.30/linux/drivers/scsi/README.BusLogic	Sat Mar 29 09:01:00 1997
+++ linux/drivers/scsi/README.BusLogic	Mon Aug 11 00:10:00 1997
@@ -1,10 +1,10 @@
 	   BusLogic MultiMaster and FlashPoint SCSI Driver for Linux
 
-			  Version 2.0.9 for Linux 2.0
+			 Version 2.0.10 for Linux 2.0
 
 			      PRODUCTION RELEASE
 
-				 29 March 1997
+				11 August 1997
 
 			       Leonard N. Zubkoff
 			       Dandelion Digital
@@ -284,6 +284,14 @@
   so as to recognize the host adapters in the same order as they are enumerated
   by the host adapter's BIOS.
 
+o Mega-Transfers/Second
+
+  The driver reports on the synchronous transfer parameters negotiated between
+  the host adapter and target devices in units of "mega-transfers/second".  For
+  wide devices, the unit of transfer is 16 bits if wide negotiation has been
+  successfully completed.  Therefore, the total transfer rate to wide devices
+  will generally be twice the synchronous tranfer rate reported by the driver.
+
 
 			     COMMAND LINE OPTIONS
 
@@ -372,16 +380,17 @@
 
 				 INSTALLATION
 
-This distribution was prepared for Linux kernel version 2.0.29, but should be
-compatible with 2.0.4 or any later 2.0 series kernel.
+This distribution was prepared for Linux kernel version 2.0.30, but should be
+compatible with 2.0.4 or any later 2.0 series kernel if BusLogic.patch is also
+applied.
 
 To install the new BusLogic SCSI driver, you may use the following commands,
 replacing "/usr/src" with wherever you keep your Linux kernel source tree:
 
   cd /usr/src
-  tar -xvzf BusLogic-2.0.9.tar.gz
+  tar -xvzf BusLogic-2.0.10.tar.gz
   mv README.* LICENSE.* BusLogic.[ch] FlashPoint.c linux/drivers/scsi
-  patch -p < BusLogic.patch
+  patch -p < BusLogic.patch	    # Only for kernels prior to 2.0.30
   cd linux
   make config
   make depend
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/README.in2000 linux/drivers/scsi/README.in2000
--- v2.0.30/linux/drivers/scsi/README.in2000	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/README.in2000	Tue Aug  5 13:26:16 1997
@@ -0,0 +1,180 @@
+
+UPDATE NEWS: version 1.31 - 6 Jul 97
+
+   Fixed a bug that caused incorrect SCSI status bytes to be
+   returned from commands sent to LUN's greater than 0. This
+   means that CDROM changers work now! Fixed a bug in the
+   handling of command-line arguments when loaded as a module.
+   Also put all the header data in in2000.h where it belongs.
+   There are no longer any differences between this driver in
+   the 2.1.xx source tree and the 2.0.xx tree, as of 2.0.31
+   and 2.1.45 (or is it .46?) - this makes things much easier
+   for me...
+
+UPDATE NEWS: version 1.30 - 14 Oct 96
+
+   Fixed a bug in the code that sets the transfer direction
+   bit (DESTID_DPD in the WD_DESTINATION_ID register). There
+   are quite a few SCSI commands that do a write-to-device;
+   now we deal with all of them correctly. Thanks to Joerg
+   Dorchain for catching this one.
+
+UPDATE NEWS: version 1.29 - 24 Sep 96
+
+   The memory-mapped hardware on the card is now accessed via
+   the 'readb()' and 'readl()' macros - required by the new
+   memory management scheme in the 2.1.x kernel series.
+   As suggested by Andries Brouwer, 'bios_param()' no longer
+   forces an artificial 1023 track limit on drives. Also
+   removed some kludge-code left over from struggles with
+   older (buggy) compilers.
+
+UPDATE NEWS: version 1.28 - 07 May 96
+
+   Tightened up the "interrupts enabled/disabled" discipline
+   in 'in2000_queuecommand()' and maybe 1 or 2 other places.
+   I _think_ it may have been a little too lax, causing an
+   occasional crash during full moon. A fully functional
+   /proc interface is now in place - if you want to play
+   with it, start by doing 'cat /proc/scsi/in2000/0'. You
+   can also use it to change a few run-time parameters on
+   the fly, but it's mostly for debugging. The curious
+   should take a good look at 'in2000_proc_info()' in the
+   in2000.c file to get an understanding of what it's all
+   about; I figure that people who are really into it will
+   want to add features suited to their own needs...
+   Also, sync is now DISABLED by default.
+
+UPDATE NEWS: version 1.27 - 10 Apr 96
+
+   Fixed a well-hidden bug in the adaptive-disconnect code
+   that would show up every now and then during extreme
+   heavy loads involving 2 or more simultaneously active
+   devices. Thanks to Joe Mack for keeping my nose to the
+   grindstone on this one.
+
+UPDATE NEWS: version 1.26 - 07 Mar 96
+
+   1.25 had a nasty bug that bit people with swap partitions
+   and tape drives. Also, in my attempt to guess my way
+   through Intel assembly language, I made an error in the
+   inline code for IO writes. Made a few other changes and
+   repairs - this version (fingers crossed) should work well.
+
+UPDATE NEWS: version 1.25 - 05 Mar 96
+
+   Kernel 1.3.70 interrupt mods added; old kernels still OK.
+   Big help from Bill Earnest and David Willmore on speed
+   testing and optimizing: I think there's a real improvement
+   in this area.
+   New! User-friendly command-line interface for LILO and
+   module loading - the old method is gone, so you'll need
+   to read the comments for 'setup_strings' near the top
+   of in2000.c. For people with CDROM's or other devices
+   that have a tough time with sync negotiation, you can
+   now selectively disable sync on individual devices -
+   search for the 'nosync' keyword in the command-line
+   comments. Some of you disable the BIOS on the card, which
+   caused the auto-detect function to fail; there is now a
+   command-line option to force detection of a ROM-less card.
+
+UPDATE NEWS: version 1.24a - 24 Feb 96
+
+   There was a bug in the synchronous transfer code. Only
+   a few people downloaded before I caught it - could have
+   been worse.
+
+UPDATE NEWS: version 1.24 - 23 Feb 96
+
+   Lots of good changes. Advice from Bill Earnest resulted
+   in much better detection of cards, more efficient usage
+   of the fifo, and (hopefully) faster data transfers. The
+   jury is still out on speed - I hope it's improved some.
+   One nifty new feature is a cool way of doing disconnect/
+   reselect. The driver defaults to what I'm calling
+   'adaptive disconnect' - meaning that each command is
+   evaluated individually as to whether or not it should be
+   run with the option to disconnect/reselect (if the device
+   chooses), or as a "SCSI-bus-hog". When several devices
+   are operating simultaneously, disconnects are usually an
+   advantage. In a single device system, or if only 1 device
+   is being accessed, transfers usually go faster if disconnects
+   are not allowed.
+
+
+
+The default arguments (you get these when you don't give an 'in2000'
+command-line argument, or you give a blank argument) will cause
+the driver to do adaptive disconnect, synchronous transfers, and a
+minimum of debug messages. If you want to fool with the options,
+search for 'setup_strings' near the top of the in2000.c file and
+check the 'hostdata->args' section in in2000.h - but be warned! Not
+everything is working yet (some things will never work, probably).
+I believe that disabling disconnects (DIS_NEVER) will allow you
+to choose a LEVEL2 value higher than 'L2_BASIC', but I haven't
+spent a lot of time testing this. You might try 'ENABLE_CLUSTERING'
+to see what happens: my tests showed little difference either way.
+There's also a define called 'DEFAULT_SX_PER'; this sets the data
+transfer speed for the asynchronous mode. I've put it at 500 ns
+despite the fact that the card could handle settings of 376 or
+252, because higher speeds may be a problem with poor quality
+cables or improper termination; 500 ns is a compromise. You can
+choose your own default through the command-line with the
+'period' keyword.
+
+
+------------------------------------------------
+***********  DIP switch settings  **************
+------------------------------------------------
+
+   sw1-1 sw1-2    BIOS address (hex)
+   -----------------------------------------
+    off   off     C8000 - CBFF0
+    on    off     D8000 - DBFF0
+    off   on      D0000 - D3FF0
+    on    on      BIOS disabled
+
+   sw1-3 sw1-4    IO port address (hex)
+   ------------------------------------
+    off   off     220 - 22F
+    on    off     200 - 20F
+    off   on      110 - 11F
+    on    on      100 - 10F
+
+   sw1-5 sw1-6 sw1-7    Interrupt
+   ------------------------------
+    off   off   off     15
+    off   on    off     14
+    off   off   on      11
+    off   on    on      10
+    on    -     -       disabled
+
+   sw1-8 function depends on BIOS version. In earlier versions this
+   controlled synchronous data transfer support for MSDOS:
+      off = disabled
+      on  = enabled
+   In later ROMs (starting with 01.3 in April 1994) sw1-8 controls
+   the "greater than 2 disk drive" feature that first appeared in
+   MSDOS 5.0 (ignored by Linux):
+      off = 2 drives maximum
+      on  = 7 drives maximum
+
+   sw1-9    Floppy controller
+   --------------------------
+    off     disabled
+    on      enabled
+
+------------------------------------------------
+
+   I should mention that Drew Eckhardt's 'Generic NCR5380' sources
+   were my main inspiration, with lots of reference to the IN2000
+   driver currently distributed in the kernel source. I also owe
+   much to a driver written by Hamish Macdonald for Linux-m68k(!).
+   And to Eric Wright for being an ALPHA guinea pig. And to Bill
+   Earnest for 2 tons of great input and information. And to David
+   Willmore for extensive 'bonnie' testing. And to Joe Mack for
+   continual testing and feedback.
+
+
+            John Shifflett    jshiffle@netcom.com
+
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/README.ncr53c8xx linux/drivers/scsi/README.ncr53c8xx
--- v2.0.30/linux/drivers/scsi/README.ncr53c8xx	Fri Sep 20 07:00:34 1996
+++ linux/drivers/scsi/README.ncr53c8xx	Thu Aug 14 10:30:08 1997
@@ -1,10 +1,10 @@
-The linux NCR53C8XX driver README file
+The Linux NCR53C8XX driver README file
 
 Written by Gerard Roudier <groudier@club-internet.fr>
 21 Rue Carnot
 95170 DEUIL LA BARRE - FRANCE
 
-12 June 1995
+19 June 1997
 ===============================================================================
 
 1.  Introduction
@@ -21,15 +21,31 @@
       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
 9.  Configuration parameters
-10. Some constants and flags of the ncr53c8xx.h header files
-11. Provided files
-12. Installation procedure for Linux version 1
-13. Installation procedure for Linux version 2
-14. Control commands under linux-1.2.13
-15. Known problems
-      15.1 Tagged commands with Iomega Jaz device
-      15.2 Tagged command queueing cannot be disabled at run time
+10. Boot setup commands
+      10.1 Syntax
+      10.2 Available arguments
+      10.3 Advised boot setup commands
+      10.4 PCI configuration fix-up boot option
+      10.5 Serial NVRAM support 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
+14. Known problems
+      14.1 Tagged commands with Iomega Jaz device
+      14.2 Device names change when another controller is added
+15. SCSI problem troubleshooting
+16. Synchonous transfer negotiation tables
+      16.1 Synchronous timings for 53C875 and 53C860 Ultra-SCSI controllers
+      16.2 Synchronous timings for fast SCSI-2 53C8XX controllers
+17. Serial NVRAM support (by Richard Waltham)
+      17.1 Features
+      17.2 Symbios NVRAM layout
+      17.3 Tekram  NVRAM layout
 
 ===============================================================================
 
@@ -43,21 +59,37 @@
           Wolfgang Stanglmeier        <wolf@cologne.de>
           Stefan Esser                <se@mi.Uni-Koeln.de>
 
-You can find technical information about the NCR 8xx family in the PCI-HOWTO 
-written by Michael Will and in the SCSI-HOWTO written by Drew Eckhardt.
+You can find technical information about the NCR 8xx family in the
+PCI-HOWTO written by Michael Will and in the SCSI-HOWTO written by
+Drew Eckhardt.
 
 Information about new chips is available at SYMBIOS web server:
-          http://www.symbios.com
 
-This short documentation only describes the features of the NCR53C8XX driver, 
-configuration parameters and control commands available through the proc SCSI
-file system read / write operations.
+          http://www.symbios.com/
 
-This driver has been tested OK with linux/i386  and is currently untested
-under linux/Alpha. If you intend to use this driver under linux/Alpha, just 
-try it first with read-only or mounted read-only devices.
+SCSI standard documentations are available at SYMBIOS ftp server:
 
-I am not a native speaker of English and there are probably lots of 
+          ftp://ftp.symbios.com/
+
+Usefull SCSI tools written by Eric Youngdale are available at tsx-11:
+
+          ftp://tsx-11.mit.edu/pub/linux/ALPHA/scsi/scsiinfo-X.Y.tar.gz
+          ftp://tsx-11.mit.edu/pub/linux/ALPHA/scsi/scsidev-X.Y.tar.gz
+
+These tools are not ALPHA but quite clean and work quite well.
+It is essential you have the 'scsiinfo' package.
+
+This short documentation only describes the features of the NCR53C8XX
+driver, configuration parameters and control commands available
+through the proc SCSI file system read / write operations.
+
+This driver has been tested OK with linux/i386 and Linux/Alpha.
+
+Latest driver version and patches are available at:
+
+          ftp://linux.wauug.org/pub/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.
 
 
@@ -71,8 +103,8 @@
 	SCSI parity checking
 	Master parity checking
 
-"Wide negotiation" is supported for chips that allow it.
-The following table shows some characteristics of NCR 8xx family chips:
+"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
@@ -81,12 +113,12 @@
 810A       N         N        N            Y             Y
 815        Y         N        N            Y             Y
 825        Y         Y        N            Y             Y
-825A       Y         Y        N            Y             Not yet
-875        Y         Y        Y(1)         Y             Not yet
-
-(1) Ultra SCSI extensions will be supported in a future release of the 
-    driver.
+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 
 
+(1) The 895 chip is supported 'on paper'.
 
 3. Summary of other supported features.
 
@@ -97,45 +129,58 @@
         Debugging information: written to syslog (expert only)
 	Scatter / gather
 	Shared interrupt
+        Boot setup commands
+        Serial NVRAM:          Symbios and Tekram formats
 
 
 4. Memory mapped I/O versus normal I/O
 
-Memory mapped I/O has less latency than normal I/O.
-Since linux-1.3.x, memory mapped I/O is used rather than normal I/O.
-Memory mapped I/O seems to work fine on most hardware configurations, but some 
-poorly designed motherboards may break this feature.
-
-During the initialization phase, the driver first tries to use memory mapped
-I/O.  If nothing seems wrong, it will use memory mapped I/O.
-If a flaw is detected, it will use normal I/O.
+Memory mapped I/O has less latency than normal I/O.  Since
+linux-1.3.x, memory mapped I/O is used rather than normal I/O.  Memory
+mapped I/O seems to work fine on most hardware configurations, but
+some poorly designed motherboards may break this feature.
 
-However, it's possible that memory mapped I/O does not work properly and the 
-driver has not detected the problem.
-
-The configuration option CONFIG_SCSI_NCR53C8XX_IOMAPPED forces the 
+The configuration option CONFIG_SCSI_NCR53C8XX_IOMAPPED forces the
 driver to use normal I/O in all cases.
 
 
 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.
+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.
+
+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.
+
+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:
+
+- Each time a QUEUE FULL status is returned, tagged command queueing is 
+  temporarily disabled. 
+
+- 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.
 
 
 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:
+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)
@@ -145,16 +190,19 @@
 
 Profiling information is available through the proc SCSI file system.
 The device associated with a host has the following pathname:
+
           /proc/scsi/ncr53c8xx/N     (N=0,1,2 ....)
 
 Generally, only 1 board is used on hardware configuration, and that device is:
           /proc/scsi/ncr53c8xx/0
 
-However, if the driver has been made as module, the number of the hosts is 
-incremented each time the driver is loaded.
+However, if the driver has been made as module, the number of the
+hosts is incremented each time the driver is loaded.
 
 In order to display profiling information, just enter:
+
          cat /proc/scsi/ncr53c8xx/0
+
 and you will get something like the following text:
 
 -------------------------------------------------------
@@ -162,6 +210,7 @@
   Chip NCR53C810, device id 0x1, revision id 0x2
   IO port address 0x6000, IRQ number 10
   Using memory mapped IO at virtual address 0x282c000
+  Synchronous transfer period 25, max commands per lun 4
 Profiling information:
   num_trans    = 18014
   num_kbytes   = 671314
@@ -175,7 +224,7 @@
   ms_post      = 1320
 -------------------------------------------------------
 
-General information is easy to understand. The device ID and the 
+General information is easy to understand. The device ID and the
 revision ID identify the SCSI chip as follows:
 
 Chip    Device id     Revision Id
@@ -187,15 +236,18 @@
 860       0x6
 825A      0x3            >= 0x10
 875       0xf
+895       0xc
 
 The profiling information is updated upon completion of SCSI commands.
-A data structure is allocated and zeroed when the host adapter is 
-attached. So, if the driver is a module, the profile counters are cleared each 
-time the driver is loaded.
-The "clearprof" command allows you to clear these counters at any time.
+A data structure is allocated and zeroed when the host adapter is
+attached. So, if the driver is a module, the profile counters are
+cleared each time the driver is loaded.  The "clearprof" command
+allows you to clear these counters at any time.
 
 The following counters are available:
-("num" prefix means "number of", "ms" means milli-seconds)
+
+("num" prefix means "number of",
+"ms" means milli-seconds)
 
 num_trans
 	Number of completed commands
@@ -238,46 +290,43 @@
 	(time from SCSI status get to command completion call)
 	Example above: 1.32 seconds spent for post processing
 
-Due to the 1/100 second tick of the system clock, "ms_post" time may be 
-wrong.
+Due to the 1/100 second tick of the system clock, "ms_post" time may
+be wrong.
 
-In the example above, we got 18038 interrupts "on the fly" and only 1673 script 
-breaks probably due to disconnections inside a segment of the scatter list. 
-This is an excellent result due to the fact that the driver tries to use small 
-data segments (512) for the scatter list. The CPU load of this rescatter process 
-is acceptable. Unlike other SCSI processors, NCR53C8XX controllers do not need  
-large data chunks in order to get better performance, and it seems that it 
-is just the opposite.
-The scatter/gather algorithm of the middle SCSI driver is not optimal for 
-NCR SCSI processors and should be tunable according to host type.
-
-You can tune the "wished" segment size for the scatterlist by changing the 
-following "define" in the file ncr53c8xx.h.
-Use only power of 2 greater than 512 (1024, 2048 or 4096).
-
-SCSI_NCR_SEGMENT_SIZE		(default: 512)
+In the example above, we got 18038 interrupts "on the fly" and only
+1673 script breaks generally due to disconnections inside a segment 
+of the scatter list.
 
 
 8. Control commands
 
-Control commands can be sent to the driver with write operations to the 
-proc SCSI file system. The generic command syntax is the following:
+Control commands can be sent to the driver with write operations to
+the proc SCSI file system. The generic command syntax is the
+following:
 
       echo "<verb> <parameters>" >/proc/scsi/ncr53c8xx/0
       (assumes controller number is 0)
 
+Using "all" for "<target>" parameter with the commands below will
+apply to all targets of the SCSI chain (except the controller).
+
 Available commands:
 
-8.1 Set minimum synchronous period
+8.1 Set minimum synchronous period factor
 
-    setsync <target> <period>
+    setsync <target> <period factor>
 
     target:    target number
-    period:    minimum synchronous period in nano-seconds.
-               Maximum speed = 1000/(4*period) MB/second
+    period:    minimum synchronous period.
+               Maximum speed = 1000/(4*period factor) except for special
+               cases below.
 
     Specify a period of 255, to force asynchronous transfer mode.
 
+      10 means 25 nano-seconds synchronous period
+      11 means 30 nano-seconds synchronous period
+      12 means 50 nano-seconds synchronous period
+
 8.2 Set wide size
 
     setwide <target> <size>
@@ -292,7 +341,6 @@
     target:    target number
     tags:      number of concurrent tagged commands
                must not be greater than SCSI_NCR_MAX_TAGS (default: 4)
-               must not be lower that 1 (see: known problems)
 
 8.4 Set order type for tagged command
 
@@ -321,23 +369,63 @@
 	nego:    print information about SCSI negotiations
 	phase:   print information on script interruptions
 
+    Use "setdebug" with no argument to reset debug flags.
+
 
 8.6 Clear profile counters
 
     clearprof
 
-    The profile counters are automatically cleared when the amount of data 
-    transfered reaches 1000 GB in order to avoid overflow.
+    The profile counters are automatically cleared when the amount of
+    data transfered reaches 1000 GB in order to avoid overflow.
     The "clearprof" command allows you to clear these counters at any time.
 
 
+8.7 Set flag (no_sync)
+ 
+    setflag <target> <flag>
+
+    target:    target number
+
+    For the moment, only one flag is available:
+
+        no_sync:   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.
+    - setflag all
+      will allow disconnection for all devices on the SCSI bus.
+
+
+8.8 Debug error recovery
+
+    debug_error_recovery <error to trigger>
+
+    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.
+
 9. Configuration parameters
 
-If the firmware of all your devices is perfect enough, all the features 
-supported by the driver can be enabled at start-up.
-However, if only one has a flaw for some SCSI feature, you can disable the 
-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.
+If the firmware of all your devices is perfect enough, all the
+features supported by the driver can be enabled at start-up.  However,
+if only one has a flaw for some SCSI feature, you can disable the
+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_IOMAPPED       (default answer: n)
     Answer "y" if you suspect your mother board to not allow memory mapped I/O.
@@ -347,9 +435,16 @@
     Answer "y" if you are sure that all your SCSI devices that are able to 
     accept tagged commands will proceed safely.
 
-CONFIG_SCSI_NCR53C8XX_FORCE_ASYNCHRONOUS (default answer: n)
-    This option forces asynchronous transfer mode for all SCSI devices. 
- 
+CONFIG_SCSI_NCR53C8XX_MAX_TAGS         (default answer: 4)
+    This option allows you to specify the maximum number of tagged commands 
+    that can be queued to a device.
+
+CONFIG_SCSI_NCR53C8XX_SYNC            (default answer: 5)
+    This option allows you to specify the frequency in MHz the driver 
+    will use at boot time for synchronous data transfer negotiations.
+    This frequency can be changed later with the "setsync" control command.
+    0 means "asynchronous data transfers".
+
 CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO (default answer: n)
     Force synchronous negotiation for all SCSI-2 devices.
     Some SCSI-2 devices do not report this feature in byte 7 of inquiry 
@@ -360,12 +455,331 @@
     you can answer "y". Then, all SCSI devices will never disconnect the bus 
     even while performing long SCSI operations.
 
-
-10. Some constants and flags of the ncr53c8xx.h header files
-
-Some of these are defined from the configuration parameters.
-To change other "defines", you must edit the header file.
-Do that only if you know what you are doing.
+CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT
+    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.
+    This option must NOT be enabled if your system has at least one 53C8XX 
+    based scsi board with a vendor-specific BIOS.
+    For example, Tekram DC-390/U, DC-390/W and DC-390/F scsi controllers 
+    use a vendor-specific BIOS and are known to not use SYMBIOS compatible 
+    GPIO wiring. So, this option must not be enabled if your system has 
+    such a board installed.
+
+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).
+
+10. Boot setup commands
+
+10.1 Syntax
+
+Setup commands can be passed to the driver at boot time.
+A boot setup command for the ncr53c8xx driver begins with the driver name 
+"ncr53c8xx=". The kernel syntax parser then expects an optionnal list of
+integers separated with comma followed by an optionnal list of  comma- 
+separated strings. Example of boot setup command under lilo prompt:
+
+lilo: linux root=/dev/hda2 ncr53c8xx=tags:4,sync:10,debug:0x200
+
+- enable tagged commands, up to 4 tagged commands queued.
+- set synchronous negotiation speed to 10 Mega-transfers / second.
+- set DEBUG_NEGO flag.
+
+For the moment, the integer list of arguments is disgarded by the driver. 
+It will be used in the future in order to allow a per controller setup.
+
+Each string argument must be specified as "keyword:value". Only lower-case 
+characters and digits are allowed.
+
+10.2 Available arguments
+
+Master parity checking
+      mpar:y     enabled
+      mpar:n     disabled
+
+Scsi parity checking
+      spar:y     enabled
+      spar:n     disabled
+
+Scsi disconnections
+      disc:y     enabled
+      disc:n     disabled
+ 
+Special features
+   Only apply to 810A, 825A, 860 and 875 controllers.
+   Have no effect with normal 810 and 825.
+      specf:y    enabled
+      specf:n    disabled
+
+Ultra SCSI support
+   Only apply to 860 and 875 controllers.
+   Have no effect with other ones.
+      ultra:y    enabled
+      ultra:n    disabled
+
+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.
+
+Default synchronous period factor
+      sync:255     disabled (asynchronous transfer mode)
+      sync:#factor
+  #factor = 10     Ultra-2 SCSI 40 Mega-transfers / second
+  #factor = 11     Ultra-2 SCSI 33 Mega-transfers / second
+  #factor < 25     Ultra   SCSI 20 Mega-transfers / second
+  #factor < 50     Fast    SCSI-2
+
+  In all cases, the driver will use the minimum transfer period supported by 
+  controllers according to NCR53C8XX chip type.
+
+Negotiate synchronous with all devices
+  (force sync nego)
+      fsn:y      enabled
+      fsn:n      disabled
+
+Verbosity level
+      verb:0     minimal
+      verb:1     normal
+      verb:2     too much
+
+Debug mode
+      debug:0	 clear debug flags
+      debug:#x   set debug flags
+  #x is an integer value combining the following power-of-2 values:
+  DEBUG_ALLOC       0x1
+  DEBUG_PHASE       0x2
+  DEBUG_POLL        0x4
+  DEBUG_QUEUE       0x8
+  DEBUG_RESULT     0x10
+  DEBUG_SCATTER    0x20
+  DEBUG_SCRIPT     0x40
+  DEBUG_TINY       0x80
+  DEBUG_TIMING    0x100
+  DEBUG_NEGO      0x200
+  DEBUG_TAGS      0x400
+  DEBUG_FREEZE    0x800
+  DEBUG_RESTART  0x1000
+
+  You can play safely with DEBUG_NEGO. However, some of these flags may 
+  generate bunches of syslog messages. 
+
+Burst max
+      burst:0    burst disabled
+      burst:255  get burst length from initial IO register settings.
+      burst:#x   burst enabled (1<<#x burst transfers max)
+  #x is an integer value which is log base 2 of the burst transfers max.
+  The NCR53C875 and NCR53C825A support up to 128 burst transfers (#x = 7).
+  Other chips only support up to 16 (#x = 4).
+  This is a maximum value. The driver set the burst length according to chip 
+  and revision ids. By default the driver uses the maximum value supported 
+  by the chip.
+
+LED support
+     led:1      enable  LED support
+     led:0      disable LED support
+  Donnot enable LED support if your scsi board does not use SDMS BIOS.
+  (See 'Configuration parameters')
+
+Max wide
+    wide:1      wide scsi enabled
+    wide:0      wide scsi disabled
+  Some scsi boards use a 875 (ultra wide) and only supply narrow connectors.
+  If you have connected a wide device with a 50 pins to 68 pins cable 
+  converter, any accepted wide negotiation will break further data transfers.
+  In such a case, using "wide:0" in the bootup command will be helpfull. 
+
+Differential mode
+    diff:0	never set up diff mode
+    diff:1	set up diff mode if BIOS set it
+    diff:2	always set up diff mode
+    diff:3	set diff mode if GPIO3 is not set
+
+IRQ mode
+    irqm:0	always open drain
+    irqm:1	same as initial settings (assumed BIOS settings)
+    irqm:2	always totem pole
+
+Reverse probe
+    revprob:n   probe chip ids from the PCI configuration in this order:
+                810, 815, 820, 860, 875, 885, 895, 896
+    revprob:y   probe chip ids in the reverse order.
+
+Fix up PCI configuration space
+    pcifix:<option bits>
+
+    Available option bits:
+        0x1:   Set PCI cache-line size register if not set.
+        0x2:   Set write and invalidate bit in PCI command register.
+        0x4:   Increase if necessary PCI latency timer according to burst max.
+
+    Use 'pcifix:7' in order to allow the driver to fix up all PCI features.
+
+Serial NVRAM
+    nvram:n     do not look for serial NVRAM
+    nvram:y     test controllers for onboard serial NVRAM
+
+Boot fail safe
+    safe:y	load the following assumed fail safe initial setup
+
+  master parity			disabled		mpar:n
+  scsi parity			enabled			spar:y
+  disconnections		not allowed		disc:n
+  special features		disabled		specf:n
+  ultra scsi			disabled		ultra:n
+  force sync negotiation	disabled		fsn:n
+  reverse probe			disabled		revprob:n
+  PCI fix up                    disabled                pcifix:0
+  serial NVRAM                  enabled                 nvram:y
+  verbosity level		2			verb:2
+  tagged command queuing	disabled		tags:0
+  synchronous negotiation	disabled		sync:255
+  debug flags			none			debug:0
+  burst length			from BIOS settings	burst:255
+  LED support			disabled		led:0
+  wide support			disabled		wide:0
+  settle time			10 seconds		settle:10
+  differential support		from BIOS settings	diff:1
+  irq mode			from BIOS settings	irqm:1
+
+10.3 Advised boot setup commands
+
+If the driver has been configured with default options, the equivalent 
+boot setup is:
+
+   ncr53c8xx=mpar:y,spar:y,disc:y,specf:y,fsn:n,ultra:y,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,
+boot setup can be:
+
+    ncr53c8xx=safe:y,mpar:y,disc:y
+    ncr53c8xx=safe:y,disc:y
+    ncr53c8xx=safe:y,mpar:y
+    ncr53c8xx=safe:y
+
+My personnal system works flawlessly with the following equivalent setup:
+
+   ncr53c8xx=mpar:y,spar:y,disc:y,specf:y,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
+
+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" 
+to your boot setup command in order to check the actual setup the driver is 
+using.
+
+10.4 PCI configuration fix-up boot option
+
+pcifix:<option bits>
+
+Available option bits:
+    0x1:     Set PCI cache-line size register if not set.
+    0x2:     Set write and invalidate bit in PCI command register.
+
+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.
+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 
+space of the chips. On the other hand, chips will use PCI write and 
+invalidate commands only if the corresponding bit is set to 1 in the 
+PCI command register.
+
+Not all PCI bioses set the PCI cache line register and the PCI write and 
+invalidate bit in the PCI configuration space of 53C8XX chips.
+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.
+(MB Triton HX / 53C875 / 53C810A)
+I use these options at my own risks as you will do if you decide to 
+use them too.
+
+
+10.5 Serial NVRAM support boot option
+
+nvram:n     do not look for serial NVRAM
+nvram:y     test controllers for onboard serial NVRAM
+
+This option is described below (see 17. Serial NVRAM support).
+When this option is enabled, the driver tries to detect all boards using 
+a Serial NVRAM. This memory is used to hold user set up parameters.
+
+The parameters the driver is able to get from the NVRAM depend on the 
+data format used, as follow:
+
+                                 Tekram format      Symbios format
+General and host parameters
+    Boot order                         N                   Y
+    Host SCSI ID                       Y                   Y
+    SCSI parity checking               Y                   Y
+    Verbose boot messages              N                   Y
+SCSI devices parameters
+    Synchronous transfer speed         Y                   Y
+    Wide 16 / Narrow                   Y                   Y
+    Tagged Command Queuing enabled     Y                   Y
+    Disconnections enabled             Y                   Y
+    Scan at boot time                  N                   Y
+
+In order to speed up the system boot, for each device configured without 
+the "scan at boot time" option, the driver forces an error on the 
+first TEST UNIT READY command received for this device.
+
+Some SDMS BIOS revisions seem to be unable to boot cleanly with very fast 
+hard disks. In such a situation you cannot configure the NVRAM with 
+optimized parameters value.
+
+The 'nvram' boot option can be entered in hexadecimal form in order 
+to ignore some options configured in the NVRAM, as follow:
+
+mvram=<bits options>
+      0x01   look for NVRAM  (equivalent to nvram=y)
+      0x02   ignore NVRAM "Synchronous negotiation" parameters for all devices
+      0x04   ignore NVRAM "Wide negotiation"  parameter for all devices
+      0x08   ignore NVRAM "Scan at boot time" parameter for all devices
+
+My Atlas Wide only boots cleanly in 8 bits asynchronous data transfer 
+mode. However, it works flawlessly at 20 MB/second with the driver.
+Using "nvram=0x7" allows me to boot in 8 bits/async and to let the driver 
+use its setup for synchronous and wide negotiations.
+
+
+11. Some constants and flags of the ncr53c8xx.h header file
+
+Some of these are defined from the configuration parameters.  To
+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, 
+        read line, prefetch, cache line 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:
+		ncr53c8xx=specf:n
 
 SCSI_NCR_IOMAPPED		(default: not defined)
 	If defined, normal I/O is forced.
@@ -377,26 +791,31 @@
 	Maximum number of simultaneous tagged commands to a device.
 	Can be changed by "settags <target> <maxtags>"
 
+SCSI_NCR_SETUP_DEFAULT_SYNC     (default: 50)
+	Transfer period factor the driver will use at boot time for synchronous 
+	negotiation. 0 means asynchronous.
+	Can be changed by "setsync <target> <period factor>"
+
+SCSI_NCR_SETUP_DEFAULT_TAGS     (default: 4)
+	Default number of simultaneous tagged commands to a device.
+	< 1 means tagged command queuing disabled at start-up.
+
 SCSI_NCR_ALWAYS_SIMPLE_TAG	(default: defined)
 	Use SIMPLE TAG for read and write commands.
 	Can be changed by "setorder <ordered|simple|default>"
 
-SCSI_NCR_TAGGED_QUEUE_DISABLED	(default: defined)
-	If defined, tagged command queuing is disable at start-up.
-	Can be changed by "settags <target> <maxtags>"
-
-SCSI_NCR_NO_DISCONNECT		(default: not defined)
-	If defined, targets are not allowed to disconnect.
+SCSI_NCR_SETUP_DISCONNECTION	(default: defined)
+	If defined, targets are allowed to disconnect.
 
-SCSI_NCR_FORCE_SYNC_NEGO	(default: not defined)
+SCSI_NCR_SETUP_FORCE_SYNC_NEGO	(default: not defined)
 	If defined, synchronous negotiation is tried for all SCSI-2 devices.
 	Can be changed by "setsync <target> <period>"
 
-SCSI_NCR_DISABLE_MPARITY_CHECK	(default: not defined)
-	If defined, master parity checking is disabled.
+SCSI_NCR_SETUP_MASTER_PARITY	(default: defined)
+	If defined, master parity checking is enabled.
 
-SCSI_NCR_DISABLE_PARITY_CHECK	(default: not defined)
-	If defined, SCSI parity checking is disabled.
+SCSI_NCR_SETUP_MASTER_PARITY	(default: defined)
+	If defined, SCSI parity checking is enabled.
 
 SCSI_NCR_PROFILE		(default: defined)
 	If defined, profiling information is gathered.
@@ -404,10 +823,6 @@
 SCSI_NCR_MAX_SCATTER		(default: 128)
 	Scatter list size of the driver ccb.
 
-SCSI_NCR_SEGMENT_SIZE		(default: 512)
-	If defined, the driver will try to use scatter segments of this size.
-	If not defined, the Linux scatter list is used as is.
-
 SCSI_NCR_MAX_TARGET		(default: 16)
 	Max number of targets per host.
 
@@ -435,49 +850,38 @@
 	Max number of LUNs per target.
 
 
-11. Provided files
+12. Installation
+
+12.1 Provided files
 
 Driver and common 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
 
-Installation procedure 1 replacing the standard NCR53c7,8xx driver:
-
 	Install.ncr53c8xx	: installation script
-	Uninstall.ncr53c8xx	: uninstallation script
-	53c7,8xx.h		: included by hosts.c, override the standard driver
-
-	Patch-1.2.13.ncr53c8xx     : patch for linux-1.2.13
-	Patch-1.3.45-49.ncr53c8xx  : patch for linux-1.3.45 to linux-1.3.49
-	Patch-1.3.50-100.ncr53c8xx : patch for linux-1.3.50 to linux-1.3.100
-
-Installation procedure 2 adding the driver to the kernel tree:
 
-	Install2.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
 
-	Patch2-2.0.0-1.ncr53c8xx   : patch for linux-2.0.0 and linux-2.0.1
-	Patch2-Current.ncr53c8xx   : patch used for sub-levels > 1
+You must untar the distribution with the following command:
 
-Prior to installing the driver, you must untar the distribution, as follow:
+	tar zxvf ncrBsd2Linux-2.2b-src.tar.gz
 
-	mkdir ncrB2L
-	cd ncrB2L
-	tar zxvf ncrBsd2Linux-1.12a-src.tar.gz
+The sub-directory ncr53c8xx-2.2b will be created. Change to this directory.
 
 
-12. Installation procedure for Linux version 1
+12.2 Installation procedure
 
-This install script only supports linux-1.2.13 and linux-1.3.45 to 1.3.100.
+This install script has been tested with linux-1.2.13 and 2.0.29.
 
-This procedure just moves the standard driver files to SAVE_53 sub-directory 
-of linux/drivers/scsi, copies the drivers file to that directory and patches 
-the SCSI Makefile.
-The standard driver can be restored with Uninstall.ncr53c8xx
+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:
@@ -488,83 +892,546 @@
 
 	Make the kernel:
 		Change to linux source directory
-		Configure with 53c7,8xx support (Y or m)  for Install.ncr53c8xx
+		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'
 
-	Notes:
-		Since release 1.3.90, additionnal configuation parameters 
-		have been added for the standard NCR driver.
-		Just reply Y or N as you want to these questions;
-		The NCR53C8XX driver ignores those parameters.
-
-	If you prefer the standard NCR driver of Linux:
-	just enter:
-		Uninstall.ncr53c8xx
-	or
-		Uninstall.ncr53c8xx <your_linux_directory>
-	if your linux root is not /usr/src/linux.
-
-
-13. Installation procedure for Linux version 2
-
-This procedure adds the driver to the kernel tree.
-Using "make config" you can choose between the standard driver and the BSD one.
-It is possible to configure both drivers as modules and to switch from one 
-to the other at run time.
-Take care to unload the current driver module before loading the other one.
 
-	If your linux directory is at the standard location
-	"/usr/src/linux", just enter:
-		Install2.ncr53c8xx
+13. Control commands under linux-1.2.13
 
-	Else enter:
-		Install2.ncr53c8xx <your_linux_directory>
-
-	Make the kernel:
-		Change to linux source directory
-		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'
-
-
-14. 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.
+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.
 
 Tagged command queueing is disabled by default at system startup.
 
 You can enable tagged queue per device with the following command:
+
 	scsitag device_name (ex: scsitag /dev/sda)
 
 Use "cc -o scsitag scsitag.c" to create the "scsitag" executable.
 
 
-15. Known problems
+14. Known problems
 
-15.1 Tagged commands with Iomega Jaz device
+14.1 Tagged commands with Iomega Jaz device
 
-I have not tried this device, however it has been reported to me the following:
-This device is capable of Tagged command queuing. However while spinning up,
-it rejects Tagged commands. This behaviour is conforms to 6.8.2 of SCSI-2 
-specifications. The current behaviour of the driver in that situation is not 
-satisfying. So do not enable Tagged command queuing for devices that are able 
-to spin down.
-The other problem that may appear is timeouts. The only way to avoid timeouts 
-seems to edit linux/drivers/scsi/sd.c and to increase the current timeout 
-values.
-
-15.2 Tagged command queuing cannot be disabled at run time
-
-Once Tagged command queuing has been enabled, the driver will not allow you to 
-disable this feature ("settags <target> 0" is not supported).
-This problem is due to some limitations of the code added to the Linux version 
-of the driver.
+I have not tried this device, however it has been reported to me the
+following: This device is capable of Tagged command queuing. However
+while spinning up, it rejects Tagged commands. This behaviour is
+conforms to 6.8.2 of SCSI-2 specifications. The current behaviour of
+the driver in that situation is not satisfying. So do not enable
+Tagged command queuing for devices that are able to spin down.  The
+other problem that may appear is timeouts. The only way to avoid
+timeouts seems to edit linux/drivers/scsi/sd.c and to increase the
+current timeout values.
+
+14.2 Device names change when another controller is added.
+
+When you add a new NCR53C8XX chip based controller to a system that already 
+has one or more controllers of this family, it may happen that the order 
+the driver registers them to the kernel causes problems due to device 
+name changes.
+When at least one controller uses NvRAM, SDMS BIOS version 4 allows you to 
+define the order the BIOS will scan the scsi boards. The driver attaches 
+controllers according to BIOS information if NvRAM detect option is set.
+
+If your controllers do not have NvRAM, you can:
+
+- Ask the driver to probe chip ids in reverse order from the boot command
+  line: ncr53c8xx=revprob:y
+- Make appropriate changes in the fstab.
+- Use the 'scsidev' tool from Eric Youngdale.
+
+15. SCSI problem troubleshooting
+
+Most SCSI problems are due to a non conformant SCSI bus or to buggy
+devices.  If infortunately you have SCSI problems, you can check the
+following things:
+
+- SCSI bus cables
+- terminations at both end of the SCSI chain
+- linux syslog messages (some of them may help you)
+
+If you donnot find the source of problems, you can configure the
+driver with no features enabled.
+
+- only asynchronous data transfers
+- tagged commands disabled
+- disconnections not allowed
+
+Now, if your SCSI bus is ok, your system have every chance to work
+with this safe configuration but performances will not be optimal.
+
+If it still fails, then you can send your problem description to
+appropriate mailing lists or news-groups.  Send me a copy in order to
+be sure I will receive it.  Obviously, a bug in the driver code is
+possible.
+
+     My email address: Gerard Roudier <groudier@club-internet.fr>
+
+Allowing disconnections is important if you use several devices on
+your SCSI bus but often causes problems with buggy devices.
+Synchronous data transfers increases throughput of fast devices like
+hard disks.  Good SCSI hard disks with a large cache gain advantage of
+tagged commands queuing.
+
+Try to enable one feature at a time with control commands.  For example:
+
+- echo "setsync all 25" >/proc/scsi/ncr53c8xx/0
+  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 
+  the SCSI Bus.
+
+- echo "settags 3 4" >/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 
+disable that feature for that device.
+
+
+16. Synchonous transfer negotiation tables
+
+Tables below have been created by calling the routine the driver uses
+for synchronisation negotiation timing calculation and chip setting.
+The first table corresponds to Ultra chips 53875 and 53C860 with 80 MHz 
+clock and 5 clock divisors.
+The second one has been calculated by setting the scsi clock to 40 Mhz 
+and using 4 clock divisors and so applies to all NCR53C8XX chips in fast 
+SCSI-2 mode.
+
+Periods are in nano-seconds and speeds are in Mega-transfers per second.
+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
+
+ ----------------------------------------------
+ Negotiated                     NCR settings
+ Factor   Period   Speed        Period   Speed
+ ------   ------   ------       ------   ------
+ 12       50       20.000       50       20.000
+ 13       52       19.230       62       16.000
+ 14       56       17.857       62       16.000
+ 15       60       16.666       62       16.000
+ 16       64       15.625       75       13.333
+ 17       68       14.705       75       13.333
+ 18       72       13.888       75       13.333
+ 19       76       13.157       87       11.428
+ 20       80       12.500       87       11.428
+ 21       84       11.904       87       11.428
+ 22       88       11.363       93       10.666
+ 23       92       10.869       93       10.666
+ 24       96       10.416      100       10.000
+ 25      100       10.000      100       10.000
+ 26      104        9.615      112        8.888
+ 27      108        9.259      112        8.888
+ 28      112        8.928      112        8.888
+ 29      116        8.620      125        8.000
+ 30      120        8.333      125        8.000
+ 31      124        8.064      125        8.000
+ 32      128        7.812      131        7.619
+ 33      132        7.575      150        6.666
+ 34      136        7.352      150        6.666
+ 35      140        7.142      150        6.666
+ 36      144        6.944      150        6.666
+ 37      148        6.756      150        6.666
+ 38      152        6.578      175        5.714
+ 39      156        6.410      175        5.714
+ 40      160        6.250      175        5.714
+ 41      164        6.097      175        5.714
+ 42      168        5.952      175        5.714
+ 43      172        5.813      175        5.714
+ 44      176        5.681      187        5.333
+ 45      180        5.555      187        5.333
+ 46      184        5.434      187        5.333
+ 47      188        5.319      200        5.000
+ 48      192        5.208      200        5.000
+ 49      196        5.102      200        5.000
+
+
+16.2 Synchronous timings for fast SCSI-2 53C8XX controllers
+
+ ----------------------------------------------
+ Negotiated                     NCR settings
+ Factor   Period   Speed        Period   Speed
+ ------   ------   ------       ------   ------
+ 25      100       10.000      100       10.000
+ 26      104        9.615      125        8.000
+ 27      108        9.259      125        8.000
+ 28      112        8.928      125        8.000
+ 29      116        8.620      125        8.000
+ 30      120        8.333      125        8.000
+ 31      124        8.064      125        8.000
+ 32      128        7.812      131        7.619
+ 33      132        7.575      150        6.666
+ 34      136        7.352      150        6.666
+ 35      140        7.142      150        6.666
+ 36      144        6.944      150        6.666
+ 37      148        6.756      150        6.666
+ 38      152        6.578      175        5.714
+ 39      156        6.410      175        5.714
+ 40      160        6.250      175        5.714
+ 41      164        6.097      175        5.714
+ 42      168        5.952      175        5.714
+ 43      172        5.813      175        5.714
+ 44      176        5.681      187        5.333
+ 45      180        5.555      187        5.333
+ 46      184        5.434      187        5.333
+ 47      188        5.319      200        5.000
+ 48      192        5.208      200        5.000
+ 49      196        5.102      200        5.000
+
+
+17. Serial NVRAM (added by Richard Waltham: dormouse@farsrobt.demon.co.uk)
+
+17.1 Features
+
+Enabling serial NVRAM support enables detection of the serial NVRAM included
+on Symbios and some Symbios compatible host adaptors, and Tekram boards. The 
+serial NVRAM is used by Symbios and Tekram to hold set up parameters for the 
+host adaptor and it's attached drives.
+
+The Symbios NVRAM also holds data on the boot order of host adaptors in a
+system with more than one host adaptor. This enables the order of scanning
+the cards for drives to be changed from the default used during host adaptor
+detection.
+
+This can be done to a limited extent at the moment using "reverse probe" but
+this only changes the order of detection of different types of cards. The
+NVRAM boot order settings can do this as well as change the order the same
+types of cards are scanned in, something "reverse probe" cannot do.
+
+Tekram boards using Symbios chips, DC390W/F/U, which have NVRAM are detected
+and this is used to distinguish between Symbios compatible and Tekram host 
+adaptors. This is used to disable the Symbios compatible "diff" setting
+incorrectly set on Tekram boards if the CONFIG_SCSI_53C8XX_SYMBIOS_COMPAT 
+configuration parameter is set enabling both Symbios and Tekram boards to be 
+used together with the Symbios cards using all their features, including
+"diff" support. ("led pin" support for Symbios compatible cards can remain
+enabled when using Tekram cards. It does nothing useful for Tekram host
+adaptors but does not cause problems either.)
+
+
+17.2 Symbios NVRAM layout
+
+typical data at NVRAM address 0x100 (53c810a NVRAM)
+-----------------------------------------------------------
+00 00
+64 01
+8e 0b
+
+00 30 00 00 00 00 07 00 00 00 00 00 00 00 07 04 10 04 00 00 
+
+04 00 0f 00 00 10 00 50 00 00 01 00 00 62 
+04 00 03 00 00 10 00 58 00 00 01 00 00 63 
+04 00 01 00 00 10 00 48 00 00 01 00 00 61 
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 
+
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+
+fe fe
+00 00
+00 00
+-----------------------------------------------------------
+NVRAM layout details
+
+NVRAM Address 0x000-0x0ff not used
+              0x100-0x26f initialised data
+              0x270-0x7ff not used
+
+general layout
+
+        header  -   6 bytes,
+        data    - 356 bytes (checksum is byte sum of this data)
+        trailer -   6 bytes
+                  ---
+        total     368 bytes
+
+data area layout
+
+        controller set up  -  20 bytes
+        boot configuration -  56 bytes (4x14 bytes)
+        device set up      - 128 bytes (16x8 bytes)
+        unused (spare?)    - 152 bytes (19x8 bytes)
+                             ---
+        total                356 bytes
+
+-----------------------------------------------------------
+header
+
+00 00   - ?? start marker
+64 01   - byte count (lsb/msb excludes header/trailer)
+8e 0b   - checksum (lsb/msb excludes header/trailer)
+-----------------------------------------------------------
+controller set up
+
+00 30 00 00 00 00 07 00 00 00 00 00 00 00 07 04 10 04 00 00
+                   |     |                 |
+                   |     |                  -- host ID
+                   |      --flag bits 2
+                   |        0x00000001= scan order hi->low
+                   |            (default 0x00 - scan low->hi)
+                    --flag bits 1
+                       0x00000001 scam enable
+                       0x00000010 parity enable
+                       0x00000100 verbose boot msgs
+
+remaining bytes unknown - they do not appear to change in my
+current set up for any of the controllers.
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+boot configuration
+
+boot order set by order of the devices in this table
+
+04 00 0f 00 00 10 00 50 00 00 01 00 00 62 -- 1st controller
+04 00 03 00 00 10 00 58 00 00 01 00 00 63    2nd controller
+04 00 01 00 00 10 00 48 00 00 01 00 00 61    3rd controller
+00 00 00 00 00 00 00 00 00 00 00 00 00 00    4th controller
+       |  |  |  |     |        |     |  |
+       |  |  |  |     |        |      ---- PCI io port adr
+       |  |  |  |     |         --0x01 init/scan at boot time
+       |  |  |  |      --PCI device/function number (0xdddddfff)
+       |  |   ----- ?? PCI vendor ID (lsb/msb)
+        ----PCI device ID (lsb/msb)
+
+?? use of this data is a guess but seems reasonable
+
+remaining bytes unknown - they do not appear to change in my
+current set up
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+device set up (up to 16 devices - includes controller)
+
+0f 00 08 08 64 00 0a 00 - id 0
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 - id 15
+ |     |  |  |     |  |
+ |     |  |  |      ----timeout (lsb/msb)
+ |     |  |   --synch period (0x?? 40 Mtrans/sec- fast 40) (probably 0x28)
+ |     |  |                  (0x30 20 Mtrans/sec- fast 20)
+ |     |  |                  (0x64 10 Mtrans/sec- fast )
+ |     |  |                  (0xc8  5 Mtrans/sec)
+ |     |  |                  (0x00  asynchronous)
+ |     |   -- ?? max sync offset (0x08 in NVRAM on 53c810a) 
+ |     |                         (0x10 in NVRAM on 53c875)
+ |      --device bus width (0x08 narrow)
+ |                         (0x10 16 bit wide)
+  --flag bits
+    0x00000001 - disconnect enabled
+    0x00000010 - scan at boot time
+    0x00000100 - scan luns
+    0x00001000 - queue tags enabled
+
+remaining bytes unknown - they do not appear to change in my
+current set up
+
+?? use of this data is a guess but seems reasonable 
+(but it could be max bus width)
+
+default set up for 53c810a NVRAM
+default set up for 53c875 NVRAM - bus width     - 0x10
+                                - sync offset ? - 0x10
+                                - sync period   - 0x30
+-----------------------------------------------------------
+?? spare device space (32 bit bus ??)
+
+00 00 00 00 00 00 00 00  (19x8bytes)
+.
+.
+00 00 00 00 00 00 00 00
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+trailer
+
+fe fe   - ? end marker ?
+00 00
+00 00
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+
+
+
+17.3 Tekram NVRAM layout
+
+nvram 64x16 (1024 bit)
+
+Drive settings
+
+Drive ID 0-15 (addr 0x0yyyy0 = device setup, yyyy = ID)
+              (addr 0x0yyyy1 = 0x0000)
+
+    x x x x  x x x x  x x x x  x x x x
+               | | |      | |  | | | |
+               | | |      | |  | | |  ----- parity check   0 - off
+               | | |      | |  | | |                       1 - on
+               | | |      | |  | | |
+               | | |      | |  | |  ------- sync neg       0 - off
+               | | |      | |  | |                         1 - on
+               | | |      | |  | |
+               | | |      | |  |  --------- disconnect     0 - off
+               | | |      | |  |                           1 - on
+               | | |      | |  |
+               | | |      | |   ----------- start cmd      0 - off
+               | | |      | |                              1 - on
+               | | |      | |
+               | | |      |  -------------- tagged cmds    0 - off
+               | | |      |                                1 - on
+               | | |      | 
+               | | |       ---------------- wide neg       0 - off
+               | | |                                       1 - on
+               | | |
+                --------------------------- sync rate      0 - 10.0 Mtrans/sec
+                                                           1 -  8.0
+                                                           2 -  6.6
+                                                           3 -  5.7
+                                                           4 -  5.0
+                                                           5 -  4.0
+                                                           6 -  3.0
+                                                           7 -  2.0
+                                                           7 -  2.0
+                                                           8 - 20.0
+                                                           9 - 16.7
+                                                           a - 13.9
+                                                           b - 11.9
+
+Global settings
+
+Host flags 0 (addr 0x100000, 32) 
+
+    x x x x  x x x x  x x x x  x x x x
+    | | | |  | | | |           | | | |
+    | | | |  | | | |            ----------- host ID    0x00 - 0x0f
+    | | | |  | | | |
+    | | | |  | | |  ----------------------- support for    0 - off
+    | | | |  | | |                          > 2 drives     1 - on
+    | | | |  | | | 
+    | | | |  | |  ------------------------- support drives 0 - off
+    | | | |  | |                            > 1Gbytes      1 - on
+    | | | |  | |
+    | | | |  |  --------------------------- bus reset on   0 - off
+    | | | |  |                                power on     1 - on
+    | | | |  |
+    | | | |   ----------------------------- active neg     0 - off
+    | | | |                                                1 - on
+    | | | |
+    | | |  -------------------------------- imm seek       0 - off
+    | | |                                                  1 - on
+    | | |
+    | |  ---------------------------------- scan luns      0 - off
+    | |                                                    1 - on
+    | |
+     -------------------------------------- removable      0 - disable
+                                            as BIOS dev    1 - boot device
+                                                           2 - all
+
+Host flags 1 (addr 0x100001, 33)
+
+    x x x x  x x x x  x x x x  x x x x
+               | | |             | | | 
+               | | |              --------- boot delay     0 -   3 sec
+               | | |                                       1 -   5
+               | | |                                       2 -  10
+               | | |                                       3 -  20
+               | | |                                       4 -  30
+               | | |                                       5 -  60
+               | | |                                       6 - 120
+               | | |
+                --------------------------- max tag cmds   0 -  2
+                                                           1 -  4
+                                                           2 -  8
+                                                           3 - 16
+                                                           4 - 32
+
+Host flags 2 (addr 0x100010, 34)
+
+    x x x x  x x x x  x x x x  x x x x
+                                     |
+                                      ----- F2/F6 enable   0 - off ???
+                                                           1 - on  ???
+
+checksum (addr 0x111111)
+
+checksum = 0x1234 - (sum addr 0-63)
+
+----------------------------------------------------------------------------
+
+default nvram data:
+
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 
+
+0x0f07 0x0400 0x0001 0x0000 0x0000 0x0000 0x0000 0x0000
+0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
+0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
+0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0xfbbc
 
 ===============================================================================
 End of NCR53C8XX driver README file
+
+
+
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx/aic7xxx.reg linux/drivers/scsi/aic7xxx/aic7xxx.reg
--- v2.0.30/linux/drivers/scsi/aic7xxx/aic7xxx.reg	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aic7xxx.reg	Wed Aug 13 09:53:45 1997
@@ -0,0 +1,1135 @@
+/*
+ * Aic7xxx register and scratch ram definitions.
+ *
+ * Copyright (c) 1994-1997 Justin Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	$Id: aic7xxx.reg,v 1.4 1997/06/27 19:38:39 gibbs Exp $
+ */
+
+/*
+ * This file is processed by the aic7xxx_asm utility for use in assembling
+ * firmware for the aic7xxx family of SCSI host adapters as well as to generate
+ * a C header file for use in the kernel portion of the Aic7xxx driver.
+ *
+ * All page numbers refer to the Adaptec AIC-7770 Data Book available from
+ * Adaptec's Technical Documents Department 1-800-934-2766
+ */
+
+/*
+ * SCSI Sequence Control (p. 3-11).
+ * Each bit, when set starts a specific SCSI sequence on the bus
+ */
+register SCSISEQ {
+	address			0x000
+	access_mode RW
+	bit	TEMODE		0x80
+	bit	ENSELO		0x40
+	bit	ENSELI		0x20
+	bit	ENRSELI		0x10
+	bit	ENAUTOATNO	0x08
+	bit	ENAUTOATNI	0x04
+	bit	ENAUTOATNP	0x02
+	bit	SCSIRSTO	0x01
+}
+
+/*
+ * SCSI Transfer Control 0 Register (pp. 3-13).
+ * Controls the SCSI module data path.
+ */
+register SXFRCTL0 {
+	address			0x001
+	access_mode RW
+	bit	DFON		0x80
+	bit	DFPEXP		0x40
+	bit	FAST20		0x20
+	bit	CLRSTCNT	0x10
+	bit	SPIOEN		0x08
+	bit	SCAMEN		0x04
+	bit	CLRCHN		0x02
+}
+
+/*
+ * SCSI Transfer Control 1 Register (pp. 3-14,15).
+ * Controls the SCSI module data path.
+ */
+register SXFRCTL1 {
+	address			0x002
+	access_mode RW
+	bit	BITBUCKET	0x80
+	bit	SWRAPEN		0x40
+	bit	ENSPCHK		0x20
+	mask	STIMESEL	0x18
+	bit	ENSTIMER	0x04
+	bit	ACTNEGEN	0x02
+	bit	STPWEN		0x01	/* Powered Termination */
+}
+
+/*
+ * SCSI Control Signal Read Register (p. 3-15).
+ * Reads the actual state of the SCSI bus pins
+ */
+register SCSISIGI {
+	address			0x003
+	access_mode RO
+	bit	CDI		0x80
+	bit	IOI		0x40
+	bit	MSGI		0x20
+	bit	ATNI		0x10
+	bit	SELI		0x08
+	bit	BSYI		0x04
+	bit	REQI		0x02
+	bit	ACKI		0x01
+/*
+ * Possible phases in SCSISIGI
+ */
+	mask	PHASE_MASK	CDI|IOI|MSGI
+	mask	P_DATAOUT	0x00
+	mask	P_DATAIN	IOI
+	mask	P_COMMAND	CDI
+	mask	P_MESGOUT	CDI|MSGI
+	mask	P_STATUS	CDI|IOI
+	mask	P_MESGIN	CDI|IOI|MSGI
+}
+
+/*
+ * SCSI Control Signal Write Register (p. 3-16).
+ * Writing to this register modifies the control signals on the bus.  Only
+ * those signals that are allowed in the current mode (Initiator/Target) are
+ * asserted.
+ */
+register SCSISIGO {
+	address			0x003
+	access_mode WO
+	bit	CDO		0x80
+	bit	IOO		0x40
+	bit	MSGO		0x20
+	bit	ATNO		0x10
+	bit	SELO		0x08
+	bit	BSYO		0x04
+	bit	REQO		0x02
+	bit	ACKO		0x01
+/*
+ * Possible phases to write into SCSISIG0
+ */
+	mask	PHASE_MASK	CDI|IOI|MSGI
+	mask	P_DATAOUT	0x00
+	mask	P_DATAIN	IOI
+	mask	P_COMMAND	CDI
+	mask	P_MESGOUT	CDI|MSGI
+	mask	P_STATUS	CDI|IOI
+	mask	P_MESGIN	CDI|IOI|MSGI
+}
+
+/* 
+ * SCSI Rate Control (p. 3-17).
+ * Contents of this register determine the Synchronous SCSI data transfer
+ * rate and the maximum synchronous Req/Ack offset.  An offset of 0 in the
+ * SOFS (3:0) bits disables synchronous data transfers.  Any offset value
+ * greater than 0 enables synchronous transfers.
+ */
+register SCSIRATE {
+	address			0x004
+	access_mode RW
+	bit	WIDEXFER	0x80		/* Wide transfer control */
+	mask	SXFR		0x70		/* Sync transfer rate */
+	mask	SOFS		0x0f		/* Sync offset */
+}
+
+/*
+ * SCSI ID (p. 3-18).
+ * Contains the ID of the board and the current target on the
+ * selected channel.
+ */
+register SCSIID	{
+	address			0x005
+	access_mode RW
+	mask	TID		0xf0		/* Target ID mask */
+	mask	OID		0x0f		/* Our ID mask */
+}
+
+/*
+ * SCSI Latched Data (p. 3-19).
+ * Read/Write latches used to transfer data on the SCSI bus during
+ * Automatic or Manual PIO mode.  SCSIDATH can be used for the
+ * upper byte of a 16bit wide asynchronouse data phase transfer.
+ */
+register SCSIDATL {
+	address			0x006
+	access_mode RW
+}
+
+register SCSIDATH {
+	address			0x007
+	access_mode RW
+}
+
+/*
+ * SCSI Transfer Count (pp. 3-19,20)
+ * These registers count down the number of bytes transferred
+ * across the SCSI bus.  The counter is decremented only once
+ * the data has been safely transferred.  SDONE in SSTAT0 is
+ * set when STCNT goes to 0
+ */ 
+register STCNT {
+	address			0x008
+	size	3
+	access_mode RW
+}
+
+/*
+ * Clear SCSI Interrupt 0 (p. 3-20)
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0.
+ */
+register CLRSINT0 {
+	address			0x00b
+	access_mode WO
+	bit	CLRSELDO	0x40
+	bit	CLRSELDI	0x20
+	bit	CLRSELINGO	0x10
+	bit	CLRSWRAP	0x08
+	bit	CLRSPIORDY	0x02
+}
+
+/*
+ * SCSI Status 0 (p. 3-21)
+ * Contains one set of SCSI Interrupt codes
+ * These are most likely of interest to the sequencer
+ */
+register SSTAT0	{
+	address			0x00b
+	access_mode RO
+	bit	TARGET		0x80		/* Board acting as target */
+	bit	SELDO		0x40		/* Selection Done */
+	bit	SELDI		0x20		/* Board has been selected */
+	bit	SELINGO		0x10		/* Selection In Progress */
+	bit	SWRAP		0x08		/* 24bit counter wrap */
+	bit	SDONE		0x04		/* STCNT = 0x000000 */
+	bit	SPIORDY		0x02		/* SCSI PIO Ready */
+	bit	DMADONE		0x01		/* DMA transfer completed */
+}
+
+/*
+ * Clear SCSI Interrupt 1 (p. 3-23)
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1.
+ */
+register CLRSINT1 {
+	address			0x00c
+	access_mode WO
+	bit	CLRSELTIMEO	0x80
+	bit	CLRATNO		0x40
+	bit	CLRSCSIRSTI	0x20
+	bit	CLRBUSFREE	0x08
+	bit	CLRSCSIPERR	0x04
+	bit	CLRPHASECHG	0x02
+	bit	CLRREQINIT	0x01
+}
+
+/*
+ * SCSI Status 1 (p. 3-24)
+ */
+register SSTAT1	{
+	address			0x00c
+	access_mode RO
+	bit	SELTO		0x80
+	bit	ATNTARG 	0x40
+	bit	SCSIRSTI	0x20
+	bit	PHASEMIS	0x10
+	bit	BUSFREE		0x08
+	bit	SCSIPERR	0x04
+	bit	PHASECHG	0x02
+	bit	REQINIT		0x01
+}
+
+/*
+ * SCSI Status 2 (pp. 3-25,26)
+ */
+register SSTAT2 {
+	address			0x00d
+	access_mode RO
+	bit	OVERRUN		0x80
+	mask	SFCNT		0x1f
+}
+
+/*
+ * SCSI Status 3 (p. 3-26)
+ */
+register SSTAT3 {
+	address			0x00e
+	access_mode RO
+	mask	SCSICNT		0xf0
+	mask	OFFCNT		0x0f
+}
+
+/*
+ * SCSI Test Control (p. 3-27)
+ */
+register SCSITEST {
+	address			0x00f
+	access_mode RW
+	bit	RQAKCNT		0x04
+	bit	CNTRTEST	0x02
+	bit	CMODE		0x01
+}
+
+/*
+ * SCSI Interrupt Mode 1 (p. 3-28)
+ * Setting any bit will enable the corresponding function
+ * in SIMODE0 to interrupt via the IRQ pin.
+ */
+register SIMODE0 {
+	address			0x010
+	access_mode RW
+	bit	ENSELDO		0x40
+	bit	ENSELDI		0x20
+	bit	ENSELINGO	0x10
+	bit	ENSWRAP		0x08
+	bit	ENSDONE		0x04
+	bit	ENSPIORDY	0x02
+	bit	ENDMADONE	0x01
+}
+
+/*
+ * SCSI Interrupt Mode 1 (pp. 3-28,29)
+ * Setting any bit will enable the corresponding function
+ * in SIMODE1 to interrupt via the IRQ pin.
+ */
+register SIMODE1 {
+	address			0x011
+	access_mode RW
+	bit	ENSELTIMO	0x80
+	bit	ENATNTARG	0x40
+	bit	ENSCSIRST	0x20
+	bit	ENPHASEMIS	0x10
+	bit	ENBUSFREE	0x08
+	bit	ENSCSIPERR	0x04
+	bit	ENPHASECHG	0x02
+	bit	ENREQINIT	0x01
+}
+
+/*
+ * SCSI Data Bus (High) (p. 3-29)
+ * This register reads data on the SCSI Data bus directly.
+ */
+register SCSIBUSL {
+	address			0x012
+	access_mode RO
+}
+
+register SCSIBUSH {
+	address			0x013
+	access_mode RO
+}
+
+/*
+ * SCSI/Host Address (p. 3-30)
+ * These registers hold the host address for the byte about to be
+ * transferred on the SCSI bus.  They are counted up in the same
+ * manner as STCNT is counted down.  SHADDR should always be used
+ * to determine the address of the last byte transferred since HADDR
+ * can be skewed by write ahead.
+ */
+register SHADDR {
+	address			0x014
+	size	4
+	access_mode RO
+}
+
+/*
+ * Selection Timeout Timer (p. 3-30)
+ */
+register SELTIMER {
+	address			0x018
+	access_mode RW
+	bit	STAGE6		0x20
+	bit	STAGE5		0x10
+	bit	STAGE4		0x08
+	bit	STAGE3		0x04
+	bit	STAGE2		0x02
+	bit	STAGE1		0x01
+}
+
+/*
+ * Selection/Reselection ID (p. 3-31)
+ * Upper four bits are the device id.  The ONEBIT is set when the re/selecting
+ * device did not set its own ID.
+ */
+register SELID {
+	address			0x019
+	access_mode RW
+	mask	SELID_MASK	0xf0
+	bit	ONEBIT		0x08
+}
+
+/*
+ * SCSI Block Control (p. 3-32)
+ * Controls Bus type and channel selection.  In a twin channel configuration
+ * addresses 0x00-0x1e are gated to the appropriate channel based on this
+ * register.  SELWIDE allows for the coexistence of 8bit and 16bit devices
+ * on a wide bus.
+ */
+register SBLKCTL {
+	address			0x01f
+	access_mode RW
+	bit	DIAGLEDEN	0x80	/* Aic78X0 only */
+	bit	DIAGLEDON	0x40	/* Aic78X0 only */
+	bit	AUTOFLUSHDIS	0x20
+	bit	SELBUSB		0x08
+	bit	SELWIDE		0x02
+}
+
+/*
+ * Sequencer Control (p. 3-33)
+ * Error detection mode and speed configuration
+ */
+register SEQCTL {
+	address			0x060
+	access_mode RW
+	bit	PERRORDIS	0x80
+	bit	PAUSEDIS	0x40
+	bit	FAILDIS		0x20
+	bit	FASTMODE	0x10
+	bit	BRKADRINTEN	0x08
+	bit	STEP		0x04
+	bit	SEQRESET	0x02
+	bit	LOADRAM		0x01
+}
+
+/*
+ * Sequencer RAM Data (p. 3-34)
+ * Single byte window into the Scratch Ram area starting at the address
+ * specified by SEQADDR0 and SEQADDR1.  To write a full word, simply write
+ * four bytes in sucessesion.  The SEQADDRs will increment after the most
+ * significant byte is written
+ */
+register SEQRAM {
+	address			0x061
+	access_mode RW
+}
+
+/*
+ * Sequencer Address Registers (p. 3-35)
+ * Only the first bit of SEQADDR1 holds addressing information
+ */
+register SEQADDR0 {
+	address			0x062
+	access_mode RW
+}
+
+register SEQADDR1 {
+	address			0x063
+	access_mode RW
+	mask	SEQADDR1_MASK	0x01
+}
+
+/*
+ * Accumulator
+ * We cheat by passing arguments in the Accumulator up to the kernel driver
+ */
+register ACCUM {
+	address			0x064
+	access_mode RW
+	accumulator
+}
+
+register SINDEX	{
+	address			0x065
+	access_mode RW
+	sindex
+}
+
+register DINDEX {
+	address			0x066
+	access_mode RW
+}
+
+register ALLONES {
+	address			0x069
+	access_mode RO
+	allones
+}
+
+register ALLZEROS {
+	address			0x06a
+	access_mode RO
+	allzeros
+}
+
+register NONE {
+	address			0x06a
+	access_mode WO
+	none
+}
+
+register FLAGS {
+	address			0x06b
+	access_mode RO
+	bit	ZERO		0x02
+	bit	CARRY		0x01
+}
+
+register SINDIR	{
+	address			0x06c
+	access_mode RO
+}
+
+register DINDIR	 {
+	address			0x06d
+	access_mode WO
+}
+
+register FUNCTION1 {
+	address			0x06e
+	access_mode RW
+}
+
+register STACK {
+	address			0x06f
+	access_mode RO
+}
+
+/*
+ * Board Control (p. 3-43)
+ */
+register BCTL {
+	address			0x084
+	access_mode RW
+	bit	ACE		0x08
+	bit	ENABLE		0x01
+}
+
+/*
+ * On the aic78X0 chips, Board Control is replaced by the DSCommand
+ * register (p. 4-64)
+ */
+register DSCOMMAND {
+	address			0x084
+	access_mode RW
+	bit	CACHETHEN	0x80	/* Cache Threshold enable */
+	bit	DPARCKEN	0x40	/* Data Parity Check Enable */
+	bit	MPARCKEN	0x20	/* Memory Parity Check Enable */
+	bit	EXTREQLCK	0x10	/* External Request Lock */
+}
+
+/*
+ * Bus On/Off Time (p. 3-44)
+ */
+register BUSTIME {
+	address			0x085
+	access_mode RW
+	mask	BOFF		0xf0
+	mask	BON		0x0f
+}
+
+/*
+ * Bus Speed (p. 3-45)
+ */
+register BUSSPD {
+	address			0x086
+	access_mode RW
+	mask	DFTHRSH		0xc0
+	mask	STBOFF		0x38
+	mask	STBON		0x07
+	mask	DFTHRSH_100	0xc0
+}
+
+/*
+ * Host Control (p. 3-47) R/W
+ * Overall host control of the device.
+ */
+register HCNTRL {
+	address			0x087
+	access_mode RW
+	bit	POWRDN		0x40
+	bit	SWINT		0x10
+	bit	IRQMS		0x08
+	bit	PAUSE		0x04
+	bit	INTEN		0x02
+	bit	CHIPRST		0x01
+	bit	CHIPRSTACK	0x01
+}
+
+/*
+ * Host Address (p. 3-48)
+ * This register contains the address of the byte about
+ * to be transferred across the host bus.
+ */
+register HADDR {
+	address			0x088
+	size	4
+	access_mode RW
+}
+
+register HCNT {
+	address			0x08c
+	size	3
+	access_mode RW
+}
+
+/*
+ * SCB Pointer (p. 3-49)
+ * Gate one of the four SCBs into the SCBARRAY window.
+ */
+register SCBPTR {
+	address			0x090
+	access_mode RW
+}
+
+/*
+ * Interrupt Status (p. 3-50)
+ * Status for system interrupts
+ */
+register INTSTAT {
+	address			0x091
+	access_mode RW
+	bit	BRKADRINT 0x08
+	bit	SCSIINT	  0x04
+	bit	CMDCMPLT  0x02
+	bit	SEQINT    0x01
+	mask	BAD_PHASE	SEQINT		/* unknown scsi bus phase */
+	mask	SEND_REJECT	0x10|SEQINT	/* sending a message reject */
+	mask	NO_IDENT	0x20|SEQINT	/* no IDENTIFY after reconnect*/
+	mask	NO_MATCH	0x30|SEQINT	/* no cmd match for reconnect */
+	mask	EXTENDED_MSG	0x40|SEQINT	/* Extended message received */
+	mask	NO_MATCH_BUSY	0x50|SEQINT	/* Couldn't find BUSY SCB */
+	mask	REJECT_MSG	0x60|SEQINT	/* Reject message received */
+	mask	BAD_STATUS	0x70|SEQINT	/* Bad status from target */
+	mask	RESIDUAL	0x80|SEQINT	/* Residual byte count != 0 */
+	mask	ABORT_CMDCMPLT	0x91		/*
+						 * Command tagged for abort
+						 * completed successfully.
+						 */
+	mask	AWAITING_MSG	0xa0|SEQINT	/*
+						 * Kernel requested to specify
+                                                 * a message to this target
+                                                 * (command was null), so tell
+                                                 * it that it can fill the
+                                                 * message buffer.
+                                                 */
+	mask	MSG_BUFFER_BUSY	0xc0|SEQINT	/*
+						 * Sequencer wants to use the
+						 * message buffer, but it
+						 * already contains a message
+						 */
+	mask	MSGIN_PHASEMIS	0xd0|SEQINT	/*
+						 * Target changed phase on us
+						 * when we were expecting
+						 * another msgin byte.
+						 */
+	mask	DATA_OVERRUN	0xe0|SEQINT	/*
+						 * Target attempted to write
+						 * beyond the bounds of its
+						 * command.
+						 */
+
+	mask	SEQINT_MASK	0xf0|SEQINT	/* SEQINT Status Codes */
+	mask	INT_PEND  (BRKADRINT|SEQINT|SCSIINT|CMDCMPLT)
+}
+
+/*
+ * Hard Error (p. 3-53)
+ * Reporting of catastrophic errors.  You usually cannot recover from
+ * these without a full board reset.
+ */
+register ERROR {
+	address			0x092
+	access_mode RO
+	bit	PARERR		0x08
+	bit	ILLOPCODE	0x04
+	bit	ILLSADDR	0x02
+	bit	ILLHADDR	0x01
+}
+
+/*
+ * Clear Interrupt Status (p. 3-52)
+ */
+register CLRINT {
+	address			0x092
+	access_mode WO
+	bit	CLRBRKADRINT	0x08
+	bit	CLRSCSIINT      0x04
+	bit	CLRCMDINT 	0x02
+	bit	CLRSEQINT 	0x01
+}
+
+register DFCNTRL {
+	address			0x093
+	access_mode RW
+	bit	WIDEODD		0x40
+	bit	SCSIEN		0x20
+	bit	SDMAEN		0x10
+	bit	SDMAENACK	0x10
+	bit	HDMAEN		0x08
+	bit	HDMAENACK	0x08
+	bit	DIRECTION	0x04
+	bit	FIFOFLUSH	0x02
+	bit	FIFORESET	0x01
+}
+
+register DFSTATUS {
+	address			0x094
+	access_mode RO
+	bit	DWORDEMP	0x20
+	bit	MREQPEND	0x10
+	bit	HDONE		0x08
+	bit	DFTHRESH	0x04
+	bit	FIFOFULL	0x02
+	bit	FIFOEMP		0x01
+}
+
+register DFDAT {
+	address			0x099
+	access_mode RW
+}
+
+/*
+ * SCB Auto Increment (p. 3-59)
+ * Byte offset into the SCB Array and an optional bit to allow auto
+ * incrementing of the address during download and upload operations
+ */
+register SCBCNT {
+	address			0x09a
+	access_mode RW
+	bit	SCBAUTO		0x80
+	mask	SCBCNT_MASK	0x1f
+}
+
+/*
+ * Queue In FIFO (p. 3-60)
+ * Input queue for queued SCBs (commands that the seqencer has yet to start)
+ */
+register QINFIFO {
+	address			0x09b
+	access_mode RW
+}
+
+/*
+ * Queue In Count (p. 3-60)
+ * Number of queued SCBs
+ */
+register QINCNT	{
+	address			0x09c
+	access_mode RO
+}
+
+/*
+ * Queue Out FIFO (p. 3-61)
+ * Queue of SCBs that have completed and await the host
+ */
+register QOUTFIFO {
+	address			0x09d
+	access_mode WO
+}
+
+/*
+ * Queue Out Count (p. 3-61)
+ * Number of queued SCBs in the Out FIFO
+ */
+register QOUTCNT {
+	address			0x09e
+	access_mode RO
+}
+
+/*
+ * SCB Definition (p. 5-4)
+ */
+scb {
+	address			0x0a0
+	SCB_CONTROL {
+		size	1
+		bit	MK_MESSAGE      0x80
+		bit	DISCENB         0x40
+		bit	TAG_ENB		0x20
+		bit	MUST_DMAUP_SCB	0x10
+		bit	ABORT_SCB	0x08
+		bit	DISCONNECTED	0x04
+		mask	SCB_TAG_TYPE	0x03
+	}
+	SCB_TCL {
+		size	1
+		bit	SELBUSB		0x08
+		mask	TID		0xf0
+		mask	LID		0x07
+	}
+	SCB_TARGET_STATUS {
+		size	1
+	}
+	SCB_SGCOUNT {
+		size	1
+	}
+	SCB_SGPTR {
+		size	4
+	}
+	SCB_RESID_SGCNT {
+		size	1
+	}
+	SCB_RESID_DCNT	{
+		size	3
+	}
+	SCB_DATAPTR {
+		size	4
+	}
+	SCB_DATACNT {
+		size	3
+	}
+	SCB_LINKED_NEXT {
+		size	1
+	}
+	SCB_CMDPTR {
+		size	4
+	}
+	SCB_CMDLEN {
+		size	1
+	}
+	SCB_TAG {
+		size	1
+	}
+	SCB_NEXT {
+		size	1
+	}
+	SCB_PREV {
+		size	1
+	}
+	SCB_BUSYTARGETS {
+		size	4
+	}
+}
+
+const	SG_SIZEOF	0x08		/* sizeof(struct ahc_dma) */
+
+/* --------------------- AHA-2840-only definitions -------------------- */
+
+register SEECTL_2840 {
+	address			0x0c0
+	access_mode RW
+	bit	CS_2840		0x04
+	bit	CK_2840		0x02
+	bit	DO_2840		0x01
+}
+
+register STATUS_2840 {
+	address			0x0c1
+	access_mode RW
+	bit	EEPROM_TF	0x80
+	mask	BIOS_SEL	0x60
+	mask	ADSEL		0x1e
+	bit	DI_2840		0x01
+}
+
+/* --------------------- AIC-7870-only definitions -------------------- */
+
+register DSPCISTATUS {
+	address			0x086
+}
+
+register BRDCTL	{
+	address			0x01d
+	bit	BRDDAT7		0x80
+	bit	BRDDAT6		0x40
+	bit	BRDDAT5		0x20
+	bit	BRDSTB		0x10
+	bit	BRDCS		0x08
+	bit	BRDRW		0x04
+	bit	BRDCTL1		0x02
+	bit	BRDCTL0		0x01
+}
+
+/*
+ * Serial EEPROM Control (p. 4-92 in 7870 Databook)
+ * Controls the reading and writing of an external serial 1-bit
+ * EEPROM Device.  In order to access the serial EEPROM, you must
+ * first set the SEEMS bit that generates a request to the memory
+ * port for access to the serial EEPROM device.  When the memory
+ * port is not busy servicing another request, it reconfigures
+ * to allow access to the serial EEPROM.  When this happens, SEERDY
+ * gets set high to verify that the memory port access has been
+ * granted.  
+ *
+ * After successful arbitration for the memory port, the SEECS bit of 
+ * the SEECTL register is connected to the chip select.  The SEECK, 
+ * SEEDO, and SEEDI are connected to the clock, data out, and data in 
+ * lines respectively.  The SEERDY bit of SEECTL is useful in that it 
+ * gives us an 800 nsec timer.  After a write to the SEECTL register, 
+ * the SEERDY goes high 800 nsec later.  The one exception to this is 
+ * when we first request access to the memory port.  The SEERDY goes 
+ * high to signify that access has been granted and, for this case, has 
+ * no implied timing.
+ *
+ * See 93cx6.c for detailed information on the protocol necessary to 
+ * read the serial EEPROM.
+ */
+register SEECTL {
+	address			0x01e
+	bit	EXTARBACK	0x80
+	bit	EXTARBREQ	0x40
+	bit	SEEMS		0x20
+	bit	SEERDY		0x10
+	bit	SEECS		0x08
+	bit	SEECK		0x04
+	bit	SEEDO		0x02
+	bit	SEEDI		0x01
+}
+/* ---------------------- Scratch RAM Offsets ------------------------- */
+/* These offsets are either to values that are initialized by the board's
+ * BIOS or are specified by the sequencer code.
+ *
+ * The host adapter card (at least the BIOS) uses 20-2f for SCSI
+ * device information, 32-33 and 5a-5f as well. As it turns out, the
+ * BIOS trashes 20-2f, writing the synchronous negotiation results
+ * on top of the BIOS values, so we re-use those for our per-target
+ * scratchspace (actually a value that can be copied directly into
+ * SCSIRATE).  The kernel driver will enable synchronous negotiation
+ * for all targets that have a value other than 0 in the lower four
+ * bits of the target scratch space.  This should work regardless of
+ * whether the bios has been installed.
+ */
+
+scratch_ram {
+	address			0x020
+
+	/*
+	 * 1 byte per target starting at this address for configuration values
+	 */
+	TARG_SCRATCH {
+		size		16
+	}
+	ULTRA_ENB {
+		size		2
+	}
+	/*
+	 * Bit vector of targets that have disconnection disabled.
+	 */
+	DISC_DSB {
+		size		2
+	}
+	/*
+	 * Length of pending message
+	 */
+	MSG_LEN	{
+		size		1
+	}
+	/* We reserve 8bytes to store outgoing messages */
+	MSG_OUT {
+		size		8
+	}
+	/* Parameters for DMA Logic */
+	DMAPARAMS {
+		size		1
+		bit	WIDEODD		0x40
+		bit	SCSIEN		0x20
+		bit	SDMAEN		0x10
+		bit	SDMAENACK	0x10
+		bit	HDMAEN		0x08
+		bit	HDMAENACK	0x08
+		bit	DIRECTION	0x04
+		bit	FIFOFLUSH	0x02
+		bit	FIFORESET	0x01
+	}
+	/*
+	 * Number of SCBs supported by
+	 * this card.
+	 */
+	SCBCOUNT {
+		size		1
+	}
+	/*
+	 * Two's complement of SCBCOUNT
+	 */
+	COMP_SCBCOUNT {
+		size		1
+	}
+	/*
+	 * Mask of bits to test against
+	 * when looking at the Queue Count
+	 * registers.  Works around a bug
+	 * on aic7850 chips. 
+	 */
+	QCNTMASK {
+		size		1
+	}
+	SEQ_FLAGS {
+		size		1
+		bit	RESELECTED	0x80
+		bit	IDENTIFY_SEEN	0x40
+		bit	TAGGED_SCB	0x20
+		bit	DPHASE		0x10
+		bit	PAGESCBS	0x04
+		bit	WIDE_BUS	0x02
+		bit	TWIN_BUS	0x01
+	}
+	/*
+	 * Temporary storage for the
+	 * target/channel/lun of a
+	 * reconnecting target
+	 */
+	SAVED_TCL {
+		size		1
+	}
+	SG_COUNT {
+		size		1
+	}
+	/* working value of SG pointer */
+	SG_NEXT	{
+		size		4
+	}
+	/*
+	 * head of list of SCBs awaiting
+	 * selection
+	 */
+	WAITING_SCBH {
+		size		1
+	}
+	SAVED_LINKPTR {
+		size		1
+	}
+	SAVED_SCBPTR {
+		size		1
+	}
+	/*
+	 * The sequencer will stick the frist byte of any rejected message here
+	 * so we can see what is getting thrown away.
+	 */
+	REJBYTE	{
+		size		1
+	}
+	/*
+	 * The last bus phase as seen by the sequencer. 
+	 */
+	LASTPHASE {
+		size		1
+		bit	CDI		0x80
+		bit	IOI		0x40
+		bit	MSGI		0x20
+		mask	PHASE_MASK	CDI|IOI|MSGI
+		mask	P_DATAOUT	0x00
+		mask	P_DATAIN	IOI
+		mask	P_COMMAND	CDI
+		mask	P_MESGOUT	CDI|MSGI
+		mask	P_STATUS	CDI|IOI
+		mask	P_MESGIN	CDI|IOI|MSGI
+		mask	P_BUSFREE	0x01
+	}
+	MSGIN_EXT_LEN {
+		size		1
+	}
+	MSGIN_EXT_OPCODE {
+		size		1
+	}
+	/*
+	 * location 3, stores the last
+	 * byte of an extended message if
+	 * it passes the two bytes of space
+	 * we allow now.  This byte isn't
+	 * used for anything, it just makes
+	 * the code shorter for tossing
+	 * extra bytes.
+	 */
+	MSGIN_EXT_BYTES	{
+		size		3
+	}
+	/*
+	 * head of list of SCBs that are
+	 * disconnected.  Used for SCB
+	 * paging.
+	 */
+	DISCONNECTED_SCBH {
+		size		1
+	}
+	/*
+	 * head of list of SCBs that are
+	 * not in use.  Used for SCB paging.
+	 */
+	FREE_SCBH {
+		size		1
+	}
+	HSCB_ADDR {
+		size		4
+	}
+	CUR_SCBID {
+		size		1
+	}
+	/*
+	 * Running count of commands placed in
+	 * the QOUTFIFO.  This is cleared by the
+	 * kernel driver every FIFODEPTH commands.
+	 */
+	CMDOUTCNT {
+		size		1
+	}
+	/*
+	 * Maximum number of entries allowed in
+	 * the QOUT/INFIFO.
+	 */
+	FIFODEPTH {
+		size		1
+	}
+	ARG_1 {
+		size		1
+		mask	SEND_MSG	0x80
+		mask	SEND_SENSE	0x40
+		mask	SEND_REJ	0x20
+		alias	RETURN_1
+	}
+	/*
+	 * These are reserved registers in the card's scratch ram.  Some of
+	 * the values are specified in the AHA2742 technical reference manual
+	 * and are initialized by the BIOS at boot time.
+	 */
+	SCSICONF {
+		address		0x05a
+		size		1
+		bit	RESET_SCSI	0x40
+	}
+	HOSTCONF {
+		address		0x05d
+		size		1
+	}
+	HA_274_BIOSCTRL	{
+		address		0x05f
+		size		1
+		mask	BIOSMODE		0x30
+		mask	BIOSDISABLED		0x30	
+		bit	CHANNEL_B_PRIMARY	0x08
+	}
+}
+
+const SCB_LIST_NULL	0xff
+
+
+/* WDTR Message values */
+const BUS_8_BIT		0x00
+const BUS_16_BIT		0x01
+const BUS_32_BIT		0x02
+const MAX_OFFSET_8BIT		0x0f
+const MAX_OFFSET_16BIT	0x08
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx/aic7xxx.seq linux/drivers/scsi/aic7xxx/aic7xxx.seq
--- v2.0.30/linux/drivers/scsi/aic7xxx/aic7xxx.seq	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aic7xxx.seq	Wed Aug 13 09:53:45 1997
@@ -0,0 +1,1156 @@
+/*
+ * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
+ *
+ * Copyright (c) 1994-1997 Justin Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	$Id: aic7xxx.seq,v 1.74 1997/06/27 19:38:42 gibbs Exp $
+ */
+
+#include <aic7xxx.reg>
+#include <scsi_message.h>
+
+/*
+ * A few words on the waiting SCB list:
+ * After starting the selection hardware, we check for reconnecting targets
+ * as well as for our selection to complete just in case the reselection wins
+ * bus arbitration.  The problem with this is that we must keep track of the
+ * SCB that we've already pulled from the QINFIFO and started the selection
+ * on just in case the reselection wins so that we can retry the selection at
+ * a later time.  This problem cannot be resolved by holding a single entry
+ * in scratch ram since a reconnecting target can request sense and this will
+ * create yet another SCB waiting for selection.  The solution used here is to 
+ * use byte 27 of the SCB as a psuedo-next pointer and to thread a list
+ * of SCBs that are awaiting selection.  Since 0-0xfe are valid SCB indexes, 
+ * SCB_LIST_NULL is 0xff which is out of range.  An entry is also added to
+ * this list everytime a request sense occurs or after completing a non-tagged
+ * command for which a second SCB has been queued.  The sequencer will
+ * automatically consume the entries.
+ */
+
+/*
+ * We assume that the kernel driver may reset us at any time, even in the
+ * middle of a DMA, so clear DFCNTRL too.
+ */
+reset:
+	clr	SCSISIGO;		/* De-assert BSY */
+	/* Always allow reselection */
+	mvi	SCSISEQ, ENRSELI|ENAUTOATNP;
+	call	clear_target_state;
+poll_for_work:
+	test	SSTAT0,SELDO	jnz select;
+	test	SSTAT0,SELDI	jnz reselect;
+	test	SCSISEQ, ENSELO	jnz poll_for_work;
+.if ( TWIN_CHANNEL )
+	/*
+	 * Twin channel devices cannot handle things like SELTO
+	 * interrupts on the "background" channel.  So, if we
+	 * are selecting, keep polling the current channel util
+	 * either a selection or reselection occurs.
+	 */
+	xor	SBLKCTL,SELBUSB;	/* Toggle to the other bus */
+	test	SSTAT0,SELDO	jnz select;
+	test	SSTAT0,SELDI	jnz reselect;
+	test	SCSISEQ, ENSELO	jnz poll_for_work;
+	xor	SBLKCTL,SELBUSB;	/* Toggle back */
+.endif
+	cmp	WAITING_SCBH,SCB_LIST_NULL jne start_waiting;
+test_queue:
+	/* Has the driver posted any work for us? */
+	mov	A, QCNTMASK;
+	test	QINCNT,A	jz poll_for_work;
+
+/*
+ * We have at least one queued SCB now and we don't have any 
+ * SCBs in the list of SCBs awaiting selection.  If we have
+ * any SCBs available for use, pull the tag from the QINFIFO
+ * and get to work on it.
+ */
+.if ( SCB_PAGING )
+	mov	ALLZEROS	call	get_free_or_disc_scb;
+	cmp	SINDEX, SCB_LIST_NULL	je poll_for_work;
+.endif
+dequeue_scb:
+	mov	CUR_SCBID,QINFIFO;
+.if !( SCB_PAGING )
+	/* In the non-paging case, the SCBID == hardware SCB index */
+	mov	SCBPTR, CUR_SCBID;
+.endif
+dma_queued_scb:
+/*
+ * DMA the SCB from host ram into the current SCB location.
+ */
+	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+	mov	CUR_SCBID	call dma_scb;
+
+/*
+ * See if there is not already an active SCB for this target.  This code
+ * locks out on a per target basis instead of target/lun.  Although this
+ * is not ideal for devices that have multiple luns active at the same
+ * time, it is faster than looping through all SCB's looking for active
+ * commands.  We also don't have enough spare SCB space for us to store the
+ * SCBID of the currently busy transaction for each target/lun making it
+ * impossible to link up the SCBs.
+ */
+test_busy:
+	test	SCB_CONTROL, TAG_ENB|ABORT_SCB jnz start_scb;
+	mvi	SEQCTL, PAUSEDIS|FASTMODE;
+	mov	SAVED_SCBPTR, SCBPTR;
+	mov	SCB_TCL		call	index_untagged_scb;
+	mov	ARG_1, SINDIR;			/*
+						 * ARG_1 should
+						 * now have the SCB ID of
+						 * any active, non-tagged,
+						 * command for this target.
+						 */
+	cmp	ARG_1, SCB_LIST_NULL je make_busy;
+.if ( SCB_PAGING )
+	/*
+	 * Put this SCB back onto the free list.  It
+	 * may be necessary to satisfy the search for
+	 * the active SCB.
+	 */
+	mov	SCBPTR, SAVED_SCBPTR;
+	call	add_scb_to_free_list;
+	/* Find the active SCB */
+	mov	ALLZEROS	call findSCB;
+	/*
+	 * If we couldn't find it, tell the kernel.  This should
+	 * never happen.
+	 */
+	cmp	SINDEX, SCB_LIST_NULL	jne paged_busy_link;
+	mvi	INTSTAT, NO_MATCH_BUSY;
+paged_busy_link:
+	/* Link us in */
+	mov	SCB_LINKED_NEXT, CUR_SCBID;
+	/* Put it back on the disconnected list */
+	call	add_scb_to_disc_list;
+	mvi	SEQCTL, FASTMODE;
+	jmp	poll_for_work;
+.else
+simple_busy_link:
+	mov	SCBPTR, ARG_1;
+	mov	SCB_LINKED_NEXT, CUR_SCBID;
+	mvi	SEQCTL, FASTMODE;
+	jmp	poll_for_work;
+.endif
+make_busy:
+	mov	DINDIR, CUR_SCBID;
+	mov	SCBPTR, SAVED_SCBPTR;
+	mvi	SEQCTL, FASTMODE;
+
+start_scb:
+	/*
+	 * Place us on the waiting list in case our selection
+	 * doesn't win during bus arbitration.
+	 */
+	mov	SCB_NEXT,WAITING_SCBH;
+	mov	WAITING_SCBH, SCBPTR;
+start_waiting:
+	/*
+	 * Pull the first entry off of the waiting SCB list
+	 * We don't have to "test_busy" because only transactions that
+	 * have passed that test can be in the WAITING_SCB list.
+	 */
+	mov	SCBPTR, WAITING_SCBH;
+	call	start_selection;
+	jmp	poll_for_work;
+
+start_selection:
+.if ( TWIN_CHANNEL )
+	and	SINDEX,~SELBUSB,SBLKCTL;/* Clear the channel select bit */
+	and	A,SELBUSB,SCB_TCL;	/* Get new channel bit */
+	or	SINDEX,A;
+	mov	SBLKCTL,SINDEX;		/* select channel */
+.endif
+initialize_scsiid:
+	and	A, TID, SCB_TCL;	/* Get target ID */
+	and	SCSIID, OID;		/* Clear old target */
+	or	SCSIID, A;
+	mvi	SCSISEQ, ENSELO|ENAUTOATNO|ENRSELI|ENAUTOATNP ret;
+/*
+ * Reselection has been initiated by a target. Make a note that we've been
+ * reselected, but haven't seen an IDENTIFY message from the target yet.
+ */
+reselect:
+	clr	MSG_LEN;	/* Don't have anything in the mesg buffer */
+	mvi	CLRSINT0, CLRSELDI;
+	/* XXX test for and handle ONE BIT condition */
+	and	SAVED_TCL, SELID_MASK, SELID;
+	or	SEQ_FLAGS,RESELECTED;
+	jmp	select2;
+
+/*
+ * After the selection, remove this SCB from the "waiting SCB"
+ * list.  This is achieved by simply moving our "next" pointer into
+ * WAITING_SCBH.  Our next pointer will be set to null the next time this
+ * SCB is used, so don't bother with it now.
+ */
+select:
+	/* Turn off the selection hardware */
+	mvi	SCSISEQ, ENRSELI|ENAUTOATNP;	/*
+						 * ATN on parity errors
+						 * for "in" phases
+						 */
+	mvi	CLRSINT0, CLRSELDO;
+	mov	SCBPTR, WAITING_SCBH;
+	mov	WAITING_SCBH,SCB_NEXT;
+	mov	SAVED_TCL, SCB_TCL;
+/*
+ * As soon as we get a successful selection, the target should go
+ * into the message out phase since we have ATN asserted.  Prepare
+ * the message to send.
+ *
+ * Messages are stored in scratch RAM starting with a length byte
+ * followed by the message itself.
+ */
+
+mk_identify:
+	and	MSG_OUT,0x7,SCB_TCL;	/* lun */
+	and	A,DISCENB,SCB_CONTROL;	/* mask off disconnect privledge */
+	or	MSG_OUT,A;		/* or in disconnect privledge */
+	or	MSG_OUT,MSG_IDENTIFYFLAG;
+	mvi	MSG_LEN, 1;
+
+/*
+ * Send a tag message if TAG_ENB is set in the SCB control block.
+ * Use SCB_TAG (the position in the kernel's SCB array) as the tag value.
+ */
+mk_tag:
+	test	SCB_CONTROL,TAG_ENB jz  mk_message;
+	and	MSG_OUT[1],TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
+	mov	MSG_OUT[2],SCB_TAG;
+	add	MSG_LEN,2;	/* update message length */
+
+/*
+ * Interrupt the driver, and allow it to tweak the message buffer
+ * if it asks.
+ */
+mk_message:
+	test	SCB_CONTROL,MK_MESSAGE  jz select2;
+	mvi     INTSTAT,AWAITING_MSG;
+
+select2:
+	mvi	CLRSINT1,CLRBUSFREE;
+	or	SIMODE1, ENBUSFREE;		/*
+						 * We aren't expecting a
+						 * bus free, so interrupt
+						 * the kernel driver if it
+						 * happens.
+						 */
+/*
+ * Initialize Ultra mode setting and clear the SCSI channel.
+ */
+	or	SXFRCTL0, CLRSTCNT|SPIOEN|CLRCHN;
+.if ( ULTRA )
+ultra:
+	mvi	SINDEX, ULTRA_ENB+1;
+	test	SAVED_TCL, 0x80		jnz ultra_2;	/* Target ID > 7 */
+	dec	SINDEX;
+ultra_2:
+	mov     FUNCTION1,SAVED_TCL;
+	mov     A,FUNCTION1;
+	test	SINDIR, A	jz ndx_dtr;
+	or	SXFRCTL0, FAST20;
+.endif
+ 
+/*
+ * Initialize SCSIRATE with the appropriate value for this target.
+ * The SCSIRATE settings for each target are stored in an array
+ * based at TARG_SCRATCH.
+ */
+ndx_dtr:
+	shr	A,4,SAVED_TCL;
+	test	SBLKCTL,SELBUSB	jz ndx_dtr_2;
+	or	SAVED_TCL, SELBUSB; /* Add the channel bit while we're here */
+	or	A,0x08;			/* Channel B entries add 8 */
+ndx_dtr_2:
+	add	SINDEX,TARG_SCRATCH,A;
+	mov	SCSIRATE,SINDIR;
+
+
+/*
+ * Main loop for information transfer phases.  If BSY is false, then
+ * we have a bus free condition, expected or not.  Otherwise, wait
+ * for the target to assert REQ before checking MSG, C/D and I/O
+ * for the bus phase.
+ *
+ */
+ITloop:
+	test	SSTAT1,REQINIT		jz ITloop;
+	test	SSTAT1, SCSIPERR	jnz ITloop;
+
+	and	A,PHASE_MASK,SCSISIGI;
+	mov	LASTPHASE,A;
+	mov	SCSISIGO,A;
+
+	cmp	ALLZEROS,A	je p_dataout;
+	cmp	A,P_DATAIN	je p_datain;
+	cmp	A,P_COMMAND	je p_command;
+	cmp	A,P_MESGOUT	je p_mesgout;
+	cmp	A,P_STATUS	je p_status;
+	cmp	A,P_MESGIN	je p_mesgin;
+
+	mvi	INTSTAT,BAD_PHASE;	/* unknown phase - signal driver */
+	jmp	ITloop;			/* Try reading the bus again. */
+
+await_busfree:
+	and	SIMODE1, ~ENBUSFREE;
+	call	clear_target_state;
+	mov	NONE, SCSIDATL;		/* Ack the last byte */
+	test	SSTAT1,REQINIT|BUSFREE	jz .;
+	test	SSTAT1, BUSFREE jnz poll_for_work;
+	mvi	INTSTAT, BAD_PHASE;
+	
+clear_target_state:
+	clr	DFCNTRL;
+	clr	SCSIRATE;		/*
+					 * We don't know the target we will
+					 * connect to, so default to narrow
+					 * transfers to avoid parity problems.
+					 */
+	and	SXFRCTL0, ~FAST20;	
+	mvi	LASTPHASE, P_BUSFREE;
+	/* clear target specific flags */
+	and	SEQ_FLAGS,~(RESELECTED|IDENTIFY_SEEN|TAGGED_SCB|DPHASE) ret;
+
+p_dataout:
+	mvi	DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET;
+	jmp	data_phase_init;
+
+/*
+ * If we re-enter the data phase after going through another phase, the
+ * STCNT may have been cleared, so restore it from the residual field.
+ */
+data_phase_reinit:
+	mvi	DINDEX, STCNT;
+	mvi	SCB_RESID_DCNT	call bcopy_3;
+	jmp	data_phase_loop;
+
+p_datain:
+	mvi	DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
+data_phase_init:
+	call	assert;			/*
+					 * Ensure entering a data
+					 * phase is okay - seen identify, etc.
+					 */
+
+	test	SEQ_FLAGS, DPHASE	jnz data_phase_reinit;
+
+	/*
+	 * Initialize the DMA address and counter from the SCB.
+	 * Also set SG_COUNT and SG_NEXT in memory since we cannot
+	 * modify the values in the SCB itself until we see a
+	 * save data pointers message.
+	 */
+	mvi	DINDEX, HADDR;
+	mvi	SCB_DATAPTR	call bcopy_7;
+
+	call	set_stcnt_from_hcnt;
+
+	mov	SG_COUNT,SCB_SGCOUNT;
+
+	mvi	DINDEX, SG_NEXT;
+	mvi	SCB_SGPTR	call bcopy_4;
+
+data_phase_loop:
+/* Guard against overruns */
+	test	SG_COUNT, 0xff jnz data_phase_inbounds;
+/*
+ * Turn on 'Bit Bucket' mode, set the transfer count to
+ * 16meg and let the target run until it changes phase.
+ * When the transfer completes, notify the host that we
+ * had an overrun.
+ */
+	or	SXFRCTL1,BITBUCKET;
+	mvi	HCNT[0], 0xff;
+	mvi	HCNT[1], 0xff;
+	mvi	HCNT[2], 0xff;
+	call	set_stcnt_from_hcnt;
+
+data_phase_inbounds:
+/* If we are the last SG block, ensure wideodd is off. */
+	cmp	SG_COUNT,0x01 jne data_phase_wideodd;
+	and	DMAPARAMS, ~WIDEODD;
+data_phase_wideodd:
+	mov	DMAPARAMS  call dma;
+
+/* Go tell the host about any overruns */
+	test	SXFRCTL1,BITBUCKET jnz data_phase_overrun;
+
+/* Exit if we had an underrun.  dma clears SINDEX in this case. */
+	test	SINDEX,0xff	jz data_phase_finish;
+
+/*
+ * Advance the scatter-gather pointers if needed 
+ */
+sg_advance:
+	dec	SG_COUNT;	/* one less segment to go */
+
+	test	SG_COUNT, 0xff	jz data_phase_finish; /* Are we done? */
+
+	clr	A;			/* add sizeof(struct scatter) */
+	add	SG_NEXT[0],SG_SIZEOF;
+	adc	SG_NEXT[1],A;
+
+/*
+ * Load a struct scatter and set up the data address and length.
+ * If the working value of the SG count is nonzero, then
+ * we need to load a new set of values.
+ *
+ * This, like all DMA's, assumes little-endian host data storage.
+ */
+sg_load:
+	mvi	DINDEX, HADDR;
+	mvi	SG_NEXT	call bcopy_4;
+
+	mvi	HCNT[0],SG_SIZEOF;
+	clr	HCNT[1];
+	clr	HCNT[2];
+
+	or	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
+
+	call	dma_finish;
+
+/*
+ * Copy data from FIFO into SCB data pointer and data count.  This assumes
+ * that the SG segments are of the form:
+ *
+ * struct ahc_dma_seg {
+ *	u_int32_t	addr;		four bytes, little-endian order
+ *	u_int32_t	len;		four bytes, little endian order
+ * };
+ */
+	mvi	HADDR	call dfdat_in_7;
+
+/* Load STCNT as well.  It is a mirror of HCNT */
+	call	set_stcnt_from_hcnt;
+	test	SSTAT1,PHASEMIS	jz data_phase_loop;
+
+data_phase_finish:
+/*
+ * After a DMA finishes, save the SG and STCNT residuals back into the SCB
+ * We use STCNT instead of HCNT, since it's a reflection of how many bytes 
+ * were transferred on the SCSI (as opposed to the host) bus.
+ */
+	mov	SCB_RESID_DCNT[0],STCNT[0];
+	mov	SCB_RESID_DCNT[1],STCNT[1];
+	mov	SCB_RESID_DCNT[2],STCNT[2];
+	mov	SCB_RESID_SGCNT, SG_COUNT;
+
+	/* We have seen a data phase */
+	or	SEQ_FLAGS, DPHASE;
+
+	jmp	ITloop;
+
+data_phase_overrun:
+/*
+ * Turn off BITBUCKET mode and notify the host
+ */
+	and	SXFRCTL1, ~BITBUCKET;
+	mvi	INTSTAT,DATA_OVERRUN;
+	jmp	ITloop;
+
+/*
+ * Command phase.  Set up the DMA registers and let 'er rip.
+ */
+p_command:
+	call	assert;
+
+/*
+ * Load HADDR and HCNT.
+ */
+	mvi	DINDEX, HADDR;
+	mvi	SCB_CMDPTR	call bcopy_5;
+	clr	HCNT[1];
+	clr	HCNT[2];
+
+	call	set_stcnt_from_hcnt;
+
+	mvi	(SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET) call dma;
+	jmp	ITloop;
+
+/*
+ * Status phase.  Wait for the data byte to appear, then read it
+ * and store it into the SCB.
+ */
+p_status:
+	call	assert;
+
+	mov	SCB_TARGET_STATUS, SCSIDATL;
+	jmp	ITloop;
+
+/*
+ * Message out phase.  If there is not an active message, but the target
+ * took us into this phase anyway, build a no-op message and send it.
+ */
+p_mesgout:
+	test	MSG_LEN, 0xff	jnz  p_mesgout_start;
+	mvi	MSG_NOOP	call mk_mesg;	/* build NOP message */
+p_mesgout_start:
+/*
+ * Set up automatic PIO transfer from MSG_OUT.  Bit 3 in
+ * SXFRCTL0 (SPIOEN) is already on.
+ */
+	mvi	SINDEX,MSG_OUT;
+	mov	DINDEX,MSG_LEN;
+
+/*
+ * When target asks for a byte, drop ATN if it's the last one in
+ * the message.  Otherwise, keep going until the message is exhausted.
+ * ATN must be dropped *at least* 90ns before we ack the last byte, so
+ * the code is aranged to execute two instructions before the byte is
+ * transferred to give a good margin of safety
+ *
+ * Keep an eye out for a phase change, in case the target issues
+ * a MESSAGE REJECT.
+ */
+p_mesgout_loop:
+	test	SSTAT1, REQINIT		jz p_mesgout_loop;
+	test	SSTAT1, SCSIPERR	jnz p_mesgout_loop;
+	and	LASTPHASE, PHASE_MASK, SCSISIGI;
+	cmp	LASTPHASE, P_MESGOUT jne p_mesgout_done;
+p_mesgout_testretry:
+	test	DINDEX,0xff	jnz p_mesgout_dropatn;
+	or	SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */
+	jmp	p_mesgout_start;
+/*
+ * If the next bus phase after ATN drops is a message out, it means
+ * that the target is requesting that the last message(s) be resent.
+ */
+p_mesgout_dropatn:
+	cmp	DINDEX,1	jne p_mesgout_outb;	/* last byte? */
+	mvi	CLRSINT1,CLRATNO;			/* drop ATN */
+p_mesgout_outb:
+	dec	DINDEX;
+	mov	SCSIDATL,SINDIR;
+	jmp	p_mesgout_loop;
+
+p_mesgout_done:
+	mvi	CLRSINT1,CLRATNO;	/* Be sure to turn ATNO off */
+	clr	MSG_LEN;		/* no active msg */
+	jmp	ITloop;
+
+/*
+ * Message in phase.  Bytes are read using Automatic PIO mode.
+ */
+p_mesgin:
+	mvi	ACCUM		call inb_first;	/* read the 1st message byte */
+	mov	REJBYTE,A;			/* save it for the driver */
+
+	test	A,MSG_IDENTIFYFLAG	jnz mesgin_identify;
+	cmp	A,MSG_DISCONNECT	je mesgin_disconnect;
+	cmp	A,MSG_SAVEDATAPOINTER	je mesgin_sdptrs;
+	cmp	ALLZEROS,A		je mesgin_complete;
+	cmp	A,MSG_RESTOREPOINTERS	je mesgin_rdptrs;
+	cmp	A,MSG_EXTENDED		je mesgin_extended;
+	cmp	A,MSG_MESSAGE_REJECT	je mesgin_reject;
+	cmp	A,MSG_NOOP		je mesgin_done;
+
+rej_mesgin:
+/*
+ * We have no idea what this message in is, so we issue a message reject
+ * and hope for the best.  In any case, rejection should be a rare
+ * occurrence - signal the driver when it happens.
+ */
+	mvi	INTSTAT,SEND_REJECT;		/* let driver know */
+
+	mvi	MSG_MESSAGE_REJECT	call mk_mesg;
+
+mesgin_done:
+	mov	NONE,SCSIDATL;		/*dummy read from latch to ACK*/
+	jmp	ITloop;
+
+
+mesgin_complete:
+/*
+ * We got a "command complete" message, so put the SCB_TAG into the QOUTFIFO,
+ * and trigger a completion interrupt.  Before doing so, check to see if there
+ * is a residual or the status byte is something other than NO_ERROR (0).  In
+ * either of these conditions, we upload the SCB back to the host so it can
+ * process this information.  In the case of a non zero status byte, we 
+ * additionally interrupt the kernel driver synchronously, allowing it to
+ * decide if sense should be retrieved.  If the kernel driver wishes to request
+ * sense, it will fill the kernel SCB with a request sense command and set
+ * RETURN_1 to SEND_SENSE.  If RETURN_1 is set to SEND_SENSE we redownload
+ * the SCB, and process it as the next command by adding it to the waiting list.
+ * If the kernel driver does not wish to request sense, it need only clear
+ * RETURN_1, and the command is allowed to complete normally.  We don't bother
+ * to post to the QOUTFIFO in the error cases since it would require extra
+ * work in the kernel driver to ensure that the entry was removed before the
+ * command complete code tried processing it.
+ */
+
+/*
+ * First check for residuals
+ */
+	test	SCB_RESID_SGCNT,0xff	jnz upload_scb;
+	test	SCB_TARGET_STATUS,0xff	jz status_ok;	/* Good Status? */
+upload_scb:
+	mvi	DMAPARAMS, FIFORESET;
+	mov	SCB_TAG		call dma_scb;
+check_status:
+	test	SCB_TARGET_STATUS,0xff	jz status_ok;	/* Just a residual? */
+	mvi	INTSTAT,BAD_STATUS;			/* let driver know */
+	cmp	RETURN_1, SEND_SENSE	jne status_ok;
+	/* This SCB becomes the next to execute as it will retrieve sense */
+	mov	SCB_LINKED_NEXT, SCB_TAG;
+	jmp	dma_next_scb;
+
+status_ok:
+/* First, mark this target as free. */
+	test	SCB_CONTROL,TAG_ENB jnz complete;	/*
+							 * Tagged commands
+							 * don't busy the
+							 * target.
+							 */
+	mov	SAVED_SCBPTR, SCBPTR;
+	mov	SAVED_LINKPTR, SCB_LINKED_NEXT;
+	mov	SCB_TCL	call index_untagged_scb;
+	mov	DINDIR, SAVED_LINKPTR;
+	mov	SCBPTR, SAVED_SCBPTR;
+
+complete:
+	/* Post the SCB and issue an interrupt */
+.if ( SCB_PAGING )
+	/*
+	 * Spin loop until there is space
+	 * in the QOUTFIFO.
+	 */
+	mov	A, FIFODEPTH;
+	cmp	CMDOUTCNT, A	je .;
+	inc	CMDOUTCNT;
+.endif
+	mov	QOUTFIFO,SCB_TAG;
+	mvi	INTSTAT,CMDCMPLT;
+	test	SCB_CONTROL, ABORT_SCB jz dma_next_scb;
+	mvi	INTSTAT, ABORT_CMDCMPLT;
+
+dma_next_scb:
+	cmp	SCB_LINKED_NEXT, SCB_LIST_NULL	je add_to_free_list;
+.if !( SCB_PAGING )
+	/* Only DMA on top of ourselves if we are the SCB to download */
+	mov	A, SCB_LINKED_NEXT;
+	cmp	SCB_TAG, A	je dma_next_scb2;
+	call	add_scb_to_free_list;
+	mov	SCBPTR, A;
+	jmp	add_to_waiting_list;
+.endif
+dma_next_scb2:
+	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+	mov	SCB_LINKED_NEXT		call dma_scb;
+add_to_waiting_list:
+	mov	SCB_NEXT,WAITING_SCBH;
+	mov	WAITING_SCBH, SCBPTR;
+	/*
+	 * Prepare our selection hardware before the busfree so we have a
+	 * high probability of winning arbitration.
+	 */
+	call	start_selection;
+	jmp	await_busfree;
+add_to_free_list:
+	call	add_scb_to_free_list;
+	jmp	await_busfree;
+
+/*
+ * Is it an extended message?  Copy the message to our message buffer and
+ * notify the host.  The host will tell us whether to reject this message,
+ * respond to it with the message that the host placed in our message buffer,
+ * or simply to do nothing.
+ */
+mesgin_extended:
+	mvi	MSGIN_EXT_LEN	 call inb_next;
+	mov	A, MSGIN_EXT_LEN;
+mesgin_extended_loop:
+	mov	DINDEX	call	inb_next;
+	dec	A;
+	cmp	DINDEX, MSGIN_EXT_BYTES+3 jne mesgin_extended_loop_test;
+	dec	DINDEX;		/* dump by repeatedly filling the last byte */
+mesgin_extended_loop_test:
+	test	A, 0xFF		jnz mesgin_extended_loop;
+mesgin_extended_intr:
+	mvi	INTSTAT,EXTENDED_MSG;		/* let driver know */
+	cmp	RETURN_1,SEND_REJ je rej_mesgin;
+	cmp	RETURN_1,SEND_MSG jne mesgin_done;
+/* The kernel has setup a message to be sent */
+	or	SCSISIGO,ATNO,LASTPHASE;	/* turn on ATNO */
+	jmp	mesgin_done;
+
+/*
+ * Is it a disconnect message?  Set a flag in the SCB to remind us
+ * and await the bus going free.
+ */
+mesgin_disconnect:
+	or	SCB_CONTROL,DISCONNECTED;
+.if ( SCB_PAGING )
+	call	add_scb_to_disc_list;
+.endif
+	jmp	await_busfree;
+
+/*
+ * Save data pointers message:
+ * Copying RAM values back to SCB, for Save Data Pointers message, but
+ * only if we've actually been into a data phase to change them.  This
+ * protects against bogus data in scratch ram and the residual counts
+ * since they are only initialized when we go into data_in or data_out.
+ */
+mesgin_sdptrs:
+	test	SEQ_FLAGS, DPHASE	jz mesgin_done;
+	mov	SCB_SGCOUNT,SG_COUNT;
+
+	/* The SCB SGPTR becomes the next one we'll download */
+	mvi	DINDEX, SCB_SGPTR;
+	mvi	SG_NEXT	call bcopy_4;
+	
+	/* The SCB DATAPTR0 becomes the current SHADDR */
+	mvi	DINDEX, SCB_DATAPTR;
+	mvi	SHADDR		call bcopy_4;
+
+/*
+ * Use the residual number since STCNT is corrupted by any message transfer.
+ */
+	mvi	SCB_RESID_DCNT	call	bcopy_3;
+
+	jmp	mesgin_done;
+
+/*
+ * Restore pointers message?  Data pointers are recopied from the
+ * SCB anytime we enter a data phase for the first time, so all
+ * we need to do is clear the DPHASE flag and let the data phase
+ * code do the rest.
+ */
+mesgin_rdptrs:
+	and	SEQ_FLAGS, ~DPHASE;		/*
+						 * We'll reload them
+						 * the next time through
+						 * the dataphase.
+						 */
+	jmp	mesgin_done;
+
+/*
+ * Identify message?  For a reconnecting target, this tells us the lun
+ * that the reconnection is for - find the correct SCB and switch to it,
+ * clearing the "disconnected" bit so we don't "find" it by accident later.
+ */
+mesgin_identify:
+	test	A,0x78	jnz rej_mesgin;	/*!DiscPriv|!LUNTAR|!Reserved*/
+	and	A,0x07;			/* lun in lower three bits */
+	or      SAVED_TCL,A;		/* SAVED_TCL should be complete now */
+	mov	SAVED_TCL call index_untagged_scb;
+	mov	ARG_1, SINDIR;
+.if ( SCB_PAGING )
+	cmp	ARG_1,SCB_LIST_NULL	jne use_findSCB;
+.else
+	cmp	ARG_1,SCB_LIST_NULL	je snoop_tag;
+	/* Directly index the SCB */
+	mov	SCBPTR,ARG_1;
+	test	SCB_CONTROL,DISCONNECTED jz not_found;
+	jmp	setup_SCB;
+.endif
+/*
+ * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
+ * If we get one, we use the tag returned to find the proper
+ * SCB.  With SCB paging, this requires using findSCB for both tagged
+ * and non-tagged transactions since the SCB may exist in any slot.
+ * If we're not using SCB paging, we can use the tag as the direct
+ * index to the SCB.
+ */
+snoop_tag:
+	mov	NONE,SCSIDATL;		/* ACK Identify MSG */
+snoop_tag_loop:
+	test	SSTAT1,REQINIT		jz snoop_tag_loop;
+	test	SSTAT1, SCSIPERR	jnz snoop_tag_loop;
+	and	LASTPHASE, PHASE_MASK, SCSISIGI;
+	cmp	LASTPHASE, P_MESGIN	jne not_found;
+	cmp	SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found;
+get_tag:
+	or	SEQ_FLAGS, TAGGED_SCB;
+	mvi	ARG_1	call inb_next;	/* tag value */
+/*
+ * See if the tag is in range.  The tag is < SCBCOUNT if we add
+ * the complement of SCBCOUNT to the incomming tag and there is
+ * no carry.
+ */
+	mov	A,COMP_SCBCOUNT;
+	add	SINDEX,A,ARG_1;
+	jc	not_found;
+
+.if ! ( SCB_PAGING )
+index_by_tag:
+	mov	SCBPTR,ARG_1;
+	mov	A, SAVED_TCL;
+	cmp	SCB_TCL,A		jne not_found;
+	test	SCB_CONTROL,TAG_ENB	jz  not_found;
+	test	SCB_CONTROL,DISCONNECTED jz not_found;
+.else
+/*
+ * Ensure that the SCB the tag points to is for an SCB transaction
+ * to the reconnecting target.
+ */
+use_findSCB:
+	mov	ALLZEROS	call findSCB;	  /* Have to search */
+	cmp	SINDEX, SCB_LIST_NULL	je not_found;
+.endif
+setup_SCB:
+	and	SCB_CONTROL,~DISCONNECTED;
+	or	SEQ_FLAGS,IDENTIFY_SEEN;	  /* make note of IDENTIFY */
+	jmp	mesgin_done;
+
+not_found:
+	mvi	INTSTAT, NO_MATCH;
+	mvi	MSG_BUS_DEV_RESET	call mk_mesg;
+	jmp	mesgin_done;
+
+/*
+ * Message reject?  Let the kernel driver handle this.  If we have an 
+ * outstanding WDTR or SDTR negotiation, assume that it's a response from 
+ * the target selecting 8bit or asynchronous transfer, otherwise just ignore 
+ * it since we have no clue what it pertains to.
+ */
+mesgin_reject:
+	mvi	INTSTAT, REJECT_MSG;
+	jmp	mesgin_done;
+
+/*
+ * [ ADD MORE MESSAGE HANDLING HERE ]
+ */
+
+/*
+ * Locking the driver out, build a one-byte message passed in SINDEX
+ * if there is no active message already.  SINDEX is returned intact.
+ */
+mk_mesg:
+	mvi	SEQCTL, PAUSEDIS|FASTMODE;
+	test	MSG_LEN,0xff	jz mk_mesg1;	/* Should always succeed */
+	
+	/*
+	 * Hmmm.  For some reason the mesg buffer is in use.
+	 * Tell the driver.  It should look at SINDEX to find
+	 * out what we wanted to use the buffer for and resolve
+	 * the conflict.
+	 */
+	mvi	SEQCTL,FASTMODE;
+	mvi	INTSTAT,MSG_BUFFER_BUSY;
+
+mk_mesg1:
+	or	SCSISIGO,ATNO,LASTPHASE;/* turn on ATNO */
+	mvi	MSG_LEN,1;		/* length = 1 */
+	mov	MSG_OUT,SINDEX;		/* 1-byte message */
+	mvi	SEQCTL,FASTMODE	ret;
+
+/*
+ * Functions to read data in Automatic PIO mode.
+ *
+ * According to Adaptec's documentation, an ACK is not sent on input from
+ * the target until SCSIDATL is read from.  So we wait until SCSIDATL is
+ * latched (the usual way), then read the data byte directly off the bus
+ * using SCSIBUSL.  When we have pulled the ATN line, or we just want to
+ * acknowledge the byte, then we do a dummy read from SCISDATL.  The SCSI
+ * spec guarantees that the target will hold the data byte on the bus until
+ * we send our ACK.
+ *
+ * The assumption here is that these are called in a particular sequence,
+ * and that REQ is already set when inb_first is called.  inb_{first,next}
+ * use the same calling convention as inb.
+ */
+
+inb_next:
+	mov	NONE,SCSIDATL;		/*dummy read from latch to ACK*/
+inb_next_wait:
+	/*
+	 * If there is a parity error, wait for the kernel to
+	 * see the interrupt and prepare our message response
+	 * before continuing.
+	 */
+	test	SSTAT1, REQINIT	jz inb_next_wait;
+	test	SSTAT1, SCSIPERR jnz inb_next_wait;
+	and	LASTPHASE, PHASE_MASK, SCSISIGI;
+	cmp	LASTPHASE, P_MESGIN jne mesgin_phasemis;
+inb_first:
+	mov	DINDEX,SINDEX;
+	mov	DINDIR,SCSIBUSL	ret;		/*read byte directly from bus*/
+inb_last:
+	mov	NONE,SCSIDATL ret;		/*dummy read from latch to ACK*/
+
+mesgin_phasemis:
+/*
+ * We expected to receive another byte, but the target changed phase
+ */
+	mvi	INTSTAT, MSGIN_PHASEMIS;
+	jmp	ITloop;
+
+/*
+ * DMA data transfer.  HADDR and HCNT must be loaded first, and
+ * SINDEX should contain the value to load DFCNTRL with - 0x3d for
+ * host->scsi, or 0x39 for scsi->host.  The SCSI channel is cleared
+ * during initialization.
+ */
+dma:
+	mov	DFCNTRL,SINDEX;
+dma_loop:
+	test	SSTAT0,DMADONE	jnz dma_dmadone;
+	test	SSTAT1,PHASEMIS	jz dma_loop;	/* ie. underrun */
+dma_phasemis:
+	test	SSTAT0,SDONE	jnz dma_checkfifo;
+	mov	SINDEX,ALLZEROS;		/* Notify caller of phasemiss */
+
+/*
+ * We will be "done" DMAing when the transfer count goes to zero, or
+ * the target changes the phase (in light of this, it makes sense that
+ * the DMA circuitry doesn't ACK when PHASEMIS is active).  If we are
+ * doing a SCSI->Host transfer, the data FIFO should be flushed auto-
+ * magically on STCNT=0 or a phase change, so just wait for FIFO empty
+ * status.
+ */
+dma_checkfifo:
+	test	DFCNTRL,DIRECTION	jnz dma_fifoempty;
+dma_fifoflush:
+	test	DFSTATUS,FIFOEMP	jz dma_fifoflush;
+
+dma_fifoempty:
+	/* Don't clobber an inprogress host data transfer */
+	test	DFSTATUS, MREQPEND	jnz dma_fifoempty;
+/*
+ * Now shut the DMA enables off and make sure that the DMA enables are 
+ * actually off first lest we get an ILLSADDR.
+ */
+dma_dmadone:
+	and	DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
+dma_halt:
+	test	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN)	jnz dma_halt; 
+return:
+	ret;
+
+/*
+ * Assert that if we've been reselected, then we've seen an IDENTIFY
+ * message.
+ */
+assert:
+	test	SEQ_FLAGS,RESELECTED	jz return;	/* reselected? */
+	test	SEQ_FLAGS,IDENTIFY_SEEN	jnz return;	/* seen IDENTIFY? */
+
+	mvi	INTSTAT,NO_IDENT 	ret;	/* no - tell the kernel */
+
+.if ( SCB_PAGING )
+/*
+ * Locate a disconnected SCB either by SAVED_TCL (ARG_1 is SCB_LIST_NULL)
+ * or by the SCBIDn ARG_1.  The search begins at the SCB index passed in
+ * via SINDEX.  If the SCB cannot be found, SINDEX will be SCB_LIST_NULL,
+ * otherwise, SCBPTR is set to the proper SCB.
+ */
+findSCB:
+	mov	SCBPTR,SINDEX;			/* switch to next SCB */
+	mov	A, ARG_1;			/* Tag passed in ARG_1 */
+	cmp	SCB_TAG,A	jne findSCB_loop;
+	test	SCB_CONTROL,DISCONNECTED jnz foundSCB;/*should be disconnected*/
+findSCB_loop:
+	inc	SINDEX;
+	mov	A,SCBCOUNT;
+	cmp	SINDEX,A	jne findSCB;
+/*
+ * We didn't find it.  If we're paging, pull an SCB and DMA down the
+ * one we want.  If we aren't paging or the SCB we dma down has the
+ * abort flag set, return not found.
+ */
+	mov	ALLZEROS	call	get_free_or_disc_scb;
+	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+	mov	ARG_1	call dma_scb;
+	test	SCB_RESID_SGCNT, 0xff jz . + 2;
+	or	SCB_CONTROL, MUST_DMAUP_SCB;
+	test	SCB_CONTROL, ABORT_SCB jz return;
+find_error:
+	mvi	SINDEX, SCB_LIST_NULL ret;
+foundSCB:
+	test	SCB_CONTROL, ABORT_SCB jnz find_error;
+rem_scb_from_disc_list:
+/* Remove this SCB from the disconnection list */
+	cmp	SCB_NEXT,SCB_LIST_NULL je unlink_prev;
+	mov	SAVED_LINKPTR, SCB_PREV;
+	mov	SCBPTR, SCB_NEXT;
+	mov	SCB_PREV, SAVED_LINKPTR;
+	mov	SCBPTR, SINDEX;
+unlink_prev:
+	cmp	SCB_PREV,SCB_LIST_NULL	je rHead;/* At the head of the list */
+	mov	SAVED_LINKPTR, SCB_NEXT;
+	mov	SCBPTR, SCB_PREV;
+	mov	SCB_NEXT, SAVED_LINKPTR;
+	mov	SCBPTR, SINDEX ret;
+rHead:
+	mov	DISCONNECTED_SCBH,SCB_NEXT ret;
+.else
+	ret;
+.endif
+
+set_stcnt_from_hcnt:
+	mov	STCNT[0], HCNT[0];
+	mov	STCNT[1], HCNT[1];
+	mov	STCNT[2], HCNT[2] ret;
+
+bcopy_7:
+	mov	DINDIR, SINDIR;
+	mov	DINDIR, SINDIR;
+bcopy_5:
+	mov	DINDIR, SINDIR;
+bcopy_4:
+	mov	DINDIR, SINDIR;
+bcopy_3:
+	mov	DINDIR, SINDIR;
+	mov	DINDIR, SINDIR;
+	mov	DINDIR, SINDIR ret;
+
+dma_scb:
+	/*
+	 * SCB index is in SINDEX.  Determine the physical address in
+	 * the host where this SCB is located and load HADDR with it.
+	 */
+	shr	DINDEX, 3, SINDEX;
+	shl	A, 5, SINDEX;
+	add	HADDR[0], A, HSCB_ADDR[0];
+	mov	A, DINDEX;
+	adc	HADDR[1], A, HSCB_ADDR[1];
+	clr	A;
+	adc	HADDR[2], A, HSCB_ADDR[2];
+	adc	HADDR[3], A, HSCB_ADDR[3];
+	/* Setup Count */
+	mvi	HCNT[0], 28;
+	clr	HCNT[1];
+	clr	HCNT[2];
+	mov	DFCNTRL, DMAPARAMS;
+	test	DMAPARAMS, DIRECTION	jnz dma_scb_fromhost;
+	/* Fill it with the SCB data */
+copy_scb_tofifo:
+	mvi	SINDEX, SCB_CONTROL;
+	add	A, 28, SINDEX;
+copy_scb_tofifo_loop:
+	mov	DFDAT,SINDIR;
+	mov	DFDAT,SINDIR;
+	mov	DFDAT,SINDIR;
+	mov	DFDAT,SINDIR;
+	mov	DFDAT,SINDIR;
+	mov	DFDAT,SINDIR;
+	mov	DFDAT,SINDIR;
+	cmp	SINDEX, A jne copy_scb_tofifo_loop;
+	or	DFCNTRL, HDMAEN|FIFOFLUSH;
+dma_scb_fromhost:
+	call	dma_finish;
+	/* If we were putting the SCB, we are done */
+	test	DMAPARAMS, DIRECTION	jz	return;
+	mvi	SCB_CONTROL  call dfdat_in_7;
+	call	dfdat_in_7_continued;
+	call	dfdat_in_7_continued;
+	jmp	dfdat_in_7_continued;
+dfdat_in_7:
+	mov     DINDEX,SINDEX;
+dfdat_in_7_continued:
+	mov	DINDIR,DFDAT;
+	mov	DINDIR,DFDAT;
+	mov	DINDIR,DFDAT;
+	mov	DINDIR,DFDAT;
+	mov	DINDIR,DFDAT;
+	mov	DINDIR,DFDAT;
+	mov	DINDIR,DFDAT ret;
+
+/*
+ * Wait for DMA from host memory to data FIFO to complete, then disable
+ * DMA and wait for it to acknowledge that it's off.
+ */
+dma_finish:
+	test	DFSTATUS,HDONE	jz dma_finish;
+	/* Turn off DMA */
+	and	DFCNTRL, ~HDMAEN;
+	test	DFCNTRL, HDMAEN jnz .;
+	ret;
+
+index_untagged_scb:
+	mov	DINDEX, SINDEX;
+	shr	DINDEX, 4;
+	and	DINDEX, 0x03;			/* Bottom two bits of tid */
+	add	DINDEX, SCB_BUSYTARGETS;
+	shr	A, 6, SINDEX;			/* Target ID divided by 4 */
+	test	SINDEX, SELBUSB jz index_untagged_scb2;
+	add	A, 2;				/* Add 2 positions */
+index_untagged_scb2:
+	mov	SCBPTR, A;			/*
+						 * Select the SCB with this 
+						 * target's information.
+						 */
+	mov	SINDEX, DINDEX	ret;
+
+add_scb_to_free_list:
+	mov	SCB_NEXT, FREE_SCBH;
+	mvi	SCB_TAG, SCB_LIST_NULL;
+	mov	FREE_SCBH, SCBPTR ret;
+
+.if ( SCB_PAGING )
+get_free_or_disc_scb:
+	cmp	FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb;
+	cmp	DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb;
+return_error:
+	mvi	SINDEX, SCB_LIST_NULL	ret;
+dequeue_disc_scb:
+	mov	SCBPTR, DISCONNECTED_SCBH;
+/*
+ * If we have a residual, then we are in the middle of some I/O
+ * and we have to send this SCB back up to the kernel so that the
+ * saved data pointers and residual information isn't lost.
+ */
+	test	SCB_CONTROL, MUST_DMAUP_SCB jz . + 3;
+	and	SCB_CONTROL, ~MUST_DMAUP_SCB;
+	jmp	dma_up_scb;
+	test	SCB_RESID_SGCNT,0xff	jnz dma_up_scb;
+	cmp	SCB_LINKED_NEXT, SCB_LIST_NULL je unlink_disc_scb;
+dma_up_scb:
+	mvi	DMAPARAMS, FIFORESET;
+	mov	SCB_TAG		call dma_scb;
+unlink_disc_scb:
+	/* jmp instead of call since we want to return anyway */
+	mov	SCBPTR	jmp rem_scb_from_disc_list;
+dequeue_free_scb:
+	mov	SCBPTR, FREE_SCBH;
+	mov	FREE_SCBH, SCB_NEXT ret;
+
+add_scb_to_disc_list:
+/*
+ * Link this SCB into the DISCONNECTED list.  This list holds the
+ * candidates for paging out an SCB if one is needed for a new command.
+ * Modifying the disconnected list is a critical(pause dissabled) section.
+ */
+	mvi	SCB_PREV, SCB_LIST_NULL;
+	mov	SCB_NEXT, DISCONNECTED_SCBH;
+	mov	DISCONNECTED_SCBH, SCBPTR;
+	cmp	SCB_NEXT,SCB_LIST_NULL je return;
+	mov	SCBPTR,SCB_NEXT;
+	mov	SCB_PREV,DISCONNECTED_SCBH;
+	mov	SCBPTR,DISCONNECTED_SCBH ret;
+.endif
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx/scsi_message.h linux/drivers/scsi/aic7xxx/scsi_message.h
--- v2.0.30/linux/drivers/scsi/aic7xxx/scsi_message.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/scsi_message.h	Thu Jul 31 12:37:17 1997
@@ -0,0 +1,41 @@
+/*
+ * SCSI messages definitions.
+ */
+
+/* Messages (1 byte) */		     /* I/T (M)andatory or (O)ptional */
+#define MSG_CMDCOMPLETE		0x00 /* M/M */
+#define MSG_EXTENDED		0x01 /* O/O */
+#define MSG_SAVEDATAPOINTER	0x02 /* O/O */
+#define MSG_RESTOREPOINTERS	0x03 /* O/O */
+#define MSG_DISCONNECT		0x04 /* O/O */
+#define MSG_INITIATOR_DET_ERR	0x05 /* M/M */
+#define MSG_ABORT		0x06 /* O/M */
+#define MSG_MESSAGE_REJECT	0x07 /* M/M */
+#define MSG_NOOP		0x08 /* M/M */
+#define MSG_PARITY_ERROR	0x09 /* M/M */
+#define MSG_LINK_CMD_COMPLETE	0x0a /* O/O */
+#define MSG_LINK_CMD_COMPLETEF	0x0b /* O/O */
+#define MSG_BUS_DEV_RESET	0x0c /* O/M */
+#define MSG_ABORT_TAG		0x0d /* O/O */
+#define MSG_CLEAR_QUEUE		0x0e /* O/O */
+#define MSG_INIT_RECOVERY	0x0f /* O/O */
+#define MSG_REL_RECOVERY	0x10 /* O/O */
+#define MSG_TERM_IO_PROC	0x11 /* O/O */
+
+/* Messages (2 byte) */
+#define MSG_SIMPLE_Q_TAG	0x20 /* O/O */
+#define MSG_HEAD_OF_Q_TAG	0x21 /* O/O */
+#define MSG_ORDERED_Q_TAG	0x22 /* O/O */
+#define MSG_IGN_WIDE_RESIDUE	0x23 /* O/O */
+
+/* Identify message */		     /* M/M */	
+#define MSG_IDENTIFYFLAG	0x80 
+#define MSG_IDENTIFY(lun, disc)	(((disc) ? 0xc0 : MSG_IDENTIFYFLAG) | (lun))
+#define MSG_ISIDENTIFY(m)	((m) & MSG_IDENTIFYFLAG)
+
+/* Extended messages (opcode and length) */
+#define MSG_EXT_SDTR		0x01
+#define MSG_EXT_SDTR_LEN	0x03
+
+#define MSG_EXT_WDTR		0x03
+#define MSG_EXT_WDTR_LEN	0x02
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx/sequencer.h linux/drivers/scsi/aic7xxx/sequencer.h
--- v2.0.30/linux/drivers/scsi/aic7xxx/sequencer.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/sequencer.h	Thu Jul 31 12:37:17 1997
@@ -0,0 +1,102 @@
+/*
+ * Instruction formats for the sequencer program downloaded to
+ * Aic7xxx SCSI host adapters
+ *
+ * Copyright (c) 1997 Justin T. Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *      $Id: sequencer.h,v 1.2 1997/06/27 19:38:52 gibbs Exp $
+ */
+
+#if defined(__KERNEL__)
+typedef unsigned char u_int8_t;
+#endif
+
+struct ins_format1 {
+	u_int8_t immediate;
+	u_int8_t source;
+	u_int8_t destination;
+	u_int8_t opcode_ret;
+};
+
+struct ins_format2 {
+	u_int8_t shift_control;
+	u_int8_t source;
+	u_int8_t destination;
+	u_int8_t opcode_ret;
+#define RETURN_BIT 0x01
+};
+
+struct ins_format3 {
+	u_int8_t immediate;
+	u_int8_t source;
+	u_int8_t address;
+	u_int8_t opcode_addr;
+#define ADDR_HIGH_BIT 0x01
+};
+
+struct instruction {
+	union {
+		struct ins_format1 format1;
+		struct ins_format2 format2;
+		struct ins_format3 format3;
+		u_int8_t	   bytes[4];
+	} format;
+	u_int	srcline;
+	struct symbol *patch_label;
+	struct {
+		struct instruction *stqe_next; /* next element */
+	} links;
+};
+
+#define	AIC_OP_OR	0x0
+#define	AIC_OP_AND	0x1
+#define AIC_OP_XOR	0x2
+#define	AIC_OP_ADD	0x3
+#define	AIC_OP_ADC	0x4
+#define	AIC_OP_ROL	0x5
+
+#define	AIC_OP_JMP	0x8
+#define AIC_OP_JC	0x9
+#define AIC_OP_JNC	0xa
+#define AIC_OP_CALL	0xb
+#define	AIC_OP_JNE	0xc
+#define	AIC_OP_JNZ	0xd
+#define	AIC_OP_JE	0xe
+#define	AIC_OP_JZ	0xf
+
+/* Pseudo Ops */
+#define	AIC_OP_SHL	0x10
+#define	AIC_OP_SHR	0x20
+#define	AIC_OP_ROR	0x30
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
--- v2.0.30/linux/drivers/scsi/aic7xxx.c	Tue Apr  8 08:47:46 1997
+++ linux/drivers/scsi/aic7xxx.c	Wed Aug 13 09:53:45 1997
@@ -1,5 +1,3 @@
-#define EXPERIMENTAL_FLAGS 0
-
 /*+M*************************************************************************
  * Adaptec AIC7xxx device driver for Linux.
  *
@@ -29,21 +27,73 @@
  * the Adaptec AIC-7770 Data Book, the ANSI SCSI specification, the
  * ANSI SCSI-2 specification (draft 10c), ...
  *
- * ----------------------------------------------------------------
- *  Modified to include support for wide and twin bus adapters,
- *  DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes,
+ * --------------------------------------------------------------------------
+ *
+ *  Modifications by Daniel M. Eischen (deischen@iworks.InterWorks.org):
+ *
+ *  Substantially modified to include support for wide and twin bus
+ *  adapters, DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes,
  *  SCB paging, and other rework of the code.
  *
- *  Parts of this driver are based on the FreeBSD driver by Justin
- *  T. Gibbs.
+ *  Parts of this driver were also based on the FreeBSD driver by
+ *  Justin T. Gibbs.  His copyright follows:
+ *
+ * --------------------------------------------------------------------------
+ * Copyright (c) 1994-1997 Justin Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *      $Id: aic7xxx.c,v 1.119 1997/06/27 19:39:18 gibbs Exp $
+ *---------------------------------------------------------------------------
+ *
+ *  Thanks also go to (in alphabetical order) the following:
+ *
+ *    Rory Bolt     - Sequencer bug fixes
+ *    Jay Estabrook - Initial DEC Alpha support
+ *    Doug Ledford  - Much needed abort/reset bug fixes
+ *    Kai Makisara  - DMAing of SCBs
  *
  *  A Boot time option was also added for not resetting the scsi bus.
  *
- *    Form:  aic7xxx=extended,no_reset
+ *    Form:  aic7xxx=extended
+ *           aic7xxx=no_reset
+ *           aic7xxx=ultra
+ *           aic7xxx=irq_trigger:[0,1]  # 0 edge, 1 level
+ *           aic7xxx=verbose
  *
- *    -- Daniel M. Eischen, deischen@iworks.InterWorks.org, 07/07/96
+ *  Daniel M. Eischen, deischen@iworks.InterWorks.org, 1/23/97
  *
- *  $Id: aic7xxx.c,v 4.0 1996/10/13 08:23:42 deang Exp $
+ *  $Id: aic7xxx.c,v 4.1 1997/06/12 08:23:42 deang Exp $
  *-M*************************************************************************/
 
 #ifdef MODULE
@@ -67,7 +117,11 @@
 #include "scsi.h"
 #include "hosts.h"
 #include "aic7xxx.h"
+
+#include "aic7xxx/sequencer.h"
+#include "aic7xxx/scsi_message.h"
 #include "aic7xxx_reg.h"
+#include "aic7xxx_seq.h"
 #include <linux/stat.h>
 #include <linux/malloc.h>	/* for kmalloc() */
 
@@ -81,14 +135,18 @@
 
 struct proc_dir_entry proc_scsi_aic7xxx = {
     PROC_SCSI_AIC7XXX, 7, "aic7xxx",
-    S_IFDIR | S_IRUGO | S_IXUGO, 2
+    S_IFDIR | S_IRUGO | S_IXUGO, 2,
+    0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-#define AIC7XXX_C_VERSION  "$Revision: 4.0 $"
+#define AIC7XXX_C_VERSION  "$Revision: 4.1 $"
 
 #define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
-#define MIN(a,b)        ((a < b) ? a : b)
+#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
+#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
 #define ALL_TARGETS -1
+#define ALL_CHANNELS '\0'
+#define ALL_LUNS -1
 #ifndef TRUE
 #  define TRUE 1
 #endif
@@ -107,11 +165,8 @@
  *     support because all PCI dependent code is bracketed with
  *     "#ifdef CONFIG_PCI ... #endif CONFIG_PCI".
  *
- *   o Twin bus support - this has been tested and does work.
- *
- *   o DMAing of SCBs - thanks to Kai Makisara, this now works.
- *     This define is now taken out and DMAing of SCBs is always
- *     performed (8/12/95 - DE).
+ *   o Twin bus support - this has been tested and does work.  It is
+ *     not an option anymore.
  *
  *   o Tagged queueing - this driver is capable of tagged queueing
  *     but I am unsure as to how well the higher level driver implements
@@ -140,16 +195,16 @@
  *     LUN using its own heuristic based on the number of available
  *     SCBs.
  *
- *   o 3985 support - The 3985 adapter is much like the 3940, but
- *     has three 7870 controllers as opposed to two for the 3940.
- *     It will get probed and recognized as three different adapters,
- *     but all three controllers can share the same external bank of
- *     255 SCBs.  If you enable AIC7XXX_SHARE_SCBS, then the driver
- *     will attempt to share the common bank of SCBs between the three
- *     controllers of the 3985.  This is experimental and hasn't
- *     been tested.  By default, we do not share the bank of SCBs,
- *     and force the controllers to use their own internal bank of
- *     16 SCBs.  Please let us know if sharing the SCB array works.
+ *   o 3985 support - The 3985 adapter is much like the 3940, but has
+ *     three 7870 controllers as opposed to two for the 3940.  It will
+ *     be probed and recognized as three different adapters, but all
+ *     three controllers can share the same external bank of 255 SCBs.
+ *     If you enable AIC7XXX_USE_EXT_SCBRAM, then the driver will attempt
+ *     to use and share the common bank of SCBs between the three
+ *     controllers of the 3985.  This is experimental and hasn't been
+ *     been tested.  By default, we do not use external SCB RAM, and
+ *     force the controllers to use their own internal bank of 16 SCBs.
+ *     Please let us know if using the external SCB array works.
  *
  *   o SCB paging support - SCB paging is enabled by defining
  *     AIC7XXX_PAGE_ENABLE.  Support for this was taken from the
@@ -162,43 +217,54 @@
  *  Note that sharing of IRQs is not an option any longer.  Linux supports
  *  it so we support it.
  *
- *  Daniel M. Eischen, deischen@iworks.InterWorks.org, 06/30/96
+ *  Daniel M. Eischen, deischen@iworks.InterWorks.org, 01/26/96
  */
 
-/* Uncomment this for testing twin bus support. */
-#define AIC7XXX_TWIN_SUPPORT
-
 /* Uncomment this for tagged queueing. */
-/* #define AIC7XXX_TAGGED_QUEUEING */
+#ifdef CONFIG_AIC7XXX_TAGGED_QUEUEING
+#define AIC7XXX_TAGGED_QUEUEING
+#endif
 
 /*
  * You can try raising me if tagged queueing is enabled, or lowering
  * me if you only have 4 SCBs.
  */
-/* #define AIC7XXX_CMDS_PER_LUN 8 */
+#ifdef CONFIG_AIC7XXX_CMDS_PER_LUN
+#define AIC7XXX_CMDS_PER_LUN CONFIG_AIC7XXX_CMDS_PER_LUN
+#endif
 
 /* Set this to the delay in seconds after SCSI bus reset. */
+#ifdef CONFIG_AIC7XXX_RESET_DELAY
+#define AIC7XXX_RESET_DELAY CONFIG_AIC7XXX_RESET_DELAY
+#else
 #define AIC7XXX_RESET_DELAY 15
+#endif
 
 /*
- * Uncomment the following define for collection of SCSI transfer statistics
- * for the /proc filesystem.
+ * Control collection of SCSI transfer statistics for the /proc filesystem.
  *
  * NOTE: Do NOT enable this when running on kernels version 1.2.x and below.
  * NOTE: This does affect performance since it has to maintain statistics.
  */
-/* #define AIC7XXX_PROC_STATS */
+#ifdef CONFIG_AIC7XXX_PROC_STATS
+#define AIC7XXX_PROC_STATS
+#endif
 
 /*
- * Uncomment the following to enable SCB paging.
+ * Enable SCB paging.
  */
-/* #define AIC7XXX_PAGE_ENABLE */
+#ifdef CONFIG_AIC7XXX_PAGE_ENABLE
+#define AIC7XXX_PAGE_ENABLE
+#endif
 
 /*
- * Uncomment the following to enable sharing of the external bank
- * of 255 SCBs for the 3985.
+ * Uncomment the following to enable use of the external bank
+ * of 255 SCBs.  For 3985 adapters, this will also enable sharing
+ * of the SCB array across all three controllers.
  */
-#define AIC7XXX_SHARE_SCBS
+#ifdef CONFIG_AIC7XXX_USE_EXT_SCBRAM
+#define AIC7XXX_USE_EXT_SCBRAM
+#endif
 
 /*
  * For debugging the abort/reset code.
@@ -211,6 +277,87 @@
 #define AIC7XXX_DEBUG
 
 /*
+ * Set this for defining the number of tagged commands on a device
+ * by device, and controller by controller basis.  The first set
+ * of tagged commands will be used for the first detected aic7xxx
+ * controller, the second set will be used for the second detected
+ * aic7xxx controller, and so on.  These values will *only* be used
+ * for targets that are tagged queueing capable; these values will
+ * be ignored in all other cases.  The tag_commands is an array of
+ * 16 to allow for wide and twin adapters.  Twin adapters will use
+ * indexes 0-7 for channel 0, and indexes 8-15 for channel 1.
+ *
+ * *** Determining commands per LUN ***
+ * 
+ * When AIC7XXX_CMDS_PER_LUN is not defined, the driver will use its
+ * own algorithm to determine the commands/LUN.  If SCB paging is
+ * enabled, the commands/LUN is 8.  When SCB paging is not enabled,
+ * then commands/LUN is 8 for adapters with 16 or more hardware SCBs
+ * and 4 commands/LUN for adapters with 3 or 4 SCBs.
+ *
+ */
+/* #define AIC7XXX_TAGGED_QUEUEING_BY_DEVICE */
+
+#ifdef AIC7XXX_TAGGED_QUEUEING_BY_DEVICE
+typedef struct
+{
+  char tag_commands[16];   /* Allow for wide/twin channel adapters. */
+} adapter_tag_info_t;
+
+/*
+ * Make a define that will tell the driver to use it's own algorithm
+ * for determining commands/LUN (see Determining commands per LUN
+ * above).
+ */
+#define DEFAULT_TAG_COMMANDS {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+
+/*
+ * Modify this as you see fit for your system.  By setting tag_commands
+ * to 0, the driver will use it's own algorithm for determining the
+ * number of commands to use (see above).  When -1, the driver will
+ * not enable tagged queueing for that particular device.  When positive
+ * (> 0) the values in the array are used for the queue_depth.  Note
+ * that the maximum value for an entry is 127.
+ *
+ * In this example, the first line will enable tagged queueing for all
+ * the devices on the first probed aic7xxx adapter and tells the driver
+ * to use it's own algorithm for determining commands/LUN.
+ *
+ * The second line enables tagged queueing with 4 commands/LUN for IDs
+ * (1, 2-11, 13-15), disables tagged queueing for ID 12, and tells the
+ * driver to use its own algorithm for ID 1.
+ *
+ * The third line is the same as the first line.
+ *
+ * The fourth line disables tagged queueing for devices 0 and 3.  It
+ * enables tagged queueing for the other IDs, with 16 commands/LUN
+ * for IDs 1 and 4, 127 commands/LUN for ID 8, and 4 commands/LUN for
+ * IDs 2, 5-7, and 9-15.
+ */
+adapter_tag_info_t aic7xxx_tag_info[] =
+{
+  {DEFAULT_TAG_COMMANDS},
+  {{4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, -1, 4, 4, 4}},
+  {DEFAULT_TAG_COMMANDS},
+  {{-1, 16, 4, -1, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}}
+};
+#endif
+
+/*
+ * Don't define this unless you have problems with the driver
+ * interrupt handler.  The old method would register the drivers
+ * interrupt handler as a "fast" type interrupt handler that would
+ * lock out other interrupts.  Since this driver can spend a lot
+ * of time in the interrupt handler, this is _not_ a good idea.
+ * It also conflicts with some of the more common ethernet drivers
+ * that don't use fast interrupts.  Currently, Linux does not allow
+ * IRQ sharing unless both drivers can agree on the type of interrupt
+ * handler.
+ */
+/* #define AIC7XXX_OLD_ISR_TYPE */
+
+
+/*
  * Controller type and options
  */
 typedef enum {
@@ -232,14 +379,15 @@
   AIC_7882,	/* PCI  aic7882 on 3940 Ultra */
   AIC_7883,	/* PCI  aic7883 on 3985 Ultra */
   AIC_7884	/* PCI  aic7884 on 294x Ultra Differential */
-} aha_type;
+} aha_chip_type;
 
 typedef enum {
   AIC_777x,	/* AIC-7770 based */
-  AIC_785x,	/* AIC-7850 based */
+  AIC_785x,	/* AIC-7850 based (3 SCBs)*/
+  AIC_786x,	/* AIC-7860 based (7850 ultra) */
   AIC_787x,	/* AIC-7870 based */
-  AIC_788x	/* AIC-7880 based */
-} aha_chip_type;
+  AIC_788x	/* AIC-7880 based (ultra) */
+} aha_chip_class_type;
 
 typedef enum {
   AIC_SINGLE,  /* Single Channel */
@@ -269,24 +417,24 @@
  * Don't forget to change this when changing the types!
  */
 static const char *board_names[] = {
-  "<AIC-7xxx Unknown>",		/* AIC_NONE */
-  "AIC-7770",			/* AIC_7770 */
-  "AHA-2740",			/* AIC_7771 */
-  "AHA-2840",			/* AIC_284x */
-  "AIC-7850",			/* AIC_7850 */
-  "AIC-7855",			/* AIC_7855 */
-  "AIC-7850 Ultra",		/* AIC_7860 */
-  "AHA-2940A Ultra",		/* AIC_7861 */
-  "AIC-7870",			/* AIC_7870 */
-  "AHA-2940",			/* AIC_7871 */
-  "AHA-3940",			/* AIC_7872 */
-  "AHA-3985",			/* AIC_7873 */
-  "AHA-2940 Differential",	/* AIC_7874 */
-  "AIC-7880 Ultra",		/* AIC_7880 */
-  "AHA-2940 Ultra",		/* AIC_7881 */
-  "AHA-3940 Ultra",		/* AIC_7882 */
-  "AHA-3985 Ultra",		/* AIC_7883 */
-  "AHA-2940 Ultra Differential"	/* AIC_7884 */
+  "AIC-7xxx Unknown",		                        /* AIC_NONE */
+  "Adaptec AIC-7770 SCSI host adapter",			/* AIC_7770 */
+  "Adaptec AHA-274X SCSI host adapter",			/* AIC_7771 */
+  "Adaptec AHA-284X SCSI host adapter",			/* AIC_284x */
+  "Adaptec AIC-7850 SCSI host adapter",			/* AIC_7850 */
+  "Adaptec AIC-7855 SCSI host adapter",			/* AIC_7855 */
+  "Adaptec AIC-7860 Ultra SCSI host adapter",		/* AIC_7860 */
+  "Adaptec AHA-2940A Ultra SCSI host adapter",		/* AIC_7861 */
+  "Adaptec AIC-7870 SCSI host adapter",			/* AIC_7870 */
+  "Adaptec AHA-294X SCSI host adapter",			/* AIC_7871 */
+  "Adaptec AHA-394X SCSI host adapter",			/* AIC_7872 */
+  "Adaptec AHA-398X SCSI host adapter",			/* AIC_7873 */
+  "Adaptec AHA-2944 SCSI host adapter",	                /* AIC_7874 */
+  "Adaptec AIC-7880 Ultra SCSI host adapter",		/* AIC_7880 */
+  "Adaptec AHA-294X Ultra SCSI host adapter",		/* AIC_7881 */
+  "Adaptec AHA-394X Ultra SCSI host adapter",		/* AIC_7882 */
+  "Adaptec AHA-398X Ultra SCSI host adapter",		/* AIC_7883 */
+  "Adaptec AHA-2944 Ultra SCSI host adapter"	        /* AIC_7884 */
 };
 
 /*
@@ -310,12 +458,17 @@
  */
 #define DID_RETRY_COMMAND DID_ERROR
 
+#define HSCSIID        0x07
+#define HWSCSIID       0x0F
+#define SCSI_RESET     0x040
+
 /*
  * EISA/VL-bus stuff
  */
 #define MINSLOT		1
 #define MAXSLOT		15
 #define SLOTBASE(x)	((x) << 12)
+#define BASE_TO_SLOT(x) ((x) >> 12)
 
 /*
  * Standard EISA Host ID regs  (Offset from slot base)
@@ -334,14 +487,6 @@
 #define INTDEF		0x5C		/* Interrupt Definition Register */
 
 /*
- * Some defines for the HCNTRL register.
- */
-#define	REQ_PAUSE	IRQMS | INTEN | PAUSE
-#define	UNPAUSE_274X	IRQMS | INTEN
-#define	UNPAUSE_284X	INTEN
-#define	UNPAUSE_294X	IRQMS | INTEN
-
-/*
  * AIC-78X0 PCI registers
  */
 #define	CLASS_PROGIF_REVID	0x08
@@ -377,7 +522,7 @@
  * each word, while the C56 and C66 (4096 bits) use 8 bits to
  * address each word.
  */
-typedef enum {c46 = 6, c56_66 = 8} seeprom_chip_type;
+typedef enum {C46 = 6, C56_66 = 8} seeprom_chip_type;
 
 /*
  *
@@ -417,15 +562,15 @@
 /*
  * Host Adapter Control Bits
  */
-/* UNUSED               0x0001 */
+#define CFAUTOTERM      0x0001          /* Perform Auto termination */
 #define CFULTRAEN       0x0002          /* Ultra SCSI speed enable (Ultra cards) */
 #define CF284XSELTO     0x0003          /* Selection timeout (284x cards) */
 #define CF284XFIFO      0x000C          /* FIFO Threshold (284x cards) */
-#define CFSTERM         0x0004          /* SCSI low byte termination (non-wide cards) */
+#define CFSTERM         0x0004          /* SCSI low byte termination */
 #define CFWSTERM        0x0008          /* SCSI high byte termination (wide card) */
 #define CFSPARITY	0x0010		/* SCSI parity */
 #define CF284XSTERM	0x0020		/* SCSI low byte termination (284x cards) */
-#define CFRESETB	0x0040		/* reset SCSI bus at IC initialization */
+#define CFRESETB	0x0040		/* reset SCSI bus at boot */
 /* UNUSED		0xFF80 */
   unsigned short adapter_control;	/* word 17 */
 
@@ -448,35 +593,17 @@
   unsigned short checksum;		/* word 31 */
 };
 
-
-#define SCSI_RESET 0x040
-
-/*
- * Pause the sequencer and wait for it to actually stop - this
- * is important since the sequencer can disable pausing for critical
- * sections.
- */
-#define PAUSE_SEQUENCER(p) \
-  outb(p->pause, HCNTRL + p->base);			\
-  while ((inb(HCNTRL + p->base) & PAUSE) == 0)		\
-    ;							\
-
-/*
- * Unpause the sequencer. Unremarkable, yet done often enough to
- * warrant an easy way to do it.
- */
-#define UNPAUSE_SEQUENCER(p) \
-  outb(p->unpause, HCNTRL + p->base)
-
-/*
- * Restart the sequencer program from address zero
- */
-#define RESTART_SEQUENCER(p) \
-  do {							\
-    outb(SEQRESET | FASTMODE, SEQCTL + p->base);	\
-  } while (inb(SEQADDR0 + p->base) != 0 &&		\
-	   inb(SEQADDR1 + p->base) != 0);		\
-  UNPAUSE_SEQUENCER(p);
+#define SELBUS_MASK		0x0a
+#define 	SELNARROW	0x00
+#define 	SELBUSB		0x08
+#define SINGLE_BUS		0x00
+
+#define SCB_TARGET(scb)         \
+       (((scb)->hscb->target_channel_lun & TID) >> 4)
+#define SCB_LUN(scb)            \
+       ((scb)->hscb->target_channel_lun & LID)
+#define SCB_IS_SCSIBUS_B(scb)   \
+       (((scb)->hscb->target_channel_lun & SELBUSB) != 0)
 
 /*
  * If an error occurs during a data transfer phase, run the command
@@ -540,12 +667,6 @@
 static int aic7xxx_spurious_count;
 
 /*
- * The driver keeps up to four scb structures per card in memory. Only the
- * first 25 bytes of the structure are valid for the hardware, the rest used
- * for driver level bookkeeping.
- */
-
-/*
  * As of Linux 2.1, the mid-level SCSI code uses virtual addresses
  * in the scatter-gather lists.  We need to convert the virtual
  * addresses to physical addresses.
@@ -558,20 +679,28 @@
 /*
  * Maximum number of SG segments these cards can support.
  */
-#define	MAX_SG 256
+#define	AIC7XXX_MAX_SG 27
 
-struct aic7xxx_scb {
+/*
+ * The maximum number of SCBs we could have for ANY type
+ * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE
+ * SEQUENCER CODE IF THIS IS MODIFIED!
+ */
+#define AIC7XXX_MAXSCB	255
+
+
+struct aic7xxx_hwscb {
 /* ------------    Begin hardware supported fields    ---------------- */
 /* 0*/  unsigned char control;
 /* 1*/  unsigned char target_channel_lun;       /* 4/1/3 bits */
 /* 2*/  unsigned char target_status;
 /* 3*/  unsigned char SG_segment_count;
-/* 4*/  unsigned char SG_list_pointer[4] __attribute__ ((packed));
+/* 4*/  unsigned int  SG_list_pointer;
 /* 8*/  unsigned char residual_SG_segment_count;
-/* 9*/  unsigned char residual_data_count[3] __attribute__ ((packed));
-/*12*/  unsigned char data_pointer[4] __attribute__ ((packed));
-/*16*/  unsigned int  data_count __attribute__ ((packed)); /* must be 32 bits */
-/*20*/  unsigned char SCSI_cmd_pointer[4] __attribute__ ((packed));
+/* 9*/  unsigned char residual_data_count[3];
+/*12*/  unsigned int  data_pointer;
+/*16*/  unsigned int  data_count;
+/*20*/  unsigned int  SCSI_cmd_pointer;
 /*24*/  unsigned char SCSI_cmd_length;
 /*25*/	u_char tag;			/* Index into our kernel SCB array.
 					 * Also used as the tag for tagged I/O
@@ -579,29 +708,47 @@
 #define SCB_PIO_TRANSFER_SIZE	26 	/* amount we need to upload/download
 					 * via PIO to initialize a transaction.
 					 */
-/*26*/  u_char next;                    /* Used to thread SCBs awaiting selection
+/*26*/  unsigned char next;             /* Used to thread SCBs awaiting selection
                                          * or disconnected down in the sequencer.
                                          */
-	/*-----------------end of hardware supported fields----------------*/
-	Scsi_Cmnd          *cmd;	/* Scsi_Cmnd for this scb */
-        struct aic7xxx_scb *q_next;     /* next scb in queue */
-#define SCB_FREE               0x00
-#define SCB_ACTIVE             0x01
-#define SCB_ABORTED            0x02
-#define SCB_DEVICE_RESET       0x04
-#define SCB_IMMED              0x08
-#define SCB_SENSE              0x10
-#define SCB_QUEUED_FOR_DONE    0x40
-#define SCB_PAGED_OUT          0x80
-#define SCB_WAITINGQ           0x100
-#define SCB_ASSIGNEDQ          0x200
-#define SCB_SENTORDEREDTAG     0x400
-#define SCB_IN_PROGRESS        (SCB_ACTIVE | SCB_PAGED_OUT | \
-                                SCB_WAITINGQ | SCB_ASSIGNEDQ)
-	int                 state;          /* current state of scb */
-	unsigned int        position;       /* Position in scb array */
-	struct hw_scatterlist  sg_list[MAX_SG]; /* SG list in adapter format */
-	unsigned char       sense_cmd[6];   /* Allocate 6 characters for sense command */
+/*27*/  unsigned char prev;
+/*28*/  unsigned int pad;               /*
+                                         * Unused by the kernel, but we require
+                                         * the padding so that the array of
+                                         * hardware SCBs is alligned on 32 byte
+                                         * boundaries so the sequencer can index
+                                         */
+};
+
+typedef enum {
+	SCB_FREE		= 0x0000,
+	SCB_ACTIVE		= 0x0001,
+	SCB_ABORTED		= 0x0002,
+	SCB_DEVICE_RESET	= 0x0004,
+	SCB_SENSE		= 0x0008,
+	SCB_TIMEDOUT		= 0x0010,
+	SCB_QUEUED_FOR_DONE	= 0x0020,
+	SCB_RECOVERY_SCB	= 0x0040,
+	SCB_WAITINGQ		= 0x0080,
+	SCB_ASSIGNEDQ		= 0x0100,
+	SCB_SENTORDEREDTAG	= 0x0200,
+	SCB_MSGOUT_SDTR		= 0x0400,
+	SCB_MSGOUT_WDTR		= 0x0800,
+	SCB_ABORT		= 0x1000,
+	SCB_QUEUED_ABORT	= 0x2000
+} scb_flag_type;
+
+struct aic7xxx_scb {
+        struct aic7xxx_hwscb  *hscb;          /* corresponding hardware scb */
+	Scsi_Cmnd             *cmd;	      /* Scsi_Cmnd for this scb */
+        struct aic7xxx_scb    *q_next;        /* next scb in queue */
+	scb_flag_type          flags;         /* current state of scb */
+	struct hw_scatterlist *sg_list;       /* SG list in adapter format */
+        unsigned char          sg_count;
+	unsigned char          sense_cmd[6];  /*
+                                               * Allocate 6 characters for
+                                               * sense command.
+                                               */
 };
 
 /*
@@ -626,20 +773,17 @@
 generic_sense[] = { REQUEST_SENSE, 0, 0, 0, 255, 0 };
 
 typedef struct {
+  struct aic7xxx_hwscb *hscbs;
   scb_queue_type free_scbs;        /*
                                     * SCBs assigned to free slot on
                                     * card (no paging required)
                                     */
-  int            numscbs;          /* current number of scbs */
-  int            activescbs;       /* active scbs */
-} scb_usage_type;
-
-/*
- * The maximum number of SCBs we could have for ANY type
- * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE
- * SEQUENCER CODE IF THIS IS MODIFIED!
- */
-#define AIC7XXX_MAXSCB	255
+  unsigned char  numscbs;          /* current number of scbs */
+  unsigned char  maxhscbs;         /* hardware scbs */
+  unsigned char  maxscbs;          /* max scbs including pageable scbs */
+  struct aic7xxx_scb   *scb_array[AIC7XXX_MAXSCB];
+  unsigned int   reserve[100];
+} scb_data_type;
 
 /*
  * Define a structure used for each host adapter, only one per IRQ.
@@ -647,17 +791,26 @@
 struct aic7xxx_host {
   struct Scsi_Host        *host;             /* pointer to scsi host */
   int                      host_no;          /* SCSI host number */
+  int                      instance;         /* aic7xxx instance number */
+  int                      scsi_id;          /* host adapter SCSI ID */
+  int                      scsi_id_b;        /*   channel B for twin adapters */
+  int                      irq;              /* IRQ for this adapter */
   int                      base;             /* card base address */
-  int                      maxhscbs;         /* hardware SCBs */
-  int                      maxscbs;          /* max SCBs (including pageable) */
-#define A_SCANNED              0x0001
-#define B_SCANNED              0x0002
-#define EXTENDED_TRANSLATION   0x0004
-#define HAVE_SEEPROM           0x0008
-#define ULTRA_ENABLED          0x0010
-#define PAGE_ENABLED           0x0020
-#define IN_ISR                 0x0040
-#define USE_DEFAULTS           0x0080
+  unsigned int             mbase;            /* I/O memory address */
+  volatile unsigned char  *maddr;            /* memory mapped address */
+#define A_SCANNED               0x0001
+#define B_SCANNED               0x0002
+#define EXTENDED_TRANSLATION    0x0004
+#define FLAGS_CHANNEL_B_PRIMARY 0x0008
+#define MULTI_CHANNEL           0x0010
+#define ULTRA_ENABLED           0x0020
+#define PAGE_ENABLED            0x0040
+#define USE_DEFAULTS            0x0080
+#define BIOS_ENABLED            0x0100
+#define IN_ISR                  0x0200
+#define IN_TIMEOUT              0x0400
+#define SHARED_SCBDATA          0x0800
+#define HAVE_SEEPROM            0x1000
   unsigned int             flags;
   unsigned int             isr_count;        /* Interrupt count */
   unsigned short           needsdtr_copy;    /* default config */
@@ -668,36 +821,23 @@
   unsigned short           wdtr_pending;
   unsigned short           orderedtag;
   unsigned short           discenable;	     /* Targets allowed to disconnect */
-  aha_type                 type;             /* card type */
-  aha_chip_type            chip_type;        /* chip base type */
+  aha_chip_type            chip_type;        /* card type */
+  aha_chip_class_type      chip_class;
   aha_bus_type             bus_type;         /* normal/twin/wide bus */
-  char *                   mbase;            /* I/O memory address */
-  unsigned char            chan_num;         /* for 3940/3985, channel number */
+  unsigned char            chan_num;         /* for 39xx, channel number */
   unsigned char            unpause;          /* unpause value for HCNTRL */
   unsigned char            pause;            /* pause value for HCNTRL */
   unsigned char            qcntmask;
-  struct seeprom_config    seeprom;
+  unsigned char            qfullcount;
+  unsigned char            cmdoutcnt;
+  unsigned char            curqincnt;
   struct Scsi_Host        *next;             /* allow for multiple IRQs */
-  struct aic7xxx_scb      *scb_array[AIC7XXX_MAXSCB];  /* active commands */
-  struct aic7xxx_scb      *pagedout_ntscbs[16];  /*
-                                                  * paged-out, non-tagged scbs
-                                                  * indexed by target.
-                                                  */
-  scb_queue_type           page_scbs;        /*
-                                              * SCBs that will require paging
-                                              * before use (no assigned slot)
-                                              */
+  unsigned char            activescbs;       /* active scbs */
   scb_queue_type           waiting_scbs;     /*
-                                              * SCBs waiting to be paged and
-                                              * started.
-                                              */
-  scb_queue_type           assigned_scbs;    /*
-                                              * SCBs that were waiting but have
-                                              * have now been assigned a slot
-                                              * by aic7xxx_free_scb
+                                              * SCBs waiting for space in
+                                              * the QINFIFO.
                                               */
-  scb_usage_type           scb_usage;
-  scb_usage_type          *scb_link;
+  scb_data_type           *scb_data;
 
   struct aic7xxx_cmd_queue {
     Scsi_Cmnd *head;
@@ -709,6 +849,7 @@
 #define  BUS_DEVICE_RESET_PENDING       0x02
     int  flags;
     int  commands_sent;
+    int  active_cmds;
   } device_status[16];
 #ifdef AIC7XXX_PROC_STATS
   /*
@@ -734,34 +875,10 @@
 #endif /* AIC7XXX_PROC_STATS */
 };
 
-struct aic7xxx_host_config {
-  int              irq;        /* IRQ number */
-  int              mbase;      /* memory base address*/
-  int              base;       /* I/O base address*/
-  int              maxhscbs;   /* hardware SCBs */
-  int              maxscbs;    /* max SCBs (including pageable) */
-  int              unpause;    /* unpause value for HCNTRL */
-  int              pause;      /* pause value for HCNTRL */
-  int              scsi_id;    /* host SCSI ID */
-  int              scsi_id_b;  /* host SCSI ID B channel for twin cards */
-  unsigned int     flags;      /* used the same as struct aic7xxx_host flags */
-  int              chan_num;   /* for 3940/3985, channel number */
-  unsigned char    busrtime;   /* bus release time */
-  unsigned char    bus_speed;  /* bus speed */
-  unsigned char    qcntmask;
-  aha_type         type;       /* card type */
-  aha_chip_type    chip_type;  /* chip base type */
-  aha_bus_type     bus_type;   /* normal/twin/wide bus */
-  aha_status_type  bios;       /* BIOS is enabled/disabled */
-  aha_status_type  parity;     /* bus parity enabled/disabled */
-  aha_status_type  low_term;   /* bus termination low byte */
-  aha_status_type  high_term;  /* bus termination high byte (wide cards only) */
-};
-
 /*
  * Valid SCSIRATE values. (p. 3-17)
- * Provides a mapping of transfer periods in ns to the proper value to
- * stick in the scsiscfr reg to use that transfer rate.
+ * Provides a mapping of transfer periods in ns/4 to the proper value to
+ * stick in the SCSIRATE reg to use that transfer rate.
  */
 static struct {
   short period;
@@ -770,17 +887,17 @@
   short rate;
   const char *english;
 } aic7xxx_syncrates[] = {
-  {  50,  0x100,  "20.0"  },
-  {  62,  0x110,  "16.0"  },
-  {  75,  0x120,  "13.4"  },
-  { 100,  0x000,  "10.0"  },
-  { 125,  0x010,   "8.0"  },
-  { 150,  0x020,   "6.67" },
-  { 175,  0x030,   "5.7"  },
-  { 200,  0x040,   "5.0"  },
-  { 225,  0x050,   "4.4"  },
-  { 250,  0x060,   "4.0"  },
-  { 275,  0x070,   "3.6"  }
+  { 12,  0x100,  "20.0"  },
+  { 15,  0x110,  "16.0"  },
+  { 18,  0x120,  "13.4"  },
+  { 25,  0x000,  "10.0"  },
+  { 31,  0x010,   "8.0"  },
+  { 37,  0x020,   "6.67" },
+  { 43,  0x030,   "5.7"  },
+  { 50,  0x040,   "5.0"  },
+  { 56,  0x050,   "4.4"  },
+  { 62,  0x060,   "4.0"  },
+  { 68,  0x070,   "3.6"  }
 };
 
 static int num_aic7xxx_syncrates =
@@ -789,166 +906,51 @@
 #ifdef CONFIG_PCI
 static int number_of_3940s = 0;
 static int number_of_3985s = 0;
-#ifdef AIC7XXX_SHARE_SCBS
-static scb_usage_type *shared_3985_scbs = NULL;
-#endif
-#endif CONFIG_PCI
+#endif /* CONFIG_PCI */
 
 #ifdef AIC7XXX_DEBUG
 
-static void
-debug_config(struct aic7xxx_host_config *p)
-{
-  int scsi_conf;
-  unsigned char brelease;
-  unsigned char dfthresh;
-
-  static int DFT[] = { 0, 50, 75, 100 };
-  static int SST[] = { 256, 128, 64, 32 };
-  static const char *BUSW[] = { "", "-TWIN", "-WIDE" };
-
-  scsi_conf = inb(SCSICONF + p->base);
-
-  /*
-   * Scale the Data FIFO Threshhold and the Bus Release Time; they are
-   * stored in formats compatible for writing to sequencer registers.
-   */
-  dfthresh = p->bus_speed  >> 6;
-
-  if (p->chip_type == AIC_777x)
-  {
-    brelease = p->busrtime >> 2;
-  }
-  else
-  {
-    brelease = p->busrtime;
-  }
-  if (brelease == 0)
-  {
-    brelease = 2;
-  }
-
-  switch (p->type)
-  {
-    case AIC_7770:
-    case AIC_7771:
-      printk("%s%s AT EISA SLOT %d:\n", board_names[p->type], BUSW[p->bus_type],
-             p->base >> 12);
-      break;
-
-    case AIC_284x:
-      printk("%s%s AT VLB SLOT %d:\n", board_names[p->type], BUSW[p->bus_type],
-             p->base >> 12);
-      break;
-
-    case AIC_7850:
-    case AIC_7855:
-    case AIC_7860:
-    case AIC_7861:
-    case AIC_7870:
-    case AIC_7871:
-    case AIC_7872:
-    case AIC_7873:
-    case AIC_7874:
-    case AIC_7880:
-    case AIC_7881:
-    case AIC_7882:
-    case AIC_7883:
-    case AIC_7884:
-      printk("%s%s (PCI-bus), I/O 0x%x, Mem 0x%x:\n", board_names[p->type],
-             BUSW[p->bus_type], p->base, p->mbase);
-      break;
-
-    default:
-      panic("aic7xxx: (debug_config) internal error.\n");
-  }
-
-  printk("    irq %d\n"
-	 "    bus release time %d bclks\n"
-	 "    data fifo threshold %d%%\n",
-	 p->irq,
-	 brelease,
-	 DFT[dfthresh]);
-
-  printk("    SCSI CHANNEL A:\n"
-	 "        scsi id %d\n"
-	 "        scsi selection timeout %d ms\n"
-	 "        scsi bus reset at power-on %sabled\n",
-	 (p->bus_type & AIC_WIDE) ? (scsi_conf & 0x0f) : (scsi_conf & 0x07),
-	 SST[(scsi_conf >> 3) & 0x03],
-	 (scsi_conf & 0x40) ? "en" : "dis");
-
-  if ((p->chip_type == AIC_777x) && (p->parity == AIC_UNKNOWN))
-  {
-    /*
-     * Set the parity for 7770 based cards.
-     */
-    p->parity = (scsi_conf & 0x20) ? AIC_ENABLED : AIC_DISABLED;
-  }
-  if (p->parity != AIC_UNKNOWN)
-  {
-    printk("        scsi bus parity %sabled\n",
-	   (p->parity == AIC_ENABLED) ? "en" : "dis");
-  }
-
-  if ((p->type == AIC_7770) || (p->type == AIC_7771))
-  {
-    p->low_term = (scsi_conf & 0x80) ? AIC_ENABLED : AIC_DISABLED;
-  }
-  if (p->low_term != AIC_UNKNOWN)
-  {
-    printk("        scsi bus termination (low byte) %sabled\n",
-	  (p->low_term == AIC_ENABLED) ? "en" : "dis");
-  }
-  if ((p->bus_type == AIC_WIDE) && (p->high_term != AIC_UNKNOWN))
-  {
-    printk("        scsi bus termination (high byte) %sabled\n",
-	  (p->high_term == AIC_ENABLED) ? "en" : "dis");
-  }
-}
-
 #if 0
 static void
 debug_scb(struct aic7xxx_scb *scb)
 {
-  printk("control 0x%x, tcl 0x%x, sg_count %d, sg_ptr 0x%x, cmdp 0x%x, cmdlen %d\n",
-         scb->control, scb->target_channel_lun, scb->SG_segment_count,
-         (scb->SG_list_pointer[3] << 24) | (scb->SG_list_pointer[2] << 16) |
-         (scb->SG_list_pointer[1] << 8) | scb->SG_list_pointer[0],
-         (scb->SCSI_cmd_pointer[3] << 24) | (scb->SCSI_cmd_pointer[2] << 16) |
-         (scb->SCSI_cmd_pointer[1] << 8) | scb->SCSI_cmd_pointer[0],
-         scb->SCSI_cmd_length);
-  printk("reserved 0x%x, target status 0x%x, resid SG count %d, resid data count %d\n",
-         (scb->RESERVED[1] << 8) | scb->RESERVED[0], scb->target_status,
-         scb->residual_SG_segment_count,
-         ((scb->residual_data_count[2] << 16) |
-          (scb->residual_data_count[1] <<  8) |
-          (scb->residual_data_count[0]));
-  printk("data ptr 0x%x, data count %d, next waiting %d\n",
-         (scb->data_pointer[3] << 24) | (scb->data_pointer[2] << 16) |
-         (scb->data_pointer[1] << 8) | scb->data_pointer[0],
-         scb->data_count, scb->next_waiting);
-  printk("next ptr 0x%lx, Scsi Cmnd 0x%lx, state 0x%x, position %d\n",
-         (unsigned long) scb->next, (unsigned long) scb->cmd, scb->state,
-         scb->position);
+  struct aic7xxx_hwscb *hscb = scb->hscb;
+
+  printk("scb:%p control:0x%x tcl:0x%x cmdlen:%d cmdpointer:0x%lx\n",
+    scb,
+    hscb->control,
+    hscb->target_channel_lun,
+    hscb->SCSI_cmd_length,
+    hscb->SCSI_cmd_pointer );
+  printk("        datlen:%d data:0x%lx segs:0x%x segp:0x%lx\n",
+    hscb->data_count,
+    hscb->data_pointer,
+    hscb->SG_segment_count,
+    hscb->SG_list_pointer);
+  printk("        sg_addr:%lx sg_len:%ld\n",
+    hscb->sg_list[0].address,
+    hscb->sg_list[0].length);
 }
 #endif
 
 #else
-#  define debug_config(x)
 #  define debug_scb(x)
 #endif AIC7XXX_DEBUG
 
-#define TCL_OF_SCB(x)  (((x)->target_channel_lun >> 4) & 0xf),  \
-                       (((x)->target_channel_lun >> 3) & 0x01), \
-                       ((x)->target_channel_lun & 0x07)
+#define TCL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 4) & 0xf),  \
+                        (((scb->hscb)->target_channel_lun >> 3) & 0x01), \
+                        ((scb->hscb)->target_channel_lun & 0x07)
 
-#define TARGET_INDEX(x)  ((x)->target | ((x)->channel << 3))
+#define TC_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 4) & 0xf),  \
+                       (((scb->hscb)->target_channel_lun >> 3) & 0x01)
+
+#define CHAN_TO_INT(chan) ((chan) == 'A' ? 0 : 1)
+
+#define TARGET_INDEX(cmd)  ((cmd)->target | ((cmd)->channel << 3))
 
 /*
  * XXX - these options apply unilaterally to _all_ 274x/284x/294x
- *       cards in the system. This should be fixed, but then,
- *       does anyone really have more than one in a machine?
+ *       cards in the system.  This should be fixed.
  */
 static unsigned int aic7xxx_extended = 0;    /* extended translation on? */
 static unsigned int aic7xxx_no_reset = 0;    /* no resetting of SCSI bus */
@@ -958,6 +960,53 @@
                                               *  1 use level triggered
                                               */
 static int aic7xxx_enable_ultra = 0;         /* enable ultra SCSI speeds */
+static int aic7xxx_verbose = 0;	             /* verbose messages */
+
+
+/****************************************************************************
+ *
+ * These functions are not used yet, but when we do memory mapped
+ * IO, we'll use them then.
+ *
+ ***************************************************************************/
+static inline unsigned char
+aic_inb(struct aic7xxx_host *p, long port)
+{
+  if (p->maddr != NULL)
+    return (p->maddr[port]);
+  else
+    return (inb(p->base + port));
+}
+
+static inline void
+aic_outb(struct aic7xxx_host *p, unsigned char val, long port)
+{
+  if (p->maddr != NULL)
+    p->maddr[port] = val;
+  else
+    outb(val, p->base + port);
+}
+
+static inline void
+aic_outsb(struct aic7xxx_host *p, long port, unsigned char *valp, size_t size)
+{
+  if (p->maddr != NULL)
+  {
+    __asm __volatile("
+      cld;
+    1:  lodsb;
+      movb %%al,(%0);
+      loop 1b"      :
+              :
+      "r" ((p)->maddr + (port)),
+      "S" ((valp)), "c" ((size))  :
+      "%esi", "%ecx", "%eax");
+  }
+  else
+  {
+    outsb(p->base + port, valp, size);
+  }
+}
 
 /*+F*************************************************************************
  * Function:
@@ -982,6 +1031,7 @@
     { "no_reset",    &aic7xxx_no_reset },
     { "irq_trigger", &aic7xxx_irq_trigger },
     { "ultra",       &aic7xxx_enable_ultra },
+    { "verbose",     &aic7xxx_verbose },
     { NULL,          NULL }
   };
 
@@ -1007,162 +1057,343 @@
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_loadseq
+ *   pause_sequencer
  *
  * Description:
- *   Load the sequencer code into the controller memory.
+ *   Pause the sequencer and wait for it to actually stop - this
+ *   is important since the sequencer can disable pausing for critical
+ *   sections.
  *-F*************************************************************************/
-static void
-aic7xxx_loadseq(int base)
+static inline void
+pause_sequencer(struct aic7xxx_host *p)
 {
-  static unsigned char seqprog[] = {
-    /*
-     * Each sequencer instruction is 29 bits
-     * long (fill in the excess with zeroes)
-     * and has to be loaded from least -> most
-     * significant byte, so this table has the
-     * byte ordering reversed.
-     */
-#   include "aic7xxx_seq.h"
-  };
-
-  /*
-   * When the AIC-7770 is paused (as on chip reset), the
-   * sequencer address can be altered and a sequencer
-   * program can be loaded by writing it, byte by byte, to
-   * the sequencer RAM port - the Adaptec documentation
-   * recommends using REP OUTSB to do this, hence the inline
-   * assembly. Since the address autoincrements as we load
-   * the program, reset it back to zero afterward. Disable
-   * sequencer RAM parity error detection while loading, and
-   * make sure the LOADRAM bit is enabled for loading.
-   */
-  outb(PERRORDIS | SEQRESET | LOADRAM, SEQCTL + base);
-
-  outsb(SEQRAM + base, seqprog, sizeof(seqprog));
-
-  /*
-   * WARNING!  This is a magic sequence!  After extensive
-   * experimentation, it seems that you MUST turn off the
-   * LOADRAM bit before you play with SEQADDR again, else
-   * you will end up with parity errors being flagged on
-   * your sequencer program. (You would also think that
-   * turning off LOADRAM and setting SEQRESET to reset the
-   * address to zero would work, but you need to do it twice
-   * for it to take effect on the address. Timing problem?)
-   */
-  do {
-    /*
-     * Actually, reset it until
-     * the address shows up as
-     * zero just to be safe..
-     */
-    outb(SEQRESET | FASTMODE, SEQCTL + base);
-  } while ((inb(SEQADDR0 + base) != 0) && (inb(SEQADDR1 + base) != 0));
+  outb(p->pause, p->base + HCNTRL);
+  while ((inb(p->base + HCNTRL) & PAUSE) == 0)
+  {
+    ;
+  }
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_delay
+ *   unpause_sequencer
  *
  * Description:
- *   Delay for specified amount of time.
+ *   Unpause the sequencer. Unremarkable, yet done often enough to
+ *   warrant an easy way to do it.
  *-F*************************************************************************/
-static void
-aic7xxx_delay(int seconds)
+static inline void
+unpause_sequencer(struct aic7xxx_host *p, int unpause_always)
 {
-  unsigned long i;
-
-  i = jiffies + (seconds * HZ);  /* compute time to stop */
-
-  while (jiffies < i)
+  if (unpause_always ||
+      ((inb(p->base + INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0))
   {
-    ;  /* Do nothing! */
+    outb(p->unpause, p->base + HCNTRL);
   }
 }
 
 /*+F*************************************************************************
  * Function:
- *   rcs_version
+ *   restart_sequencer
  *
  * Description:
- *   Return a string containing just the RCS version number from either
- *   an Id or Revision RCS clause.
+ *   Restart the sequencer program from address zero.  This assumes
+ *   that the sequencer is already paused.
  *-F*************************************************************************/
-const char *
-rcs_version(const char *version_info)
+static inline void
+restart_sequencer(struct aic7xxx_host *p)
 {
-  static char buf[10];
-  char *bp, *ep;
+  /* Set the sequencer address to 0. */
+  outb(0, p->base + SEQADDR0);
+  outb(0, p->base + SEQADDR1);
 
-  bp = NULL;
-  strcpy(buf, "????");
-  if (!strncmp(version_info, "$Id: ", 5))
-  {
-    if ((bp = strchr(version_info, ' ')) != NULL)
-    {
-      bp++;
-      if ((bp = strchr(bp, ' ')) != NULL)
-      {
-	bp++;
-      }
-    }
-  }
-  else
+  /*
+   * Reset and unpause the sequencer.  The reset is suppose to
+   * start the sequencer running, but we do an unpause to make
+   * sure.
+   */
+  outb(SEQRESET | FASTMODE, p->base + SEQCTL);
+
+  unpause_sequencer(p, /*unpause_always*/ TRUE);
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_next_patch
+ *
+ * Description:
+ *   Find the next patch to download.
+ *-F*************************************************************************/
+static struct patch *
+aic7xxx_next_patch(struct patch *cur_patch, int options, int instrptr)
+{
+  while (cur_patch != NULL)
   {
-    if (!strncmp(version_info, "$Revision: ", 11))
+    if ((((cur_patch->options & options) != 0) && (cur_patch->negative == FALSE))
+      || (((cur_patch->options & options) == 0) && (cur_patch->negative == TRUE))
+      || (instrptr >= cur_patch->end))
     {
-      if ((bp = strchr(version_info, ' ')) != NULL)
+      /*
+       * Either we want to keep this section of code, or we have consumed
+       * this patch.  Skip to the next patch.
+       */
+      cur_patch++;
+      if (cur_patch->options == 0)
       {
-	bp++;
+        /* Out of patches. */
+        cur_patch = NULL;
       }
     }
-  }
-
-  if (bp != NULL)
-  {
-    if ((ep = strchr(bp, ' ')) != NULL)
+    else
     {
-      register int len = ep - bp;
-
-      strncpy(buf, bp, len);
-      buf[len] = '\0';
+      /* Found an OK patch. */
+      break;
     }
   }
-
-  return buf;
+  return (cur_patch);
 }
 
+
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_info
+ *   aic7xxx_download_instr
  *
  * Description:
- *   Return a string describing the driver.
+ *   Find the next patch to download.
  *-F*************************************************************************/
-const char *
-aic7xxx_info(struct Scsi_Host *notused)
+static void
+aic7xxx_download_instr(struct aic7xxx_host *p, int options, int instrptr)
 {
-  static char buffer[128];
+  unsigned char opcode;
+  struct ins_format3 *instr;
 
-  strcpy(buffer, "Adaptec AHA274x/284x/294x (EISA/VLB/PCI-Fast SCSI) ");
-  strcat(buffer, rcs_version(AIC7XXX_C_VERSION));
-  strcat(buffer, "/");
-  strcat(buffer, rcs_version(AIC7XXX_H_VERSION));
-  strcat(buffer, "/");
-  strcat(buffer, rcs_version(AIC7XXX_SEQ_VER));
+  instr = (struct ins_format3 *) &seqprog[instrptr * 4];
+  /* Pull the opcode */
+  opcode = instr->opcode_addr >> 1;
+  switch (opcode)
+  {
+    case AIC_OP_JMP:
+    case AIC_OP_JC:
+    case AIC_OP_JNC:
+    case AIC_OP_CALL:
+    case AIC_OP_JNE:
+    case AIC_OP_JNZ:
+    case AIC_OP_JE:
+    case AIC_OP_JZ:
+    {
+      int address_offset;
+      struct ins_format3 new_instr;
+      unsigned int address;
+      struct patch *patch;
+      int i;
+
+      address_offset = 0;
+      new_instr = *instr;  /* Strucure copy */
+      address = new_instr.address;
+      address |= (new_instr.opcode_addr & ADDR_HIGH_BIT) << 8;
+      for (i = 0; i < NUMBER(patches); i++)
+      {
+        patch = &patches[i];
+        if ((((patch->options & options) == 0) && (patch->negative == FALSE)) ||
+            (((patch->options & options) != 0) && (patch->negative == TRUE)))
+        {
+          if (address >= patch->end)
+          {
+            address_offset += patch->end - patch->begin;
+          }
+        }
+      }
+      address -= address_offset;
+      new_instr.address = address &0xFF;
+      new_instr.opcode_addr &= ~ADDR_HIGH_BIT;
+      new_instr.opcode_addr |= (address >> 8) & ADDR_HIGH_BIT;
+      outsb(p->base + SEQRAM, &new_instr.immediate, 4);
+      break;
+    }
 
-  return buffer;
-}
+    case AIC_OP_OR:
+    case AIC_OP_AND:
+    case AIC_OP_XOR:
+    case AIC_OP_ADD:
+    case AIC_OP_ADC:
+    case AIC_OP_ROL:
+      outsb(p->base + SEQRAM, &instr->immediate, 4);
+      break;
+
+    default:
+      panic("aic7xxx: Unknown opcode encountered in sequencer program.");
+      break;
+  }
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_loadseq
+ *
+ * Description:
+ *   Load the sequencer code into the controller memory.
+ *-F*************************************************************************/
+static void
+aic7xxx_loadseq(struct aic7xxx_host *p)
+{
+  int options;
+  struct patch *cur_patch;
+  int i;
+  int downloaded;
+
+  if (aic7xxx_verbose)
+  {
+    printk(KERN_INFO "aic7xxx: Downloading sequencer code...");
+  }
+  options = 1;  /* Code for all options. */
+  downloaded = 0;
+  if ((p->flags & ULTRA_ENABLED) != 0)
+    options |= ULTRA;
+  if (p->bus_type == AIC_TWIN)
+    options |= TWIN_CHANNEL;
+  if (p->scb_data->maxscbs > p->scb_data->maxhscbs)
+    options |= SCB_PAGING;
+
+  cur_patch = patches;
+  outb(PERRORDIS | LOADRAM, p->base + SEQCTL);
+  outb(0, p->base + SEQADDR0);
+  outb(0, p->base + SEQADDR1);
+
+  for (i = 0; i < sizeof(seqprog) / 4;  i++)
+  {
+    cur_patch = aic7xxx_next_patch(cur_patch, options, i);
+    if (cur_patch && (cur_patch->begin <= i) && (cur_patch->end > i))
+    {
+      /* Skip this instruction for this configuration. */
+      continue;
+    }
+    aic7xxx_download_instr(p, options, i);
+    downloaded++;
+  }
+
+  outb(FASTMODE, p->base + SEQCTL);
+  outb(0, p->base + SEQADDR0);
+  outb(0, p->base + SEQADDR1);
+
+  if (aic7xxx_verbose)
+  {
+     printk(" %d instructions downloaded\n", downloaded);
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_delay
+ *
+ * Description:
+ *   Delay for specified amount of time.  We use udelay because the timer
+ *   interrupt is not guaranteed to be enabled.  This will cause an
+ *   infinite loop since jiffies (clock ticks) is not updated.
+ *-F*************************************************************************/
+static void
+aic7xxx_delay(int seconds)
+{
+  int i;
+
+  /*                        
+   * Call udelay() for 1 millisecond inside a loop for  
+   * the requested amount of seconds.
+   */
+  for (i=0; i < seconds*1000; i++)
+  {
+    udelay(1000);  /* Delay for 1 millisecond. */
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   rcs_version
+ *
+ * Description:
+ *   Return a string containing just the RCS version number from either
+ *   an Id or Revision RCS clause.
+ *-F*************************************************************************/
+const char *
+rcs_version(const char *version_info)
+{
+  static char buf[10];
+  char *bp, *ep;
+
+  bp = NULL;
+  strcpy(buf, "????");
+  if (!strncmp(version_info, "$Id: ", 5))
+  {
+    if ((bp = strchr(version_info, ' ')) != NULL)
+    {
+      bp++;
+      if ((bp = strchr(bp, ' ')) != NULL)
+      {
+	bp++;
+      }
+    }
+  }
+  else
+  {
+    if (!strncmp(version_info, "$Revision: ", 11))
+    {
+      if ((bp = strchr(version_info, ' ')) != NULL)
+      {
+	bp++;
+      }
+    }
+  }
+
+  if (bp != NULL)
+  {
+    if ((ep = strchr(bp, ' ')) != NULL)
+    {
+      register int len = ep - bp;
+
+      strncpy(buf, bp, len);
+      buf[len] = '\0';
+    }
+  }
+
+  return buf;
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_info
+ *
+ * Description:
+ *   Return a string describing the driver.
+ *-F*************************************************************************/
+const char *
+aic7xxx_info(struct Scsi_Host *notused)
+{
+  static char buffer[128];
+
+  strcpy(buffer, "Adaptec AHA274x/284x/294x (EISA/VLB/PCI-Fast SCSI) ");
+  strcat(buffer, rcs_version(AIC7XXX_C_VERSION));
+  strcat(buffer, "/");
+  strcat(buffer, rcs_version(AIC7XXX_H_VERSION));
+#if 0
+  strcat(buffer, "/");
+  strcat(buffer, rcs_version(AIC7XXX_SEQ_VER));
+#endif
+
+  return buffer;
+}
 
 /*+F*************************************************************************
  * Function:
  *   aic7xxx_length
  *
  * Description:
- *   How much data should be transferred for this SCSI command? Stop
- *   at segment sg_last if it's a scatter-gather command so we can
- *   compute underflow easily.
+ *   How much data should be transferred for this SCSI command?  Assume
+ *   all segments are to be transferred except for the last sg_last
+ *   segments.  This will allow us to compute underflow easily.  To
+ *   calculate the total length of the command, use sg_last = 0.  To
+ *   calculate the length of all but the last 2 SG segments, use
+ *   sg_last = 2.
  *-F*************************************************************************/
 static unsigned
 aic7xxx_length(Scsi_Cmnd *cmd, int sg_last)
@@ -1176,7 +1407,7 @@
 
   if (cmd->use_sg)
   {
-    for (i = length = 0; (i < cmd->use_sg) && (i < segments); i++)
+    for (i = length = 0; i < segments; i++)
     {
       length += sg[i].length;
     }
@@ -1198,9 +1429,9 @@
  *-F*************************************************************************/
 static void
 aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate,
-    short period, unsigned char offset, int target, char channel)
+    unsigned char *period, unsigned char *offset, int target, char channel)
 {
-  int i;
+  int i = num_aic7xxx_syncrates;
   unsigned long ultra_enb_addr;
   unsigned char ultra_enb, sxfrctl0;
 
@@ -1208,11 +1439,11 @@
    * If the offset is 0, then the device is requesting asynchronous
    * transfers.
    */
-  if (offset != 0)
+  if ((*period >= aic7xxx_syncrates[i].period) && *offset != 0)
   {
     for (i = 0; i < num_aic7xxx_syncrates; i++)
     {
-      if ((aic7xxx_syncrates[i].period - period) >= 0)
+      if (*period <= aic7xxx_syncrates[i].period)
       {
         /*
          * Watch out for Ultra speeds when ultra is not enabled and
@@ -1228,99 +1459,57 @@
            */
           continue;
         }
-        *scsirate = (aic7xxx_syncrates[i].rate) | (offset & 0x0F);
+        *scsirate = (aic7xxx_syncrates[i].rate & 0xF0) | (*offset & 0x0F);
+        *period = aic7xxx_syncrates[i].period;
 
-        /*
-         * Ensure Ultra mode is set properly for this target.
-         */
-        ultra_enb_addr = ULTRA_ENB;
-        if ((channel == 'B') || (target > 7))
+        if (aic7xxx_verbose)
         {
-          ultra_enb_addr++;
+          printk("scsi%d: Target %d, channel %c, now synchronous at %sMHz, "
+                 "offset %d.\n", p->host_no, target, channel,
+                 aic7xxx_syncrates[i].english, *offset);
         }
-        ultra_enb = inb(p->base + ultra_enb_addr);
-        sxfrctl0 = inb(p->base + SXFRCTL0);
-        if (aic7xxx_syncrates[i].rate & ULTRA_SXFR)
-        {
-          ultra_enb |= 0x01 << (target & 0x07);
-          sxfrctl0 |= ULTRAEN;
-        }
-        else
-        {
-          ultra_enb &= ~(0x01 << (target & 0x07));
-          sxfrctl0 &= ~ULTRAEN;
-        }
-        outb(ultra_enb, p->base + ultra_enb_addr);
-        outb(sxfrctl0, p->base + SXFRCTL0);
-
-        printk("scsi%d: Target %d, channel %c, now synchronous at %sMHz, "
-               "offset %d.\n", p->host_no, target, channel,
-               aic7xxx_syncrates[i].english, offset);
-        return;
+        break;
       }
     }
   }
 
-  /*
-   * Default to asynchronous transfer
-   */
-  *scsirate = 0;
-  printk("scsi%d: Target %d, channel %c, using asynchronous transfers.\n",
-         p->host_no, target, channel);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_putscb
- *
- * Description:
- *   Transfer a SCB to the controller.
- *-F*************************************************************************/
-static inline void
-aic7xxx_putscb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
-  int base = p->base;
-
-  outb(SCBAUTO, SCBCNT + base);
+  if (i >= num_aic7xxx_syncrates)
+  {
+    /*
+     * Use asynchronous transfers.
+     */
+    *scsirate = 0;
+    *period = 0;
+    *offset = 0;
+    if (aic7xxx_verbose)
+    {
+      printk("scsi%d: Target %d, channel %c, using asynchronous transfers.\n",
+             p->host_no, target, channel);
+    }
+  }
 
   /*
-   * By turning on the SCB auto increment, any reference
-   * to the SCB I/O space postincrements the SCB address
-   * we're looking at. So turn this on and dump the relevant
-   * portion of the SCB to the card.
-   *
-   * We can do 16bit transfers on all but 284x.
+   * Ensure Ultra mode is set properly for this target.
    */
-  if (p->type == AIC_284x)
+  ultra_enb_addr = ULTRA_ENB;
+  if ((channel == 'B') || (target > 7))
+  {
+    ultra_enb_addr++;
+  }
+  ultra_enb = inb(p->base + ultra_enb_addr);
+  sxfrctl0 = inb(p->base + SXFRCTL0);
+  if ((*scsirate != 0) && (aic7xxx_syncrates[i].rate & ULTRA_SXFR))
   {
-    outsb(SCBARRAY + base, scb, SCB_PIO_TRANSFER_SIZE);
+    ultra_enb |= 0x01 << (target & 0x07);
+    sxfrctl0 |= FAST20;
   }
   else
   {
-    outsl(SCBARRAY + base, scb, (SCB_PIO_TRANSFER_SIZE + 3) / 4);
+    ultra_enb &= ~(0x01 << (target & 0x07));
+    sxfrctl0 &= ~FAST20;
   }
-
-  outb(0, SCBCNT + base);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_getscb
- *
- * Description:
- *   Get a SCB from the controller.
- *-F*************************************************************************/
-static inline void
-aic7xxx_getscb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
-  int base = p->base;
-
-  /*
-   * This is almost identical to aic7xxx_putscb().
-   */
-  outb(SCBAUTO, SCBCNT + base);
-  insb(SCBARRAY + base, scb, SCB_PIO_TRANSFER_SIZE);
-  outb(0, SCBCNT + base);
+  outb(ultra_enb, p->base + ultra_enb_addr);
+  outb(sxfrctl0, p->base + SXFRCTL0);
 }
 
 /*+F*************************************************************************
@@ -1374,6 +1563,47 @@
 
 /*+F*************************************************************************
  * Function:
+ *   scbq_remove
+ *
+ * Description:
+ *   Removes an SCB from the list.
+ *
+ *-F*************************************************************************/
+static inline void
+scbq_remove(scb_queue_type *queue, struct aic7xxx_scb *scb)
+{
+  if (queue->head == scb)
+  {
+    /* At beginning of queue, remove from head. */
+    scbq_remove_head(queue);
+  }
+  else
+  {
+    struct aic7xxx_scb *curscb = queue->head;
+
+    /*
+     * Search until the next scb is the one we're looking for, or
+     * we run out of queue.
+     */
+    while ((curscb != NULL) && (curscb->q_next != scb))
+    {
+      curscb = curscb->q_next;
+    }
+    if (curscb != NULL)
+    {
+      /* Found it. */
+      curscb->q_next = scb->q_next;
+      if (scb->q_next == NULL)
+      {
+        /* Update the tail when removing the tail. */
+        queue->tail = curscb;
+      }
+    }
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
  *   scbq_insert_tail
  *
  * Description:
@@ -1403,23 +1633,87 @@
  *   to be reset and all devices on that channel must be aborted.
  *-F*************************************************************************/
 static int
-aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel)
+aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel,
+    int lun, unsigned char tag)
 {
-  int targ = (scb->target_channel_lun >> 4) & 0x0F;
-  char chan = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+  int targ = (scb->hscb->target_channel_lun >> 4) & 0x0F;
+  char chan = (scb->hscb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+  int slun = scb->hscb->target_channel_lun & 0x07;
+  int match;
 
 #ifdef AIC7XXX_DEBUG_ABORT
-  printk("aic7xxx: (match_scb) comparing target/channel %d/%c to scb %d/%c\n",
-         target, channel, targ, chan);
+  printk("scsi%d: (targ %d/chan %c) matching scb to (targ %d/chan %c)\n",
+         scb->cmd->device->host->host_no, target, channel, targ, chan);
 #endif
-  if (target == ALL_TARGETS)
+  match = ((chan == channel) || (channel == ALL_CHANNELS));
+  if (match != 0)
+    match = ((targ == target) || (target == ALL_TARGETS));
+  if (match != 0)
+    match = ((lun == slun) || (lun == ALL_LUNS));
+  if (match != 0)
+    match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL));
+
+  return (match);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_add_curscb_to_free_list
+ *
+ * Description:
+ *   Adds the current scb (in SCBPTR) to the list of free SCBs.
+ *-F*************************************************************************/
+static void
+aic7xxx_add_curscb_to_free_list(struct aic7xxx_host *p)
+{
+  /*
+   * Invalidate the tag so that aic7xxx_find_scb doesn't think
+   * it's active
+   */
+  outb(SCB_LIST_NULL, p->base + SCB_TAG);
+
+  outb(inb(p->base + FREE_SCBH), p->base + SCB_NEXT);
+  outb(inb(p->base + SCBPTR), p->base + FREE_SCBH);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_rem_scb_from_disc_list
+ *
+ * Description:
+ *   Removes the current SCB from the disconnected list and adds it
+ *   to the free list.
+ *-F*************************************************************************/
+static unsigned char
+aic7xxx_rem_scb_from_disc_list(struct aic7xxx_host *p, unsigned char scbptr)
+{
+  unsigned char next;
+  unsigned char prev;
+
+  outb(scbptr, p->base + SCBPTR);
+  next = inb(p->base + SCB_NEXT);
+  prev = inb(p->base + SCB_PREV);
+
+  outb(0, p->base + SCB_CONTROL);
+
+  aic7xxx_add_curscb_to_free_list(p);
+
+  if (prev != SCB_LIST_NULL)
   {
-    return (chan == channel);
+    outb(prev, p->base + SCBPTR);
+    outb(next, p->base + SCB_NEXT);
   }
   else
   {
-    return ((chan == channel) && (targ == target));
+    outb(next, p->base + DISCONNECTED_SCBH);
   }
+
+  if (next != SCB_LIST_NULL)
+  {
+    outb(next, p->base + SCBPTR);
+    outb(prev, p->base + SCB_PREV);
+  }
+  return next;
 }
 
 /*+F*************************************************************************
@@ -1427,51 +1721,93 @@
  *   aic7xxx_busy_target
  *
  * Description:
- *   Set the specified target active.
+ *   Set the specified target busy.
  *-F*************************************************************************/
 static void
-aic7xxx_busy_target(unsigned char target, char channel, int base)
+aic7xxx_busy_target(struct aic7xxx_host *p, unsigned char target,
+    char channel, unsigned char scbid)
+{
+  unsigned char active_scb;
+  unsigned char info_scb;
+  unsigned int  scb_offset;
+
+  info_scb = target / 4;
+  if (channel == 'B')
+    info_scb = info_scb + 2;
+
+  active_scb = inb(p->base + SCBPTR);
+  outb(info_scb, p->base + SCBPTR);
+  scb_offset = SCB_BUSYTARGETS + (target & 0x03);
+  outb(scbid, p->base + scb_offset);
+  outb(active_scb, p->base + SCBPTR);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_index_busy_target
+ *
+ * Description:
+ *   Returns the index of the busy target, and optionally sets the
+ *   target inactive.
+ *-F*************************************************************************/
+static unsigned char
+aic7xxx_index_busy_target(struct aic7xxx_host *p, unsigned char target,
+    char channel, int unbusy)
 {
-  unsigned char active;
-  unsigned long active_port = ACTIVE_A + base;
+  unsigned char active_scb;
+  unsigned char info_scb;
+  unsigned char busy_scbid;
+  unsigned int  scb_offset;
+
+  info_scb = target / 4;
+  if (channel == 'B')
+    info_scb = info_scb + 2;
 
-  if ((target > 0x07) || (channel == 'B'))
+  active_scb = inb(p->base + SCBPTR);
+  outb(info_scb, p->base + SCBPTR);
+  scb_offset = SCB_BUSYTARGETS + (target & 0x03);
+  busy_scbid = inb(p->base + scb_offset);
+  if (unbusy)
   {
-    /*
-     * targets on the Second channel or above id 7 store info in byte two
-     * of ACTIVE
-     */
-    active_port++;
+    outb(SCB_LIST_NULL, p->base + scb_offset);
   }
-  active = inb(active_port);
-  active |= (0x01 << (target & 0x07));
-  outb(active, active_port);
+  outb(active_scb, p->base + SCBPTR);
+  return (busy_scbid);
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_unbusy_target
+ *   aic7xxx_find_scb
  *
  * Description:
- *   Set the specified target inactive.
+ *   Look through the SCB array of the card and attempt to find the
+ *   hardware SCB that corresponds to the passed in SCB.  Return
+ *   SCB_LIST_NULL if unsuccessful.  This routine assumes that the
+ *   card is already paused.
  *-F*************************************************************************/
-static void
-aic7xxx_unbusy_target(unsigned char target, char channel, int base)
+static unsigned char
+aic7xxx_find_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 {
-  unsigned char active;
-  unsigned long active_port = ACTIVE_A + base;
+  unsigned char saved_scbptr;
+  unsigned char curindex;
 
-  if ((target > 0x07) || (channel == 'B'))
+  saved_scbptr = inb(p->base + SCBPTR);
+  curindex = 0;
+  for (curindex = 0; curindex < p->scb_data->maxhscbs; curindex++)
   {
-    /*
-     * targets on the Second channel or above id 7 store info in byte two
-     * of ACTIVE
-     */
-    active_port++;
+    outb(curindex, p->base + SCBPTR);
+    if (inb(p->base + SCB_TAG) == scb->hscb->tag)
+    {
+      break;
+    }
   }
-  active = inb(active_port);
-  active &= ~(0x01 << (target & 0x07));
-  outb(active, active_port);
+  outb(saved_scbptr, p->base + SCBPTR);
+  if (curindex >= p->scb_data->maxhscbs)
+  {
+    curindex = SCB_LIST_NULL;
+  }
+
+  return (curindex);
 }
 
 /*+F*************************************************************************
@@ -1479,68 +1815,60 @@
  *   aic7xxx_allocate_scb
  *
  * Description:
- *   Get a free SCB either from one already assigned to a hardware
- *   slot, or one that will require an SCB to be paged out before
- *   use.  If there are none, attempt to allocate a new one.
+ *   Get an SCB from the free list or by allocating a new one.
  *-F*************************************************************************/
 static struct aic7xxx_scb *
 aic7xxx_allocate_scb(struct aic7xxx_host *p)
 {
-  struct aic7xxx_scb *scbp = NULL;
-  int maxscbs;
+  struct aic7xxx_scb   *scbp = NULL;
+  struct aic7xxx_hwscb *hscbp = NULL;
+#ifdef AGRESSIVE
+  long processor_flags;
+
+  save_flags(processor_flags);
+  cli();
+#endif
 
-  scbp = p->scb_link->free_scbs.head;
+  scbp = p->scb_data->free_scbs.head;
   if (scbp != NULL)
   {
-    scbq_remove_head(&p->scb_link->free_scbs);
+    scbq_remove_head(&p->scb_data->free_scbs);
   }
   else
   {
-    /*
-     * This should always be NULL if paging is not enabled.
-     */
-    scbp = p->page_scbs.head;
-    if (scbp != NULL)
-    {
-      scbq_remove_head(&p->page_scbs);
-    }
-    else
+    if (p->scb_data->numscbs < p->scb_data->maxscbs)
     {
-      /*
-       * Set limit the SCB allocation to the maximum number of
-       * hardware SCBs if paging is not enabled; otherwise use
-       * the maximum (255).
-       */
-      if (p->flags & PAGE_ENABLED)
-        maxscbs = p->maxscbs;
-      else
-        maxscbs = p->maxhscbs;
-      if (p->scb_link->numscbs < maxscbs)
-      {
-        int scb_index = p->scb_link->numscbs;
-        int scb_size = sizeof(struct aic7xxx_scb);
-
-        p->scb_array[scb_index] = kmalloc(scb_size, GFP_ATOMIC | GFP_DMA);
-        scbp = (p->scb_array[scb_index]);
-        if (scbp != NULL)
-        {
-          memset(scbp, 0, sizeof(*scbp));
-          scbp->tag = scb_index;
-          if (scb_index < p->maxhscbs)
-            scbp->position = scb_index;
-          else
-	    scbp->position = SCB_LIST_NULL;
-          p->scb_link->numscbs++;
-        }
+      int scb_index = p->scb_data->numscbs;
+      int scb_size = sizeof(struct aic7xxx_scb) +
+                     sizeof (struct hw_scatterlist) * AIC7XXX_MAX_SG;
+
+      scbp = kmalloc(scb_size, GFP_ATOMIC);
+      if (scbp != NULL)
+      {
+        memset(scbp, 0, sizeof(struct aic7xxx_scb));
+        hscbp = &p->scb_data->hscbs[scb_index];
+        scbp->hscb = hscbp;
+        scbp->sg_list = (struct hw_scatterlist *) &scbp[1];
+        memset(hscbp, 0, sizeof(struct aic7xxx_hwscb));
+        hscbp->tag = scb_index;
+        p->scb_data->numscbs++;
+        /*
+         * Place in the scb array; never is removed
+         */
+        p->scb_data->scb_array[scb_index] = scbp;
       }
     }
   }
+#ifdef AIC7XXX_DEBUG
   if (scbp != NULL)
   {
-#ifdef AIC7XXX_DEBUG
-    p->scb_link->activescbs++;
-#endif
+    p->activescbs++;
   }
+#endif
+
+#ifdef AGRESSIVE
+  restore_flags(processor_flags);
+#endif
   return (scbp);
 }
 
@@ -1580,6 +1908,7 @@
     cmd = p->completeq.head;
     p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble;
     cmd->host_scribble = NULL;
+    p->device_status[TARGET_INDEX(cmd)].active_cmds--;
     cmd->scsi_done(cmd);
   }
   p->completeq.tail = NULL;
@@ -1590,53 +1919,29 @@
  *   aic7xxx_free_scb
  *
  * Description:
- *   Free the scb and update the page, waiting, free scb lists.
+ *   Free the scb and insert into the free scb list.
  *-F*************************************************************************/
 static void
 aic7xxx_free_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 {
-  struct aic7xxx_scb *wscb;
+  struct aic7xxx_hwscb *hscb;
+  long flags;
+
+  hscb = scb->hscb;
+  save_flags(flags);
+  cli();
 
-  scb->state = SCB_FREE;
+  scb->flags = SCB_FREE;
   scb->cmd = NULL;
-  scb->control = 0;
-  scb->state = 0;
+  hscb->control = 0;
+  hscb->target_status = 0;
 
-  if (scb->position == SCB_LIST_NULL)
-  {
-    scbq_insert_head(&p->page_scbs, scb);
-  }
-  else
-  {
-    /*
-     * If there are any SCBS on the waiting queue, assign the slot of this
-     * "freed" SCB to the first one.  We'll run the waiting queues after
-     * all command completes for a particular interrupt are completed or
-     * when we start another command.
-     */
-    wscb = p->waiting_scbs.head;
-    if (wscb != NULL)
-    {
-      scbq_remove_head(&p->waiting_scbs);
-      wscb->position = scb->position;
-      scbq_insert_tail(&p->assigned_scbs, wscb);
-      wscb->state = (wscb->state & ~SCB_WAITINGQ) | SCB_ASSIGNEDQ;
-
-      /* 
-       * The "freed" SCB will need to be assigned a slot before being
-       * used, so put it in the page_scbs queue.
-       */
-      scb->position = SCB_LIST_NULL;
-      scbq_insert_head(&p->page_scbs, scb);
-    }
-    else
-    {
-      scbq_insert_head(&p->scb_link->free_scbs, scb);
-    }
+  scbq_insert_head(&p->scb_data->free_scbs, scb);
 #ifdef AIC7XXX_DEBUG
-    p->scb_link->activescbs--;  /* For debugging purposes. */
+  p->activescbs--;  /* For debugging purposes. */
 #endif
-  }
+
+  restore_flags(flags);
 }
 
 /*+F*************************************************************************
@@ -1651,68 +1956,113 @@
 {
   Scsi_Cmnd *cmd = scb->cmd;
 
+  if (scb->flags & SCB_RECOVERY_SCB)
+  {
+    p->flags &= ~IN_TIMEOUT;
+  }
+  if (cmd->result == DID_OK)
+  {
+    if (scb->flags & SCB_ABORTED)
+    {
+      cmd->result = (DID_RESET << 16);
+    }
+  }
+  if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0)
+  {
+    unsigned short mask;
+
+    mask = 0x01 << TARGET_INDEX(scb->cmd);
+    if (scb->flags & SCB_MSGOUT_WDTR)
+    {
+      p->wdtr_pending &= ~mask;
+    }
+    if (scb->flags & SCB_MSGOUT_SDTR)
+    {
+      p->sdtr_pending &= ~mask;
+    }
+  }
   aic7xxx_free_scb(p, scb);
   aic7xxx_queue_cmd_complete(p, cmd);
 
+#ifdef AIC7XXX_PROC_STATS
+  {
+    int actual;
+
+    /*
+     * XXX: we should actually know how much actually transferred
+     * XXX: for each command, but apparently that's too difficult.
+     */
+    actual = aic7xxx_length(cmd, 0);
+    if (!(scb->flags & (SCB_ABORTED | SCB_SENSE)) && (actual > 0)
+        && (aic7xxx_error(cmd) == 0))
+    {
+      struct aic7xxx_xferstats *sp;
+      long *ptr;
+      int x;
+
+      sp = &p->stats[cmd->channel & 0x01][cmd->target & 0x0F][cmd->lun & 0x07];
+      sp->xfers++;
+
+      if (cmd->request.cmd == WRITE)
+      {
+        sp->w_total++;
+        sp->w_total512 += (actual >> 9);
+        ptr = sp->w_bins;
+      }
+      else
+      {
+        sp->r_total++;
+        sp->r_total512 += (actual >> 9);
+        ptr = sp->r_bins;
+      }
+      for (x = 9; x <= 17; x++)
+      {
+        if (actual < (1 << x))
+        {
+          ptr[x - 9]++;
+          break;
+        }
+      }
+      if (x > 17)
+      {
+        ptr[x - 9]++;
+      }
+    }
+  }
+#endif /* AIC7XXX_PROC_STATS */
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_done_aborted_scbs
+ *   aic7xxx_run_done_queue
  *
  * Description:
- *   Calls the scsi_done() for the Scsi_Cmnd of each scb in the
- *   aborted list, and adds each scb to the free list.
+ *   Calls the aic7xxx_done() for the Scsi_Cmnd of each scb in the
+ *   aborted list, and adds each scb to the free list.  If complete
+ *   is TRUE, we also process the commands complete list.
  *-F*************************************************************************/
 static void
-aic7xxx_done_aborted_scbs(struct aic7xxx_host *p)
+aic7xxx_run_done_queue(struct aic7xxx_host *p, /*complete*/ int complete)
 {
-  Scsi_Cmnd *cmd;
   struct aic7xxx_scb *scb;
   int i;
 
-  for (i = 0; i < p->scb_link->numscbs; i++)
+  for (i = 0; i < p->scb_data->numscbs; i++)
   {
-    scb = (p->scb_array[i]);
-    if (scb->state & SCB_QUEUED_FOR_DONE)
+    scb = p->scb_data->scb_array[i];
+    if (scb->flags & SCB_QUEUED_FOR_DONE)
     {
 #ifdef AIC7XXX_DEBUG_ABORT
-      printk("aic7xxx: (done_aborted_scbs) Aborting scb %d, TCL=%d/%d/%d\n",
-      scb->position, TCL_OF_SCB(scb));
+      printk("(scsi%d:%d:%d) Aborting scb %d\n",
+             p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
 #endif
-      /*
-       * Process the command after marking the scb as free
-       * and adding it to the free list.
-       */
-      cmd = scb->cmd;
-      p->device_status[TARGET_INDEX(cmd)].flags = 0;
-      aic7xxx_free_scb(p, scb);
-      cmd->scsi_done(cmd);  /* call the done function */
+      aic7xxx_done(p, scb);
     }
   }
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_add_waiting_scb
- *
- * Description:
- *   Add this SCB to the head of the "waiting for selection" list.
- *-F*************************************************************************/
-static void
-aic7xxx_add_waiting_scb(u_long base, struct aic7xxx_scb *scb)
-{
-  unsigned char next;
-  unsigned char curscb;
-
-  curscb = inb(SCBPTR + base);
-  next = inb(WAITING_SCBH + base);
-
-  outb(scb->position, SCBPTR + base);
-  outb(next, SCB_NEXT + base);
-  outb(scb->position, WAITING_SCBH + base);
-
-  outb(curscb, SCBPTR + base);
+  if (complete)
+  {
+    aic7xxx_done_cmds_complete(p);
+  }
 }
 
 /*+F*************************************************************************
@@ -1725,26 +2075,23 @@
  *-F*************************************************************************/
 static unsigned char
 aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
-    unsigned char prev)
+    unsigned char scbpos, unsigned char prev)
 {
   unsigned char curscb, next;
-  int target = (scb->target_channel_lun >> 4) & 0x0F;
-  char channel = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A';
-  int base = p->base;
 
   /*
    * Select the SCB we want to abort and pull the next pointer out of it.
    */
-  curscb = inb(SCBPTR + base);
-  outb(scb->position, SCBPTR + base);
-  next = inb(SCB_NEXT + base);
+  curscb = inb(p->base + SCBPTR);
+  outb(scbpos, p->base + SCBPTR);
+  next = inb(p->base + SCB_NEXT);
 
   /*
    * Clear the necessary fields
    */
-  outb(0, SCB_CONTROL + base);
-  outb(SCB_LIST_NULL, SCB_NEXT + base);
-  aic7xxx_unbusy_target(target, channel, base);
+  outb(0, p->base + SCB_CONTROL);
+
+  aic7xxx_add_curscb_to_free_list(p);
 
   /*
    * Update the waiting list
@@ -1754,22 +2101,23 @@
     /*
      * First in the list
      */
-    outb(next, WAITING_SCBH + base);
+    outb(next, p->base + WAITING_SCBH);
   }
   else
   {
     /*
      * Select the scb that pointed to us and update its next pointer.
      */
-    outb(prev, SCBPTR + base);
-    outb(next, SCB_NEXT + base);
+    outb(prev, p->base + SCBPTR);
+    outb(next, p->base + SCB_NEXT);
   }
   /*
    * Point us back at the original scb position and inform the SCSI
    * system that the command has been aborted.
    */
-  outb(curscb, SCBPTR + base);
-  scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
+  outb(curscb, p->base + SCBPTR);
+  scb->flags |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
+  scb->flags &= ~SCB_ACTIVE;
   scb->cmd->result = (DID_RESET << 16);
 
   return (next);
@@ -1777,6 +2125,75 @@
 
 /*+F*************************************************************************
  * Function:
+ *   aic7xxx_search_qinfifo
+ *
+ * Description:
+ *   Search the queue-in FIFO for matching SCBs and conditionally
+ *   requeue.  Returns the number of matching SCBs.
+ *-F*************************************************************************/
+static int
+aic7xxx_search_qinfifo(struct aic7xxx_host *p, int target, char channel,
+    int lun, unsigned char tag, int flags, int requeue)
+{
+  unsigned char saved_queue[AIC7XXX_MAXSCB];
+  int      queued = inb(p->base + QINCNT) & p->qcntmask;
+  int      i;
+  int      found;
+  struct aic7xxx_scb *scbp;
+  scb_queue_type removed_scbs;
+
+  found = 0;
+  scbq_init (&removed_scbs);
+  for (i = 0; i < (queued - found); i++)
+  {
+    saved_queue[i] = inb(p->base + QINFIFO);
+    scbp = p->scb_data->scb_array[saved_queue[i]];
+    if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+    {
+       /*
+        * We found an scb that needs to be removed.
+        */
+       if (requeue)
+       {
+         scbq_insert_head(&removed_scbs, scbp);
+       }
+       else
+       {
+         scbp->flags = flags;
+         scbp->flags &= ~SCB_ACTIVE;
+         /*
+          * XXX - Don't know what error to use here.
+          */
+         aic7xxx_error(scbp->cmd) = DID_RESET;
+       }
+       i--;
+       found++;
+    }
+  }
+  /* Now put the saved scbs back. */
+  for (queued = 0; queued < i; queued++)
+    outb(saved_queue[queued], p->base + QINFIFO);
+
+  if (requeue)
+  {
+    scbp = removed_scbs.head;
+    while (scbp != NULL)
+    {
+      scbq_remove_head(&removed_scbs);
+      /*
+       * XXX - Shouldn't we be adding this to the free list?
+       */
+      scbq_insert_head(&p->waiting_scbs, scbp);
+      scbp->flags |= SCB_WAITINGQ;
+      scbp = removed_scbs.head;
+    }
+  }
+
+  return (found);
+}
+
+/*+F*************************************************************************
+ * Function:
  *   aic7xxx_reset_device
  *
  * Description:
@@ -1784,131 +2201,280 @@
  *   all active and queued scbs for that target/channel.
  *-F*************************************************************************/
 static int
-aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel)
+aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel,
+                     int lun, unsigned char tag)
 {
-  int base = p->base;
-  struct aic7xxx_scb *scb;
+  struct aic7xxx_scb *scbp;
   unsigned char active_scb;
   int i = 0;
-  int found = 0;
+  int found;
 
   /*
    * Restore this when we're done
    */
-  active_scb = inb(SCBPTR + base);
+  active_scb = inb(p->base + SCBPTR);
 
 #ifdef AIC7XXX_DEBUG_ABORT
-  printk("aic7xxx: (reset_device) target/channel %d/%c, active_scb %d\n",
-         target, channel, active_scb);
+  printk("(scsi%d:%d:%d) Reset device, active_scb %d\n",
+         p->host_no, target, CHAN_TO_INT(channel), active_scb);
 #endif
+
   /*
-   * Search the QINFIFO.
+   * Deal with the busy target and linked next issues.
    */
   {
-    int saved_queue[AIC7XXX_MAXSCB];
-    int queued = inb(QINCNT + base) & p->qcntmask;
+    int min_target, max_target;
+    unsigned char busy_scbid;
 
-    for (i = 0; i < (queued - found); i++)
+    /* Make all targets 'relative' to bus A. */
+    if (target == ALL_TARGETS)
     {
-      saved_queue[i] = inb(QINFIFO + base);
-      outb(saved_queue[i], SCBPTR + base);
-      scb = (p->scb_array[inb(SCB_TAG + base)]);
-      if (aic7xxx_match_scb(scb, target, channel))
+      switch (channel)
       {
-        /*
-         * We found an scb that needs to be aborted.
-         */
-#ifdef AIC7XXX_DEBUG_ABORT
-        printk("aic7xxx: (reset_device) aborting SCB %d, TCL=%d/%d/%d\n",
-               saved_queue[i], TCL_OF_SCB(scb));
-#endif
-        scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
-        scb->cmd->result = (DID_RESET << 16);
-        outb(0, SCB_CONTROL + base);
-        i--;
-        found++;
+        case 'A':
+  	  min_target = 0;
+  	  max_target = (p->bus_type == AIC_SINGLE) ? 7 : 15;
+  	  break;
+        case 'B':
+  	  min_target = 8;
+  	  max_target = 15;
+  	  break;
+        case ALL_CHANNELS:
+        default:
+  	  min_target = 0;
+  	  max_target = (p->bus_type == AIC_SINGLE) ? 7 : 15;
+  	  break;
       }
     }
-    /*
-     * Now put the saved scbs back.
-     */
-    for (queued = 0; queued < i; queued++)
+    else
+    { 
+      min_target = target + channel == 'B' ? 8 : 0;
+      max_target = min_target;
+    }
+
+    for (i = min_target; i <= max_target; i++)
     {
-      outb(saved_queue[queued], QINFIFO + base);
+      busy_scbid = aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/FALSE);
+      if (busy_scbid < p->scb_data->numscbs)
+      {
+  	struct aic7xxx_scb *busy_scb;
+  	struct aic7xxx_scb *next_scb;
+  	unsigned char next_scbid;
+
+  	busy_scb = p->scb_data->scb_array[busy_scbid];
+  
+  	next_scbid = busy_scb->hscb->data_count >> 24;
+
+  	if (next_scbid == SCB_LIST_NULL)
+        {
+  	  busy_scbid = aic7xxx_find_scb(p, busy_scb);
+
+  	  if (busy_scbid != SCB_LIST_NULL)
+          {
+  	    outb(busy_scbid, p->base + SCBPTR);
+  	    next_scbid = inb(p->base + SCB_LINKED_NEXT);
+  	  }
+  	}
+
+  	if (aic7xxx_match_scb(busy_scb, target, channel, lun, tag))
+        {
+  	  aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/TRUE);
+  	}
+
+  	if (next_scbid != SCB_LIST_NULL)
+        {
+  	  next_scb = p->scb_data->scb_array[next_scbid];
+  	  if (aic7xxx_match_scb(next_scb, target, channel, lun, tag))
+          {
+  	    continue;
+          }
+  	  /* Requeue for later processing */
+  	  scbq_insert_head(&p->waiting_scbs, next_scb);
+  	  next_scb->flags |= SCB_WAITINGQ;
+  	}
+      }
     }
   }
 
+  found = aic7xxx_search_qinfifo(p, target, channel, lun, tag,
+      SCB_ABORTED | SCB_QUEUED_FOR_DONE, /* requeue */ FALSE);
+
   /*
    * Search waiting for selection list.
    */
   {
-    unsigned char next, prev;
+    unsigned char next, prev, scb_index;
 
-    next = inb(WAITING_SCBH + base);  /* Start at head of list. */
+    next = inb(p->base + WAITING_SCBH);  /* Start at head of list. */
     prev = SCB_LIST_NULL;
 
     while (next != SCB_LIST_NULL)
     {
-      outb(next, SCBPTR + base);
-      scb = (p->scb_array[inb(SCB_TAG + base)]);
-      /*
-       * Select the SCB.
-       */
-      if (aic7xxx_match_scb(scb, target, channel))
+      outb(next, p->base + SCBPTR);
+      scb_index = inb(p->base + SCB_TAG);
+      if (scb_index >= p->scb_data->numscbs)
+      {
+        panic("aic7xxx: Waiting List inconsistency; SCB index=%d, numscbs=%d\n",
+              scb_index, p->scb_data->numscbs);
+      }
+      scbp = p->scb_data->scb_array[scb_index];
+      if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
       {
-        next = aic7xxx_abort_waiting_scb(p, scb, prev);
+        unsigned char linked_next;
+
+        next = aic7xxx_abort_waiting_scb(p, scbp, next, prev);
+        linked_next = inb(p->base + SCB_LINKED_NEXT);
+        if (linked_next != SCB_LIST_NULL)
+        {
+          struct aic7xxx_scb *next_scb;
+          /*
+           * Requeue the waiting SCB via the waiting list.
+           */
+          next_scb = p->scb_data->scb_array[linked_next];
+          if (! aic7xxx_match_scb(next_scb, target, channel, lun, tag))
+          {
+            scbq_insert_head(&p->waiting_scbs, next_scb);
+            next_scb->flags |= SCB_WAITINGQ;
+          }
+        }
         found++;
       }
       else
       {
         prev = next;
-        next = inb(SCB_NEXT + base);
+        next = inb(p->base + SCB_NEXT);
       }
     }
   }
 
   /*
-   * Go through the entire SCB array now and look for commands for
-   * for this target that are active.  These are other (most likely
-   * tagged) commands that were disconnected when the reset occurred.
+   * Go through disconnected list and remove any entries we have queued
+   * for completion, zeroing their control byte too.
    */
-  for (i = 0; i < p->scb_link->numscbs; i++)
   {
-    scb = (p->scb_array[i]);
-    if ((scb->state & SCB_ACTIVE) && aic7xxx_match_scb(scb, target, channel))
+    unsigned char next, prev, scb_index;
+
+    next = inb(p->base + DISCONNECTED_SCBH);
+    prev = SCB_LIST_NULL;
+
+    while (next != SCB_LIST_NULL)
     {
-      /*
-       * Ensure the target is "free"
-       */
-      aic7xxx_unbusy_target(target, channel, base);
-      if (! (scb->state & SCB_PAGED_OUT))
+      outb(next, p->base + SCBPTR);
+      scb_index = inb(p->base + SCB_TAG);
+      if (scb_index > p->scb_data->numscbs)
       {
-        outb(scb->position, SCBPTR + base);
-        outb(0, SCB_CONTROL + base);
+        panic("aic7xxx: Disconnected List inconsistency, SCB index = %d, "
+              "num scbs = %d.\n", scb_index, p->scb_data->numscbs);
       }
-      scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
-      scb->cmd->result = (DID_RESET << 16);
+      scbp = p->scb_data->scb_array[scb_index];
+      if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+      {
+        next = aic7xxx_rem_scb_from_disc_list(p, next);
+      }
+      else
+      {
+        prev = next;
+        next = inb(p->base + SCB_NEXT);
+      }
+    }
+  }
+
+  /*
+   * Go through the hardware SCB array looking for commands that
+   * were active but not on any list.
+   */
+  for (i = 0; i < p->scb_data->maxhscbs; i++)
+  {
+    unsigned char scbid;
+
+    outb(i, p->base + SCBPTR);
+    scbid = inb(p->base + SCB_TAG);
+    if (scbid < p->scb_data->numscbs)
+    {
+      scbp = p->scb_data->scb_array[scbid];
+      if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+      {
+        aic7xxx_add_curscb_to_free_list(p);
+      }
+    }
+  }
+
+  /*
+   * Go through the entire SCB array now and look for commands for
+   * for this target that are stillactive.  These are other (most likely
+   * tagged) commands that were disconnected when the reset occurred.
+   */
+  for (i = 0; i < p->scb_data->numscbs; i++)
+  {
+    scbp = p->scb_data->scb_array[i];
+    if (((scbp->flags & SCB_ACTIVE) != 0) &&
+        aic7xxx_match_scb(scbp, target, channel, lun, tag))
+    {
+      scbp->flags |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
+      scbp->flags &= ~SCB_ACTIVE;
+      aic7xxx_error(scbp->cmd) = DID_RESET;
+
       found++;
+
+      if ((scbp->flags & SCB_WAITINGQ) != 0)
+      {
+        scbq_remove(&p->waiting_scbs, scbp);
+        scbp->flags &= ~SCB_WAITINGQ;
+      }
     }
   }
 
-  outb(active_scb, SCBPTR + base);
+  outb(active_scb, p->base + SCBPTR);
   return (found);
 }
 
 /*+F*************************************************************************
  * Function:
+ *   aic7xxx_clear_intstat
+ *
+ * Description:
+ *   Clears the interrupt status.
+ *-F*************************************************************************/
+static void
+aic7xxx_clear_intstat(struct aic7xxx_host *p)
+{
+  /* Clear any interrupt conditions this may have caused. */
+  outb(CLRSELDO | CLRSELDI | CLRSELINGO, p->base + CLRSINT0);
+  outb(CLRSELTIMEO | CLRATNO | CLRSCSIRSTI | CLRBUSFREE | CLRSCSIPERR |
+       CLRPHASECHG | CLRREQINIT, p->base + CLRSINT1);
+  outb(CLRSCSIINT, p->base + CLRINT);
+}
+
+/*+F*************************************************************************
+ * Function:
  *   aic7xxx_reset_current_bus
  *
  * Description:
  *   Reset the current SCSI bus.
  *-F*************************************************************************/
 static void
-aic7xxx_reset_current_bus(int base)
+aic7xxx_reset_current_bus(struct aic7xxx_host *p)
 {
-  outb(SCSIRSTO, SCSISEQ + base);
+  unsigned char scsiseq;
+
+  /* Disable reset interrupts. */
+  outb(inb(p->base + SIMODE1) & ~ENSCSIRST, p->base + SIMODE1);
+
+  /* Turn on the bus reset. */
+  scsiseq = inb(p->base + SCSISEQ);
+  outb(scsiseq | SCSIRSTO, p->base + SCSISEQ);
+
+  udelay(1000);
+
+  /* Turn off the bus reset. */
+  outb(scsiseq & ~SCSIRSTO, p->base + SCSISEQ);
+
+  aic7xxx_clear_intstat(p);
+
+  /* Re-enable reset interrupts. */
+  outb(inb(p->base + SIMODE1) | ENSCSIRST, p->base + SIMODE1);
+
   udelay(1000);
-  outb(0, SCSISEQ + base);
 }
 
 /*+F*************************************************************************
@@ -1921,25 +2487,24 @@
 static int
 aic7xxx_reset_channel(struct aic7xxx_host *p, char channel, int initiate_reset)
 {
-  int base = p->base;
-  unsigned char sblkctl;
-  char cur_channel;
   unsigned long offset, offset_max;
   int found;
+  unsigned char sblkctl;
+  char cur_channel;
 
+  pause_sequencer(p);
   /*
-   * Clean up all the state information for the
-   * pending transactions on this bus.
+   * Clean up all the state information for the pending transactions
+   * on this bus.
    */
-  found = aic7xxx_reset_device(p, ALL_TARGETS, channel);
+  found = aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);
 
   if (channel == 'B')
   {
     p->needsdtr |= (p->needsdtr_copy & 0xFF00);
     p->sdtr_pending &= 0x00FF;
-    outb(0, ACTIVE_B + base);
-    offset = TARG_SCRATCH + base + 8;
-    offset_max = TARG_SCRATCH + base + 16;
+    offset = TARG_SCRATCH + 8;
+    offset_max = TARG_SCRATCH + 16;
   }
   else
   {
@@ -1949,132 +2514,100 @@
       p->needwdtr = p->needwdtr_copy;
       p->sdtr_pending = 0x0;
       p->wdtr_pending = 0x0;
-      outb(0, ACTIVE_A + base);
-      outb(0, ACTIVE_B + base);
-      offset = TARG_SCRATCH + base;
-      offset_max = TARG_SCRATCH + base + 16;
+      offset = TARG_SCRATCH;
+      offset_max = TARG_SCRATCH + 16;
     }
     else
     {
+      /* Channel A */
       p->needsdtr |= (p->needsdtr_copy & 0x00FF);
       p->sdtr_pending &= 0xFF00;
-      outb(0, ACTIVE_A + base);
-      offset = TARG_SCRATCH + base;
-      offset_max = TARG_SCRATCH + base + 8;
+      offset = TARG_SCRATCH;
+      offset_max = TARG_SCRATCH + 8;
     }
   }
+
   while (offset < offset_max)
   {
     /*
-     * Revert to async/narrow transfers
-     * until we renegotiate.
+     * Revert to async/narrow transfers until we renegotiate.
      */
     u_char targ_scratch;
-    targ_scratch = inb(offset);
+
+    targ_scratch = inb(p->base + offset);
     targ_scratch &= SXFR;
-    outb(targ_scratch, offset);
+    outb(targ_scratch, p->base + offset);
     offset++;
   }
 
   /*
    * Reset the bus and unpause/restart the controller
    */
-
-  /*
-   * Case 1: Command for another bus is active
-   */
-  sblkctl = inb(SBLKCTL + base);
+  sblkctl = inb(p->base + SBLKCTL);
   cur_channel = (sblkctl & SELBUSB) ? 'B' : 'A';
   if (cur_channel != channel)
   {
+    /*
+     * Case 1: Command for another bus is active
+     */
 #ifdef AIC7XXX_DEBUG_ABORT
-    printk("aic7xxx: (reset_channel) Stealthily resetting channel %c\n",
-           channel);
+    printk("scsi%d: Stealthily resetting channel %c\n",
+           p->host_no, channel);
 #endif
     /*
-     * Stealthily reset the other bus without upsetting the current bus
+     * Stealthily reset the other bus without upsetting the current bus.
      */
-    outb(sblkctl ^ SELBUSB, SBLKCTL + base);
+    outb(sblkctl ^ SELBUSB, p->base + SBLKCTL);
+    outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
     if (initiate_reset)
     {
-      aic7xxx_reset_current_bus(base);
+      aic7xxx_reset_current_bus(p);
+      /*
+       * Cause the mid-level SCSI code to delay any further 
+       * queueing by the bus settle time for us.
+       */
+      p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ));
     }
-    outb(CLRSCSIRSTI | CLRSELTIMEO, CLRSINT1 + base);
-    outb(CLRSCSIINT, CLRINT + base);
-    outb(sblkctl, SBLKCTL + base);
-
-    UNPAUSE_SEQUENCER(p);
+    outb(0, p->base + SCSISEQ);
+    aic7xxx_clear_intstat(p);
+    outb(sblkctl, p->base + SBLKCTL);
+    unpause_sequencer(p, /* unpause_always */ FALSE);
   }
-  /*
-   * Case 2: A command from this bus is active or we're idle
-   */
   else
   {
+    /*
+     * Case 2: A command from this bus is active or we're idle.
+     */
 #ifdef AIC7XXX_DEBUG_ABORT
-    printk("aic7xxx: (reset_channel) Resetting current channel %c\n",
-           channel);
+    printk("scsi%d: Resetting current channel %c\n",
+           p->host_no, channel);
 #endif
+    outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
     if (initiate_reset)
     {
-      aic7xxx_reset_current_bus(base);
+      aic7xxx_reset_current_bus(p);
+      /*
+       * Cause the mid-level SCSI code to delay any further 
+       * queueing by the bus settle time for us.
+       */
+#if 0
+      p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ));
+#endif
     }
-    outb(CLRSCSIRSTI | CLRSELTIMEO, CLRSINT1 + base);
-    outb(CLRSCSIINT, CLRINT + base);
-    RESTART_SEQUENCER(p);
+    outb(0, p->base + SCSISEQ);
+    aic7xxx_clear_intstat(p);
+    restart_sequencer(p);
 #ifdef AIC7XXX_DEBUG_ABORT
-    printk("aic7xxx: (reset_channel) Channel reset, sequencer restarted\n");
+    printk("scsi%d: Channel reset, sequencer restarted\n", p->host_no);
 #endif
   }
 
   /*
-   * Cause the mid-level SCSI code to delay any further 
-   * queueing by the bus settle time for us.
-   */
-  p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ));
-
-  /*
    * Now loop through all the SCBs that have been marked for abortion,
    * and call the scsi_done routines.
    */
-  aic7xxx_done_aborted_scbs(p);
-  return found;
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_page_scb
- *
- * Description:
- *   Swap in_scbp for out_scbp down in the cards SCB array.
- *   We assume that the SCB for out_scbp is already selected in SCBPTR.
- *
- *-F*************************************************************************/
-static inline void
-aic7xxx_page_scb(struct aic7xxx_host *p, struct aic7xxx_scb *out_scbp,
-    struct aic7xxx_scb *in_scbp)
-{
-  int index;
-
-  /* Page-out */
-#if 0
-printk("aic7xxx: Paging out target %d SCB and paging in target %d SCB\n",
-       out_scbp->cmd->target, in_scbp->cmd->target);
-#endif
-  aic7xxx_getscb(p, out_scbp);
-  out_scbp->state |= SCB_PAGED_OUT;
-  if (!(out_scbp->control & TAG_ENB))
-  {
-    /* Stick in non-tagged array */
-    index = (out_scbp->target_channel_lun >> 4) | 
-            (out_scbp->target_channel_lun & SELBUSB);
-    p->pagedout_ntscbs[index] = out_scbp;
-  }
-
-  /* Page-in */
-  in_scbp->position = out_scbp->position;
-  out_scbp->position = SCB_LIST_NULL;
-  aic7xxx_putscb(p, in_scbp);
-  in_scbp->state &= ~SCB_PAGED_OUT;
+  aic7xxx_run_done_queue(p, /*complete*/ TRUE);
+  return (found);
 }
 
 /*+F*************************************************************************
@@ -2082,1159 +2615,1339 @@
  *   aic7xxx_run_waiting_queues
  *
  * Description:
- *   Scan the assigned_scbs and waiting_scbs queues.  For scbs in the
- *   assigned_scbs queue, we download and start them.  For scbs in the
- *   waiting_scbs queue, we page in as many as we can being careful
- *   not to cause a deadlock for a reconnecting target.
- *
+ *   Scan the awaiting_scbs queue downloading and starting as many
+ *   scbs as we can.
  *-F*************************************************************************/
 static inline void
 aic7xxx_run_waiting_queues(struct aic7xxx_host *p)
 {
   struct aic7xxx_scb *scb;
-  u_char cur_scb, intstat;
-  u_long base = p->base;
-  long flags;
 
-  if ((p->assigned_scbs.head == NULL) && (p->waiting_scbs.head == NULL))
+  if (p->waiting_scbs.head == NULL)
     return;
 
-  save_flags(flags);
-  cli();
-
-  PAUSE_SEQUENCER(p);
-  cur_scb = inb(SCBPTR + base);
-  intstat = inb(INTSTAT + base);
-
+  pause_sequencer(p);
   /*
    * First handle SCBs that are waiting but have been assigned a slot.
    */
-  scb = p->assigned_scbs.head;
-  while (scb != NULL)
-  {
-    scbq_remove_head(&(p->assigned_scbs));
-    outb(scb->position, SCBPTR + base);
-    aic7xxx_putscb(p, scb);
-    /* Mark this as an active command. */
-    scb->state = (scb->state & ~SCB_ASSIGNEDQ) | SCB_ACTIVE;
-    outb(scb->position, QINFIFO + base);
-    scb = p->assigned_scbs.head;
-  }
-
-  /* Now deal with SCBs that require paging. */
   scb = p->waiting_scbs.head;
-  if (scb != NULL)
+  while (scb != NULL)
   {
-    u_char disc_scb = inb(DISCONNECTED_SCBH + base);
-    u_char active = inb(FLAGS + base) & (SELECTED | IDENTIFY_SEEN);
-    int count = 0;
-    u_char next_scb;
-
-    while (scb != NULL)
+    if (p->curqincnt >= p->qfullcount)
     {
-      /* Attempt to page this SCB in */
-      if (disc_scb == SCB_LIST_NULL)
-        break;
-
-      /*
-       * Advance disc_scb to the next one in the list.
-       */
-      outb(disc_scb, SCBPTR + base);
-      next_scb = inb(SCB_NEXT + base); 
-
-      /*
-       * We have to be careful about when we allow an SCB to be paged out. 
-       * There must always be at least one slot availible for a reconnecting
-       * target in case it references an SCB that has been paged out.  Our
-       * heuristic is that either the disconnected list has at least two
-       * entries in it or there is one entry and the sequencer is activily
-       * working on an SCB which implies that it will either complete or
-       * disconnect before another reconnection can occur.
-       */
-      if ((next_scb != SCB_LIST_NULL) || active)
+      p->curqincnt = inb(p->base + QINCNT) & p->qcntmask;
+      if (p->curqincnt >= p->qfullcount)
       {
-        u_char out_scbi;
-        struct aic7xxx_scb *out_scbp;
-
-        scbq_remove_head(&(p->waiting_scbs));
-
-        /*
-         * Find the in-core SCB for the one we're paging out.
-         */
-        out_scbi = inb(SCB_TAG + base); 
-        out_scbp = (p->scb_array[out_scbi]);
-
-        /* Do the page out and mark the paged in SCB as active. */
-        aic7xxx_page_scb(p, out_scbp, scb);
-
-        /* Mark this as an active command. */
-        scb->state = (scb->state & ~SCB_WAITINGQ) | SCB_ACTIVE;
-
-        /* Queue the command */
-        outb(scb->position, QINFIFO + base);
-        count++;
-
-        /* Advance to the next disconnected SCB */
-        disc_scb = next_scb;
-        scb = p->waiting_scbs.head;
+        break;
       }
-      else
-        scb = NULL;
     }
 
-    if (count)
+    /*
+     * We have some space.
+     */
+    scbq_remove_head(&(p->waiting_scbs));
+    scb->flags &= ~SCB_WAITINGQ;
+
+    outb(scb->hscb->tag, p->base + QINFIFO);
+
+    if ((p->flags & PAGE_ENABLED) != 0)
     {
-      /* 
-       * Update the head of the disconnected list.
+      /*
+       * We only care about this statistic when paging
+       * since it's impossible to overflow the qinfifo
+       * in the non-paging case.
        */
-      outb(disc_scb, DISCONNECTED_SCBH + base);
-      if (disc_scb != SCB_LIST_NULL)
-      {
-        outb(disc_scb, SCBPTR + base);
-        outb(SCB_LIST_NULL, SCB_PREV + base);
-      }
+      p->curqincnt++;
     }
+    scb = p->waiting_scbs.head;
   }
-  /* Restore old position */
-  outb(cur_scb, SCBPTR + base);
 
-  /*
-   * Guard against unpausing the sequencer if there is an interrupt
-   * waiting to happen.
-   */
-  if (!(intstat & (BRKADRINT | SEQINT | SCSIINT)))
-  {
-    UNPAUSE_SEQUENCER(p);
-  }
+  unpause_sequencer(p, FALSE);
+}
 
-  restore_flags(flags);
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_construct_sdtr
+ *
+ * Description:
+ *   Constucts a synchronous data transfer message in the message
+ *   buffer on the sequencer.
+ *-F*************************************************************************/
+static void
+aic7xxx_construct_sdtr(struct aic7xxx_host *p, int start_byte,
+    unsigned char period, unsigned char offset)
+{
+  outb(MSG_EXTENDED,     p->base + MSG_OUT + start_byte);
+  outb(MSG_EXT_SDTR_LEN, p->base + MSG_OUT + 1 + start_byte);
+  outb(MSG_EXT_SDTR,     p->base + MSG_OUT + 2 + start_byte);
+  outb(period,           p->base + MSG_OUT + 3 + start_byte);
+  outb(offset,           p->base + MSG_OUT + 4 + start_byte);
+  outb(start_byte + 5,   p->base + MSG_LEN);
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_isr
+ *   aic7xxx_construct_wdtr
  *
  * Description:
- *   SCSI controller interrupt handler.
+ *   Constucts a wide data transfer message in the message buffer
+ *   on the sequencer.
+ *-F*************************************************************************/
+static void
+aic7xxx_construct_wdtr(struct aic7xxx_host *p, int start_byte,
+    unsigned char bus_width)
+{
+  outb(MSG_EXTENDED,     p->base + MSG_OUT + start_byte);
+  outb(MSG_EXT_WDTR_LEN, p->base + MSG_OUT + 1 + start_byte);
+  outb(MSG_EXT_WDTR,     p->base + MSG_OUT + 2 + start_byte);
+  outb(bus_width,        p->base + MSG_OUT + 3 + start_byte);
+  outb(start_byte + 4,   p->base + MSG_LEN);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_calc_residual
  *
- *   NOTE: Since we declared this using SA_INTERRUPT, interrupts should
- *         be disabled all through this function unless we say otherwise.
+ * Description:
+ *   Calculate the residual data not yet transferred.
  *-F*************************************************************************/
 static void
-aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
+aic7xxx_calculate_residual (struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 {
-  int base, intstat, actual, scb_index, run_aborted_queue = FALSE;
-  struct aic7xxx_host *p;
-  struct aic7xxx_scb *scb = NULL;
-  short         transfer;
-  unsigned char ha_flags, scsi_id, bus_width;
-  unsigned char offset, rate, scratch, scratch_offset;
-  unsigned char max_offset, rej_byte;
-  unsigned short target_mask;
-  char channel;
-  unsigned int addr; /* must be 32 bits */
+  struct aic7xxx_hwscb *hscb;
   Scsi_Cmnd *cmd;
+  int actual;
 
-  p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata;
+  cmd = scb->cmd;
+  hscb = scb->hscb;
 
   /*
-   * Search for the host with a pending interrupt.  If we can't find
-   * one, then we've encountered a spurious interrupt.
+   *  Don't destroy valid residual information with
+   *  residual coming from a check sense operation.
    */
-  while ((p != NULL) && !(inb(INTSTAT + p->base) & INT_PEND))
+  if (((scb->hscb->control & DISCONNECTED) == 0) &&
+      (scb->flags & SCB_SENSE) == 0)
   {
-    if (p->next == NULL)
-    {
-      p = NULL;
-    }
-    else
+    /*
+     *  We had an underflow. At this time, there's only
+     *  one other driver that bothers to check for this,
+     *  and cmd->underflow seems to be set rather half-
+     *  heartedly in the higher-level SCSI code.
+     */
+    actual = aic7xxx_length(cmd, hscb->residual_SG_segment_count);
+
+    actual -= (hscb->residual_data_count[2] << 16) |
+              (hscb->residual_data_count[1] <<  8) |
+              hscb->residual_data_count[0];
+
+    if (actual < cmd->underflow)
     {
-      p = (struct aic7xxx_host *) p->next->hostdata;
+      printk(KERN_WARNING "(scsi%d:%d:%d) Underflow - "
+             "Wanted at least %u, got %u, residual SG count %d.\n",
+             p->host_no, TC_OF_SCB(scb), cmd->underflow, actual,
+             hscb->residual_SG_segment_count);
+      aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+      aic7xxx_status(cmd) = hscb->target_status;
     }
   }
 
-  if (p == NULL)
-    return;
-
   /*
-   * Keep track of interrupts for /proc/scsi
+   * Clean out the residual information in the SCB for the
+   * next consumer.
    */
-  p->isr_count++;
+  hscb->residual_data_count[2] = 0;
+  hscb->residual_data_count[1] = 0;
+  hscb->residual_data_count[0] = 0;
+  hscb->residual_SG_segment_count = 0;
+}
 
-  if (!(p->flags & A_SCANNED) && (p->isr_count == 1))
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_handle_device_reset
+ *
+ * Description:
+ *   Interrupt handler for sequencer interrupts (SEQINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_device_reset(struct aic7xxx_host *p, int target, char channel)
+{
+  unsigned short targ_mask;
+  unsigned char  targ_scratch;
+  int scratch_offset = target;
+  int found;
+
+  if (channel == 'B')
   {
-    /*
-     * We must only have one card at this IRQ and it must have been
-     * added to the board data before the spurious interrupt occurred.
-     * It is sufficient that we check isr_count and not the spurious
-     * interrupt count.
-     */
-    printk("aic7xxx: (aic7xxx_isr) Encountered spurious interrupt.\n");
-    return;
+    scratch_offset += 8;
   }
-
-  base = p->base;
+  targ_mask = (0x01 << scratch_offset);
   /*
-   * Handle all the interrupt sources - especially for SCSI
-   * interrupts, we won't get a second chance at them.
+   * Go back to async/narrow transfers and renegotiate.
    */
-  intstat = inb(INTSTAT + base);
+  p->needsdtr |= p->needsdtr_copy & targ_mask;
+  p->needwdtr |= p->needwdtr_copy & targ_mask;
+  p->sdtr_pending &= ~targ_mask;
+  p->wdtr_pending &= ~targ_mask;
+  targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+  targ_scratch &= SXFR;
+  outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
+  found = aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
+  printk(KERN_WARNING "(scsi%d:%d:%d) Bus Device Reset delivered, "
+         "%d SCBs aborted.\n", p->host_no, target, CHAN_TO_INT(channel), found);
+  aic7xxx_run_done_queue(p, /*complete*/ TRUE);
+}
 
-  /*
-   * Indicate that we're in the interrupt handler.
-   */
-  p->flags |= IN_ISR;
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_handle_seqint
+ *
+ * Description:
+ *   Interrupt handler for sequencer interrupts (SEQINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
+{
+  struct aic7xxx_scb *scb;
+  unsigned short target_mask;
+  unsigned char target, scratch_offset;
+  char channel;
 
-  if (intstat & BRKADRINT)
+  if ((inb(p->base + SEQ_FLAGS) & RESELECTED) != 0)
   {
-    int i;
-    unsigned char errno = inb(ERROR + base);
-
-    printk(KERN_ERR "scsi%d: BRKADRINT error(0x%x):\n", p->host_no, errno);
-    for (i = 0; i < NUMBER(hard_error); i++)
-    {
-      if (errno & hard_error[i].errno)
-      {
-        printk(KERN_ERR "  %s\n", hard_error[i].errmesg);
-      }
-    }
-    panic("scsi%d: BRKADRINT, error 0x%x, seqaddr 0x%x.\n", p->host_no,
-          inb(ERROR + base), (inb(SEQADDR1 + base) << 8) | inb(SEQADDR0 + base));
+    target = (inb(p->base + SELID) >> 4) & 0x0F;
   }
-
-  if (intstat & SEQINT)
+  else
   {
-    /*
-     * Although the sequencer is paused immediately on
-     * a SEQINT, an interrupt for a SCSIINT condition will
-     * unpaused the sequencer before this point.
-     */
-    PAUSE_SEQUENCER(p);
+    target = (inb(p->base + SCSIID) >> 4) & 0x0F;
+  }
+  scratch_offset = target;
+  channel = 'A';
+  if (inb(p->base + SBLKCTL) & SELBUSB)
+  {
+    channel = 'B';
+    scratch_offset += 8;
+  }
+  target_mask = (0x01 << scratch_offset);
 
-    scsi_id = (inb(SCSIID + base) >> 4) & 0x0F;
-    scratch_offset = scsi_id;
-    channel = 'A';
-    if (inb(SBLKCTL + base) & SELBUSB)
-    {
-      channel = 'B';
-      scratch_offset += 8;
-    }
-    target_mask = (0x01 << scratch_offset);
+  switch (intstat & SEQINT_MASK)
+  {
+    case NO_MATCH:
+      {
+        /*
+         * This could be for a normal abort request.  Figure out
+         * which SCB we were trying to find and only give an error
+         * if we didn't ask for this to happen.
+         */
+        unsigned char scb_index;
+        unsigned char busy_scbid;
+        unsigned char arg1;
+
+        busy_scbid = aic7xxx_index_busy_target(p, target, channel,
+            /*unbusy*/ FALSE);
+        arg1 = inb(p->base + ARG_1);
 
-    switch (intstat & SEQINT_MASK)
-    {
-      case NO_MATCH:
-    	if (p->flags & PAGE_ENABLED)
+        if (arg1 == SCB_LIST_NULL)
         {
-    	  /* SCB Page-in request */
-    	  struct aic7xxx_scb *outscb;
-    	  u_char arg_1 = inb(ARG_1 + base);
-          int use_disconnected = FALSE;
-
-          /*
-           * The sequencer expects this value upon return.  Assume
-           * we will find the paged out SCB and set the value now.
-           * If we don't, and one of the methods used to acquire an
-           * SCB calls aic7xxx_done(), we will end up in our queue
-           * routine and unpause the sequencer without giving it the
-           * correct return value, which causes a hang.
-           */
-    	  outb(SCB_PAGEDIN, RETURN_1 + base);
-    	  if (arg_1 == SCB_LIST_NULL)
-          {
-    	    /* Non-tagged command */
-    	    int index = scsi_id;
-            if (channel == 'B')
-            {
-              index |= SELBUSB;
-            }
-    	    scb = p->pagedout_ntscbs[index];
-    	  }
-    	  else
-    	    scb = (p->scb_array[arg_1]);
+          /* untagged request */
+          scb_index = busy_scbid;
+        }
+        else
+        {
+          scb_index = arg1;
+        }
 
-          if (!(scb->state & SCB_PAGED_OUT))
+        if (scb_index < p->scb_data->numscbs)
+        {
+          scb = p->scb_data->scb_array[scb_index];
+          if (scb->hscb->control & ABORT_SCB)
           {
-  	    printk(KERN_WARNING "scsi%d: No active paged-out SCB for reconnecting "
-  	  	  "target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n",
-    		  p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
-    	    aic7xxx_unbusy_target(scsi_id, channel, base);
-  	    outb(CLRSELTIMEO, CLRSINT1 + base);
-            outb(0, RETURN_1 + base);
+            /*
+             * We expected this.  Let the busfree handler take care
+             * of this when we the abort is finially sent.  Set
+             * IDENTIFY_SEEN so that the busfree handler knows that
+             * there is an SCB to cleanup.
+             */
+            outb(inb(p->base + SEQ_FLAGS) | IDENTIFY_SEEN, p->base + SEQ_FLAGS);
+            printk(KERN_INFO "(scsi%d:%d:%d) reconnect SCB abort successful\n",
+                   p->host_no, TC_OF_SCB(scb));
             break;
           }
+        }
+        printk(KERN_WARNING "(scsi%d:%d:%d) No active SCB for reconnecting "
+               "target - Issuing BUS DEVICE RESET.\n",
+               p->host_no, target, CHAN_TO_INT(channel));
+
+        printk(KERN_WARNING "      SAVED_TCL=0x%x, ARG_1=0x%x, SEQADDR=0x%x\n",
+               inb(p->base + SAVED_TCL), arg1,
+               (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+        aic7xxx_handle_device_reset(p, target, channel);
+      }
+      break;
 
-    	  /*
-    	   * Now to pick the SCB to page out.  Either take a free SCB, an
-           * assigned SCB, an SCB that just completed, or the first one
-           * on the disconnected SCB list.
-    	   */
-    	  if (p->scb_link->free_scbs.head != NULL)
-          {
-    	    outscb = p->scb_link->free_scbs.head;
-    	    scbq_remove_head(&p->scb_link->free_scbs);
-    	    scb->position = outscb->position;
-    	    outscb->position = SCB_LIST_NULL;
-    	    scbq_insert_head(&p->page_scbs, outscb);
-    	    outb(scb->position, SCBPTR + base);
-    	    aic7xxx_putscb(p, scb);
-    	    scb->state &= ~SCB_PAGED_OUT;
-    	  }
-    	  else if (p->assigned_scbs.head != NULL)
-          {
-            outscb = p->assigned_scbs.head;
-            scbq_remove_head(&p->assigned_scbs);
-            scb->position = outscb->position;
-            outscb->position = SCB_LIST_NULL;
-            scbq_insert_head(&p->waiting_scbs, outscb);
-            outscb->state = (outscb->state & ~SCB_ASSIGNEDQ) | SCB_WAITINGQ;
-            outb(scb->position, SCBPTR + base);
-    	    aic7xxx_putscb(p, scb);
-            scb->state &= ~SCB_PAGED_OUT;
-          }
-          else if (intstat & CMDCMPLT)
+    case NO_MATCH_BUSY:
+      {
+        /*
+         * XXX - Leave this as a panic for the time being since it
+         * indicates a bug in the timeout code for this to happen.
+         */
+        unsigned char scb_index;
+
+        scb_index = inb(p->base + CUR_SCBID);
+        scb = p->scb_data->scb_array[scb_index];
+
+        panic("scsi%d:  Target %d, channel %c, Target busy link failure, "
+              "but busy SCB exists!\n",
+              p->host_no, target, channel);
+      }
+      break;
+
+    case SEND_REJECT:
+      {
+        unsigned char rej_byte;
+
+        rej_byte = inb(p->base + REJBYTE);
+        printk(KERN_WARNING "(scsi%d:%d:%d) Rejecting unknown message (0x%x) "
+               "received from target, SEQ_FLAGS=0x%x\n",
+               p->host_no, target, CHAN_TO_INT(channel), rej_byte,
+               inb(p->base + SEQ_FLAGS));
+      }
+      break;
+
+    case NO_IDENT:
+      {
+        /*
+         * The reconnecting target either did not send an identify
+         * message, or did, but we didn't find and SCB to match and
+         * before it could respond to our ATN/abort, it hit a dataphase.
+         * The only safe thing to do is to blow it away with a bus
+         * reset.
+         */
+        int found;
+
+        printk(KERN_WARNING "(scsi%d:%d:%d): Target did not send an IDENTIFY "
+               "message; LASTPHASE 0x%x, SAVED_TCL 0x%x\n",
+               p->host_no, target, CHAN_TO_INT(channel),
+               inb(p->base + LASTPHASE), inb(p->base + SAVED_TCL));
+
+        found = aic7xxx_reset_channel(p, channel, /*initiate reset*/ TRUE);
+
+        printk(KERN_WARNING "scsi%d: Issued channel %c bus reset; "
+     	       "%d SCBs aborted\n", p->host_no, channel, found);
+      }
+      break;
+
+    case BAD_PHASE:
+      if (inb(p->base + LASTPHASE) == P_BUSFREE)
+      {
+        printk(KERN_WARNING "(scsi%d:%d:%d): Missed busfree.\n",
+               p->host_no, CHAN_TO_INT(channel), target);
+        restart_sequencer(p);
+      }
+      else
+      {
+        printk(KERN_WARNING "(scsi%d:%d:%d): Unknown scsi bus phase, attempting "
+               "to continue\n", p->host_no, CHAN_TO_INT(channel), target);
+      }
+      break;
+
+    case EXTENDED_MSG:
+      {
+	unsigned char message_length;
+	unsigned char message_code;
+        unsigned char scb_index;
+
+	message_length = inb(p->base + MSGIN_EXT_LEN);
+	message_code = inb(p->base + MSGIN_EXT_OPCODE);
+        scb_index = inb(p->base + SCB_TAG);
+        scb = p->scb_data->scb_array[scb_index];
+
+	switch (message_code)
+	{
+          case MSG_EXT_SDTR:
           {
-            int scb_index;
+            unsigned char period;
+            unsigned char offset;
+            unsigned char saved_offset;
+            unsigned char targ_scratch;
+            unsigned char max_offset;
+            unsigned char rate;
 
-            outb(CLRCMDINT, CLRINT + base);
-            scb_index = inb(QOUTFIFO + base);
-            if (!(inb(QOUTCNT + base) & p->qcntmask))
+            if (message_length != MSG_EXT_SDTR_LEN)
             {
-              intstat &= ~CMDCMPLT;
-            }
-            outscb = (p->scb_array[scb_index]);
-            if (!(outscb->state & SCB_ACTIVE))
-            {
-	      printk(KERN_WARNING "scsi%d: No command for completed SCB %d "
-	             "during NO_MATCH interrupt\n", scb_index, p->host_no);
-              use_disconnected = TRUE;
+              outb(SEND_REJ, p->base + RETURN_1);
+              break;
             }
+
+            period = inb(p->base + MSGIN_EXT_BYTES);
+            saved_offset = inb(p->base + MSGIN_EXT_BYTES + 1);
+            targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+
+            if (targ_scratch & WIDEXFER)
+              max_offset = MAX_OFFSET_16BIT;
             else
+              max_offset = MAX_OFFSET_8BIT;
+            offset = MIN(saved_offset, max_offset);
+
+            aic7xxx_scsirate(p, &rate, &period, &offset, target, channel);
+
+            /*
+             * Preserve the WideXfer flag.
+             */
+            targ_scratch = rate | (targ_scratch & WIDEXFER);
+
+            /*
+             * Update both the target scratch area and current SCSIRATE.
+             */
+            outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
+            outb(targ_scratch, p->base + SCSIRATE);
+
+            /*
+             * See if we initiated Sync Negotiation and didn't have
+             * have to fall down to async transfers.
+             */
+            if ((scb->flags & SCB_MSGOUT_SDTR) != 0)
             {
-              scb->position = outscb->position;
-              outscb->position = SCB_LIST_NULL;
-              outb(scb->position, SCBPTR + base);
-              aic7xxx_putscb(p, scb);
-              scb->state &= ~SCB_PAGED_OUT;
-              outscb->cmd->result |= (aic7xxx_error(outscb->cmd) << 16);
-              if ((outscb->cmd->flags & WAS_SENSE) && 
-                 !(outscb->cmd->flags & ASKED_FOR_SENSE))
+              /* We started it. */
+              if (saved_offset == offset)
               {
-                /*
-                 * Got sense information.
-                 */
-	        outscb->cmd->flags &= ASKED_FOR_SENSE;
+        	/*
+        	 * Don't send an SDTR back to the target.
+        	 */
+        	outb(0, p->base + RETURN_1);
+              }
+              else
+              {
+        	/* We went too low - force async. */
+        	outb(SEND_REJ, p->base + RETURN_1);
               }
-              p->device_status[TARGET_INDEX(outscb->cmd)].flags
-                |= DEVICE_SUCCESS;
-              aic7xxx_done(p, outscb);
             }
+            else
+            {
+              /*
+               * Send our own SDTR in reply.
+               *
+               * We want to see this message as we don't expect a target
+               * to send us a SDTR request first.
+               */
+              printk(KERN_WARNING "scsi%d: Sending SDTR!!\n", p->host_no);
+              aic7xxx_construct_sdtr(p, /* start byte */ 0, period, offset);
+              outb(SEND_MSG, p->base + RETURN_1);
+            }
+            /*
+             * Clear the flags.
+             */
+            p->needsdtr &= ~target_mask;
+            break;
           }
-          else
-          {
-            use_disconnected = TRUE;
-          }
-          if (use_disconnected)
+
+          case MSG_EXT_WDTR:
           {
-    	    u_char tag;
-    	    u_char next;
-    	    u_char disc_scb = inb(DISCONNECTED_SCBH + base);
-    	    if (disc_scb != SCB_LIST_NULL)
+            unsigned char scratch, bus_width;
+
+            if (message_length != MSG_EXT_WDTR_LEN)
             {
-    	      outb(disc_scb, SCBPTR + base);
-    	      tag = inb(SCB_TAG + base);
-    	      outscb = (p->scb_array[tag]);
-    	      next = inb(SCB_NEXT + base);
-    	      if (next != SCB_LIST_NULL)
-              {
-    	        outb(next, SCBPTR + base);
-    	        outb(SCB_LIST_NULL, SCB_PREV + base);
-    	        outb(disc_scb, SCBPTR + base);
-    	      }
-    	      outb(next, DISCONNECTED_SCBH + base);
-    	      aic7xxx_page_scb(p, outscb, scb);
-    	    }
-            else if (inb(QINCNT + base) & p->qcntmask)
+              outb(SEND_REJ, p->base + RETURN_1);
+              break;
+            }
+
+            bus_width = inb(p->base + MSGIN_EXT_BYTES);
+            scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+
+            if ((scb->flags & SCB_MSGOUT_WDTR) != 0)
             {
-              /* Pull one of our queued commands as a last resort. */
-              disc_scb = inb(QINFIFO + base);
-              outb(disc_scb, SCBPTR + base);
-              tag = inb(SCB_TAG + base);
-              outscb = (p->scb_array[tag]);
-              if ((outscb->control & 0x23) != TAG_ENB)
+              /*
+               * Don't send an WDTR back to the target, since we asked first.
+               */
+              outb(0, p->base + RETURN_1);
+              switch (bus_width)
               {
-                /*
-                 * This is not a simple tagged command so its position
-                 * in the queue matters.  Take the command at the end of
-                 * the queue instead.
-                 */
-                int i;
-                int saved_queue[AIC7XXX_MAXSCB];
-                int queued = inb(QINCNT + base) & p->qcntmask;
-
-                /* Count the command we removed already */
-                saved_queue[0] = disc_scb;
-                queued++;
-
-                /* Empty the input queue. */
-                for (i = 1; i < queued; i++)
-                {
-                  saved_queue[i] = inb(QINFIFO + base);
-                }
-
-                /* Put everyone back but the last entry. */
-                queued--;
-                for (i = 0; i < queued; i++)
-                {
-                  outb(saved_queue[i], QINFIFO + base);
-                }
-
-                outb(saved_queue[queued], SCBPTR + base);
-                tag = inb(SCB_TAG + base);
-                outscb = (p->scb_array[tag]);
+        	case BUS_8_BIT:
+        	  scratch &= 0x7F;
+        	  break;
+
+        	case BUS_16_BIT:
+                  if (aic7xxx_verbose)
+                  {
+        	    printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 "
+  	  	         "bit transfers.\n", p->host_no, target, channel);
+                  }
+        	  scratch |= WIDEXFER;
+        	  break;
+
+        	case BUS_32_BIT:
+        	  outb(SEND_REJ, p->base + RETURN_1);
+                  /* No verbose here!  We want to see this condition. */
+        	  printk(KERN_WARNING "scsi%d: Target %d, channel %c, "
+  			"requesting 32 bit transfers, rejecting...\n",
+                	 p->host_no, target, channel);
+        	  break;
+
+        	default:
+        	  break;
               }
-              scb->position = outscb->position;
-              outscb->position = SCB_LIST_NULL;
-              scbq_insert_head(&p->waiting_scbs, outscb);
-              outscb->state |= SCB_WAITINGQ;
-              aic7xxx_putscb(p, scb);
-              scb->state &= ~SCB_PAGED_OUT;
             }
             else
             {
-  	      printk(KERN_WARNING "scsi%d: Page-in request with no candidates "
-  	  	    "target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n",
-    		    p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
-              aic7xxx_unbusy_target(scsi_id, channel, base);
-              outb(CLRSELTIMEO, CLRSINT1 + base);
-              outb(0, RETURN_1 + base);
+              /*
+               * Send our own WDTR in reply.
+               */
+              switch (bus_width)
+              {
+        	case BUS_8_BIT:
+        	  scratch &= 0x7F;
+        	  break;
+
+        	case BUS_32_BIT:
+        	case BUS_16_BIT:
+        	  if (p->bus_type == AIC_WIDE)
+        	  {
+                    printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 "
+  			   "bit transfers.\n", p->host_no, target, channel);
+                    bus_width = BUS_16_BIT;
+                    scratch |= WIDEXFER;
+        	  }
+        	  else
+        	  {
+                    bus_width = BUS_8_BIT;
+                    scratch &= 0x7F;  /* XXX - FreeBSD doesn't do this. */
+        	  }
+        	  break;
+
+        	default:
+        	  break;
+              }
+              aic7xxx_construct_wdtr(p, /* start byte */ 0, bus_width);
+              outb(SEND_MSG, p->base + RETURN_1);
             }
-          }
-    	}
-    	else
-        {
-  	  printk(KERN_WARNING "scsi%d: No active SCB for reconnecting "
-  	  	"target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n",
-    		p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
-    	  aic7xxx_unbusy_target(scsi_id, channel, base);
-    	  outb(0, SCB_CONTROL + base);
-  	  outb(CLRSELTIMEO, CLRSINT1 + base);
-          outb(0, RETURN_1 + base);
-        }
-  	break;
-
-      case BAD_PHASE:
-	panic("scsi%d: Unknown scsi bus phase.\n", p->host_no);
-	break;
-
-      case SEND_REJECT:
-        rej_byte = inb(REJBYTE + base);
-        if ((rej_byte & 0xF0) == 0x20)
-        {
-          scb_index = inb(SCB_TAG + base);
-          scb = (p->scb_array[scb_index]);
-          printk(KERN_WARNING "scsi%d: Tagged message received without identify."
-                 "Disabling tagged commands for target %d channel %c.\n",
-                  p->host_no, scsi_id, channel);
-          scb->cmd->device->tagged_supported = 0;
-          scb->cmd->device->tagged_queue = 0;
-        }
-        else
-        {
-          printk(KERN_WARNING "scsi%d: Rejecting unknown message (0x%x) received "
-                 "from target %d channel %c.\n",
-                 p->host_no, rej_byte, scsi_id, channel);
-        }
-	break;
-
-      case NO_IDENT:
-	panic("scsi%d: Target %d, channel %c, did not send an IDENTIFY "
-	      "message. SAVED_TCL 0x%x.\n",
-              p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
-	break;
+            p->needwdtr &= ~target_mask;
+            outb(scratch, p->base + TARG_SCRATCH + scratch_offset);
+            outb(scratch, p->base + SCSIRATE);
+            break;
+	  }  /* case MSG_EXT_WDTR */
 
-      case SDTR_MSG:
-	/*
-	 * Help the sequencer to translate the negotiated
-	 * transfer rate. Transfer is 1/4 the period
-	 * in ns as is returned by the sync negotiation
-	 * message. So, we must multiply by four.
-	 */
-	transfer = (inb(ARG_1 + base) << 2);
-	offset = inb(ACCUM + base);
-	scratch = inb(TARG_SCRATCH + base + scratch_offset);
+          default:
+            /*
+             * Unknown extended message - reject it.
+             */
+            outb(SEND_REJ, p->base + RETURN_1);
+            break;
+	}  /* switch (message_code) */
+      }  /* case EXTENDED_MSG */
+      break;
+
+    case REJECT_MSG:
+      {
 	/*
-	 * The maximum offset for a wide device is 0x08; for a
-	 * 8-bit bus device the maximum offset is 0x0F.
+	 * What we care about here is if we had an outstanding SDTR
+	 * or WDTR message for this target. If we did, this is a
+	 * signal that the target is refusing negotiation.
 	 */
-	if (scratch & WIDEXFER)
+	unsigned char targ_scratch;
+        unsigned char scb_index;
+
+        scb_index = inb(p->base + SCB_TAG);
+        scb = p->scb_data->scb_array[scb_index];
+	targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+
+	if ((scb->flags & SCB_MSGOUT_WDTR) != 0)
 	{
-	  max_offset = 0x08;
+          /*
+           * note 8bit xfers and clear flag
+           */
+          targ_scratch &= 0x7F;
+          p->needwdtr &= ~target_mask;
+          printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing WIDE "
+  		 "negotiation; using 8 bit transfers.\n",
+  		 p->host_no, target, channel);
 	}
 	else
 	{
-	  max_offset = 0x0F;
-	}
-	aic7xxx_scsirate(p, &rate, transfer, MIN(offset, max_offset),
-                         scsi_id, channel);
-	/*
-	 * Preserve the wide transfer flag.
-	 */
-	scratch = rate | (scratch & WIDEXFER);
-	outb(scratch, TARG_SCRATCH + base + scratch_offset);
-	outb(scratch, SCSIRATE + base);
-	if ((scratch & 0x0F) == 0)
-	{
-          /*
-           * One of two things happened.  Either the device requested
-           * asynchronous data transfers, or it requested a synchronous
-           * data transfer rate that was so low that asynchronous
-           * transfers are faster (not to mention the controller won't
-           * support them).  In both cases the synchronous data transfer
-           * rate and the offset are set to 0 indicating asynchronous
-           * transfers.
-           *
-           * If the device requested an asynchronous transfer, then
-           * accept the request.  If the device is being forced to
-           * asynchronous data transfers and this is the first time
-           * we've seen the request, accept the request.  If we've
-           * already seen the request, then attempt to force
-           * asynchronous data transfers by rejecting the message.
-           */
-          if ((offset == 0) || (p->sdtr_pending & target_mask))
+          if ((scb->flags & SCB_MSGOUT_SDTR) != 0)
           {
             /*
-             * Device requested asynchronous transfers or we're
-             * forcing asynchronous transfers for the first time.
+             * note asynch xfers and clear flag
              */
-            outb(0, RETURN_1 + base);
-          }
-          else
-          {
-            /*
-	     * The first time in forcing asynchronous transfers
-             * failed, so we try sending a reject message.
-	     */
-	    outb(SEND_REJ, RETURN_1 + base);
+            targ_scratch &= 0xF0;
+            p->needsdtr &= ~target_mask;
+            printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing "
+  		   "synchronous negotiation; using asynchronous transfers.\n",
+  		   p->host_no, target, channel);
           }
+          /*
+           * Otherwise, we ignore it.
+           */
 	}
-	else
-	{
-	  /*
-	   * See if we initiated Sync Negotiation
-	   */
-	  if (p->sdtr_pending & target_mask)
-	  {
-	    /*
-	     * Don't send an SDTR back to the target.
-	     */
-	    outb(0, RETURN_1 + base);
-	  }
-	  else
-	  {
-	    /*
-	     * Send our own SDTR in reply.
-	     */
-	    printk("aic7xxx: Sending SDTR!!\n");
-	    outb(SEND_SDTR, RETURN_1 + base);
-	  }
-	}
-	/*
-	 * Clear the flags.
-	 */
-	p->needsdtr &= ~target_mask;
-	p->sdtr_pending &= ~target_mask;
-	break;
-
-      case WDTR_MSG:
-      {
-	bus_width = inb(ARG_1 + base);
-	printk(KERN_INFO "scsi%d: Received MSG_WDTR, Target %d, channel %c "
-	       "needwdtr(0x%x).\n", p->host_no, scsi_id, channel, p->needwdtr);
-	scratch = inb(TARG_SCRATCH + base + scratch_offset);
-
-	if (p->wdtr_pending & target_mask)
-	{
-	  /*
-	   * Don't send an WDTR back to the target, since we asked first.
-	   */
-	  outb(0, RETURN_1 + base);
-	  switch (bus_width)
-	  {
-	    case BUS_8_BIT:
-	      scratch &= 0x7F;
-	      break;
-
-	    case BUS_16_BIT:
-	      printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 bit "
-                     "transfers.\n", p->host_no, scsi_id, channel);
-	      scratch |= 0x80;
-	      break;
-
-	    case BUS_32_BIT:
-	      outb(SEND_REJ, RETURN_1 + base);
-	      printk(KERN_INFO "scsi%d: Target %d, channel %c, requesting 32 bit "
-                     "transfers, rejecting...\n", p->host_no, scsi_id, channel);
-	      break;
-	  }
-	}
-	else
-	{
-	  /*
-	   * Send our own WDTR in reply.
-	   */
-	  printk(KERN_INFO "scsi%d: Will send WDTR!!\n", p->host_no);
-	  switch (bus_width)
-	  {
-	    case BUS_8_BIT:
-	      scratch &= 0x7F;
-	      break;
-
-	    case BUS_32_BIT:
-	      /*
-               * Negotiate 16 bits.
-               */
-	      bus_width = BUS_16_BIT;
-	      /* Yes, we mean to fall thru here. */
-
-	    case BUS_16_BIT:
-	      printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 bit "
-                     "transfers.\n", p->host_no, scsi_id, channel);
-	      scratch |= 0x80;
-	      break;
-	  }
-	  outb(bus_width | SEND_WDTR, RETURN_1 + base);
-	}
-	p->needwdtr &= ~target_mask;
-	p->wdtr_pending &= ~target_mask;
-	outb(scratch, TARG_SCRATCH + base + scratch_offset);
-	outb(scratch, SCSIRATE + base);
-	break;
+        outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
+        outb(targ_scratch, p->base + SCSIRATE);
       }
+      break;
 
-      case REJECT_MSG:
+    case BAD_STATUS:
       {
-	/*
-	 * What we care about here is if we had an
-	 * outstanding SDTR or WDTR message for this
-	 * target. If we did, this is a signal that
-	 * the target is refusing negotiation.
+	unsigned char scb_index;
+	struct aic7xxx_hwscb *hscb;
+	Scsi_Cmnd *cmd;
+
+	/* The sequencer will notify us when a command has an error that
+	 * would be of interest to the kernel.  This allows us to leave
+	 * the sequencer running in the common case of command completes
+	 * without error.  The sequencer will have DMA'd the SCB back
+	 * up to us, so we can reference the drivers SCB array.
 	 */
+	scb_index = inb(p->base + SCB_TAG);
+	scb = p->scb_data->scb_array[scb_index];
+	hscb = scb->hscb;
 
-	scratch = inb(TARG_SCRATCH + base + scratch_offset);
-
-	if (p->wdtr_pending & target_mask)
-	{
-	  /*
-	   * note 8bit xfers and clear flag
-	   */
-	  scratch &= 0x7F;
-	  p->needwdtr &= ~target_mask;
-	  p->wdtr_pending &= ~target_mask;
-	  printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing WIDE "
-                 "negotiation; using 8 bit transfers.\n",
-                 p->host_no, scsi_id, channel);
-	}
-	else
-	{
-	  if (p->sdtr_pending & target_mask)
-	  {
-	    /*
-	     * note asynch xfers and clear flag
-	     */
-	    scratch &= 0xF0;
-	    p->needsdtr &= ~target_mask;
-	    p->sdtr_pending &= ~target_mask;
-	    printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing "
-                   "synchronous negotiation; using asynchronous transfers.\n",
-                   p->host_no, scsi_id, channel);
-	  }
-	  /*
-	   * Otherwise, we ignore it.
-	   */
-	}
-	outb(scratch, TARG_SCRATCH + base + scratch_offset);
-	outb(scratch, SCSIRATE + base);
-	break;
-      }
-
-      case BAD_STATUS:
-        /* The sequencer will notify us when a command has an error that
-         * would be of interest to the kernel.  This allows us to leave
-         * the sequencerrunning in the common case of command completes
-         * without error.
-         */
-
-	scb_index = inb(SCB_TAG + base);
-	scb = (p->scb_array[scb_index]);
-	outb(0, RETURN_1 + base);   /* CHECK_CONDITION may change this */
-	if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+	/*
+	 * Set the default return value to 0 indicating not to send
+	 * sense.  The sense code will change this if needed and this
+	 * reduces code duplication.
+	 */
+	outb(0, p->base + RETURN_1);
+	if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
 	{
-	  printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
-		 "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no,
-		 intstat, scb_index, scb->state, (unsigned long) scb->cmd);
+          printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
+        	 "SEQINT 0x%x, scb %d, flags 0x%x, cmd 0x%x.\n", p->host_no,
+        	 intstat, scb_index, scb->flags, (unsigned int) scb->cmd);
 	}
 	else
 	{
-	  cmd = scb->cmd;
-          scb->target_status = inb(SCB_TARGET_STATUS + base);
-	  aic7xxx_status(cmd) = scb->target_status;
+          cmd = scb->cmd;
+  	  hscb->target_status = inb(p->base + SCB_TARGET_STATUS);
+          aic7xxx_status(cmd) = hscb->target_status;
 
-	  cmd->result |= scb->target_status;
+          cmd->result |= hscb->target_status;
 
-	  switch (status_byte(scb->target_status))
-	  {
-	    case GOOD:
-              printk(KERN_WARNING "aic7xxx: Interrupted for status of GOOD???\n");
-	      break;
-
-	    case CHECK_CONDITION:
-	      if ((aic7xxx_error(cmd) == 0) && !(cmd->flags & WAS_SENSE))
-	      {
-                unsigned char tcl;
-		unsigned int  req_buf; /* must be 32 bits */
+          switch (status_byte(hscb->target_status))
+          {
+            case GOOD:
+  	      printk(KERN_WARNING "(scsi%d:%d:%d) Interrupted for status of "
+                     "GOOD???\n", p->host_no, TC_OF_SCB(scb));
+              break;
 
-                tcl = scb->target_channel_lun;
+            case CHECK_CONDITION:
+              if ((aic7xxx_error(cmd) == 0) && !(scb->flags & SCB_SENSE))
+              {
+        	unsigned int addr;    /* must be 32 bits */
+        	/*
+        	 * XXX - How do we save the residual (if there is one).
+        	 */
+                aic7xxx_calculate_residual(p, scb);
+
+        	/*
+  		 * Send a sense command to the requesting target.
+        	 * XXX - revisit this and get rid of the memcopys.
+  		 */
+        	memcpy((void *) scb->sense_cmd, (void *) generic_sense,
+        	       sizeof(generic_sense));
+
+        	scb->sense_cmd[1] = (cmd->lun << 5);
+        	scb->sense_cmd[4] = sizeof(cmd->sense_buffer);
+
+        	scb->sg_list[0].address = VIRT_TO_BUS(&cmd->sense_buffer);
+        	scb->sg_list[0].length = sizeof(cmd->sense_buffer);
+        	cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
 
-		/*
-                 * Send a sense command to the requesting target.
+                /*
+                 * XXX - We should allow disconnection, but can't as it
+                 * might allow overlapped tagged commands.
                  */
-		cmd->flags |= WAS_SENSE;
-		memcpy((void *) scb->sense_cmd, (void *) generic_sense,
-		       sizeof(generic_sense));
-
-		scb->sense_cmd[1] = (cmd->lun << 5);
-		scb->sense_cmd[4] = sizeof(cmd->sense_buffer);
-
-		scb->sg_list[0].address = VIRT_TO_BUS(&cmd->sense_buffer);
-		scb->sg_list[0].length = sizeof(cmd->sense_buffer);
-		req_buf = VIRT_TO_BUS(&scb->sg_list[0]);
-		cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
-
-                scb->control = scb->control & DISCENB;
-		scb->target_channel_lun = tcl;
-		addr = VIRT_TO_BUS(scb->sense_cmd);
-		scb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
-		memcpy(scb->SCSI_cmd_pointer, &addr,
-		       sizeof(scb->SCSI_cmd_pointer));
-		scb->SG_segment_count = 1;
-		memcpy(scb->SG_list_pointer, &req_buf,
-		       sizeof(scb->SG_list_pointer));
-                scb->data_count = scb->sg_list[0].length;
-		memcpy(scb->data_pointer, &(scb->sg_list[0].address),
-		       sizeof(scb->data_pointer));
+  		/* hscb->control &= DISCENB; */
+                hscb->control = 0;
+        	hscb->target_status = 0;
+        	hscb->SG_segment_count = 1;
+
+        	addr = VIRT_TO_BUS(&scb->sg_list[0]);
+        	memcpy(&hscb->SG_list_pointer, &addr,
+        	       sizeof(hscb->SG_list_pointer));
+
+        	memcpy(&hscb->data_pointer, &(scb->sg_list[0].address),
+        	       sizeof(hscb->data_pointer));
+        	/* Maintain SCB_LINKED_NEXT */
+        	hscb->data_count &= 0xFF000000;
+  		hscb->data_count |= scb->sg_list[0].length;
+
+        	addr = VIRT_TO_BUS(scb->sense_cmd);
+        	memcpy(&hscb->SCSI_cmd_pointer, &addr,
+        	       sizeof(hscb->SCSI_cmd_pointer));
+        	hscb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
 
-                aic7xxx_putscb(p, scb);
+                scb->sg_count = hscb->SG_segment_count;
+        	scb->flags |= SCB_SENSE;
                 /*
-                 * Ensure that the target is "BUSY" so we don't get overlapping
-                 * commands if we happen to be doing tagged I/O.
+                 * Ensure the target is busy since this will be an
+                 * an untagged request.
                  */
-		aic7xxx_busy_target(scsi_id, channel, base);
+                aic7xxx_busy_target(p, target, channel, hscb->tag);
+        	outb(SEND_SENSE, p->base + RETURN_1);
+              }  /* first time sense, no errors */
+  	      else
+  	      {
+        	if (aic7xxx_error(cmd) == 0)
+  		{
+        	  aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+  		}
+  	      }
+              break;
 
-                aic7xxx_add_waiting_scb(base, scb);
-		outb(SEND_SENSE, RETURN_1 + base);
-	      }  /* first time sense, no errors */
-              else
+            case QUEUE_FULL:
+#ifdef NOT_YET
+              if (scb->hscb->control & TAG_ENB)
+              {
+        	if (cmd->device->queue_depth > 2)
+        	{
+                  cmd->device->queue_depth--;  /* Not correct */
+                  printk(KERN_WARNING "(scsi%d:%d:%d) Tagged queue depth "
+                	 "reduced to %d\n", p->host_no,
+                	 TC_OF_SCB(scb), cmd->device->queue_depth);
+        	}
+        	/*
+        	 * XXX - Requeue this unconditionally?
+        	 */
+
+        	/*
+        	 * We'd like to be able to give the SCB some more time
+        	 * (untimeout, then timeout).
+        	 */
+        	break;
+              }
+#endif
+              printk(KERN_WARNING "(scsi%d:%d:%d) Queue full received; "
+                     "queue depth %d, active %d\n", p->host_no,
+                     TC_OF_SCB(scb), cmd->device->queue_depth,
+                     p->device_status[TARGET_INDEX(cmd)].active_cmds);
+
+              /* Else treat this as if it was a BUSY condition. */
+              scb->hscb->target_status = (BUSY << 1) |
+                  (scb->hscb->target_status & 0x01);
+              /* Fall through to the BUSY case. */
+
+            case BUSY:
+              printk(KERN_WARNING "(scsi%d:%d:%d) Target busy\n",
+                     p->host_no, TC_OF_SCB(scb));
+              if (!aic7xxx_error(cmd))
               {
-                cmd->flags &= ~ASKED_FOR_SENSE;
-	        if (aic7xxx_error(cmd) == 0)
-                {
-		  aic7xxx_error(cmd) = DID_RETRY_COMMAND;
-                }
+  		/*
+        	 * The mid-level SCSI code should be fixed to
+        	 * retry the command at a later time instead of
+        	 * trying right away.
+        	 */
+        	aic7xxx_error(cmd) = DID_BUS_BUSY | (SUGGEST_RETRY << 8);
               }
-	      break;
+              udelay(1000);  /*  A small pause (1ms) to help the drive */
+              break;
 
-	    case BUSY:
-	      printk(KERN_WARNING "scsi%d: Target busy, TCL=0x%x.\n",
-                     p->host_no, scb->target_channel_lun);
-	      if (!aic7xxx_error(cmd))
-	      {
-                /* The error code here used to be DID_BUS_BUSY,
-                 * but after extensive testing, it has been determined
-                 * that a DID_BUS_BUSY return is a waste of time.  If
-                 * the problem is something that will go away, then it
-                 * will, if it isn't, then you don't want the endless
-                 * looping that you get with a DID_BUS_BUSY.  Better
-                 * to be on the safe side and specify an error condition
-                 * that will eventually lead to a reset or abort of some
-                 * sort instead of an endless loop.
-                 */
-	        aic7xxx_error(cmd) = DID_RETRY_COMMAND;
-	      }
-	      break;
-
-	    case QUEUE_FULL:
-	      printk(KERN_WARNING "scsi%d: Queue full.\n", p->host_no);
-              scb->state |= SCB_ASSIGNEDQ;
-              scbq_insert_tail(&p->assigned_scbs, scb);
-	      break;
-
-	    default:
-	      printk(KERN_WARNING "scsi%d: Unexpected target status 0x%x.\n",
-		     p->host_no, scb->target_status);
-	      if (!aic7xxx_error(cmd))
-	      {
-		aic7xxx_error(cmd) = DID_RETRY_COMMAND;
-	      }
-	      break;
-	  }  /* end switch */
+            default:
+              printk(KERN_WARNING "(scsi%d:%d:%d) Unexpected target "
+                     "status 0x%x.\n", p->host_no,
+        	     TC_OF_SCB(scb), scb->hscb->target_status);
+              if (!aic7xxx_error(cmd))
+              {
+        	aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+              }
+              break;
+          }  /* end switch */
 	}  /* end else of */
-	break;
-
-      case RESIDUAL:
-	scb_index = inb(SCB_TAG + base);
-	scb = (p->scb_array[scb_index]);
-	if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
-	{
-	  printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
-		 "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no,
-		 intstat, scb_index, scb->state, (unsigned long) scb->cmd);
-	}
-	else
-	{
-	  cmd = scb->cmd;
-	  /*
-	   *  Don't destroy valid residual information with
-	   *  residual coming from a check sense operation.
-	   */
-	  if (!(cmd->flags & WAS_SENSE))
-	  {
-	    /*
-	     *  We had an underflow. At this time, there's only
-	     *  one other driver that bothers to check for this,
-	     *  and cmd->underflow seems to be set rather half-
-	     *  heartedly in the higher-level SCSI code.
-	     */
-	    actual = aic7xxx_length(cmd, scb->residual_SG_segment_count);
-
-	    actual -= (inb(SCB_RESID_DCNT2 + base) << 16) |
-		      (inb(SCB_RESID_DCNT1 + base) <<  8) |
-		      inb(SCB_RESID_DCNT0 + base);
-
-	    if (actual < cmd->underflow)
-	    {
-	      printk(KERN_WARNING "scsi%d: Target %d underflow - "
-		     "Wanted at least %u, got %u, residual SG count %d.\n",
-		     p->host_no, cmd->target, cmd->underflow, actual,
-                     inb(SCB_RESID_SGCNT + base));
-	      aic7xxx_error(cmd) = DID_RETRY_COMMAND;
-	      aic7xxx_status(cmd) = scb->target_status;
-	    }
-	  }
-	}
-	break;
+      }
+      break;
 
-      case ABORT_TAG:
-	scb_index = inb(SCB_TAG + base);
-	scb = (p->scb_array[scb_index]);
-	if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
-	{
-	  printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
-		 "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx\n", p->host_no,
-		 intstat, scb_index, scb->state, (unsigned long) scb->cmd);
-	}
-	else
-	{
-	  cmd = scb->cmd;
-	  /*
-	   * We didn't receive a valid tag back from the target
-	   * on a reconnect.
-	   */
-	  printk("scsi%d: Invalid tag received on target %d, channel %c, "
-                 "lun %d - Sending ABORT_TAG.\n", p->host_no,
-		  scsi_id, channel, cmd->lun & 0x07);
+    case AWAITING_MSG:
+      {
+	unsigned char scb_index;
+        unsigned char message_offset;
 
-	  cmd->result = (DID_RETRY_COMMAND << 16);
-          aic7xxx_done(p, scb);
-	}
-	break;
+	scb_index = inb(p->base + SCB_TAG);
+	scb = p->scb_data->scb_array[scb_index];
 
-      case AWAITING_MSG:
-	scb_index = inb(SCB_TAG + base);
-	scb = (p->scb_array[scb_index]);
-	if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+	/*
+	 * This SCB had a MK_MESSAGE set in its control byte informing
+	 * the sequencer that we wanted to send a special message to
+	 * this target.
+	 */
+        message_offset = inb(p->base + MSG_LEN);
+	if (scb->flags & SCB_DEVICE_RESET)
 	{
-	  printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
-		 "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no,
-		 intstat, scb_index, scb->state, (unsigned long) scb->cmd);
+          outb(MSG_BUS_DEV_RESET, p->base + MSG_OUT);
+          outb(1, p->base + MSG_LEN);
+          printk(KERN_INFO "(scsi%d:%d:%d) Bus device reset sent\n",
+        	 p->host_no, TC_OF_SCB(scb));
 	}
-	else
+        else if (scb->flags & SCB_ABORT)
+        {
+          if ((scb->hscb->control & TAG_ENB) != 0)
+          {
+            outb(MSG_ABORT_TAG, p->base + MSG_OUT + message_offset);
+          }
+          else
+          {
+            outb(MSG_ABORT, p->base + MSG_OUT + message_offset);
+          }
+          outb(message_offset + 1, p->base + MSG_LEN);
+          printk(KERN_WARNING "(scsi%d:%d:%d): Abort message sent.\n",
+                 p->host_no, TC_OF_SCB(scb));
+        }
+	else if (scb->flags & SCB_MSGOUT_WDTR)
 	{
-	  /*
-	   * This SCB had a zero length command, informing the sequencer
-	   * that we wanted to send a special message to this target.
-	   * We only do this for BUS_DEVICE_RESET messages currently.
-	   */
-	   if (scb->state & SCB_DEVICE_RESET)
-	   {
-#ifdef AIC7XXX_DEBUG_ABORT
-  printk ("aic7xxx: (isr) sending bus device reset to target %d\n",
-          scsi_id);
-#endif
-	     outb(MSG_BUS_DEVICE_RESET, MSG0 + base);
-	     outb(1, MSG_LEN + base);
-	   }
-	   else
-	   {
-	     panic("scsi%d: AWAITING_SCB for an SCB that does "
-		   "not have a waiting message.\n", p->host_no);
-	   }
-	}
-	break;
-
-      case IMMEDDONE:
-        scb_index = inb(SCB_TAG + base);
-	scb = (p->scb_array[scb_index]);
-#ifdef AIC7XXX_DEBUG_ABORT
-  printk("aic7xxx: received IMMEDDONE for target %d, scb %d, state %d\n",
-         scsi_id, scb_index, scb->state);
-#endif
-        if (scb->state & SCB_DEVICE_RESET)
+          aic7xxx_construct_wdtr(p, message_offset, BUS_16_BIT);
+        }
+        else if (scb->flags & SCB_MSGOUT_SDTR)
         {
-          int found;
+          unsigned char target_scratch;
+          unsigned short ultra_enable;
+          int i, sxfr;
 
           /*
-           * Go back to async/narrow transfers and renegotiate.
+           * Pull the user defined setting from scratch RAM.
            */
-          aic7xxx_unbusy_target(scsi_id, channel, base);
-          p->needsdtr |= (p->needsdtr_copy & target_mask);
-          p->needwdtr |= (p->needwdtr_copy & target_mask);
-          p->sdtr_pending &= ~target_mask;
-          p->wdtr_pending &= ~target_mask;
-          scratch = inb(TARG_SCRATCH + base + scratch_offset);
-          scratch &= SXFR;
-          outb(scratch, TARG_SCRATCH + base + scratch_offset);
-          found = aic7xxx_reset_device(p, (int) scsi_id, channel);
-          printk(KERN_INFO "scsi%d: Bus Device Reset delivered, %d SCBs "
-                 "aborted.\n", p->host_no, found);
-          /* Indicate that we want to call aic7xxx_done_aborted_scbs() */
-          run_aborted_queue = TRUE;
+          target_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+          sxfr = target_scratch & SXFR;
+          ultra_enable = inb(p->base + ULTRA_ENB) |
+              (inb(p->base + ULTRA_ENB + 1) << 8);
+          if (ultra_enable & target_mask)
+          {
+            sxfr |= 0x100;
+          }
+          for (i = 0; i < num_aic7xxx_syncrates; i++)
+          {
+            if (sxfr == aic7xxx_syncrates[i].rate)
+            break;
+          }
+          aic7xxx_construct_sdtr(p, message_offset,
+                                 aic7xxx_syncrates[i].period,
+                                 target_scratch & WIDEXFER ?
+                                 MAX_OFFSET_16BIT : MAX_OFFSET_8BIT);
         }
-        else
+        else 
         {
-          panic("scsi%d: Immediate complete for unknown operation.\n",
-                p->host_no);
-        }
-        break;
+          panic("aic7xxx: AWAITING_MSG for an SCB that does "
+                "not have a waiting message.");
+	}
+      }
+      break;
 
-      case DATA_OVERRUN:
+    case DATA_OVERRUN:
       {
-        unsigned int overrun;
-
-        scb = (p->scb_array[inb(base + SCB_TAG)]);
-        overrun = inb(base + STCNT0) | (inb(base + STCNT1) << 8) |
-                  (inb(base + STCNT2) << 16);
-        overrun =0x00FFFFFF - overrun;
-        printk(KERN_WARNING "scsi%d: data overrun of %d bytes detected; forcing "
-               "a retry.\n", p->host_no, overrun);
-        aic7xxx_error(scb->cmd) = DID_RETRY_COMMAND;
-        break;
+	unsigned char scb_index = inb(p->base + SCB_TAG);
+        unsigned char lastphase = inb(p->base + LASTPHASE);
+	unsigned int i, overrun;
+
+	scb = (p->scb_data->scb_array[scb_index]);
+	overrun = inb(p->base + STCNT) | (inb(p->base + STCNT + 1) << 8) |
+  		  (inb(p->base + STCNT + 2) << 16);
+	overrun = 0x00FFFFFF - overrun;
+	printk(KERN_WARNING "(scsi%d:%d:%d) Data overrun of %d bytes detected "
+               "in %s phase, tag %d; forcing a retry.\n",
+               p->host_no, TC_OF_SCB(scb), overrun,
+               lastphase == P_DATAIN ? "Data-In" : "Data-Out",
+               scb->hscb->tag);
+        printk(KERN_WARNING "%s seen Data Phase.  Length = %d, NumSGs = %d.\n",
+               inb(p->base + SEQ_FLAGS) & DPHASE ? "Have" : "Haven't",
+               aic7xxx_length(scb->cmd, 0), scb->sg_count);
+        for (i = 0; i < scb->sg_count; i++)
+        {
+          printk(KERN_INFO "     sg[%d] - Addr 0x%x : Length %d\n",
+                 i, scb->sg_list[i].address, scb->sg_list[i].length);
+        }
+	/*
+	 * XXX - What do we really want to do on an overrun?  The
+	 *       mid-level SCSI code should handle this, but for now,
+	 *       we'll just indicate that the command should retried.
+	 */
+	aic7xxx_error(scb->cmd) = DID_RETRY_COMMAND;
       }
+      break;
 
-#if AIC7XXX_NOT_YET
-      /* XXX Fill these in later */
-      case MESG_BUFFER_BUSY:
-        break;
-      case MSGIN_PHASEMIS:
-        break;
-#endif
+/* #if AIC7XXX_NOT_YET */
+    /* XXX Fill these in later */
+    case MSG_BUFFER_BUSY:
+      printk("aic7xxx: Message buffer busy.\n");
+      break;
+    case MSGIN_PHASEMIS:
+      printk("aic7xxx: Message-in phasemis.\n");
+      break;
+/*#endif */
+
+    case ABORT_CMDCMPLT:
+      /* This interrupt serves to pause the sequencer until we can clean
+       * up the QOUTFIFO allowing us to handle any abort SCBs that may
+       * completed yet still have an SCB in the QINFIFO or waiting for
+       * selection queue.  By the time we get here, we should have
+       * already cleaned up the queues, so all we need to do is unpause
+       * the sequencer.
+       */
+      break;
+
+    default:		   /* unknown */
+      printk(KERN_WARNING "scsi%d: SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
+             p->host_no, intstat, inb(p->base + SCSISIGI));
+      break;
+  }
+
+  /*
+   * Clear the sequencer interrupt and unpause the sequencer.
+   */
+  outb(CLRSEQINT, p->base + CLRINT);
+  unpause_sequencer(p, /* unpause always */ TRUE);
+}
 
-      default:               /* unknown */
-	printk(KERN_WARNING "scsi%d: SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
-	       p->host_no, intstat, inb(SCSISIGI + base));
-	break;
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_handle_scsiint
+ *
+ * Description:
+ *   Interrupt handler for SCSI interrupts (SCSIINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat)
+{
+  unsigned char scb_index;
+  unsigned char status;
+  struct aic7xxx_scb *scb;
+
+  scb_index = inb(p->base + SCB_TAG);
+  status = inb(p->base + SSTAT1);
+
+  if (scb_index < p->scb_data->numscbs)
+  {
+    scb = p->scb_data->scb_array[scb_index];
+    if ((scb->flags & SCB_ACTIVE) == 0)
+    {
+      scb = NULL;
     }
+  }
+  else
+  {
+    scb = NULL;
+  }
 
+  if ((status & SCSIRSTI) != 0)
+  {
+    char channel;
+
+    channel = (inb(p->base + SBLKCTL) & SELBUSB) ? 'B' : 'A';
+
+    printk(KERN_WARNING "scsi%d: SCSIINT - Someone reset channel %c.\n",
+           p->host_no, channel);
     /*
-     * Clear the sequencer interrupt and unpause the sequencer.
+     * Go through and abort all commands for the channel, but do not
+     * reset the channel again.
      */
-    outb(CLRSEQINT, CLRINT + base);
-    UNPAUSE_SEQUENCER(p);
+    aic7xxx_reset_channel(p, channel, /* Initiate Reset */ FALSE);
+    scb = NULL;
   }
-
-  if (intstat & SCSIINT)
+  else if ( ((status & BUSFREE) != 0) && ((status & SELTO) == 0) )
   {
-    int status = inb(SSTAT1 + base);
-    scsi_id = (inb(SCSIID + base) >> 4) & 0x0F;
-    channel = 'A';
-    if (inb(SBLKCTL + base) & SELBUSB)
-    {
-      channel = 'B';
+    /*
+     * First look at what phase we were last in.  If it's message-out,
+     * chances are pretty good that the bus free was in response to
+     * one of our abort requests.
+     */
+    unsigned char lastphase = inb(p->base + LASTPHASE);
+    unsigned char target = (inb(p->base + SAVED_TCL) >> 4) & 0x0F;
+    char channel = (inb(p->base + SBLKCTL) & SELBUSB) ? 'B' : 'A';
+    int printerror = TRUE;
+
+    outb(0, p->base + SCSISEQ);
+    if (lastphase == P_MESGOUT)
+    {
+      unsigned char sindex;
+      unsigned char message;
+
+      sindex = inb(p->base + SINDEX);
+      message = inb(p->base + sindex - 1);
+
+      if (message == MSG_ABORT)
+      {
+        printk(KERN_WARNING "(scsi%d:%d:%d) SCB %d abort completed.\n",
+                   p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
+        aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), SCB_LIST_NULL);
+        aic7xxx_run_done_queue(p, /* complete */ TRUE);
+        scb = NULL;
+        printerror = 0;
+      }
+      else if (message == MSG_ABORT_TAG)
+      {
+        printk(KERN_WARNING "(scsi%d:%d:%d) SCB %d abort Tag completed.\n",
+                   p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
+        aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), scb->hscb->tag);
+        aic7xxx_run_done_queue(p, /* complete */ TRUE);
+        scb = NULL;
+        printerror = 0;
+      }
+      else if (message == MSG_BUS_DEV_RESET)
+      {
+        aic7xxx_handle_device_reset(p, target, channel);
+        scb = NULL;
+        printerror = 0;
+      }
     }
+    if (printerror != 0)
+    {
+      if (scb != NULL)
+      {
+        unsigned char tag;
 
-    scb_index = inb(SCB_TAG + base);
-    scb = (p->scb_array[scb_index]);
-    if (status & SCSIRSTI)
+        if ((scb->hscb->control & TAG_ENB) != 0)
+        {
+          tag = scb->hscb->tag;
+        }
+        else
+        {
+          tag = SCB_LIST_NULL;
+        }
+        aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), tag);
+      }
+      else
+      {
+        aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
+      }
+      printk(KERN_WARNING "scsi%d: Unexpected busfree, LASTPHASE = 0x%x, "
+             "SEQADDR = 0x%x\n", p->host_no, lastphase,
+             (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+    }
+    outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
+    outb(CLRBUSFREE, p->base + CLRSINT1);
+    outb(CLRSCSIINT, p->base + CLRINT);
+    restart_sequencer(p);
+  }
+  else if ((status & SELTO) != 0)
+  {
+    unsigned char scbptr;
+    unsigned char nextscb;
+    Scsi_Cmnd *cmd;
+
+    scbptr = inb(p->base + WAITING_SCBH);
+    outb(scbptr, p->base + SCBPTR);
+    scb_index = inb(p->base + SCB_TAG);
+
+    scb = NULL;
+    if (scb_index < p->scb_data->numscbs)
     {
-      PAUSE_SEQUENCER(p);
-      printk(KERN_WARNING "scsi%d: SCSIINT - Someone reset channel %c.\n",
-             p->host_no, channel);
-      /*
-       * Go through and abort all commands for the channel, but do not
-       * reset the channel again.
-       */
-      aic7xxx_reset_channel(p, channel, FALSE);
-      run_aborted_queue = TRUE;
+      scb = p->scb_data->scb_array[scb_index];
+      if ((scb->flags & SCB_ACTIVE) == 0)
+      {
+        scb = NULL;
+      }
+    }
+    if (scb == NULL)
+    {
+      printk(KERN_WARNING "scsi%d: Referenced SCB %d not valid during SELTO.\n",
+             p->host_no, scb_index);
+      printk(KERN_WARNING "        SCSISEQ = 0x%x SEQADDR = 0x%x SSTAT0 = 0x%x "
+             "SSTAT1 = 0x%x\n", inb(p->base + SCSISEQ),
+             inb(p->base + SEQADDR0) | (inb(p->base + SEQADDR1) << 8),
+             inb(p->base + SSTAT0), inb(p->base + SSTAT1));
     }
-    else if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+    else
     {
-      printk(KERN_WARNING "scsi%d: SCSIINT - No command for SCB.\n", p->host_no);
       /*
-       * Turn off the interrupt and set status to zero, so that it
-       * falls through the rest of the SCSIINT code.
+       * XXX - If we queued an abort tag, go clean up the disconnected list.
+       */
+      cmd = scb->cmd;
+      cmd->result = (DID_TIME_OUT << 16);
+
+      /*
+       * Clear an pending messages for the timed out
+       * target and mark the target as free.
+       */
+      outb(0, p->base + MSG_LEN);
+      aic7xxx_index_busy_target(p, cmd->target,
+          cmd->channel ? 'B': 'A', /*unbusy*/ TRUE);
+      outb(0, p->base + SCB_CONTROL);
+
+      /*
+       * Shift the waiting for selection queue forward
+       */
+      nextscb = inb(p->base + SCB_NEXT);
+      outb(nextscb, p->base + WAITING_SCBH);
+
+      /*
+       * Put this SCB back on the free list.
        */
-      outb(status, CLRSINT1 + base);
-      UNPAUSE_SEQUENCER(p);
-      outb(CLRSCSIINT, CLRINT + base);
+      aic7xxx_add_curscb_to_free_list(p);
+    }
+    /*
+     * Stop the selection.
+     */
+    outb(0, p->base + SCSISEQ);
+    outb(CLRSELTIMEO | CLRBUSFREE, p->base + CLRSINT1);
+    outb(CLRSCSIINT, p->base + CLRINT);
+    restart_sequencer(p);
+  }
+  else if (scb == NULL)
+  {
+    printk(KERN_WARNING "scsi%d: aic7xxx_isr - referenced scb not valid "
+           "during scsiint 0x%x scb(%d)\n"
+           "      SIMODE0 0x%x, SIMODE1 0x%x, SSTAT0 0x%x, SEQADDR 0x%x\n",
+           p->host_no, status, scb_index, inb(p->base + SIMODE0),
+           inb(p->base + SIMODE1), inb(p->base + SSTAT0),
+           (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+    /*
+     * Turn off the interrupt and set status to zero, so that it
+     * falls through the rest of the SCSIINT code.
+     */
+    outb(status, p->base + CLRSINT1);
+    outb(CLRSCSIINT, p->base + CLRINT);
+    unpause_sequencer(p, /* unpause always */ TRUE);
+    scb = NULL;
+  }
+  else if (status & SCSIPERR)
+  {
+    /*
+     * Determine the bus phase and queue an appropriate message.
+     */
+    char  *phase;
+    Scsi_Cmnd *cmd;
+    unsigned char mesg_out = MSG_NOOP;
+    unsigned char lastphase = inb(p->base + LASTPHASE);
+
+    cmd = scb->cmd;
+    switch (lastphase)
+    {
+      case P_DATAOUT:
+        phase = "Data-Out";
+        break;
+      case P_DATAIN:
+        phase = "Data-In";
+        mesg_out = MSG_INITIATOR_DET_ERR;
+        break;
+      case P_COMMAND:
+        phase = "Command";
+        break;
+      case P_MESGOUT:
+        phase = "Message-Out";
+        break;
+      case P_STATUS:
+        phase = "Status";
+        mesg_out = MSG_INITIATOR_DET_ERR;
+        break;
+      case P_MESGIN:
+        phase = "Message-In";
+        mesg_out = MSG_PARITY_ERROR;
+        break;
+      default:
+        phase = "unknown";
+        break;
+    }
+
+    /*
+     * A parity error has occurred during a data
+     * transfer phase. Flag it and continue.
+     */
+    printk(KERN_WARNING "(scsi%d:%d:%d) Parity error during phase %s.\n",
+           p->host_no, TC_OF_SCB(scb), phase);
+
+    /*
+     * We've set the hardware to assert ATN if we get a parity
+     * error on "in" phases, so all we need to do is stuff the
+     * message buffer with the appropriate message.  "In" phases
+     * have set mesg_out to something other than MSG_NOP.
+     */
+    if (mesg_out != MSG_NOOP)
+    {
+      outb(mesg_out, p->base + MSG_OUT);
+      outb(1, p->base + MSG_LEN);
       scb = NULL;
     }
-    else if (status & SCSIPERR)
+    else
     {
-      char  *phase;
-      unsigned char mesg_out = MSG_NOP;
-      unsigned char lastphase = inb(LASTPHASE + base);
+      /*
+       * Should we allow the target to make this decision for us?
+       */
+      cmd->result = DID_RETRY_COMMAND << 16;
+    }
+    outb(CLRSCSIPERR, p->base + CLRSINT1);
+    outb(CLRSCSIINT, p->base + CLRINT);
+    unpause_sequencer(p, /* unpause_always */ TRUE);
+  }
+  else
+  {
+    /*
+     * We don't know what's going on. Turn off the
+     * interrupt source and try to continue.
+     */
+    printk(KERN_WARNING "aic7xxx: SSTAT1(0x%x).\n", status);
+    outb(status, p->base + CLRSINT1);
+    outb(CLRSCSIINT, p->base + CLRINT);
+    unpause_sequencer(p, /* unpause always */ TRUE);
+    scb = NULL;
+  }
+  if (scb != NULL)
+  {
+    aic7xxx_done(p, scb);
+    aic7xxx_done_cmds_complete(p);
+  }
+}
 
-      cmd = scb->cmd;
-      switch (lastphase)
-      {
-        case P_DATAOUT:
-          phase = "Data-Out";
-          break;
-        case P_DATAIN:
-          phase = "Data-In";
-          mesg_out = MSG_INITIATOR_DET_ERROR;
-          break;
-        case P_COMMAND:
-          phase = "Command";
-          break;
-        case P_MESGOUT:
-          phase = "Message-Out";
-          break;
-        case P_STATUS:
-          phase = "Status";
-          mesg_out = MSG_INITIATOR_DET_ERROR;
-          break;
-        case P_MESGIN:
-          phase = "Message-In";
-          mesg_out = MSG_MSG_PARITY_ERROR;
-          break;
-        default:
-          phase = "unknown";
-          break;
-      }
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_isr
+ *
+ * Description:
+ *   SCSI controller interrupt handler.
+ *
+ *   NOTE: Since we declared this using SA_INTERRUPT, interrupts should
+ *         be disabled all through this function unless we say otherwise.
+ *-F*************************************************************************/
+static void
+aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+  struct aic7xxx_host *p;
+  unsigned char intstat;
+  unsigned long flags;
 
-      /*
-       * A parity error has occurred during a data
-       * transfer phase. Flag it and continue.
-       */
-      printk(KERN_WARNING "scsi%d: Parity error during phase %s on target %d, "
-             "channel %d, lun %d.\n", p->host_no, phase,
-             cmd->target, cmd->channel & 0x01, cmd->lun & 0x07);
+  p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata;
 
-      /*
-       * We've set the hardware to assert ATN if we get a parity
-       * error on "in" phases, so all we need to do is stuff the
-       * message buffer with the appropriate message. In phases
-       * have set mesg_out to something other than MSG_NOP.
-       */
-      if (mesg_out != MSG_NOP)
-      {
-        outb(mesg_out, MSG0 + base);
-        outb(1, MSG_LEN + base);
-        cmd->result = DID_PARITY << 16;
-      }
-      else
-      {
-        /*
-         * Should we allow the target to make this decision for us?
-         */
-        cmd->result = DID_RETRY_COMMAND << 16;
-      }
-      aic7xxx_done(p, scb);
+  /*
+   * Search for the host with a pending interrupt.  If we can't find
+   * one, then we've encountered a spurious interrupt.
+   */
+  while ((p != NULL) && !(inb(p->base + INTSTAT) & INT_PEND))
+  {
+    if (p->next == NULL)
+    {
+      p = NULL;
     }
-    else if (status & SELTO)
+    else
     {
-      unsigned char waiting;
+      p = (struct aic7xxx_host *) p->next->hostdata;
+    }
+  }
 
-      cmd = scb->cmd;
+  if (p == NULL)
+    return;
 
-      cmd->result = (DID_TIME_OUT << 16);
-      /*
-       * Clear an pending messages for the timed out
-       * target and mark the target as free.
-       */
-      ha_flags = inb(FLAGS + base);
-      outb(0, MSG_LEN + base);
-      aic7xxx_unbusy_target(scsi_id, channel, base);
-      /*
-       * Stop the selection.
-       */
-      outb(0, SCSISEQ + base);
-      outb(0, SCB_CONTROL + base);
-      outb(CLRSELTIMEO, CLRSINT1 + base);
-      outb(CLRSCSIINT, CLRINT + base);
+  /*
+   * Handle all the interrupt sources - especially for SCSI
+   * interrupts, we won't get a second chance at them.
+   */
+  intstat = inb(p->base + INTSTAT);
 
-      /*
-       * Shift the waiting for selection queue forward
-       */
-      waiting = inb(WAITING_SCBH + base);
-      outb(waiting, SCBPTR + base);
-      waiting = inb(SCB_NEXT + base);
-      outb(waiting, WAITING_SCBH + base);
+  /*
+   * Keep track of interrupts for /proc/scsi
+   */
+  p->isr_count++;
 
-      RESTART_SEQUENCER(p);
-      aic7xxx_done(p, scb);
-    }
-    else if (!(status & BUSFREE))
+  if (!(p->flags & A_SCANNED) && (p->isr_count == 1))
+  {
+    /*
+     * We must only have one card at this IRQ and it must have been
+     * added to the board data before the spurious interrupt occurred.
+     * It is sufficient that we check isr_count and not the spurious
+     * interrupt count.
+     */
+    printk("scsi%d: Encountered spurious interrupt.\n", p->host_no);
+    if (intstat)
     {
-      /*
-       * We don't know what's going on. Turn off the
-       * interrupt source and try to continue.
-       */
-      printk(KERN_WARNING "aic7xxx: SSTAT1(0x%x).\n", status);
-      outb(status, CLRSINT1 + base);
-      UNPAUSE_SEQUENCER(p);
-      outb(CLRSCSIINT, CLRINT + base);
+      /* Try clearing all interrupts. */
+      outb(CLRBRKADRINT | CLRSCSIINT | CLRCMDINT | CLRSEQINT, p->base + CLRINT);
     }
+    return;
+  }
+
+  if (p->flags & IN_ISR)
+  {
+    printk(KERN_WARNING "scsi%d: Warning!! Interrupt routine called reentrantly!\n",
+           p->host_no);
+    return;
   }
 
-  if (run_aborted_queue)
-    aic7xxx_done_aborted_scbs(p);
+  /*
+   * Indicate that we're in the interrupt handler.
+   */
+  save_flags(flags);
+  cli();
+  p->flags |= IN_ISR;
 
   if (intstat & CMDCMPLT)
   {
-    int complete;
+    struct aic7xxx_scb *scb = NULL;
+    Scsi_Cmnd *cmd;
+    unsigned char qoutcnt;
+    unsigned char scb_index;
+    int i, interrupts_cleared = 0;
 
     /*
      * The sequencer will continue running when it
      * issues this interrupt. There may be >1 commands
      * finished, so loop until we've processed them all.
      */
-    do {
-      complete = inb(QOUTFIFO + base);
+    qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask;
 
-      scb = (p->scb_array[complete]);
-      if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+#if 1
+  if (qoutcnt >= p->qfullcount - 1)
+    printk(KERN_WARNING "aic7xxx: Command complete near Qfull count, "
+           "qoutcnt = %d.\n", qoutcnt);
+#endif
+    while (qoutcnt > 0)
+    {
+      if ((p->flags & PAGE_ENABLED) != 0)
       {
-	printk(KERN_WARNING "scsi%d: CMDCMPLT without command for SCB %d.\n"
-	       "       QOUTCNT %d, QINCNT %d, SCB state 0x%x, cmd 0x%lx, "
-               "pos(%d).\n", p->host_no, complete, inb(QOUTCNT + base),
-               inb(QINCNT + base), scb->state, (unsigned long) scb->cmd,
-               scb->position);
-	outb(CLRCMDINT, CLRINT + base);
-	continue;
+        p->cmdoutcnt += qoutcnt;
+        if (p->cmdoutcnt >= p->qfullcount)
+        {
+          /*
+           * Since paging only occurs on aic78x0 chips, we can use
+           * Auto Access Pause to clear the command count.
+           */
+          outb(0, p->base + CMDOUTCNT);
+          p->cmdoutcnt = 0;
+        }
       }
-      cmd = scb->cmd;
-      cmd->result |= (aic7xxx_error(cmd) << 16);
-      if ((cmd->flags & WAS_SENSE) && !(cmd->flags & ASKED_FOR_SENSE))
+      for (i = 0; i < qoutcnt; i++)
       {
-        /*
-         * Got sense information.
-         */
-	cmd->flags &= ASKED_FOR_SENSE;
+        scb_index = inb(p->base + QOUTFIFO);
+        scb = p->scb_data->scb_array[scb_index];
+        if (scb == NULL)
+        {
+	  printk(KERN_WARNING "scsi%d: CMDCMPLT with invalid SCB index %d, "
+	         "QOUTCNT %d, QINCNT %d\n", p->host_no, scb_index,
+                 inb(p->base + QOUTCNT), inb(p->base + QINCNT));
+          continue;
+        }
+        else if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
+        {
+	  printk(KERN_WARNING "scsi%d: CMDCMPLT without command for SCB %d, "
+	         "QOUTCNT %d, QINCNT %d, SCB flags 0x%x, cmd 0x%lx\n",
+                 p->host_no, scb_index, inb(p->base + QOUTCNT),
+                 inb(p->base + QINCNT), scb->flags, (unsigned long) scb->cmd);
+	  continue;
+        }
+        cmd = scb->cmd;
+        if (scb->hscb->residual_SG_segment_count != 0)
+        {
+          aic7xxx_calculate_residual(p, scb);
+        }
+        if ((scb->flags & SCB_QUEUED_ABORT) != 0)
+        {
+          /*
+           * Have to clean up any possible entries in the
+           * waiting queue and the QINFIFO.
+           */
+          int target;
+          char channel;
+          int lun;
+          unsigned char tag;
+
+          tag = SCB_LIST_NULL;
+          target = cmd->target;
+          lun = cmd->lun;
+          channel = (scb->hscb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+          if (scb->hscb->control & TAG_ENB)
+          {
+            tag = scb->hscb->tag;
+          }
+          aic7xxx_reset_device(p, target, channel, lun, tag);
+          /*
+           * Run the done queue, but don't complete the commands; we
+           * do this once at the end of the loop.
+           */
+          aic7xxx_run_done_queue(p, /*complete*/ FALSE);
+        }
+        cmd->result |= (aic7xxx_error(cmd) << 16);
+        p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS;
+        aic7xxx_done(p, scb);
       }
-      p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS;
-
       /*
        * Clear interrupt status before checking the output queue again.
        * This eliminates a race condition whereby a command could
@@ -3242,56 +3955,152 @@
        * so notification of the command being complete never made it
        * back up to the kernel.
        */
-      outb(CLRCMDINT, CLRINT + base);
-      aic7xxx_done(p, scb);
+      outb(CLRCMDINT, p->base + CLRINT);
+      interrupts_cleared++;
+      qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask;
+    }
 
-#ifdef AIC7XXX_PROC_STATS
-      /*
-       * XXX: we should actually know how much actually transferred
-       * XXX: for each command, but apparently that's too difficult.
-       */
-      actual = aic7xxx_length(cmd, 0);
-      if (!(cmd->flags & WAS_SENSE) && (actual > 0))
+    if (interrupts_cleared == 0)
+    {
+      outb(CLRCMDINT, p->base + CLRINT);
+    }
+
+    aic7xxx_done_cmds_complete(p);
+  }
+
+  if (intstat & BRKADRINT)
+  {
+    int i;
+    unsigned char errno = inb(p->base + ERROR);
+
+    printk(KERN_ERR "scsi%d: BRKADRINT error(0x%x):\n", p->host_no, errno);
+    for (i = 0; i < NUMBER(hard_error); i++)
+    {
+      if (errno & hard_error[i].errno)
       {
-        struct aic7xxx_xferstats *sp;
-        long *ptr;
-        int x;
+        printk(KERN_ERR "  %s\n", hard_error[i].errmesg);
+      }
+    }
+    printk("scsi%d: BRKADRINT, error 0x%x, seqaddr 0x%x.\n", p->host_no,
+           inb(p->base + ERROR),
+           (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+    aic7xxx_reset_device(p, ALL_TARGETS, ALL_CHANNELS, ALL_LUNS, SCB_LIST_NULL);
+    aic7xxx_run_done_queue(p, /*complete*/ TRUE);
+  }
+
+  if (intstat & SEQINT)
+  {
+    aic7xxx_handle_seqint(p, intstat);
+  }
+
+  if (intstat & SCSIINT)
+  {
+    aic7xxx_handle_scsiint(p, intstat);
+  }
+
+  if (p->waiting_scbs.head != NULL)
+  {
+    aic7xxx_run_waiting_queues(p);
+  }
+
+  p->flags &= ~IN_ISR;
+  restore_flags(flags);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_device_queue_depth
+ *
+ * Description:
+ *   Determines the queue depth for a given device.  There are two ways
+ *   a queue depth can be obtained for a tagged queueing device.  One
+ *   way is the default queue depth which is determined by whether
+ *   AIC7XXX_CMDS_PER_LUN is defined.  If it is defined, then it is used
+ *   as the default queue depth.  Otherwise, we use either 4 or 8 as the
+ *   default queue depth (dependent on the number of hardware SCBs).
+ *   The other way we determine queue depth is through the use of the
+ *   aic7xxx_tag_info array which is enabled by defining
+ *   AIC7XXX_TAGGED_QUEUEING_BY_DEVICE.  This array can be initialized
+ *   with queue depths for individual devices.  It also allows tagged
+ *   queueing to be [en|dis]abled for a specific adapter.
+ *-F*************************************************************************/
+static void
+aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device)
+{
+  int default_depth = 2;
+
+  device->queue_depth = default_depth;
+#ifdef AIC7XXX_TAGGED_QUEUEING
+  if (device->tagged_supported)
+  {
+    unsigned short target_mask;
+    int tag_enabled = TRUE;
+
+    target_mask = (1 << (device->id | (device->channel << 3)));
 
-        sp = &p->stats[cmd->channel & 0x01][cmd->target & 0x0F][cmd->lun & 0x07];
-        sp->xfers++;
+#ifdef AIC7XXX_CMDS_PER_LUN
+    default_depth = AIC7XXX_CMDS_PER_LUN;
+#else
+    if (p->scb_data->maxhscbs <= 4)
+    {
+      default_depth = 4;  /* Not many SCBs to work with. */
+    }
+    else
+    {
+      default_depth = 8;
+    }
+#endif
+ 
+    if (!(p->discenable & target_mask))
+    {
+      printk(KERN_INFO "(scsi%d:%d:%d) Disconnection disabled, unable to "
+             "enable tagged queueing.\n",
+             p->host_no, device->id, device->channel);
+    }
+    else
+    {
+#ifndef AIC7XXX_TAGGED_QUEUEING_BY_DEVICE
+      device->queue_depth = default_depth;
+#else
+      if (p->instance > NUMBER(aic7xxx_tag_info))
+      {
+        device->queue_depth = default_depth;
+      }
+      else
+      {
+        unsigned char  tindex;
 
-        if (cmd->request.cmd == WRITE)
+        tindex = device->id | (device->channel << 3);
+        if (aic7xxx_tag_info[p->instance].tag_commands[tindex] < 0)
         {
-          sp->w_total++;
-          sp->w_total512 += (actual >> 9);
-          ptr = sp->w_bins;
+          tag_enabled = FALSE;
+          device->queue_depth = 2;  /* Tagged queueing is disabled. */
         }
-        else
+        else if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 0)
         {
-          sp->r_total++;
-          sp->r_total512 += (actual >> 9);
-          ptr = sp->r_bins;
+          device->queue_depth = default_depth;
         }
-        for (x = 9; x <= 17; x++)
+        else
         {
-          if (actual < (1 << x))
-          {
-            ptr[x - 9]++;
-            break;
-          }
+          device->queue_depth =
+            aic7xxx_tag_info[p->instance].tag_commands[tindex];
         }
-        if (x > 17)
+      }
+#endif
+      if ((device->tagged_queue == 0) && tag_enabled)
+      {
+        if (aic7xxx_verbose)
         {
-          ptr[x - 9]++;
+    	  printk(KERN_INFO "(scsi%d:%d:%d) Enabled tagged queuing, "
+    	         "queue depth %d.\n", p->host_no,
+    	         device->id, device->channel, device->queue_depth);
         }
+        device->tagged_queue = 1;
+        device->current_tag = SCB_LIST_NULL;
       }
-#endif /* AIC7XXX_PROC_STATS */
-
-    } while (inb(QOUTCNT + base) & p->qcntmask);
+    }
   }
-  aic7xxx_done_cmds_complete(p);
-  p->flags &= ~IN_ISR;
-  aic7xxx_run_waiting_queues(p);
+#endif
 }
 
 /*+F*************************************************************************
@@ -3306,59 +4115,18 @@
  *   algorithm for determining the queue depth based on the maximum
  *   SCBs for the controller.
  *-F*************************************************************************/
-static void aic7xxx_select_queue_depth(struct Scsi_Host *host,
+static void
+aic7xxx_select_queue_depth(struct Scsi_Host *host,
     Scsi_Device *scsi_devs)
 {
-  Scsi_Device *device = scsi_devs;
-  int tq_depth = 2;
+  Scsi_Device *device;
   struct aic7xxx_host *p = (struct aic7xxx_host *) host->hostdata;
 
-#ifdef AIC7XXX_CMDS_PER_LUN
-  tq_depth = AIC7XXX_CMDS_PER_LUN;
-#else
-  {
-    if (p->maxhscbs <= 4)
-    {
-      tq_depth = 4;  /* Not many SCBs to work with. */
-    }
-    else
-    {
-      tq_depth = 8;
-    }
-  }
-#endif
-
   for (device = scsi_devs; device != NULL; device = device->next)
   {
     if (device->host == host)
     {
-      device->queue_depth = 2;
-#ifdef AIC7XXX_TAGGED_QUEUEING
-      if (device->tagged_supported)
-      {
-        unsigned short target_mask = (1 << device->id) | device->channel;
-
-        if (!(p->discenable & target_mask))
-        {
-          printk(KERN_INFO "scsi%d: Disconnection disabled, unable to enable "
-                 "tagged queueing for target %d, channel %d, LUN %d.\n",
-                 host->host_no, device->id, device->channel, device->lun);
-        }
-        else
-        {
-          device->queue_depth = tq_depth;
-          if (device->tagged_queue == 0)
-          {
-            printk(KERN_INFO "scsi%d: Enabled tagged queuing for target %d, "
-	           "channel %d, LUN %d, queue depth %d.\n", host->host_no,
-                   device->id, device->channel, device->lun,
-                   device->queue_depth);
-            device->tagged_queue = 1;
-            device->current_tag = SCB_LIST_NULL;
-          }
-        }
-      }
-#endif
+      aic7xxx_device_queue_depth(p, device);
     }
   }
 }
@@ -3385,7 +4153,7 @@
  *   The fourth byte's lowest bit seems to be an enabled/disabled
  *   flag (rest of the bits are reserved?).
  *-F*************************************************************************/
-static aha_type
+static aha_chip_type
 aic7xxx_probe(int slot, int base, aha_status_type *bios)
 {
   int i;
@@ -3394,7 +4162,7 @@
   static struct {
     int n;
     unsigned char signature[sizeof(buf)];
-    aha_type type;
+    aha_chip_type type;
     int bios_disabled;
   } AIC7xxx[] = {
     { 4, { 0x04, 0x90, 0x77, 0x71 }, AIC_7771, FALSE }, /* host adapter 274x */
@@ -3433,7 +4201,8 @@
 	return (AIC7xxx[i].type);
       }
 
-      printk("aic7xxx: Disabled at slot %d, ignored.\n", slot);
+      printk("aic7xxx: <Adaptec 7770 SCSI Host Adapter> "
+             "disabled at slot %d, ignored.\n", slot);
     }
   }
 
@@ -3460,10 +4229,9 @@
  *   useful in that it gives us an 800 nsec timer.  After a read from the
  *   SEECTL_2840 register the timing flag is cleared and goes high 800 nsec
  *   later.
- *
  *-F*************************************************************************/
 static int
-read_2840_seeprom(int base, struct seeprom_config *sc)
+read_284x_seeprom(struct aic7xxx_host *p, struct seeprom_config *sc)
 {
   int i = 0, k = 0;
   unsigned char temp;
@@ -3476,11 +4244,11 @@
   struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
 
 #define CLOCK_PULSE(p) \
-  while ((inb(STATUS_2840 + base) & EEPROM_TF) == 0)	\
+  while ((inb(p->base + STATUS_2840) & EEPROM_TF) == 0)	\
   {						\
     ;  /* Do nothing */				\
   }						\
-  (void) inb(SEECTL_2840 + base);
+  (void) inb(p->base + SEECTL_2840);
 
   /*
    * Read the first 32 registers of the seeprom.  For the 2840,
@@ -3493,8 +4261,8 @@
     /*
      * Send chip select for one clock cycle.
      */
-    outb(CK_2840 | CS_2840, SEECTL_2840 + base);
-    CLOCK_PULSE(base);
+    outb(CK_2840 | CS_2840, p->base + SEECTL_2840);
+    CLOCK_PULSE(p);
 
     /*
      * Now we're ready to send the read command followed by the
@@ -3503,11 +4271,11 @@
     for (i = 0; i < seeprom_read.len; i++)
     {
       temp = CS_2840 | seeprom_read.bits[i];
-      outb(temp, SEECTL_2840 + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL_2840);
+      CLOCK_PULSE(p);
       temp = temp ^ CK_2840;
-      outb(temp, SEECTL_2840 + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL_2840);
+      CLOCK_PULSE(p);
     }
     /*
      * Send the 6 bit address (MSB first, LSB last).
@@ -3517,11 +4285,11 @@
       temp = k;
       temp = (temp >> i) & 1;  /* Mask out all but lower bit. */
       temp = CS_2840 | temp;
-      outb(temp, SEECTL_2840 + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL_2840);
+      CLOCK_PULSE(p);
       temp = temp ^ CK_2840;
-      outb(temp, SEECTL_2840 + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL_2840);
+      CLOCK_PULSE(p);
     }
 
     /*
@@ -3533,12 +4301,12 @@
     for (i = 0; i <= 16; i++)
     {
       temp = CS_2840;
-      outb(temp, SEECTL_2840 + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL_2840);
+      CLOCK_PULSE(p);
       temp = temp ^ CK_2840;
-      seeprom[k] = (seeprom[k] << 1) | (inb(STATUS_2840 + base) & DI_2840);
-      outb(temp, SEECTL_2840 + base);
-      CLOCK_PULSE(base);
+      seeprom[k] = (seeprom[k] << 1) | (inb(p->base + STATUS_2840) & DI_2840);
+      outb(temp, p->base + SEECTL_2840);
+      CLOCK_PULSE(p);
     }
     /*
      * The serial EEPROM has a checksum in the last word.  Keep a
@@ -3554,12 +4322,12 @@
     /*
      * Reset the chip select for the next command cycle.
      */
-    outb(0, SEECTL_2840 + base);
-    CLOCK_PULSE(base);
-    outb(CK_2840, SEECTL_2840 + base);
-    CLOCK_PULSE(base);
-    outb(0, SEECTL_2840 + base);
-    CLOCK_PULSE(base);
+    outb(0, p->base + SEECTL_2840);
+    CLOCK_PULSE(p);
+    outb(CK_2840, p->base + SEECTL_2840);
+    CLOCK_PULSE(p);
+    outb(0, p->base + SEECTL_2840);
+    CLOCK_PULSE(p);
   }
 
 #if 0
@@ -3588,6 +4356,53 @@
 
 /*+F*************************************************************************
  * Function:
+ *   acquire_seeprom
+ *
+ * Description:
+ *   Acquires access to the memory port on PCI controllers.
+ *-F*************************************************************************/
+static inline int
+acquire_seeprom(struct aic7xxx_host *p)
+{
+  int wait;
+
+  /*
+   * Request access of the memory port.  When access is
+   * granted, SEERDY will go high.  We use a 1 second
+   * timeout which should be near 1 second more than
+   * is needed.  Reason: after the 7870 chip reset, there
+   * should be no contention.
+   */
+  outb(SEEMS, p->base + SEECTL);
+  wait = 1000;  /* 1000 msec = 1 second */
+  while ((wait > 0) && ((inb(p->base + SEECTL) & SEERDY) == 0))
+  {
+    wait--;
+    udelay(1000);  /* 1 msec */
+  }
+  if ((inb(p->base + SEECTL) & SEERDY) == 0)
+  {
+    outb(0, p->base + SEECTL);
+    return (0);
+  }
+  return (1);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   release_seeprom
+ *
+ * Description:
+ *   Releases access to the memory port on PCI controllers.
+ *-F*************************************************************************/
+static inline void
+release_seeprom(struct aic7xxx_host *p)
+{
+  outb(0, p->base + SEECTL);
+}
+
+/*+F*************************************************************************
+ * Function:
  *   read_seeprom
  *
  * Description:
@@ -3625,7 +4440,7 @@
  *   first).  The clock cycling from low to high initiates the next data
  *   bit to be sent from the chip.
  *
- *   The 7870 interface to the 93C46 serial EEPROM is through the SEECTL
+ *   The 78xx interface to the 93C46 serial EEPROM is through the SEECTL
  *   register.  After successful arbitration for the memory port, the
  *   SEECS bit of the SEECTL register is connected to the chip select.
  *   The SEECK, SEEDO, and SEEDI are connected to the clock, data out,
@@ -3635,17 +4450,14 @@
  *   to this is when we first request access to the memory port.  The
  *   SEERDY goes high to signify that access has been granted and, for
  *   this case, has no implied timing.
- *
  *-F*************************************************************************/
 static int
-read_seeprom(int base, int offset, struct seeprom_config *sc,
-    seeprom_chip_type chip)
+read_seeprom(struct aic7xxx_host *p, int offset, unsigned short *scarray,
+    unsigned int len, seeprom_chip_type chip)
 {
   int i = 0, k;
-  unsigned long timeout;
   unsigned char temp;
   unsigned short checksum = 0;
-  unsigned short *seeprom = (unsigned short *) sc;
   struct seeprom_cmd {
     unsigned char len;
     unsigned char bits[3];
@@ -3653,43 +4465,33 @@
   struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
 
 #define CLOCK_PULSE(p) \
-  while ((inb(SEECTL + base) & SEERDY) == 0)	\
+  while ((inb(p->base + SEECTL) & SEERDY) == 0)	\
   {						\
     ;  /* Do nothing */				\
   }
 
   /*
-   * Request access of the memory port.  When access is
-   * granted, SEERDY will go high.  We use a 1 second
-   * timeout which should be near 1 second more than
-   * is needed.  Reason: after the 7870 chip reset, there
-   * should be no contention.
+   * Request access of the memory port.
    */
-  outb(SEEMS, SEECTL + base);
-  timeout = jiffies + 100;  /* 1 second timeout */
-  while ((jiffies < timeout) && ((inb(SEECTL + base) & SEERDY) == 0))
-  {
-    ; /* Do nothing!  Wait for access to be granted.  */
-  }
-  if ((inb(SEECTL + base) & SEERDY) == 0)
+  if (acquire_seeprom(p) == 0)
   {
-    outb(0, SEECTL + base);
     return (0);
   }
 
   /*
-   * Read the first 32 registers of the seeprom.  For the 7870,
-   * the 93C46 SEEPROM is a 1024-bit device with 64 16-bit registers
-   * but only the first 32 are used by Adaptec BIOS.  The loop
-   * will range from 0 to 31.
+   * Read 'len' registers of the seeprom.  For the 7870, the 93C46
+   * SEEPROM is a 1024-bit device with 64 16-bit registers but only
+   * the first 32 are used by Adaptec BIOS.  Some adapters use the
+   * 93C56 SEEPROM which is a 2048-bit device.  The loop will range
+   * from 0 to 'len' - 1.
    */
-  for (k = 0; k < (sizeof(*sc) / 2); k++)
+  for (k = 0; k < len; k++)
   {
     /*
      * Send chip select for one clock cycle.
      */
-    outb(SEEMS | SEECK | SEECS, SEECTL + base);
-    CLOCK_PULSE(base);
+    outb(SEEMS | SEECK | SEECS, p->base + SEECTL);
+    CLOCK_PULSE(p);
 
     /*
      * Now we're ready to send the read command followed by the
@@ -3698,25 +4500,25 @@
     for (i = 0; i < seeprom_read.len; i++)
     {
       temp = SEEMS | SEECS | (seeprom_read.bits[i] << 1);
-      outb(temp, SEECTL + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL);
+      CLOCK_PULSE(p);
       temp = temp ^ SEECK;
-      outb(temp, SEECTL + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL);
+      CLOCK_PULSE(p);
     }
     /*
-     * Send the 6 bit address (MSB first, LSB last).
+     * Send the 6 or 8 bit address (MSB first, LSB last).
      */
     for (i = ((int) chip - 1); i >= 0; i--)
     {
       temp = k + offset;
       temp = (temp >> i) & 1;  /* Mask out all but lower bit. */
       temp = SEEMS | SEECS | (temp << 1);
-      outb(temp, SEECTL + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL);
+      CLOCK_PULSE(p);
       temp = temp ^ SEECK;
-      outb(temp, SEECTL + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL);
+      CLOCK_PULSE(p);
     }
 
     /*
@@ -3728,56 +4530,57 @@
     for (i = 0; i <= 16; i++)
     {
       temp = SEEMS | SEECS;
-      outb(temp, SEECTL + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL);
+      CLOCK_PULSE(p);
       temp = temp ^ SEECK;
-      seeprom[k] = (seeprom[k] << 1) | (inb(SEECTL + base) & SEEDI);
-      outb(temp, SEECTL + base);
-      CLOCK_PULSE(base);
+      scarray[k] = (scarray[k] << 1) | (inb(p->base + SEECTL) & SEEDI);
+      outb(temp, p->base + SEECTL);
+      CLOCK_PULSE(p);
     }
 
     /*
-     * The serial EEPROM has a checksum in the last word.  Keep a
-     * running checksum for all words read except for the last
-     * word.  We'll verify the checksum after all words have been
-     * read.
+     * The serial EEPROM should have a checksum in the last word.
+     * Keep a running checksum for all words read except for the
+     * last word.  We'll verify the checksum after all words have
+     * been read.
      */
-    if (k < (sizeof(*sc) / 2) - 1)
+    if (k < (len - 1))
     {
-      checksum = checksum + seeprom[k];
+      checksum = checksum + scarray[k];
     }
 
     /*
      * Reset the chip select for the next command cycle.
      */
-    outb(SEEMS, SEECTL + base);
-    CLOCK_PULSE(base);
-    outb(SEEMS | SEECK, SEECTL + base);
-    CLOCK_PULSE(base);
-    outb(SEEMS, SEECTL + base);
-    CLOCK_PULSE(base);
+    outb(SEEMS, p->base + SEECTL);
+    CLOCK_PULSE(p);
+    outb(SEEMS | SEECK, p->base + SEECTL);
+    CLOCK_PULSE(p);
+    outb(SEEMS, p->base + SEECTL);
+    CLOCK_PULSE(p);
   }
 
   /*
    * Release access to the memory port and the serial EEPROM.
    */
-  outb(0, SEECTL + base);
+  release_seeprom(p);
 
 #if 0
-  printk("Computed checksum 0x%x, checksum read 0x%x\n", checksum, sc->checksum);
+  printk("Computed checksum 0x%x, checksum read 0x%x\n",
+         checksum, scarray[len - 1]);
   printk("Serial EEPROM:");
-  for (k = 0; k < (sizeof(*sc) / 2); k++)
+  for (k = 0; k < len; k++)
   {
     if (((k % 8) == 0) && (k != 0))
     {
       printk("\n              ");
     }
-    printk(" 0x%x", seeprom[k]);
+    printk(" 0x%x", scarray[k]);
   }
   printk("\n");
 #endif
 
-  if (checksum != sc->checksum)
+  if (checksum != scarray[len - 1])
   {
     return (0);
   }
@@ -3788,563 +4591,452 @@
 
 /*+F*************************************************************************
  * Function:
- *   detect_maxscb
+ *   write_brdctl
  *
  * Description:
- *   Detects the maximum number of SCBs for the controller and returns
- *   the count and a mask in config (config->maxscbs, config->qcntmask).
+ *   Writes a value to the BRDCTL register.
  *-F*************************************************************************/
-static void
-detect_maxscb(struct aic7xxx_host_config *config)
+static inline void
+write_brdctl(struct aic7xxx_host *p, unsigned char value)
 {
-  unsigned char sblkctl_reg;
-  int base, i;
-
-#ifdef AIC7XXX_PAGE_ENABLE
-  config->flags |= PAGE_ENABLED;
-#endif
-  base = config->base;
-  switch (config->type)
-  {
-    case AIC_7770:
-    case AIC_7771:
-    case AIC_284x:
-      /*
-       * Check for Rev C or E boards. Rev E boards can supposedly have
-       * more than 4 SCBs, while the Rev C boards are limited to 4 SCBs.
-       * It's still not clear extactly what is different about the Rev E
-       * boards, but we think it allows 8 bit entries in the QOUTFIFO to
-       * support "paging" SCBs (more than 4 commands can be active at once).
-       *
-       * The Rev E boards have a read/write autoflush bit in the
-       * SBLKCTL register, while in the Rev C boards it is read only.
-       */
-      sblkctl_reg = inb(SBLKCTL + base) ^ AUTOFLUSHDIS;
-      outb(sblkctl_reg, SBLKCTL + base);
-      if (inb(SBLKCTL + base) == sblkctl_reg)
-      {
-        /*
-         * We detected a Rev E board, we allow paging on this board.
-         */
-        printk(KERN_INFO "aic7xxx: %s Rev E and subsequent.\n",
-               board_names[config->type]);
-	outb(sblkctl_reg ^ AUTOFLUSHDIS, SBLKCTL + base);
-      }
-      else
-      {
-        /* Do not allow paging. */
-        config->flags &= ~PAGE_ENABLED;
-        printk(KERN_INFO "aic7xxx: %s Rev C and previous.\n",
-               board_names[config->type]);
-      }
-      break;
-
-    default:
-      break;
-  }
-
-  /*
-   * Walk the SCBs to determine how many there are.
-   */
-  i = 1;
-  outb(0, SCBPTR + base);
-  outb(0, SCBARRAY + base);
-
-  while (i < AIC7XXX_MAXSCB)
-  {
-    outb(i, SCBPTR + base);
-    outb(i, SCBARRAY + base);
-    if (inb(SCBARRAY + base) != i)
-      break;
-    outb(0, SCBPTR + base);
-    if (inb(SCBARRAY + base) != 0)
-      break;
-
-    outb(i, SCBPTR + base);      /* Clear the control byte. */
-    outb(0, SCBARRAY + base);
-
-    config->qcntmask |= i;       /* Update the count mask. */
-    i++;
-  }
-  outb(i, SCBPTR + base);   /* Ensure we clear the control bytes. */
-  outb(0, SCBARRAY + base);
-  outb(0, SCBPTR + base); 
-  outb(0, SCBARRAY + base);
-
-  config->maxhscbs = i;
-  config->qcntmask |= i;
-  if ((config->flags & PAGE_ENABLED) && (config->maxhscbs < AIC7XXX_MAXSCB))
-  {
-    config->maxscbs = AIC7XXX_MAXSCB;
-  }
-  else
-  {
-    config->flags &= ~PAGE_ENABLED;  /* Disable paging if we have 255 SCBs!. */
-    config->maxscbs = config->maxhscbs;
-  }
-
-  printk(KERN_INFO "aic7xxx: Memory check yields %d SCBs", config->maxhscbs);
-  if (config->flags & PAGE_ENABLED)
-    printk(", %d page-enabled SCBs.\n", config->maxscbs);
-  else
-    printk(", paging not enabled.\n");
+  unsigned char brdctl;
 
+  brdctl = BRDCS | BRDSTB;
+  outb(brdctl, p->base + BRDCTL);
+  brdctl |= value;
+  outb(brdctl, p->base + BRDCTL);
+  brdctl &= ~BRDSTB;
+  outb(brdctl, p->base + BRDCTL);
+  brdctl &= ~BRDCS;
+  outb(brdctl, p->base + BRDCTL);
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_register
+ *   read_brdctl
  *
  * Description:
- *   Register a Adaptec aic7xxx chip SCSI controller with the kernel.
+ *   Reads the BRDCTL register.
  *-F*************************************************************************/
-static int
-aic7xxx_register(Scsi_Host_Template *template,
-    struct aic7xxx_host_config *config)
+static inline unsigned char
+read_brdctl(struct aic7xxx_host *p)
 {
-  int i;
-  unsigned char sblkctl, flags = 0;
-  int max_targets;
-  int found = 1;
-  unsigned int sram, base;
-  unsigned char target_settings;
-  unsigned char scsi_conf, host_conf;
-  unsigned short ultraenable = 0;
-  int have_seeprom = FALSE;
-  struct Scsi_Host *host;
-  struct aic7xxx_host *p;
-  struct seeprom_config sc;
-
-  base = config->base;
+  outb(BRDRW | BRDCS, p->base + BRDCTL);
+  return (inb(p->base + BRDCTL));
+}
 
-  /*
-   * Lock out other contenders for our i/o space.
-   */
-  request_region(base, MAXREG - MINREG, "aic7xxx");
+/*+F*************************************************************************
+ * Function:
+ *   configure_termination
+ *
+ * Description:
+ *   Configures the termination settings on PCI adapters that have
+ *   SEEPROMs available.
+ *-F*************************************************************************/
+static void
+configure_termination(struct aic7xxx_host *p, unsigned char *sxfrctl1,
+    unsigned short adapter_control, unsigned char max_targ)
+{
+  unsigned char brdctl_int, brdctl_ext;
+  int internal50_present;
+  int internal68_present = 0;
+  int external_present = 0;
+  int eprom_present;
+  int high_on;
+  int low_on;
+  int old_verbose;
+
+  if (acquire_seeprom(p))
+  {
+    if (adapter_control & CFAUTOTERM)
+    {
+      old_verbose = aic7xxx_verbose;
+      printk(KERN_INFO "aic7xxx: Warning - detected auto-termination.  Please "
+                       "verify driver\n");
+      printk(KERN_INFO "         detected settings and use manual termination "
+                       "if necessary.\n"); 
 
-  switch (config->type)
-  {
-    case AIC_7770:
-    case AIC_7771:
-      /*
-       * Use the boot-time option for the interrupt trigger type.  If not
-       * supplied (-1), then we use BIOS settings to determine the interrupt
-       * trigger type (level or edge) and use this value for pausing and
-       * unpausing the sequencer.
-       */
-      switch (aic7xxx_irq_trigger)
-      {
-        case  0: config->unpause = INTEN;          /* Edge */
-                 break;
-        case  1: config->unpause = IRQMS | INTEN;  /* Level */
-                 break;
-        case -1:
-        default: config->unpause = (inb(HCNTRL + base) & IRQMS) | INTEN; 
-                 break;
-      }
-      config->pause = config->unpause | PAUSE;
+      /* Configure auto termination. */
+      outb(SEECS | SEEMS, p->base + SEECTL);
 
       /*
-       * For some 274x boards, we must clear the CHIPRST bit and pause
-       * the sequencer. For some reason, this makes the driver work.
-       * For 284x boards, we give it a CHIPRST just like the 294x boards.
+       * First read the status of our cables.  Set the rom bank to
+       * 0 since the bank setting serves as a multiplexor for the
+       * cable detection logic.  BRDDAT5 controls the bank switch.
        */
-      outb(config->pause | CHIPRST, HCNTRL + base);
-      aic7xxx_delay(1);
-      if (inb(HCNTRL + base) & CHIPRST)
-      {
-	printk(KERN_INFO "aic7xxx: Chip reset not cleared; clearing manually.\n");
-      }
-      outb(config->pause, HCNTRL + base);
+      write_brdctl(p, 0);
 
       /*
-       * Just to be on the safe side with the 274x, we will re-read the irq
-       * since there was some issue about resetting the board.
+       * Now read the state of the internal connectors.  The
+       * bits BRDDAT6 and BRDDAT7 are 0 when cables are present
+       * set when cables are not present (BRDDAT6 is INT50 and
+       * BRDDAT7 is INT68).
        */
-      config->irq = inb(INTDEF + base) & 0x0F;
-      if ((config->type == AIC_7771) &&
-          (inb(HA_274_BIOSCTRL + base) & BIOSMODE) == BIOSDISABLED)
-      {
-        config->bios = AIC_DISABLED;
-        config->flags |= USE_DEFAULTS;
-      }
-      else
+      brdctl_int = read_brdctl(p);
+      internal50_present = (brdctl_int & BRDDAT6) ? 0 : 1;
+      if (max_targ > 8)
       {
-        host_conf = inb(HOSTCONF + base);
-        config->bus_speed = host_conf & DFTHRSH;
-        config->busrtime = (host_conf << 2) & BOFF;
+        internal68_present = (brdctl_int & BRDDAT7) ? 0 : 1;
       }
 
       /*
-       * Setup the FIFO threshold and the bus off time
+       * Set the rom bank to 1 and determine
+       * the other signals.
        */
-      outb(config->bus_speed & DFTHRSH, BUSSPD + base);
-      outb(config->busrtime, BUSTIME + base);
+      write_brdctl(p, BRDDAT5);
 
       /*
-       * A reminder until this can be detected automatically.
+       * Now read the state of the external connectors.  BRDDAT6 is
+       * 0 when an external cable is present, and BRDDAT7 (EPROMPS) is
+       * set when the eprom is present.
        */
-      printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
-	     (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
-      break;
-
-    case AIC_284x:
-      outb(CHIPRST, HCNTRL + base);
-      config->unpause = UNPAUSE_284X;
-      config->pause = REQ_PAUSE; /* DWG would like to be like the rest */
-      aic7xxx_delay(1);
-      outb(config->pause, HCNTRL + base);
-
-      config->parity = AIC_ENABLED;
-      config->irq = inb(INTDEF + base) & 0x0F;
-      host_conf = inb(HOSTCONF + base);
-
-      printk(KERN_INFO "aic7xxx: Reading SEEPROM...");
-      have_seeprom = read_2840_seeprom(base, &sc);
-      if (!have_seeprom)
-      {
-	printk("aic7xxx: Unable to read SEEPROM.\n");
-      }
-      else
+      brdctl_ext = read_brdctl(p);
+      external_present = (brdctl_ext & BRDDAT6) ? 0 : 1;
+      eprom_present = brdctl_ext & BRDDAT7;
+      if (aic7xxx_verbose)
       {
-	printk("done.\n");
-        config->flags |= HAVE_SEEPROM;
-        if (sc.bios_control & CF284XEXTEND)
-          config->flags |= EXTENDED_TRANSLATION;
-        if (!(sc.bios_control & CFBIOSEN))
+        if (max_targ > 8)
         {
-          /*
-           * The BIOS is disabled; the values left over in scratch
-           * RAM are still valid.  Do not use defaults as in the
-           * AIC-7770 case.
-           */
-          config->bios = AIC_DISABLED;
+          printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Int-68 %s, "
+                 "Ext-68 %s)\n",
+                 internal50_present ? "YES" : "NO",
+                 internal68_present ? "YES" : "NO",
+                 external_present ? "YES" : "NO");
         }
         else
         {
-	  config->parity = (sc.adapter_control & CFSPARITY) ?
-			   AIC_ENABLED : AIC_DISABLED;
-	  config->low_term = (sc.adapter_control & CF284XSTERM) ?
-			        AIC_ENABLED : AIC_DISABLED;
-	  /*
-	   * XXX - Adaptec *does* make 284x wide controllers, but the
-	   *       documents do not say where the high byte termination
-	   *       enable bit is located.
-           */
+          printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Ext-50 %s)\n",
+                 internal50_present ? "YES" : "NO",
+                 external_present ? "YES" : "NO");
         }
+        printk(KERN_INFO "aic7xxx: eprom %s present, brdctl_int=0x%x, "
+               "brdctl_ext=0x%x\n",
+               eprom_present ? "is" : "not", brdctl_int, brdctl_ext);
       }
 
-      host_conf = inb(HOSTCONF + base);
-      config->bus_speed = host_conf & DFTHRSH;
-      config->busrtime = (host_conf << 2) & BOFF;
-
-      /*
-       * Setup the FIFO threshold and the bus off time
-       */
-      outb(config->bus_speed & DFTHRSH, BUSSPD + base);
-      outb(config->busrtime, BUSTIME + base);
-
-      printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
-	     (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
-      break;
-
-    case AIC_7860:
-    case AIC_7861:
-    case AIC_7880:
-    case AIC_7881:
-    case AIC_7882:
-    case AIC_7883:
-    case AIC_7884:
-      /*
-       * Remember if Ultra was enabled in case there is no SEEPROM.
-       * Fall through to the rest of the AIC_78xx code.
-       */
-      if ((inb(SXFRCTL0 + base) & ULTRAEN) || aic7xxx_enable_ultra)
-        config->flags |= ULTRA_ENABLED;
-
-    case AIC_7850:
-    case AIC_7855:
-    case AIC_7870:
-    case AIC_7871:
-    case AIC_7872:
-    case AIC_7873:
-    case AIC_7874:
       /*
-       * Grab the SCSI ID before chip reset in case there is no SEEPROM.
+       * Now set the termination based on what we found.  BRDDAT6
+       * controls wide termination enable.
        */
-      config->scsi_id = inb(SCSIID + base) & OID;
-      outb(CHIPRST, HCNTRL + base);
-      config->unpause = UNPAUSE_294X;
-      config->pause = config->unpause | PAUSE;
-      aic7xxx_delay(1);
-      outb(config->pause, HCNTRL + base);
-
-      config->parity = AIC_ENABLED;
+      high_on = FALSE;
+      low_on = FALSE;
+      if ((max_targ > 8) &&
+          ((external_present == 0) || (internal68_present == 0)))
+      {
+        high_on = TRUE;
+      }
 
-      printk(KERN_INFO "aic7xxx: Reading SEEPROM...");
-      if ((config->type == AIC_7873) || (config->type == AIC_7883))
+      if ((internal50_present + internal68_present + external_present) <= 1)
       {
-        have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2),
-                                    &sc, c56_66);
+        low_on = TRUE;
       }
-      else
+          
+      if (internal50_present && internal68_present && external_present)
       {
-        have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2),
-                                    &sc, c46);
+        printk(KERN_WARNING "aic7xxx: Illegal cable configuration!!\n"
+               "         Only two connectors on the adapter may be "
+               "used at a time!\n");
       }
-      if (!have_seeprom)
+
+      if (high_on == TRUE)
+        write_brdctl(p, BRDDAT6);
+      else
+        write_brdctl(p, 0);
+
+      if (low_on == TRUE)
+        *sxfrctl1 |= STPWEN;
+
+      if (aic7xxx_verbose)
       {
-        for (sram = base + TARG_SCRATCH; sram < base + 0x60; sram++)
-        {
-          if (inb(sram) != 0x00)
-            break;
-        }
-        if (sram == base + TARG_SCRATCH)
-        {
-          for (sram = base + TARG_SCRATCH; sram < base + 0x60; sram++)
-          {
-            if (inb(sram) != 0xFF)
-              break;
-          }
-        }
-        if ((sram != base + 0x60) && (config->scsi_id != 0))
+        if (max_targ > 8)
         {
-          config->flags &= ~USE_DEFAULTS;
-	  printk("\naic7xxx: Unable to read SEEPROM; "
-                 "using leftover BIOS values.\n");
+          printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n",
+                 low_on ? "ON" : "OFF",
+                 high_on ? "ON" : "OFF");
         }
         else
         {
-          printk("\n");
-          printk(KERN_INFO "aic7xxx: Unable to read SEEPROM; using default "
-                 "settings.\n");
-          config->flags |= USE_DEFAULTS;
-          config->flags &= ~ULTRA_ENABLED;
-          config->scsi_id = 7;
+          printk(KERN_INFO "aic7xxx: Termination %s\n", low_on ? "ON" : "OFF");
         }
-        scsi_conf = ENSPCHK | RESET_SCSI;
+      }
+      aic7xxx_verbose = old_verbose;
+    }
+    else
+    {
+      if (adapter_control & CFSTERM)
+      {
+        *sxfrctl1 |= STPWEN;
+      }
+      outb(SEEMS | SEECS, p->base + SEECTL);
+      /*
+       * Configure high byte termination.
+       */
+      if (adapter_control & CFWSTERM)
+      {
+        write_brdctl(p, BRDDAT6);
       }
       else
       {
-	printk("done.\n");
-        config->flags |= HAVE_SEEPROM;
-        if (!(sc.bios_control & CFBIOSEN))
-        {
-          /*
-           * The BIOS is disabled; the values left over in scratch
-           * RAM are still valid.  Do not use defaults as in the
-           * AIC-7770 case.
-           */
-          config->bios = AIC_DISABLED;
-          scsi_conf = ENSPCHK | RESET_SCSI;
-        }
-        else
-        {
-          scsi_conf = 0;
-          if (sc.adapter_control & CFRESETB)
-            scsi_conf |= RESET_SCSI;
-          if (sc.adapter_control & CFSPARITY)
-            scsi_conf |= ENSPCHK;
-	  if (sc.bios_control & CFEXTEND)
-            config->flags |= EXTENDED_TRANSLATION;
-	  config->scsi_id = (sc.brtime_id & CFSCSIID);
-	  config->parity = (sc.adapter_control & CFSPARITY) ?
-			     AIC_ENABLED : AIC_DISABLED;
-	  config->low_term = (sc.adapter_control & CFSTERM) ?
-			       AIC_ENABLED : AIC_DISABLED;
-	  config->high_term = (sc.adapter_control & CFWSTERM) ?
-			        AIC_ENABLED : AIC_DISABLED;
-	  config->busrtime = ((sc.brtime_id & CFBRTIME) >> 8);
-          if (((config->type == AIC_7880) || (config->type == AIC_7881) ||
-               (config->type == AIC_7882) || (config->type == AIC_7883) ||
-               (config->type == AIC_7884)) && (sc.adapter_control & CFULTRAEN))
-          {
-            printk(KERN_INFO "aic7xxx: Enabling support for Ultra SCSI "
-                   "speed.\n");
-            config->flags |= ULTRA_ENABLED;
-          }
-        }
+        write_brdctl(p, 0);
+      }
+      if (aic7xxx_verbose)
+      {
+        printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n",
+               (adapter_control & CFSTERM) ? "ON" : "OFF",
+               (adapter_control & CFWSTERM) ? "ON" : "OFF");
       }
+    }
+    release_seeprom(p);
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   detect_maxscb
+ *
+ * Description:
+ *   Detects the maximum number of SCBs for the controller and returns
+ *   the count and a mask in p (p->maxscbs, p->qcntmask).
+ *-F*************************************************************************/
+static void
+detect_maxscb(struct aic7xxx_host *p)
+{
+  int i;
+  unsigned char max_scbid = 255;
 
-      outb(scsi_conf | (config->scsi_id & 0x07), SCSICONF + base);
-      config->bus_speed = DFTHRSH_100;
-      outb(config->bus_speed, DSPCISTATUS + base);
+  /*
+   * It's possible that we've already done this for multichannel
+   * adapters.
+   */
+  if (p->scb_data->maxhscbs == 0)
+  {
+    /*
+     * We haven't initialized the SCB settings yet.  Walk the SCBs to
+     * determince how many there are.
+     */
+    outb(0, p->base + FREE_SCBH);
 
-      /*
-       * In case we are a wide card...
-       */
-      outb(config->scsi_id, SCSICONF + base + 1);
+    for (i = 0; i < AIC7XXX_MAXSCB; i++)
+    {
+      outb(i, p->base + SCBPTR);
+      outb(i, p->base + SCB_CONTROL);
+      if (inb(p->base + SCB_CONTROL) != i)
+        break;
+      outb(0, p->base + SCBPTR);
+      if (inb(p->base + SCB_CONTROL) != 0)
+        break;
 
-      printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
-	     (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
-      break;
+      outb(i, p->base + SCBPTR);
+      outb(0, p->base + SCB_CONTROL);   /* Clear the control byte. */
+      outb(i + 1, p->base + SCB_NEXT);  /* Set the next pointer. */
+      outb(SCB_LIST_NULL, p->base + SCB_TAG);  /* Make the tag invalid. */
 
-    default:
-      panic(KERN_WARNING "aic7xxx: (aic7xxx_register) Internal error.\n");
+      /* Make the non-tagged targets not busy. */
+      outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS);
+      outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 1);
+      outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 2);
+      outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 3);
+    }
+
+    /* Make sure the last SCB terminates the free list. */
+    outb(i - 1, p->base + SCBPTR);
+    outb(SCB_LIST_NULL, p->base + SCB_NEXT);
+
+    /* Ensure we clear the first (0) SCBs control byte. */
+    outb(0, p->base + SCBPTR);
+    outb(0, p->base + SCB_CONTROL);
+
+    p->scb_data->maxhscbs = i;
   }
 
-  detect_maxscb(config);
+  if ((p->flags & PAGE_ENABLED) && (p->scb_data->maxhscbs < AIC7XXX_MAXSCB))
+  {
+    /* Determine the number of valid bits in the FIFOs. */
+    outb(max_scbid, p->base + QINFIFO);
+    max_scbid = inb(p->base + QINFIFO);
+    p->scb_data->maxscbs = MIN(AIC7XXX_MAXSCB, max_scbid + 1);
+  }
+  else
+  {
+    p->scb_data->maxscbs = p->scb_data->maxhscbs;
+  }
+  if (p->scb_data->maxscbs == p->scb_data->maxhscbs)
+  {
+    /*
+     * Disable paging if the QINFIFO doesn't allow more SCBs than
+     * we have in hardware.
+     */
+    p->flags &= ~PAGE_ENABLED;
+  }
 
-  if (config->chip_type == AIC_777x)
+  /*
+   * Set the Queue Full Count.  Some cards have more queue space than
+   * SCBs.
+   */
+  switch (p->chip_class)
   {
-    if (config->pause & IRQMS)
-    {
-      printk(KERN_INFO "aic7xxx: Using level sensitive interrupts.\n");
-    }
-    else
-    {
-      printk(KERN_INFO "aic7xxx: Using edge triggered interrupts.\n");
-    }
+    case AIC_777x:
+      p->qfullcount = 4;
+      p->qcntmask = 0x07;
+      break;
+    case AIC_785x:
+    case AIC_786x:
+      p->qfullcount = 8;
+      p->qcntmask = 0x0f;
+      break;
+    case AIC_787x:
+    case AIC_788x:
+      if (p->scb_data->maxhscbs == AIC7XXX_MAXSCB)
+      {
+        p->qfullcount = AIC7XXX_MAXSCB;
+        p->qcntmask = 0xFF;
+      }
+      else
+      {
+        p->qfullcount = 16;
+        p->qcntmask = 0x1F;
+      }
+      break;
   }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_register
+ *
+ * Description:
+ *   Register a Adaptec aic7xxx chip SCSI controller with the kernel.
+ *-F*************************************************************************/
+static int
+aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p)
+{
+  int i;
+  unsigned char sblkctl, flags = 0;
+  int max_targets;
+  int found = 1;
+  char channel_ids[] = {'A', 'B', 'C'};
+  unsigned char target_settings;
+  unsigned char scsi_conf, sxfrctl1;
+  unsigned short ultraenable = 0;
+  struct Scsi_Host *host;
+
+  /*
+   * Lock out other contenders for our i/o space.
+   */
+  request_region(p->base, MAXREG - MINREG, "aic7xxx");
 
   /*
    * Read the bus type from the SBLKCTL register. Set the FLAGS
    * register in the sequencer for twin and wide bus cards.
    */
-  sblkctl = inb(SBLKCTL + base);
-  if (config->flags & PAGE_ENABLED)
+  sblkctl = inb(p->base + SBLKCTL);
+  if (p->flags & PAGE_ENABLED)
     flags = PAGESCBS;
 
   switch (sblkctl & SELBUS_MASK)
   {
     case SELNARROW:     /* narrow/normal bus */
-      config->scsi_id = inb(SCSICONF + base) & 0x07;
-      config->bus_type = AIC_SINGLE;
-      outb(flags | SINGLE_BUS, FLAGS + base);
+      p->scsi_id = inb(p->base + SCSICONF) & 0x07;
+      p->bus_type = AIC_SINGLE;
+      p->flags &= ~FLAGS_CHANNEL_B_PRIMARY;
+      if (p->flags & MULTI_CHANNEL)
+      {
+        printk(KERN_INFO "aic7xxx: Channel %c, SCSI ID %d, ",
+               channel_ids[p->chan_num], p->scsi_id);
+      }
+      else
+      {
+        printk (KERN_INFO "aic7xxx: Single Channel, SCSI ID %d, ",
+                p->scsi_id);
+      }
+      outb(flags | SINGLE_BUS, p->base + SEQ_FLAGS);
       break;
 
     case SELWIDE:     /* Wide bus */
-      config->scsi_id = inb(SCSICONF + base + 1) & 0x0F;
-      config->bus_type = AIC_WIDE;
-      printk("aic7xxx: Enabling wide channel of %s-Wide.\n",
-	     board_names[config->type]);
-      outb(flags | WIDE_BUS, FLAGS + base);
+      p->scsi_id = inb(p->base + SCSICONF + 1) & HWSCSIID;
+      p->bus_type = AIC_WIDE;
+      p->flags &= ~FLAGS_CHANNEL_B_PRIMARY;
+      if (p->flags & MULTI_CHANNEL)
+      {
+        printk(KERN_INFO "aic7xxx: Wide Channel %c, SCSI ID %d, ",
+               channel_ids[p->chan_num], p->scsi_id);
+      }
+      else
+      {
+        printk (KERN_INFO "aic7xxx: Wide Channel, SCSI ID %d, ",
+                p->scsi_id);
+      }
+      outb(flags | WIDE_BUS, p->base + SEQ_FLAGS);
       break;
 
     case SELBUSB:     /* Twin bus */
-      config->scsi_id = inb(SCSICONF + base) & 0x07;
-#ifdef AIC7XXX_TWIN_SUPPORT
-      config->scsi_id_b = inb(SCSICONF + base + 1) & 0x07;
-      config->bus_type = AIC_TWIN;
-      printk(KERN_INFO "aic7xxx: Enabled channel B of %s-Twin.\n",
-	     board_names[config->type]);
-      outb(flags | TWIN_BUS, FLAGS + base);
-#else
-      config->bus_type = AIC_SINGLE;
-      printk(KERN_INFO "aic7xxx: Channel B of %s-Twin will be ignored.\n",
-	     board_names[config->type]);
-      outb(flags, FLAGS + base);
-#endif
-      break;
-
-    default:
-      printk(KERN_WARNING "aic7xxx: Unsupported type 0x%x, please "
-	     "mail deang@teleport.com\n", inb(SBLKCTL + base));
-      outb(0, FLAGS + base);
-      return (0);
-  }
-
-  /*
-   * For the 294x cards, clearing DIAGLEDEN and DIAGLEDON, will
-   * take the card out of diagnostic mode and make the host adapter
-   * LED follow bus activity (will not always be on).
-   */
-  outb(sblkctl & ~(DIAGLEDEN | DIAGLEDON), SBLKCTL + base);
-
-  /*
-   * The IRQ level in i/o port 4 maps directly onto the real
-   * IRQ number. If it's ok, register it with the kernel.
-   *
-   * NB. the Adaptec documentation says the IRQ number is only
-   *     in the lower four bits; the ECU information shows the
-   *     high bit being used as well. Which is correct?
-   *
-   * The PCI cards get their interrupt from PCI BIOS.
-   */
-  if ((config->chip_type == AIC_777x) && ((config->irq < 9) || (config->irq > 15)))
-  {
-    printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ level, "
-          "ignoring.\n");
-    return (0);
+      p->scsi_id = inb(p->base + SCSICONF) & HSCSIID;
+      p->scsi_id_b = inb(p->base + SCSICONF + 1) & HSCSIID;
+      p->bus_type = AIC_TWIN;
+      printk(KERN_INFO "aic7xxx: Twin Channel, A SCSI ID %d, B SCSI ID %d, ",
+             p->scsi_id, p->scsi_id_b);
+      outb(flags | TWIN_BUS, p->base + SEQ_FLAGS);
+      break;
+
+    default:
+      printk(KERN_WARNING "aic7xxx: Unsupported type 0x%x, please "
+	     "mail deang@teleport.com\n", inb(p->base + SBLKCTL));
+      outb(0, p->base + SEQ_FLAGS);
+      return (0);
   }
 
   /*
-   * Print out debugging information before re-enabling
-   * the card - a lot of registers on it can't be read
-   * when the sequencer is active.
-   */
-  debug_config(config);
-
-  /*
-   * Register each "host" and fill in the returned Scsi_Host
-   * structure as best we can. Some of the parameters aren't
-   * really relevant for bus types beyond ISA, and none of the
-   * high-level SCSI code looks at it anyway. Why are the fields
-   * there? Also save the pointer so that we can find the
-   * information when an IRQ is triggered.
+   * Detect SCB parameters and initialize the SCB array.
    */
-  host = scsi_register(template, sizeof(struct aic7xxx_host));
-  host->can_queue = config->maxscbs;
+  detect_maxscb(p);
+  printk("%d/%d SCBs, QFull %d, QMask 0x%x\n",
+         p->scb_data->maxhscbs, p->scb_data->maxscbs,
+         p->qfullcount, p->qcntmask);
+
+  host = p->host;
+
+  host->can_queue = p->scb_data->maxscbs;
   host->cmd_per_lun = 2;
+  host->sg_tablesize = AIC7XXX_MAX_SG;
   host->select_queue_depths = aic7xxx_select_queue_depth;
-  host->this_id = config->scsi_id;
-  host->io_port = config->base;
+  host->this_id = p->scsi_id;
+  host->io_port = p->base;
   host->n_io_port = 0xFF;
-  host->base = (unsigned char *)config->mbase;
-  host->irq = config->irq;
-  if (config->bus_type == AIC_WIDE)
+  host->base = (unsigned char *) p->mbase;
+  host->irq = p->irq;
+  if (p->bus_type == AIC_WIDE)
   {
     host->max_id = 16;
   }
-  if (config->bus_type == AIC_TWIN)
+  if (p->bus_type == AIC_TWIN)
   {
     host->max_channel = 1;
   }
 
-  p = (struct aic7xxx_host *) host->hostdata;
-
   p->host = host;
-  p->host_no = (int)host->host_no;
+  p->host_no = host->host_no;
   p->isr_count = 0;
-  p->base = base;
-  p->maxscbs = config->maxscbs;
-  p->maxhscbs = config->maxhscbs;
-  p->qcntmask = config->qcntmask;
-  p->mbase = (char *)config->mbase;
-  p->type = config->type;
-  p->chip_type = config->chip_type;
-  p->flags = config->flags;
-  p->chan_num = config->chan_num;
-  p->scb_link = &(p->scb_usage);
-#if defined(CONFIG_PCI) && defined(AIC7XXX_SHARE_SCBS)
-  if ((p->chan_num == 0) && ((p->type == AIC_7873) | (p->type == AIC_7883)))
-  {
-    shared_3985_scbs = &(p->scb_usage);
-    p->scb_link = &(p->scb_usage);
-  }
-#endif
-  p->scb_link->numscbs = 0;
-  p->bus_type = config->bus_type;
-  p->seeprom = sc;
   p->next = NULL;
   p->completeq.head = NULL;
   p->completeq.tail = NULL;
-  scbq_init(&p->scb_link->free_scbs);
-  scbq_init(&p->page_scbs);
+  scbq_init(&p->scb_data->free_scbs);
   scbq_init(&p->waiting_scbs);
-  scbq_init(&p->assigned_scbs);
-
-  p->unpause = config->unpause;
-  p->pause = config->pause;
 
-  for (i = 0; i <= 15; i++)
+  for (i = 0; i <= NUMBER(p->device_status); i++)
   {
     p->device_status[i].commands_sent = 0;
     p->device_status[i].flags = 0;
+    p->device_status[i].active_cmds = 0;
     p->device_status[i].last_reset = 0;
   }
-  if (aic7xxx_boards[config->irq] == NULL)
+  if (aic7xxx_boards[p->irq] == NULL)
   {
+    int result;
+    int irq_flags = 0;
+
+#ifdef AIC7XXX_OLD_ISR_TYPE
+    irg_flags = SA_INTERRUPT;
+#endif
     /*
      * Warning! This must be done before requesting the irq.  It is
      * possible for some boards to raise an interrupt as soon as
@@ -4352,17 +5044,26 @@
      * kernel, an interrupt is triggered immediately.  Therefore, we
      * must ensure the board data is correctly set before the request.
      */
-    aic7xxx_boards[config->irq] = host;
+    aic7xxx_boards[p->irq] = host;
 
     /*
-     * Register IRQ with the kernel.
+     * Register IRQ with the kernel.  Only allow sharing IRQs with
+     * PCI devices.
      */
-    if (request_irq(config->irq, aic7xxx_isr, SA_INTERRUPT | SA_SHIRQ,
-       "aic7xxx", NULL))
+    if (p->chip_class == AIC_777x)
+    {
+      result = (request_irq(p->irq, aic7xxx_isr, irq_flags, "aic7xxx", NULL));
+    }
+    else
+    {
+      result = (request_irq(p->irq, aic7xxx_isr, irq_flags | SA_SHIRQ,
+                "aic7xxx", NULL));
+    }
+    if (result < 0)
     {
       printk(KERN_WARNING "aic7xxx: Couldn't register IRQ %d, ignoring.\n",
-             config->irq);
-      aic7xxx_boards[config->irq] = NULL;
+             p->irq);
+      aic7xxx_boards[p->irq] = NULL;
       return (0);
     }
   }
@@ -4373,79 +5074,74 @@
      * registered host adapter. Add this host adapter's Scsi_Host
      * to the beginning of the linked list of hosts at the same IRQ.
      */
-    p->next = aic7xxx_boards[config->irq];
-    aic7xxx_boards[config->irq] = host;
-  }
-
-  /*
-   * Load the sequencer program, then re-enable the board -
-   * resetting the AIC-7770 disables it, leaving the lights
-   * on with nobody home. On the PCI bus you *may* be home,
-   * but then your mailing address is dynamically assigned
-   * so no one can find you anyway :-)
-   */
-  printk(KERN_INFO "aic7xxx: Downloading sequencer code...");
-  aic7xxx_loadseq(base);
-
-  /*
-   * Set Fast Mode and Enable the board
-   */
-  outb(FASTMODE, SEQCTL + base);
-
-  if (p->chip_type == AIC_777x)
-  {
-    outb(ENABLE, BCTL + base);
+    p->next = aic7xxx_boards[p->irq];
+    aic7xxx_boards[p->irq] = host;
   }
 
-  printk("done.\n");
-
   /*
    * Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels
    */
   if (p->bus_type == AIC_TWIN)
   {
     /*
-     * Select Channel B.
+     * The controller is gated to channel B after a chip reset; set
+     * bus B values first.
      */
-    outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL + base);
-
-    outb(config->scsi_id_b, SCSIID + base);
-    scsi_conf = inb(SCSICONF + base + 1) & (ENSPCHK | STIMESEL);
-    outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base);
-#if EXPERIMENTAL_FLAGS
-    outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1 + base);
-#else
-    outb(ENSELTIMO, SIMODE1 + base);
-#endif
+    outb(p->scsi_id_b, p->base + SCSIID);
+    scsi_conf = inb(p->base + SCSICONF + 1);
+    sxfrctl1 = inb(p->base + SXFRCTL1);
+    outb((scsi_conf & (ENSPCHK | STIMESEL)) | (sxfrctl1 & STPWEN) | 
+         ENSTIMER | ACTNEGEN, p->base + SXFRCTL1);
+    outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, p->base + SIMODE1);
     if (p->flags & ULTRA_ENABLED)
     {
-      outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base);
+      outb(DFON | SPIOEN | FAST20, p->base + SXFRCTL0);
     }
     else
     {
-      outb(DFON | SPIOEN, SXFRCTL0 + base);
+      outb(DFON | SPIOEN, p->base + SXFRCTL0);
     }
 
-    /*
-     * Select Channel A
-     */
-    outb((sblkctl & ~SELBUS_MASK) | SELNARROW, SBLKCTL + base);
+    if ((scsi_conf & RESET_SCSI) && (aic7xxx_no_reset == 0))
+    {
+      /* Reset SCSI bus B. */
+      if (aic7xxx_verbose)
+        printk(KERN_INFO "aic7xxx: Resetting channel B\n");
+
+      aic7xxx_reset_current_bus(p);
+    }
+
+    /* Select channel A */
+    outb(SELNARROW, p->base + SBLKCTL);
   }
-  outb(config->scsi_id, SCSIID + base);
-  scsi_conf = inb(SCSICONF + base) & (ENSPCHK | STIMESEL);
-  outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base);
-#if EXPERIMENTAL_FLAGS
-  outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1 + base);
-#else
-  outb(ENSELTIMO, SIMODE1 + base);
-#endif
+
+  outb(p->scsi_id, p->base + SCSIID);
+  scsi_conf = inb(p->base + SCSICONF);
+  sxfrctl1 = inb(p->base + SXFRCTL1);
+  outb((scsi_conf & (ENSPCHK | STIMESEL)) | (sxfrctl1 & STPWEN) | 
+       ENSTIMER | ACTNEGEN, p->base + SXFRCTL1);
+  outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, p->base + SIMODE1);
   if (p->flags & ULTRA_ENABLED)
   {
-    outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base);
+    outb(DFON | SPIOEN | FAST20, p->base + SXFRCTL0);
   }
   else
   {
-    outb(DFON | SPIOEN, SXFRCTL0 + base);
+    outb(DFON | SPIOEN, p->base + SXFRCTL0);
+  }
+
+  if ((scsi_conf & RESET_SCSI) && (aic7xxx_no_reset == 0))
+  {
+    /* Reset SCSI bus A. */
+    if (aic7xxx_verbose)
+      printk(KERN_INFO "aic7xxx: Resetting channel A\n");
+
+    aic7xxx_reset_current_bus(p);
+
+    /*
+     * Delay for the reset delay.
+     */
+    aic7xxx_delay(AIC7XXX_RESET_DELAY);
   }
 
   /*
@@ -4472,67 +5168,47 @@
   /*
    * Grab the disconnection disable table and invert it for our needs
    */
-  if (have_seeprom)
+  if (p->flags & USE_DEFAULTS)
   {
-    p->discenable = 0x0;
+    printk(KERN_INFO "aic7xxx: Host adapter BIOS disabled. Using default SCSI "
+           "device parameters.\n");
+    p->discenable = 0xFFFF;
   }
   else
   {
-    if (config->bios == AIC_DISABLED)
-    {
-      printk(KERN_INFO "aic7xxx : Host adapter BIOS disabled. Using default SCSI "
-             "device parameters.\n");
-      p->discenable = 0xFFFF;
-    }
-    else
-    {
-      p->discenable = ~((inb(DISC_DSB + base + 1) << 8) |
-          inb(DISC_DSB + base));
-    }
+    p->discenable = ~((inb(p->base + DISC_DSB + 1) << 8) |
+        inb(p->base + DISC_DSB));
   }
 
   for (i = 0; i < max_targets; i++)
   {
-    if (config->flags & USE_DEFAULTS)
+    if (p->flags & USE_DEFAULTS)
     {
-      target_settings = 0;  /* 10 MHz */
+      target_settings = 0;  /* 10 or 20 MHz depending on Ultra enable */
       p->needsdtr_copy |= (0x01 << i);
       p->needwdtr_copy |= (0x01 << i);
+      if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
+        ultraenable |= (0x01 << i);
     }
     else
     {
-      if (have_seeprom)
+      target_settings = inb(p->base + TARG_SCRATCH + i);
+      if (target_settings & 0x0F)
       {
-  	target_settings = ((sc.device_flags[i] & CFXFER) << 4);
-  	if (sc.device_flags[i] & CFSYNCH)
-  	{
-  	  p->needsdtr_copy |= (0x01 << i);
-  	}
-  	if (sc.device_flags[i] & CFWIDEB)
-  	{
-  	  p->needwdtr_copy |= (0x01 << i);
-  	}
-  	if (sc.device_flags[i] & CFDISC)
-        {
-          p->discenable |= (0x01 << i);
-        }
+        p->needsdtr_copy |= (0x01 << i);
+        /*
+         * Default to asynchronous transfers (0 offset)
+         */
+        target_settings &= 0xF0;
       }
-      else
+      if (target_settings & 0x80)
       {
-        target_settings = inb(TARG_SCRATCH + base + i);
-        if (target_settings & 0x0F)
-        {
-          p->needsdtr_copy |= (0x01 << i);
-          /*
-           * Default to asynchronous transfers (0 offset)
-           */
-          target_settings &= 0xF0;
-        }
-        if (target_settings & 0x80)
-        {
-          p->needwdtr_copy |= (0x01 << i);
-          target_settings &= 0x7F;
-        }
+        p->needwdtr_copy |= (0x01 << i);
+        /*
+         * Clear the wide flag. When wide negotiation is successful,
+         * we'll enable it.
+         */
+        target_settings &= 0x7F;
       }
       if (p->flags & ULTRA_ENABLED)
       {
@@ -4543,126 +5219,474 @@
           case 0x20:
             ultraenable |= (0x01 << i);
             break;
-          case 0x40:
+          case 0x40:  /* treat 10MHz as 10MHz without Ultra enabled */
             target_settings &= ~(0x70);
             break;
           default:
             break;
         }
       }
-    }
-    outb(target_settings, (TARG_SCRATCH + base + i));
+    }
+    outb(target_settings, p->base + TARG_SCRATCH + i);
+  }
+
+  /*
+   * If we are not wide, forget WDTR. This makes the driver
+   * work on some cards that don't leave these fields cleared
+   * when BIOS is not installed.
+   */
+  if (p->bus_type != AIC_WIDE)
+  {
+    p->needwdtr_copy = 0;
+  }
+  p->needsdtr = p->needsdtr_copy;
+  p->needwdtr = p->needwdtr_copy;
+  p->orderedtag = 0;
+  outb(ultraenable & 0xFF, p->base + ULTRA_ENB);
+  outb((ultraenable >> 8) & 0xFF, p->base + ULTRA_ENB + 1);
+
+  /*
+   * Set the number of available hardware SCBs.
+   */
+  outb(p->scb_data->maxhscbs, p->base + SCBCOUNT);
+
+  /*
+   * 2s compliment of maximum tag value.
+   */
+  i = p->scb_data->maxscbs;
+  outb(-i & 0xFF, p->base + COMP_SCBCOUNT);
+
+  /*
+   * Allocate enough hardware scbs to handle the maximum number of
+   * concurrent transactions we can have.  We have to make sure that
+   * the allocated memory is contiguous memory.  The Linux kmalloc
+   * routine should only allocate contiguous memory, but note that
+   * this could be a problem if kmalloc() is changed.
+   */
+  if (p->scb_data->hscbs == NULL)
+  {
+    size_t array_size;
+    unsigned int hscb_physaddr;
+
+    array_size = p->scb_data->maxscbs * sizeof(struct aic7xxx_hwscb);
+    p->scb_data->hscbs = kmalloc(array_size, GFP_ATOMIC);
+    if (p->scb_data->hscbs == NULL)
+    {
+      printk("aic7xxx: Unable to allocate hardware SCB array; "
+             "failing detection.\n");
+      release_region(p->base, MAXREG - MINREG);
+      /*
+       * Ensure that we only free the IRQ when there is _not_ another
+       * aic7xxx adapter sharing this IRQ.  The adapters are always
+       * added to the beginning of the list, so we can grab the next
+       * pointer and place it back in the board array.
+       */
+      if (p->next == NULL)
+      {
+        free_irq(p->irq, aic7xxx_isr);
+      }
+      aic7xxx_boards[p->irq] = p->next;
+      return(0);
+    }
+    /* At least the control byte of each SCB needs to be 0. */
+    memset(p->scb_data->hscbs, 0, array_size);
+
+    /* Tell the sequencer where it can find the hardware SCB array. */
+    hscb_physaddr = VIRT_TO_BUS(p->scb_data->hscbs);
+    outb(hscb_physaddr & 0xFF, p->base + HSCB_ADDR);
+    outb((hscb_physaddr >> 8) & 0xFF, p->base + HSCB_ADDR + 1);
+    outb((hscb_physaddr >> 16) & 0xFF, p->base + HSCB_ADDR + 2);
+    outb((hscb_physaddr >> 24) & 0xFF, p->base + HSCB_ADDR + 3);
+  }
+
+  /*
+   * QCount mask to deal with broken aic7850s that sporadically get
+   * garbage in the upper bits of their QCNT registers.
+    */
+  outb(p->qcntmask, p->base + QCNTMASK);
+
+  outb(p->qfullcount, p->base + FIFODEPTH);
+  outb(0, p->base + CMDOUTCNT);
+
+  /*
+   * We don't have any waiting selections or disconnected SCBs.
+   */
+  outb(SCB_LIST_NULL, p->base + WAITING_SCBH);
+  outb(SCB_LIST_NULL, p->base + DISCONNECTED_SCBH);
+
+  /*
+   * Message out buffer starts empty
+   */
+  outb(0, p->base + MSG_LEN);
+
+  /*
+   * Load the sequencer program, then re-enable the board -
+   * resetting the AIC-7770 disables it, leaving the lights
+   * on with nobody home. On the PCI bus you *may* be home,
+   * but then your mailing address is dynamically assigned
+   * so no one can find you anyway :-)
+   */
+  aic7xxx_loadseq(p);
+
+  if (p->chip_class == AIC_777x)
+  {
+    outb(ENABLE, p->base + BCTL);  /* Enable the boards BUS drivers. */
+  }
+
+  /*
+   * Unpause the sequencer before returning and enable
+   * interrupts - we shouldn't get any until the first
+   * command is sent to us by the high-level SCSI code.
+   */
+  unpause_sequencer(p, /* unpause_always */ TRUE);
+
+  return (found);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_chip_reset
+ *
+ * Description:
+ *   Perform a chip reset on the aic7xxx SCSI controller.  The controller
+ *   is paused upon return.
+ *-F*************************************************************************/
+static void
+aic7xxx_chip_reset(struct aic7xxx_host *p)
+{
+  unsigned char hcntrl;
+  int wait;
+
+  /* Retain the IRQ type across the chip reset. */
+  hcntrl = (inb(p->base + HCNTRL) & IRQMS) | INTEN;
+
+  /*
+   * For some 274x boards, we must clear the CHIPRST bit and pause
+   * the sequencer. For some reason, this makes the driver work.
+   */
+  outb(PAUSE | CHIPRST, p->base + HCNTRL);
+
+  /*
+   * In the future, we may call this function as a last resort for
+   * error handling.  Let's be nice and not do any unecessary delays.
+   */
+  wait = 1000;  /* 1 second (1000 * 1000 usec) */
+  while ((wait > 0) && ((inb(p->base + HCNTRL) & CHIPRSTACK) == 0))
+  {
+    udelay(1000);  /* 1 msec = 1000 usec */
+    wait = wait - 1;
+  }
+
+  if ((inb(p->base + HCNTRL) & CHIPRSTACK) == 0)
+  {
+    printk(KERN_INFO "aic7xxx: Chip reset not cleared; clearing manually.\n");
+  }
+
+  outb(hcntrl | PAUSE, p->base + HCNTRL);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_alloc
+ *
+ * Description:
+ *   Allocate and initialize a host structure.  Returns NULL upon error
+ *   and a pointer to a aic7xxx_host struct upon success.
+ *-F*************************************************************************/
+static struct aic7xxx_host *
+aic7xxx_alloc(Scsi_Host_Template *sht, unsigned int base, unsigned int mbase,
+    aha_chip_type chip_type, int flags, scb_data_type *scb_data)
+{
+  struct aic7xxx_host *p = NULL;
+  struct Scsi_Host *host;
+
+  /*
+   * Allocate a storage area by registering us with the mid-level
+   * SCSI layer.
+   */
+  host = scsi_register(sht, sizeof(struct aic7xxx_host));
+
+  if (host != NULL)
+  {
+    p = (struct aic7xxx_host *) host->hostdata;
+    memset(p, 0, sizeof(struct aic7xxx_host));
+    p->host = host;
+
+    if (scb_data != NULL)
+    {
+      /*
+       * We are sharing SCB data areas; use the SCB data pointer
+       * provided.
+       */
+      p->scb_data = scb_data;
+      p->flags |= SHARED_SCBDATA;
+    }
+    else
+    {
+      /*
+       * We are not sharing SCB data; allocate one.
+       */
+      p->scb_data = kmalloc(sizeof(scb_data_type), GFP_ATOMIC);
+      if (p->scb_data != NULL)
+      {
+        memset(p->scb_data, 0, sizeof(scb_data_type));
+        scbq_init (&p->scb_data->free_scbs);
+      }
+      else
+      {
+        /*
+         * For some reason we don't have enough memory.  Free the
+         * allocated memory for the aic7xxx_host struct, and return NULL.
+         */
+        scsi_unregister(host);
+        p = NULL;
+      }
+    }
+    if (p != NULL)
+    {
+      p->host_no = host->host_no;
+      p->base = base;
+      p->mbase = mbase;
+      p->maddr = NULL;
+      p->flags = flags;
+      p->chip_type = chip_type;
+      p->unpause = (inb(p->base + HCNTRL) & IRQMS) | INTEN;
+      p->pause = p->unpause | PAUSE;
+    }
+  }
+  return (p);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_free
+ *
+ * Description:
+ *   Frees and releases all resources associated with an instance of
+ *   the driver (struct aic7xxx_host *).
+ *-F*************************************************************************/
+static void
+aic7xxx_free (struct aic7xxx_host *p)
+{
+  int i;
+
+  /*
+   * We should be careful in freeing the scb_data area.  For those
+   * adapters sharing external SCB RAM(398x), there will be only one
+   * scb_data area allocated.  The flag SHARED_SCBDATA indicates if
+   * one adapter is sharing anothers SCB RAM.
+   */
+  if (!(p->flags & SHARED_SCBDATA))
+  {
+    /*
+     * Free the allocated hardware SCB space.
+     */
+    if (p->scb_data->hscbs != NULL)
+    {
+      kfree(p->scb_data->hscbs);
+    }
+    /*
+     * Free the driver SCBs.  These were allocated on an as-need
+     * basis.
+     */
+    for (i = 0; i < p->scb_data->numscbs; i++)
+    {
+      kfree(p->scb_data->scb_array[i]);
+    }
+    /*
+     * Free the hardware SCBs.
+     */
+    if (p->scb_data->hscbs != NULL)
+    {
+      kfree(p->scb_data->hscbs);
+    }
+
+    /*
+     * Free the SCB data area.
+     */
+    kfree(p->scb_data);
+  }
+  /*
+   * Free the instance of the device structure.
+   */
+  scsi_unregister(p->host);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_load_seeprom
+ *
+ * Description:
+ *   Load the seeprom and configure adapter and target settings.
+ *   Returns 1 if the load was successful and 0 otherwise.
+ *-F*************************************************************************/
+static int
+load_seeprom (struct aic7xxx_host *p, unsigned char *sxfrctl1)
+{
+  int have_seeprom = 0;
+  int i, max_targets;
+  unsigned char target_settings, scsi_conf;
+  unsigned short scarray[128];
+  struct seeprom_config *sc = (struct seeprom_config *) scarray;
+
+  if (aic7xxx_verbose)
+  {
+    printk(KERN_INFO "aic7xxx: Loading serial EEPROM...");
+  }
+  switch (p->chip_type)
+  {
+    case AIC_7770:  /* None of these adapters have seeproms. */
+    case AIC_7771:
+    case AIC_7850:
+    case AIC_7855:
+      break;
+
+    case AIC_284x:
+      have_seeprom = read_284x_seeprom(p, (struct seeprom_config *) scarray);
+      break;
+
+    case AIC_7861:
+    case AIC_7870:
+    case AIC_7871:
+    case AIC_7872:
+    case AIC_7874:
+    case AIC_7881:
+    case AIC_7882:
+    case AIC_7884:
+      have_seeprom = read_seeprom(p, p->chan_num * (sizeof(*sc)/2),
+                                  scarray, sizeof(*sc)/2, C46);
+      break;
+
+    case AIC_7860:  /* Motherboard Ultra controllers might have RAID port. */
+    case AIC_7880:
+      have_seeprom = read_seeprom(p, 0, scarray, sizeof(*sc)/2, C46);
+      if (!have_seeprom)
+      {
+        have_seeprom = read_seeprom(p, 0, scarray, sizeof(scarray)/2, C56_66);
+      }
+      break;
+
+    case AIC_7873:  /* The 3985 adapters use the 93c56 serial EEPROM. */
+    case AIC_7883:
+      have_seeprom = read_seeprom(p, p->chan_num * (sizeof(*sc)/2),
+                                  scarray, sizeof(scarray)/2, C56_66);
+      break;
+
+    default:
+      break;
   }
 
-  /*
-   * If we are not wide, forget WDTR. This makes the driver
-   * work on some cards that don't leave these fields cleared
-   * when BIOS is not installed.
-   */
-  if (p->bus_type != AIC_WIDE)
+  if (!have_seeprom)
   {
-    p->needwdtr_copy = 0;
+    if (aic7xxx_verbose)
+    {
+      printk("\naic7xxx: No SEEPROM available; using defaults.\n");
+    }
+    p->flags |= USE_DEFAULTS;
   }
-  p->needsdtr = p->needsdtr_copy;
-  p->needwdtr = p->needwdtr_copy;
-  p->orderedtag = 0;
-#if 0
-  printk("NeedSdtr = 0x%x, 0x%x\n", p->needsdtr_copy, p->needsdtr);
-  printk("NeedWdtr = 0x%x, 0x%x\n", p->needwdtr_copy, p->needwdtr);
-#endif
-  outb(ultraenable & 0xFF, ULTRA_ENB + base);
-  outb((ultraenable >> 8) & 0xFF, ULTRA_ENB + base + 1);
+  else
+  {
+    if (aic7xxx_verbose)
+    {
+      printk("done\n");
+    }
+    p->flags |= HAVE_SEEPROM;
 
-  /*
-   * Set the number of available SCBs.
-   */
-  outb(config->maxhscbs, SCBCOUNT + base);
+    /*
+     * Update the settings in sxfrctl1 to match the termination settings.
+     */
+    *sxfrctl1 = 0;
 
-  /*
-   * 2s compliment of maximum tag value.
-   */
-  i = p->maxscbs;
-  outb(-i & 0xFF, COMP_SCBCOUNT + base);
+    /*
+     * First process the settings that are different between the VLB
+     * and PCI adapter seeproms.
+     */
+    if (p->chip_class == AIC_777x)
+    {
+      /* VLB adapter seeproms */
+      if (sc->bios_control & CF284XEXTEND)
+        p->flags |= EXTENDED_TRANSLATION;
 
-  /*
-   * Set the QCNT (queue count) mask to deal with broken aic7850s that
-   * sporatically get garbage in the upper bits of their QCNT registers.
-   */
-  outb(config->qcntmask, QCNTMASK + base);
+      if (sc->adapter_control & CF284XSTERM)
+        *sxfrctl1 |= STPWEN;
+      /*
+       * The 284x SEEPROM doesn't have a max targets field.  We
+       * set it to 16 to make sure we take care of the 284x-wide
+       * adapters.  For narrow adapters, going through the extra
+       * 8 target entries will not cause any harm since they will
+       * will not be used.
+       *
+       * XXX - We should probably break out the bus detection
+       *       from the register function so we can use it here
+       *       to tell us how many targets there really are.
+       */
+      max_targets = 16;
+    }
+    else
+    {
+      /* PCI adapter seeproms */
+      if (sc->bios_control & CFEXTEND)
+        p->flags |= EXTENDED_TRANSLATION;
 
-  /*
-   * Clear the active flags - no targets are busy.
-   */
-  outb(0, ACTIVE_A + base);
-  outb(0, ACTIVE_B + base);
+      if (sc->adapter_control & CFSTERM)
+        *sxfrctl1 |= STPWEN;
 
-  /*
-   * We don't have any waiting selections or disconnected SCBs.
-   */
-  outb(SCB_LIST_NULL, WAITING_SCBH + base);
-  outb(SCB_LIST_NULL, DISCONNECTED_SCBH + base);
+      /* Limit to 16 targets just in case. */
+      max_targets = MIN(sc->max_targets & CFMAXTARG, 16);
+    }
 
-  /*
-   * Message out buffer starts empty
-   */
-  outb(0, MSG_LEN + base);
+    for (i = 0; i < max_targets; i++)
+    {
+      target_settings = (sc->device_flags[i] & CFXFER) << 4;
+      if (sc->device_flags[i] & CFSYNCH)
+        target_settings |= SOFS;
+      if (sc->device_flags[i] & CFWIDEB)
+        target_settings |= WIDEXFER;
+      if (sc->device_flags[i] & CFDISC)
+        p->discenable |= (0x01 << i);
+      outb(target_settings, p->base + TARG_SCRATCH + i);
+    }
+    outb(~(p->discenable & 0xFF), p->base + DISC_DSB);
+    outb(~((p->discenable >> 8) & 0xFF), p->base + DISC_DSB + 1);
 
-  /*
-   * Reset the SCSI bus. Is this necessary?
-   *   There may be problems for a warm boot without resetting
-   *   the SCSI bus. Either BIOS settings in scratch RAM
-   *   will not get reinitialized, or devices may stay at
-   *   previous negotiated settings (SDTR and WDTR) while
-   *   the driver will think that no negotiations have been
-   *   performed.
-   *
-   * Some devices need a long time to "settle" after a SCSI
-   * bus reset.
-   */
-  if (!aic7xxx_no_reset)
-  {
-    printk("aic7xxx: Resetting the SCSI bus...");
-    if (p->bus_type == AIC_TWIN)
+    p->scsi_id = sc->brtime_id & CFSCSIID;
+
+    scsi_conf = (p->scsi_id & 0x7);
+    if (sc->adapter_control & CFSPARITY)
+      scsi_conf |= ENSPCHK;
+    if (sc->adapter_control & CFRESETB)
+      scsi_conf |= RESET_SCSI;
+
+    if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
     {
       /*
-       * Select Channel B.
+       * We allow the operator to override ultra enable through
+       * the boot prompt.
        */
-      outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL + base);
+      if (!(sc->adapter_control & CFULTRAEN) && (aic7xxx_enable_ultra == 0))
+      {
+        /* Treat us as a non-ultra card */
+        p->flags &= ~ULTRA_ENABLED;
+      }
+    }
 
-      outb(SCSIRSTO, SCSISEQ + base);
-      udelay(1000);
-      outb(0, SCSISEQ + base);
-
-      /* Ensure we don't get a RSTI interrupt from this. */
-      outb(CLRSCSIRSTI, CLRSINT1 + base);
-      outb(CLRSCSIINT, CLRINT + base);
+    /* Set the host ID */
+    outb(scsi_conf, p->base + SCSICONF);
+    /* In case we are a wide card */
+    outb(p->scsi_id, p->base + SCSICONF + 1);
 
-     /*
-       * Select Channel A.
+    if (p->chip_class != AIC_777x)
+    {
+      /*
+       * Update the settings in sxfrctl1 to match the termination
+       * settings.
        */
-      outb((sblkctl & ~SELBUS_MASK) | SELNARROW, SBLKCTL + base);
+      *sxfrctl1 = 0;
+      configure_termination(p, sxfrctl1, sc->adapter_control,
+        (unsigned char) sc->max_targets & CFMAXTARG);
     }
-
-    outb(SCSIRSTO, SCSISEQ + base);
-    udelay(1000);
-    outb(0, SCSISEQ + base);
-
-    /* Ensure we don't get a RSTI interrupt from this. */
-    outb(CLRSCSIRSTI, CLRSINT1 + base);
-    outb(CLRSCSIINT, CLRINT + base);
-
-    aic7xxx_delay(AIC7XXX_RESET_DELAY);
-
-    printk("done.\n");
   }
-
-  /*
-   * Unpause the sequencer before returning and enable
-   * interrupts - we shouldn't get any until the first
-   * command is sent to us by the high-level SCSI code.
-   */
-  UNPAUSE_SEQUENCER(p);
-  return (found);
+  return (have_seeprom);
 }
 
 /*+F*************************************************************************
@@ -4671,17 +5695,24 @@
  *
  * Description:
  *   Try to detect and register an Adaptec 7770 or 7870 SCSI controller.
+ *
+ * XXX - This should really be called aic7xxx_probe().  A sequence of
+ *       probe(), attach()/detach(), and init() makes more sense than
+ *       one do-it-all function.  This may be useful when (and if) the
+ *       mid-level SCSI code is overhauled.
  *-F*************************************************************************/
 int
 aic7xxx_detect(Scsi_Host_Template *template)
 {
-  int found = 0, slot, base;
-  unsigned char irq = 0;
+  int found = 0;
+  aha_status_type adapter_bios;
+  aha_chip_class_type chip_class;
+  aha_chip_type chip_type;
+  int slot, base;
+  int chan_num = 0;
+  unsigned char hcntrl, sxfrctl1, sblkctl, hostconf, irq = 0;
   int i;
-  struct aic7xxx_host_config config;
-
-  template->proc_dir = &proc_scsi_aic7xxx;
-  config.chan_num = 0;
+  struct aic7xxx_host *p;
 
   /*
    * Since we may allow sharing of IRQs, it is imperative
@@ -4695,6 +5726,10 @@
     aic7xxx_boards[i] = NULL;
   }
 
+  template->proc_dir = &proc_scsi_aic7xxx;
+  template->name = aic7xxx_info(NULL);
+  template->sg_tablesize = AIC7XXX_MAX_SG;
+
   /*
    * Initialize the spurious count to 0.
    */
@@ -4716,33 +5751,174 @@
       continue;
     }
 
-    config.type = aic7xxx_probe(slot, HID0 + base, &(config.bios));
-    if (config.type != AIC_NONE)
+    chip_type = aic7xxx_probe(slot, base + HID0, &(adapter_bios));
+    if (chip_type != AIC_NONE)
     {
+
+      switch (chip_type)
+      {
+        case AIC_7770:
+        case AIC_7771:
+          printk("aic7xxx: <%s> at EISA %d\n",
+                 board_names[chip_type], slot);
+          break;
+        case AIC_284x:
+          printk("aic7xxx: <%s> at VLB %d\n",
+                 board_names[chip_type], slot);
+          break;
+        default:
+          break;
+      }
+
       /*
        * We found a card, allow 1 spurious interrupt.
        */
       aic7xxx_spurious_count = 1;
 
       /*
-       * We "find" a AIC-7770 if we locate the card
-       * signature and we can set it up and register
-       * it with the kernel without incident.
+       * Pause the card preserving the IRQ type.  Allow the operator
+       * to override the IRQ trigger.
        */
-      config.chip_type = AIC_777x;
-      config.base = base;
-      config.mbase = 0;
-      config.irq = irq;
-      config.parity = AIC_ENABLED;
-      config.low_term = AIC_UNKNOWN;
-      config.high_term = AIC_UNKNOWN;
-      config.flags = 0;
-      if (aic7xxx_extended)
-        config.flags |= EXTENDED_TRANSLATION;
-      config.bus_speed = DFTHRSH_100;
-      config.busrtime = BOFF;
-      found += aic7xxx_register(template, &config);
+      if (aic7xxx_irq_trigger == 1)
+        hcntrl = IRQMS;  /* Level */
+      else if (aic7xxx_irq_trigger == 0)
+        hcntrl = 0;  /* Edge */
+      else
+        hcntrl = inb(base + HCNTRL) & IRQMS;  /* Default */
+      outb(hcntrl | PAUSE, base + HCNTRL);
+
+      irq = inb(INTDEF + base) & 0x0F;
+      switch (irq)
+      {
+        case 9:
+        case 10:
+        case 11:
+        case 12:
+        case 14:
+        case 15:
+          break;
+
+        default:
+          printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ "
+          "level, ignoring.\n");
+          irq = 0;
+          break;
+      }
+
+      if (irq != 0)
+      {
+        p = aic7xxx_alloc(template, base, 0, chip_type, 0, NULL);
+        if (p == NULL)
+        {
+          printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
+          continue;
+        }
+        p->irq = irq & 0x0F;
+        p->chip_class = AIC_777x;
+#ifdef AIC7XXX_PAGE_ENABLE
+        p->flags |= PAGE_ENABLED;
+#endif
+        p->instance = found;
+        if (aic7xxx_extended)
+        {
+          p->flags |= EXTENDED_TRANSLATION;
+        }
+        aic7xxx_chip_reset(p);
+
+        switch (p->chip_type)
+        {
+          case AIC_7770:
+          case AIC_7771:
+          {
+            unsigned char biosctrl = inb(p->base + HA_274_BIOSCTRL);
+
+            /*
+             * Get the primary channel information.  Right now we don't
+             * do anything with this, but someday we will be able to inform
+             * the mid-level SCSI code which channel is primary.
+             */
+            if (biosctrl & CHANNEL_B_PRIMARY)
+            {
+              p->flags |= FLAGS_CHANNEL_B_PRIMARY;
+            }
+
+            if ((biosctrl & BIOSMODE) == BIOSDISABLED)
+            {
+              p->flags |= USE_DEFAULTS;
+            }
+            break;
+          }
 
+          case AIC_284x:
+            if (!load_seeprom(p, &sxfrctl1))
+            {
+              if (aic7xxx_verbose)
+                printk(KERN_INFO "aic7xxx: SEEPROM not available.\n");
+            }
+            break;
+
+          default:  /* Won't get here. */
+            break;
+        }
+        printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%x, IRQ %d (%s), ",
+               (p->flags & USE_DEFAULTS) ? "dis" : "en", p->base, p->irq,
+               (p->pause & IRQMS) ? "level sensitive" : "edge triggered");
+        /*
+         * Check for Rev C or E boards. Rev E boards can supposedly have
+         * more than 4 SCBs, while the Rev C boards are limited to 4 SCBs.
+         * It's still not clear extactly what is different about the Rev E
+         * boards, but we think it allows 8 bit entries in the QOUTFIFO to
+         * support "paging" SCBs (more than 4 commands can be active at once).
+         *
+         * The Rev E boards have a read/write autoflush bit in the
+         * SBLKCTL register, while in the Rev C boards it is read only.
+         */
+        sblkctl = inb(p->base + SBLKCTL) ^ AUTOFLUSHDIS;
+        outb(sblkctl, p->base + SBLKCTL);
+        if (inb(p->base + SBLKCTL) == sblkctl)
+        {
+          /*
+           * We detected a Rev E board, we allow paging on this board.
+           */
+          printk("Revision >= E\n");
+          outb(sblkctl & ~AUTOFLUSHDIS, base + SBLKCTL);
+        }
+        else
+        {
+          /* Do not allow paging. */
+          p->flags &= ~PAGE_ENABLED;
+          printk("Revision <= C\n");
+        }
+
+        if (aic7xxx_verbose)
+          printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
+                 (p->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
+
+        /*
+         * Set the FIFO threshold and the bus off time.
+         */
+        hostconf = inb(p->base + HOSTCONF);
+        outb(hostconf & DFTHRSH, p->base + BUSSPD);
+        outb((hostconf << 2) & BOFF, p->base + BUSTIME);
+
+        /*
+         * Try to initialize the card and register it with the kernel.
+         */
+        if (aic7xxx_register(template, p))
+        {
+          /*
+           * We successfully found a board and registered it.
+           */
+          found = found + 1;
+        }
+        else
+        {
+          /*
+           * Something went wrong; release and free all resources.
+           */
+          aic7xxx_free(p);
+        }
+      }
       /*
        * Disallow spurious interrupts.
        */
@@ -4758,15 +5934,15 @@
   {
     struct
     {
-      unsigned short vendor_id;
-      unsigned short device_id;
-      aha_type       card_type;
-      aha_chip_type  chip_type;
+      unsigned short      vendor_id;
+      unsigned short      device_id;
+      aha_chip_type       chip_type;
+      aha_chip_class_type chip_class;
     } const aic7xxx_pci_devices[] = {
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7850, AIC_7850, AIC_785x},
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AIC_7855, AIC_785x},
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AIC_7860, AIC_785x},
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AIC_7861, AIC_785x},
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AIC_7860, AIC_786x},
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AIC_7861, AIC_786x},
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AIC_7870, AIC_787x},
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AIC_7871, AIC_787x},
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AIC_7872, AIC_787x},
@@ -4779,14 +5955,14 @@
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AIC_7884, AIC_788x}
     };
 
-    int error;
+    int error, flags;
     int done = 0;
     unsigned int iobase, mbase;
     unsigned short index = 0;
     unsigned char pci_bus, pci_device_fn;
-    unsigned int  csize_lattime;
-    unsigned int  class_revid;
-    unsigned int  devconfig;
+    unsigned char ultra_enb = 0;
+    unsigned int  devconfig, class_revid;
+    scb_data_type *shared_scb_data = NULL;
     char rev_id[] = {'B', 'C', 'D'};
 
     for (i = 0; i < NUMBER(aic7xxx_pci_devices); i++)
@@ -4803,36 +5979,33 @@
         }
         else  /* Found an Adaptec PCI device. */
         {
-          config.type = aic7xxx_pci_devices[i].card_type;
-          config.chip_type = aic7xxx_pci_devices[i].chip_type;
-          config.chan_num = 0;
-          config.bios = AIC_ENABLED;  /* Assume bios is enabled. */
-          config.flags = 0;
-          config.busrtime = 40;
-          switch (config.type)
+          chip_class = aic7xxx_pci_devices[i].chip_class;
+          chip_type = aic7xxx_pci_devices[i].chip_type;
+          chan_num = 0;
+          flags = 0;
+          switch (aic7xxx_pci_devices[i].chip_type)
           {
             case AIC_7850:
             case AIC_7855:
-            case AIC_7860:
-            case AIC_7861:
-              config.bios = AIC_DISABLED;
-              config.flags |= USE_DEFAULTS;
-              config.bus_speed = DFTHRSH_100;
+              flags |= USE_DEFAULTS;
               break;
 
             case AIC_7872:  /* 3940 */
             case AIC_7882:  /* 3940-Ultra */
-              config.chan_num = number_of_3940s & 0x1;  /* Has 2 controllers */
+              flags |= MULTI_CHANNEL;
+              chan_num = number_of_3940s & 0x1;  /* Has 2 controllers */
               number_of_3940s++;
               break;
 
             case AIC_7873:  /* 3985 */
             case AIC_7883:  /* 3985-Ultra */
-              config.chan_num = number_of_3985s;  /* Has 3 controllers */
+              chan_num = number_of_3985s;  /* Has 3 controllers */
+              flags |= MULTI_CHANNEL;
               number_of_3985s++;
               if (number_of_3985s == 3)
               {
                 number_of_3985s = 0;
+                shared_scb_data = NULL;
               }
               break;
 
@@ -4849,39 +6022,165 @@
                                             PCI_INTERRUPT_LINE, &irq);
           error += pcibios_read_config_dword(pci_bus, pci_device_fn,
                                             PCI_BASE_ADDRESS_1, &mbase);
+          error += pcibios_read_config_dword(pci_bus, pci_device_fn,
+                                             DEVCONFIG, &devconfig);
+          error += pcibios_read_config_dword(pci_bus, pci_device_fn,
+                                             CLASS_PROGIF_REVID, &class_revid);
+
+          printk("aic7xxx: <%s> at PCI %d\n",
+                 board_names[chip_type], PCI_SLOT(pci_device_fn));
 
           /*
-           * The first bit of PCI_BASE_ADDRESS_0 is always set, so
+           * The first bit (LSB) of PCI_BASE_ADDRESS_0 is always set, so
            * we mask it off.
            */
           iobase &= PCI_BASE_ADDRESS_IO_MASK;
 
+          p = aic7xxx_alloc(template, iobase, mbase, chip_type, flags,
+                            shared_scb_data);
+
+          if (p == NULL)
+          {
+            printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
+            continue;
+          }
+
+          /* Remember to set the channel number, irq, and chip class. */
+          p->chan_num = chan_num;
+          p->irq = irq;
+          p->chip_class = chip_class;
+#ifdef AIC7XXX_PAGE_ENABLE
+          p->flags |= PAGE_ENABLED;
+#endif
+          p->instance = found;
+
           /*
-           * Read the PCI burst size and latency timer.
+           * Remember how the card was setup in case there is no seeprom.
            */
-          error += pcibios_read_config_dword(pci_bus, pci_device_fn,
-                                             CSIZE_LATTIME, &csize_lattime);
-          printk(KERN_INFO "aic7xxx: BurstLen = %d DWDs, Latency Timer = %d "
-                 "PCLKS\n", (int) (csize_lattime & CACHESIZE),
-                 (csize_lattime >> 8) & 0x000000ff);
+          p->scsi_id = inb(p->base + SCSIID) & OID;
+          if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
+          {
+            p->flags |= ULTRA_ENABLED;
+            ultra_enb = inb(p->base + SXFRCTL1) & FAST20;
+          }
+	  sxfrctl1 = inb(p->base + SXFRCTL1) & STPWEN;
 
-          error += pcibios_read_config_dword(pci_bus, pci_device_fn,
-                                             CLASS_PROGIF_REVID, &class_revid);
-          if ((class_revid & DEVREVID) < 3)
+          aic7xxx_chip_reset(p);
+
+#ifdef AIC7XXX_USE_EXT_SCBRAM
+          if (devconfig & RAMPSM)
+          {
+            printk(KERN_INFO "aic7xxx: External RAM detected; enabling RAM "
+                   "access.\n");
+            /*
+             * XXX - Assume 9 bit SRAM and enable parity checking.
+             */
+            devconfig |= EXTSCBPEN;
+
+            /*
+             * XXX - Assume fast SRAM and only enable 2 cycle access if we
+             *       are sharing the SRAM across multiple adapters (398x).
+             */
+            if ((devconfig & MPORTMODE) == 0)
+            {
+              devconfig |= EXTSCBTIME;
+            }
+            devconfig &= ~SCBRAMSEL;
+            pcibios_write_config_dword(pci_bus, pci_device_fn,
+                                       DEVCONFIG, devconfig);
+          }
+#endif
+
+          if ((p->flags & USE_DEFAULTS) == 0)
           {
-            printk(KERN_INFO "aic7xxx: %s Rev %c.\n", board_names[config.type],
-                   rev_id[class_revid & DEVREVID]);
+            load_seeprom(p, &sxfrctl1);
           }
 
-          error += pcibios_read_config_dword(pci_bus, pci_device_fn,
-                                             DEVCONFIG, &devconfig);
-          if (error)
+          /*
+           * Take the LED out of diagnostic mode
+           */
+          sblkctl = inb(p->base + SBLKCTL);
+          outb((sblkctl & ~(DIAGLEDEN | DIAGLEDON)), p->base + SBLKCTL);
+
+          /*
+           * We don't know where this is set in the SEEPROM or by the
+           * BIOS, so we default to 100%.
+           */
+          outb(DFTHRSH_100, p->base + DSPCISTATUS);
+
+          if (p->flags & USE_DEFAULTS)
           {
-            panic("aic7xxx: (aic7xxx_detect) Error %d reading PCI registers.\n",
-                  error);
+            int j;
+            /*
+             * Default setup; should only be used if the adapter does
+             * not have a SEEPROM.
+             */
+            /*
+             * Check the target scratch area to see if someone set us
+             * up already.  We are previously set up if the scratch
+             * area contains something other than all zeroes and ones.
+             */
+            for (j = TARG_SCRATCH; j < 0x60; j++)
+            {
+              if (inb(p->base + j) != 0x00)      /* Check for all zeroes. */
+                break;
+            }
+            if (j == TARG_SCRATCH)
+            {
+              for (j = TARG_SCRATCH; j < 0x60; j++)
+              {
+                if (inb(p->base + 1) != 0xFF)    /* Check for all ones. */
+                  break;
+              }
+            }
+            if ((j != 0x60) && (p->scsi_id != 0))
+            {
+              p->flags &= ~USE_DEFAULTS;
+              if (aic7xxx_verbose)
+              {
+                printk(KERN_INFO "aic7xxx: Using leftover BIOS values.\n");
+              }
+            }
+            else
+            {
+              if (aic7xxx_verbose)
+              {
+                printk(KERN_INFO "aic7xxx: No BIOS found; using default "
+                       "settings.\n");
+              }
+              /*
+               * Assume only one connector and always turn on
+               * termination.
+               */
+              sxfrctl1 = STPWEN;
+              p->scsi_id = 7;
+            }
+            outb((p->scsi_id & HSCSIID) | ENSPCHK | RESET_SCSI,
+                 p->base + SCSICONF);
+            /* In case we are a wide card. */
+            outb(p->scsi_id, p->base + SCSICONF + 1);
+            if ((ultra_enb == 0) && ((p->flags & USE_DEFAULTS) == 0))
+            {
+              /*
+               * If there wasn't a BIOS or the board wasn't in this mode
+               * to begin with, turn off Ultra.
+               */
+              p->flags &= ~ULTRA_ENABLED;
+            }
           }
 
-          printk(KERN_INFO "aic7xxx: devconfig = 0x%x.\n", devconfig);
+          /*
+           * Print some additional information about the adapter.
+           */
+          printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%x, "
+                 "IO Mem 0x%x, IRQ %d",
+                 (p->flags & USE_DEFAULTS) ? "dis" : "en",
+                 p->base, p->mbase, p->irq);
+          if ((class_revid & DEVREVID) < 3)
+          {
+            printk(", Revision %c", rev_id[class_revid & DEVREVID]);
+          }
+          printk("\n");
 
           /*
            * I don't think we need to bother with allowing
@@ -4890,58 +6189,57 @@
            */
           aic7xxx_spurious_count = 1;
 
-          config.base = iobase;
-          config.mbase = mbase;
-          config.irq = irq;
-          config.parity = AIC_ENABLED;
-          config.low_term = AIC_UNKNOWN;
-          config.high_term = AIC_UNKNOWN;
           if (aic7xxx_extended)
-            config.flags |= EXTENDED_TRANSLATION;
-#ifdef AIC7XXX_SHARE_SCBs
-          if (devconfig & RAMPSM)
-#else
-          if ((devconfig & RAMPSM) && (config.type != AIC_7873) &&
-              (config.type != AIC_7883))
-#endif
+            p->flags |= EXTENDED_TRANSLATION;
+
+          if (aic7xxx_verbose)
+            printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
+                   (p->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
+
+          /*
+           * Put our termination setting into sxfrctl1 now that the
+           * generic initialization is complete.
+           */
+          sxfrctl1 |= inb(p->base + SXFRCTL1);
+          outb(sxfrctl1, p->base + SXFRCTL1);
+
+          if (aic7xxx_register(template, p) == 0)
+          {
+            aic7xxx_free(p);
+          }
+          else
           {
+            found = found + 1;
+
+#ifdef AIC7XXX_USE_EXT_SCBRAM
             /*
-             * External SRAM present.  The probe will walk the SCBs to see
-             * how much SRAM we have and set the number of SCBs accordingly.
-             * We have to turn off SCBRAMSEL to access the external SCB
-             * SRAM.
-             *
-             * It seems that early versions of the aic7870 didn't use these
-             * bits, hence the hack for the 3940 above.  I would guess that
-             * recent 3940s using later aic7870 or aic7880 chips do actually
-             * set RAMPSM.
+             * Set the shared SCB data once we've successfully probed a
+             * 398x adapter.
              *
-             * The documentation isn't clear, but it sounds like the value
-             * written to devconfig must not have RAMPSM set.  The second
-             * sixteen bits of the register are R/O anyway, so it shouldn't
-             * affect RAMPSM either way.
+             * Note that we can only do this if the use of external
+             * SCB RAM is enabled.
              */
-            printk(KERN_INFO "aic7xxx: External RAM detected; enabling RAM "
-                   "access.\n");
-            devconfig &= ~(RAMPSM | SCBRAMSEL);
-            pcibios_write_config_dword(pci_bus, pci_device_fn,
-                                       DEVCONFIG, devconfig);
+            if ((p->chip_type == AIC_7873) || (p->chip_type == AIC_7883))
+            {
+              if (shared_scb_data == NULL)
+              {
+                shared_scb_data = p->scb_data;
+              }
+            }
+#endif
           }
-          found += aic7xxx_register(template, &config);
 
+          index++;
           /*
            * Disable spurious interrupts.
            */
           aic7xxx_spurious_count = 0;
-
-          index++;
         }  /* Found an Adaptec PCI device. */
       }
     }
   }
 #endif CONFIG_PCI
 
-  template->name = aic7xxx_info(NULL);
   return (found);
 }
 
@@ -4957,45 +6255,45 @@
 aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
     struct aic7xxx_scb *scb)
 {
-  unsigned int addr; /* must be 32 bits */
   unsigned short mask;
+  struct aic7xxx_hwscb *hscb;
 
   mask = (0x01 << TARGET_INDEX(cmd));
+  hscb = scb->hscb;
+
   /*
    * Setup the control byte if we need negotiation and have not
    * already requested it.
    */
-#ifdef AIC7XXX_TAGGED_QUEUEING
-  if (cmd->device->tagged_queue)
+  if (p->discenable & mask)
   {
-    cmd->tag = scb->tag;
-    cmd->device->current_tag = scb->tag;
-    scb->control |= TAG_ENB;
-    p->device_status[TARGET_INDEX(cmd)].commands_sent++;
-    if (p->device_status[TARGET_INDEX(cmd)].commands_sent == 200)
-    {
-      scb->control |= 0x02;
-      p->device_status[TARGET_INDEX(cmd)].commands_sent = 0;
-    }
-#if 0
-    if (p->orderedtag & mask)
+    hscb->control |= DISCENB;
+#ifdef AIC7XXX_TAGGED_QUEUEING
+    if (cmd->device->tagged_queue)
     {
-      scb->control |= 0x02;
-      p->orderedtag = p->orderedtag & ~mask;
+      cmd->tag = hscb->tag;
+      p->device_status[TARGET_INDEX(cmd)].commands_sent++;
+      if (p->device_status[TARGET_INDEX(cmd)].commands_sent < 75)
+      {
+        hscb->control |= MSG_SIMPLE_Q_TAG;
+      }
+      else
+      {
+        hscb->control |= MSG_ORDERED_Q_TAG;
+        p->device_status[TARGET_INDEX(cmd)].commands_sent = 0;
+      }
     }
-#endif
-  }
-#endif
-  if (p->discenable & mask)
-  {
-    scb->control |= DISCENB;
+#endif  /* Tagged queueing */
   }
+
   if ((p->needwdtr & mask) && !(p->wdtr_pending & mask))
   {
     p->wdtr_pending |= mask;
-    scb->control |= NEEDWDTR;
+    hscb->control |= MK_MESSAGE;
+    scb->flags |= SCB_MSGOUT_WDTR;
 #if 0
-    printk("aic7xxx: Sending WDTR request to target %d.\n", cmd->target);
+    printk("scsi%d: Sending WDTR request to target %d.\n",
+           p->host_no, cmd->target);
 #endif
   }
   else
@@ -5003,19 +6301,20 @@
     if ((p->needsdtr & mask) && !(p->sdtr_pending & mask))
     {
       p->sdtr_pending |= mask;
-      scb->control |= NEEDSDTR;
+      hscb->control |= MK_MESSAGE;
+      scb->flags |= SCB_MSGOUT_SDTR;
 #if 0
-      printk("aic7xxx: Sending SDTR request to target %d.\n", cmd->target);
+      printk("scsi%d: Sending SDTR request to target %d.\n",
+             p->host_no, cmd->target);
 #endif
     }
   }
-
 #if 0
   printk("aic7xxx: (build_scb) Target %d, cmd(0x%x) size(%u) wdtr(0x%x) "
          "mask(0x%x).\n",
 	 cmd->target, cmd->cmnd[0], cmd->cmd_len, p->needwdtr, mask);
 #endif
-  scb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
+  hscb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
 	((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07);
 
   /*
@@ -5029,9 +6328,8 @@
    * XXX - this relies on the host data being stored in a
    *       little-endian format.
    */
-  addr = VIRT_TO_BUS(cmd->cmnd);
-  scb->SCSI_cmd_length = cmd->cmd_len;
-  memcpy(scb->SCSI_cmd_pointer, &addr, sizeof(scb->SCSI_cmd_pointer));
+  hscb->SCSI_cmd_length = cmd->cmd_len;
+  hscb->SCSI_cmd_pointer = VIRT_TO_BUS(cmd->cmnd);
 
   if (cmd->use_sg)
   {
@@ -5051,15 +6349,16 @@
       scb->sg_list[i].address = VIRT_TO_BUS(sg[i].address);
       scb->sg_list[i].length = (unsigned int) sg[i].length;
     }
-    scb->SG_segment_count = cmd->use_sg;
-    addr = VIRT_TO_BUS(scb->sg_list);
-    memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer));
-    memcpy(scb->data_pointer, &(scb->sg_list[0].address),
-           sizeof(scb->data_pointer));
-    scb->data_count = scb->sg_list[0].length;
+    hscb->SG_list_pointer = VIRT_TO_BUS(scb->sg_list);
+    hscb->SG_segment_count = cmd->use_sg;
+    scb->sg_count = hscb->SG_segment_count;
+
+    /* Copy the first SG into the data pointer area. */
+    hscb->data_pointer = scb->sg_list[0].address;
+    hscb->data_count = scb->sg_list[0].length | (SCB_LIST_NULL << 24);
 #if 0
     printk("aic7xxx: (build_scb) SG segs(%d), length(%u), sg[0].length(%d).\n",
-           cmd->use_sg, aic7xxx_length(cmd, 0), scb->data_count);
+           cmd->use_sg, aic7xxx_length(cmd, 0), hscb->data_count);
 #endif
   }
   else
@@ -5068,28 +6367,23 @@
   printk("aic7xxx: (build_scb) Creating scatterlist, addr(0x%lx) length(%d).\n",
 	(unsigned long) cmd->request_buffer, cmd->request_bufflen);
 #endif
-    if (cmd->request_bufflen == 0)
+    if (cmd->request_bufflen)
     {
-      /*
-       * In case the higher level SCSI code ever tries to send a zero
-       * length command, ensure the SCB indicates no data.  The driver
-       * will interpret a zero length command as a Bus Device Reset.
-       */
-      scb->SG_segment_count = 0;
-      memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer));
-      memset(scb->data_pointer, 0, sizeof(scb->data_pointer));
-      scb->data_count = 0;
+      hscb->SG_segment_count = 1;
+      scb->sg_count = 1;
+      scb->sg_list[0].address = VIRT_TO_BUS(cmd->request_buffer);
+      scb->sg_list[0].length = cmd->request_bufflen;
+      hscb->SG_list_pointer = VIRT_TO_BUS(&scb->sg_list[0]);
+      hscb->data_count = scb->sg_list[0].length | (SCB_LIST_NULL << 24);
+      hscb->data_pointer = VIRT_TO_BUS(cmd->request_buffer);
     }
     else
     {
-      scb->SG_segment_count = 1;
-      scb->sg_list[0].address = VIRT_TO_BUS(cmd->request_buffer);
-      scb->sg_list[0].length = cmd->request_bufflen;
-      addr = VIRT_TO_BUS(&scb->sg_list[0]);
-      memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer));
-      scb->data_count = scb->sg_list[0].length;
-      addr = VIRT_TO_BUS(cmd->request_buffer);
-      memcpy(scb->data_pointer, &addr, sizeof(scb->data_pointer));
+      hscb->SG_segment_count = 0;
+      scb->sg_count = 0;
+      hscb->SG_list_pointer = 0;
+      hscb->data_pointer = 0;
+      hscb->data_count = SCB_LIST_NULL << 24;
     }
   }
 }
@@ -5107,7 +6401,6 @@
   long processor_flags;
   struct aic7xxx_host *p;
   struct aic7xxx_scb *scb;
-  u_char curscb, intstat;
 
   p = (struct aic7xxx_host *) cmd->host->hostdata;
   if (p->host != cmd->host)
@@ -5139,34 +6432,21 @@
 	cmd->lun & 0x07);
 #endif
 
-  /*
-   * This is a critical section, since we don't want the interrupt
-   * routine mucking with the host data or the card.  For this reason
-   * it is nice to know that this function can only be called in one
-   * of two ways from scsi.c  First, as part of a routine queue command,
-   * in which case, the irq for our card is disabled before this
-   * function is called.  This doesn't help us if there is more than
-   * one card using more than one IRQ in our system, therefore, we
-   * should disable all interrupts on these grounds alone.  Second,
-   * this can be called as part of the scsi_done routine, in which case
-   * we are in the aic7xxx_isr routine already and interrupts are
-   * disabled, therefore we should saveflags first, then disable the
-   * interrupts, do our work, then restore the CPU flags. If it weren't
-   * for the possibility of more than one card using more than one IRQ
-   * in our system, we wouldn't have to touch the interrupt flags at all.
-   */
-  save_flags(processor_flags);
-  cli();
-
+  if (p->device_status[TARGET_INDEX(cmd)].active_cmds
+      > cmd->device->queue_depth)
+  {
+    printk(KERN_WARNING "(scsi%d:%d:%d) Commands queued exceeds queue depth\n",
+           p->host_no, cmd->target, cmd->channel);
+  }
   scb = aic7xxx_allocate_scb(p);
   if (scb == NULL)
   {
-    panic("aic7xxx: (aic7xxx_free) Couldn't find a free SCB.\n");
+    panic("aic7xxx: (aic7xxx_queue) Couldn't find a free SCB.\n");
   }
   else
   {
     scb->cmd = cmd;
-    aic7xxx_position(cmd) = scb->tag;
+    aic7xxx_position(cmd) = scb->hscb->tag;
 #if 0
     debug_scb(scb);
 #endif;
@@ -5178,14 +6458,14 @@
     aic7xxx_buildscb(p, cmd, scb);
 
 #if 0
-    if (scb != (p->scb_array[scb->position]))
+    if (scb != (p->scb_data->scb_array[scb->hscb->tag]))
     {
       printk("aic7xxx: (queue) Address of SCB by position does not match SCB "
              "address.\n");
     }
     printk("aic7xxx: (queue) SCB pos(%d) cmdptr(0x%x) state(%d) freescb(0x%x)\n",
-	   scb->position, (unsigned int) scb->cmd,
-	   scb->state, (unsigned int) p->free_scb);
+	   scb->hscb->tag, (unsigned int) scb->cmd,
+	   scb->flags, (unsigned int) p->free_scb);
 #endif
 
     /*
@@ -5200,70 +6480,28 @@
     cmd->host_scribble = NULL;
     memset(&cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
 
-    if (scb->position != SCB_LIST_NULL)
-    {
-      /* We've got a valid slot, yeah! */
-      if (p->flags & IN_ISR)
-      {
-        scbq_insert_tail(&p->assigned_scbs, scb);
-        scb->state |= SCB_ASSIGNEDQ;
-      }
-      else
-      {
-        /*
-         * Pause the sequencer so we can play with its registers -
-         * wait for it to acknowledge the pause.
-         *
-         * XXX - should the interrupts be left on while doing this?
-         */
-        PAUSE_SEQUENCER(p);
-        intstat = inb(INTSTAT + p->base);
-
-        /*
-         * Save the SCB pointer and put our own pointer in - this
-         * selects one of the four banks of SCB registers. Load
-         * the SCB, then write its pointer into the queue in FIFO
-         * and restore the saved SCB pointer.
-         */
-        curscb = inb(SCBPTR + p->base);
-        outb(scb->position, SCBPTR + p->base);
-        aic7xxx_putscb(p, scb);
-        outb(curscb, SCBPTR + p->base);
-        outb(scb->position, QINFIFO + p->base);
-        scb->state |= SCB_ACTIVE;
+    scb->flags |= SCB_ACTIVE | SCB_WAITINGQ;
 
-        /*
-         * Guard against unpausing the sequencer if there is an interrupt
-         * waiting to happen.
-         */
-        if (!(intstat & (BRKADRINT | SEQINT | SCSIINT)))
-        {
-          UNPAUSE_SEQUENCER(p);
-        }
-      }
-    }
-    else
+    save_flags(processor_flags);
+    cli();
+    scbq_insert_tail(&p->waiting_scbs, scb);
+    if ((p->flags & (IN_ISR | IN_TIMEOUT)) == 0)
     {
-      scb->state |= SCB_WAITINGQ;
-      scbq_insert_tail(&p->waiting_scbs, scb);
-      if (!(p->flags & IN_ISR))
-      {
-        aic7xxx_run_waiting_queues(p);
-      }
+      aic7xxx_run_waiting_queues(p);
     }
 
+    restore_flags(processor_flags);
 #if 0
     printk("aic7xxx: (queue) After - cmd(0x%lx) scb->cmd(0x%lx) pos(%d).\n",
-           (long) cmd, (long) scb->cmd, scb->position);
+           (long) cmd, (long) scb->cmd, scb->hscb->tag);
 #endif;
-    restore_flags(processor_flags);
   }
   return (0);
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_abort_reset
+ *   aic7xxx_bus_device_reset
  *
  * Description:
  *   Abort or reset the current SCSI command(s).  If the scb has not
@@ -5275,204 +6513,257 @@
 static int
 aic7xxx_bus_device_reset(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
 {
-  struct aic7xxx_scb  *scb;
+  struct aic7xxx_scb   *scb;
+  struct aic7xxx_hwscb *hscb;
   unsigned char bus_state;
-  int base, result = -1;
+  int result = -1;
   char channel;
 
-  scb = (p->scb_array[aic7xxx_position(cmd)]);
-  base = p->base;
+  scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
+  hscb = scb->hscb;
+
+  /*
+   * Ensure that the card doesn't do anything behind our back.
+   * Also make sure that we didn't just miss an interrupt that
+   * could affect this abort/reset.
+   */
+  pause_sequencer(p);
+  while (inb(p->base + INTSTAT) & INT_PEND);
+  {
+    aic7xxx_isr(p->irq, (void *) NULL, (void *) NULL);
+    pause_sequencer(p);
+  } 
+  if ((cmd != scb->cmd) || ((scb->flags & SCB_ACTIVE) == 0))
+  {
+    result = SCSI_RESET_NOT_RUNNING;
+    unpause_sequencer(p, /* unpause_always */ TRUE);
+    return(result);
+  }
+
+
+  printk(KERN_WARNING "(scsi%d:%d:%d) Abort_reset, scb flags 0x%x, ",
+         p->host_no, TC_OF_SCB(scb), scb->flags);
+  bus_state = inb(p->base + LASTPHASE);
 
-  channel = scb->target_channel_lun & SELBUSB ? 'B': 'A';
-  if ((cmd == scb->cmd) && (scb->state & SCB_IN_PROGRESS))
+  switch (bus_state)
   {
+    case P_DATAOUT:
+      printk("Data-Out phase, ");
+      break;
+    case P_DATAIN:
+      printk("Data-In phase, ");
+      break;
+    case P_COMMAND:
+      printk("Command phase, ");
+      break;
+    case P_MESGOUT:
+      printk("Message-Out phase, ");
+      break;
+    case P_STATUS:
+      printk("Status phase, ");
+      break;
+    case P_MESGIN:
+      printk("Message-In phase, ");
+      break;
+    default:
+      /*
+       * We're not in a valid phase, so assume we're idle.
+       */
+      printk("while idle, LASTPHASE = 0x%x, ", bus_state);
+      break;
+  }
+  printk("SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 0x%x\n",
+         inb(p->base + SCSISIGI),
+         inb(p->base + SEQADDR0) | (inb(p->base + SEQADDR1) << 8),
+         inb(p->base + SSTAT0), inb(p->base + SSTAT1));
 
-    if (scb->state & SCB_IN_PROGRESS)
+  channel = hscb->target_channel_lun & SELBUSB ? 'B': 'A';
+  /*
+   * Determine our course of action.
+   */
+  if (scb->flags & SCB_ABORT)
+  {
+    /*
+     * Been down this road before; do a full bus reset.
+     */
+    scb->flags |= SCB_RECOVERY_SCB;
+    unpause_sequencer(p, /* unpause_always */ TRUE);
+    result = -1;
+  }
+#if 0
+  else if (hscb->control & TAG_ENB)
     {
       /*
-       * Ensure that the card doesn't do anything
-       * behind our back.
+       * We could be starving this command; try sending and ordered tag
+       * command to the target we come from.
        */
-      PAUSE_SEQUENCER(p);
+      scb->flags |= SCB_SENTORDEREDTAG | SCB_RECOVERY_SCB;
+      p->orderedtag = p->orderedtag | 0xFF;
+      result = SCSI_RESET_PENDING;
+      unpause_sequencer(p, /* unpause_always */ TRUE);
+      printk(KERN_WARNING "scsi%d: Abort_reset, odered tag queued.\n",
+             p->host_no);
+    }
+#endif
+  else
+  {
+    unsigned char active_scb_index, saved_scbptr;
+    struct aic7xxx_scb *active_scb;
 
-      printk(KERN_WARNING "aic7xxx: (abort_reset) scb state 0x%x, ", scb->state);
-      bus_state = inb(LASTPHASE + p->base);
+    /*
+     * Send an Abort Message:
+     * The target that is holding up the bus may not be the same as
+     * the one that triggered this timeout (different commands have
+     * different timeout lengths).  Our strategy here is to queue an
+     * abort message to the timed out target if it is disconnected.
+     * Otherwise, if we have an active target we stuff the message buffer
+     * with an abort message and assert ATN in the hopes that the target
+     * will let go of the bus and go to the mesgout phase.  If this
+     * fails, we'll get another timeout a few seconds later which will
+     * attempt a bus reset.
+     */
+    saved_scbptr = inb(p->base + SCBPTR);
+    active_scb_index = inb(p->base + SCB_TAG);
+    active_scb = p->scb_data->scb_array[active_scb_index];
 
-      switch (bus_state)
+    if (bus_state != P_BUSFREE)
+    {
+      if (active_scb_index >= p->scb_data->numscbs)
       {
-	case P_DATAOUT:
-          printk("Data-Out phase, ");
-          break;
-	case P_DATAIN:
-          printk("Data-In phase, ");
-          break;
-	case P_COMMAND:
-          printk("Command phase, ");
-          break;
-	case P_MESGOUT:
-          printk("Message-Out phase, ");
-          break;
-	case P_STATUS:
-          printk("Status phase, ");
-          break;
-	case P_MESGIN:
-          printk("Message-In phase, ");
-          break;
-	default:
-          printk("while idle, LASTPHASE = 0x%x, ", bus_state);
-          /*
-           * We're not in a valid phase, so assume we're idle.
-           */
-          bus_state = 0;
-          break;
+        /*
+         * Perform a bus reset.
+         *
+         * XXX - We want to queue an abort for the timedout SCB
+         *       instead.
+         */
+        result = -1;
+        printk(KERN_WARNING "scsi%d: Invalid SCB ID %d is active, "
+               "SCB flags = 0x%x.\n", p->host_no, scb->hscb->tag, scb->flags);
       }
-      printk("SCSISIGI = 0x%x\n", inb(p->base + SCSISIGI));
-
-      /*
-       * First, determine if we want to do a bus reset or simply a bus device
-       * reset.  If this is the first time that a transaction has timed out
-       * and the SCB is not paged out, just schedule a bus device reset.
-       * Otherwise, we reset the bus and abort all pending I/Os on that bus.
-       */
-      if (!(scb->state & (SCB_ABORTED | SCB_PAGED_OUT)))
+      else
       {
-#if 0
-	if (scb->control & TAG_ENB)
-	{
+        /* Send the abort message to the active SCB. */
+        outb(1, p->base + MSG_LEN);
+        if (active_scb->hscb->control & TAG_ENB)
+        {
+          outb(MSG_ABORT_TAG, p->base + MSG_OUT);
+        }
+        else
+        {
+          outb(MSG_ABORT, p->base + MSG_OUT);
+        }
+        outb(bus_state | ATNO, p->base + SCSISIGO);
+        printk(KERN_WARNING "scsi%d: abort message in message buffer\n",
+               p->host_no);
+        active_scb->flags |= SCB_ABORT | SCB_RECOVERY_SCB;
+        if (active_scb != scb)
+        {
           /*
-           * We could be starving this command; try sending and ordered tag
-           * command to the target we come from.
+           * XXX - We would like to increment the timeout on scb, but
+           *       access to that routine is denied because it is hidden
+           *       in scsi.c.  If we were able to do this, it would give
+           *       scb a new lease on life.
            */
-          scb->state = scb->state | SCB_ABORTED | SCB_SENTORDEREDTAG;
-          p->orderedtag = p->orderedtag | 0xFF;
           result = SCSI_RESET_PENDING;
-          UNPAUSE_SEQUENCER(p);
-          printk(KERN_WARNING "aic7xxx: (abort_reset) Ordered tag queued.\n");
-	}
-#endif
-	unsigned char active_scb, control;
-	struct aic7xxx_scb *active_scbp;
+          aic7xxx_error(active_scb->cmd) = DID_RESET;
+        }
+        else
+        {
+          aic7xxx_error(scb->cmd) = DID_RESET;
+          result = SCSI_RESET_PENDING;
+        }
+        unpause_sequencer(p, /* unpause_always */ TRUE);
+      }
+    }
+    else
+    {
+      unsigned char hscb_index, linked_next;
+      int disconnected;
 
-	/*
-	 * Send a Bus Device Reset Message:
-	 * The target we select to send the message to may be entirely
-	 * different than the target pointed to by the scb that timed
-	 * out.  If the command is in the QINFIFO or the waiting for
-	 * selection list, its not tying up the bus and isn't responsible
-	 * for the delay so we pick off the active command which should
-	 * be the SCB selected by SCBPTR.  If its disconnected or active,
-	 * we device reset the target scbp points to.  Although it may
-	 * be that this target is not responsible for the delay, it may
-	 * may also be that we're timing out on a command that just takes
-	 * too much time, so we try the bus device reset there first.
-	 */
-	active_scb = inb(SCBPTR + base);
-	active_scbp = (p->scb_array[inb(SCB_TAG + base)]);
-	control = inb(SCB_CONTROL + base);
+      disconnected = FALSE;
+      hscb_index = aic7xxx_find_scb(p, scb);
+      if (hscb_index == SCB_LIST_NULL)
+      {
+        disconnected = TRUE;
+        linked_next = (scb->hscb->data_count >> 24) & 0xFF;
+      }
+      else
+      {
+        outb(hscb_index, p->base + SCBPTR);
+        if (inb(p->base + SCB_CONTROL) & DISCONNECTED)
+        {
+          disconnected = TRUE;
+        }
+        linked_next = inb(p->base + SCB_LINKED_NEXT);
+      }
+      if (disconnected)
+      {
+        /*
+         * Simply set the ABORT_SCB control bit and preserve the
+         * linked next pointer.
+         */
+        scb->hscb->control |= ABORT_SCB | MK_MESSAGE;
+        scb->hscb->data_count &= ~0xFF000000;
+        scb->hscb->data_count |= linked_next << 24;
+        if ((p->flags & PAGE_ENABLED) == 0)
+        {
+          scb->hscb->control &= ~DISCONNECTED;
+        }
+        scb->flags |= SCB_QUEUED_ABORT | SCB_ABORT | SCB_RECOVERY_SCB;
+        if (hscb_index != SCB_LIST_NULL)
+        {
+          unsigned char scb_control;
 
-	/*
-	 * Test to see if scbp is disconnected
-	 */
-	outb(scb->position, SCBPTR + base);
-	if (inb(SCB_CONTROL + base) & DISCONNECTED)
-	{
-#ifdef AIC7XXX_DEBUG_ABORT
-          printk("aic7xxx: (abort_scb) scb %d is disconnected; "
-                 "bus device reset message queued.\n", scb->position);
-#endif
-          if (p->flags & PAGE_ENABLED)
-          {
-            /* Pull this SCB out of the disconnected list. */
-            u_char prev = inb(SCB_PREV + base);
-            u_char next = inb(SCB_NEXT + base);
-            if (prev == SCB_LIST_NULL)
-            {
-              /* Head of list */
-              outb(next, DISCONNECTED_SCBH + base);
-            }
-            else
-            {
-              outb(prev, SCBPTR + base);
-              outb(next, SCB_NEXT + base);
-              if (next != SCB_LIST_NULL)
-              {
-        	outb(next, SCBPTR + base);
-        	outb(prev, SCB_PREV + base);
-              }
-              outb(scb->position, SCBPTR + base);
-            }
-          }
-	  scb->state |= (SCB_DEVICE_RESET | SCB_ABORTED);
-          scb->control = scb->control & DISCENB;
-          scb->SCSI_cmd_length = 0;
-	  scb->SG_segment_count = 0;
-	  memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer));
-	  memset(scb->data_pointer, 0, sizeof(scb->data_pointer));
-	  scb->data_count = 0;
-	  aic7xxx_putscb(p, scb);
-	  aic7xxx_add_waiting_scb(base, scb);
-	  outb(active_scb, SCBPTR + base);
-          result = SCSI_RESET_PENDING;
-	  UNPAUSE_SEQUENCER(p);
-	}
-	else
-	{
-	  /*
-	   * Is the active SCB really active?
-	   */
-	  if ((active_scbp->state & SCB_ACTIVE) && bus_state)
-	  {
-            /*
-             * Load the message buffer and assert attention.
-             */
-            active_scbp->state |= (SCB_DEVICE_RESET | SCB_ABORTED);
-            outb(1, MSG_LEN + base);
-            outb(MSG_BUS_DEVICE_RESET, MSG0 + base);
-            outb(bus_state | ATNO, SCSISIGO + base);
-#ifdef AIC7XXX_DEBUG_ABORT
-            printk("aic7xxx: (abort_scb) asserted ATN - "
-                   "bus device reset in message buffer.\n");
-#endif
-            if (active_scbp != scb)
-            {
-              /*
-               * XXX - We would like to increment the timeout on scb, but
-               *       access to that routine is denied because it is hidden
-               *       in scsi.c.  If we were able to do this, it would give
-               *       scb a new lease on life.
-               */
-              ;
-            }
-            aic7xxx_error(scb->cmd) = DID_RESET;
-            /*
-             * Restore the active SCB and unpause the sequencer.
-             */
-            outb(active_scb, SCBPTR + base);
-            if (active_scbp != scb)
-            {
-              /*
-               * The mid-level SCSI code requested us to reset a command
-               * different from the one that we actually reset.  Return
-               * a "not running" indication and hope that the SCSI code
-               * will Do the Right Thing (tm).
-               */
-              result = SCSI_RESET_NOT_RUNNING;
-            }
-            else
-            {
-              result = SCSI_RESET_PENDING;
-            }
-            UNPAUSE_SEQUENCER(p);
-	  }
-	}
+          scb_control = inb(p->base + SCB_CONTROL);
+          outb(scb_control | MK_MESSAGE| ABORT_SCB, p->base + SCB_CONTROL);
+        }
+        /*
+         * Actually requeue this SCB in case we can select the
+         * device before it reconnects.  If the transaction we
+         * want to abort is not tagged, unbusy it first so that
+         * we don't get held back from sending the command.
+         */
+        if ((scb->hscb->control & TAG_ENB) == 0)
+        {
+          unsigned char target;
+          int lun;
+
+          target = scb->cmd->target;
+          lun = scb->cmd->lun;
+          aic7xxx_search_qinfifo(p, target, channel, lun, SCB_LIST_NULL,
+              0, /* requeue */ TRUE);
+        }
+        printk(KERN_WARNING "(scsi%d:%d:%d) Queueing an Abort SCB.\n",
+               p->host_no, TC_OF_SCB(scb));
+        scbq_insert_head(&p->waiting_scbs, scb);
+        scb->flags |= SCB_WAITINGQ;
+        outb(saved_scbptr, p->base + SCBPTR);
+        if ((p->flags & IN_ISR) == 0)
+        {
+          /*
+           * Processing the waiting queue may unpause us.
+           */
+          aic7xxx_run_waiting_queues(p);
+          /*
+           * If we are using AAP, aic7xxx_run_waiting_queues() will not
+           * unpause us, so ensure we are unpaused.
+           */
+          unpause_sequencer(p, /*unpause_always*/ FALSE);
+        }
+        else
+        {
+          unpause_sequencer(p, /*unpause_always*/ TRUE);
+        }
+        result = SCSI_RESET_PENDING;
+      }
+      else
+      {
+        scb->flags |= SCB_RECOVERY_SCB;
+        unpause_sequencer(p, /* unpause_always */ TRUE);
+        result = -1;
       }
     }
   }
-  /* Make sure the sequencer is unpaused upon return. */
-  if (result == -1)
-  {
-    UNPAUSE_SEQUENCER(p);
-  }
   return (result);
 }
 
@@ -5490,16 +6781,48 @@
   struct aic7xxx_scb  *scb = NULL;
   struct aic7xxx_host *p;
   int    base, result;
+  unsigned long processor_flags;
 
   p = (struct aic7xxx_host *) cmd->host->hostdata;
-  scb = (p->scb_array[aic7xxx_position(cmd)]);
+  scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
   base = p->base;
 
+  save_flags(processor_flags);
+  cli();
+
 #ifdef AIC7XXX_DEBUG_ABORT
-  printk("aic7xxx: (abort) Aborting scb %d, TCL %d/%d/%d\n",
-         scb->position, TCL_OF_SCB(scb));
+  if (scb != NULL)
+  {
+    printk("(scsi%d:%d:%d) Aborting scb %d, flags 0x%x\n",
+           p->host_no, TC_OF_SCB(scb), scb->hscb->tag, scb->flags);
+  }
+  else
+  {
+    printk("aic7xxx: Abort called with no SCB for cmd.\n");
+  }
 #endif
 
+  if (p->flags & IN_TIMEOUT)
+  {
+    /*
+     * We've already started a recovery operation.
+     */
+    if ((scb->flags & SCB_RECOVERY_SCB) == 0)
+    {
+      restore_flags(processor_flags);
+      return (SCSI_ABORT_PENDING);
+    }
+    else
+    {
+      /*
+       * This is the second time we've tried to abort the recovery
+       * SCB.  We want the mid-level SCSI code to call the reset
+       * function to reset the SCSI bus.
+       */
+      restore_flags(processor_flags);
+      return (SCSI_ABORT_NOT_RUNNING);
+    }
+  }
   if (cmd->serial_number != cmd->serial_number_at_timeout)
   {
     result = SCSI_ABORT_NOT_RUNNING;
@@ -5508,14 +6831,34 @@
   {
     result = SCSI_ABORT_NOT_RUNNING;
   }
-  else if ((scb->cmd != cmd) || (!(scb->state & SCB_IN_PROGRESS)))
+  else if ((scb->cmd != cmd) || (!(scb->flags & SCB_ACTIVE)))
   {
     result = SCSI_ABORT_NOT_RUNNING;
   }
   else
   {
-    result = SCSI_ABORT_SNOOZE;
+    /*
+     * XXX - Check use of IN_TIMEOUT to see if we're Doing the
+     *       Right Thing with it.
+     */
+    p->flags |= IN_TIMEOUT;
+    result = aic7xxx_bus_device_reset(p, scb->cmd);
+    switch (result)
+    {
+      case SCSI_RESET_NOT_RUNNING:
+        p->flags &= ~IN_TIMEOUT;
+        result = SCSI_ABORT_NOT_RUNNING;
+        break;
+      case SCSI_RESET_PENDING:
+        result = SCSI_ABORT_PENDING;
+        break;
+      default:
+        p->flags &= ~IN_TIMEOUT;
+        result = SCSI_ABORT_SNOOZE;
+        break;
+     }
   }
+  restore_flags(processor_flags);
   return (result);
 }
 
@@ -5535,18 +6878,27 @@
 {
   struct aic7xxx_scb *scb = NULL;
   struct aic7xxx_host *p;
-  int    base, found, tindex, min_target, max_target, result = -1;
+  int    base, found, tindex, min_target, max_target;
+  int    result = -1;
   char   channel = 'A';
   unsigned long processor_flags;
 
   p = (struct aic7xxx_host *) cmd->host->hostdata;
-  scb = (p->scb_array[aic7xxx_position(cmd)]);
+  scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
   base = p->base;
   channel = cmd->channel ? 'B': 'A';
   tindex = (cmd->channel << 4) | cmd->target;
 
-#ifdef AIC7XXX_DEBUG_ABORT
-  printk("aic7xxx: (reset) target/channel %d/%d\n", cmd->target, cmd->channel);
+#ifdef 0   /* AIC7XXX_DEBUG_ABORT */
+  if (scb != NULL)
+  {
+    printk("(scsi%d:%d:%d) Reset called, scb %d, flags 0x%x\n",
+           p->host_no, TC_OF_SCB(scb), scb->hscb->tag, scb->flags);
+  }
+  else
+  {
+    printk("aic7xxx: Reset called with no SCB for cmd.\n");
+  }
 #endif
 
   /* 
@@ -5561,34 +6913,45 @@
   if (scb->cmd != cmd)
     scb = NULL;
 
-  if (!(flags & (SCSI_RESET_SUGGEST_HOST_RESET | SCSI_RESET_SUGGEST_BUS_RESET)) 
-      && (scb != NULL))
+  if (p->flags & IN_TIMEOUT)
   {
     /*
-     * Attempt a bus device reset if commands have completed successfully
-     * since the last bus device reset, or it has been less than 100ms
-     * since the last reset.
+     * We've already started a recovery operation.
      */
-    if ((p->flags & DEVICE_SUCCESS) ||
-        ((jiffies - p->device_status[tindex].last_reset) < HZ/10))
+    if ((scb->flags & SCB_RECOVERY_SCB) == 0)
     {
-      if (cmd->serial_number != cmd->serial_number_at_timeout)
-      {
-        result = SCSI_RESET_NOT_RUNNING;
-      }
-      else
+      restore_flags(processor_flags);
+      return (SCSI_RESET_PENDING);
+    }
+  }
+  else
+  {
+    if (!(flags & (SCSI_RESET_SUGGEST_HOST_RESET | SCSI_RESET_SUGGEST_BUS_RESET))
+        && (scb != NULL))
+    {
+      /*
+       * Attempt a bus device reset if commands have completed successfully
+       * since the last bus device reset, or it has been less than 100ms
+       * since the last reset.
+       */
+      if ((p->flags & DEVICE_SUCCESS) ||
+          ((jiffies - p->device_status[tindex].last_reset) < HZ/10))
       {
-        if (scb == NULL)
+	if (cmd->serial_number != cmd->serial_number_at_timeout)
+	{
+          result = SCSI_RESET_NOT_RUNNING;
+	}
+	else if (scb == NULL)
         {
           result = SCSI_RESET_NOT_RUNNING;
         }
         else if (flags & SCSI_RESET_ASYNCHRONOUS)
         {
-          if (scb->state & SCB_ABORTED)
+          if (scb->flags & SCB_ABORTED)
           {
             result = SCSI_RESET_PENDING;
           }
-          else if (!(scb->state & SCB_IN_PROGRESS))
+          else if (!(scb->flags & SCB_ACTIVE))
           {
             result = SCSI_RESET_NOT_RUNNING;
           }
@@ -5599,20 +6962,23 @@
           if ((flags & SCSI_RESET_SYNCHRONOUS) &&
               (p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING))
           {
-            scb->state |= SCB_ABORTED;
+            scb->flags |= SCB_ABORTED;
             result = SCSI_RESET_PENDING;
           }
           else
           {
+            p->flags |= IN_TIMEOUT;
             result = aic7xxx_bus_device_reset(p, cmd);
             if (result == 0)
+            {
+              p->flags &= ~IN_TIMEOUT;
               result = SCSI_RESET_PENDING;
+            }
           }
-        }
+	}
       }
     }
   }
-
   if (result == -1)
   {
     /*
@@ -5625,11 +6991,11 @@
       {
 	result = SCSI_RESET_NOT_RUNNING;
       }
-      else if (!(scb->state & SCB_IN_PROGRESS))
+      else if (!(scb->flags & SCB_ACTIVE))
       {
 	result = SCSI_RESET_NOT_RUNNING;
       }
-      else if ((scb->state & SCB_ABORTED) &&
+      else if ((scb->flags & SCB_ABORTED) &&
                (!(p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING)))
       {
 	result = SCSI_RESET_PENDING;
@@ -5641,8 +7007,9 @@
       /*
        * The reset channel function assumes that the sequencer is paused.
        */
-      PAUSE_SEQUENCER(p);
+      pause_sequencer(p);
       found = aic7xxx_reset_channel(p, channel, TRUE);
+      p->flags = p->flags & ~IN_TIMEOUT;
 
       /*
        * If this is a synchronous reset and there is no SCB for this
@@ -5688,8 +7055,10 @@
       }
 
       result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET;
+      p->flags &= ~IN_TIMEOUT;
     }
   }
+  aic7xxx_run_waiting_queues(p);
   restore_flags(processor_flags);
   return (result);
 }
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx.h linux/drivers/scsi/aic7xxx.h
--- v2.0.30/linux/drivers/scsi/aic7xxx.h	Sat Aug 17 11:19:27 1996
+++ linux/drivers/scsi/aic7xxx.h	Thu Jul 31 12:37:17 1997
@@ -40,13 +40,13 @@
 	aic7xxx_info,						\
 	NULL,							\
 	aic7xxx_queue,						\
-	aic7xxx_abort,						\
+	NULL,							\
 	aic7xxx_reset,						\
 	NULL,							\
 	aic7xxx_biosparam,					\
 	-1,			/* max simultaneous cmds      */\
 	-1,			/* scsi id of host adapter    */\
-	SG_ALL,			/* max scatter-gather cmds    */\
+	0,			/* max scatter-gather cmds    */\
 	2,			/* cmds per lun (linked cmds) */\
 	0,			/* number of 7xxx's present   */\
 	0,			/* no memory DMA restrictions */\
@@ -57,7 +57,6 @@
 extern int aic7xxx_biosparam(Disk *, kdev_t, int[]);
 extern int aic7xxx_detect(Scsi_Host_Template *);
 extern int aic7xxx_command(Scsi_Cmnd *);
-extern int aic7xxx_abort(Scsi_Cmnd *);
 extern int aic7xxx_reset(Scsi_Cmnd *, unsigned int);
 
 extern const char *aic7xxx_info(struct Scsi_Host *);
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx.seq linux/drivers/scsi/aic7xxx.seq
--- v2.0.30/linux/drivers/scsi/aic7xxx.seq	Sun Oct 13 03:42:28 1996
+++ linux/drivers/scsi/aic7xxx.seq	Wed Dec 31 16:00:00 1969
@@ -1,1127 +0,0 @@
-/*+M*************************************************************************
- * Adaptec AIC7xxx device driver for Linux and FreeBSD.
- *
- * Copyright (c) 1994 John Aycock
- *   The University of Calgary Department of Computer Science.
- *
- *Modifications/enhancements:
- *  Copyright (c) 1994, 1995, 1996 Justin Gibbs. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING.  If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * FreeBSD, Twin, Wide, 2 command per target support, tagged queuing and other 
- * optimizations provided by Justin T. Gibbs (gibbs@FreeBSD.org)
- *
- * This version corresponds to version 1.42 of FreeBSDs aic7xxx.seq.
- *
- *-M*************************************************************************/
-
-VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 4.0 1996/10/13 08:23:42 deang Exp $"
-
-#ifdef linux
-#include "aic7xxx_reg.h"
-#else
-#if defined(__NetBSD__)
-#include "../../../../dev/ic/aic7xxxreg.h"
-#elif defined(__FreeBSD__)
-#include "../../dev/aic7xxx/aic7xxx_reg.h"
-#endif
-#endif
-
-/*
- * We can't just use ACCUM in the sequencer code because it
- * must be treated specially by the assembler, and it currently
- * looks for the symbol 'A'.  This is the only register defined in
- * the assembler's symbol space.
- */
-A = ACCUM
-
-/* After starting the selection hardware, we check for reconnecting targets
- * as well as for our selection to complete just in case the reselection wins
- * bus arbitration.  The problem with this is that we must keep track of the
- * SCB that we've already pulled from the QINFIFO and started the selection
- * on just in case the reselection wins so that we can retry the selection at
- * a later time.  This problem cannot be resolved by holding a single entry
- * in scratch ram since a reconnecting target can request sense and this will
- * create yet another SCB waiting for selection.  The solution used here is to 
- * use byte 27 of the SCB as a pseudo-next pointer and to thread a list
- * of SCBs that are awaiting selection.  Since 0-0xfe are valid SCB offsets, 
- * SCB_LIST_NULL is 0xff which is out of range.  The kernel driver must
- * add an entry to this list every time a request sense occurs.  The sequencer
- * will automatically consume the entries.
- */
-
-/*
- * We assume that the kernel driver may reset us at any time, even in the
- * middle of a DMA, so clear DFCNTRL too.
- */
-reset:
-	clr	DFCNTRL
-	clr	SCSISIGO		/* De-assert BSY */
-/*
- * We jump to start after every bus free.
- */
-start:
-	and	FLAGS,0x0f		/* clear target specific flags */
-	mvi	SCSISEQ,ENRSELI		/* Always allow reselection */
-	clr	SCSIRATE		/*
-					 * We don't know the target we will
-					 * connect to, so default to narrow
-					 * transfers to avoid parity problems.
-					 */
-poll_for_work:
-	/*
-	 * Are we a twin channel device?
-	 * For fairness, we check the other bus first,
-	 * since we just finished a transaction on the
-	 * current channel.
-	 */
-	test	FLAGS,TWIN_BUS	jz start2
-	xor	SBLKCTL,SELBUSB			/* Toggle to the other bus */
-	test	SSTAT0,SELDI	jnz reselect
-	xor	SBLKCTL,SELBUSB			/* Toggle to the original bus */
-start2:
-	test	SSTAT0,SELDI	jnz reselect
-	cmp	WAITING_SCBH,SCB_LIST_NULL jne start_waiting
-	mov	A, QCNTMASK
-	test	QINCNT,A	jz poll_for_work
-
-/*
- * We have at least one queued SCB now and we don't have any 
- * SCBs in the list of SCBs awaiting selection.  Set the SCB
- * pointer from the FIFO so we see the right bank of SCB 
- * registers.
- */
-	mov	SCBPTR,QINFIFO
-
-/*
- * See if there is not already an active SCB for this target.  This code
- * locks out on a per target basis instead of target/lun.  Although this
- * is not ideal for devices that have multiple luns active at the same
- * time, it is faster than looping through all SCB's looking for active
- * commands.  It may be beneficial to make findscb a more general procedure
- * to see if the added cost of the search is negligible.  This code also 
- * assumes that the kernel driver will clear the active flags on board 
- * initialization, board reset, and a target SELTO.  Tagged commands
- * don't set the active bits since you can queue more than one command
- * at a time.  We do, however, look to see if there are any non-tagged
- * I/Os in progress, and requeue the command if there are.  Tagged and
- * non-tagged commands cannot be mixed to a single target.
- */
-
-test_busy:
-	mov	FUNCTION1,SCB_TCL
-	mov	A,FUNCTION1
-	test	SCB_TCL,0x88	jz test_a	/* Id < 8 && A channel */
-
-	test	ACTIVE_B,A	jnz requeue
-	test	SCB_CONTROL,TAG_ENB	jnz start_scb
-	/* Mark the current target as busy */
-	or	ACTIVE_B,A
-	jmp	start_scb
-
-/* Place the currently active SCB back on the queue for later processing */
-requeue:
-	mov	QINFIFO, SCBPTR
-	jmp	poll_for_work
-
-/*
- * Pull the first entry off of the waiting for selection list
- * We don't have to "test_busy" because only transactions that
- * have passed that test can be in the waiting_scb list.
- */
-start_waiting:
-	mov	SCBPTR,WAITING_SCBH
-	jmp	start_scb2
-
-test_a:
-	test	ACTIVE_A,A jnz requeue
-	test	SCB_CONTROL,TAG_ENB jnz start_scb
-	/* Mark the current target as busy */
-	or	ACTIVE_A,A
-
-start_scb:
-	mov	SCB_NEXT,WAITING_SCBH
-	mov	WAITING_SCBH, SCBPTR
-start_scb2:
-	and	SINDEX,0xf7,SBLKCTL	/* Clear the channel select bit */
-	and	A,0x08,SCB_TCL		/* Get new channel bit */
-	or	SINDEX,A
-	mov	SBLKCTL,SINDEX		/* select channel */
-	mov	SCB_TCL	call initialize_scsiid
-
-/*
- * Enable selection phase as an initiator, and do automatic ATN
- * after the selection.  We do this now so that we can overlap the
- * rest of our work to set up this target with the arbitration and
- * selection bus phases.
- */
-start_selection:
-	mvi	SCSISEQ,0x58		/* ENSELO|ENAUTOATNO|ENRSELI */
-
-/*
- * As soon as we get a successful selection, the target should go
- * into the message out phase since we have ATN asserted.  Prepare
- * the message to send.
- *
- * Messages are stored in scratch RAM starting with a length byte
- * followed by the message itself.
- */
-	test	SCB_CMDLEN,0xff jnz mk_identify	/* 0 Length Command? */
-
-/*
- * The kernel has sent us an SCB with no command attached.  This implies
- * that the kernel wants to send a message of some sort to this target,
- * so we interrupt the driver, allow it to fill the message buffer, and
- * then go back into the arbitration loop
- */
-	mvi     INTSTAT,AWAITING_MSG
-	jmp     wait_for_selection
-
-mk_identify:
-	and	A,DISCENB,SCB_CONTROL	/* mask off disconnect privledge */
-
-	and	MSG0,0x7,SCB_TCL	/* lun */
-	or	MSG0,A			/* or in disconnect privledge */
-	or	MSG0,MSG_IDENTIFY
-	mvi	MSG_LEN, 1
-
-	test	SCB_CONTROL,0xb0 jz  !message	/* WDTR, SDTR or TAG?? */
-/*
- * Send a tag message if TAG_ENB is set in the SCB control block.
- * Use SCB_TAG (the position in the kernel's SCB array) as the tag value.
- */
-
-mk_tag:
-	mvi	DINDEX, MSG1
-	test	SCB_CONTROL,TAG_ENB jz mk_tag_done
-	and	DINDIR,0x23,SCB_CONTROL
-	mov	DINDIR,SCB_TAG
-
-	add	MSG_LEN,COMP_MSG0,DINDEX	/* update message length */
-
-mk_tag_done:
-
-	test	SCB_CONTROL,0x90 jz !message	/* NEEDWDTR|NEEDSDTR */
-	mov	DINDEX	call mk_dtr	/* build DTR message if needed */
-
-!message:
-wait_for_selection:
-	test	SSTAT0,SELDO	jnz select 
-	test	SSTAT0,SELDI	jz wait_for_selection
-
-/*
- * Reselection has been initiated by a target. Make a note that we've been
- * reselected, but haven't seen an IDENTIFY message from the target
- * yet.
- */
-reselect:
-	clr	MSG_LEN		/* Don't have anything in the mesg buffer */
-	mov	SELID		call initialize_scsiid
-	or	FLAGS,RESELECTED
-	jmp	select2
-
-/*
- * After the selection, remove this SCB from the "waiting for selection"
- * list.  This is achieved by simply moving our "next" pointer into
- * WAITING_SCBH.  Our next pointer will be set to null the next time this
- * SCB is used, so don't bother with it now.
- */
-select:
-	mov	WAITING_SCBH,SCB_NEXT
-	or	FLAGS,SELECTED
-select2:
-/*
- * Set CLRCHN here before the target has entered a data transfer mode -
- * with synchronous SCSI, if you do it later, you blow away some
- * data in the SCSI FIFO that the target has already sent to you.
- */
-	or	SXFRCTL0,CLRCHN
-/*
- * Initialize SCSIRATE with the appropriate value for this target.
- */
-	call	ndx_dtr
-	mov	SCSIRATE,SINDIR
-
-/*
- * Initialize Ultra mode setting.
- */
-	mov	FUNCTION1,SCSIID
-	mov	A,FUNCTION1
-	and	SINDEX,0xdf,SXFRCTL0		/* default to Ultra disabled */
-	test	SCSIID, 0x80	 jnz ultra_b	/* Target ID > 7 */
-	test	SBLKCTL, SELBUSB jnz ultra_b	/* Second channel device */
-	test	ULTRA_ENB,A	 jz  set_sxfrctl0
-	or	SINDEX, ULTRAEN  jmp set_sxfrctl0
-ultra_b:
-	test	ULTRA_ENB_B,A	 jz  set_sxfrctl0
-	or	SINDEX, ULTRAEN
-
-set_sxfrctl0:
-	mov	SXFRCTL0,SINDEX
-
-	mvi	SCSISEQ,ENAUTOATNP		/*
-						 * ATN on parity errors
-						 * for "in" phases
-						 */
-	mvi	CLRSINT1,CLRBUSFREE
-	mvi	CLRSINT0,0x60			/* CLRSELDI|CLRSELDO */
-/*
- * Main loop for information transfer phases.  If BSY is false, then
- * we have a bus free condition, expected or not.  Otherwise, wait
- * for the target to assert REQ before checking MSG, C/D and I/O
- * for the bus phase.
- *
- */
-ITloop:
-	test	SSTAT1,BUSFREE	jnz p_busfree
-	test	SSTAT1,REQINIT	jz ITloop
-
-	and	A,PHASE_MASK,SCSISIGI
-	mov	LASTPHASE,A
-	mov	SCSISIGO,A
-
-	cmp	ALLZEROS,A	je p_dataout
-	cmp	A,P_DATAIN	je p_datain
-	cmp	A,P_COMMAND	je p_command
-	cmp	A,P_MESGOUT	je p_mesgout
-	cmp	A,P_STATUS	je p_status
-	cmp	A,P_MESGIN	je p_mesgin
-
-	mvi	INTSTAT,BAD_PHASE	/* unknown phase - signal driver */
-	jmp	ITloop			/* Try reading the bus again. */
-
-p_dataout:
-	mvi	DMAPARAMS,0x7d			/*
-						 * WIDEODD|SCSIEN|SDMAEN|HDMAEN|
-						 * DIRECTION|FIFORESET
-						 */
-	jmp	data_phase_init
-
-/*
- * If we re-enter the data phase after going through another phase, the
- * STCNT may have been cleared, so restore it from the residual field.
- */
-data_phase_reinit:
-	mov	STCNT0,SCB_RESID_DCNT0
-	mov	STCNT1,SCB_RESID_DCNT1
-	mov	STCNT2,SCB_RESID_DCNT2
-	jmp	data_phase_loop
-
-p_datain:
-	mvi	DMAPARAMS,0x79		/*
-					 * WIDEODD|SCSIEN|SDMAEN|HDMAEN|
-					 * !DIRECTION|FIFORESET
-					 */
-data_phase_init:
-	call	assert
-
-	test	FLAGS, DPHASE	jnz data_phase_reinit
-	call	sg_scb2ram
-	or	FLAGS, DPHASE		/* We have seen a data phase */
-
-data_phase_loop:
-/* Guard against overruns */
-	test	SG_COUNT, 0xff jnz data_phase_inbounds
-/*
- * Turn on 'Bit Bucket' mode, set the transfer count to
- * 16meg and let the target run until it changes phase.
- * When the transfer completes, notify the host that we
- * had an overrun.
- */
-	or	SXFRCTL1,BITBUCKET
-	mvi	STCNT0,0xff
-	mvi	STCNT1,0xff
-	mvi	STCNT2,0xff
-
-data_phase_inbounds:
-/* If we are the last SG block, don't set wideodd. */
-	cmp	SG_COUNT,0x01 jne data_phase_wideodd
-	and	DMAPARAMS, 0xbf		/* Turn off WIDEODD */
-data_phase_wideodd:
-	mov	DMAPARAMS  call dma
-
-/* Go tell the host about any overruns */
-	test	SXFRCTL1,BITBUCKET jnz data_phase_overrun
-
-/* Exit if we had an underrun */
-	test	SSTAT0,SDONE	jz data_phase_finish /* underrun STCNT != 0 */
-
-/*
- * Advance the scatter-gather pointers if needed 
- */
-sg_advance:
-	dec	SG_COUNT	/* one less segment to go */
-
-	test	SG_COUNT, 0xff	jz data_phase_finish /* Are we done? */
-
-	clr	A			/* add sizeof(struct scatter) */
-	add	SG_NEXT0,SG_SIZEOF,SG_NEXT0
-	adc	SG_NEXT1,A,SG_NEXT1
-
-/*
- * Load a struct scatter and set up the data address and length.
- * If the working value of the SG count is nonzero, then
- * we need to load a new set of values.
- *
- * This, like all DMA's, assumes a little-endian host data storage.
- */
-sg_load:
-	clr	HCNT2
-	clr	HCNT1
-	mvi	HCNT0,SG_SIZEOF
-
-	mov	HADDR0,SG_NEXT0
-	mov	HADDR1,SG_NEXT1
-	mov	HADDR2,SG_NEXT2
-	mov	HADDR3,SG_NEXT3
-
-	or	DFCNTRL,0xd			/* HDMAEN|DIRECTION|FIFORESET */
-
-/*
- * Wait for DMA from host memory to data FIFO to complete, then disable
- * DMA and wait for it to acknowledge that it's off.
- */
-dma_finish:
-	test	DFSTATUS,HDONE	jz dma_finish
-	/* Turn off DMA preserving WIDEODD */
-	and	DFCNTRL,WIDEODD
-dma_finish2:
-	test	DFCNTRL,HDMAENACK jnz dma_finish2
-
-/*
- * Copy data from FIFO into SCB data pointer and data count.  In
- * both FreeBSD and Linux, the scatter list entry is 8 bytes.
- * 
- * struct ahc_dma_seg {
- *       physaddr addr;                  four bytes, little-endian order
- *       long    len;                    four bytes, little endian order
- * };
- */
-
-	mov	HADDR0,DFDAT
-	mov	HADDR1,DFDAT
-	mov	HADDR2,DFDAT
-	mov	HADDR3,DFDAT
-	mov	HCNT0,DFDAT
-	mov	HCNT1,DFDAT
-	mov	HCNT2,DFDAT
-
-/* Load STCNT as well.  It is a mirror of HCNT */
-	mov	STCNT0,HCNT0
-	mov	STCNT1,HCNT1
-	mov	STCNT2,HCNT2
-        test    SSTAT1,PHASEMIS  jz data_phase_loop
-
-data_phase_finish:
-/*
- * After a DMA finishes, save the SG and STCNT residuals back into the SCB
- * We use STCNT instead of HCNT, since it's a reflection of how many bytes 
- * were transferred on the SCSI (as opposed to the host) bus.
- */
-	mov	SCB_RESID_DCNT0,STCNT0
-	mov	SCB_RESID_DCNT1,STCNT1
-	mov	SCB_RESID_DCNT2,STCNT2
-	mov	SCB_RESID_SGCNT, SG_COUNT
-	jmp	ITloop
-
-data_phase_overrun:
-/*
- * Turn off BITBUCKET mode and notify the host
- */
-	and	SXFRCTL1,0x7f		/* ~BITBUCKET */
-	mvi	INTSTAT,DATA_OVERRUN
-	jmp	ITloop
-
-/*
- * Command phase.  Set up the DMA registers and let 'er rip.
- */
-p_command:
-	call	assert
-
-/*
- * Load HADDR and HCNT.
- */
-	mov	HADDR0, SCB_CMDPTR0
-	mov	HADDR1, SCB_CMDPTR1
-	mov	HADDR2, SCB_CMDPTR2
-	mov	HADDR3, SCB_CMDPTR3
-	mov	HCNT0, SCB_CMDLEN
-	clr	HCNT1
-	clr	HCNT2
-
-	mov	STCNT0, HCNT0
-	mov	STCNT1, HCNT1
-	mov	STCNT2, HCNT2
-
-	mvi	0x3d		call dma	# SCSIEN|SDMAEN|HDMAEN|
-						#   DIRECTION|FIFORESET
-	jmp	ITloop
-
-/*
- * Status phase.  Wait for the data byte to appear, then read it
- * and store it into the SCB.
- */
-p_status:
-	mvi	SCB_TARGET_STATUS	call inb_first
-	jmp	mesgin_done
-
-/*
- * Message out phase.  If there is not an active message, but the target
- * took us into this phase anyway, build a no-op message and send it.
- */
-p_mesgout:
-	test	MSG_LEN, 0xff	jnz  p_mesgout_start
-	mvi	MSG_NOP		call mk_mesg	/* build NOP message */
-
-p_mesgout_start:
-/*
- * Set up automatic PIO transfer from MSG0.  Bit 3 in
- * SXFRCTL0 (SPIOEN) is already on.
- */
-	mvi	SINDEX,MSG0
-	mov	DINDEX,MSG_LEN
-
-/*
- * When target asks for a byte, drop ATN if it's the last one in
- * the message.  Otherwise, keep going until the message is exhausted.
- *
- * Keep an eye out for a phase change, in case the target issues
- * a MESSAGE REJECT.
- */
-p_mesgout_loop:
-	test	SSTAT1,PHASEMIS	jnz p_mesgout_phasemis
-	test	SSTAT0,SPIORDY	jz p_mesgout_loop
-	test	SSTAT1,PHASEMIS	jnz p_mesgout_phasemis
-	cmp	DINDEX,1	jne p_mesgout_outb	/* last byte? */
-	mvi	CLRSINT1,CLRATNO			/* drop ATN */
-p_mesgout_outb:
-	dec	DINDEX
-	or	CLRSINT0, CLRSPIORDY
-	mov	SCSIDATL,SINDIR
-	
-p_mesgout4:
-	test	DINDEX,0xff	jnz p_mesgout_loop
-
-/*
- * If the next bus phase after ATN drops is a message out, it means
- * that the target is requesting that the last message(s) be resent.
- */
-p_mesgout_snoop:
-	test	SSTAT1,BUSFREE	jnz p_mesgout_done
-	test	SSTAT1,REQINIT	jz p_mesgout_snoop
-
-	test	SSTAT1,PHASEMIS	jnz p_mesgout_done
-
-	or	SCSISIGO,ATNO			/* turn on ATNO */
-
-	jmp	ITloop
-
-p_mesgout_phasemis:
-	mvi	CLRSINT1,CLRATNO	/* Be sure to turn ATNO off */
-p_mesgout_done:
-	clr	MSG_LEN			/* no active msg */
-	jmp	ITloop
-
-/*
- * Message in phase.  Bytes are read using Automatic PIO mode.
- */
-p_mesgin:
-	mvi	A		call inb_first	/* read the 1st message byte */
-	mov	REJBYTE,A			/* save it for the driver */
-
-	test	A,MSG_IDENTIFY		jnz mesgin_identify
-	cmp	A,MSG_DISCONNECT	je mesgin_disconnect
-	cmp	A,MSG_SDPTRS		je mesgin_sdptrs
-	cmp	ALLZEROS,A		je mesgin_complete
-	cmp	A,MSG_RDPTRS		je mesgin_rdptrs
-	cmp	A,MSG_EXTENDED		je mesgin_extended
-	cmp	A,MSG_REJECT		je mesgin_reject
-
-rej_mesgin:
-/*
- * We have no idea what this message in is, and there's no way
- * to pass it up to the kernel, so we issue a message reject and
- * hope for the best.  Since we're now using manual PIO mode to
- * read in the message, there should no longer be a race condition
- * present when we assert ATN.  In any case, rejection should be a
- * rare occurrence - signal the driver when it happens.
- */
-	or	SCSISIGO,ATNO			/* turn on ATNO */
-	mvi	INTSTAT,SEND_REJECT		/* let driver know */
-
-	mvi	MSG_REJECT	call mk_mesg
-
-mesgin_done:
-	call	inb_last			/*ack & turn auto PIO back on*/
-	jmp	ITloop
-
-
-mesgin_complete:
-/*
- * We got a "command complete" message, so put the SCB_TAG into QUEUEOUT,
- * and trigger a completion interrupt.  Check status for non zero return
- * and interrupt driver if needed.  This allows the driver to interpret
- * errors only when they occur instead of always uploading the scb.  If
- * the status is SCSI_CHECK, the driver will download a new scb requesting
- * sense to replace the old one, modify the "waiting for selection" SCB list
- * and set RETURN_1 to SEND_SENSE.  If RETURN_1 is set to SEND_SENSE the
- * sequencer imediately jumps to main loop where it will run down the waiting
- * SCB list and process the sense request.  If the kernel driver does not
- * wish to request sense, it need only clear RETURN_1, and the command is
- * allowed to complete.  We don't bother to post to the QOUTFIFO in the
- * error case since it would require extra work in the kernel driver to
- * ensure that the entry was removed before the command complete code tried
- * processing it.
- *
- * First check for residuals
- */
-	test	SCB_RESID_SGCNT,0xff	jz check_status
-/*
- * If we have a residual count, interrupt and tell the host.  Other
- * alternatives are to pause the sequencer on all command completes (yuck),
- * dma the resid directly to the host (slick, we may have space to do it now)
- * or have the sequencer pause itself when it encounters a non-zero resid 
- * (unnecessary pause just to flag the command -yuck-, but takes one instruction
- * and since it shouldn't happen that often is good enough for our purposes).  
- */
-resid:
-	mvi	INTSTAT,RESIDUAL
-
-check_status:
-	test	SCB_TARGET_STATUS,0xff	jz status_ok	/* Good Status? */
-	mvi	INTSTAT,BAD_STATUS			/* let driver know */
-	cmp	RETURN_1, SEND_SENSE	jne status_ok
-	jmp	mesgin_done
-
-status_ok:
-/* First, mark this target as free. */
-	test	SCB_CONTROL,TAG_ENB jnz test_immediate	/*
-							 * Tagged commands
-							 * don't busy the
-							 * target.
-							 */
-	mov	FUNCTION1,SCB_TCL
-	mov	A,FUNCTION1
-	test	SCB_TCL,0x88 jz clear_a
-	xor	ACTIVE_B,A
-	jmp	test_immediate
-
-clear_a:
-	xor	ACTIVE_A,A
-
-test_immediate:
-	test    SCB_CMDLEN,0xff jnz complete  /* Immediate message complete */
-/*
- * Pause the sequencer until the driver gets around to handling the command
- * complete.  This is so that any action that might require careful timing
- * with the completion of this command can occur.
- */
-	mvi	INTSTAT,IMMEDDONE
-	jmp	start
-complete:
-	mov	QOUTFIFO,SCB_TAG
-	mvi	INTSTAT,CMDCMPLT
-	jmp	mesgin_done
-
-
-/*
- * Is it an extended message?  We only support the synchronous and wide data
- * transfer request messages, which will probably be in response to
- * WDTR or SDTR message outs from us.  If it's not SDTR or WDTR, reject it -
- * apparently this can be done after any message in byte, according
- * to the SCSI-2 spec.
- */
-mesgin_extended:
-	mvi	ARG_1		call inb_next	/* extended message length */
-	mvi	REJBYTE_EXT	call inb_next	/* extended message code */
-
-	cmp	REJBYTE_EXT,MSG_SDTR	je p_mesginSDTR
-	cmp	REJBYTE_EXT,MSG_WDTR	je p_mesginWDTR
-	jmp	rej_mesgin
-
-p_mesginWDTR:
-	cmp	ARG_1,2		jne rej_mesgin	/* extended mesg length=2 */
-	mvi	ARG_1		call inb_next	/* Width of bus */
-	mvi	INTSTAT,WDTR_MSG		/* let driver know */
-	test	RETURN_1,0xff jz mesgin_done	/* Do we need to send WDTR? */
-	cmp	RETURN_1,SEND_REJ je rej_mesgin /*
-						 * Bus width was too large 
-						 * Reject it.
-						 */
-
-/* We didn't initiate the wide negotiation, so we must respond to the request */
-	and	RETURN_1,0x7f			/* Clear the SEND_WDTR Flag */
-	mvi	DINDEX,MSG0
-	mvi	MSG0	call mk_wdtr		/* build WDTR message */
-	or	SCSISIGO,ATNO			/* turn on ATNO */
-	jmp	mesgin_done
-
-p_mesginSDTR:
-	cmp	ARG_1,3		jne rej_mesgin	/* extended mesg length=3 */
-	mvi	ARG_1		call inb_next	/* xfer period */
-	mvi	A		call inb_next	/* REQ/ACK offset */
-	mvi	INTSTAT,SDTR_MSG		/* call driver to convert */
-
-	test	RETURN_1,0xff	jz mesgin_done  /* Do we need to mk_sdtr/rej */
-	cmp	RETURN_1,SEND_REJ je rej_mesgin /*
-						 * Requested SDTR too small
-						 * Reject it.
-						 */
-	clr	ARG_1				/* Use the scratch ram rate */
-	mvi	DINDEX, MSG0
-	mvi     MSG0     call mk_sdtr
-	or	SCSISIGO,ATNO			/* turn on ATNO */
-	jmp	mesgin_done
-
-/*
- * Is it a disconnect message?  Set a flag in the SCB to remind us
- * and await the bus going free.
- */
-mesgin_disconnect:
-	or	SCB_CONTROL,DISCONNECTED
-	test	FLAGS, PAGESCBS jz mesgin_done
-/*
- * Link this SCB into the DISCONNECTED list.  This list holds the
- * candidates for paging out an SCB if one is needed for a new command.
- * Modifying the disconnected list is a critical(pause dissabled) section.
- */
-	mvi	SCB_PREV, SCB_LIST_NULL
-	mvi	SEQCTL,0x50			/* PAUSEDIS|FASTMODE */
-	mov	SCB_NEXT, DISCONNECTED_SCBH
-	mov	DISCONNECTED_SCBH, SCBPTR
-	cmp	SCB_NEXT,SCB_LIST_NULL je linkdone
-	mov	SCBPTR,SCB_NEXT
-	mov	SCB_PREV,DISCONNECTED_SCBH
-	mov	SCBPTR,DISCONNECTED_SCBH
-linkdone:
-	mvi	SEQCTL,0x10			/* !PAUSEDIS|FASTMODE */
-	jmp	mesgin_done
-
-/*
- * Save data pointers message?  Copy working values into the SCB,
- * usually in preparation for a disconnect.
- */
-mesgin_sdptrs:
-	call	sg_ram2scb
-	jmp	mesgin_done
-
-/*
- * Restore pointers message?  Data pointers are recopied from the
- * SCB anytime we enter a data phase for the first time, so all
- * we need to do is clear the DPHASE flag and let the data phase
- * code do the rest.
- */
-mesgin_rdptrs:
-	and	FLAGS,0xef			/*
-						 * !DPHASE we'll reload them
-						 * the next time through
-						 */
-	jmp	mesgin_done
-
-/*
- * Identify message?  For a reconnecting target, this tells us the lun
- * that the reconnection is for - find the correct SCB and switch to it,
- * clearing the "disconnected" bit so we don't "find" it by accident later.
- */
-mesgin_identify:
-	test	A,0x78	jnz rej_mesgin	/*!DiscPriv|!LUNTAR|!Reserved*/
-
-	and	A,0x07			/* lun in lower three bits */
-	or      SAVED_TCL,A,SELID          
-	and     SAVED_TCL,0xf7
-	and     A,SELBUSB,SBLKCTL	/* B Channel?? */
-	or      SAVED_TCL,A
-	call	inb_last		/* ACK */
-
-/*
- * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
- * If we get one, we use the tag returned to switch to find the proper
- * SCB.  With SCB paging, this requires using findSCB for both tagged
- * and non-tagged transactions since the SCB may exist in any slot.
- * If we're not using SCB paging, we can use the tag as the direct
- * index to the SCB.
- */
-	mvi	ARG_1,SCB_LIST_NULL	/* Default to no-tag */
-snoop_tag_loop:
-	test	SSTAT1,BUSFREE	jnz use_findSCB
-	test	SSTAT1,REQINIT	jz snoop_tag_loop
-	test	SSTAT1,PHASEMIS	jnz use_findSCB
-	mvi	A		call inb_first
-	cmp	A,MSG_SIMPLE_TAG jne use_findSCB
-get_tag:
-	mvi	ARG_1	call inb_next	/* tag value */
-/*
- * See if the tag is in range.  The tag is < SCBCOUNT if we add
- * the complement of SCBCOUNT to the incoming tag and there is
- * no carry.
- */
-	mov	A,COMP_SCBCOUNT	
-	add	SINDEX,A,ARG_1
-	jc	abort_tag
-
-/*
- * Ensure that the SCB the tag points to is for a SCB transaction
- * to the reconnecting target.
- */
-	test	FLAGS, PAGESCBS	jz index_by_tag
-	call	inb_last			/* Ack Tag */
-use_findSCB:
-	mov	ALLZEROS	call findSCB	  /* Have to search */
-setup_SCB:
-	and	SCB_CONTROL,0xfb	  /* clear disconnect bit in SCB */
-	or	FLAGS,IDENTIFY_SEEN	  /* make note of IDENTIFY */
-	jmp	ITloop
-index_by_tag:
-	mov	SCBPTR,ARG_1
-	mov	A,SAVED_TCL
-	cmp	SCB_TCL,A		jne abort_tag
-	test	SCB_CONTROL,TAG_ENB	jz  abort_tag
-	call	inb_last			/* Ack Successful tag */
-	jmp	setup_SCB
-
-abort_tag:
-	or	SCSISIGO,ATNO			/* turn on ATNO */
-	mvi	INTSTAT,ABORT_TAG 		/* let driver know */
-	mvi	MSG_ABORT_TAG	call mk_mesg	/* ABORT TAG message */
-	jmp	mesgin_done
-
-/*
- * Message reject?  Let the kernel driver handle this.  If we have an 
- * outstanding WDTR or SDTR negotiation, assume that it's a response from 
- * the target selecting 8bit or asynchronous transfer, otherwise just ignore 
- * it since we have no clue what it pertains to.
- */
-mesgin_reject:
-	mvi	INTSTAT, REJECT_MSG
-	jmp	mesgin_done
-
-/*
- * [ ADD MORE MESSAGE HANDLING HERE ]
- */
-
-/*
- * Bus free phase.  It might be useful to interrupt the device
- * driver if we aren't expecting this.  For now, make sure that
- * ATN isn't being asserted and look for a new command.
- */
-p_busfree:
-	mvi	CLRSINT1,CLRATNO
-	clr	LASTPHASE
-
-/*
- * if this is an immediate command, perform a pseudo command complete to
- * notify the driver.
- */
-	test	SCB_CMDLEN,0xff	jz status_ok
-	jmp	start
-
-/*
- * Locking the driver out, build a one-byte message passed in SINDEX
- * if there is no active message already.  SINDEX is returned intact.
- */
-mk_mesg:
-	mvi	SEQCTL,0x50			/* PAUSEDIS|FASTMODE */
-	test	MSG_LEN,0xff	jz mk_mesg1	/* Should always succeed */
-	
-	/*
-	 * Hmmm.  For some reason the mesg buffer is in use.
-	 * Tell the driver.  It should look at SINDEX to find
-	 * out what we wanted to use the buffer for and resolve
-	 * the conflict.
-	 */
-	mvi	SEQCTL,0x10			/* !PAUSEDIS|FASTMODE */
-	mvi	INTSTAT,MSG_BUFFER_BUSY
-
-mk_mesg1:
-	mvi	MSG_LEN,1		/* length = 1 */
-	mov	MSG0,SINDEX		/* 1-byte message */
-	mvi	SEQCTL,0x10	ret	/* !PAUSEDIS|FASTMODE */
-
-/*
- * Functions to read data in Automatic PIO mode.
- *
- * According to Adaptec's documentation, an ACK is not sent on input from
- * the target until SCSIDATL is read from.  So we wait until SCSIDATL is
- * latched (the usual way), then read the data byte directly off the bus
- * using SCSIBUSL.  When we have pulled the ATN line, or we just want to
- * acknowledge the byte, then we do a dummy read from SCISDATL.  The SCSI
- * spec guarantees that the target will hold the data byte on the bus until
- * we send our ACK.
- *
- * The assumption here is that these are called in a particular sequence,
- * and that REQ is already set when inb_first is called.  inb_{first,next}
- * use the same calling convention as inb.
- */
-
-inb_next:
-	or	CLRSINT0, CLRSPIORDY
-	mov	NONE,SCSIDATL			/*dummy read from latch to ACK*/
-inb_next_wait:
-	test	SSTAT1,PHASEMIS	jnz mesgin_phasemis
-	test	SSTAT0,SPIORDY	jz inb_next_wait /* wait for next byte */
-inb_first:
-	mov	DINDEX,SINDEX
-	test	SSTAT1,PHASEMIS	jnz mesgin_phasemis
-	mov	DINDIR,SCSIBUSL	ret		/*read byte directly from bus*/
-inb_last:
-	mov	NONE,SCSIDATL ret		/*dummy read from latch to ACK*/
-
-mesgin_phasemis:
-/*
- * We expected to receive another byte, but the target changed phase
- */
-	mvi	INTSTAT, MSGIN_PHASEMIS
-	jmp	ITloop
-
-/*
- * DMA data transfer.  HADDR and HCNT must be loaded first, and
- * SINDEX should contain the value to load DFCNTRL with - 0x3d for
- * host->scsi, or 0x39 for scsi->host.  The SCSI channel is cleared
- * during initialization.
- */
-dma:
-	mov	DFCNTRL,SINDEX
-dma1:
-	test	SSTAT0,DMADONE	jnz dma3
-	test	SSTAT1,PHASEMIS	jz dma1		/* ie. underrun */
-
-/*
- * We will be "done" DMAing when the transfer count goes to zero, or
- * the target changes the phase (in light of this, it makes sense that
- * the DMA circuitry doesn't ACK when PHASEMIS is active).  If we are
- * doing a SCSI->Host transfer, the data FIFO should be flushed auto-
- * magically on STCNT=0 or a phase change, so just wait for FIFO empty
- * status.
- */
-dma3:
-	test	SINDEX,DIRECTION	jnz dma5
-dma4:
-	test	DFSTATUS,FIFOEMP	jz dma4
-
-/*
- * Now shut the DMA enables off and make sure that the DMA enables are 
- * actually off first lest we get an ILLSADDR.
- */
-dma5:
-	/* disable DMA, but maintain WIDEODD */
-	and	DFCNTRL,WIDEODD
-dma6:
-	test	DFCNTRL,0x38	jnz dma6  /* SCSIENACK|SDMAENACK|HDMAENACK */
-
-	ret
-
-/*
- * Common SCSI initialization for selection and reselection.  Expects
- * the target SCSI ID to be in the upper four bits of SINDEX, and A's
- * contents are stomped on return.
- */
-initialize_scsiid:
-	and	SINDEX,0xf0		/* Get target ID */
-	and	A,0x0f,SCSIID
-	or	SINDEX,A
-	mov	SCSIID,SINDEX ret
-
-/*
- * Assert that if we've been reselected, then we've seen an IDENTIFY
- * message.
- */
-assert:
-	test	FLAGS,RESELECTED	jz return	/* reselected? */
-	test	FLAGS,IDENTIFY_SEEN	jnz return	/* seen IDENTIFY? */
-
-	mvi	INTSTAT,NO_IDENT 	ret	/* no - cause a kernel panic */
-
-/*
- * Locate the SCB matching the target ID/channel/lun in SAVED_TCL, and the tag
- * value in ARG_1.  If ARG_1 == SCB_LIST_NULL, we're looking for a non-tagged
- * SCB.  Have the kernel print a warning message if it can't be found, and
- * generate an ABORT/ABORT_TAG message to the target.  SINDEX should be
- * cleared on call.
- */
-findSCB:
-	mov	A,SAVED_TCL
-	mov	SCBPTR,SINDEX			/* switch to next SCB */
-	mvi	SEQCTL,0x50			/* PAUSEDIS|FASTMODE */
-	cmp	SCB_TCL,A	jne findSCB1 /* target ID/channel/lun match? */
-	test	SCB_CONTROL,DISCONNECTED jz findSCB1 /*should be disconnected*/
-	test	SCB_CONTROL,TAG_ENB jnz findTaggedSCB
-	cmp	ARG_1,SCB_LIST_NULL je foundSCB
-	jmp	findSCB1
-findTaggedSCB:
-	mov	A, ARG_1			/* Tag passed in ARG_1 */
-	cmp	SCB_TAG,A	jne findSCB1	/* Found it? */
-foundSCB:
-	test	FLAGS,PAGESCBS	jz foundSCB_ret
-/* Remove this SCB from the disconnection list */
-	cmp	SCB_NEXT,SCB_LIST_NULL je unlink_prev
-	mov	SAVED_LINKPTR, SCB_PREV
-	mov	SCBPTR, SCB_NEXT
-	mov	SCB_PREV, SAVED_LINKPTR
-	mov	SCBPTR, SINDEX
-unlink_prev:
-	cmp	SCB_PREV,SCB_LIST_NULL	je rHead/* At the head of the list */
-	mov	SAVED_LINKPTR, SCB_NEXT
-	mov	SCBPTR, SCB_PREV
-	mov	SCB_NEXT, SAVED_LINKPTR
-	mov	SCBPTR, SINDEX
-	mvi	SEQCTL,0x10	ret		/* !PAUSEDIS|FASTMODE */
-rHead:
-	mov	DISCONNECTED_SCBH,SCB_NEXT
-foundSCB_ret:
-	mvi	SEQCTL,0x10	ret		/* !PAUSEDIS|FASTMODE */
-
-findSCB1:
-	mvi	SEQCTL,0x10			/* !PAUSEDIS|FASTMODE */
-	inc	SINDEX
-	mov	A,SCBCOUNT
-	cmp	SINDEX,A	jne findSCB
-
-	mvi	INTSTAT,NO_MATCH		/* not found - signal kernel */
-	cmp	RETURN_1,SCB_PAGEDIN je return
-	or	SCSISIGO,ATNO			/* assert ATNO */
-	cmp	ARG_1,SCB_LIST_NULL jne find_abort_tag
-	mvi	MSG_ABORT	call mk_mesg
-	jmp	ITloop
-find_abort_tag:
-	mvi	MSG_ABORT_TAG	call mk_mesg
-	jmp	ITloop
-
-/*
- * Make a working copy of the scatter-gather parameters from the SCB.
- */
-sg_scb2ram:
-	mov	HADDR0, SCB_DATAPTR0
-	mov	HADDR1, SCB_DATAPTR1
-	mov	HADDR2, SCB_DATAPTR2
-	mov	HADDR3, SCB_DATAPTR3
-	mov	HCNT0, SCB_DATACNT0
-	mov	HCNT1, SCB_DATACNT1
-	mov	HCNT2, SCB_DATACNT2
-
-	mov	STCNT0, HCNT0
-	mov	STCNT1, HCNT1
-	mov	STCNT2, HCNT2
-
-	mov	SG_COUNT,SCB_SGCOUNT
-
-	mov	SG_NEXT0, SCB_SGPTR0
-	mov	SG_NEXT1, SCB_SGPTR1
-	mov	SG_NEXT2, SCB_SGPTR2
-	mov	SG_NEXT3, SCB_SGPTR3 ret
-
-/*
- * Copying RAM values back to SCB, for Save Data Pointers message, but
- * only if we've actually been into a data phase to change them.  This
- * protects against bogus data in scratch ram and the residual counts
- * since they are only initialized when we go into data_in or data_out.
- */
-sg_ram2scb:
-	test	FLAGS, DPHASE	jz return
-	mov	SCB_SGCOUNT,SG_COUNT
-
-	mov	SCB_SGPTR0,SG_NEXT0
-	mov	SCB_SGPTR1,SG_NEXT1
-	mov	SCB_SGPTR2,SG_NEXT2
-	mov	SCB_SGPTR3,SG_NEXT3
-	
-	mov	SCB_DATAPTR0,SHADDR0
-	mov	SCB_DATAPTR1,SHADDR1
-	mov	SCB_DATAPTR2,SHADDR2
-	mov	SCB_DATAPTR3,SHADDR3
-
-/*
- * Use the residual number since STCNT is corrupted by any message transfer
- */
-	mov	SCB_DATACNT0,SCB_RESID_DCNT0
-	mov	SCB_DATACNT1,SCB_RESID_DCNT1
-	mov	SCB_DATACNT2,SCB_RESID_DCNT2 ret
-
-/*
- * Add the array base TARG_SCRATCH to the target offset (the target address
- * is in SCSIID), and return the result in SINDEX.  The accumulator
- * contains the 3->8 decoding of the target ID on return.
- */
-ndx_dtr:
-	shr	A,SCSIID,4
-	test	SBLKCTL,SELBUSB	jz ndx_dtr_2
-	or	A,0x08		/* Channel B entries add 8 */
-ndx_dtr_2:
-	add	SINDEX,TARG_SCRATCH,A ret
-
-/*
- * If we need to negotiate transfer parameters, build the WDTR or SDTR message
- * starting at the address passed in SINDEX.  DINDEX is modified on return.
- * The SCSI-II spec requires that Wide negotiation occur first and you can
- * only negotiate one or the other at a time otherwise in the event of a message
- * reject, you wouldn't be able to tell which message was the culprit.
- */
-mk_dtr:
-	test	SCB_CONTROL,NEEDWDTR jnz  mk_wdtr_16bit
-	mvi	ARG_1, MAXOFFSET	/* Force an offset of 15 or 8 if WIDE */
-
-mk_sdtr:
-	mvi	DINDIR,1		/* extended message */
-	mvi	DINDIR,3		/* extended message length = 3 */
-	mvi	DINDIR,1		/* SDTR code */
-	call	sdtr_to_rate
-	mov	DINDIR,RETURN_1		/* REQ/ACK transfer period */
-	cmp	ARG_1, MAXOFFSET je mk_sdtr_max_offset
-	and	DINDIR,0x0f,SINDIR	/* Sync Offset */
-
-mk_sdtr_done:
-	add	MSG_LEN,COMP_MSG0,DINDEX ret	/* update message length */
-
-mk_sdtr_max_offset:
-/*
- * We're initiating sync negotiation, so request the max offset we can (15 or 8)
- */
-	/* Talking to a WIDE device? */
-	test	SCSIRATE, WIDEXFER	jnz wmax_offset	
-	mvi	DINDIR, MAX_OFFSET_8BIT
-	jmp	mk_sdtr_done
-
-wmax_offset:
-	mvi	DINDIR, MAX_OFFSET_16BIT
-	jmp	mk_sdtr_done
-
-mk_wdtr_16bit:
-	mvi	ARG_1,BUS_16_BIT
-mk_wdtr:
-	mvi	DINDIR,1		/* extended message */
-	mvi	DINDIR,2		/* extended message length = 2 */
-	mvi	DINDIR,3		/* WDTR code */
-	mov	DINDIR,ARG_1		/* bus width */
-
-	add	MSG_LEN,COMP_MSG0,DINDEX ret	/* update message length */
-	
-sdtr_to_rate:
-	call	ndx_dtr			/* index scratch space for target */
-	shr	A,SINDIR,0x4
-	dec	SINDEX			/* Preserve SINDEX */
-	and	A,0x7
-	clr	RETURN_1
-sdtr_to_rate_loop:
-	test	A,0x0f	jz sdtr_to_rate_done
-	add	RETURN_1,0x19
-	dec	A	
-	jmp	sdtr_to_rate_loop
-sdtr_to_rate_done:
-	shr	RETURN_1,0x2
-	add	RETURN_1,0x19
-	test	SXFRCTL0,ULTRAEN jz return
-	shr	RETURN_1,0x1
-return:
-	ret
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx_asm.c linux/drivers/scsi/aic7xxx_asm.c
--- v2.0.30/linux/drivers/scsi/aic7xxx_asm.c	Sat Apr 20 10:59:10 1996
+++ linux/drivers/scsi/aic7xxx_asm.c	Wed Dec 31 16:00:00 1969
@@ -1,734 +0,0 @@
-/*+M*************************************************************************
- * Adaptec AIC7xxx sequencer code assembler.
- *
- * Copyright (c) 1994 John Aycock
- *   The University of Calgary Department of Computer Science.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING.  If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Comments are started by `#' and continue to the end of the line; lines
- * may be of the form:
- *      <label>*
- *      <label>*  <undef-sym> = <value>
- *      <label>*  <opcode> <operand>*
- *
- * A <label> is an <undef-sym> ending in a colon.  Spaces, tabs, and commas
- * are token separators.
- *-M*************************************************************************/
-static const char id[] = "$Id: aic7xxx_asm.c,v 3.0 1996/04/16 08:52:23 deang Exp $";
-#include <ctype.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#define MEMORY		448
-#define MAXLINE		1024
-#define MAXTOKEN	32
-#define ADOTOUT		"a.out"
-#define NOVALUE		-1
-
-#ifndef TRUE
-#  define TRUE 1
-#endif
-#ifndef FALSE
-#  define FALSE 0
-#endif
-#define MAX_ARGS	16
-static const char *cpp[] = {
-  "/lib/cpp -P - -",
-  "/usr/lib/cpp -P - -",
-  "/usr/bin/cpp -P - -",
-  "/usr/bin/gcc -E -P -",
-  "/usr/bin/cc -E -P -"
-};
-
-#define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
-
-/*
- * AIC-7770/AIC-7870 register definitions
- */
-#define R_SINDEX	0x65
-#define R_ALLONES	0x69
-#define R_ALLZEROS	0x6a
-#define R_NONE		0x6a
-
-int debug;
-int lineno, LC;
-char *filename;
-unsigned char M[MEMORY][4];
-
-void 
-error(const char *s)
-{
-	fprintf(stderr, "%s: %s at line %d\n", filename, s, lineno);
-	exit(EXIT_FAILURE);
-}
-
-void *
-Malloc(size_t size)
-{
-	void *p = malloc(size);
-	if (!p)
-		error("out of memory");
-	return(p);
-}
-
-void *
-Realloc(void *ptr, size_t size)
-{
-	void *p = realloc(ptr, size);
-	if (!p)
-		error("out of memory");
-	return(p);
-}
-
-char *
-Strdup(char *s)
-{
-	char *p = (char *)Malloc(strlen(s) + 1);
-	strcpy(p, s);
-	return(p);
-}
-
-typedef struct sym_t {
-	struct sym_t	*next;		/* MUST BE FIRST */
-	char		*name;
-	int		value;
-	int		npatch; 
-	int		*patch;
-} sym_t;
-
-sym_t *head;
-
-void
-define(char *name, int value)
-{
-	sym_t *p, *q;
-
-	for (p = head, q = (sym_t *)&head; p; p = p->next) {
-		if (!strcmp(p->name, name))
-			error("redefined symbol");
-		q = p;
-	}
-
-	p = q->next = (sym_t *)Malloc(sizeof(sym_t));
-	p->next = NULL;
-	p->name = Strdup(name);
-	p->value = value;
-	p->npatch = 0;
-	p->patch = NULL;
-
-	if (debug) {
-		fprintf(stderr, "\"%s\" ", p->name);
-		if (p->value != NOVALUE)
-			fprintf(stderr, "defined as 0x%x\n", p->value);
-		else
-			fprintf(stderr, "undefined\n");
-	}
-}
-
-sym_t *
-lookup(char *name)
-{
-	sym_t *p;
-
-	for (p = head; p; p = p->next)
-		if (!strcmp(p->name, name))
-			return(p);
-	return(NULL);
-}
-
-void 
-patch(sym_t *p, int location)
-{
-	p->npatch += 1;
-	p->patch = (int *)Realloc(p->patch, p->npatch * sizeof(int *));
-
-	p->patch[p->npatch - 1] = location;
-}
-
-void backpatch(void)
-{
-	int i;
-	sym_t *p;
-
-	for (p = head; p; p = p->next) {
-
-		if (p->value == NOVALUE) {
-			fprintf(stderr,
-				"%s: undefined symbol \"%s\"\n",
-				filename, p->name);
-			exit(EXIT_FAILURE);
-		}
-
-		if (p->npatch) {
-			if (debug)
-				fprintf(stderr,
-					"\"%s\" (0x%x) patched at",
-					p->name, p->value);
-
-			for (i = 0; i < p->npatch; i++) {
-				M[p->patch[i]][0] &= ~1;
-				M[p->patch[i]][0] |= ((p->value >> 8) & 1);
-				M[p->patch[i]][1] = p->value & 0xff;
-
-				if (debug)
-					fprintf(stderr, " 0x%x", p->patch[i]);
-			}
-
-			if (debug)
-				fputc('\n', stderr);
-		}
-	}
-}
-
-/*
- *  Output words in byte-reversed order (least significant first)
- *  since the sequencer RAM is loaded that way.
- */
-void
-output(FILE *fp)
-{
-	int i;
-
-	for (i = 0; i < LC; i++)
-		fprintf(fp, "\t0x%02x, 0x%02x, 0x%02x, 0x%02x,\n",
-			M[i][3],
-			M[i][2],
-			M[i][1],
-			M[i][0]);
-	printf("%d out of %d instructions used.\n", LC, MEMORY);
-}
-
-char **
-getl(int *n)
-{
-	int i;
-	char *p, *quote;
-	static char buf[MAXLINE];
-	static char *a[MAXTOKEN];
-
-	i = 0;
-
-	while (fgets(buf, sizeof(buf), stdin)) {
-
-		lineno += 1;
-
-		if (buf[strlen(buf)-1] != '\n')
-			error("line too long");
-
-		p = strchr(buf, '#');
-		if (p)
-			*p = '\0';
-		p = buf;
-rescan:
-		quote = strchr(p, '\"');
-		if (quote)
-			*quote = '\0';
-		for (p = strtok(p, ", \t\n"); p; p = strtok(NULL, ", \t\n"))
-			if (i < MAXTOKEN-1)
-				a[i++] = p;
-			else
-				error("too many tokens");
-		if (quote) {
-			quote++; 
-			p = strchr(quote, '\"');
-			if (!p)
-				error("unterminated string constant");
-			else if (i < MAXTOKEN-1) {
-				a[i++] = quote;
-				*p = '\0';
-				p++;
-			}
-			else
-				error("too many tokens");
-			goto rescan;
-		}		
-		if (i) {
-			*n = i;
-			return(a);
-		}
-	}
-	return(NULL);
-}
-
-#define A	0x8000		/* `A'ccumulator ok */
-#define I	0x4000		/* use as immediate value */
-#define SL	0x2000		/* shift left */
-#define SR	0x1000		/* shift right */
-#define RL	0x0800		/* rotate left */
-#define RR	0x0400		/* rotate right */
-#define LO	0x8000		/* lookup: ori-{jmp,jc,jnc,call} */
-#define LA	0x4000		/* lookup: and-{jz,jnz} */
-#define LX	0x2000		/* lookup: xor-{je,jne} */
-#define NA	-1		/* not applicable */
-
-struct {
-	const char *name;
-	int n;			/* number of operands, including opcode */
-	unsigned int op;	/* immediate or L?|pos_from_0 */
-	unsigned int dest;	/* NA, pos_from_0, or I|immediate */
-	unsigned int src;	/* NA, pos_from_0, or I|immediate */
-	unsigned int imm;	/* pos_from_0, A|pos_from_0, or I|immediate */
-	unsigned int addr;	/* NA or pos_from_0 */
-	int fmt;		/* instruction format - 1, 2, or 3 */
-} instr[] = {
-/*
- *		  N  OP    DEST		SRC		IMM	ADDR	FMT
- */
-	{ "mov",  3, 1,	   1,		2,		I|0xff,	NA,	1 },
-	{ "mov",  4, LO|2, NA,		1,		I|0,	3,	3 },
-	{ "mvi",  3, 0,	   1,		I|R_ALLZEROS,	A|2,	NA,	1 },
-	{ "mvi",  4, LO|2, NA,		I|R_ALLZEROS,	1,	3,	3 },
-	{ "not",  2, 2,	   1,		1,		I|0xff,	NA,	1 },
-	{ "and",  3, 1,	   1,		1,		A|2,	NA,	1 },
-	{ "and",  4, 1,	   1,		3,		A|2,	NA,	1 },
-	{ "or",	  3, 0,	   1,		1,		A|2,	NA,	1 },
-	{ "or",	  4, 0,	   1,		3,		A|2,	NA,	1 },
-	{ "or",   5, LO|3, NA,		1,		2,	4,	3 },
-	{ "xor",  3, 2,	   1,		1,		A|2,	NA,	1 },
-	{ "xor",  4, 2,	   1,		3,		A|2,	NA,	1 },
-	{ "nop",  1, 1,	   I|R_NONE,	I|R_ALLZEROS,	I|0xff,	NA,	1 },
-	{ "inc",  2, 3,	   1,		1,		I|1,	NA,	1 },
-	{ "inc",  3, 3,	   1,		2,		I|1,	NA,	1 },
-	{ "dec",  2, 3,	   1,		1,		I|0xff,	NA,	1 },
-	{ "dec",  3, 3,	   1,		2,		I|0xff,	NA,	1 },
-	{ "jmp",  2, LO|0,   NA,	I|R_SINDEX,	I|0,	1,	3 },
-	{ "jc",   2, LO|0,   NA,	I|R_SINDEX,	I|0,	1,	3 },
-	{ "jnc",  2, LO|0,   NA,	I|R_SINDEX,	I|0,	1,	3 },
-	{ "call", 2, LO|0,   NA,	I|R_SINDEX,	I|0,	1,	3 },
-	{ "test", 5, LA|3,   NA,	1,		A|2,	4,	3 },
-	{ "cmp",  5, LX|3,   NA,	1,		A|2,	4,	3 },
-	{ "ret",  1, 1,	 I|R_NONE,	I|R_ALLZEROS,	I|0xff,	NA,	1 },
-	{ "ret",  1, 1,	 I|R_NONE,	I|R_ALLZEROS,	I|0xff,	NA,	1 },
-	{ "clc",  1, 3,	 I|R_NONE,	I|R_ALLZEROS,	I|1,	NA,	1 },
-	{ "clc",  4, 3,	 2,		I|R_ALLZEROS,	A|3,	NA,	1 },
-	{ "stc",  2, 3,	 1,		I|R_ALLONES,	I|1,	NA,	1 },
-	{ "add",  3, 3,	 1,		1,		A|2,	NA,	1 },
-	{ "add",  4, 3,	 1,		3,		A|2,	NA,	1 },
-	{ "adc",  3, 4,	 1,		1,		A|2,	NA,	1 },
-	{ "adc",  4, 4,	 1,		3,		A|2,	NA,	1 },
-	{ "shl",  3, 5,	 1,		1,		SL|2,	NA,	2 },
-	{ "shl",  4, 5,	 1,		2,		SL|3,	NA,	2 },
-	{ "shr",  3, 5,	 1,		1,		SR|2,	NA,	2 },
-	{ "shr",  4, 5,	 1,		2,		SR|3,	NA,	2 },
-	{ "rol",  3, 5,	 1,		1,		RL|2,	NA,	2 },
-	{ "rol",  4, 5,	 1,		2,		RL|3,	NA,	2 },
-	{ "ror",  3, 5,	 1,		1,		RR|2,	NA,	2 },
-	{ "ror",  4, 5,	 1,		2,		RR|3,	NA,	2 },
-	/*
-	 *  Extensions (note also that mvi allows A)
-	 */
- 	{ "clr",  2, 1,	 1,	I|R_ALLZEROS,		I|0xff,	NA,	1 },
-	{ 0,      0, 0,  0, 	0,			0,	0,	0 }
-};
-
-int 
-eval_operand(char **a, int spec)
-{
-	int i;
-	unsigned int want = spec & (LO|LA|LX);
-
-	static struct {
-		unsigned int what;
-		const char *name;
-		int value;
-	} jmptab[] = {
-		{ LO,	"jmp",		8  },
-		{ LO,	"jc",		9  },
-		{ LO,	"jnc",		10 },
-		{ LO,	"call",		11 },
-		{ LA,	"jz",		15 },
-		{ LA,	"jnz",		13 },
-		{ LX,	"je",		14 },
-		{ LX,	"jne",		12 },
-	};
-
-	spec &= ~(LO|LA|LX);
-
-	for (i = 0; i < sizeof(jmptab)/sizeof(jmptab[0]); i++)
-		if (jmptab[i].what == want &&
-		    !strcmp(jmptab[i].name, a[spec]))
-		{
-			return(jmptab[i].value);
-		}
-
-	if (want)
-		error("invalid jump");
-
-	return(spec);		/* "case 0" - no flags set */
-}
-
-int
-eval_sdi(char **a, int spec)
-{
-	sym_t *p;
-	unsigned val;
-
-	if (spec == NA)
-		return(NA);
-
-	switch (spec & (A|I|SL|SR|RL|RR)) {
-	    case SL:
-	    case SR:
-	    case RL:
-	    case RR:
-		if (isdigit(*a[spec &~ (SL|SR|RL|RR)]))
-			val = strtol(a[spec &~ (SL|SR|RL|RR)], NULL, 0);
-		else {
-			p = lookup(a[spec &~ (SL|SR|RL|RR)]);
-			if (!p)
-				error("undefined symbol used");
-			val = p->value;
-		}
-
-		switch (spec & (SL|SR|RL|RR)) {		/* blech */
-		    case SL:
-			if (val > 7)
-				return(0xf0);
-			return(((val % 8) << 4) |
-			       (val % 8));
-		    case SR:
-			if (val > 7)
-				return(0xf0);
-			return(((val % 8) << 4) |
-			       (1 << 3) |
-			       ((8 - (val % 8)) % 8));
-		    case RL:
-			return(val % 8);
-		    case RR:
-			return((8 - (val % 8)) % 8);
-		}
-	    case I:
-		return(spec &~ I);
-	    case A:
-		/*
-		 *  An immediate field of zero selects
-		 *  the accumulator.  Vigorously object
-		 *  if zero is given otherwise - it's
-		 *  most likely an error.
-		 */
-		spec &= ~A;
-		if (!strcmp("A", a[spec]))
-			return(0);
-		if (isdigit(*a[spec]) &&
-		    strtol(a[spec], NULL, 0) == 0)
-		{
-			error("immediate value of zero selects accumulator");
-		}
-		/* falls through */
-	    case 0:
-		if (isdigit(*a[spec]))
-			return(strtol(a[spec], NULL, 0));
-		p = lookup(a[spec]);
-		if (p)
-			return(p->value);
-		error("undefined symbol used");
-	}
-
-	return(NA);		/* shut the compiler up */
-}
-
-int
-eval_addr(char **a, int spec)
-{
-	sym_t *p;
-
-	if (spec == NA)
-		return(NA);
-	if (isdigit(*a[spec]))
-		return(strtol(a[spec], NULL, 0));
-
-	p = lookup(a[spec]);
-
-	if (p) {
-		if (p->value != NOVALUE)
-			return(p->value);
-		patch(p, LC);
-	} else {
-		define(a[spec], NOVALUE);
-		p = lookup(a[spec]);
-		patch(p, LC);
-	}
-
-	return(NA);		/* will be patched in later */
-}
-
-int
-crack(char **a, int n)
-{
-	int i;
-	int I_imm, I_addr;
-	int I_op, I_dest, I_src, I_ret;
-
-	/*
-	 *  Check for "ret" at the end of the line; remove
-	 *  it unless it's "ret" alone - we still want to
-	 *  look it up in the table.
-	 */
-	I_ret = (strcmp(a[n-1], "ret") ? 0 : !0);
-	if (I_ret && n > 1)
-		n -= 1;
-
-	for (i = 0; instr[i].name; i++) {
-		/*
-		 *  Look for match in table given constraints,
-		 *  currently just the name and the number of
-		 *  operands.
-		 */
-		if (!strcmp(instr[i].name, *a) && instr[i].n == n)
-			break;
-	}
-	if (!instr[i].name)
-		error("unknown opcode or wrong number of operands");
-
-	I_op	= eval_operand(a, instr[i].op);
-	I_src	= eval_sdi(a, instr[i].src);
-	I_imm	= eval_sdi(a, instr[i].imm);
-	I_dest	= eval_sdi(a, instr[i].dest);
-	I_addr	= eval_addr(a, instr[i].addr);
-
-	if( LC >= MEMORY )
-		error("Memory exhausted!\n");
-
-	switch (instr[i].fmt) {
-	    case 1:
-	    case 2:
-		M[LC][0] = (I_op << 1) | I_ret;
-		M[LC][1] = I_dest;
-		M[LC][2] = I_src;
-		M[LC][3] = I_imm;
-		break;
-	    case 3:
-		if (I_ret)
-			error("illegal use of \"ret\"");
-		M[LC][0] = (I_op << 1) | ((I_addr >> 8) & 1);
-		M[LC][1] = I_addr & 0xff;
-		M[LC][2] = I_src;
-		M[LC][3] = I_imm;
-		break;
-	}
-
-	return (1);		/* no two-byte instructions yet */
-}
-
-#undef SL
-#undef SR
-#undef RL
-#undef RR
-#undef LX
-#undef LA
-#undef LO
-#undef I
-#undef A
-
-void
-assemble(FILE *ofile)
-{
-	int n;
-	char **a;
-	sym_t *p;
-
-	while ((a = getl(&n))) {
-
-		while (a[0][strlen(*a)-1] == ':') {
-			a[0][strlen(*a)-1] = '\0';
-			p = lookup(*a);
-			if (p)
-				p->value = LC;
-			else
-				define(*a, LC);
-			a += 1;
-			n -= 1;
-		}
-
-		if (!n)			/* line was all labels */
-			continue;
-
-		if (n == 3 && !strcmp("VERSION", *a))
-			fprintf(ofile, "#define %s \"%s\"\n", a[1], a[2]);
-		else {
-			if (n == 3 && !strcmp("=", a[1]))
-				define(*a, strtol(a[2], NULL, 0));
-			else
-				LC += crack(a, n);
-		}
-	}
-
-	backpatch();
-	output(ofile);
-
-	if (debug)
-		output(stderr);
-}
-
-int
-main(int argc, char **argv)
-{
-	int c;
-	int pid;
-	int ifile;
-	int status;
-	FILE *ofile;
-	char *ofilename;
-	int fd[2];
-
-	ofile = NULL;
-	ofilename = NULL;
-	while ((c = getopt(argc, argv, "dho:vD:")) != EOF) {
-		switch (c) {
-		    case 'd':
-			debug = !0;
-			break;
-		    case 'D':
-		    {
-			char *p;
-			if ((p = strchr(optarg, '=')) != NULL) {
-				*p = '\0';
-				define(optarg, strtol(p + 1, NULL, 0));
-			}
-			else
-				define(optarg, 1);
-			break;
-		    }
-		    case 'o':
-		        ofilename = optarg;
-			if ((ofile = fopen(ofilename, "w")) == NULL) {
-				perror(optarg);
-				exit(EXIT_FAILURE);
-			}
-			break;
-		    case 'h':
-			printf("usage: %s [-d] [-Dname] [-ooutput] input\n", 
-				*argv);
-			exit(EXIT_SUCCESS);
-			break;
-		    case 'v':
-			printf("%s\n", id);
-			exit(EXIT_SUCCESS);
-			break;
-		    default:
-			exit(EXIT_FAILURE);
-			break;
-		}
-	}
-
-	if (argc - optind != 1) {
-		fprintf(stderr, "%s: must have one input file\n", *argv);
-		exit(EXIT_FAILURE);
-	}
-	filename = argv[optind];
-
-	
-	if ((ifile = open(filename, O_RDONLY)) < 0) {
-		perror(filename);
-		exit(EXIT_FAILURE);
-	}
-
-	if (!ofilename) {
-		ofilename = ADOTOUT;
-		if ((ofile = fopen(ofilename, "w")) < 0) {
-			perror(ofilename);
-			exit(EXIT_FAILURE);
-		}
-	}
-
-	if (pipe(fd) < 0) {
-		perror("pipe failed");
-		exit(1);
-	}
-
-	if ((pid = fork()) < 0 ) {
-		perror("fork failed");
-		exit(1);
-	}
-	else if (pid > 0) {		/* Parent */
-		close(fd[1]);		/* Close write end */
-		if (fd[0] != STDIN_FILENO) {
-			if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) {
-				perror("dup2 error on stdin");
-				exit(EXIT_FAILURE);
-			}
-			close(fd[0]);
-		}
-		assemble(ofile);
-		if (wait(&status) < 0) {
-			perror("wait error");
-		}
-
-		if (status != 0) {
-			unlink(ofilename);
-		}
-		exit(status);
-	} else {				/* Child */
-                int i, arg_cnt, found;
-		char *args[MAX_ARGS];
-		char *buf;
-
-                arg_cnt = 0;
-                found = FALSE;
-                for (i = 0; (!found && (i < NUMBER(cpp))); i++) {
-			char *bp;
-
-			buf = strdup(cpp[i]);
-
-			for (bp = strtok(buf, " \t\n"), arg_cnt = 0;
-                             bp != NULL;
-                             bp = strtok(NULL, " \t\n"), arg_cnt++) {
-				if (arg_cnt == 0) {
-					if (access(bp, X_OK) == 0) {
-						found = TRUE;
-					}
-				}
-
-				args[arg_cnt] = bp;
-			}
-
-                        if (!found) {
-				free(buf);
-                        }
-                }
-		args[arg_cnt] = NULL;
-
-                if (found) {
-			close(fd[0]);		/* Close Read end */
-			if (fd[1] != STDOUT_FILENO) {
-				if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO) {
-					perror("dup2 error on stdout");
-					exit(EXIT_FAILURE);
-				}
-				close(fd[1]);
-			}
-			if (ifile != STDIN_FILENO) {
-				if (dup2(ifile, STDIN_FILENO) != STDIN_FILENO) {
-					perror("dup2 error on stdin");
-					exit(EXIT_FAILURE);
-				}
-				close(ifile);
-			}
-
-			if (execvp(args[0], args) < 0) {
-				perror("execvp() error");
-				exit(EXIT_FAILURE);
-			}
-        	} else {
-			fprintf(stderr, "%s: Cannot find CPP command.\n", argv[0]);
-			exit(EXIT_FAILURE);
-		}
-	}
-	return(EXIT_SUCCESS);
-}
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx_proc.c linux/drivers/scsi/aic7xxx_proc.c
--- v2.0.30/linux/drivers/scsi/aic7xxx_proc.c	Sun Oct 13 03:42:28 1996
+++ linux/drivers/scsi/aic7xxx_proc.c	Wed Aug  6 21:51:02 1997
@@ -24,7 +24,7 @@
  *
  *  Dean W. Gehnert, deang@teleport.com, 05/01/96
  *
- *  $Id: aic7xxx_proc.c,v 4.0 1996/10/13 08:23:42 deang Exp $
+ *  $Id: aic7xxx_proc.c,v 4.1 1997/06/97 08:23:42 deang Exp $
  *-M*************************************************************************/
 
 #define BLS buffer + len + size
@@ -76,17 +76,18 @@
 {
   struct Scsi_Host *HBAptr;
   struct aic7xxx_host *p;
-  static u8 buff[512];
-  int   i; 
+  int   i;
+  int   found = FALSE;
   int   size = 0;
   int   len = 0;
   off_t begin = 0;
   off_t pos = 0;
   static char *bus_names[] = { "Single", "Twin", "Wide" };
-  static char *chip_names[] = { "AIC-777x", "AIC-785x", "AIC-787x", "AIC-788x" };
+  static char *chip_names[] = { "AIC-777x", "AIC-785x", "AIC-786x",
+      "AIC-787x", "AIC-788x" };
 
   HBAptr = NULL;
-  for (i = 0; i < NUMBER(aic7xxx_boards); i++)
+  for (i=0; i < NUMBER(aic7xxx_boards); i++)
   {
     if ((HBAptr = aic7xxx_boards[i]) != NULL)
     {
@@ -95,16 +96,23 @@
         break;
       }
 
-      while ((HBAptr->hostdata != NULL) &&
+      while ((HBAptr->hostdata != NULL) && !found &&
           ((HBAptr = ((struct aic7xxx_host *) HBAptr->hostdata)->next) != NULL))
       {
         if (HBAptr->host_no == hostno)
         {
-          break; break;
+          found = TRUE;
         }
       }
 
-      HBAptr = NULL;
+      if (!found)
+      {
+        HBAptr = NULL;
+      }
+      else
+      {
+        break;
+      }
     }
   }
 
@@ -120,18 +128,22 @@
     return (aic7xxx_set_info(buffer, length, HBAptr));
   }
 
-  if (offset == 0)
-  {
-    memset(buff, 0, sizeof(buff));
-  }
-
   p = (struct aic7xxx_host *) HBAptr->hostdata;
 
   size += sprintf(BLS, "Adaptec AIC7xxx driver version: ");
   size += sprintf(BLS, "%s/", rcs_version(AIC7XXX_C_VERSION));
-  size += sprintf(BLS, "%s/", rcs_version(AIC7XXX_H_VERSION));
+  size += sprintf(BLS, "%s", rcs_version(AIC7XXX_H_VERSION));
+#if 0
   size += sprintf(BLS, "%s\n", rcs_version(AIC7XXX_SEQ_VER));
+#endif
+  if (size > 512)
+    printk(KERN_CRIT "aic7xxx: possible overflow at first position\n");
   len += size; pos = begin + len; size = 0;
+  if (pos < offset)
+  {
+    begin = pos;
+    len = 0;
+  }
 
   size += sprintf(BLS, "\n");
   size += sprintf(BLS, "Compile Options:\n");
@@ -141,11 +153,6 @@
 #ifdef AIC7XXX_CMDS_PER_LUN
   size += sprintf(BLS, "  AIC7XXX_CMDS_PER_LUN   : %d\n", AIC7XXX_CMDS_PER_LUN);
 #endif
-#ifdef AIC7XXX_TWIN_SUPPORT
-  size += sprintf(BLS, "  AIC7XXX_TWIN_SUPPORT   : Enabled\n");
-#else
-  size += sprintf(BLS, "  AIC7XXX_TWIN_SUPPORT   : Disabled\n");
-#endif
 #ifdef AIC7XXX_TAGGED_QUEUEING
   size += sprintf(BLS, "  AIC7XXX_TAGGED_QUEUEING: Enabled\n");
 #else
@@ -161,20 +168,31 @@
 #else
   size += sprintf(BLS, "  AIC7XXX_PROC_STATS     : Disabled\n");
 #endif
+  if (size > 512)
+    printk(KERN_CRIT "aic7xxx: possible overflow at second position\n");
   len += size; pos = begin + len; size = 0;
+  if (pos < offset)
+  {
+    begin = pos;
+    len = 0;
+  }
+  else if (pos >= offset + length)
+    goto stop_output;
 
   size += sprintf(BLS, "\n");
   size += sprintf(BLS, "Adapter Configuration:\n");
-  size += sprintf(BLS, "          SCSI Adapter: %s\n", board_names[p->type]);
+  size += sprintf(BLS, "          SCSI Adapter: %s\n",
+      board_names[p->chip_type]);
   size += sprintf(BLS, "                        (%s chipset)\n",
-      chip_names[p->chip_type]);
+      chip_names[p->chip_class]);
   size += sprintf(BLS, "              Host Bus: %s\n", bus_names[p->bus_type]);
   size += sprintf(BLS, "               Base IO: %#.4x\n", p->base);
+  size += sprintf(BLS, "        Base IO Memory: 0x%x\n", p->mbase);
   size += sprintf(BLS, "                   IRQ: %d\n", HBAptr->irq);
   size += sprintf(BLS, "                  SCBs: Used %d, HW %d, Page %d\n",
-      p->scb_link->numscbs, p->maxhscbs, p->maxscbs);
+      p->scb_data->numscbs, p->scb_data->maxhscbs, p->scb_data->maxscbs);
   size += sprintf(BLS, "            Interrupts: %d", p->isr_count);
-  if (p->chip_type == AIC_777x)
+  if (p->chip_class == AIC_777x)
   {
     size += sprintf(BLS, " %s\n",
         (p->pause & IRQMS) ? "(Level Sensitive)" : "(Edge Triggered)");
@@ -193,7 +211,16 @@
       (p->flags & ULTRA_ENABLED) ? "En" : "Dis");
   size += sprintf(BLS, "     Target Disconnect: %sabled\n",
       p->discenable ? "En" : "Dis");
+  if (size > 512)
+    printk(KERN_CRIT "aic7xxx: possible overflow at third position\n");
   len += size; pos = begin + len; size = 0;
+  if (pos < offset)
+  {
+    begin = pos;
+    len = 0;
+  }
+  else if (pos >= offset + length)
+    goto stop_output;
 
 #ifdef AIC7XXX_PROC_STATS
   {
@@ -202,6 +229,7 @@
 
     /*
      * XXX: Need to fix this to avoid overflow...
+     * Fixed - gordo.
      */
     size += sprintf(BLS, "\n");
     size += sprintf(BLS, "Statistics:\n");
@@ -239,9 +267,18 @@
               sp->w_bins[9]);
           size += sprintf(BLS, "\n");
         }
+	if (size > 512)
+	  printk(KERN_CRIT "aic7xxx: possible overflow at loop %d:%d\n", target, lun);
+        len += size; pos = begin + len; size = 0;
+	if (pos < offset)
+	{
+	  begin = pos;
+	  len = 0;
+	}
+	else if (pos >= offset + length)
+	  goto stop_output;
       }
     }
-    len += size; pos = begin + len; size = 0;
   }
 #endif /* AIC7XXX_PROC_STATS */
 
@@ -249,7 +286,11 @@
   proc_debug("2pos: %ld offset: %ld len: %d\n", pos, offset, len);
   *start = buffer + (offset - begin);   /* Start of wanted data */
   len -= (offset - begin);      /* Start slop */
-  if (len > length)
+  if (len < 0)
+  {
+    len = 0;			/* off end of file */
+  }
+  else if (len > length)
   {
     len = length;               /* Ending slop */
   }
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx_reg.h linux/drivers/scsi/aic7xxx_reg.h
--- v2.0.30/linux/drivers/scsi/aic7xxx_reg.h	Sun Oct 13 03:42:28 1996
+++ linux/drivers/scsi/aic7xxx_reg.h	Wed Aug 13 09:53:45 1997
@@ -1,771 +1,473 @@
-/*+M*************************************************************************
- * Adaptec AIC7xxx register and scratch ram definitions.
- *
- * Copyright (c) 1994, 1995, 1996 Justin T. Gibbs.
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING.  If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * This version corresponds to version 1.12 of FreeBSDs aic7xxx_reg.h
- *
- * $Id: aic7xxx_reg.h,v 4.0 1996/10/13 08:23:42 deang Exp $
- *-M*************************************************************************/
-
-/*
- * This header is shared by the sequencer code and the kernel level driver.
- *
- * All page numbers refer to the Adaptec AIC-7770 Data Book available from
- * Adaptec's Technical Documents Department 1-800-934-2766
- */
-
-/*
- * SCSI Sequence Control (p. 3-11).
- * Each bit, when set starts a specific SCSI sequence on the bus
- */
-#define SCSISEQ			0x000
-#define		TEMODEO		0x80
-#define		ENSELO		0x40
-#define		ENSELI		0x20
-#define		ENRSELI		0x10
-#define		ENAUTOATNO	0x08
-#define		ENAUTOATNI	0x04
-#define		ENAUTOATNP	0x02
-#define		SCSIRSTO	0x01
-
-/*
- * SCSI Transfer Control 0 Register (pp. 3-13).
- * Controls the SCSI module data path.
- */
-#define	SXFRCTL0		0x001
-#define		DFON		0x80
-#define		DFPEXP		0x40
-#define		ULTRAEN		0x20
-#define		CLRSTCNT	0x10
-#define		SPIOEN		0x08
-#define		SCAMEN		0x04
-#define		CLRCHN		0x02
-/*  UNUSED			0x01 */
-
-/*
- * SCSI Transfer Control 1 Register (pp. 3-14,15).
- * Controls the SCSI module data path.
- */
-#define	SXFRCTL1		0x002
-#define		BITBUCKET	0x80
-#define		SWRAPEN		0x40
-#define		ENSPCHK		0x20
-#define		STIMESEL	0x18
-#define		ENSTIMER	0x04
-#define		ACTNEGEN	0x02
-#define		STPWEN		0x01	/* Powered Termination */
-
-/*
- * SCSI Control Signal Read Register (p. 3-15).
- * Reads the actual state of the SCSI bus pins
- */
-#define SCSISIGI		0x003
-#define		CDI		0x80
-#define		IOI		0x40
-#define		MSGI		0x20
-#define		ATNI		0x10
-#define		SELI		0x08
-#define		BSYI		0x04
-#define		REQI		0x02
-#define		ACKI		0x01
-
-/*
- * Possible phases in SCSISIGI
- */
-#define		PHASE_MASK	0xe0
-#define		P_DATAOUT	0x00
-#define		P_DATAIN	0x40
-#define		P_COMMAND	0x80
-#define		P_MESGOUT	0xa0
-#define		P_STATUS	0xc0
-#define		P_MESGIN	0xe0
-/*
- * SCSI Control Signal Write Register (p. 3-16).
- * Writing to this register modifies the control signals on the bus.  Only
- * those signals that are allowed in the current mode (Initiator/Target) are
- * asserted.
- */
-#define SCSISIGO		0x003
-#define		CDO		0x80
-#define		IOO		0x40
-#define		MSGO		0x20
-#define		ATNO		0x10
-#define		SELO		0x08
-#define		BSYO		0x04
-#define		REQO		0x02
-#define		ACKO		0x01
-
-/* 
- * SCSI Rate Control (p. 3-17).
- * Contents of this register determine the Synchronous SCSI data transfer
- * rate and the maximum synchronous Req/Ack offset.  An offset of 0 in the
- * SOFS (3:0) bits disables synchronous data transfers.  Any offset value
- * greater than 0 enables synchronous transfers.
- */
-#define SCSIRATE		0x004
-#define		WIDEXFER	0x80		/* Wide transfer control */
-#define		SXFR		0x70		/* Sync transfer rate */
-#define		SOFS		0x0f		/* Sync offset */
-
-/*
- * SCSI ID (p. 3-18).
- * Contains the ID of the board and the current target on the
- * selected channel.
- */
-#define SCSIID			0x005
-#define		TID		0xf0		/* Target ID mask */
-#define		OID		0x0f		/* Our ID mask */
-
-/*
- * SCSI Latched Data (p. 3-19).
- * Read/Write latches used to transfer data on the SCSI bus during
- * Automatic or Manual PIO mode.  SCSIDATH can be used for the
- * upper byte of a 16bit wide asynchronous data phase transfer.
- */
-#define SCSIDATL		0x006
-#define SCSIDATH		0x007
-
-/*
- * SCSI Transfer Count (pp. 3-19,20)
- * These registers count down the number of bytes transfered
- * across the SCSI bus.  The counter is decremented only once
- * the data has been safely transfered.  SDONE in SSTAT0 is
- * set when STCNT goes to 0
- */ 
-#define STCNT			0x008
-#define STCNT0			0x008
-#define STCNT1			0x009
-#define STCNT2			0x00a
-
-/*
- * Clear SCSI Interrupt 0 (p. 3-20)
- * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0.
- */
-#define	CLRSINT0		0x00b
-#define		CLRSELDO	0x40
-#define		CLRSELDI	0x20
-#define		CLRSELINGO	0x10
-#define		CLRSWRAP	0x08
-/*  UNUSED			0x04 */
-#define		CLRSPIORDY	0x02
-/*  UNUSED			0x01 */
-
-/*
- * SCSI Status 0 (p. 3-21)
- * Contains one set of SCSI Interrupt codes
- * These are most likely of interest to the sequencer
- */
-#define SSTAT0			0x00b
-#define		TARGET		0x80		/* Board acting as target */
-#define		SELDO		0x40		/* Selection Done */
-#define		SELDI		0x20		/* Board has been selected */
-#define		SELINGO		0x10		/* Selection In Progress */
-#define		SWRAP		0x08		/* 24bit counter wrap */
-#define		SDONE		0x04		/* STCNT = 0x000000 */
-#define		SPIORDY		0x02		/* SCSI PIO Ready */
-#define		DMADONE		0x01		/* DMA transfer completed */
-
-/*
- * Clear SCSI Interrupt 1 (p. 3-23)
- * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1.
- */
-#define CLRSINT1		0x00c
-#define		CLRSELTIMEO	0x80
-#define		CLRATNO		0x40
-#define		CLRSCSIRSTI	0x20
-/*  UNUSED			0x10 */
-#define		CLRBUSFREE	0x08
-#define		CLRSCSIPERR	0x04
-#define		CLRPHASECHG	0x02
-#define		CLRREQINIT	0x01
-
-/*
- * SCSI Status 1 (p. 3-24)
- */
-#define SSTAT1			0x00c
-#define		SELTO		0x80
-#define		ATNTARG 	0x40
-#define		SCSIRSTI	0x20
-#define		PHASEMIS	0x10
-#define		BUSFREE		0x08
-#define		SCSIPERR	0x04
-#define		PHASECHG	0x02
-#define		REQINIT		0x01
-
-/*
- * SCSI Interrupt Mode 1 (pp. 3-28,29)
- * Setting any bit will enable the corresponding function
- * in SIMODE1 to interrupt via the IRQ pin.
- */
-#define	SIMODE1			0x011
-#define		ENSELTIMO	0x80
-#define		ENATNTARG	0x40
-#define		ENSCSIRST	0x20
-#define		ENPHASEMIS	0x10
-#define		ENBUSFREE	0x08
-#define		ENSCSIPERR	0x04
-#define		ENPHASECHG	0x02
-#define		ENREQINIT	0x01
-
-/*
- * SCSI Data Bus (High) (p. 3-29)
- * This register reads data on the SCSI Data bus directly.
- */
-#define	SCSIBUSL		0x012
-#define	SCSIBUSH		0x013
-
-/*
- * SCSI/Host Address (p. 3-30)
- * These registers hold the host address for the byte about to be
- * transfered on the SCSI bus.  They are counted up in the same
- * manner as STCNT is counted down.  SHADDR should always be used
- * to determine the address of the last byte transfered since HADDR
- * can be skewed by write ahead.
- */
-#define	SHADDR			0x014
-#define	SHADDR0			0x014
-#define	SHADDR1			0x015
-#define	SHADDR2			0x016
-#define	SHADDR3			0x017
-
-/*
- * Selection/Reselection ID (p. 3-31)
- * Upper four bits are the device id.  The ONEBIT is set when the re/selecting
- * device did not set its own ID.
- */
-#define SELID			0x019
-#define		SELID_MASK	0xf0
-#define		ONEBIT		0x08
-/*  UNUSED			0x07 */
-
-/*
- * SCSI Block Control (p. 3-32)
- * Controls Bus type and channel selection.  In a twin channel configuration
- * addresses 0x00-0x1e are gated to the appropriate channel based on this
- * register.  SELWIDE allows for the coexistence of 8bit and 16bit devices
- * on a wide bus.
- */
-#define SBLKCTL			0x01f
-#define		DIAGLEDEN	0x80	/* Aic78X0 only */
-#define		DIAGLEDON	0x40	/* Aic78X0 only */
-#define		AUTOFLUSHDIS	0x20
-/*  UNUSED			0x10 */
-#define		SELBUS_MASK	0x0a
-#define		SELBUSB		0x08
-/*  UNUSED			0x04 */
-#define		SELWIDE		0x02
-/*  UNUSED			0x01 */
-#define		SELNARROW	0x00
-
-/*
- * Sequencer Control (p. 3-33)
- * Error detection mode and speed configuration
- */
-#define SEQCTL			0x060
-#define		PERRORDIS	0x80
-#define		PAUSEDIS	0x40
-#define		FAILDIS		0x20
-#define 	FASTMODE	0x10
-#define		BRKADRINTEN	0x08
-#define		STEP		0x04
-#define		SEQRESET	0x02
-#define		LOADRAM		0x01
-
-/*
- * Sequencer RAM Data (p. 3-34)
- * Single byte window into the Scratch Ram area starting at the address
- * specified by SEQADDR0 and SEQADDR1.  To write a full word, simply write
- * four bytes in succession.  The SEQADDRs will increment after the most
- * significant byte is written
- */
-#define SEQRAM			0x061
-
-/*
- * Sequencer Address Registers (p. 3-35)
- * Only the first bit of SEQADDR1 holds addressing information
- */
-#define SEQADDR0		0x062
-#define SEQADDR1		0x063
-#define 	SEQADDR1_MASK	0x01
-
-/*
- * Accumulator
- * We cheat by passing arguments in the Accumulator up to the kernel driver
- */
-#define ACCUM			0x064
-
-#define SINDEX			0x065
-#define DINDEX			0x066
-#define ALLZEROS		0x06a
-#define NONE			0x06a
-#define SINDIR			0x06c
-#define DINDIR			0x06d
-#define FUNCTION1		0x06e
-
-/*
- * Host Address (p. 3-48)
- * This register contains the address of the byte about
- * to be transfered across the host bus.
- */
-#define HADDR			0x088
-#define HADDR0			0x088
-#define HADDR1			0x089
-#define HADDR2			0x08a
-#define HADDR3			0x08b
-
-#define HCNT			0x08c
-#define HCNT0			0x08c
-#define HCNT1			0x08d
-#define HCNT2			0x08e
-/*
- * SCB Pointer (p. 3-49)
- * Gate one of the four SCBs into the SCBARRAY window.
- */
-#define SCBPTR			0x090
-
-/*
- * Board Control (p. 3-43)
- */
-#define BCTL			0x084
-/*   RSVD			0xf0 */
-#define		ACE		0x08	/* Support for external processors */
-/*   RSVD			0x06 */
-#define		ENABLE		0x01
-
-/*
- * On the aic78X0 chips, Board Control is replaced by the DSCommand
- * register (p. 4-64)
- */
-#define	DSCOMMAND		0x084
-#define		CACHETHEN	0x80	/* Cache Threshold enable */
-#define		DPARCKEN	0x40	/* Data Parity Check Enable */
-#define		MPARCKEN	0x20	/* Memory Parity Check Enable */
-#define		EXTREQLCK	0x10	/* External Request Lock */
-
-/*
- * Bus On/Off Time (p. 3-44)
- */
-#define BUSTIME			0x085
-#define		BOFF		0xf0
-#define		BON		0x0f
-
-/*
- * Bus Speed (p. 3-45)
- */
-#define	BUSSPD			0x086
-#define		DFTHRSH		0xc0
-#define		STBOFF		0x38
-#define		STBON		0x07
-#define		DFTHRSH_100	0xc0
-
-/*
- * Host Control (p. 3-47) R/W
- * Overall host control of the device.
- */
-#define HCNTRL			0x087
-/*    UNUSED			0x80 */
-#define		POWRDN		0x40
-/*    UNUSED			0x20 */
-#define		SWINT		0x10
-#define		IRQMS		0x08
-#define		PAUSE		0x04
-#define		INTEN		0x02
-#define		CHIPRST		0x01
-#define		CHIPRSTACK	0x01
-
-/*
- * Interrupt Status (p. 3-50)
- * Status for system interrupts
- */
-#define INTSTAT			0x091
-#define		SEQINT_MASK	0xf1		/* SEQINT Status Codes */
-#define			BAD_PHASE	0x01	/* unknown scsi bus phase */
-#define			SEND_REJECT	0x11	/* sending a message reject */
-#define			NO_IDENT	0x21	/* no IDENTIFY after reconnect*/
-#define			NO_MATCH	0x31	/* no cmd match for reconnect */
-#define			SDTR_MSG	0x41	/* SDTR message received */
-#define			WDTR_MSG	0x51	/* WDTR message received */
-#define			REJECT_MSG	0x61	/* Reject message received */
-#define			BAD_STATUS	0x71	/* Bad status from target */
-#define			RESIDUAL	0x81	/* Residual byte count != 0 */
-#define			ABORT_TAG	0x91	/* Sent an ABORT_TAG message */
-#define			AWAITING_MSG	0xa1	/*
-						 * Kernel requested to specify
-                                                 * a message to this target
-                                                 * (command was null), so tell
-                                                 * it that it can fill the
-                                                 * message buffer.
-                                                 */
-#define			IMMEDDONE	0xb1	/*
-						 * An immediate command has
-						 * completed
-						 */
-#define			MSG_BUFFER_BUSY	0xc1	/*
-						 * Sequencer wants to use the
-						 * message buffer, but it
-						 * already contains a message
-						 */
-#define			MSGIN_PHASEMIS	0xd1	/*
-						 * Target changed phase on us
-						 * when we were expecting
-						 * another msgin byte.
-						 */
-#define			DATA_OVERRUN	0xe1	/*
-						 * Target attempted to write
-						 * beyond the bounds of its
-						 * command.
-						 */
-#define 	BRKADRINT 0x08
-#define		SCSIINT	  0x04
-#define		CMDCMPLT  0x02
-#define		SEQINT    0x01
-#define		INT_PEND  (BRKADRINT | SEQINT | SCSIINT | CMDCMPLT)
-
-/*
- * Hard Error (p. 3-53)
- * Reporting of catastrophic errors.  You usually cannot recover from
- * these without a full board reset.
- */
-#define ERROR			0x092
-/*    UNUSED			0xf0 */
-#define		PARERR		0x08
-#define		ILLOPCODE	0x04
-#define		ILLSADDR	0x02
-#define		ILLHADDR	0x01
-
-/*
- * Clear Interrupt Status (p. 3-52)
- */
-#define CLRINT			0x092
-#define		CLRBRKADRINT	0x08
-#define		CLRSCSIINT      0x04
-#define		CLRCMDINT 	0x02
-#define		CLRSEQINT 	0x01
-
-#define	DFCNTRL			0x093
-#define		WIDEODD		0x40
-#define		SCSIEN		0x20
-#define		SDMAEN		0x10
-#define		SDMAENACK	0x10
-#define		HDMAEN		0x08
-#define		HDMAENACK	0x08
-#define		DIRECTION	0x04
-#define		FIFOFLUSH	0x02
-#define		FIFORESET	0x01
-
-#define	DFSTATUS		0x094
-#define		HDONE		0x08
-#define		FIFOEMP		0x01
-
-#define	DFDAT			0x099
-
-/*
- * SCB Auto Increment (p. 3-59)
- * Byte offset into the SCB Array and an optional bit to allow auto
- * incrementing of the address during download and upload operations
- */
-#define SCBCNT			0x09a
-#define		SCBAUTO		0x80
-#define		SCBCNT_MASK	0x1f
-
-/*
- * Queue In FIFO (p. 3-60)
- * Input queue for queued SCBs (commands that the sequencer has yet to start)
- */
-#define QINFIFO			0x09b
-
-/*
- * Queue In Count (p. 3-60)
- * Number of queued SCBs
- */
-#define QINCNT			0x09c
-
-/*
- * Queue Out FIFO (p. 3-61)
- * Queue of SCBs that have completed and await the host
- */
-#define QOUTFIFO		0x09d
-
-/*
- * Queue Out Count (p. 3-61)
- * Number of queued SCBs in the Out FIFO
- */
-#define QOUTCNT			0x09e
-
-/*
- * SCB Definition (p. 5-4)
- * The two reserved bytes at SCBARRAY+1[23] are expected to be set to
- * zero. Bit 3 in SCBARRAY+0 is used as an internal flag to indicate
- * whether or not to DMA an SCB from host ram. This flag prevents the
- * "re-fetching" of transactions that are requeued because the target is
- * busy with another command. We also use bits 6 & 7 to indicate whether
- * or not to initiate SDTR or WDTR respectively when starting this command.
- */
-#define SCBARRAY		0x0a0
-#define	SCB_CONTROL		0x0a0
-#define		NEEDWDTR        0x80
-#define		DISCENB         0x40
-#define		TAG_ENB		0x20
-#define		NEEDSDTR	0x10
-#define		DISCONNECTED	0x04
-#define		SCB_TAG_TYPE	0x03
-#define	SCB_TCL			0x0a1
-#define	SCB_TARGET_STATUS	0x0a2
-#define	SCB_SGCOUNT		0x0a3
-#define	SCB_SGPTR		0x0a4
-#define		SCB_SGPTR0	0x0a4
-#define		SCB_SGPTR1	0x0a5
-#define		SCB_SGPTR2	0x0a6
-#define		SCB_SGPTR3	0x0a7
-#define	SCB_RESID_SGCNT		0x0a8
-#define SCB_RESID_DCNT		0x0a9
-#define		SCB_RESID_DCNT0	0x0a9
-#define		SCB_RESID_DCNT1	0x0aa
-#define		SCB_RESID_DCNT2	0x0ab
-#define SCB_DATAPTR		0x0ac
-#define		SCB_DATAPTR0	0x0ac
-#define		SCB_DATAPTR1	0x0ad
-#define		SCB_DATAPTR2	0x0ae
-#define		SCB_DATAPTR3	0x0af
-#define	SCB_DATACNT		0x0b0
-#define		SCB_DATACNT0	0x0b0
-#define		SCB_DATACNT1	0x0b1
-#define		SCB_DATACNT2	0x0b2
-/* UNUSED - QUAD PADDING	0x0b3 */
-#define SCB_CMDPTR		0x0b4
-#define		SCB_CMDPTR0	0x0b4
-#define		SCB_CMDPTR1	0x0b5
-#define		SCB_CMDPTR2	0x0b6
-#define		SCB_CMDPTR3	0x0b7
-#define	SCB_CMDLEN		0x0b8
-#define SCB_TAG			0x0b9
-#define	SCB_NEXT		0x0ba
-#define	SCB_PREV		0x0bb
-
-#define	SG_SIZEOF		0x08		/* sizeof(struct ahc_dma) */
-
-/* --------------------- AHA-2840-only definitions -------------------- */
-
-#define	SEECTL_2840		0x0c0
-/*	UNUSED			0xf8 */
-#define		CS_2840		0x04
-#define		CK_2840		0x02
-#define		DO_2840		0x01
-
-#define	STATUS_2840		0x0c1
-#define		EEPROM_TF	0x80
-#define		BIOS_SEL	0x60
-#define		ADSEL		0x1e
-#define		DI_2840		0x01
-
-/* --------------------- AIC-7870-only definitions -------------------- */
-
-#define DSPCISTATUS		0x086
-
-/*
- * Serial EEPROM Control (p. 4-92 in 7870 Databook)
- * Controls the reading and writing of an external serial 1-bit
- * EEPROM Device.  In order to access the serial EEPROM, you must
- * first set the SEEMS bit that generates a request to the memory
- * port for access to the serial EEPROM device.  When the memory
- * port is not busy servicing another request, it reconfigures
- * to allow access to the serial EEPROM.  When this happens, SEERDY
- * gets set high to verify that the memory port access has been
- * granted.  
- *
- * After successful arbitration for the memory port, the SEECS bit of 
- * the SEECTL register is connected to the chip select.  The SEECK, 
- * SEEDO, and SEEDI are connected to the clock, data out, and data in 
- * lines respectively.  The SEERDY bit of SEECTL is useful in that it 
- * gives us an 800 nsec timer.  After a write to the SEECTL register, 
- * the SEERDY goes high 800 nsec later.  The one exception to this is 
- * when we first request access to the memory port.  The SEERDY goes 
- * high to signify that access has been granted and, for this case, has 
- * no implied timing.
- *
- * See 93cx6.c for detailed information on the protocol necessary to 
- * read the serial EEPROM.
- */
-#define SEECTL			0x01e
-#define		EXTARBACK	0x80
-#define		EXTARBREQ	0x40
-#define		SEEMS		0x20
-#define		SEERDY		0x10
-#define		SEECS		0x08
-#define		SEECK		0x04
-#define		SEEDO		0x02
-#define		SEEDI		0x01
-
-/* ---------------------- Scratch RAM Offsets ------------------------- */
-/* These offsets are either to values that are initialized by the board's
- * BIOS or are specified by the sequencer code.
- *
- * The host adapter card (at least the BIOS) uses 20-2f for SCSI
- * device information, 32-33 and 5a-5f as well. As it turns out, the
- * BIOS trashes 20-2f, writing the synchronous negotiation results
- * on top of the BIOS values, so we re-use those for our per-target
- * scratchspace (actually a value that can be copied directly into
- * SCSIRATE).  The kernel driver will enable synchronous negotiation
- * for all targets that have a value other than 0 in the lower four
- * bits of the target scratch space.  This should work regardless of
- * whether the bios has been installed.
- */
-
-/*
- * 1 byte per target starting at this address for configuration values
- */
-#define TARG_SCRATCH		0x020
-
-/*
- * The sequencer will stick the frist byte of any rejected message here so
- * we can see what is getting thrown away.  Extended messages put the
- * extended message type in REJBYTE_EXT.
- */
-#define REJBYTE			0x030
-#define REJBYTE_EXT		0x031
-
-/*
- * Bit vector of targets that have disconnection disabled.
- */
-#define	DISC_DSB		0x032
-#define		DISC_DSB_A	0x032
-#define		DISC_DSB_B	0x033
-
-/*
- * Length of pending message
- */
-#define MSG_LEN			0x034
-
-/* We reserve 8bytes to store outgoing messages */
-#define MSG0			0x035
-#define		COMP_MSG0	0xcb      /* 2's complement of MSG0 */
-#define MSG1			0x036
-#define MSG2			0x037
-#define MSG3			0x038
-#define MSG4			0x039
-#define MSG5			0x03a
-#define MSG6			0x03b
-#define MSG7			0x03c
-
 /*
- * These are offsets into the card's scratch ram.  Some of the values are
- * specified in the AHA2742 technical reference manual and are initialized
- * by the BIOS at boot time.
- */
-#define LASTPHASE		0x03d
-#define ARG_1			0x03e
-#define		MAXOFFSET	0x01
-#define RETURN_1		0x03f
-#define		SEND_WDTR	0x80
-#define		SEND_SDTR	0x60
-#define		SEND_SENSE	0x40
-#define		SEND_REJ	0x20
-#define		SCB_PAGEDIN	0x10
-
-#define SIGSTATE		0x040
-
-#define DMAPARAMS		0x041	/* Parameters for DMA Logic */
-
-#define	SG_COUNT		0x042
-#define	SG_NEXT			0x043	/* working value of SG pointer */
-#define		SG_NEXT0	0x043
-#define		SG_NEXT1	0x044
-#define		SG_NEXT2	0x045
-#define		SG_NEXT3	0x046
-
-#define	SCBCOUNT		0x047	/*
-					 * Number of SCBs supported by
-					 * this card.
-					 */
-#define	COMP_SCBCOUNT		0x048	/*
-					 * Two's compliment of SCBCOUNT
-					 */
-#define QCNTMASK		0x049	/*
-					 * Mask of bits to test against
-					 * when looking at the Queue Count
-					 * registers.  Works around a bug
-					 * on aic7850 chips. 
-					 */
-#define FLAGS			0x04a
-#define		SINGLE_BUS	0x00
-#define		TWIN_BUS	0x01
-#define		WIDE_BUS	0x02
-#define		PAGESCBS	0x04
-#define		DPHASE		0x10
-#define		SELECTED	0x20
-#define		IDENTIFY_SEEN	0x40
-#define		RESELECTED	0x80
-
-#define	SAVED_TCL		0x04b	/*
-					 * Temporary storage for the
-					 * target/channel/lun of a
-					 * reconnecting target
-					 */
-#define	ACTIVE_A		0x04c
-#define	ACTIVE_B		0x04d
-#define WAITING_SCBH		0x04e	/*
-					 * head of list of SCBs awaiting
-					 * selection
-					 */
-#define DISCONNECTED_SCBH	0x04f	/*
-					 * head of list of SCBs that are
-					 * disconnected.  Used for SCB
-					 * paging.
-					 */
-#define		SCB_LIST_NULL	0xff
-
-#define SAVED_LINKPTR		0x050
-#define SAVED_SCBPTR		0x051
-#define ULTRA_ENB		0x052
-#define ULTRA_ENB_B		0x053
-
-#define SCSICONF		0x05a
-#define		RESET_SCSI	0x40
-
-#define HOSTCONF		0x05d
-
-#define HA_274_BIOSCTRL		0x05f
-#define BIOSMODE		0x30
-#define BIOSDISABLED		0x30
-#define CHANNEL_B_PRIMARY	0x08
-
-/* Message codes */
-#define MSG_EXTENDED		0x01
-#define		MSG_SDTR	0x01
-#define		MSG_WDTR	0x03
-#define MSG_SDPTRS		0x02
-#define MSG_RDPTRS		0x03
-#define MSG_DISCONNECT		0x04
-#define MSG_INITIATOR_DET_ERROR	0x05
-#define MSG_ABORT		0x06
-#define	MSG_REJECT		0x07
-#define MSG_NOP			0x08
-#define MSG_MSG_PARITY_ERROR	0x09
-#define MSG_BUS_DEVICE_RESET	0x0c
-#define MSG_ABORT_TAG		0x0d
-#define MSG_SIMPLE_TAG		0x20
-#define MSG_IDENTIFY		0x80
-
-/* WDTR Message values */
-#define	BUS_8_BIT		0x00
-#define BUS_16_BIT		0x01
-#define BUS_32_BIT		0x02
+  * DO NOT EDIT - This file is automatically generated.
+  */
 
-#define MAX_OFFSET_8BIT		0x0f
-#define MAX_OFFSET_16BIT	0x08
+#define	SCSISEQ         		0x00
+#define		TEMODE          	0x80
+#define		ENSELO          	0x40
+#define		ENSELI          	0x20
+#define		ENRSELI         	0x10
+#define		ENAUTOATNO      	0x08
+#define		ENAUTOATNI      	0x04
+#define		ENAUTOATNP      	0x02
+#define		SCSIRSTO        	0x01
+
+#define	SXFRCTL0        		0x01
+#define		DFON            	0x80
+#define		DFPEXP          	0x40
+#define		FAST20          	0x20
+#define		CLRSTCNT        	0x10
+#define		SPIOEN          	0x08
+#define		SCAMEN          	0x04
+#define		CLRCHN          	0x02
+
+#define	SXFRCTL1        		0x02
+#define		BITBUCKET       	0x80
+#define		SWRAPEN         	0x40
+#define		ENSPCHK         	0x20
+#define		STIMESEL        	0x18
+#define		ENSTIMER        	0x04
+#define		ACTNEGEN        	0x02
+#define		STPWEN          	0x01
+
+#define	SCSISIGO        		0x03
+#define		CDO             	0x80
+#define		IOO             	0x40
+#define		MSGO            	0x20
+#define		ATNO            	0x10
+#define		SELO            	0x08
+#define		BSYO            	0x04
+#define		REQO            	0x02
+#define		ACKO            	0x01
+
+#define	SCSISIGI        		0x03
+#define		ATNI            	0x10
+#define		SELI            	0x08
+#define		BSYI            	0x04
+#define		REQI            	0x02
+#define		ACKI            	0x01
+
+#define	SCSIRATE        		0x04
+#define		WIDEXFER        	0x80
+#define		SXFR            	0x70
+#define		SOFS            	0x0f
+
+#define	SCSIID          		0x05
+#define		OID             	0x0f
+
+#define	SCSIDATL        		0x06
+
+#define	SCSIDATH        		0x07
+
+#define	STCNT           		0x08
+
+#define	CLRSINT0        		0x0b
+#define		CLRSELDO        	0x40
+#define		CLRSELDI        	0x20
+#define		CLRSELINGO      	0x10
+#define		CLRSWRAP        	0x08
+#define		CLRSPIORDY      	0x02
+
+#define	SSTAT0          		0x0b
+#define		TARGET          	0x80
+#define		SELDO           	0x40
+#define		SELDI           	0x20
+#define		SELINGO         	0x10
+#define		SWRAP           	0x08
+#define		SDONE           	0x04
+#define		SPIORDY         	0x02
+#define		DMADONE         	0x01
+
+#define	CLRSINT1        		0x0c
+#define		CLRSELTIMEO     	0x80
+#define		CLRATNO         	0x40
+#define		CLRSCSIRSTI     	0x20
+#define		CLRBUSFREE      	0x08
+#define		CLRSCSIPERR     	0x04
+#define		CLRPHASECHG     	0x02
+#define		CLRREQINIT      	0x01
+
+#define	SSTAT1          		0x0c
+#define		SELTO           	0x80
+#define		ATNTARG         	0x40
+#define		SCSIRSTI        	0x20
+#define		PHASEMIS        	0x10
+#define		BUSFREE         	0x08
+#define		SCSIPERR        	0x04
+#define		PHASECHG        	0x02
+#define		REQINIT         	0x01
+
+#define	SSTAT2          		0x0d
+#define		OVERRUN         	0x80
+#define		SFCNT           	0x1f
+
+#define	SSTAT3          		0x0e
+#define		SCSICNT         	0xf0
+#define		OFFCNT          	0x0f
+
+#define	SCSITEST        		0x0f
+#define		RQAKCNT         	0x04
+#define		CNTRTEST        	0x02
+#define		CMODE           	0x01
+
+#define	SIMODE0         		0x10
+#define		ENSELDO         	0x40
+#define		ENSELDI         	0x20
+#define		ENSELINGO       	0x10
+#define		ENSWRAP         	0x08
+#define		ENSDONE         	0x04
+#define		ENSPIORDY       	0x02
+#define		ENDMADONE       	0x01
+
+#define	SIMODE1         		0x11
+#define		ENSELTIMO       	0x80
+#define		ENATNTARG       	0x40
+#define		ENSCSIRST       	0x20
+#define		ENPHASEMIS      	0x10
+#define		ENBUSFREE       	0x08
+#define		ENSCSIPERR      	0x04
+#define		ENPHASECHG      	0x02
+#define		ENREQINIT       	0x01
+
+#define	SCSIBUSL        		0x12
+
+#define	SCSIBUSH        		0x13
+
+#define	SHADDR          		0x14
+
+#define	SELTIMER        		0x18
+#define		STAGE6          	0x20
+#define		STAGE5          	0x10
+#define		STAGE4          	0x08
+#define		STAGE3          	0x04
+#define		STAGE2          	0x02
+#define		STAGE1          	0x01
+
+#define	SELID           		0x19
+#define		SELID_MASK      	0xf0
+#define		ONEBIT          	0x08
+
+#define	BRDCTL          		0x1d
+#define		BRDDAT7         	0x80
+#define		BRDDAT6         	0x40
+#define		BRDDAT5         	0x20
+#define		BRDSTB          	0x10
+#define		BRDCS           	0x08
+#define		BRDRW           	0x04
+#define		BRDCTL1         	0x02
+#define		BRDCTL0         	0x01
+
+#define	SEECTL          		0x1e
+#define		EXTARBACK       	0x80
+#define		EXTARBREQ       	0x40
+#define		SEEMS           	0x20
+#define		SEERDY          	0x10
+#define		SEECS           	0x08
+#define		SEECK           	0x04
+#define		SEEDO           	0x02
+#define		SEEDI           	0x01
+
+#define	SBLKCTL         		0x1f
+#define		DIAGLEDEN       	0x80
+#define		DIAGLEDON       	0x40
+#define		AUTOFLUSHDIS    	0x20
+#define		SELWIDE         	0x02
+
+#define	SRAM_BASE       		0x20
+
+#define	TARG_SCRATCH    		0x20
+
+#define	ULTRA_ENB       		0x30
+
+#define	DISC_DSB        		0x32
+
+#define	MSG_LEN         		0x34
+
+#define	MSG_OUT         		0x35
+
+#define	DMAPARAMS       		0x3d
+#define		WIDEODD         	0x40
+#define		SCSIEN          	0x20
+#define		SDMAENACK       	0x10
+#define		SDMAEN          	0x10
+#define		HDMAEN          	0x08
+#define		HDMAENACK       	0x08
+#define		DIRECTION       	0x04
+#define		FIFOFLUSH       	0x02
+#define		FIFORESET       	0x01
+
+#define	SCBCOUNT        		0x3e
+
+#define	COMP_SCBCOUNT   		0x3f
+
+#define	QCNTMASK        		0x40
+
+#define	SEQ_FLAGS       		0x41
+#define		RESELECTED      	0x80
+#define		IDENTIFY_SEEN   	0x40
+#define		TAGGED_SCB      	0x20
+#define		DPHASE          	0x10
+#define		PAGESCBS        	0x04
+#define		WIDE_BUS        	0x02
+#define		TWIN_BUS        	0x01
+
+#define	SAVED_TCL       		0x42
+
+#define	SG_COUNT        		0x43
+
+#define	SG_NEXT         		0x44
+
+#define	WAITING_SCBH    		0x48
+
+#define	SAVED_LINKPTR   		0x49
+
+#define	SAVED_SCBPTR    		0x4a
+
+#define	REJBYTE         		0x4b
+
+#define	LASTPHASE       		0x4c
+#define		P_MESGIN        	0xe0
+#define		PHASE_MASK      	0xe0
+#define		P_STATUS        	0xc0
+#define		P_MESGOUT       	0xa0
+#define		P_COMMAND       	0x80
+#define		CDI             	0x80
+#define		IOI             	0x40
+#define		P_DATAIN        	0x40
+#define		MSGI            	0x20
+#define		P_BUSFREE       	0x01
+#define		P_DATAOUT       	0x00
+
+#define	MSGIN_EXT_LEN   		0x4d
+
+#define	MSGIN_EXT_OPCODE		0x4e
+
+#define	MSGIN_EXT_BYTES 		0x4f
+
+#define	DISCONNECTED_SCBH		0x52
+
+#define	FREE_SCBH       		0x53
+
+#define	HSCB_ADDR       		0x54
+
+#define	CUR_SCBID       		0x58
+
+#define	CMDOUTCNT       		0x59
+
+#define	SCSICONF        		0x5a
+#define		RESET_SCSI      	0x40
+
+#define	FIFODEPTH       		0x5a
+
+#define	ARG_1           		0x5b
+#define	RETURN_1        		0x5b
+#define		SEND_MSG        	0x80
+#define		SEND_SENSE      	0x40
+#define		SEND_REJ        	0x20
+
+#define	HOSTCONF        		0x5d
+
+#define	HA_274_BIOSCTRL 		0x5f
+#define		BIOSMODE        	0x30
+#define		BIOSDISABLED    	0x30
+#define		CHANNEL_B_PRIMARY	0x08
+
+#define	SEQCTL          		0x60
+#define		PERRORDIS       	0x80
+#define		PAUSEDIS        	0x40
+#define		FAILDIS         	0x20
+#define		FASTMODE        	0x10
+#define		BRKADRINTEN     	0x08
+#define		STEP            	0x04
+#define		SEQRESET        	0x02
+#define		LOADRAM         	0x01
+
+#define	SEQRAM          		0x61
+
+#define	SEQADDR0        		0x62
+
+#define	SEQADDR1        		0x63
+#define		SEQADDR1_MASK   	0x01
+
+#define	ACCUM           		0x64
+
+#define	SINDEX          		0x65
+
+#define	DINDEX          		0x66
+
+#define	ALLONES         		0x69
+
+#define	ALLZEROS        		0x6a
+
+#define	NONE            		0x6a
+
+#define	FLAGS           		0x6b
+#define		ZERO            	0x02
+#define		CARRY           	0x01
+
+#define	SINDIR          		0x6c
+
+#define	DINDIR          		0x6d
+
+#define	FUNCTION1       		0x6e
+
+#define	STACK           		0x6f
+
+#define	BCTL            		0x84
+#define		ACE             	0x08
+#define		ENABLE          	0x01
+
+#define	DSCOMMAND       		0x84
+#define		CACHETHEN       	0x80
+#define		DPARCKEN        	0x40
+#define		MPARCKEN        	0x20
+#define		EXTREQLCK       	0x10
+
+#define	BUSTIME         		0x85
+#define		BOFF            	0xf0
+#define		BON             	0x0f
+
+#define	BUSSPD          		0x86
+#define		DFTHRSH_100     	0xc0
+#define		DFTHRSH         	0xc0
+#define		STBOFF          	0x38
+#define		STBON           	0x07
+
+#define	DSPCISTATUS     		0x86
+
+#define	HCNTRL          		0x87
+#define		POWRDN          	0x40
+#define		SWINT           	0x10
+#define		IRQMS           	0x08
+#define		PAUSE           	0x04
+#define		INTEN           	0x02
+#define		CHIPRST         	0x01
+#define		CHIPRSTACK      	0x01
+
+#define	HADDR           		0x88
+
+#define	HCNT            		0x8c
+
+#define	SCBPTR          		0x90
+
+#define	INTSTAT         		0x91
+#define		SEQINT_MASK     	0xf1
+#define		DATA_OVERRUN    	0xe1
+#define		MSGIN_PHASEMIS  	0xd1
+#define		MSG_BUFFER_BUSY 	0xc1
+#define		AWAITING_MSG    	0xa1
+#define		ABORT_CMDCMPLT  	0x91
+#define		RESIDUAL        	0x81
+#define		BAD_STATUS      	0x71
+#define		REJECT_MSG      	0x61
+#define		NO_MATCH_BUSY   	0x51
+#define		EXTENDED_MSG    	0x41
+#define		NO_MATCH        	0x31
+#define		NO_IDENT        	0x21
+#define		SEND_REJECT     	0x11
+#define		INT_PEND        	0x0f
+#define		BRKADRINT       	0x08
+#define		SCSIINT         	0x04
+#define		CMDCMPLT        	0x02
+#define		BAD_PHASE       	0x01
+#define		SEQINT          	0x01
+
+#define	CLRINT          		0x92
+#define		CLRBRKADRINT    	0x08
+#define		CLRSCSIINT      	0x04
+#define		CLRCMDINT       	0x02
+#define		CLRSEQINT       	0x01
+
+#define	ERROR           		0x92
+#define		PARERR          	0x08
+#define		ILLOPCODE       	0x04
+#define		ILLSADDR        	0x02
+#define		ILLHADDR        	0x01
+
+#define	DFCNTRL         		0x93
+
+#define	DFSTATUS        		0x94
+#define		DWORDEMP        	0x20
+#define		MREQPEND        	0x10
+#define		HDONE           	0x08
+#define		DFTHRESH        	0x04
+#define		FIFOFULL        	0x02
+#define		FIFOEMP         	0x01
+
+#define	DFDAT           		0x99
+
+#define	SCBCNT          		0x9a
+#define		SCBAUTO         	0x80
+#define		SCBCNT_MASK     	0x1f
+
+#define	QINFIFO         		0x9b
+
+#define	QINCNT          		0x9c
+
+#define	QOUTFIFO        		0x9d
+
+#define	QOUTCNT         		0x9e
+
+#define	SCB_CONTROL     		0xa0
+#define		MK_MESSAGE      	0x80
+#define		DISCENB         	0x40
+#define		TAG_ENB         	0x20
+#define		MUST_DMAUP_SCB  	0x10
+#define		ABORT_SCB       	0x08
+#define		DISCONNECTED    	0x04
+#define		SCB_TAG_TYPE    	0x03
+
+#define	SCB_BASE        		0xa0
+
+#define	SCB_TCL         		0xa1
+#define		TID             	0xf0
+#define		SELBUSB         	0x08
+#define		LID             	0x07
+
+#define	SCB_TARGET_STATUS		0xa2
+
+#define	SCB_SGCOUNT     		0xa3
+
+#define	SCB_SGPTR       		0xa4
+
+#define	SCB_RESID_SGCNT 		0xa8
+
+#define	SCB_RESID_DCNT  		0xa9
+
+#define	SCB_DATAPTR     		0xac
+
+#define	SCB_DATACNT     		0xb0
+
+#define	SCB_LINKED_NEXT 		0xb3
+
+#define	SCB_CMDPTR      		0xb4
+
+#define	SCB_CMDLEN      		0xb8
+
+#define	SCB_TAG         		0xb9
+
+#define	SCB_NEXT        		0xba
+
+#define	SCB_PREV        		0xbb
+
+#define	SCB_BUSYTARGETS 		0xbc
+
+#define	SEECTL_2840     		0xc0
+#define		CS_2840         	0x04
+#define		CK_2840         	0x02
+#define		DO_2840         	0x01
+
+#define	STATUS_2840     		0xc1
+#define		EEPROM_TF       	0x80
+#define		BIOS_SEL        	0x60
+#define		ADSEL           	0x1e
+#define		DI_2840         	0x01
+
+
+#define	BUS_8_BIT	0x00
+#define	MAX_OFFSET_8BIT	0x0f
+#define	BUS_16_BIT	0x01
+#define	MAX_OFFSET_16BIT	0x08
+#define	SCB_LIST_NULL	0xff
+#define	SG_SIZEOF	0x08
+#define	BUS_32_BIT	0x02
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx_seq.h linux/drivers/scsi/aic7xxx_seq.h
--- v2.0.30/linux/drivers/scsi/aic7xxx_seq.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx_seq.h	Wed Aug 13 09:53:45 1997
@@ -0,0 +1,483 @@
+/*
+  * DO NOT EDIT - This file is automatically generated.
+  */
+static u_int8_t seqprog[] = {
+	0xff, 0x6a, 0x03, 0x02,
+	0x12, 0x6a, 0x00, 0x00,
+	0x00, 0x65, 0x6f, 0x16,
+	0x40, 0x0b, 0x3c, 0x1a,
+	0x20, 0x0b, 0x37, 0x1a,
+	0x40, 0x00, 0x03, 0x1a,
+	0x08, 0x1f, 0x1f, 0x04,
+	0x40, 0x0b, 0x3c, 0x1a,
+	0x20, 0x0b, 0x37, 0x1a,
+	0x40, 0x00, 0x03, 0x1a,
+	0x08, 0x1f, 0x1f, 0x04,
+	0xff, 0x48, 0x2c, 0x18,
+	0xff, 0x40, 0x64, 0x02,
+	0x00, 0x9c, 0x03, 0x1e,
+	0x00, 0x6a, 0xad, 0x17,
+	0xff, 0x65, 0x03, 0x1c,
+	0xff, 0x9b, 0x58, 0x02,
+	0xff, 0x58, 0x90, 0x02,
+	0x0d, 0x6a, 0x3d, 0x00,
+	0x00, 0x58, 0x77, 0x17,
+	0x28, 0xa0, 0x2a, 0x1a,
+	0x50, 0x6a, 0x60, 0x00,
+	0xff, 0x90, 0x4a, 0x02,
+	0x00, 0xa1, 0xa1, 0x17,
+	0xff, 0x6c, 0x5b, 0x02,
+	0xff, 0x5b, 0x27, 0x1c,
+	0xff, 0x4a, 0x90, 0x02,
+	0x00, 0x65, 0xaa, 0x17,
+	0x00, 0x6a, 0x52, 0x17,
+	0xff, 0x65, 0x1f, 0x18,
+	0x51, 0x6a, 0x91, 0x00,
+	0xff, 0x58, 0xb3, 0x02,
+	0x00, 0x65, 0xbb, 0x17,
+	0x10, 0x6a, 0x60, 0x00,
+	0x00, 0x65, 0x03, 0x10,
+	0xff, 0x5b, 0x90, 0x02,
+	0xff, 0x58, 0xb3, 0x02,
+	0x10, 0x6a, 0x60, 0x00,
+	0x00, 0x65, 0x03, 0x10,
+	0xff, 0x58, 0x6d, 0x02,
+	0xff, 0x4a, 0x90, 0x02,
+	0x10, 0x6a, 0x60, 0x00,
+	0xff, 0x48, 0xba, 0x02,
+	0xff, 0x90, 0x48, 0x02,
+	0xff, 0x48, 0x90, 0x02,
+	0x00, 0x65, 0x2f, 0x16,
+	0x00, 0x65, 0x03, 0x10,
+	0xf7, 0x1f, 0x65, 0x02,
+	0x08, 0xa1, 0x64, 0x02,
+	0x00, 0x65, 0x65, 0x00,
+	0xff, 0x65, 0x1f, 0x02,
+	0xf0, 0xa1, 0x64, 0x02,
+	0x0f, 0x05, 0x05, 0x02,
+	0x00, 0x05, 0x05, 0x00,
+	0x5a, 0x6a, 0x00, 0x01,
+	0xff, 0x6a, 0x34, 0x02,
+	0x20, 0x6a, 0x0b, 0x00,
+	0xf0, 0x19, 0x42, 0x02,
+	0x80, 0x41, 0x41, 0x00,
+	0x00, 0x65, 0x4c, 0x10,
+	0x12, 0x6a, 0x00, 0x00,
+	0x40, 0x6a, 0x0b, 0x00,
+	0xff, 0x48, 0x90, 0x02,
+	0xff, 0xba, 0x48, 0x02,
+	0xff, 0xa1, 0x42, 0x02,
+	0x07, 0xa1, 0x35, 0x02,
+	0x40, 0xa0, 0x64, 0x02,
+	0x00, 0x35, 0x35, 0x00,
+	0x80, 0x35, 0x35, 0x00,
+	0x01, 0x6a, 0x34, 0x00,
+	0x20, 0xa0, 0x4a, 0x1e,
+	0x23, 0xa0, 0x36, 0x02,
+	0xff, 0xb9, 0x37, 0x02,
+	0x02, 0x34, 0x34, 0x06,
+	0x80, 0xa0, 0x4c, 0x1e,
+	0xa1, 0x6a, 0x91, 0x00,
+	0x08, 0x6a, 0x0c, 0x00,
+	0x08, 0x11, 0x11, 0x00,
+	0x1a, 0x01, 0x01, 0x00,
+	0x31, 0x6a, 0x65, 0x00,
+	0x80, 0x42, 0x52, 0x1a,
+	0xff, 0x65, 0x65, 0x06,
+	0xff, 0x42, 0x6e, 0x02,
+	0xff, 0x6e, 0x64, 0x02,
+	0x00, 0x6c, 0x56, 0x1e,
+	0x20, 0x01, 0x01, 0x00,
+	0x4c, 0x42, 0x64, 0x0a,
+	0x08, 0x1f, 0x5a, 0x1e,
+	0x08, 0x42, 0x42, 0x00,
+	0x08, 0x64, 0x64, 0x00,
+	0x20, 0x64, 0x65, 0x06,
+	0xff, 0x6c, 0x04, 0x02,
+	0x01, 0x0c, 0x5c, 0x1e,
+	0x04, 0x0c, 0x5c, 0x1a,
+	0xe0, 0x03, 0x64, 0x02,
+	0xff, 0x64, 0x4c, 0x02,
+	0xff, 0x64, 0x03, 0x02,
+	0x00, 0x6a, 0x74, 0x1c,
+	0x40, 0x64, 0x79, 0x1c,
+	0x80, 0x64, 0xa5, 0x1c,
+	0xa0, 0x64, 0xb0, 0x1c,
+	0xc0, 0x64, 0xad, 0x1c,
+	0xe0, 0x64, 0xc3, 0x1c,
+	0x01, 0x6a, 0x91, 0x00,
+	0x00, 0x65, 0x5c, 0x10,
+	0xf7, 0x11, 0x11, 0x02,
+	0x00, 0x65, 0x6f, 0x16,
+	0xff, 0x06, 0x6a, 0x02,
+	0x09, 0x0c, 0x6c, 0x1e,
+	0x08, 0x0c, 0x03, 0x1a,
+	0x01, 0x6a, 0x91, 0x00,
+	0xff, 0x6a, 0x93, 0x02,
+	0xff, 0x6a, 0x04, 0x02,
+	0xdf, 0x01, 0x01, 0x02,
+	0x01, 0x6a, 0x4c, 0x00,
+	0x0f, 0x41, 0x41, 0x03,
+	0x7d, 0x6a, 0x3d, 0x00,
+	0x00, 0x65, 0x7a, 0x10,
+	0x08, 0x6a, 0x66, 0x00,
+	0xa9, 0x6a, 0x74, 0x17,
+	0x00, 0x65, 0x82, 0x10,
+	0x79, 0x6a, 0x3d, 0x00,
+	0x00, 0x65, 0x4f, 0x17,
+	0x10, 0x41, 0x76, 0x1a,
+	0x88, 0x6a, 0x66, 0x00,
+	0xac, 0x6a, 0x70, 0x17,
+	0x00, 0x65, 0x6d, 0x17,
+	0xff, 0xa3, 0x43, 0x02,
+	0x44, 0x6a, 0x66, 0x00,
+	0xa4, 0x6a, 0x73, 0x17,
+	0xff, 0x43, 0x88, 0x1a,
+	0x80, 0x02, 0x02, 0x00,
+	0xff, 0x6a, 0x8c, 0x00,
+	0xff, 0x6a, 0x8d, 0x00,
+	0xff, 0x6a, 0x8e, 0x00,
+	0x00, 0x65, 0x6d, 0x17,
+	0x01, 0x43, 0x8a, 0x18,
+	0xbf, 0x3d, 0x3d, 0x02,
+	0x00, 0x3d, 0x44, 0x17,
+	0x80, 0x02, 0xa2, 0x1a,
+	0xff, 0x65, 0x9c, 0x1e,
+	0xff, 0x43, 0x43, 0x06,
+	0xff, 0x43, 0x9c, 0x1e,
+	0xff, 0x6a, 0x64, 0x02,
+	0x08, 0x44, 0x44, 0x06,
+	0x00, 0x45, 0x45, 0x08,
+	0x88, 0x6a, 0x66, 0x00,
+	0x44, 0x6a, 0x73, 0x17,
+	0x08, 0x6a, 0x8c, 0x00,
+	0xff, 0x6a, 0x8d, 0x02,
+	0xff, 0x6a, 0x8e, 0x02,
+	0x0d, 0x93, 0x93, 0x00,
+	0x00, 0x65, 0x9d, 0x17,
+	0x88, 0x6a, 0x95, 0x17,
+	0x00, 0x65, 0x6d, 0x17,
+	0x10, 0x0c, 0x82, 0x1e,
+	0xff, 0x08, 0xa9, 0x02,
+	0xff, 0x09, 0xaa, 0x02,
+	0xff, 0x0a, 0xab, 0x02,
+	0xff, 0x43, 0xa8, 0x02,
+	0x10, 0x41, 0x41, 0x00,
+	0x00, 0x65, 0x5c, 0x10,
+	0x7f, 0x02, 0x02, 0x02,
+	0xe1, 0x6a, 0x91, 0x00,
+	0x00, 0x65, 0x5c, 0x10,
+	0x00, 0x65, 0x4f, 0x17,
+	0x88, 0x6a, 0x66, 0x00,
+	0xb4, 0x6a, 0x72, 0x17,
+	0xff, 0x6a, 0x8d, 0x02,
+	0xff, 0x6a, 0x8e, 0x02,
+	0x00, 0x65, 0x6d, 0x17,
+	0x3d, 0x6a, 0x44, 0x17,
+	0x00, 0x65, 0x5c, 0x10,
+	0x00, 0x65, 0x4f, 0x17,
+	0xff, 0x06, 0xa2, 0x02,
+	0x00, 0x65, 0x5c, 0x10,
+	0xff, 0x34, 0xb2, 0x1a,
+	0x08, 0x6a, 0x32, 0x17,
+	0x35, 0x6a, 0x65, 0x00,
+	0xff, 0x34, 0x66, 0x02,
+	0x01, 0x0c, 0xb4, 0x1e,
+	0x04, 0x0c, 0xb4, 0x1a,
+	0xe0, 0x03, 0x4c, 0x02,
+	0xa0, 0x4c, 0xc0, 0x18,
+	0xff, 0x66, 0xbb, 0x1a,
+	0x10, 0x4c, 0x03, 0x00,
+	0x00, 0x65, 0xb2, 0x10,
+	0x01, 0x66, 0xbd, 0x18,
+	0x40, 0x6a, 0x0c, 0x00,
+	0xff, 0x66, 0x66, 0x06,
+	0xff, 0x6c, 0x06, 0x02,
+	0x00, 0x65, 0xb4, 0x10,
+	0x40, 0x6a, 0x0c, 0x00,
+	0xff, 0x6a, 0x34, 0x02,
+	0x00, 0x65, 0x5c, 0x10,
+	0x64, 0x6a, 0x3f, 0x17,
+	0xff, 0x64, 0x4b, 0x02,
+	0x80, 0x64, 0x0e, 0x1b,
+	0x04, 0x64, 0x01, 0x1d,
+	0x02, 0x64, 0x04, 0x1d,
+	0x00, 0x6a, 0xd1, 0x1c,
+	0x03, 0x64, 0x0c, 0x1d,
+	0x01, 0x64, 0xf5, 0x1c,
+	0x07, 0x64, 0x30, 0x1d,
+	0x08, 0x64, 0xcf, 0x1c,
+	0x11, 0x6a, 0x91, 0x00,
+	0x07, 0x6a, 0x32, 0x17,
+	0xff, 0x06, 0x6a, 0x02,
+	0x00, 0x65, 0x5c, 0x10,
+	0xff, 0xa8, 0xd3, 0x1a,
+	0xff, 0xa2, 0xda, 0x1e,
+	0x01, 0x6a, 0x3d, 0x00,
+	0x00, 0xb9, 0x77, 0x17,
+	0xff, 0xa2, 0xda, 0x1e,
+	0x71, 0x6a, 0x91, 0x00,
+	0x40, 0x5b, 0xda, 0x18,
+	0xff, 0xb9, 0xb3, 0x02,
+	0x00, 0x65, 0xe7, 0x10,
+	0x20, 0xa0, 0xe0, 0x1a,
+	0xff, 0x90, 0x4a, 0x02,
+	0xff, 0xb3, 0x49, 0x02,
+	0x00, 0xa1, 0xa1, 0x17,
+	0xff, 0x49, 0x6d, 0x02,
+	0xff, 0x4a, 0x90, 0x02,
+	0xff, 0x5a, 0x64, 0x02,
+	0x00, 0x59, 0xe1, 0x1c,
+	0x01, 0x59, 0x59, 0x06,
+	0xff, 0xb9, 0x9d, 0x02,
+	0x02, 0x6a, 0x91, 0x00,
+	0x08, 0xa0, 0xe7, 0x1e,
+	0x91, 0x6a, 0x91, 0x00,
+	0xff, 0xb3, 0xf3, 0x1c,
+	0xff, 0xb3, 0x64, 0x02,
+	0x00, 0xb9, 0xed, 0x1c,
+	0x00, 0x65, 0xaa, 0x17,
+	0xff, 0x64, 0x90, 0x02,
+	0x00, 0x65, 0xef, 0x10,
+	0x0d, 0x6a, 0x3d, 0x00,
+	0x00, 0xb3, 0x77, 0x17,
+	0xff, 0x48, 0xba, 0x02,
+	0xff, 0x90, 0x48, 0x02,
+	0x00, 0x65, 0x2f, 0x16,
+	0x00, 0x65, 0x69, 0x10,
+	0x00, 0x65, 0xaa, 0x17,
+	0x00, 0x65, 0x69, 0x10,
+	0x4d, 0x6a, 0x3a, 0x17,
+	0xff, 0x4d, 0x64, 0x02,
+	0x00, 0x66, 0x3a, 0x17,
+	0xff, 0x64, 0x64, 0x06,
+	0x52, 0x66, 0xfb, 0x18,
+	0xff, 0x66, 0x66, 0x06,
+	0xff, 0x64, 0xf7, 0x1a,
+	0x41, 0x6a, 0x91, 0x00,
+	0x20, 0x5b, 0xcd, 0x1c,
+	0x80, 0x5b, 0xcf, 0x18,
+	0x10, 0x4c, 0x03, 0x00,
+	0x00, 0x65, 0xcf, 0x10,
+	0x04, 0xa0, 0xa0, 0x00,
+	0x00, 0x65, 0xbb, 0x17,
+	0x00, 0x65, 0x69, 0x10,
+	0x10, 0x41, 0xcf, 0x1e,
+	0xff, 0x43, 0xa3, 0x02,
+	0xa4, 0x6a, 0x66, 0x00,
+	0x44, 0x6a, 0x73, 0x17,
+	0xac, 0x6a, 0x66, 0x00,
+	0x14, 0x6a, 0x73, 0x17,
+	0xa9, 0x6a, 0x74, 0x17,
+	0x00, 0x65, 0xcf, 0x10,
+	0xef, 0x41, 0x41, 0x02,
+	0x00, 0x65, 0xcf, 0x10,
+	0x78, 0x64, 0xcd, 0x1a,
+	0x07, 0x64, 0x64, 0x02,
+	0x00, 0x42, 0x42, 0x00,
+	0x00, 0x42, 0xa1, 0x17,
+	0xff, 0x6c, 0x5b, 0x02,
+	0xff, 0x5b, 0x28, 0x19,
+	0xff, 0x5b, 0x18, 0x1d,
+	0xff, 0x5b, 0x90, 0x02,
+	0x04, 0xa0, 0x2d, 0x1f,
+	0x00, 0x65, 0x2a, 0x11,
+	0xff, 0x06, 0x6a, 0x02,
+	0x01, 0x0c, 0x19, 0x1f,
+	0x04, 0x0c, 0x19, 0x1b,
+	0xe0, 0x03, 0x4c, 0x02,
+	0xe0, 0x4c, 0x2d, 0x19,
+	0x20, 0x12, 0x2d, 0x19,
+	0x20, 0x41, 0x41, 0x00,
+	0x5b, 0x6a, 0x3a, 0x17,
+	0xff, 0x3f, 0x64, 0x02,
+	0x00, 0x5b, 0x65, 0x06,
+	0x00, 0x65, 0x2d, 0x13,
+	0xff, 0x5b, 0x90, 0x02,
+	0xff, 0x42, 0x64, 0x02,
+	0x00, 0xa1, 0x2d, 0x19,
+	0x20, 0xa0, 0x2d, 0x1f,
+	0x04, 0xa0, 0x2d, 0x1f,
+	0x00, 0x6a, 0x52, 0x17,
+	0xff, 0x65, 0x2d, 0x1d,
+	0xfb, 0xa0, 0xa0, 0x02,
+	0x40, 0x41, 0x41, 0x00,
+	0x00, 0x65, 0xcf, 0x10,
+	0x31, 0x6a, 0x91, 0x00,
+	0x0c, 0x6a, 0x32, 0x17,
+	0x00, 0x65, 0xcf, 0x10,
+	0x61, 0x6a, 0x91, 0x00,
+	0x00, 0x65, 0xcf, 0x10,
+	0x50, 0x6a, 0x60, 0x00,
+	0xff, 0x34, 0x36, 0x1f,
+	0x10, 0x6a, 0x60, 0x00,
+	0xc1, 0x6a, 0x91, 0x00,
+	0x10, 0x4c, 0x03, 0x00,
+	0x01, 0x6a, 0x34, 0x00,
+	0xff, 0x65, 0x35, 0x02,
+	0x10, 0x6a, 0x60, 0x01,
+	0xff, 0x06, 0x6a, 0x02,
+	0x01, 0x0c, 0x3b, 0x1f,
+	0x04, 0x0c, 0x3b, 0x1b,
+	0xe0, 0x03, 0x4c, 0x02,
+	0xe0, 0x4c, 0x42, 0x19,
+	0xff, 0x65, 0x66, 0x02,
+	0xff, 0x12, 0x6d, 0x03,
+	0xff, 0x06, 0x6a, 0x03,
+	0xd1, 0x6a, 0x91, 0x00,
+	0x00, 0x65, 0x5c, 0x10,
+	0xff, 0x65, 0x93, 0x02,
+	0x01, 0x0b, 0x4c, 0x1b,
+	0x10, 0x0c, 0x45, 0x1f,
+	0x04, 0x0b, 0x49, 0x1b,
+	0xff, 0x6a, 0x65, 0x02,
+	0x04, 0x93, 0x4b, 0x1b,
+	0x01, 0x94, 0x4a, 0x1f,
+	0x10, 0x94, 0x4b, 0x1b,
+	0xc7, 0x93, 0x93, 0x02,
+	0x38, 0x93, 0x4d, 0x1b,
+	0xff, 0x6a, 0x6a, 0x03,
+	0x80, 0x41, 0x4e, 0x1f,
+	0x40, 0x41, 0x4e, 0x1b,
+	0x21, 0x6a, 0x91, 0x01,
+	0xff, 0x65, 0x90, 0x02,
+	0xff, 0x5b, 0x64, 0x02,
+	0x00, 0xb9, 0x56, 0x19,
+	0x04, 0xa0, 0x60, 0x1b,
+	0x01, 0x65, 0x65, 0x06,
+	0xff, 0x3e, 0x64, 0x02,
+	0x00, 0x65, 0x52, 0x19,
+	0x00, 0x6a, 0xad, 0x17,
+	0x0d, 0x6a, 0x3d, 0x00,
+	0x00, 0x5b, 0x77, 0x17,
+	0xff, 0xa8, 0x5e, 0x1f,
+	0x10, 0xa0, 0xa0, 0x00,
+	0x08, 0xa0, 0x4e, 0x1f,
+	0xff, 0x6a, 0x65, 0x01,
+	0x08, 0xa0, 0x5f, 0x1b,
+	0xff, 0xba, 0x66, 0x1d,
+	0xff, 0xbb, 0x49, 0x02,
+	0xff, 0xba, 0x90, 0x02,
+	0xff, 0x49, 0xbb, 0x02,
+	0xff, 0x65, 0x90, 0x02,
+	0xff, 0xbb, 0x6b, 0x1d,
+	0xff, 0xba, 0x49, 0x02,
+	0xff, 0xbb, 0x90, 0x02,
+	0xff, 0x49, 0xba, 0x02,
+	0xff, 0x65, 0x90, 0x03,
+	0xff, 0xba, 0x52, 0x03,
+	0xff, 0x6a, 0x6a, 0x03,
+	0xff, 0x8c, 0x08, 0x02,
+	0xff, 0x8d, 0x09, 0x02,
+	0xff, 0x8e, 0x0a, 0x03,
+	0xff, 0x6c, 0x6d, 0x02,
+	0xff, 0x6c, 0x6d, 0x02,
+	0xff, 0x6c, 0x6d, 0x02,
+	0xff, 0x6c, 0x6d, 0x02,
+	0xff, 0x6c, 0x6d, 0x02,
+	0xff, 0x6c, 0x6d, 0x02,
+	0xff, 0x6c, 0x6d, 0x03,
+	0x3d, 0x65, 0x66, 0x0a,
+	0x55, 0x65, 0x64, 0x0a,
+	0x00, 0x54, 0x88, 0x06,
+	0xff, 0x66, 0x64, 0x02,
+	0x00, 0x55, 0x89, 0x08,
+	0xff, 0x6a, 0x64, 0x02,
+	0x00, 0x56, 0x8a, 0x08,
+	0x00, 0x57, 0x8b, 0x08,
+	0x1c, 0x6a, 0x8c, 0x00,
+	0xff, 0x6a, 0x8d, 0x02,
+	0xff, 0x6a, 0x8e, 0x02,
+	0xff, 0x3d, 0x93, 0x02,
+	0x04, 0x3d, 0x8f, 0x1b,
+	0xa0, 0x6a, 0x65, 0x00,
+	0x1c, 0x65, 0x64, 0x06,
+	0xff, 0x6c, 0x99, 0x02,
+	0xff, 0x6c, 0x99, 0x02,
+	0xff, 0x6c, 0x99, 0x02,
+	0xff, 0x6c, 0x99, 0x02,
+	0xff, 0x6c, 0x99, 0x02,
+	0xff, 0x6c, 0x99, 0x02,
+	0xff, 0x6c, 0x99, 0x02,
+	0x00, 0x65, 0x86, 0x19,
+	0x0a, 0x93, 0x93, 0x00,
+	0x00, 0x65, 0x9d, 0x17,
+	0x04, 0x3d, 0x4e, 0x1f,
+	0xa0, 0x6a, 0x95, 0x17,
+	0x00, 0x65, 0x96, 0x17,
+	0x00, 0x65, 0x96, 0x17,
+	0x00, 0x65, 0x96, 0x11,
+	0xff, 0x65, 0x66, 0x02,
+	0xff, 0x99, 0x6d, 0x02,
+	0xff, 0x99, 0x6d, 0x02,
+	0xff, 0x99, 0x6d, 0x02,
+	0xff, 0x99, 0x6d, 0x02,
+	0xff, 0x99, 0x6d, 0x02,
+	0xff, 0x99, 0x6d, 0x02,
+	0xff, 0x99, 0x6d, 0x03,
+	0x08, 0x94, 0x9d, 0x1f,
+	0xf7, 0x93, 0x93, 0x02,
+	0x08, 0x93, 0x9f, 0x1b,
+	0xff, 0x6a, 0x6a, 0x03,
+	0xff, 0x65, 0x66, 0x02,
+	0x4c, 0x66, 0x66, 0x0a,
+	0x03, 0x66, 0x66, 0x02,
+	0xbc, 0x66, 0x66, 0x06,
+	0x6a, 0x65, 0x64, 0x0a,
+	0x08, 0x65, 0xa8, 0x1f,
+	0x02, 0x64, 0x64, 0x06,
+	0xff, 0x64, 0x90, 0x02,
+	0xff, 0x66, 0x65, 0x03,
+	0xff, 0x53, 0xba, 0x02,
+	0xff, 0x6a, 0xb9, 0x00,
+	0xff, 0x90, 0x53, 0x03,
+	0xff, 0x53, 0xb9, 0x19,
+	0xff, 0x52, 0xb0, 0x19,
+	0xff, 0x6a, 0x65, 0x01,
+	0xff, 0x52, 0x90, 0x02,
+	0x10, 0xa0, 0xb4, 0x1f,
+	0xef, 0xa0, 0xa0, 0x02,
+	0x00, 0x65, 0xb6, 0x11,
+	0xff, 0xa8, 0xb6, 0x1b,
+	0xff, 0xb3, 0xb8, 0x1d,
+	0x01, 0x6a, 0x3d, 0x00,
+	0x00, 0xb9, 0x77, 0x17,
+	0x00, 0x90, 0x61, 0x11,
+	0xff, 0x53, 0x90, 0x02,
+	0xff, 0xba, 0x53, 0x03,
+	0xff, 0x6a, 0xbb, 0x00,
+	0xff, 0x52, 0xba, 0x02,
+	0xff, 0x90, 0x52, 0x02,
+	0xff, 0xba, 0x4e, 0x1d,
+	0xff, 0xba, 0x90, 0x02,
+	0xff, 0x52, 0xbb, 0x02,
+	0xff, 0x52, 0x90, 0x03,
+};
+#define	ULTRA           	0x8
+#define	SCB_PAGING      	0x4
+#define	TWIN_CHANNEL    	0x2
+struct patch {
+	int	options;
+	int	negative;
+	int	begin;
+	int	end;
+} patches[] = {
+	{ 0x00000002, 0, 0x006, 0x00b },
+	{ 0x00000004, 0, 0x00e, 0x010 },
+	{ 0x00000004, 1, 0x011, 0x012 },
+	{ 0x00000004, 0, 0x01a, 0x023 },
+	{ 0x00000004, 1, 0x023, 0x027 },
+	{ 0x00000002, 0, 0x02f, 0x033 },
+	{ 0x00000008, 0, 0x04f, 0x056 },
+	{ 0x00000004, 0, 0x0e0, 0x0e3 },
+	{ 0x00000004, 1, 0x0e8, 0x0ed },
+	{ 0x00000004, 0, 0x102, 0x103 },
+	{ 0x00000004, 0, 0x113, 0x114 },
+	{ 0x00000004, 1, 0x114, 0x118 },
+	{ 0x00000004, 1, 0x123, 0x128 },
+	{ 0x00000004, 0, 0x128, 0x12a },
+	{ 0x00000004, 0, 0x152, 0x16c },
+	{ 0x00000004, 1, 0x16c, 0x16d },
+	{ 0x00000004, 0, 0x1ad, 0x1c2 },
+	{ 0x00000000, 0, 0x000, 0x000 }
+};
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/dc390.h linux/drivers/scsi/dc390.h
--- v2.0.30/linux/drivers/scsi/dc390.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/dc390.h	Thu Aug 14 10:31:20 1997
@@ -0,0 +1,147 @@
+/***********************************************************************
+ *	FILE NAME : DC390.H					       *
+ *	     BY   : C.L. Huang					       *
+ *	Description: Device Driver for Tekram DC-390(T) PCI SCSI       *
+ *		     Bus Master Host Adapter			       *
+ ***********************************************************************/
+
+/* Kernel version autodetection */
+
+#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
+ */
+
+#ifndef DC390_H
+#define DC390_H
+
+#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.10 Dec-05-1996",  /* *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_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.10 Dec-05-1996",  /* *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.10 Dec-05-1996",\
+	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
+
+#endif /* defined(HOSTS_C) || defined(MODULE) */
+
+#endif /* DC390_H */
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/eata.c linux/drivers/scsi/eata.c
--- v2.0.30/linux/drivers/scsi/eata.c	Wed Feb 26 10:56:51 1997
+++ linux/drivers/scsi/eata.c	Wed Aug  6 11:41:23 1997
@@ -1,6 +1,13 @@
 /*
  *      eata.c - Low-level driver for EATA/DMA SCSI host adapters.
  *
+ *      17 May 1997 rev. 3.10 for linux 2.0.30 and 2.1.38
+ *          Use of serial_number_at_timeout in abort and reset processing.
+ *          Use of the __initfunc and __initdata macro in setup code.
+ *          Minor cleanups in the list_statistics code.
+ *          Increased controller busy timeout in order to better support 
+ *          slow SCSI devices.
+ *
  *      24 Feb 1997 rev. 3.00 for linux 2.0.29 and 2.1.26
  *          When loading as a module, parameter passing is now supported
  *          both in 2.0 and in 2.1 style.
@@ -202,8 +209,7 @@
  *  lc:n  disables linked commands;
  *  tc:y  enables tagged commands;
  *  tc:n  disables tagged commands;
- *  tm:0  use head/simple/ordered queue tag sequences for reads and ordered
- *        queue tags for writes;
+ *  tm:0  use head/simple/ordered queue tag sequences;
  *  tm:1  use only simple queue tags;
  *  tm:2  use only head of queue tags;
  *  tm:3  use only ordered queue tags;
@@ -232,10 +238,12 @@
  *  between increasing or decreasing by minimizing the seek distance between
  *  the sector of the commands just completed and the sector of the first 
  *  command in the list to be sorted. 
- *  Trivial math assures that if there are (Q-1) outstanding request for
- *  random seeks over S sectors, the unsorted average seek distance is S/2,
- *  while the sorted average seek distance is S/(Q-1). The seek distance is
- *  hence divided by a factor (Q-1)/2.
+ *  Trivial math assures that the unsorted average seek distance when doing
+ *  random seeks over S sectors is S/3.
+ *  When (Q-1) requests are uniformly distributed over S sectors, the average
+ *  distance between two adjacent requests is S/((Q-1) + 1), so the sorted
+ *  average seek distance for (Q-1) random requests over S sectors is S/Q.
+ *  The elevator sorting hence divides the seek distance by a factor Q/3.
  *  The above pure geometric remarks are valid in all cases and the 
  *  driver effectively reduces the seek distance by the predicted factor
  *  when there are Q concurrent read i/o operations on the device, but this
@@ -286,10 +294,18 @@
 #include <asm/dma.h>
 #include <asm/irq.h>
 #include "eata.h"
-#include<linux/stat.h>
-#include<linux/config.h>
-#include<linux/bios32.h>
-#include<linux/pci.h>
+#include <linux/stat.h>
+#include <linux/config.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,36)
+#include <linux/init.h>
+#else
+#define __initfunc(A) A
+#define __initdata
+#define __init
+#endif
 
 struct proc_dir_entry proc_scsi_eata2x = {
     PROC_SCSI_EATA2X, 6, "eata2x",
@@ -517,7 +533,7 @@
 static const char *driver_name = "EATA";
 static unsigned int irqlist[MAX_IRQ], calls[MAX_IRQ];
 
-static unsigned int io_port[] = { 
+static unsigned int io_port[] __initdata = { 
 
    /* Space for MAX_INT_PARAM ports usable while loading as a module */
    SKIP,    SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
@@ -638,8 +654,7 @@
    return;
 }
 
-static inline int wait_on_busy(unsigned int iobase) {
-   unsigned int loop = MAXLOOP;
+static inline int wait_on_busy(unsigned int iobase, unsigned int loop) {
 
    while (inb(iobase + REG_AUX_STATUS) & ABSY_ASSERTED)
       if (--loop == 0) return TRUE;
@@ -649,7 +664,7 @@
 
 static inline int do_dma(unsigned int iobase, unsigned int addr, unchar cmd) {
 
-   if (wait_on_busy(iobase)) return TRUE;
+   if (wait_on_busy(iobase, (addr ? MAXLOOP * 100 : MAXLOOP))) return TRUE;
 
    if ((addr = V2DEV(addr))) {
       outb((char) (addr >> 24), iobase + REG_LOW);
@@ -678,8 +693,8 @@
    return FALSE;
 }
 
-static inline int port_detect(unsigned int port_base, unsigned int j, 
-			      Scsi_Host_Template *tpnt) {
+__initfunc (static inline int port_detect \
+      (unsigned int port_base, unsigned int j, Scsi_Host_Template *tpnt)) {
    unsigned char irq, dma_channel, subversion, i;
    unsigned char protocol_rev;
    struct eata_info info;
@@ -882,7 +897,7 @@
          sh[j]->max_lun = info.max_lun + 1;
       }
 
-   if (dma_channel == NO_DMA) sprintf(dma_name, "%s", "NO DMA");
+   if (dma_channel == NO_DMA) sprintf(dma_name, "%s", "BMST");
    else                       sprintf(dma_name, "DMA %u", dma_channel);
 
    for (i = 0; i < sh[j]->can_queue; i++)
@@ -941,7 +956,7 @@
    return TRUE;
 }
 
-void eata2x_setup(char *str, int *ints) {
+__initfunc (void eata2x_setup(char *str, int *ints)) {
    int i, argc = ints[0];
    char *cur = str, *pc;
 
@@ -974,7 +989,7 @@
    return;
 }
 
-static void add_pci_ports(void) {
+__initfunc (static void add_pci_ports(void)) {
 
 #if defined(CONFIG_PCI)
 
@@ -1009,7 +1024,7 @@
    return;
 }
 
-int eata2x_detect(Scsi_Host_Template *tpnt) {
+__initfunc (int eata2x_detect(Scsi_Host_Template *tpnt)) {
    unsigned long flags;
    unsigned int j = 0, k;
 
@@ -1166,6 +1181,9 @@
 
    cpp->reqsen = TRUE;
    cpp->dispri = TRUE;
+#if 0
+   if (SCpnt->device->type == TYPE_TAPE) cpp->hbaci = TRUE;
+#endif
    cpp->one = TRUE;
    cpp->channel = SCpnt->channel;
    cpp->target = SCpnt->target;
@@ -1182,14 +1200,12 @@
       else if (tag_mode == TAG_SIMPLE)  cpp->mess[0] = SIMPLE_QUEUE_TAG;
       else if (tag_mode == TAG_HEAD)    cpp->mess[0] = HEAD_OF_QUEUE_TAG;
       else if (tag_mode == TAG_ORDERED) cpp->mess[0] = ORDERED_QUEUE_TAG;
-      else if ((SCpnt->device->current_tag % SCpnt->device->queue_depth) == 0)
+      else if (SCpnt->device->current_tag == 0)
          cpp->mess[0] = ORDERED_QUEUE_TAG;
-      else if ((SCpnt->device->current_tag % SCpnt->device->queue_depth) == 1)
+      else if (SCpnt->device->current_tag == 1)
          cpp->mess[0] = HEAD_OF_QUEUE_TAG;
-      else if (cpp->din)
-         cpp->mess[0] = SIMPLE_QUEUE_TAG;
       else
-         cpp->mess[0] = ORDERED_QUEUE_TAG;
+         cpp->mess[0] = SIMPLE_QUEUE_TAG;
 
       cpp->mess[1] = SCpnt->device->current_tag++;
       }
@@ -1208,7 +1224,7 @@
    if (linked_comm && SCpnt->device->queue_depth > 2
                                      && TLDEV(SCpnt->device->type)) {
       HD(j)->cp_stat[i] = READY;
-      flush_dev(SCpnt->device, 0, j, FALSE);
+      flush_dev(SCpnt->device, SCpnt->request.sector, j, FALSE);
       restore_flags(flags);
       return 0;
       }
@@ -1238,7 +1254,8 @@
    cli();
    j = ((struct hostdata *) SCarg->host->hostdata)->board_number;
 
-   if (SCarg->host_scribble == NULL) {
+   if (SCarg->host_scribble == NULL
+       || SCarg->serial_number != SCarg->serial_number_at_timeout) {
       printk("%s: abort, target %d.%d:%d, pid %ld inactive.\n",
 	     BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
       restore_flags(flags);
@@ -1252,7 +1269,7 @@
    if (i >= sh[j]->can_queue)
       panic("%s: abort, invalid SCarg->host_scribble.\n", BN(j));
 
-   if (wait_on_busy(sh[j]->io_port)) {
+   if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
       printk("%s: abort, timeout error.\n", BN(j));
       restore_flags(flags);
       return SCSI_ABORT_ERROR;
@@ -1321,13 +1338,19 @@
    if (SCarg->host_scribble == NULL)
       printk("%s: reset, pid %ld inactive.\n", BN(j), SCarg->pid);
 
+   if (SCarg->serial_number != SCarg->serial_number_at_timeout) {
+      printk("%s: reset, pid %ld, reset not running.\n", BN(j), SCarg->pid);
+      restore_flags(flags);
+      return SCSI_RESET_NOT_RUNNING;
+      }
+
    if (HD(j)->in_reset) {
       printk("%s: reset, exit, already in reset.\n", BN(j));
       restore_flags(flags);
       return SCSI_RESET_ERROR;
       }
 
-   if (wait_on_busy(sh[j]->io_port)) {
+   if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
       printk("%s: reset, exit, timeout error.\n", BN(j));
       restore_flags(flags);
       return SCSI_RESET_ERROR;
@@ -1480,7 +1503,7 @@
    unsigned int rev = FALSE, s = TRUE, r = TRUE;
    unsigned int input_only = TRUE, overlap = FALSE;
    unsigned long sl[n_ready], pl[n_ready], ll[n_ready];
-   unsigned long maxsec = 0, minsec = ULONG_MAX, seek = 0;
+   unsigned long maxsec = 0, minsec = ULONG_MAX, seek = 0, iseek = 0;
 
    static unsigned int flushcount = 0, batchcount = 0, sortcount = 0;
    static unsigned int readycount = 0, ovlcount = 0, inputcount = 0;
@@ -1491,8 +1514,8 @@
       printk("fc %d bc %d ic %d oc %d rc %d rs %d sc %d re %d"\
              " av %ldK as %ldK.\n", flushcount, batchcount, inputcount,
              ovlcount, readycount, readysorted, sortcount, revcount,
-             seeknosort / (readycount - batchcount + 1), 
-             seeksorted / (readycount - batchcount + 1));
+             seeknosort / (readycount + 1), 
+             seeksorted / (readycount + 1));
 
    if (n_ready <= 1) return;
 
@@ -1520,6 +1543,10 @@
 
       }
 
+   if (link_statistics) {
+      if (cursec > sl[0]) seek += cursec - sl[0]; else seek += sl[0] - cursec;
+      }
+
    if (cursec > ((maxsec + minsec) / 2)) rev = TRUE;
 
    if (!((rev && r) || (!rev && s))) sort(sl, il, n_ready, rev);
@@ -1537,10 +1564,11 @@
    if (overlap) sort(pl, il, n_ready, FALSE);
 
    if (link_statistics) {
+      if (cursec > sl[0]) iseek = cursec - sl[0]; else iseek = sl[0] - cursec;
       batchcount++; readycount += n_ready, seeknosort += seek / 1024; 
       if (input_only) inputcount++;
       if (overlap) { ovlcount++; seeksorted += seek / 1024; }
-      else seeksorted += (maxsec - minsec) / 1024;
+      else seeksorted += (iseek + maxsec - minsec) / 1024;
       if (rev && !r)     {  revcount++; readysorted += n_ready; }
       if (!rev && !s)    { sortcount++; readysorted += n_ready; }
       }
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/eata.h linux/drivers/scsi/eata.h
--- v2.0.30/linux/drivers/scsi/eata.h	Wed Feb 26 10:56:51 1997
+++ linux/drivers/scsi/eata.h	Wed Aug  6 11:41:23 1997
@@ -12,7 +12,7 @@
 int eata2x_abort(Scsi_Cmnd *);
 int eata2x_reset(Scsi_Cmnd *, unsigned int);
 
-#define EATA_VERSION "3.00.09"
+#define EATA_VERSION "3.10.00"
 
 
 #define EATA {                                                 \
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/eata_pio_proc.c linux/drivers/scsi/eata_pio_proc.c
--- v2.0.30/linux/drivers/scsi/eata_pio_proc.c	Thu Apr 11 23:49:41 1996
+++ linux/drivers/scsi/eata_pio_proc.c	Mon Aug 11 13:37:24 1997
@@ -1,19 +1,4 @@
 
-#define MAX_SCSI_DEVICE_CODE 10
-const char *const pio_scsi_dev_types[MAX_SCSI_DEVICE_CODE] =
-{
-    "Direct-Access    ",
-    "Sequential-Access",
-    "Printer          ",
-    "Processor        ",
-    "WORM             ",
-    "CD-ROM           ",
-    "Scanner          ",
-    "Optical Device   ",
-    "Medium Changer   ",
-    "Communications   "
-};
-
 /*
  * eata_set_info
  * buffer : pointer to the data that has been written to the hostfile
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/g_NCR5380.c linux/drivers/scsi/g_NCR5380.c
--- v2.0.30/linux/drivers/scsi/g_NCR5380.c	Fri Nov 22 02:06:30 1996
+++ linux/drivers/scsi/g_NCR5380.c	Sun Aug 17 12:49:25 1997
@@ -606,21 +606,6 @@
     return len-start;
 }
 
-const char *const private_scsi_device_types[] =
-{
-    "Direct-Access    ",
-    "Sequential-Access",
-    "Printer          ",
-    "Processor        ",
-    "WORM             ",
-    "CD-ROM           ",
-    "Scanner          ",
-    "Optical Device   ",
-    "Medium Changer   ",
-    "Communications   "
-};
-#define MAX_SCSI_DEVICE_CODE sizeof(private_scsi_device_types)/sizeof(char*)
-
 int generic_NCR5380_proc_info(char* buffer, char** start, off_t offset, int length, int hostno, int inout)
 {
     int len = 0;
@@ -628,8 +613,8 @@
     unsigned char status;
     int i;
     struct Scsi_Host *scsi_ptr;
-	Scsi_Device *dev;
     Scsi_Cmnd *ptr;
+    Scsi_Device *dev;
     struct NCR5380_hostdata *hostdata;
 
     cli();
@@ -674,7 +659,7 @@
 	    long tr = hostdata->time_read[dev->id] / HZ;
 	    long tw = hostdata->time_write[dev->id] / HZ;
 
-	    PRINTP("  T:%d %s " ANDP dev->id ANDP (dev->type < MAX_SCSI_DEVICE_CODE) ? private_scsi_device_types[(int)dev->type] : "Unknown");
+	    PRINTP("  T:%d %s " ANDP dev->id ANDP (dev->type < MAX_SCSI_DEVICE_CODE) ? scsi_device_types[(int)dev->type] : "Unknown");
 	    for (i=0; i<8; i++)
 		if (dev->vendor[i] >= 0x20)
 		    *(buffer+(len++)) = dev->vendor[i];
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/gdth.c linux/drivers/scsi/gdth.c
--- v2.0.30/linux/drivers/scsi/gdth.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/gdth.c	Mon Aug  4 17:15:07 1997
@@ -0,0 +1,3275 @@
+/************************************************************************
+ * GDT ISA/EISA/PCI Disk Array Controller driver for Linux              *
+ *                                                                      *
+ * gdth.c                                                               *
+ * Copyright (C) 1995-97 ICP vortex Computersysteme GmbH, Achim Leubner *
+ *                                                                      *
+ * <achim@vortex.de>                                                    *
+ *                                                                      *
+ * 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 kernel; if not, write to the Free Software           *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.            *
+ *                                                                      *
+ * Tested with Linux 1.2.13, ..., 2.0.29                                *
+ *                                                                      *
+ * $Log: gdth.c,v $
+ * Revision 1.8  1997/04/02 12:14:30  achim
+ * Version 1.00 (see gdth.h), tested with kernel 2.0.29
+ *
+ * Revision 1.7  1997/03/12 13:33:37  achim
+ * gdth_reset() changed, new async. events
+ *
+ * Revision 1.6  1997/03/04 14:01:11  achim
+ * Shutdown routine gdth_halt() implemented
+ *
+ * Revision 1.5  1997/02/21 09:08:36  achim
+ * New controller included (RP, RP1, RP2 series)
+ * IOCTL interface implemented
+ *
+ * Revision 1.4  1996/07/05 12:48:55  achim
+ * Function gdth_bios_param() implemented
+ * New constant GDTH_MAXC_P_L inserted
+ * GDT_WRITE_THR, GDT_EXT_INFO implemented
+ * Function gdth_reset() changed
+ *
+ * Revision 1.3  1996/05/10 09:04:41  achim
+ * Small changes for Linux 1.2.13
+ *
+ * Revision 1.2  1996/05/09 12:45:27  achim
+ * Loadable module support implemented
+ * /proc support corrections made
+ *
+ * Revision 1.1  1996/04/11 07:35:57  achim
+ * Initial revision
+ *
+ *
+ * $Id: gdth.c,v 1.8 1997/04/02 12:14:30 achim Exp $ 
+ ************************************************************************/
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/in.h>
+#include <linux/proc_fs.h>
+#include <linux/time.h>
+#include <linux/timer.h>
+
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#if LINUX_VERSION_CODE >= 0x010300
+#include <linux/blk.h>
+#else
+#include "../block/blk.h"
+#endif
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+
+#include "gdth.h"
+
+#if LINUX_VERSION_CODE >= 0x010346
+static void gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs);
+#else
+static void gdth_interrupt(int irq,struct pt_regs *regs);
+#endif
+static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp);
+static int gdth_async_event(int hanum,int service);
+
+static void gdth_putq(int hanum,Scsi_Cmnd *scp,unchar priority);
+static void gdth_next(int hanum);
+static int gdth_fill_raw_cmd(int hanum,Scsi_Cmnd *scp,unchar b);
+static int gdth_special_cmd(int hanum,Scsi_Cmnd *scp,unchar b);
+static gdth_evt_str *gdth_store_event(ushort source, ushort idx,
+                                      gdth_evt_data *evt);
+static int gdth_read_event(int handle, gdth_evt_str *estr);
+static void gdth_readapp_event(unchar application, gdth_evt_str *estr);
+static void gdth_clear_events(void);
+
+static void gdth_copy_internal_data(Scsi_Cmnd *scp,char *buffer,ushort count);
+static int gdth_internal_cache_cmd(int hanum,Scsi_Cmnd *scp,
+                                   unchar b,ulong *flags);
+static int gdth_fill_cache_cmd(int hanum,Scsi_Cmnd *scp,ushort hdrive);
+
+static int gdth_search_eisa(ushort eisa_adr);
+static int gdth_search_isa(ulong bios_adr);
+static int gdth_search_pci(ushort device_id,ushort index,gdth_pci_str *pcistr);
+static int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha,int firsttime);
+static int gdth_init_isa(ulong bios_adr,gdth_ha_str *ha,int firsttime);
+static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha,int firsttime);
+
+static void gdth_enable_int(int hanum);
+static int gdth_get_status(unchar *pIStatus,int irq);
+static int gdth_test_busy(int hanum);
+static int gdth_get_cmd_index(int hanum);
+static void gdth_release_event(int hanum);
+static int gdth_wait(int hanum,int index,ulong time);
+static int gdth_internal_cmd(int hanum,unchar service,ushort opcode,ulong p1,
+                             ulong p2,ulong p3);
+static int gdth_search_drives(int hanum,int firsttime);
+
+static const char *gdth_ctr_name(int hanum);
+void gdth_halt(void);
+
+#ifdef DEBUG_GDTH
+static unchar   DebugState = DEBUG_GDTH;
+extern int sys_syslog(int,char*,int);
+#define LOGEN           sys_syslog(7,NULL,0);
+#define WAITSEC(a)      {ulong idx; for(idx=0;idx<a*1000L;++idx) udelay(1000);}
+
+#ifdef SLOWMOTION_GDTH
+#define SLOWM   WAITSEC(2)  
+#undef  INIT_RETRIES
+#undef  INIT_TIMEOUT
+#undef  POLL_TIMEOUT
+#define INIT_RETRIES    15
+#define INIT_TIMEOUT    150
+#define POLL_TIMEOUT    150
+#else
+#define SLOWM
+#endif
+
+#ifdef __SERIAL__
+#define MAX_SERBUF 160
+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 char strbuf[MAX_SERBUF+1];
+#ifdef __COM2__
+#define COM_BASE 0x2f8
+#else
+#define COM_BASE 0x3f8
+#endif
+static void ser_init()
+{
+    unsigned port=COM_BASE;
+
+    outb(0x80,port+3);
+    outb(0,port+1);
+    /* 19200 Baud, if 9600: outb(12,port) */
+    outb(6, port);
+    outb(3,port+3);
+    outb(0,port+1);
+    /*
+    ser_putc('I');
+    ser_putc(' ');
+    */
+}
+
+static void ser_puts(char *str)
+{
+    char *ptr;
+
+    ser_init();
+    for (ptr=str;*ptr;++ptr)
+        ser_putc(*ptr);
+}
+
+static void ser_putc(char c)
+{
+    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);
+    }
+}
+
+static int ser_printk(const char *fmt, ...)
+{
+    va_list args;
+    int i;
+
+    va_start(args,fmt);
+    i = vsprintf(strbuf,fmt,args);
+    ser_puts(strbuf);
+    va_end(args);
+    return i;
+}
+
+#define TRACE(a)    {if (DebugState==1) {ser_printk a; SLOWM}}
+#define TRACE2(a)   {if (DebugState==1 || DebugState==2) {ser_printk a; SLOWM}}
+#define TRACE3(a)   {if (DebugState!=0) {ser_printk a; SLOWM}}
+
+#else /* !__SERIAL__ */
+#define TRACE(a)    {if (DebugState==1) {LOGEN;printk a; SLOWM}}
+#define TRACE2(a)   {if (DebugState==1 || DebugState==2) {LOGEN;printk a; SLOWM}}
+#define TRACE3(a)   {if (DebugState!=0) {LOGEN;printk a; SLOWM}}
+#endif
+
+#else /* !DEBUG */
+#define TRACE(a)
+#define TRACE2(a)
+#define TRACE3(a)
+#endif
+
+
+#ifdef GDTH_STATISTICS
+static ulong max_rq=0, max_index=0, max_sg=0;
+static ulong act_ints=0, act_ios=0, act_stats=0, act_rq=0;
+#define GDTH_TIMER      31                      /* see linux/timer.h ! */
+#endif
+
+#define PTR2USHORT(a)   (ushort)(ulong)(a)
+#define JIFFYWAIT(a)    {ulong gdtjf;gdtjf=jiffies+(a);while(gdtjf>jiffies);}
+#define GDTOFFSOF(a,b)  (size_t)&(((a*)0)->b)   
+#define INDEX_OK(i,t)   ((i)<sizeof(t)/sizeof((t)[0]))
+
+#define NUMDATA(a)      ( (gdth_num_str  *)((a)->hostdata))
+#define HADATA(a)       (&((gdth_ext_str *)((a)->hostdata))->haext)
+#define CMDDATA(a)      (&((gdth_ext_str *)((a)->hostdata))->cmdext)
+#define DMADATA(a)      (&((gdth_ext_str *)((a)->hostdata))->dmaext)
+
+static unchar   gdth_drq_tab[4] = {5,6,7,7};            /* DRQ table */
+static unchar   gdth_irq_tab[6] = {0,10,11,12,14,0};    /* IRQ table */
+static unchar   gdth_polling;                           /* polling if TRUE */
+static unchar   gdth_from_wait  = FALSE;                /* gdth_wait() */
+static int      wait_index,wait_hanum;                  /* gdth_wait() */
+static int      gdth_ctr_count  = 0;                    /* controller count */
+static int      gdth_ctr_vcount = 0;                    /* virt. ctr. count */
+static struct Scsi_Host *gdth_ctr_tab[MAXHA];           /* controller table */
+static struct Scsi_Host *gdth_ctr_vtab[MAXHA*MAXBUS];   /* virt. ctr. table */
+static unchar   gdth_write_through = FALSE;             /* write through */
+static char *gdth_ioctl_tab[4][MAXHA];                  /* ioctl buffer */
+static gdth_evt_str ebuffer[MAX_EVENTS];                /* event buffer */
+static int elastidx;
+static int eoldidx;
+
+static struct {
+    Scsi_Cmnd   *cmnd;                          /* pending request */
+    ushort      service;                        /* service */
+} gdth_cmd_tab[GDTH_MAXCMDS][MAXHA];            /* table of pend. requests */
+
+#define DIN     1                               /* IN data direction */
+#define DOU     2                               /* OUT data direction */
+#define DNO     DIN                             /* no data transfer */
+#define DUN     DIN                             /* unknown data direction */
+static unchar gdth_direction_tab[0x100] = {
+    DNO,DNO,DIN,DIN,DOU,DIN,DIN,DOU,DIN,DUN,DOU,DOU,DUN,DUN,DUN,DIN,
+    DNO,DIN,DIN,DOU,DIN,DOU,DNO,DNO,DOU,DNO,DIN,DNO,DIN,DOU,DNO,DUN,
+    DIN,DUN,DIN,DUN,DOU,DIN,DUN,DUN,DIN,DIN,DIN,DUN,DUN,DIN,DIN,DIN,
+    DIN,DIN,DIN,DNO,DIN,DNO,DNO,DIN,DIN,DIN,DIN,DIN,DIN,DIN,DIN,DIN,
+    DIN,DIN,DIN,DIN,DIN,DNO,DUN,DNO,DNO,DNO,DUN,DNO,DIN,DIN,DUN,DUN,
+    DUN,DUN,DUN,DUN,DUN,DIN,DUN,DUN,DUN,DUN,DIN,DUN,DUN,DUN,DUN,DUN,
+    DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+    DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+    DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+    DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+    DUN,DUN,DUN,DUN,DUN,DNO,DNO,DUN,DIN,DNO,DIN,DUN,DNO,DUN,DIN,DIN,
+    DIN,DIN,DIN,DNO,DUN,DIN,DIN,DIN,DIN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+    DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+    DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+    DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DOU,DUN,DUN,DUN,DUN,DUN,
+    DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN
+};
+
+/* LILO params: gdth=<IRQ>
+ *
+ * Where: <IRQ> is any of the valid IRQs for EISA controllers (10,11,12,14)
+ * Sets the IRQ of the GDT3000/3020 EISA controller to this value,
+ * if the IRQ can not automat. detect (controller BIOS disabled)
+ * See gdth_init_eisa() 
+ *
+ * You can use the command line gdth=0 to disable the driver 
+ */
+static unchar irqs[MAXHA] = {0xff};
+static unchar disable_gdth_scan = FALSE;
+
+/* /proc support */
+#if LINUX_VERSION_CODE >= 0x010300
+#include <linux/stat.h> 
+struct proc_dir_entry proc_scsi_gdth = {
+    PROC_SCSI_GDTH, 4, "gdth",
+    S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+#include "gdth_proc.h"
+#include "gdth_proc.c"
+#endif
+
+
+/* controller search and initialization functions */
+
+static int gdth_search_eisa(ushort eisa_adr)
+{
+    ulong id;
+    
+    TRACE(("gdth_search_eisa() adr. %x\n",eisa_adr));
+    id = inl(eisa_adr+ID0REG);
+    if (id == GDT3A_ID || id == GDT3B_ID) {     /* GDT3000A or GDT3000B */
+        if ((inb(eisa_adr+EISAREG) & 8) == 0)   
+            return 0;                           /* not EISA configured */
+        return 1;
+    }
+    if (id == GDT3_ID)                          /* GDT3000 */
+        return 1;
+
+    return 0;                                   
+}
+
+
+static int gdth_search_isa(ulong bios_adr)
+{
+    ulong id;
+
+    TRACE(("gdth_search_isa() bios adr. %lx\n",bios_adr));
+    id = *(ulong *)(bios_adr+BIOS_ID_OFFS);
+    if (id == GDT2_ID)                          /* GDT2000 */
+        return 1;
+    return 0;
+}
+
+
+static int gdth_search_pci(ushort device_id,ushort index,gdth_pci_str *pcistr)
+{
+    int error;
+    ulong base0,base1,base2;
+
+    TRACE(("gdth_search_pci() device_id %d, index %d\n",
+                 device_id,index));
+
+    if (!pcibios_present())
+        return 0;
+
+    if (pcibios_find_device(PCI_VENDOR_ID_VORTEX,device_id,index,
+                             &pcistr->bus,&pcistr->device_fn))
+        return 0;
+
+    /* GDT PCI controller found, now read resources from config space */
+#if LINUX_VERSION_CODE >= 0x010300
+#define GDTH_BASEP      (int *)
+#else
+#define GDTH_BASEP
+#endif
+    if ((error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn,
+                                           PCI_BASE_ADDRESS_0,
+                                           GDTH_BASEP&base0)) ||
+        (error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn,
+                                           PCI_BASE_ADDRESS_1,
+                                           GDTH_BASEP&base1)) ||
+        (error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn,
+                                           PCI_BASE_ADDRESS_2,
+                                           GDTH_BASEP&base2)) ||
+        (error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn,
+                                           PCI_ROM_ADDRESS,
+                                           GDTH_BASEP&pcistr->bios)) ||
+        (error = pcibios_read_config_byte(pcistr->bus,pcistr->device_fn,
+                                          PCI_INTERRUPT_LINE,&pcistr->irq))) {
+        printk("GDT-PCI: error %s reading configuration space",
+               pcibios_strerror(error));
+        return -1;
+    }
+
+    pcistr->device_id = device_id;
+    if (device_id <= PCI_DEVICE_ID_VORTEX_GDT6000B ||   /* GDT6000 or GDT6000B */
+        device_id >= PCI_DEVICE_ID_VORTEX_GDT6x17RP) {  /* MPR */
+        if ((base0 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_MEMORY)
+            return -1;
+        pcistr->dpmem = base0 & PCI_BASE_ADDRESS_MEM_MASK;
+    } else {                                    /* GDT6110, GDT6120, .. */
+        if ((base0 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_MEMORY ||
+            (base2 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_MEMORY ||
+            (base1 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_IO)
+            return -1;
+        pcistr->dpmem = base2 & PCI_BASE_ADDRESS_MEM_MASK;
+        pcistr->io_mm = base0 & PCI_BASE_ADDRESS_MEM_MASK;
+        pcistr->io    = base1 & PCI_BASE_ADDRESS_IO_MASK;
+    }
+    return 1;
+}
+
+
+static int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha,int firsttime)
+{
+    ulong retries,id;
+    unchar prot_ver,eisacf,i,irq_found;
+
+    TRACE(("gdth_init_eisa() adr. %x\n",eisa_adr));
+    
+    /* disable board interrupts, deinitialize services */
+    outb(0xff,eisa_adr+EDOORREG);
+    outb(0x00,eisa_adr+EDENABREG);
+    outb(0x00,eisa_adr+EINTENABREG);
+    
+    outb(0xff,eisa_adr+LDOORREG);
+    retries = INIT_RETRIES;
+    JIFFYWAIT(2);
+    while (inb(eisa_adr+EDOORREG) != 0xff) {
+        if (--retries == 0) {
+            printk("GDT-EISA: Initialization error (DEINIT failed)\n");
+            return 0;
+        }
+        udelay(1000);
+        TRACE2(("wait for DEINIT: retries=%ld\n",retries));
+    }
+    prot_ver = inb(eisa_adr+MAILBOXREG);
+    outb(0xff,eisa_adr+EDOORREG);
+    if (prot_ver != PROTOCOL_VERSION) {
+        printk("GDT-EISA: Illegal protocol version\n");
+        return 0;
+    }
+    ha->brd = (ulong)eisa_adr;
+    ha->brd_phys = (ulong)eisa_adr >> 12;
+
+    outl(0,eisa_adr+MAILBOXREG);
+    outl(0,eisa_adr+MAILBOXREG+4);
+    outl(0,eisa_adr+MAILBOXREG+8);
+    outl(0,eisa_adr+MAILBOXREG+12);
+
+    /* detect IRQ */ 
+    if ((id = inl(eisa_adr+ID0REG)) == GDT3_ID) {
+        ha->type = GDT_EISA;
+        ha->stype = id;
+        outl(1,eisa_adr+MAILBOXREG+8);
+        outb(0xfe,eisa_adr+LDOORREG);
+        retries = INIT_RETRIES;
+        JIFFYWAIT(2);
+        while (inb(eisa_adr+EDOORREG) != 0xfe) {
+            if (--retries == 0) {
+                printk("GDT-EISA: Initialization error (get IRQ failed)\n");
+                return 0;
+            }
+            udelay(1000);
+        }
+        if (firsttime)
+            ha->irq = inb(eisa_adr+MAILBOXREG);
+        outb(0xff,eisa_adr+EDOORREG);
+        TRACE2(("GDT3000/3020: IRQ=%d\n",ha->irq));
+        if (firsttime) {
+            /* check the result */
+            if (ha->irq == 0) {
+                TRACE2(("Unknown IRQ, check IRQ table from cmd line !\n"));
+                for (i=0,irq_found=FALSE; i<MAXHA && irqs[i]!=0xff; ++i) {
+                    if (irqs[i]!=0) {
+                        irq_found=TRUE;
+                        break;
+                    }
+                }
+                if (irq_found) {
+                    ha->irq = irqs[i];
+                    irqs[i] = 0;
+                    printk("GDT-EISA: Can not detect controller IRQ,\n");
+                    printk("Use IRQ setting from command line (IRQ = %d)\n",
+                           ha->irq);
+                } else {
+                    printk("GDT-EISA: Initialization error (unknown IRQ), Enable\n");
+                    printk("the controller BIOS or use command line parameters\n");
+                    return 0;
+                }
+            }
+        }
+    } else {
+        eisacf = inb(eisa_adr+EISAREG) & 7;
+        if (eisacf > 4)                         /* level triggered */
+            eisacf -= 4;
+        ha->irq = gdth_irq_tab[eisacf];
+        ha->type = GDT_EISA;
+        ha->stype= id;
+    }
+    return 1;
+}
+
+       
+static int gdth_init_isa(ulong bios_adr,gdth_ha_str *ha,int firsttime)
+{
+    register gdt2_dpram_str *dp2_ptr;
+    int i;
+    unchar irq_drq,prot_ver;
+    ulong retries;
+
+    TRACE(("gdth_init_isa() bios adr. %lx\n",bios_adr));
+
+    ha->brd = bios_adr;
+    dp2_ptr = (gdt2_dpram_str *)ha->brd;
+    dp2_ptr->io.memlock = 1;            /* switch off write protection */
+    /* reset interface area */
+    memset((char *)&dp2_ptr->u,0,sizeof(dp2_ptr->u));
+
+    /* disable board interrupts, read DRQ and IRQ */
+    dp2_ptr->io.irqdel     = 0xff;
+    dp2_ptr->io.irqen      = 0x00;
+    dp2_ptr->u.ic.S_Status = 0x00;
+    dp2_ptr->u.ic.Cmd_Index= 0x00;
+
+    irq_drq = dp2_ptr->io.rq;
+    for (i=0; i<3; ++i) {
+        if ((irq_drq & 1)==0)
+            break;
+        irq_drq >>= 1;
+    }
+    ha->drq = gdth_drq_tab[i];
+
+    irq_drq = dp2_ptr->io.rq >> 3;
+    for (i=1; i<5; ++i) {
+        if ((irq_drq & 1)==0)
+            break;
+        irq_drq >>= 1;
+    }
+    ha->irq = gdth_irq_tab[i];
+
+    /* deinitialize services */
+    dp2_ptr->u.ic.S_Info[0] = bios_adr;
+    dp2_ptr->u.ic.S_Cmd_Indx= 0xff;
+    dp2_ptr->io.event = 0;
+    retries = INIT_RETRIES;
+    JIFFYWAIT(2);
+    while (dp2_ptr->u.ic.S_Status != 0xff) {
+        if (--retries == 0) {
+            printk("GDT-ISA: Initialization error (DEINIT failed)\n");
+            return 0;
+        }
+        udelay(1000);
+    }
+    prot_ver = (unchar)dp2_ptr->u.ic.S_Info[0];
+    dp2_ptr->u.ic.Status = 0;
+    dp2_ptr->io.irqdel = 0xff;
+    if (prot_ver != PROTOCOL_VERSION) {
+        printk("GDT-ISA: Illegal protocol version\n");
+        return 0;
+    }
+
+    ha->type = GDT_ISA;
+    ha->ic_all_size = sizeof(dp2_ptr->u);
+    ha->stype= GDT2_ID;
+    ha->brd_phys = bios_adr >> 4;
+
+    /* special request to controller BIOS */
+    dp2_ptr->u.ic.S_Info[0] = 0x00;
+    dp2_ptr->u.ic.S_Info[1] = 0x00;
+    dp2_ptr->u.ic.S_Info[2] = 0x01;
+    dp2_ptr->u.ic.S_Info[3] = 0x00;
+    dp2_ptr->u.ic.S_Cmd_Indx= 0xfe;
+    dp2_ptr->io.event = 0;
+    retries = INIT_RETRIES;
+    JIFFYWAIT(2);
+    while (dp2_ptr->u.ic.S_Status != 0xfe) {
+        if (--retries == 0) {
+            printk("GDT-ISA: Initialization error\n");
+            return 0;
+        }
+        udelay(1000);
+    }
+    dp2_ptr->u.ic.Status = 0;
+    dp2_ptr->io.irqdel = 0xff;
+    return 1;
+}
+
+
+static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha,int firsttime)
+{
+    register gdt6_dpram_str *dp6_ptr;
+    register gdt6c_dpram_str *dp6c_ptr;
+    register gdt6m_dpram_str *dp6m_ptr;
+    ulong retries;
+    unchar prot_ver;
+    unchar remapped = FALSE;
+
+    TRACE(("gdth_init_pci()\n"));
+
+    if (firsttime) {
+        ha->brd      = pcistr->dpmem;
+        ha->brd_phys = (pcistr->bus << 8) | (pcistr->device_fn & 0xf8);
+        ha->stype    = (ulong)pcistr->device_id;
+        ha->irq      = pcistr->irq;
+    }
+
+    if (ha->stype <= PCI_DEVICE_ID_VORTEX_GDT6000B) {   /* GDT6000 or GDT6000B */
+        TRACE2(("init_pci() dpmem %lx irq %d\n",ha->brd,ha->irq));
+        dp6_ptr = (gdt6_dpram_str *)ha->brd;
+        /* reset interface area */
+        memset((char *)&dp6_ptr->u,0,sizeof(dp6_ptr->u));
+        if (*(ulong *)&dp6_ptr->u != 0) {
+            printk("GDT-PCI: Initialization error (DPMEM write error)\n");
+            return 0;
+        }
+        
+        /* disable board interrupts, deinit services */
+        dp6_ptr->io.irqdel     = 0xff;
+        dp6_ptr->io.irqen      = 0x00;
+        dp6_ptr->u.ic.S_Status = 0x00;
+        dp6_ptr->u.ic.Cmd_Index= 0x00;
+
+        dp6_ptr->u.ic.S_Info[0] = ha->brd;
+        dp6_ptr->u.ic.S_Cmd_Indx= 0xff;
+        dp6_ptr->io.event = 0;
+        retries = INIT_RETRIES;
+        JIFFYWAIT(2);
+        while (dp6_ptr->u.ic.S_Status != 0xff) {
+            if (--retries == 0) {
+                printk("GDT-PCI: Initialization error (DEINIT failed)\n");
+                return 0;
+            }
+            udelay(1000);
+        }
+        prot_ver = (unchar)dp6_ptr->u.ic.S_Info[0];
+        dp6_ptr->u.ic.S_Status = 0;
+        dp6_ptr->io.irqdel = 0xff;
+        if (prot_ver != PROTOCOL_VERSION) {
+            printk("GDT-PCI: Illegal protocol version\n");
+            return 0;
+        }
+
+        ha->type = GDT_PCI;
+        ha->ic_all_size = sizeof(dp6_ptr->u);
+        
+        /* special command to controller BIOS */
+        dp6_ptr->u.ic.S_Info[0] = 0x00;
+        dp6_ptr->u.ic.S_Info[1] = 0x00;
+        dp6_ptr->u.ic.S_Info[2] = 0x01;
+        dp6_ptr->u.ic.S_Info[3] = 0x00;
+        dp6_ptr->u.ic.S_Cmd_Indx= 0xfe;
+        dp6_ptr->io.event = 0;
+        retries = INIT_RETRIES;
+        JIFFYWAIT(2);
+        while (dp6_ptr->u.ic.S_Status != 0xfe) {
+            if (--retries == 0) {
+                printk("GDT-PCI: Initialization error\n");
+                return 0;
+            }
+            udelay(1000);
+        }
+        dp6_ptr->u.ic.S_Status = 0;
+        dp6_ptr->io.irqdel = 0xff;
+
+    } else if (ha->stype <= PCI_DEVICE_ID_VORTEX_GDT6555) { /* GDT6110, GDT6120, .. */
+        if (firsttime) {
+            ha->plx = (gdt6c_plx_regs *)pcistr->io;
+        }
+        TRACE2(("init_pci_new() dpmem %lx io %lx irq %d\n",
+                      ha->brd,(ulong)ha->plx,ha->irq));
+        dp6c_ptr = (gdt6c_dpram_str *)ha->brd;
+        /* reset interface area */
+        memset((char *)&dp6c_ptr->u,0,sizeof(dp6c_ptr->u));
+        if (*(ulong *)&dp6c_ptr->u != 0) {
+            printk("GDT-PCI: Initialization error (DPMEM write error)\n");
+            return 0;
+        }
+        
+        /* disable board interrupts, deinit services */
+        outb(0x00,PTR2USHORT(&ha->plx->control1));
+        outb(0xff,PTR2USHORT(&ha->plx->edoor_reg));
+        
+        dp6c_ptr->u.ic.S_Status = 0x00;
+        dp6c_ptr->u.ic.Cmd_Index= 0x00;
+
+        dp6c_ptr->u.ic.S_Info[0] = ha->brd;
+        dp6c_ptr->u.ic.S_Cmd_Indx= 0xff;
+
+        outb(1,PTR2USHORT(&ha->plx->ldoor_reg));
+
+        retries = INIT_RETRIES;
+        JIFFYWAIT(2);
+        while (dp6c_ptr->u.ic.S_Status != 0xff) {
+            if (--retries == 0) {
+                printk("GDT-PCI: Initialization error (DEINIT failed)\n");
+                return 0;
+            }
+            udelay(1000);
+        }
+        prot_ver = (unchar)dp6c_ptr->u.ic.S_Info[0];
+        dp6c_ptr->u.ic.Status = 0;
+        if (prot_ver != PROTOCOL_VERSION) {
+            printk("GDT-PCI: Illegal protocol version\n");
+            return 0;
+        }
+
+        ha->type = GDT_PCINEW;
+        ha->ic_all_size = sizeof(dp6c_ptr->u);
+
+        /* special command to controller BIOS */
+        dp6c_ptr->u.ic.S_Info[0] = 0x00;
+        dp6c_ptr->u.ic.S_Info[1] = 0x00;
+        dp6c_ptr->u.ic.S_Info[2] = 0x01;
+        dp6c_ptr->u.ic.S_Info[3] = 0x00;
+        dp6c_ptr->u.ic.S_Cmd_Indx= 0xfe;
+        
+        outb(1,PTR2USHORT(&ha->plx->ldoor_reg));
+
+        retries = INIT_RETRIES;
+        JIFFYWAIT(2);
+        while (dp6c_ptr->u.ic.S_Status != 0xfe) {
+            if (--retries == 0) {
+                printk("GDT-PCI: Initialization error\n");
+                return 0;
+            }
+            udelay(1000);
+        }
+        dp6c_ptr->u.ic.S_Status = 0;
+
+    } else {                                            /* MPR */
+        if (ha->brd > 0xfffff) {                        /* NOT below 1MB */
+#if LINUX_VERSION_CODE >= 0x010300
+            /* Linux 1.3.X allow to remap physical pages adresses greater
+               than the highest physical memory address to kernel virtual
+               pages using vremap()/vfree(), Linux 1.2.X doesn't */
+            TRACE2(("init_pci_mpr() dpmem %lx irq %d\n",ha->brd,ha->irq));
+            ha->brd = (ulong)vremap(ha->brd, sizeof(gdt6m_dpram_str));
+            if (ha->brd == 0L) {
+                printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+                return 0;
+            }
+            TRACE2(("init_pci_mpr() remapped dpmem %lx\n",ha->brd));
+            remapped = TRUE;
+#else
+            printk("GDT-PCI: Initialization error (DPMEM not below 1MB)\n");
+            return 0;
+#endif
+        }
+
+        dp6m_ptr = (gdt6m_dpram_str *)ha->brd;
+        /* reset interface area */
+        memset((char *)&dp6m_ptr->u,0,sizeof(dp6m_ptr->u));
+        if (*(ulong *)&dp6m_ptr->u != 0) {
+            printk("GDT-PCI: Initialization error (DPMEM write error)\n");
+#if LINUX_VERSION_CODE >= 0x010300
+            if (remapped)
+                vfree((void *)ha->brd);
+#endif
+            return 0;
+        }
+        
+        /* disable board interrupts, deinit services */
+        dp6m_ptr->i960r.edoor_en_reg |= 4;
+        dp6m_ptr->i960r.edoor_reg = 0xff;
+        dp6m_ptr->u.ic.S_Status   = 0x00;
+        dp6m_ptr->u.ic.Cmd_Index  = 0x00;
+
+        dp6m_ptr->u.ic.S_Info[0]  = ha->brd;
+        dp6m_ptr->u.ic.S_Cmd_Indx = 0xff;
+        dp6m_ptr->i960r.ldoor_reg = 1;
+        retries = INIT_RETRIES;
+        JIFFYWAIT(2);
+        while (dp6m_ptr->u.ic.S_Status != 0xff) {
+            if (--retries == 0) {
+                printk("GDT-PCI: Initialization error (DEINIT failed)\n");
+#if LINUX_VERSION_CODE >= 0x010300
+                if (remapped)
+                    vfree((void *)ha->brd);
+#endif
+                return 0;
+            }
+            udelay(1000);
+        }
+        prot_ver = (unchar)dp6m_ptr->u.ic.S_Info[0];
+        dp6m_ptr->u.ic.S_Status = 0;
+        if (prot_ver != PROTOCOL_VERSION) {
+            printk("GDT-PCI: Illegal protocol version\n");
+#if LINUX_VERSION_CODE >= 0x010300
+            if (remapped)
+                vfree((void *)ha->brd);
+#endif
+            return 0;
+        }
+
+        ha->type = GDT_PCIMPR;
+        ha->ic_all_size = sizeof(dp6m_ptr->u);
+        
+        /* special command to controller BIOS */
+        dp6m_ptr->u.ic.S_Info[0]  = 0x00;
+        dp6m_ptr->u.ic.S_Info[1]  = 0x00;
+        dp6m_ptr->u.ic.S_Info[2]  = 0x01;
+        dp6m_ptr->u.ic.S_Info[3]  = 0x00;
+        dp6m_ptr->u.ic.S_Cmd_Indx = 0xfe;
+        dp6m_ptr->i960r.ldoor_reg = 1;
+        retries = INIT_RETRIES;
+        JIFFYWAIT(2);
+        while (dp6m_ptr->u.ic.S_Status != 0xfe) {
+            if (--retries == 0) {
+                printk("GDT-PCI: Initialization error\n");
+#if LINUX_VERSION_CODE >= 0x010300
+                if (remapped)
+                    vfree((void *)ha->brd);
+#endif
+                return 0;
+            }
+            udelay(1000);
+        }
+        dp6m_ptr->u.ic.S_Status = 0;
+    }
+
+    return 1;
+}
+
+
+/* controller protocol functions */
+
+static void gdth_enable_int(int hanum)
+{
+    gdth_ha_str *ha;
+    ulong flags;
+    ushort addr;
+    gdt2_dpram_str *dp2_ptr;
+    gdt6_dpram_str *dp6_ptr;
+    gdt6m_dpram_str *dp6m_ptr;
+
+    TRACE(("gdth_enable_int() hanum %d\n",hanum));
+    ha = HADATA(gdth_ctr_tab[hanum]);
+
+    save_flags(flags);
+    cli();
+
+    if (ha->type == GDT_EISA) {
+        addr = (ushort)ha->brd;
+        outb(0xff,addr+EDOORREG);
+        outb(0xff,addr+EDENABREG);
+        outb(0x01,addr+EINTENABREG);
+    } else if (ha->type == GDT_ISA) {
+        dp2_ptr = (gdt2_dpram_str *)ha->brd;
+        dp2_ptr->io.irqdel = 1;
+        dp2_ptr->u.ic.Cmd_Index = 0;
+        dp2_ptr->io.irqen = 1;
+    } else if (ha->type == GDT_PCI) {
+        dp6_ptr = (gdt6_dpram_str *)ha->brd;
+        dp6_ptr->io.irqdel = 1;
+        dp6_ptr->u.ic.Cmd_Index = 0;
+        dp6_ptr->io.irqen = 1;
+    } else if (ha->type == GDT_PCINEW) {
+        outb(0xff,PTR2USHORT(&ha->plx->edoor_reg));
+        outb(0x03,PTR2USHORT(&ha->plx->control1));
+    } else if (ha->type == GDT_PCIMPR) {
+        dp6m_ptr = (gdt6m_dpram_str *)ha->brd;
+        dp6m_ptr->i960r.edoor_reg = 0xff;
+        dp6m_ptr->i960r.edoor_en_reg &= ~4;
+    }
+    restore_flags(flags);
+}
+
+
+static int gdth_get_status(unchar *pIStatus,int irq)
+{
+    register gdth_ha_str *ha;
+    int i;
+
+    TRACE(("gdth_get_status() irq %d ctr_count %d\n",
+                 irq,gdth_ctr_count));
+    
+    *pIStatus = 0;
+    for (i=0; i<gdth_ctr_count; ++i) {
+        ha = HADATA(gdth_ctr_tab[i]);
+        if (ha->irq != (unchar)irq)             /* check IRQ */
+            continue;
+        if (ha->type == GDT_EISA)
+            *pIStatus = inb((ushort)ha->brd+EDOORREG);
+        else if (ha->type == GDT_ISA)
+            *pIStatus = ((gdt2_dpram_str *)ha->brd)->u.ic.Cmd_Index;
+        else if (ha->type == GDT_PCI)
+            *pIStatus = ((gdt6_dpram_str *)ha->brd)->u.ic.Cmd_Index;
+        else if (ha->type == GDT_PCINEW) 
+            *pIStatus = inb(PTR2USHORT(&ha->plx->edoor_reg));
+        else if (ha->type == GDT_PCIMPR)
+            *pIStatus = ((gdt6m_dpram_str *)ha->brd)->i960r.edoor_reg;
+   
+        if (*pIStatus)                                  
+            return i;                           /* board found */
+    }
+    return -1;
+}
+                 
+    
+static int gdth_test_busy(int hanum)
+{
+    register gdth_ha_str *ha;
+    register int gdtsema0 = 0;
+
+    TRACE(("gdth_test_busy() hanum %d\n",hanum));
+    
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    if (ha->type == GDT_EISA)
+        gdtsema0 = (int)inb((ushort)ha->brd+SEMA0REG);
+    else if (ha->type == GDT_ISA)
+        gdtsema0 = (int)((gdt2_dpram_str *)ha->brd)->u.ic.Sema0;
+    else if (ha->type == GDT_PCI)
+        gdtsema0 = (int)((gdt6_dpram_str *)ha->brd)->u.ic.Sema0;
+    else if (ha->type == GDT_PCINEW) 
+        gdtsema0 = (int)inb(PTR2USHORT(&ha->plx->sema0_reg));
+    else if (ha->type == GDT_PCIMPR)
+        gdtsema0 = (int)((gdt6m_dpram_str *)ha->brd)->i960r.sema0_reg;
+
+    return (gdtsema0 & 1);
+}
+
+
+static int gdth_get_cmd_index(int hanum)
+{
+    register gdth_ha_str *ha;
+    int i;
+
+    TRACE(("gdth_get_cmd_index() hanum %d\n",hanum));
+
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    for (i=0; i<GDTH_MAXCMDS; ++i) {
+        if (gdth_cmd_tab[i][hanum].cmnd == UNUSED_CMND) {
+            gdth_cmd_tab[i][hanum].cmnd = ha->pccb->RequestBuffer;
+            gdth_cmd_tab[i][hanum].service = ha->pccb->Service;
+            ha->pccb->CommandIndex = (ulong)i+2;
+            return (i+2);
+        }
+    }
+    return 0;
+}
+
+
+static void gdth_set_sema0(int hanum)
+{
+    register gdth_ha_str *ha;
+
+    TRACE(("gdth_set_sema0() hanum %d\n",hanum));
+
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    if (ha->type == GDT_EISA)
+        outb(1,(ushort)ha->brd+SEMA0REG);
+    else if (ha->type == GDT_ISA)
+        ((gdt2_dpram_str *)ha->brd)->u.ic.Sema0 = 1;
+    else if (ha->type == GDT_PCI)
+        ((gdt6_dpram_str *)ha->brd)->u.ic.Sema0 = 1;
+    else if (ha->type == GDT_PCINEW)  
+        outb(1,PTR2USHORT(&ha->plx->sema0_reg));
+    else if (ha->type == GDT_PCIMPR)
+        ((gdt6m_dpram_str *)ha->brd)->i960r.sema0_reg = 1;
+    
+}
+
+
+static void gdth_copy_command(int hanum)
+{
+    register gdth_ha_str *ha;
+    register gdth_cmd_str *cmd_ptr;
+    register gdt6m_dpram_str *dp6m_ptr;
+    register gdt6c_dpram_str *dp6c_ptr;
+    gdt6_dpram_str *dp6_ptr;
+    gdt2_dpram_str *dp2_ptr;
+    ushort cp_count,dp_offset,cmd_no;
+    
+    TRACE(("gdth_copy_command() hanum %d\n",hanum));
+
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    cp_count = ha->cmd_len;
+    dp_offset= ha->cmd_offs_dpmem;
+    cmd_no   = ha->cmd_cnt;
+    cmd_ptr  = ha->pccb;
+
+    ++ha->cmd_cnt;                                                      
+    if (ha->type == GDT_EISA)
+        return;                                 /* no DPMEM, no copy */
+
+    /* set cpcount dword aligned */
+    if (cp_count & 3)
+        cp_count += (4 - (cp_count & 3));
+
+    ha->cmd_offs_dpmem += cp_count;
+    
+    /* set offset and service, copy command to DPMEM */
+    if (ha->type == GDT_ISA) {
+        dp2_ptr = (gdt2_dpram_str *)ha->brd;
+        dp2_ptr->u.ic.comm_queue[cmd_no].offset =
+            dp_offset + DPMEM_COMMAND_OFFSET;
+        dp2_ptr->u.ic.comm_queue[cmd_no].serv_id =
+            (ushort)cmd_ptr->Service;
+        memcpy(&dp2_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
+    } else if (ha->type == GDT_PCI) {
+        dp6_ptr = (gdt6_dpram_str *)ha->brd;
+        dp6_ptr->u.ic.comm_queue[cmd_no].offset =
+            dp_offset + DPMEM_COMMAND_OFFSET;
+        dp6_ptr->u.ic.comm_queue[cmd_no].serv_id =
+            (ushort)cmd_ptr->Service;
+        memcpy(&dp6_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
+    } else if (ha->type == GDT_PCINEW) {
+        dp6c_ptr = (gdt6c_dpram_str *)ha->brd;
+        dp6c_ptr->u.ic.comm_queue[cmd_no].offset =
+            dp_offset + DPMEM_COMMAND_OFFSET;
+        dp6c_ptr->u.ic.comm_queue[cmd_no].serv_id =
+            (ushort)cmd_ptr->Service;
+        memcpy(&dp6c_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
+    } else if (ha->type == GDT_PCIMPR) {
+        dp6m_ptr = (gdt6m_dpram_str *)ha->brd;
+        dp6m_ptr->u.ic.comm_queue[cmd_no].offset =
+            dp_offset + DPMEM_COMMAND_OFFSET;
+        dp6m_ptr->u.ic.comm_queue[cmd_no].serv_id =
+            (ushort)cmd_ptr->Service;
+        memcpy(&dp6m_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
+    }
+}
+
+
+static void gdth_release_event(int hanum)
+{
+    register gdth_ha_str *ha;
+
+#ifdef GDTH_STATISTICS
+    ulong i,j;
+    for (i=0,j=0; j<GDTH_MAXCMDS; ++j) {
+        if (gdth_cmd_tab[j][hanum].cmnd != UNUSED_CMND)
+            ++i;
+    }
+    if (max_index < i) {
+        max_index = i;
+        TRACE3(("GDT: max_index = %d\n",(ushort)i));
+    }
+#endif
+
+    TRACE(("gdth_release_event() hanum %d\n",hanum));
+    ha = HADATA(gdth_ctr_tab[hanum]);
+
+    if (ha->pccb->OpCode == GDT_INIT)
+        ha->pccb->Service |= 0x80;
+
+    if (ha->type == GDT_EISA) {
+        outb(ha->pccb->Service,(ushort)ha->brd+LDOORREG);
+        if (ha->pccb->OpCode == GDT_INIT)               /* store DMA buffer */
+            outl((ulong)ha->pccb,(ushort)ha->brd+MAILBOXREG);
+    } else if (ha->type == GDT_ISA)
+        ((gdt2_dpram_str *)ha->brd)->io.event = 0;
+    else if (ha->type == GDT_PCI)
+        ((gdt6_dpram_str *)ha->brd)->io.event = 0;
+    else if (ha->type == GDT_PCINEW) 
+        outb(1,PTR2USHORT(&ha->plx->ldoor_reg));
+    else if (ha->type == GDT_PCIMPR)
+        ((gdt6m_dpram_str *)ha->brd)->i960r.ldoor_reg = 1;
+}
+
+    
+static int gdth_wait(int hanum,int index,ulong time)
+{
+    gdth_ha_str *ha;
+    int answer_found = FALSE;
+
+    TRACE(("gdth_wait() hanum %d index %d time %ld\n",hanum,index,time));
+
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    if (index == 0)
+        return 1;                               /* no wait required */
+
+    gdth_from_wait = TRUE;
+    do {
+#if LINUX_VERSION_CODE >= 0x010346
+        gdth_interrupt((int)ha->irq,NULL,NULL);
+#else
+        gdth_interrupt((int)ha->irq,NULL);
+#endif
+        if (wait_hanum==hanum && wait_index==index) {
+            answer_found = TRUE;
+            break;
+        }
+        udelay(1000);
+    } while (--time);
+    gdth_from_wait = FALSE;
+    
+    while (gdth_test_busy(hanum))
+        udelay(1);
+
+    return (answer_found);
+}
+
+
+static int gdth_internal_cmd(int hanum,unchar service,ushort opcode,ulong p1,
+                             ulong p2,ulong p3)
+{
+    register gdth_ha_str *ha;
+    register gdth_cmd_str *cmd_ptr;
+    int retries,index;
+
+    TRACE2(("gdth_internal_cmd() service %d opcode %d\n",service,opcode));
+
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    cmd_ptr = ha->pccb;
+    memset((char*)cmd_ptr,0,sizeof(gdth_cmd_str));
+
+    /* make command  */
+    for (retries = INIT_RETRIES;;) {
+        cmd_ptr->Service          = service;
+        cmd_ptr->RequestBuffer    = INTERNAL_CMND;
+        if (!(index=gdth_get_cmd_index(hanum))) {
+            TRACE(("GDT: No free command index found\n"));
+            return 0;
+        }
+        gdth_set_sema0(hanum);
+        cmd_ptr->OpCode           = opcode;
+        cmd_ptr->BoardNode        = LOCALBOARD;
+        if (service == CACHESERVICE) {
+            if (opcode == GDT_IOCTL) {
+                cmd_ptr->u.ioctl.subfunc = p1;
+                cmd_ptr->u.ioctl.channel = p2;
+                cmd_ptr->u.ioctl.param_size = (ushort)p3;
+                cmd_ptr->u.ioctl.p_param = (ulong)ha->pscratch;
+            } else {
+                cmd_ptr->u.cache.DeviceNo = (ushort)p1;
+                cmd_ptr->u.cache.BlockNo  = p2;
+            }
+        } else if (service == SCSIRAWSERVICE) {
+            cmd_ptr->u.raw.direction  = p1;
+            cmd_ptr->u.raw.bus        = (unchar)p2;
+            cmd_ptr->u.raw.target     = (unchar)p3;
+            cmd_ptr->u.raw.lun        = 0;
+        }
+        ha->cmd_len          = sizeof(gdth_cmd_str);
+        ha->cmd_offs_dpmem   = 0;
+        ha->cmd_cnt          = 0;
+        gdth_copy_command(hanum);
+        gdth_release_event(hanum);
+        JIFFYWAIT(2);
+        if (!gdth_wait(hanum,index,INIT_TIMEOUT)) {
+            printk("GDT: Initialization error (timeout service %d)\n",service);
+            return 0;
+        }
+        if (ha->status != S_BSY || --retries == 0)
+            break;
+        udelay(1000);   
+    }   
+    
+    return (ha->status != S_OK ? 0:1);
+}
+    
+
+/* search for devices */
+
+static int gdth_search_drives(int hanum,int firsttime)
+{
+    register gdth_ha_str *ha;
+    ushort cdev_cnt,i;
+    unchar b,t,pos_found;
+    ulong drv_cyls, drv_hds, drv_secs;
+    ulong bus_no;
+    gdth_getch_str *chn;
+    
+    TRACE(("gdth_search_drives() hanum %d flag %d\n",hanum,firsttime));
+    ha = HADATA(gdth_ctr_tab[hanum]);
+
+    /* initialize controller services, at first: screen service */
+    if (!gdth_internal_cmd(hanum,SCREENSERVICE,GDT_INIT,0,0,0)) {
+        printk("GDT: Initialization error screen service (code %d)\n",
+               ha->status);
+        return 0;
+    }
+    TRACE2(("gdth_search_drives(): SCREENSERVICE initialized\n"));
+    
+    /* initialize cache service */
+    if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_INIT,LINUX_OS,0,0)) {
+        printk("GDT: Initialization error cache service (code %d)\n",
+               ha->status);
+        return 0;
+    }
+    TRACE2(("gdth_search_drives(): CACHESERVICE initialized\n"));
+    cdev_cnt = (ushort)ha->info;
+
+    if (firsttime) {
+        /* mount all cache devices */
+        if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_MOUNT,0xffff,1,0)) {
+            printk("GDT: Initialization error cache service (code %d)\n",
+                   ha->status);
+            return 0;
+        }
+        TRACE2(("gdth_search_drives(): mountall CACHESERVICE OK\n"));
+
+        /* initialize cache service after mountall */
+        if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_INIT,LINUX_OS,0,0)) {
+            printk("GDT: Initialization error cache service (code %d)\n",
+                   ha->status);
+            return 0;
+        }
+        TRACE2(("gdth_search_drives() CACHES. init. after mountall\n"));
+        cdev_cnt = (ushort)ha->info;
+
+        /* detect number of SCSI buses */
+        chn = (gdth_getch_str *)DMADATA(gdth_ctr_tab[hanum]);
+        for (bus_no=0; bus_no<MAXBUS; ++bus_no) {
+            chn->channel_no = bus_no;
+            if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,
+                                   SCSI_CHAN_CNT | L_CTRL_PATTERN,
+                                   IO_CHANNEL | INVALID_CHANNEL,
+                                   sizeof(gdth_getch_str))) {
+                if (bus_no == 0) {
+                    printk("GDT: Error detecting SCSI channel count (0x%x)\n",
+                           ha->status);
+                    return 0;
+                }
+                break;
+            }
+            if (chn->siop_id < MAXID)
+                ha->id[bus_no][chn->siop_id].type = SIOP_DTYP;
+        }       
+        ha->bus_cnt = (unchar)bus_no;
+        TRACE2(("gdth_search_drives() %d SCSI channels\n",ha->bus_cnt));
+
+        /* read cache configuration */
+        if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,CACHE_INFO,
+                               INVALID_CHANNEL,sizeof(gdth_cinfo_str))) {
+            printk("GDT: Initialization error cache service (code %d)\n",
+                   ha->status);
+            return 0;
+        }
+        ha->cpar = ((gdth_cinfo_str *)DMADATA(gdth_ctr_tab[hanum]))->cpar;
+        TRACE2(("gdth_search_drives() cinfo: vs %lx sta %d str %d dw %d b %d\n",
+                ha->cpar.version,ha->cpar.state,ha->cpar.strategy,
+                ha->cpar.write_back,ha->cpar.block_size));
+    }
+
+    /* initialize raw service */
+    if (!gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_INIT,0,0,0)) {
+        printk("GDT: Initialization error raw service (code %d)\n",
+               ha->status);
+        return 0;
+    }
+    TRACE2(("gdth_search_drives(): RAWSERVICE initialized\n"));
+
+    /* set/get features raw service (scatter/gather) */
+    ha->raw_feat = 0;
+    if (gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_SET_FEAT,SCATTER_GATHER,
+                          0,0)) {
+        TRACE2(("gdth_search_drives(): set features RAWSERVICE OK\n"));
+        if (gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_GET_FEAT,0,0,0))
+        {
+            TRACE2(("gdth_search_dr(): get feat RAWSERVICE %ld\n",
+                          ha->info));
+            ha->raw_feat = (ushort)ha->info;
+        }
+    } 
+
+    /* set/get features cache service (equal to raw service) */
+    if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_SET_FEAT,0,
+                          SCATTER_GATHER,0)) {
+        TRACE2(("gdth_search_drives(): set features CACHESERVICE OK\n"));
+        if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_GET_FEAT,0,0,0)) {
+            TRACE2(("gdth_search_dr(): get feat CACHESERV. %ld\n",
+                          ha->info));
+            ha->cache_feat = (ushort)ha->info;
+        }
+    }
+
+    /* if it is not the first scan, we are ready */
+    if (!firsttime)
+        return 1;
+
+    /* scanning for raw devices */
+    for (b=0; b<ha->bus_cnt; ++b) {
+        for (t=0; t<MAXID; ++t) {
+            TRACE(("gdth_search_drives() rawd. bus %d id %d\n",b,t));
+            if (ha->id[b][t].type != SIOP_DTYP && 
+                gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_INFO,0,b,t)) {
+                ha->id[b][t].type = RAW_DTYP;
+            }
+        }
+    }
+
+    /* scanning for cache devices */
+    for (i=0; i<cdev_cnt && i<MAX_HDRIVES; ++i) {
+        TRACE(("gdth_search_drives() cachedev. %d\n",i));
+        if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_INFO,i,0,0)) {
+            /* dynamic relation between host drive number and Bus/ID */
+            /* search free position */
+            pos_found = FALSE;
+            for (b=0,t=0; b<ha->bus_cnt; ++b) {
+                for (t=0; t<MAXID; ++t) {
+                    if (ha->id[b][t].type == EMPTY_DTYP) {
+                        pos_found = TRUE;
+                        break;
+                    }
+                }
+                if (pos_found)
+                    break;
+            }
+            TRACE(("gdth_search_dr() drive %d free pos at bus/id %d/%d\n",
+                         i,b,t));
+
+            ha->id[b][t].type      = CACHE_DTYP;
+            ha->id[b][t].devtype   = 0;
+            ha->id[b][t].size      = ha->info;
+            ha->id[b][t].hostdrive = i;
+
+            /* evaluate mapping (sectors per head, heads per cylinder) */
+            ha->id[b][t].size &= ~SECS32;
+            drv_cyls = ha->id[b][t].size /HEADS/SECS;
+            if (drv_cyls <= MAXCYLS) {
+                drv_hds = HEADS;
+                drv_secs= SECS;
+            } else {                            /* too high for 64*32 */
+                drv_cyls = ha->id[b][t].size /MEDHEADS/MEDSECS;
+                if (drv_cyls <= MAXCYLS) {
+                    drv_hds = MEDHEADS;
+                    drv_secs= MEDSECS;
+                } else {                        /* too high for 127*63 */
+                    drv_cyls = ha->id[b][t].size /BIGHEADS/BIGSECS;
+                    drv_hds = BIGHEADS;
+                    drv_secs= BIGSECS;
+                }
+            }
+            ha->id[b][t].heads = (unchar)drv_hds;
+            ha->id[b][t].secs  = (unchar)drv_secs;
+            /* round size */
+            ha->id[b][t].size  = drv_cyls * drv_hds * drv_secs;
+            TRACE(("gdth_search_dr() cdr. %d size %ld hds %ld scs %ld\n",
+                   i,ha->id[b][t].size,drv_hds,drv_secs));
+            
+            /* get informations about device */
+            if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_DEVTYPE,i,
+                                  0,0)) {
+                TRACE(("gdth_search_dr() cache drive %d devtype %ld\n",
+                       i,ha->info));
+                ha->id[b][t].devtype = (ushort)ha->info;
+            }
+        }
+    }
+
+    TRACE(("gdth_search_drives() OK\n"));
+    return 1;
+}
+
+
+/* command queueing/sending functions */
+
+static void gdth_putq(int hanum,Scsi_Cmnd *scp,unchar priority)
+{
+    register gdth_ha_str *ha;
+    register Scsi_Cmnd *pscp;
+    register Scsi_Cmnd *nscp;
+    ulong flags;
+    unchar b, t;
+
+    TRACE(("gdth_putq() priority %d\n",priority));
+    save_flags(flags);
+    cli();
+
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    scp->SCp.this_residual = (int)priority;
+    gdth_update_timeout(scp, scp->timeout * 6);
+#if LINUX_VERSION_CODE >= 0x010400
+    b = scp->channel;
+#else
+    b = NUMDATA(nscp->host)->busnum;
+#endif
+    t = scp->target;
+#if LINUX_VERSION_CODE >= 0x010300
+    if (priority >= DEFAULT_PRI && ha->id[b][t].lock) {
+        TRACE2(("gdth_putq(): locked IO -> update_timeout()\n"));
+        scp->SCp.buffers_residual = gdth_update_timeout(scp, 0);
+    }
+#endif
+
+    if (ha->req_first==NULL) {
+        ha->req_first = scp;                    /* queue was empty */
+        scp->SCp.ptr = NULL;
+    } else {                                    /* queue not empty */
+        pscp = ha->req_first;
+        nscp = (Scsi_Cmnd *)pscp->SCp.ptr;
+        /* priority: 0-highest,..,0xff-lowest */
+        while (nscp && (unchar)nscp->SCp.this_residual <= priority) {
+            pscp = nscp;
+            nscp = (Scsi_Cmnd *)pscp->SCp.ptr;
+        }
+        pscp->SCp.ptr = (char *)scp;
+        scp->SCp.ptr  = (char *)nscp;
+    }
+    restore_flags(flags);
+
+#ifdef GDTH_STATISTICS
+    flags = 0;
+    for (nscp=ha->req_first; nscp; nscp=(Scsi_Cmnd*)nscp->SCp.ptr)
+        ++flags;
+    if (max_rq < flags) {
+        max_rq = flags;
+        TRACE3(("GDT: max_rq = %d\n",(ushort)max_rq));
+    }
+#endif
+}
+
+static void gdth_next(int hanum)
+{
+    register gdth_ha_str *ha;
+    register Scsi_Cmnd *pscp;
+    register Scsi_Cmnd *nscp;
+    unchar b, t, next_cmd, firsttime;
+    ushort hdrive;
+    ulong flags;
+    int cmd_index;
+
+    TRACE(("gdth_next() hanum %d\n",hanum));
+    save_flags(flags);
+    cli();
+
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    ha->cmd_cnt = ha->cmd_offs_dpmem = 0;
+    next_cmd = firsttime = TRUE;
+    cmd_index = 0;
+
+    for (nscp = pscp = ha->req_first; nscp; nscp = (Scsi_Cmnd *)nscp->SCp.ptr) {
+        if (nscp != pscp && nscp != (Scsi_Cmnd *)pscp->SCp.ptr)
+            pscp = (Scsi_Cmnd *)pscp->SCp.ptr;
+#if LINUX_VERSION_CODE >= 0x010400
+        b = nscp->channel;
+#else
+        b = NUMDATA(nscp->host)->busnum;
+#endif
+        t = nscp->target;
+        if (nscp->SCp.this_residual < DEFAULT_PRI || !ha->id[b][t].lock) {
+
+            if (firsttime) {
+                if (gdth_test_busy(hanum)) {        /* controller busy ? */
+                    TRACE(("gdth_next() controller %d busy !\n",hanum));
+                    if (!gdth_polling) {
+                        restore_flags(flags);
+                        return;
+                    }
+                    while (gdth_test_busy(hanum))
+                        udelay(1000);
+                }
+                firsttime = FALSE;
+            }
+
+#if LINUX_VERSION_CODE >= 0x010300
+            if (nscp->done == gdth_scsi_done) {
+                if (!(cmd_index=gdth_special_cmd(hanum,nscp,b)))
+                    next_cmd = FALSE;
+            } else
+#endif
+            if (ha->id[b][t].type != CACHE_DTYP) {
+                if (!(cmd_index=gdth_fill_raw_cmd(hanum,nscp,b)))
+                    next_cmd = FALSE;
+            } else {
+                hdrive = ha->id[b][t].hostdrive;
+                switch (nscp->cmnd[0]) {
+                  case TEST_UNIT_READY:
+                  case INQUIRY:
+                  case REQUEST_SENSE:
+                  case READ_CAPACITY:
+                  case VERIFY:
+                  case START_STOP:
+                  case MODE_SENSE:
+                    TRACE2(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0],
+                        nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3],
+                        nscp->cmnd[4],nscp->cmnd[5]));
+                        gdth_internal_cache_cmd(hanum,nscp,b,&flags);
+                    break;
+
+                  case ALLOW_MEDIUM_REMOVAL:
+                    TRACE2(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0],
+                        nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3],
+                        nscp->cmnd[4],nscp->cmnd[5]));
+                    if ( (nscp->cmnd[4]&1) && !(ha->id[b][t].devtype&1) ) {
+                        TRACE2(("Prevent r. nonremov. drive->do nothing\n"));
+                        nscp->result = DID_OK << 16;
+                        restore_flags( flags );
+                        nscp->scsi_done(nscp);
+                        save_flags( flags );
+                        cli();
+                    } else {
+                        nscp->cmnd[3] = (ha->id[b][t].devtype&1) ? 1:0;
+                        TRACE2(("Prevent/allow r. %d rem. drive %d\n",
+                            nscp->cmnd[4],nscp->cmnd[3]));
+                        if (!(cmd_index=gdth_fill_cache_cmd(hanum,nscp,hdrive)))
+                            next_cmd = FALSE;
+                    }
+                    break;
+
+                  case READ_6:
+                  case WRITE_6:
+                  case READ_10:
+                  case WRITE_10:
+                    if (!(cmd_index=gdth_fill_cache_cmd(hanum,nscp,hdrive)))
+                        next_cmd = FALSE;
+                    break;
+
+                  default:
+                    TRACE2(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0],
+                        nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3],
+                        nscp->cmnd[4],nscp->cmnd[5]));
+                    printk("GDT: Unknown SCSI command 0x%x to cache service !\n",
+                       nscp->cmnd[0]);
+                    nscp->result = DID_ABORT << 16;
+                    restore_flags( flags );
+                    nscp->scsi_done( nscp );
+                    save_flags( flags );
+                    cli();
+                    break;
+                }
+            }
+
+            if (!next_cmd)
+                break;
+            if (nscp == ha->req_first)
+                ha->req_first = pscp = (Scsi_Cmnd *)nscp->SCp.ptr;
+            else
+                pscp->SCp.ptr = nscp->SCp.ptr;
+            if (gdth_polling)
+                break;
+        }
+    }
+
+    if (ha->cmd_cnt > 0) {
+        gdth_release_event(hanum);
+    }
+
+    restore_flags(flags);
+
+    if (gdth_polling && ha->cmd_cnt > 0) {
+        if (!gdth_wait(hanum,cmd_index,POLL_TIMEOUT))
+            printk("GDT: Controller %d: Command %d timed out !\n",
+                   hanum,cmd_index);
+    }
+}
+    
+static void gdth_copy_internal_data(Scsi_Cmnd *scp,char *buffer,ushort count)
+{
+    ushort cpcount,i;
+    ushort cpsum,cpnow;
+    struct scatterlist *sl;
+
+    cpcount = count<=(ushort)scp->bufflen ? count:(ushort)scp->bufflen;
+    if (scp->use_sg) {
+        sl = (struct scatterlist *)scp->request_buffer;
+        for (i=0,cpsum=0; i<scp->use_sg; ++i,++sl) {
+            cpnow = (ushort)sl->length;
+            TRACE(("copy_internal() now %d sum %d count %d %d\n",
+                          cpnow,cpsum,cpcount,(ushort)scp->bufflen));
+            if (cpsum+cpnow > cpcount) 
+                cpnow = cpcount - cpsum;
+            cpsum += cpnow;
+            memcpy((char*)sl->address,buffer,cpnow);
+            if (cpsum == cpcount)
+                break;
+            buffer += cpnow;
+        }
+    } else {
+        TRACE(("copy_internal() count %d\n",cpcount));
+        memcpy((char*)scp->request_buffer,buffer,cpcount);
+    }
+}
+
+static int gdth_internal_cache_cmd(int hanum,Scsi_Cmnd *scp,
+                                   unchar b,ulong *flags)
+{
+    register gdth_ha_str *ha;
+    ushort hdrive;
+    unchar t;
+    gdth_inq_data inq;
+    gdth_rdcap_data rdc;
+    gdth_sense_data sd;
+    gdth_modep_data mpd;
+
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    t  = scp->target;
+    hdrive = ha->id[b][t].hostdrive;
+    TRACE(("gdth_internal_cache_cmd() cmd 0x%x hdrive %d\n",
+                 scp->cmnd[0],hdrive));
+
+    if (scp->lun !=0)
+        scp->result = DID_BAD_TARGET << 16;
+    else {
+        switch (scp->cmnd[0]) {
+          case TEST_UNIT_READY:
+          case VERIFY:
+          case START_STOP:
+            TRACE2(("Test/Verify/Start hdrive %d\n",hdrive));
+            break;
+
+          case INQUIRY:
+            TRACE2(("Inquiry hdrive %d devtype %d\n",
+                          hdrive,ha->id[b][t].devtype));
+            inq.type_qual = (ha->id[b][t].devtype&4) ? TYPE_ROM:TYPE_DISK;
+            /* you can here set all disks to removable, if you want to do
+               a flush using the ALLOW_MEDIUM_REMOVAL command */
+            inq.modif_rmb = ha->id[b][t].devtype&1 ? 0x80:0x00;
+            inq.version   = 2;
+            inq.resp_aenc = 2;
+            inq.add_length= 32;
+            strcpy(inq.vendor,"ICP    ");
+            sprintf(inq.product,"Host Drive  #%02d",hdrive);
+            strcpy(inq.revision,"   ");
+            gdth_copy_internal_data(scp,(char*)&inq,sizeof(gdth_inq_data));
+            break;
+
+          case REQUEST_SENSE:
+            TRACE2(("Request sense hdrive %d\n",hdrive));
+            sd.errorcode = 0x70;
+            sd.segno     = 0x00;
+            sd.key       = NO_SENSE;
+            sd.info      = 0;
+            sd.add_length= 0;
+            gdth_copy_internal_data(scp,(char*)&sd,sizeof(gdth_sense_data));
+            break;
+
+          case MODE_SENSE:
+            TRACE2(("Mode sense hdrive %d\n",hdrive));
+            memset((char*)&mpd,0,sizeof(gdth_modep_data));
+            mpd.hd.data_length = sizeof(gdth_modep_data);
+            mpd.hd.dev_par     = (ha->id[b][t].devtype&2) ? 0x80:0;
+            mpd.hd.bd_length   = sizeof(mpd.bd);
+            mpd.bd.block_length[0] = (SECTOR_SIZE & 0x00ff0000) >> 16;
+            mpd.bd.block_length[1] = (SECTOR_SIZE & 0x0000ff00) >> 8;
+            mpd.bd.block_length[2] = (SECTOR_SIZE & 0x000000ff);
+            gdth_copy_internal_data(scp,(char*)&mpd,sizeof(gdth_modep_data));
+            break;
+
+          case READ_CAPACITY:
+            TRACE2(("Read capacity hdrive %d\n",hdrive));
+            rdc.last_block_no = ntohl(ha->id[b][t].size-1);
+            rdc.block_length  = ntohl(SECTOR_SIZE);
+            gdth_copy_internal_data(scp,(char*)&rdc,sizeof(gdth_rdcap_data));
+            break;
+
+          default:
+            TRACE2(("Internal cache cmd 0x%x unknown\n",scp->cmnd[0]));
+            break;
+        }
+        scp->result = DID_OK << 16;
+    }
+
+    restore_flags(*flags);
+    scp->scsi_done(scp);
+    save_flags(*flags);
+    cli();
+    return 1;
+}
+    
+static int gdth_fill_cache_cmd(int hanum,Scsi_Cmnd *scp,ushort hdrive)
+{
+    register gdth_ha_str *ha;
+    register gdth_cmd_str *cmdp;
+    struct scatterlist *sl;
+    ushort i;
+    int cmd_index;
+
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    cmdp = ha->pccb;
+    TRACE(("gdth_fill_cache_cmd() cmd 0x%x cmdsize %d hdrive %d\n",
+                 scp->cmnd[0],scp->cmd_len,hdrive));
+
+    if (ha->type==GDT_EISA && ha->cmd_cnt>0) 
+        return 0;
+
+    cmdp->Service = CACHESERVICE;
+    cmdp->RequestBuffer = scp;
+    /* search free command index */
+    if (!(cmd_index=gdth_get_cmd_index(hanum))) {
+        TRACE(("GDT: No free command index found\n"));
+        return 0;
+    }
+    /* if it's the first command, set command semaphore */
+    if (ha->cmd_cnt == 0)
+        gdth_set_sema0(hanum);
+
+    /* fill command */
+    if (scp->cmnd[0]==ALLOW_MEDIUM_REMOVAL) {
+        if (scp->cmnd[4] & 1)                   /* prevent ? */
+            cmdp->OpCode      = GDT_MOUNT;
+        else if (scp->cmnd[3] & 1)              /* removable drive ? */
+            cmdp->OpCode      = GDT_UNMOUNT;
+        else
+            cmdp->OpCode      = GDT_FLUSH;
+    } else {
+        if (scp->cmnd[0]==WRITE_6 || scp->cmnd[0]==WRITE_10) {
+            if (gdth_write_through)
+                cmdp->OpCode  = GDT_WRITE_THR;
+            else
+                cmdp->OpCode  = GDT_WRITE;
+        } else {
+            cmdp->OpCode      = GDT_READ;
+        }
+    }
+
+    cmdp->BoardNode           = LOCALBOARD;
+    cmdp->u.cache.DeviceNo    = hdrive;
+
+    if (scp->cmnd[0]==ALLOW_MEDIUM_REMOVAL) {
+        cmdp->u.cache.BlockNo = 1;
+        cmdp->u.cache.sg_canz = 0;
+    } else {
+        if (scp->cmd_len != 6) {
+            cmdp->u.cache.BlockNo = ntohl(*(ulong*)&scp->cmnd[2]);
+            cmdp->u.cache.BlockCnt= (ulong)ntohs(*(ushort*)&scp->cmnd[7]);
+        } else {
+            cmdp->u.cache.BlockNo = ntohl(*(ulong*)&scp->cmnd[0]) & 0x001fffffUL;
+            cmdp->u.cache.BlockCnt= scp->cmnd[4]==0 ? 0x100 : scp->cmnd[4];
+        }
+
+        if (scp->use_sg) {
+            cmdp->u.cache.DestAddr= -1UL;
+            sl = (struct scatterlist *)scp->request_buffer;
+            for (i=0; i<scp->use_sg; ++i,++sl) {
+                cmdp->u.cache.sg_lst[i].sg_ptr = (ulong)sl->address;
+                cmdp->u.cache.sg_lst[i].sg_len = (ulong)sl->length;
+            }
+            cmdp->u.cache.sg_canz = (ulong)i;
+
+#ifdef GDTH_STATISTICS
+            if (max_sg < (ulong)i) {
+                max_sg = (ulong)i;
+                TRACE3(("GDT: max_sg = %d\n",i));
+            }
+#endif
+            if (i<GDTH_MAXSG)
+                cmdp->u.cache.sg_lst[i].sg_len = 0;
+        } else {
+            if (ha->cache_feat & SCATTER_GATHER) {
+                cmdp->u.cache.DestAddr = -1UL;
+                cmdp->u.cache.sg_canz = 1;
+                cmdp->u.cache.sg_lst[0].sg_ptr = (ulong)scp->request_buffer;
+                cmdp->u.cache.sg_lst[0].sg_len = scp->request_bufflen;
+                cmdp->u.cache.sg_lst[1].sg_len = 0;
+            } else {
+                cmdp->u.cache.DestAddr  = (ulong)scp->request_buffer;
+                cmdp->u.cache.sg_canz= 0;
+            }
+        }
+    }
+    TRACE(("cache cmd: addr. %lx sganz %lx sgptr0 %lx sglen0 %lx\n",
+                  cmdp->u.cache.DestAddr,cmdp->u.cache.sg_canz,
+                  cmdp->u.cache.sg_lst[0].sg_ptr,
+                  cmdp->u.cache.sg_lst[0].sg_len));
+    TRACE(("cache cmd: cmd %d blockno. %ld, blockcnt %ld\n",
+                  cmdp->OpCode,cmdp->u.cache.BlockNo,cmdp->u.cache.BlockCnt));
+
+    /* evaluate command size, check space */
+    ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.cache.sg_lst) +
+        (ushort)cmdp->u.cache.sg_canz * sizeof(gdth_sg_str);
+    if (ha->cmd_len & 3)
+        ha->cmd_len += (4 - (ha->cmd_len & 3));
+
+    if (ha->cmd_cnt > 0) {
+        if ((ha->cmd_offs_dpmem + ha->cmd_len + DPMEM_COMMAND_OFFSET) >
+            ha->ic_all_size) {
+            TRACE2(("gdth_fill_cache() DPMEM overflow\n"));
+            gdth_cmd_tab[cmd_index-2][hanum].cmnd = UNUSED_CMND;
+            return 0;
+        }
+    }
+
+    /* copy command */
+    gdth_copy_command(hanum);
+    return cmd_index;
+}
+
+static int gdth_fill_raw_cmd(int hanum,Scsi_Cmnd *scp,unchar b)
+{
+    register gdth_ha_str *ha;
+    register gdth_cmd_str *cmdp;
+    struct scatterlist *sl;
+    ushort i;
+    int cmd_index;
+    unchar t,l;
+
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    t = scp->target;
+    l = scp->lun;
+    cmdp = ha->pccb;
+    TRACE(("gdth_fill_raw_cmd() cmd 0x%x bus %d ID %d LUN %d\n",
+                 scp->cmnd[0],b,t,l));
+
+    if (ha->type==GDT_EISA && ha->cmd_cnt>0) 
+        return 0;
+
+    cmdp->Service = SCSIRAWSERVICE;
+    cmdp->RequestBuffer = scp;
+    /* search free command index */
+    if (!(cmd_index=gdth_get_cmd_index(hanum))) {
+        TRACE(("GDT: No free command index found\n"));
+        return 0;
+    }
+    /* if it's the first command, set command semaphore */
+    if (ha->cmd_cnt == 0)
+        gdth_set_sema0(hanum);
+
+    /* fill command */  
+    cmdp->OpCode           = GDT_WRITE;         /* always */
+    cmdp->BoardNode        = LOCALBOARD;
+    cmdp->u.raw.reserved   = 0;
+    cmdp->u.raw.mdisc_time = 0;
+    cmdp->u.raw.mcon_time  = 0;
+    cmdp->u.raw.clen       = scp->cmd_len;
+    cmdp->u.raw.target     = t;
+    cmdp->u.raw.lun        = l;
+    cmdp->u.raw.bus        = b;
+    cmdp->u.raw.priority   = 0;
+    cmdp->u.raw.link_p     = NULL;
+    cmdp->u.raw.sdlen      = scp->request_bufflen;
+    cmdp->u.raw.sense_len  = 16;
+    cmdp->u.raw.sense_data = (ulong)scp->sense_buffer;
+    cmdp->u.raw.direction  = 
+        gdth_direction_tab[scp->cmnd[0]]==DOU ? DATA_OUT : DATA_IN;
+    memcpy(cmdp->u.raw.cmd,scp->cmnd,12);
+
+    if (scp->use_sg) {
+        cmdp->u.raw.sdata  = -1UL;
+        sl = (struct scatterlist *)scp->request_buffer;
+        for (i=0; i<scp->use_sg; ++i,++sl) {
+            cmdp->u.raw.sg_lst[i].sg_ptr = (ulong)sl->address;
+            cmdp->u.raw.sg_lst[i].sg_len = (ulong)sl->length;
+        }
+        cmdp->u.raw.sg_ranz = (ulong)i;
+
+#ifdef GDTH_STATISTICS
+        if (max_sg < (ulong)i) {
+            max_sg = (ulong)i;
+            TRACE3(("GDT: max_sg = %d\n",i));
+        }
+#endif
+        if (i<GDTH_MAXSG)
+            cmdp->u.raw.sg_lst[i].sg_len = 0;
+    } else {
+        if (ha->raw_feat & SCATTER_GATHER) {
+            cmdp->u.raw.sdata  = -1UL;
+            cmdp->u.raw.sg_ranz= 1;
+            cmdp->u.raw.sg_lst[0].sg_ptr = (ulong)scp->request_buffer;
+            cmdp->u.raw.sg_lst[0].sg_len = scp->request_bufflen;
+            cmdp->u.raw.sg_lst[1].sg_len = 0;
+        } else {
+            cmdp->u.raw.sdata  = (ulong)scp->request_buffer;
+            cmdp->u.raw.sg_ranz= 0;
+        }
+    }
+    TRACE(("raw cmd: addr. %lx sganz %lx sgptr0 %lx sglen0 %lx\n",
+                  cmdp->u.raw.sdata,cmdp->u.raw.sg_ranz,
+                  cmdp->u.raw.sg_lst[0].sg_ptr,
+                  cmdp->u.raw.sg_lst[0].sg_len));
+
+    /* evaluate command size, check space */
+    ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.raw.sg_lst) +
+        (ushort)cmdp->u.raw.sg_ranz * sizeof(gdth_sg_str);
+    if (ha->cmd_len & 3)
+        ha->cmd_len += (4 - (ha->cmd_len & 3));
+
+    if (ha->cmd_cnt > 0) {
+        if ((ha->cmd_offs_dpmem + ha->cmd_len + DPMEM_COMMAND_OFFSET) >
+            ha->ic_all_size) {
+            TRACE2(("gdth_fill_raw() DPMEM overflow\n"));
+            gdth_cmd_tab[cmd_index-2][hanum].cmnd = UNUSED_CMND;
+            return 0;
+        }
+    }
+
+    /* copy command */
+    gdth_copy_command(hanum);
+    return cmd_index;
+}
+
+static int gdth_special_cmd(int hanum,Scsi_Cmnd *scp,unchar b)
+{
+    register gdth_ha_str *ha;
+    register gdth_cmd_str *cmdp;
+    int cmd_index;
+
+    ha  = HADATA(gdth_ctr_tab[hanum]);
+    cmdp= ha->pccb;
+    TRACE2(("gdth_special_cmd(): "));
+
+    if (ha->type==GDT_EISA && ha->cmd_cnt>0) 
+        return 0;
+
+    memcpy( cmdp, scp->request_buffer, sizeof(gdth_cmd_str));
+    cmdp->RequestBuffer = scp;
+
+    /* search free command index */
+    if (!(cmd_index=gdth_get_cmd_index(hanum))) {
+        TRACE(("GDT: No free command index found\n"));
+        return 0;
+    }
+
+    /* if it's the first command, set command semaphore */
+    if (ha->cmd_cnt == 0)
+       gdth_set_sema0(hanum);
+
+    /* evaluate command size, check space */
+    if (cmdp->OpCode == GDT_IOCTL) {
+        TRACE2(("IOCTL\n"));
+        ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.ioctl.p_param) + sizeof(ulong);
+    } else if (cmdp->Service == CACHESERVICE) {
+        TRACE2(("cache command %d\n",cmdp->OpCode));
+        ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.cache.sg_lst) + sizeof(gdth_sg_str);
+    } else if (cmdp->Service == SCSIRAWSERVICE) {
+        TRACE2(("raw command %d/%d\n",cmdp->OpCode,cmdp->u.raw.cmd[0]));
+        ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.raw.sg_lst) + sizeof(gdth_sg_str);
+    }
+
+    if (ha->cmd_len & 3)
+        ha->cmd_len += (4 - (ha->cmd_len & 3));
+
+    if (ha->cmd_cnt > 0) {
+        if ((ha->cmd_offs_dpmem + ha->cmd_len + DPMEM_COMMAND_OFFSET) >
+            ha->ic_all_size) {
+            TRACE2(("gdth_special_cmd() DPMEM overflow\n"));
+            gdth_cmd_tab[cmd_index-2][hanum].cmnd = UNUSED_CMND;
+            return 0;
+        }
+    }
+
+    /* copy command */
+    gdth_copy_command(hanum);
+    return cmd_index;
+}    
+
+
+/* Controller event handling functions */
+static gdth_evt_str *gdth_store_event(ushort source, ushort idx,
+                                      gdth_evt_data *evt)
+{
+    gdth_evt_str *e;
+    ulong flags;
+    struct timeval tv;
+
+    TRACE2(("gdth_store_event() source %d idx %d\n", source, idx));
+    if (source == 0)                        /* no source -> no event */
+        return 0;
+
+    save_flags(flags);
+    cli();
+    if (ebuffer[elastidx].event_source == source &&
+        ebuffer[elastidx].event_idx == idx &&
+        !memcmp((char *)&ebuffer[elastidx].event_data.eu,
+            (char *)&evt->eu, evt->size)) {
+        e = &ebuffer[elastidx];
+        do_gettimeofday(&tv);
+        e->last_stamp = tv.tv_sec;
+        ++e->same_count;
+    } else {
+        if (ebuffer[elastidx].event_source != 0) {  /* entry not free ? */
+            ++elastidx;
+            if (elastidx == MAX_EVENTS)
+                elastidx = 0;
+            if (elastidx == eoldidx) {              /* reached mark ? */
+                ++eoldidx;
+                if (eoldidx == MAX_EVENTS)
+                    eoldidx = 0;
+            }
+        }
+        e = &ebuffer[elastidx];
+        e->event_source = source;
+        e->event_idx = idx;
+        do_gettimeofday(&tv);
+        e->first_stamp = e->last_stamp = tv.tv_sec;
+        e->same_count = 1;
+        e->event_data = *evt;
+    }
+    restore_flags(flags);
+    return e;
+}
+
+static int gdth_read_event(int handle, gdth_evt_str *estr)
+{
+    gdth_evt_str *e;
+    int eindex;
+    ulong flags;
+
+    TRACE2(("gdth_read_event() handle %d\n", handle));
+    save_flags(flags);
+    cli();
+    if (handle == -1)
+        eindex = eoldidx;
+    else
+        eindex = handle;
+    estr->event_source = 0;
+
+    if (eindex >= MAX_EVENTS) {
+        restore_flags(flags);
+        return eindex;
+    }
+    e = &ebuffer[eindex];
+    if (e->event_source != 0) {
+        if (eindex != elastidx) {
+            if (++eindex == MAX_EVENTS)
+                eindex = 0;
+        } else {
+            eindex = -1;
+        }
+        memcpy(estr, e, sizeof(gdth_evt_str));
+    }
+    restore_flags(flags);
+    return eindex;
+}
+
+static void gdth_readapp_event(unchar application, gdth_evt_str *estr)
+{
+    gdth_evt_str *e;
+    int eindex;
+    ulong flags;
+    unchar found = FALSE;
+
+    TRACE2(("gdth_readapp_event() app. %d\n", application));
+    save_flags(flags);
+    cli();
+    eindex = eoldidx;
+    for (;;) {
+        e = &ebuffer[eindex];
+        if (e->event_source == 0)
+            break;
+        if ((e->application & application) == 0) {
+            e->application |= application;
+            found = TRUE;
+            break;
+        }
+        if (eindex == elastidx)
+            break;
+        if (++eindex == MAX_EVENTS)
+            eindex = 0;
+    }
+    if (found)
+        memcpy(estr, e, sizeof(gdth_evt_str));
+    else
+        estr->event_source = 0;
+    restore_flags(flags);
+}
+
+static void gdth_clear_events()
+{
+    ulong flags;
+
+    TRACE(("gdth_clear_events()"));
+    save_flags(flags);
+    cli();
+
+    eoldidx = elastidx = 0;
+    ebuffer[0].event_source = 0;
+    restore_flags(flags);
+}
+
+
+/* SCSI interface functions */
+
+#if LINUX_VERSION_CODE >= 0x010346
+static void gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs)
+#else
+static void gdth_interrupt(int irq,struct pt_regs *regs)
+#endif
+{
+    register gdth_ha_str *ha;
+    gdt6m_dpram_str *dp6m_ptr;
+    gdt6_dpram_str *dp6_ptr;
+    gdt2_dpram_str *dp2_ptr;
+    Scsi_Cmnd *scp;
+    int hanum;
+    unchar IStatus;
+    ushort CmdStatus, Service = 0;
+    ulong InfoBytes, InfoBytes2 = 0;
+    gdth_evt_data dvr;
+
+    TRACE(("gdth_interrupt() IRQ %d\n",irq));
+
+    /* if polling and not from gdth_wait() -> return */
+    if (gdth_polling) {
+        if (!gdth_from_wait) {
+            return;
+        }
+    }
+
+    wait_index = 0;
+
+    /* search controller */
+    if ((hanum = gdth_get_status(&IStatus,irq)) == -1) {
+        /*
+        TRACE2(("gdth_interrupt(): Spurious interrupt received\n"));
+        */
+        return;
+    }
+
+#ifdef GDTH_STATISTICS
+    ++act_ints;
+#endif
+    
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    if (ha->type == GDT_EISA) {
+        if (IStatus & 0x80) {                   /* error flag */
+            IStatus &= ~0x80;
+            CmdStatus = inw((ushort)ha->brd+MAILBOXREG+8);
+            TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+            if (IStatus == ASYNCINDEX) {        /* async. event ? */
+                Service = inw((ushort)ha->brd+MAILBOXREG+10);
+                InfoBytes2 = inl((ushort)ha->brd+MAILBOXREG+4);
+            }
+        } else                                  /* no error */
+            CmdStatus = S_OK;
+        InfoBytes = inl((ushort)ha->brd+MAILBOXREG+12);
+        outb(0xff,(ushort)ha->brd+EDOORREG);    /* acknowledge interrupt */
+        outb(0x00,(ushort)ha->brd+SEMA1REG);    /* reset status semaphore */
+    } else if (ha->type == GDT_ISA) {
+        dp2_ptr = (gdt2_dpram_str *)ha->brd;
+        if (IStatus & 0x80) {                   /* error flag */
+            IStatus &= ~0x80;
+            CmdStatus = dp2_ptr->u.ic.Status;
+            TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+            if (IStatus == ASYNCINDEX) {        /* async. event ? */
+                Service = dp2_ptr->u.ic.Service;
+                InfoBytes2 = dp2_ptr->u.ic.Info[1];
+            }
+        } else                                  /* no error */
+            CmdStatus = S_OK;
+        InfoBytes = dp2_ptr->u.ic.Info[0];
+        dp2_ptr->io.irqdel = 0xff;              /* acknowledge interrupt */
+        dp2_ptr->u.ic.Cmd_Index = 0;            /* reset command index */
+        dp2_ptr->io.Sema1  = 0;                 /* reset status semaphore */
+    } else if (ha->type == GDT_PCI) {
+        dp6_ptr = (gdt6_dpram_str *)ha->brd;
+        if (IStatus & 0x80) {                   /* error flag */
+            IStatus &= ~0x80;
+            CmdStatus = dp6_ptr->u.ic.Status;
+            TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+            if (IStatus == ASYNCINDEX) {        /* async. event ? */
+                Service = dp6_ptr->u.ic.Service;
+                InfoBytes2 = dp6_ptr->u.ic.Info[1];
+            }
+        } else                                  /* no error */
+            CmdStatus = S_OK;
+        InfoBytes = dp6_ptr->u.ic.Info[0];
+        dp6_ptr->io.irqdel = 0xff;              /* acknowledge interrupt */
+        dp6_ptr->u.ic.Cmd_Index = 0;            /* reset command index */
+        dp6_ptr->io.Sema1  = 0;                 /* reset status semaphore */
+    } else if (ha->type == GDT_PCINEW) {
+        if (IStatus & 0x80) {                   /* error flag */
+            IStatus &= ~0x80;
+            CmdStatus = inw(PTR2USHORT(&ha->plx->status));
+            TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+            if (IStatus == ASYNCINDEX) {        /* async. event ? */
+                Service = inw(PTR2USHORT(&ha->plx->service));
+                InfoBytes2 = inl(PTR2USHORT(&ha->plx->info[1]));
+            }
+        } else
+            CmdStatus = S_OK;
+
+        InfoBytes = inl(PTR2USHORT(&ha->plx->info[0]));
+        outb(0xff,PTR2USHORT(&ha->plx->edoor_reg)); 
+        outb(0x00,PTR2USHORT(&ha->plx->sema1_reg)); 
+    } else if (ha->type == GDT_PCIMPR) {
+        dp6m_ptr = (gdt6m_dpram_str *)ha->brd;
+        if (IStatus & 0x80) {                   /* error flag */
+            IStatus &= ~0x80;
+            CmdStatus = dp6m_ptr->i960r.status;
+            TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+            if (IStatus == ASYNCINDEX) {        /* async. event ? */
+                Service = dp6m_ptr->i960r.service;
+                InfoBytes2 = dp6m_ptr->i960r.info[1];
+            }
+        } else                                  /* no error */
+            CmdStatus = S_OK;
+        InfoBytes = dp6m_ptr->i960r.info[0];
+        dp6m_ptr->i960r.edoor_reg = 0xff;
+        dp6m_ptr->i960r.sema1_reg = 0;
+    } else {
+        TRACE2(("gdth_interrupt() unknown controller type\n"));
+        return;
+    }
+
+    TRACE(("gdth_interrupt() index %d stat %d info %ld\n",
+                 IStatus,CmdStatus,InfoBytes));
+    ha->status = CmdStatus;
+    ha->info   = InfoBytes;
+    ha->info2  = InfoBytes2;
+
+    if (gdth_from_wait) {
+        wait_hanum = hanum;
+        wait_index = (int)IStatus;
+    }
+
+    if (IStatus == ASYNCINDEX) {
+        TRACE2(("gdth_interrupt() async. event\n"));
+        gdth_async_event(hanum,Service);
+    } else {
+        if (IStatus == SPEZINDEX) {
+            TRACE2(("Service unknown or not initialized !\n"));
+            dvr.size = sizeof(dvr.eu.driver);
+            dvr.eu.driver.ionode = hanum;
+            gdth_store_event(ES_DRIVER, 4, &dvr);
+            return;
+        }
+        scp     = gdth_cmd_tab[IStatus-2][hanum].cmnd;
+        Service = gdth_cmd_tab[IStatus-2][hanum].service;
+        gdth_cmd_tab[IStatus-2][hanum].cmnd = UNUSED_CMND;
+        if (scp == UNUSED_CMND) {
+            TRACE2(("gdth_interrupt() index to unused command (%d)\n",IStatus));
+            dvr.size = sizeof(dvr.eu.driver);
+            dvr.eu.driver.ionode = hanum;
+            dvr.eu.driver.index = IStatus;
+            gdth_store_event(ES_DRIVER, 1, &dvr);
+            return;
+        }
+        if (scp == INTERNAL_CMND) {
+            TRACE(("gdth_interrupt() answer to internal command\n"));
+            return;
+        }
+        TRACE(("gdth_interrupt() sync. status\n"));
+        gdth_sync_event(hanum,Service,IStatus,scp);
+    }
+    gdth_next(hanum);
+}
+
+static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp)
+{
+    register gdth_ha_str *ha;
+    gdth_msg_str *msg;
+    gdth_cmd_str *cmdp;
+    char c='\r';
+    ushort i;
+    gdth_evt_data dvr;
+
+    ha   = HADATA(gdth_ctr_tab[hanum]);
+    cmdp = ha->pccb;
+    TRACE(("gdth_sync_event() scp %lx serv %d status %d\n",
+                 (ulong)scp,service,ha->status));
+
+    if (service == SCREENSERVICE) {
+        msg  = (gdth_msg_str *)ha->pscratch;
+        TRACE(("len: %ld, answer: %d, ext: %d, alen: %ld\n",
+                     msg->msg_len,msg->msg_answer,msg->msg_ext,msg->msg_alen));
+        if (msg->msg_len)
+            if (!(msg->msg_answer && msg->msg_ext)) {
+                msg->msg_text[msg->msg_len] = '\0';
+                printk("%s",msg->msg_text);
+            }
+
+        if (msg->msg_ext && !msg->msg_answer) {
+            while (gdth_test_busy(hanum))
+                udelay(1);
+            cmdp->Service       = SCREENSERVICE;
+            cmdp->RequestBuffer = SCREEN_CMND;
+            gdth_get_cmd_index(hanum);
+            gdth_set_sema0(hanum);
+            cmdp->OpCode        = GDT_READ;
+            cmdp->BoardNode     = LOCALBOARD;
+            cmdp->u.screen.reserved  = 0;
+            cmdp->u.screen.msg_handle= msg->msg_handle;
+            cmdp->u.screen.msg_addr  = (ulong)msg;
+            ha->cmd_offs_dpmem = 0;
+            ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr) 
+                + sizeof(ulong);
+            ha->cmd_cnt = 0;
+            gdth_copy_command(hanum);
+            gdth_release_event(hanum);
+            return 1;
+        }
+
+        if (msg->msg_answer && msg->msg_alen) {
+            for (i=0; i<msg->msg_alen && i<MSGLEN; ++i) {
+                /* getchar() ?? */           
+                /* .. */
+                if (c == '\r')
+                    break;
+                msg->msg_text[i] = c; 
+            }
+            msg->msg_alen -= i;
+            if (c!='\r' && msg->msg_alen!=0) {
+                msg->msg_answer = 1;
+                msg->msg_ext    = 1;
+            } else {
+                msg->msg_ext    = 0;
+                msg->msg_answer = 0;
+            }
+            msg->msg_len = i;
+            while (gdth_test_busy(hanum))
+                udelay(1);
+            cmdp->Service       = SCREENSERVICE;
+            cmdp->RequestBuffer = SCREEN_CMND;
+            gdth_get_cmd_index(hanum);
+            gdth_set_sema0(hanum);
+            cmdp->OpCode        = GDT_WRITE;
+            cmdp->BoardNode     = LOCALBOARD;
+            cmdp->u.screen.reserved  = 0;
+            cmdp->u.screen.msg_handle= msg->msg_handle;
+            cmdp->u.screen.msg_addr  = (ulong)msg;
+            ha->cmd_offs_dpmem = 0;
+            ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr) 
+                + sizeof(ulong);
+            ha->cmd_cnt = 0;
+            gdth_copy_command(hanum);
+            gdth_release_event(hanum);
+            return 1;
+        }
+        printk("\n");
+
+    } else {
+        scp->SCp.Message = (int)ha->status;
+        /* cache or raw service */
+        if (ha->status == S_OK) {
+            scp->result = DID_OK << 16;
+        } else if (ha->status == S_BSY) {
+            TRACE2(("Controller busy -> retry !\n"));
+            gdth_putq(hanum,scp,DEFAULT_PRI);
+            return 1;
+        } else {
+            if (service == CACHESERVICE) {
+                memset((char*)scp->sense_buffer,0,16);
+                scp->sense_buffer[0] = 0x70;
+                scp->sense_buffer[2] = NOT_READY;
+                scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+
+                if (scp->done != gdth_scsi_done) {
+                    dvr.size = sizeof(dvr.eu.sync);
+                    dvr.eu.sync.ionode  = hanum;
+                    dvr.eu.sync.service = service;
+                    dvr.eu.sync.status  = ha->status;
+                    dvr.eu.sync.info    = ha->info;
+                    dvr.eu.sync.hostdrive =
+#if LINUX_VERSION_CODE >= 0x010400
+                        ha->id[scp->channel][scp->target].hostdrive;
+#else
+                        ha->id[NUMDATA(scp->host)->busnum][scp->target].hostdrive;
+#endif
+                    if (ha->status >= 0x8000)
+                        gdth_store_event(ES_SYNC, 0, &dvr);
+                    else
+                        gdth_store_event(ES_SYNC, service, &dvr);
+                }
+            } else {
+                if (ha->status!=S_RAW_SCSI || ha->status==S_RAW_ILL) {
+                    scp->result = DID_BAD_TARGET << 16;
+                } else {
+                    scp->result = (DID_OK << 16) | ha->info;
+                }
+            }
+        }
+        scp->SCp.have_data_in++;
+        scp->scsi_done(scp);
+    }
+
+    return 1;
+}
+
+static char *async_cache_tab[] = {
+/* 0*/  "\011\000\002\002\002\004\002\006\004"
+        "GDT HA %u, service %u, async. status %u/%lu unknown",
+/* 1*/  "\011\000\002\002\002\004\002\006\004"
+        "GDT HA %u, service %u, async. status %u/%lu unknown",
+/* 2*/  "\005\000\002\006\004"
+        "GDT HA %u, Host Drive %lu not ready",
+/* 3*/  "\005\000\002\006\004"
+        "GDT HA %u, Host Drive %lu: REASSIGN not successful and/or data error on reassigned blocks. Drive may crash in the future and should be replaced",
+/* 4*/  "\005\000\002\006\004"
+        "GDT HA %u, mirror update on Host Drive %lu failed",
+/* 5*/  "\005\000\002\006\004"
+        "GDT HA %u, Mirror Drive %lu failed",
+/* 6*/  "\005\000\002\006\004"
+        "GDT HA %u, Mirror Drive %lu: REASSIGN not successful and/or data error on reassigned blocks. Drive may crash in the future and should be replaced",
+/* 7*/  "\005\000\002\006\004"
+        "GDT HA %u, Host Drive %lu write protected",
+/* 8*/  "\005\000\002\006\004"
+        "GDT HA %u, media changed in Host Drive %lu",
+/* 9*/  "\005\000\002\006\004"
+        "GDT HA %u, Host Drive %lu is offline",
+/*10*/  "\005\000\002\006\004"
+        "GDT HA %u, media change of Mirror Drive %lu",
+/*11*/  "\005\000\002\006\004"
+        "GDT HA %u, Mirror Drive %lu is write protected",
+/*12*/  "\005\000\002\006\004"
+        "GDT HA %u, general error on Host Drive %lu. Please check the devices of this drive!",
+/*13*/  "\007\000\002\006\002\010\002"
+        "GDT HA %u, Array Drive %u: Cache Drive %u failed",
+/*14*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: FAIL state entered",
+/*15*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: error",
+/*16*/  "\007\000\002\006\002\010\002"
+        "GDT HA %u, Array Drive %u: failed drive replaced by Cache Drive %u",
+/*17*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: parity build failed",
+/*18*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: drive rebuild failed",
+/*19*/  "\007\000\002\010\002"
+        "GDT HA %u, Test of Hot Fix %u failed",
+/*20*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: drive build finished successfully",
+/*21*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: drive rebuild finished successfully",
+/*22*/  "\007\000\002\006\002\010\002"
+        "GDT HA %u, Array Drive %u: Hot Fix %u activated",
+/*23*/  "\005\000\002\006\002"
+        "GDT HA %u, Host Drive %u: processing of i/o aborted due to serious drive error",
+/*24*/  "\005\000\002\010\002"
+        "GDT HA %u, mirror update on Cache Drive %u completed",
+/*25*/  "\005\000\002\010\002"
+        "GDT HA %u, mirror update on Cache Drive %lu failed",
+/*26*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: drive rebuild started",
+/*27*/  "\005\000\002\012\001"
+        "GDT HA %u, Fault bus %u: SHELF OK detected",
+/*28*/  "\005\000\002\012\001"
+        "GDT HA %u, Fault bus %u: SHELF not OK detected",
+/*29*/  "\007\000\002\012\001\013\001"
+        "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug started",
+/*30*/  "\007\000\002\012\001\013\001"
+        "GDT HA %u, Fault bus %u, ID %u: new disk detected",
+/*31*/  "\007\000\002\012\001\013\001"
+        "GDT HA %u, Fault bus %u, ID %u: old disk detected",
+/*32*/  "\007\000\002\012\001\013\001"
+        "GDT HA %u, Fault bus %u, ID %u: plugging an active disk is illegal",
+/*33*/  "\007\000\002\012\001\013\001"
+        "GDT HA %u, Fault bus %u, ID %u: illegal device detected",
+/*34*/  "\011\000\002\012\001\013\001\006\004"
+        "GDT HA %u, Fault bus %u, ID %u: insufficient disk capacity (%lu MB required)",
+/*35*/  "\007\000\002\012\001\013\001"
+        "GDT HA %u, Fault bus %u, ID %u: disk write protected",
+/*36*/  "\007\000\002\012\001\013\001"
+        "GDT HA %u, Fault bus %u, ID %u: disk not available",
+/*37*/  "\007\000\002\012\001\006\004"
+        "GDT HA %u, Fault bus %u: swap detected (%lu)",
+/*38*/  "\007\000\002\012\001\013\001"
+        "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug finished successfully",
+/*39*/  "\007\000\002\012\001\013\001"
+        "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug aborted due to user Hot Plug",
+/*40*/  "\007\000\002\012\001\013\001"
+        "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug aborted",
+/*41*/  "\007\000\002\012\001\013\001"
+        "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug for Hot Fix started",
+/*42*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: drive build started",
+/*43*/  "\003\000\002"
+        "GDT HA %u, DRAM parity error detected",
+/*44*/  "\005\000\002\006\002"
+        "GDT HA %u, Mirror Drive %u: update started",
+/*45*/  "\007\000\002\006\002\010\002"
+        "GDT HA %u, Mirror Drive %u: Hot Fix %u activated",
+/*46*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: no matching Pool Hot Fix Drive available",
+/*47*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: Pool Hot Fix Drive available",
+/*48*/  "\005\000\002\006\002"
+        "GDT HA %u, Mirror Drive %u: no matching Pool Hot Fix Drive available",
+/*49*/  "\005\000\002\006\002"
+        "GDT HA %u, Mirror Drive %u: Pool Hot Fix Drive available",
+/*50*/  "\007\000\002\012\001\013\001"
+        "GDT HA %u, SCSI bus %u, ID %u: IGNORE_WIDE_RESIDUE message received",
+/*51*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: expand started",
+/*52*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: expand finished successfully",
+/*53*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: expand failed",
+/*54*/  "\003\000\002"
+        "GDT HA %u, CPU temperature critical",
+/*55*/  "\003\000\002"
+        "GDT HA %u, CPU temperature OK",
+};
+
+
+static int gdth_async_event(int hanum,int service)
+{
+    gdth_stackframe stack;
+    gdth_evt_data dvr;
+    char *f = NULL;
+    int i,j;
+    gdth_ha_str *ha;
+    gdth_msg_str *msg;
+    gdth_cmd_str *cmdp;
+    int cmd_index;
+
+    ha  = HADATA(gdth_ctr_tab[hanum]);
+    cmdp= ha->pccb;
+    msg = (gdth_msg_str *)ha->pscratch;
+    TRACE2(("gdth_async_event() ha %d serv %d\n",
+                 hanum,service));
+
+    if (service == SCREENSERVICE) {
+        if (ha->status == MSG_REQUEST) {
+            while (gdth_test_busy(hanum))
+                udelay(1);
+            cmdp->Service       = SCREENSERVICE;
+            cmdp->RequestBuffer = SCREEN_CMND;
+            cmd_index = gdth_get_cmd_index(hanum);
+            gdth_set_sema0(hanum);
+            cmdp->OpCode        = GDT_READ;
+            cmdp->BoardNode     = LOCALBOARD;
+            cmdp->u.screen.reserved  = 0;
+            cmdp->u.screen.msg_handle= MSG_INV_HANDLE;
+            cmdp->u.screen.msg_addr  = (ulong)msg;
+            ha->cmd_offs_dpmem = 0;
+            ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr) 
+                + sizeof(ulong);
+            ha->cmd_cnt = 0;
+            gdth_copy_command(hanum);
+            if (ha->type == GDT_EISA)
+                printk("[EISA slot %d] ",(ushort)ha->brd_phys);
+            else if (ha->type == GDT_ISA)
+                printk("[DPMEM 0x%4X] ",(ushort)ha->brd_phys);
+            else 
+                printk("[PCI %d/%d] ",(ushort)(ha->brd_phys>>8),
+                       (ushort)((ha->brd_phys>>3)&0x1f));
+            gdth_release_event(hanum);
+        }
+
+    } else {
+        dvr.size = sizeof(dvr.eu.async);
+        dvr.eu.async.ionode   = hanum;
+        dvr.eu.async.service = service;
+        dvr.eu.async.status  = ha->status;
+        dvr.eu.async.info    = ha->info;
+        *(ulong *)dvr.eu.async.scsi_coord  = ha->info2;
+        gdth_store_event(ES_ASYNC, service, &dvr);
+
+        if (service==CACHESERVICE && INDEX_OK(ha->status,async_cache_tab)) {
+            TRACE2(("GDT: Async. event cache service, event no.: %d\n",
+                ha->status));
+        
+            f = async_cache_tab[ha->status];
+
+            /* i: parameter to push, j: stack element to fill */
+            for (j=0,i=1; i < f[0]; i+=2) {
+                switch (f[i+1]) {
+                  case 4:
+                    stack.b[j++] = *(ulong*)&dvr.eu.stream[(int)f[i]];
+                    break;
+                  case 2:
+                    stack.b[j++] = *(ushort*)&dvr.eu.stream[(int)f[i]];
+                    break;
+                  case 1:
+                    stack.b[j++] = *(unchar*)&dvr.eu.stream[(int)f[i]];
+                    break;
+                  default:
+                    break;
+                }
+            }
+
+            printk(&f[f[0]],stack); printk("\n");
+
+        } else {
+            printk("GDT: Unknown async. event service %d event no. %d\n",
+                service,ha->status);
+        }
+    }
+    return 1;
+}
+
+#ifdef GDTH_STATISTICS
+void gdth_timeout(void)
+{
+    ulong flags,i;
+    Scsi_Cmnd *nscp;
+    gdth_ha_str *ha;
+    int hanum = 0;
+
+    save_flags(flags);
+    cli();
+
+    for (act_stats=0,i=0; i<GDTH_MAXCMDS; ++i) 
+        if (gdth_cmd_tab[i][hanum].cmnd != UNUSED_CMND)
+            ++act_stats;
+
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    for (act_rq=0,nscp=ha->req_first; nscp; nscp=(Scsi_Cmnd*)nscp->SCp.ptr)
+        ++act_rq;
+
+    TRACE2(("gdth_to(): ints %ld, ios %ld, act_stats %ld, act_rq %ld\n",
+            act_ints, act_ios, act_stats, act_rq));
+    act_ints = act_ios = 0;
+
+    timer_table[GDTH_TIMER].expires = jiffies + 30*HZ;
+    timer_active |= 1<<GDTH_TIMER;
+    sti();
+}
+#endif
+
+int gdth_detect(Scsi_Host_Template *shtp)
+{
+    struct Scsi_Host *shp;
+    gdth_ha_str *ha;
+    unsigned long flags;
+    ulong isa_bios;
+    ushort eisa_slot,device_id,index;
+    gdth_pci_str pcistr;
+    int i,j,hanum;
+    unchar b;
+    
+ 
+#ifdef DEBUG_GDTH
+    printk("GDT: This driver contains debugging information !! Trace level = %d\n",
+        DebugState);
+    printk("     Destination of debugging information: ");
+#ifdef __SERIAL__
+#ifdef __COM2__
+    printk("Serial port COM2\n");
+#else
+    printk("Serial port COM1\n");
+#endif
+#else
+    printk("Console\n");
+#endif
+    WAITSEC(3);
+#endif
+
+    TRACE(("gdth_detect()\n"));
+
+    if (disable_gdth_scan) {
+        printk("GDT: Controller driver disabled from command line !\n");
+        return 0;
+    }
+
+    /* initializations */
+    gdth_polling = TRUE; b = 0;
+    for (i=0; i<GDTH_MAXCMDS; ++i)
+        for (j=0; j<MAXHA; ++j)
+            gdth_cmd_tab[i][j].cmnd = UNUSED_CMND;
+    for (i=0; i<4; ++i)
+        for (j=0; j<MAXHA; ++j)
+            gdth_ioctl_tab[i][j] = NULL;
+    gdth_clear_events();
+
+    /* scanning for controllers, at first: ISA controller */
+    for (isa_bios=0xc8000UL; isa_bios<=0xd8000UL; isa_bios+=0x8000UL) {
+        if (gdth_search_isa(isa_bios)) {        /* controller found */
+            shp = scsi_register(shtp,sizeof(gdth_ext_str));
+            ha = HADATA(shp);
+            if (!gdth_init_isa(isa_bios,ha,TRUE)) {
+                scsi_unregister(shp);
+                continue;
+            }
+            /* controller found and initialized */
+            printk("Configuring GDT-ISA HA at BIOS 0x%05lX IRQ %u DRQ %u\n",
+                   isa_bios,ha->irq,ha->drq);
+
+            save_flags(flags);
+            cli();
+#if LINUX_VERSION_CODE >= 0x010346 
+            if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",NULL))
+#else
+            if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth")) 
+#endif
+            {
+                printk("GDT-ISA: Unable to allocate IRQ\n");
+                restore_flags(flags);
+                scsi_unregister(shp);
+                continue;
+            }
+            if (request_dma(ha->drq,"gdth")) {
+                printk("GDT-ISA: Unable to allocate DMA channel\n");
+#if LINUX_VERSION_CODE >= 0x010346 
+                free_irq(ha->irq,NULL);
+#else
+                free_irq(ha->irq);
+#endif
+                restore_flags(flags);
+                scsi_unregister(shp);
+                continue;
+            }
+            set_dma_mode(ha->drq,DMA_MODE_CASCADE);
+            enable_dma(ha->drq);
+            shp->unchecked_isa_dma = 1;
+            shp->irq = ha->irq;
+            shp->dma_channel = ha->drq;
+            for (i=0; i<MAXID; ++i) {
+                if (ha->id[0][i].type==SIOP_DTYP) {
+                    shp->this_id = i;
+                    break;
+                }
+            }
+            hanum = gdth_ctr_count;         
+            gdth_ctr_tab[gdth_ctr_count++] = shp;
+            gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+
+            NUMDATA(shp)->hanum = (ushort)hanum;
+            NUMDATA(shp)->busnum= 0;
+
+            ha->pccb = CMDDATA(shp);
+            ha->pscratch = DMADATA(shp);
+            ha->req_first = NULL;
+            for (i=0; i<MAXBUS; ++i) {
+                for (j=0; j<MAXID; ++j) {
+                    ha->id[i][j].type = EMPTY_DTYP;
+                    ha->id[i][j].lock = 0;
+                }
+            }
+            restore_flags(flags);
+
+            if (!gdth_search_drives(hanum,TRUE)) {
+                printk("GDT-ISA: Error during device scan\n");
+                --gdth_ctr_count;
+                save_flags(flags);
+                cli();
+#if LINUX_VERSION_CODE >= 0x010346 
+                free_irq(ha->irq,NULL);
+#else
+                free_irq(ha->irq);
+#endif
+                restore_flags(flags);
+                scsi_unregister(shp);
+                continue;
+            }
+
+#if LINUX_VERSION_CODE >= 0x010400
+            shp->max_id      = 8;
+            shp->max_lun     = 8;
+            shp->max_channel = ha->bus_cnt - 1;
+#else
+            /* register addit. SCSI channels as virtual controllers */
+            for (b=1; b<ha->bus_cnt; ++b) {
+                shp = scsi_register(shtp,sizeof(gdth_num_str));
+                shp->unchecked_isa_dma = 1;
+                shp->irq = ha->irq;
+                shp->dma_channel = ha->drq;
+                for (i=0; i<MAXID; ++i) {
+                    if (ha->id[b][i].type==SIOP_DTYP) {
+                        shp->this_id = i;
+                        break;
+                    }
+                }
+                gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+                NUMDATA(shp)->hanum = (ushort)hanum;
+                NUMDATA(shp)->busnum = b;
+            }
+#endif
+
+            gdth_enable_int(hanum);
+        }
+    }
+
+    /* scanning for EISA controllers */
+    for (eisa_slot=0x1000; eisa_slot<=0x8000; eisa_slot+=0x1000) {
+        if (gdth_search_eisa(eisa_slot)) {      /* controller found */
+            shp = scsi_register(shtp,sizeof(gdth_ext_str));
+            ha = HADATA(shp);
+            if (!gdth_init_eisa(eisa_slot,ha,TRUE)) {
+                scsi_unregister(shp);
+                continue;
+            }
+            /* controller found and initialized */
+            printk("Configuring GDT-EISA HA at Slot %d IRQ %u\n",
+                   eisa_slot>>12,ha->irq);
+
+            save_flags(flags);
+            cli();
+#if LINUX_VERSION_CODE >= 0x010346 
+            if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",NULL))
+#else
+            if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth")) 
+#endif
+            {
+                printk("GDT-EISA: Unable to allocate IRQ\n");
+                restore_flags(flags);
+                scsi_unregister(shp);
+                continue;
+            }
+            shp->unchecked_isa_dma = 0;
+            shp->irq = ha->irq;
+            shp->dma_channel = 0xff;
+            for (i=0; i<MAXID; ++i) {
+                if (ha->id[0][i].type==SIOP_DTYP) {
+                    shp->this_id = i;
+                    break;
+                }
+            }
+            hanum = gdth_ctr_count;
+            gdth_ctr_tab[gdth_ctr_count++] = shp;
+            gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+
+            NUMDATA(shp)->hanum = (ushort)hanum;
+            NUMDATA(shp)->busnum= 0;
+            TRACE2(("EISA detect Bus 0: shp %lx hanum %d\n",
+                          (ulong)shp,NUMDATA(shp)->hanum));
+
+            ha->pccb = CMDDATA(shp);
+            ha->pscratch = DMADATA(shp);
+            ha->req_first = NULL;
+            for (i=0; i<MAXBUS; ++i) {
+                for (j=0; j<MAXID; ++j) {
+                    ha->id[i][j].type = EMPTY_DTYP;
+                    ha->id[i][j].lock = 0;
+                }
+            }
+            restore_flags(flags);
+
+            if (!gdth_search_drives(hanum,TRUE)) {
+                printk("GDT-EISA: Error during device scan\n");
+                --gdth_ctr_count;
+                save_flags(flags);
+                cli();
+#if LINUX_VERSION_CODE >= 0x010346 
+                free_irq(ha->irq,NULL);
+#else
+                free_irq(ha->irq);
+#endif
+                restore_flags(flags);
+                scsi_unregister(shp);
+                continue;
+            }
+
+#if LINUX_VERSION_CODE >= 0x010400
+            shp->max_id      = 8;
+            shp->max_lun     = 8;
+            shp->max_channel = ha->bus_cnt - 1;
+#else
+            /* register addit. SCSI channels as virtual controllers */
+            for (b=1; b<ha->bus_cnt; ++b) {
+                shp = scsi_register(shtp,sizeof(gdth_num_str));
+                shp->unchecked_isa_dma = 0;
+                shp->irq = ha->irq;
+                shp->dma_channel = 0xff;
+                for (i=0; i<MAXID; ++i) {
+                    if (ha->id[b][i].type==SIOP_DTYP) {
+                        shp->this_id = i;
+                        break;
+                    }
+                }
+                gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+                NUMDATA(shp)->hanum = (ushort)hanum;
+                NUMDATA(shp)->busnum = b;
+                TRACE2(("EISA detect Bus %d: shp %lx hanum %d\n",
+                              NUMDATA(shp)->busnum,(ulong)shp,
+                              NUMDATA(shp)->hanum));
+            }
+#endif
+
+            gdth_enable_int(hanum);
+        }
+    }
+
+    /* scanning for PCI controllers */
+    for (device_id = 0; device_id <= PCI_DEVICE_ID_VORTEX_GDT6x21RP2; ++device_id) {
+        if (device_id > PCI_DEVICE_ID_VORTEX_GDT6555 &&
+            device_id < PCI_DEVICE_ID_VORTEX_GDT6x17RP)
+            continue;
+        for (index = 0; ; ++index) {
+            if (!gdth_search_pci(device_id,index,&pcistr)) 
+                break;                          /* next device_id */
+            shp = scsi_register(shtp,sizeof(gdth_ext_str));
+            ha = HADATA(shp);
+            if (!gdth_init_pci(&pcistr,ha,TRUE)) {
+                scsi_unregister(shp);
+                continue;
+            }
+            /* controller found and initialized */
+            printk("Configuring GDT-PCI HA at %d/%d IRQ %u\n",
+                   pcistr.bus,pcistr.device_fn>>3,ha->irq);
+
+            save_flags(flags);
+            cli();
+#if LINUX_VERSION_CODE >= 0x010346 
+            if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",NULL))
+#else
+            if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth")) 
+#endif
+            {
+                printk("GDT-PCI: Unable to allocate IRQ\n");
+                restore_flags(flags);
+                scsi_unregister(shp);
+                continue;
+            }
+            shp->unchecked_isa_dma = 0;
+            shp->irq = ha->irq;
+            shp->dma_channel = 0xff;
+            for (i=0; i<MAXID; ++i) {
+                if (ha->id[0][i].type==SIOP_DTYP) {
+                    shp->this_id = i;
+                    break;
+                }
+            }
+            hanum = gdth_ctr_count;
+            gdth_ctr_tab[gdth_ctr_count++] = shp;
+            gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+
+            NUMDATA(shp)->hanum = (ushort)hanum;
+            NUMDATA(shp)->busnum= 0;
+
+            ha->pccb = CMDDATA(shp);
+            ha->pscratch = DMADATA(shp);
+            ha->req_first = NULL;
+            for (i=0; i<MAXBUS; ++i) {
+                for (j=0; j<MAXID; ++j) {
+                    ha->id[i][j].type = EMPTY_DTYP;
+                    ha->id[i][j].lock = 0;
+                }
+            }
+            restore_flags(flags);
+
+            if (!gdth_search_drives(hanum,TRUE)) {
+                printk("GDT-PCI: Error during device scan\n");
+                --gdth_ctr_count;
+                save_flags(flags);
+                cli();
+#if LINUX_VERSION_CODE >= 0x010346 
+                free_irq(ha->irq,NULL);
+#else
+                free_irq(ha->irq);
+#endif
+                restore_flags(flags);
+                scsi_unregister(shp);
+                continue;
+            }
+
+#if LINUX_VERSION_CODE >= 0x010400
+            shp->max_id      = 8;
+            shp->max_lun     = 8;
+            shp->max_channel = ha->bus_cnt - 1;
+#else
+            /* register addit. SCSI channels as virtual controllers */
+            for (b=1; b<ha->bus_cnt; ++b) {
+                shp = scsi_register(shtp,sizeof(gdth_num_str));
+                shp->unchecked_isa_dma = 0;
+                shp->irq = ha->irq;
+                shp->dma_channel = 0xff;
+                for (i=0; i<MAXID; ++i) {
+                    if (ha->id[b][i].type==SIOP_DTYP) {
+                        shp->this_id = i;
+                        break;
+                    }
+                }
+                gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+                NUMDATA(shp)->hanum = (ushort)hanum;
+                NUMDATA(shp)->busnum = b;
+            }
+#endif
+
+            gdth_enable_int(hanum);
+        }
+    }
+
+    TRACE2(("gdth_detect() %d controller detected\n",gdth_ctr_count));
+
+#ifdef GDTH_STATISTICS
+    TRACE2(("gdth_detect(): Initializing timer !\n"));
+    timer_table[GDTH_TIMER].fn = gdth_timeout;
+    timer_table[GDTH_TIMER].expires = jiffies + HZ;
+    timer_active |= 1<<GDTH_TIMER;
+#endif
+
+    gdth_polling = FALSE;
+    return gdth_ctr_vcount;
+}
+
+
+int gdth_release(struct Scsi_Host *shp)
+{
+    unsigned long flags;
+
+    TRACE2(("gdth_release()\n"));
+
+    save_flags(flags);
+    cli();
+    if (NUMDATA(shp)->busnum == 0) {
+        if (shp->irq) {
+#if LINUX_VERSION_CODE >= 0x010346
+            free_irq(shp->irq,NULL);
+#else
+            free_irq(shp->irq);
+#endif
+        }
+        if (shp->dma_channel != 0xff) {
+            free_dma(shp->dma_channel);
+        }
+    }
+
+    restore_flags(flags);
+    scsi_unregister(shp);
+    return 0;
+}
+            
+
+static const char *gdth_ctr_name(int hanum)
+{
+    gdth_ha_str *ha;
+
+    TRACE2(("gdth_ctr_name()\n"));
+
+    ha    = HADATA(gdth_ctr_tab[hanum]);
+
+    if (ha->type == GDT_EISA) {
+        switch (ha->stype) {
+          case GDT3_ID:
+            return("GDT3000/3020 (EISA)");
+          case GDT3A_ID:
+            return("GDT3000A/3020A/3050A (EISA)");
+          case GDT3B_ID:
+            return("GDT3000B/3010A (EISA)");
+        }
+    } else if (ha->type == GDT_ISA) {
+        return("GDT2000/2020 (ISA)");
+    } else if (ha->type == GDT_PCI) {
+        switch (ha->stype) {
+          case PCI_DEVICE_ID_VORTEX_GDT60x0:
+            return("GDT6000/6020/6050 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6000B:
+            return("GDT6000B/6010 (PCI)");
+        }
+    } else if (ha->type == GDT_PCINEW) {
+        switch (ha->stype) {
+          case PCI_DEVICE_ID_VORTEX_GDT6x10:
+            return("GDT6110/6510 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x20:
+            return("GDT6120/6520 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6530:
+            return("GDT6530 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6550:
+            return("GDT6550 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x17:
+            return("GDT6117/6517 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x27:
+            return("GDT6127/6527 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6537:
+            return("GDT6537 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6557:
+            return("GDT6557/6557-ECC (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x15:
+            return("GDT6115/6515 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x25:
+            return("GDT6125/6525 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6535:
+            return("GDT6535 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6555:
+            return("GDT6555/6555-ECC (PCI)");
+        }
+    } else if (ha->type == GDT_PCIMPR) {
+        switch (ha->stype) {
+          case PCI_DEVICE_ID_VORTEX_GDT6x17RP:
+            return("GDT6117RP/GDT6517RP (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x27RP:
+            return("GDT6127RP/GDT6527RP (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6537RP:
+            return("GDT6537RP (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6557RP:
+            return("GDT6557RP (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x11RP:
+            return("GDT6111RP/GDT6511RP (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x21RP:
+            return("GDT6121RP/GDT6521RP (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x17RP1:
+            return("GDT6117RP1/GDT6517RP1 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x27RP1:
+            return("GDT6127RP1/GDT6527RP1 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6537RP1:
+            return("GDT6537RP1 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6557RP1:
+            return("GDT6557RP1 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x11RP1:
+            return("GDT6111RP1/GDT6511RP1 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x21RP1:
+            return("GDT6121RP1/GDT6521RP1 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x17RP2:
+            return("GDT6117RP2/GDT6517RP2 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x27RP2:
+            return("GDT6127RP2/GDT6527RP2 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6537RP2:
+            return("GDT6537RP2 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6557RP2:
+            return("GDT6557RP2 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x11RP2:
+            return("GDT6111RP2/GDT6511RP2 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x21RP2:
+            return("GDT6121RP2/GDT6521RP2 (PCI)");
+        }
+    }
+    return("");
+}
+
+const char *gdth_info(struct Scsi_Host *shp)
+{
+    int hanum;
+    
+    TRACE2(("gdth_info()\n"));
+    hanum = NUMDATA(shp)->hanum;
+
+    return (gdth_ctr_name(hanum));
+}
+
+
+int gdth_abort(Scsi_Cmnd *scp)
+{
+    TRACE2(("gdth_abort() reason %d\n",scp->abort_reason));
+    return SCSI_ABORT_SNOOZE;
+}
+
+#if LINUX_VERSION_CODE >= 0x010346
+int gdth_reset(Scsi_Cmnd *scp, unsigned int reset_flags)
+#else
+int gdth_reset(Scsi_Cmnd *scp)
+#endif
+{
+    TRACE2(("gdth_reset()\n"));
+    return SCSI_RESET_PUNT;
+}
+
+
+#if LINUX_VERSION_CODE >= 0x010300
+int gdth_bios_param(Disk *disk,kdev_t dev,int *ip)
+#else
+int gdth_bios_param(Disk *disk,int dev,int *ip)
+#endif
+{
+    TRACE2(("gdth_bios_param()\n"));
+
+    ip[2] = disk->capacity / HEADS / SECS;
+    if (ip[2] <= MAXCYLS) {
+        ip[0] = HEADS;
+        ip[1] = SECS;
+    } else {
+        ip[2] = disk->capacity / MEDHEADS / MEDSECS;
+        if (ip[2] <= MAXCYLS) {
+            ip[0] = MEDHEADS;
+            ip[1] = MEDSECS;
+        } else {
+            ip[2] = disk->capacity / BIGHEADS / BIGSECS;
+            ip[0] = BIGHEADS;
+            ip[1] = BIGSECS;
+        }
+    }
+    TRACE2(("gdth_bios_param(): %d heads, %d secs, %d cyls\n",
+            ip[0],ip[1],ip[2]));
+    return 0;
+}
+
+
+static void internal_done(Scsi_Cmnd *scp)
+{
+    scp->SCp.sent_command++;
+}
+
+int gdth_command(Scsi_Cmnd *scp)
+{
+    TRACE2(("gdth_command()\n"));
+
+    scp->SCp.sent_command = 0;
+    gdth_queuecommand(scp,internal_done);
+
+    while (!scp->SCp.sent_command)
+        barrier();
+    return scp->result;
+}
+
+
+int gdth_queuecommand(Scsi_Cmnd *scp,void (*done)(Scsi_Cmnd *))
+{
+    int hanum;
+    int priority;
+
+    TRACE(("gdth_queuecommand() cmd 0x%x id %d lun %d\n",
+                  scp->cmnd[0],scp->target,scp->lun));
+    
+    scp->scsi_done = (void *)done;
+    scp->SCp.have_data_in = 0;
+    hanum = NUMDATA(scp->host)->hanum;
+#ifdef GDTH_STATISTICS
+    ++act_ios;
+#endif
+
+    priority = DEFAULT_PRI;
+#if LINUX_VERSION_CODE >= 0x010300
+    if (scp->done == gdth_scsi_done)
+        priority = scp->SCp.this_residual;
+#endif
+    gdth_putq( hanum, scp, priority );
+    gdth_next( hanum );
+    return 0;
+}
+
+
+/* shutdown routine */
+void gdth_halt()
+{
+    int             hanum, i, j;
+    gdth_ha_str     *ha;
+    Scsi_Cmnd       scp;
+    Scsi_Device     sdev;
+    gdth_cmd_str    gdtcmd;
+    char            cmnd[12];
+
+    TRACE2(("gdth_halt()\n"));
+    printk("GDT: Flushing all host drives .. ");
+
+    for (hanum = 0; hanum < gdth_ctr_count; ++hanum) {
+        ha = HADATA(gdth_ctr_tab[hanum]);
+        memset(&sdev,0,sizeof(Scsi_Device));
+        memset(&scp, 0,sizeof(Scsi_Cmnd));
+        sdev.host = gdth_ctr_tab[hanum];
+        sdev.id = sdev.host->this_id;
+        scp.cmd_len = 12;
+        scp.host = gdth_ctr_tab[hanum];
+        scp.target = sdev.host->this_id;
+        scp.device = &sdev;
+        scp.use_sg = 0;
+
+        /* flush */
+        for (i = 0; i < MAXBUS; ++i) {
+            for (j = 0; j < MAXID; ++j) {
+                if (ha->id[i][j].type == CACHE_DTYP) {
+                    gdtcmd.BoardNode = LOCALBOARD;
+                    gdtcmd.Service = CACHESERVICE;
+                    gdtcmd.OpCode = GDT_FLUSH;
+                    gdtcmd.u.cache.DeviceNo = ha->id[i][j].hostdrive;
+                    gdtcmd.u.cache.BlockNo = 1;
+                    gdtcmd.u.cache.sg_canz = 0;
+                    TRACE2(("gdth_halt(): flush ha %d drive %d\n",
+                        hanum, ha->id[i][j].hostdrive));
+                    {
+                        struct semaphore sem = MUTEX_LOCKED;
+                        scp.request.rq_status = RQ_SCSI_BUSY;
+                        scp.request.sem = &sem;
+                        scsi_do_cmd(&scp, cmnd, &gdtcmd,
+                                    sizeof(gdth_cmd_str), gdth_scsi_done,
+                                    30*HZ, 1);
+                        down(&sem);
+                    }
+                }
+            }
+        }
+
+        /* controller reset */
+        gdtcmd.BoardNode = LOCALBOARD;
+        gdtcmd.Service = CACHESERVICE;
+        gdtcmd.OpCode = GDT_RESET;
+        TRACE2(("gdth_halt(): reset controller %d\n", hanum));
+        {
+            struct semaphore sem = MUTEX_LOCKED;
+            scp.request.rq_status = RQ_SCSI_BUSY;
+            scp.request.sem = &sem;
+            scsi_do_cmd(&scp, cmnd, &gdtcmd,
+                sizeof(gdth_cmd_str), gdth_scsi_done,
+                10*HZ, 1);
+            down(&sem);
+        }
+    }
+    printk("Done.\n");
+}
+
+
+/* called from init/main.c */
+void gdth_setup(char *str,int *ints)
+{
+    static size_t setup_idx = 0;
+
+    TRACE2(("gdth_setup() str %s ints[0] %d ints[1] %d\n",
+                  str ? str:"NULL", ints[0],
+                  ints[0] ? ints[1]:0));
+
+    if (setup_idx >= MAXHA) {
+        printk("GDT: gdth_setup() called too many times. Bad LILO params ?\n");
+        return;
+    }
+    if (ints[0] != 1) {
+        printk("GDT: Illegal command line !\n");
+        printk("Usage: gdth=<IRQ>\n");
+        printk("Where: <IRQ>: valid EISA controller IRQ (10,11,12,14)\n");
+        printk("              or 0 to disable controller driver\n");
+        return;
+    }
+    if (ints[1] == 10 || ints[1] == 11 || ints[1] == 12 || ints[1] == 14) {
+        irqs[setup_idx++] = ints[1];
+        irqs[setup_idx]   = 0xff;
+        return;
+    }
+    if (ints[1] == 0) {
+        disable_gdth_scan = TRUE;
+        return;
+    }
+    printk("GDT: Invalid IRQ (%d) specified\n",ints[1]);
+}
+
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = GDTH;
+#include "scsi_module.c"
+#endif
+
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/gdth.h linux/drivers/scsi/gdth.h
--- v2.0.30/linux/drivers/scsi/gdth.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/gdth.h	Mon Aug  4 17:15:07 1997
@@ -0,0 +1,719 @@
+#ifndef _GDTH_H
+#define _GDTH_H
+
+/*
+ * Header file for the GDT ISA/EISA/PCI Disk Array Controller driver for Linux
+ * 
+ * gdth.h Copyright (C) 1995-97 ICP vortex Computersysteme GmbH, Achim Leubner
+ * See gdth.c for further informations and 
+ * below for supported controller types
+ *
+ * <achim@vortex.de>
+ *
+ * $Id: gdth.h,v 1.7 1997/03/20 16:01:59 achim Exp $
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+
+#ifndef NULL
+#define NULL 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/* defines, macros */
+
+/* driver version */
+#define GDTH_VERSION_STR        "1.00"
+#define GDTH_VERSION            1
+#define GDTH_SUBVERSION         0
+
+/* protocol version */
+#define PROTOCOL_VERSION        1
+
+/* controller classes */
+#define GDT_ISA         0x01                    /* ISA controller */
+#define GDT_EISA        0x02                    /* EISA controller */
+#define GDT_PCI         0x03                    /* PCI controller */
+#define GDT_PCINEW      0x04                    /* new PCI controller */
+#define GDT_PCIMPR      0x05                    /* PCI MPR controller */
+/* GDT_EISA, controller subtypes EISA */
+#define GDT3_ID         0x0130941c              /* GDT3000/3020 */
+#define GDT3A_ID        0x0230941c              /* GDT3000A/3020A/3050A */
+#define GDT3B_ID        0x0330941c              /* GDT3000B/3010A */
+/* GDT_ISA */
+#define GDT2_ID         0x0120941c              /* GDT2000/2020 */
+/* vendor ID, device IDs (PCI) */
+/* these defines should already exist in <linux/pci.h> */
+#ifndef PCI_VENDOR_ID_VORTEX
+#define PCI_VENDOR_ID_VORTEX            0x1119  /* PCI controller vendor ID */
+#endif
+#ifndef PCI_DEVICE_ID_VORTEX_GDT60x0
+/* GDT_PCI */
+#define PCI_DEVICE_ID_VORTEX_GDT60x0    0       /* GDT6000/6020/6050 */
+#define PCI_DEVICE_ID_VORTEX_GDT6000B   1       /* GDT6000B/6010 */
+/* GDT_PCINEW */
+#define PCI_DEVICE_ID_VORTEX_GDT6x10    2       /* GDT6110/6510 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x20    3       /* GDT6120/6520 */
+#define PCI_DEVICE_ID_VORTEX_GDT6530    4       /* GDT6530 */
+#define PCI_DEVICE_ID_VORTEX_GDT6550    5       /* GDT6550 */
+/* GDT_PCINEW, wide/ultra SCSI controllers */
+#define PCI_DEVICE_ID_VORTEX_GDT6x17    6       /* GDT6117/6517 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x27    7       /* GDT6127/6527 */
+#define PCI_DEVICE_ID_VORTEX_GDT6537    8       /* GDT6537 */
+#define PCI_DEVICE_ID_VORTEX_GDT6557    9       /* GDT6557/6557-ECC */
+/* GDT_PCINEW, wide SCSI controllers */
+#define PCI_DEVICE_ID_VORTEX_GDT6x15    10      /* GDT6115/6515 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x25    11      /* GDT6125/6525 */
+#define PCI_DEVICE_ID_VORTEX_GDT6535    12      /* GDT6535 */
+#define PCI_DEVICE_ID_VORTEX_GDT6555    13      /* GDT6555/6555-ECC */
+#endif
+
+#ifndef PCI_DEVICE_ID_VORTEX_GDT6x17RP
+/* GDT_MPR, RP series, wide/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x17RP  0x100   /* GDT6117RP/GDT6517RP */
+#define PCI_DEVICE_ID_VORTEX_GDT6x27RP  0x101   /* GDT6127RP/GDT6527RP */
+#define PCI_DEVICE_ID_VORTEX_GDT6537RP  0x102   /* GDT6537RP */
+#define PCI_DEVICE_ID_VORTEX_GDT6557RP  0x103   /* GDT6557RP */
+/* GDT_MPR, RP series, narrow/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x11RP  0x104   /* GDT6111RP/GDT6511RP */
+#define PCI_DEVICE_ID_VORTEX_GDT6x21RP  0x105   /* GDT6121RP/GDT6521RP */
+/* GDT_MPR, RP1 series, wide/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x17RP1 0x110   /* GDT6117RP1/GDT6517RP1 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x27RP1 0x111   /* GDT6127RP1/GDT6527RP1 */
+#define PCI_DEVICE_ID_VORTEX_GDT6537RP1 0x112   /* GDT6537RP1 */
+#define PCI_DEVICE_ID_VORTEX_GDT6557RP1 0x113   /* GDT6557RP1 */
+/* GDT_MPR, RP1 series, narrow/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x11RP1 0x114   /* GDT6111RP1/GDT6511RP1 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x21RP1 0x115   /* GDT6121RP1/GDT6521RP1 */
+/* GDT_MPR, RP2 series, wide/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x17RP2 0x120   /* GDT6117RP2/GDT6517RP2 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x27RP2 0x121   /* GDT6127RP2/GDT6527RP2 */
+#define PCI_DEVICE_ID_VORTEX_GDT6537RP2 0x122   /* GDT6537RP2 */
+#define PCI_DEVICE_ID_VORTEX_GDT6557RP2 0x123   /* GDT6557RP2 */
+/* GDT_MPR, RP2 series, narrow/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x11RP2 0x124   /* GDT6111RP2/GDT6511RP2 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x21RP2 0x125   /* GDT6121RP2/GDT6521RP2 */
+#endif
+
+/* limits */
+#define GDTH_SCRATCH    4096                    /* 4KB scratch buffer */
+#define GDTH_MAXCMDS    124
+#define GDTH_MAXC_P_L   16                      /* max. cmds per lun */
+#define MAXOFFSETS      128
+#define MAXHA           8
+#define MAXID           8
+#define MAXLUN          8
+#define MAXBUS          5
+#define MAX_HDRIVES     35                      /* max. host drive count */
+#define MAX_EVENTS      100                     /* event buffer count */
+#define MAXCYLS         1024
+#define HEADS           64
+#define SECS            32                      /* mapping 64*32 */
+#define MEDHEADS        127
+#define MEDSECS         63                      /* mapping 127*63 */
+#define BIGHEADS        255
+#define BIGSECS         63                      /* mapping 255*63 */
+
+/* special command ptr. */
+#define UNUSED_CMND     ((Scsi_Cmnd *)-1)
+#define INTERNAL_CMND   ((Scsi_Cmnd *)-2)
+#define SCREEN_CMND     ((Scsi_Cmnd *)-3)
+#define SPECIAL_SCP(p)  (p==UNUSED_CMND || p==INTERNAL_CMND || p==SCREEN_CMND)
+
+/* device types */
+#define EMPTY_DTYP      0
+#define CACHE_DTYP      1
+#define RAW_DTYP        2
+#define SIOP_DTYP       3                       /* the SCSI processor */
+
+/* controller services */
+#define SCSIRAWSERVICE  3
+#define CACHESERVICE    9
+#define SCREENSERVICE   11
+
+/* screenservice defines */
+#define MSG_INV_HANDLE  -1                      /* special message handle */
+#define MSGLEN          16                      /* size of message text */
+#define MSG_SIZE        34                      /* size of message structure */
+#define MSG_REQUEST     0                       /* async. event: message */
+
+/* cacheservice defines */
+#define SECTOR_SIZE     0x200                   /* always 512 bytes per sector */
+
+/* DPMEM constants */
+#define IC_HEADER_BYTES 48
+#define IC_QUEUE_BYTES  4
+#define DPMEM_COMMAND_OFFSET    IC_HEADER_BYTES+IC_QUEUE_BYTES*MAXOFFSETS
+
+/* service commands */
+#define GDT_INIT        0                       /* service initialization */
+#define GDT_READ        1                       /* read command */
+#define GDT_WRITE       2                       /* write command */
+#define GDT_INFO        3                       /* information about devices */
+#define GDT_FLUSH       4                       /* flush dirty cache buffers */
+#define GDT_IOCTL       5                       /* ioctl command */
+#define GDT_DEVTYPE     9                       /* additional information */
+#define GDT_MOUNT       10                      /* mount cache device */
+#define GDT_UNMOUNT     11                      /* unmount cache device */
+#define GDT_SET_FEAT    12                      /* set feat. (scatter/gather) */
+#define GDT_GET_FEAT    13                      /* get features */
+#define GDT_RESERVE     14                      /* reserve dev. to raw service */
+#define GDT_WRITE_THR   16                      /* write through */
+#define GDT_EXT_INFO    18                      /* extended info */
+#define GDT_RESET       19                      /* controller reset */
+
+/* IOCTL command defines */
+#define SCSI_CHAN_CNT   5                       /* subfunctions */
+#define L_CTRL_PATTERN  0x20000000L
+#define CACHE_INFO      4
+#define CACHE_CONFIG    5
+#define IO_CHANNEL      0x00020000L             /* channels */
+#define INVALID_CHANNEL 0x0000ffffL     
+
+/* IOCTLs */
+#define GDTIOCTL_MASK       ('J'<<8)
+#define GDTIOCTL_GENERAL    (GDTIOCTL_MASK | 0) /* general IOCTL */
+#define GDTIOCTL_DRVERS     (GDTIOCTL_MASK | 1) /* get driver version */
+#define GDTIOCTL_CTRTYPE    (GDTIOCTL_MASK | 2) /* get controller type */
+#define GDTIOCTL_CTRCNT     (GDTIOCTL_MASK | 5) /* get controller count */
+#define GDTIOCTL_LOCKDRV    (GDTIOCTL_MASK | 6) /* lock host drive */
+#define GDTIOCTL_LOCKCHN    (GDTIOCTL_MASK | 7) /* lock channel */
+#define GDTIOCTL_EVENT      (GDTIOCTL_MASK | 8) /* read controller events */
+
+/* service errors */
+#define S_OK            1                       /* no error */
+#define S_BSY           7                       /* controller busy */
+#define S_RAW_SCSI      12                      /* raw serv.: target error */
+#define S_RAW_ILL       0xff                    /* raw serv.: illegal */
+
+/* timeout values */
+#define INIT_RETRIES    10000                   /* 10000 * 1ms = 10s */
+#define INIT_TIMEOUT    100000                  /* 1000 * 1ms = 1s */
+#define POLL_TIMEOUT    10000                   /* 10000 * 1ms = 10s */
+
+/* priorities */
+#define DEFAULT_PRI     0x20
+#define IOCTL_PRI       0x10
+
+/* data directions */
+#define DATA_IN         0x01000000L             /* data from target */
+#define DATA_OUT        0x00000000L             /* data to target */
+
+/* BMIC registers (EISA controllers) */
+#define ID0REG          0x0c80                  /* board ID */
+#define EINTENABREG     0x0c89                  /* interrupt enable */
+#define SEMA0REG        0x0c8a                  /* command semaphore */
+#define SEMA1REG        0x0c8b                  /* status semaphore */
+#define LDOORREG        0x0c8d                  /* local doorbell */
+#define EDENABREG       0x0c8e                  /* EISA system doorbell enable */
+#define EDOORREG        0x0c8f                  /* EISA system doorbell */
+#define MAILBOXREG      0x0c90                  /* mailbox reg. (16 bytes) */
+#define EISAREG         0x0cc0                  /* EISA configuration */
+
+/* other defines */
+#define LINUX_OS        8                       /* used for cache optim. */
+#define SCATTER_GATHER  1                       /* s/g feature */
+#define GDTH_MAXSG      32                      /* max. s/g elements */
+#define SECS32          0x1f                    /* round capacity */
+#define BIOS_ID_OFFS    0x10                    /* offset contr. ID in ISABIOS */
+#define LOCALBOARD      0                       /* board node always 0 */
+#define ASYNCINDEX      0                       /* cmd index async. event */
+#define SPEZINDEX       1                       /* cmd index unknown service */
+#define GDT_WR_THROUGH  0x100                   /* WRITE_THROUGH supported */
+
+/* typedefs */
+
+#pragma pack(1)
+
+typedef struct {
+    char        buffer[GDTH_SCRATCH];           /* scratch buffer */
+} gdth_scratch_str;
+
+/* screenservice message */
+typedef struct {                               
+    ulong       msg_handle;                     /* message handle */
+    ulong       msg_len;                        /* size of message */
+    ulong       msg_alen;                       /* answer length */
+    unchar      msg_answer;                     /* answer flag */
+    unchar      msg_ext;                        /* more messages */
+    unchar      msg_reserved[2];
+    char        msg_text[MSGLEN+2];             /* the message text */
+} gdth_msg_str;
+
+/* get channel count IOCTL */
+typedef struct {
+    ulong       channel_no;                     /* number of channel */
+    ulong       drive_cnt;                      /* number of drives */
+    unchar      siop_id;                        /* SCSI processor ID */
+    unchar      siop_state;                     /* SCSI processor state */ 
+} gdth_getch_str;
+
+/* cache info/config IOCTL */
+typedef struct {
+    ulong       version;                        /* firmware version */
+    ushort      state;                          /* cache state (on/off) */
+    ushort      strategy;                       /* cache strategy */
+    ushort      write_back;                     /* write back state (on/off) */
+    ushort      block_size;                     /* cache block size */
+} gdth_cpar_str;
+
+typedef struct {
+    ulong       csize;                          /* cache size */
+    ulong       read_cnt;                       /* read/write counter */
+    ulong       write_cnt;
+    ulong       tr_hits;                        /* hits */
+    ulong       sec_hits;
+    ulong       sec_miss;                       /* misses */
+} gdth_cstat_str;
+
+typedef struct {
+    gdth_cpar_str   cpar;
+    gdth_cstat_str  cstat;
+} gdth_cinfo_str;
+
+/* scatter/gather element */
+typedef struct {
+    ulong       sg_ptr;                         /* address */
+    ulong       sg_len;                         /* length */
+} gdth_sg_str;
+
+/* command structure */
+typedef struct {
+    ulong       BoardNode;                      /* board node (always 0) */
+    ulong       CommandIndex;                   /* command number */
+    ushort      OpCode;                         /* the command (READ,..) */
+    union {
+        struct {
+            ushort      DeviceNo;               /* number of cache drive */
+            ulong       BlockNo;                /* block number */
+            ulong       BlockCnt;               /* block count */
+            ulong       DestAddr;               /* dest. addr. (if s/g: -1) */
+            ulong       sg_canz;                /* s/g element count */
+            gdth_sg_str sg_lst[GDTH_MAXSG];     /* s/g list */
+        } cache;                                /* cache service cmd. str. */
+        struct {
+            ushort      param_size;             /* size of p_param buffer */
+            ulong       subfunc;                /* IOCTL function */
+            ulong       channel;                /* device */
+            ulong       p_param;                /* buffer */
+        } ioctl;                                /* IOCTL command structure */
+        struct {
+            ushort      reserved;
+            ulong       msg_handle;             /* message handle */
+            ulong       msg_addr;               /* message buffer address */
+        } screen;                               /* screen service cmd. str. */
+        struct {
+            ushort      reserved;
+            ulong       direction;              /* data direction */
+            ulong       mdisc_time;             /* disc. time (0: no timeout)*/
+            ulong       mcon_time;              /* connect time(0: no to.) */
+            ulong       sdata;                  /* dest. addr. (if s/g: -1) */
+            ulong       sdlen;                  /* data length (bytes) */
+            ulong       clen;                   /* SCSI cmd. length(6,10,12) */
+            unchar      cmd[12];                /* SCSI command */
+            unchar      target;                 /* target ID */
+            unchar      lun;                    /* LUN */
+            unchar      bus;                    /* SCSI bus number */
+            unchar      priority;               /* only 0 used */
+            ulong       sense_len;              /* sense data length */
+            ulong       sense_data;             /* sense data addr. */
+            struct raw  *link_p;                /* linked cmds (not supp.) */
+            ulong       sg_ranz;                /* s/g element count */
+            gdth_sg_str sg_lst[GDTH_MAXSG];     /* s/g list */
+        } raw;                                  /* raw service cmd. struct. */
+    } u;
+    /* additional variables */
+    unchar      Service;                        /* controller service */
+    ushort      Status;                         /* command result */
+    ulong       Info;                           /* additional information */
+    Scsi_Cmnd   *RequestBuffer;                 /* request buffer */
+} gdth_cmd_str;
+
+/* controller event structure */
+#define ES_ASYNC    1
+#define ES_DRIVER   2
+#define ES_TEST     3
+#define ES_SYNC     4
+typedef struct {
+    ushort                  size;               /* size of structure */
+    union {
+        char                stream[16];
+        struct {
+            ushort          ionode;
+            ushort          service;
+            ulong           index;
+        } driver;
+        struct {
+            ushort          ionode;
+            ushort          service;
+            ushort          status;
+            ulong           info;
+            unchar          scsi_coord[3];
+        } async;
+        struct {
+            ushort          ionode;
+            ushort          service;
+            ushort          status;
+            ulong           info;
+            ushort          hostdrive;
+            unchar          scsi_coord[3];
+            unchar          sense_key;
+        } sync;
+        struct {
+            ulong           l1, l2, l3, l4;
+        } test;
+    } eu;
+} gdth_evt_data;
+
+typedef struct {
+    ulong           first_stamp;
+    ulong           last_stamp;
+    ushort          same_count;
+    ushort          event_source;
+    ushort          event_idx;
+    unchar          application;
+    unchar          reserved;
+    gdth_evt_data   event_data;
+} gdth_evt_str;
+
+
+/* DPRAM structures */
+
+/* interface area ISA/PCI */
+typedef struct {
+    unchar              S_Cmd_Indx;             /* special command */
+    unchar volatile     S_Status;               /* status special command */
+    ushort              reserved1;
+    ulong               S_Info[4];              /* add. info special command */
+    unchar volatile     Sema0;                  /* command semaphore */
+    unchar              reserved2[3];
+    unchar              Cmd_Index;              /* command number */
+    unchar              reserved3[3];
+    ushort volatile     Status;                 /* command status */
+    ushort              Service;                /* service(for async.events) */
+    ulong               Info[2];                /* additional info */
+    struct {
+        ushort          offset;                 /* command offs. in the DPRAM*/
+        ushort          serv_id;                /* service */
+    } comm_queue[MAXOFFSETS];                   /* command queue */
+    ulong               bios_reserved[2];
+    unchar              gdt_dpr_cmd[1];         /* commands */
+} gdt_dpr_if;
+
+/* SRAM structure PCI controllers */
+typedef struct {
+    ulong       magic;                          /* controller ID from BIOS */
+    ushort      need_deinit;                    /* switch betw. BIOS/driver */
+    unchar      switch_support;                 /* see need_deinit */
+    unchar      padding[9];
+    unchar      os_used[16];                    /* OS code per service */
+    unchar      unused[28];
+    unchar      fw_magic;                       /* contr. ID from firmware */
+} gdt_pci_sram;
+
+/* SRAM structure EISA controllers (but NOT GDT3000/3020) */
+typedef struct {
+    unchar      os_used[16];                    /* OS code per service */
+    ushort      need_deinit;                    /* switch betw. BIOS/driver */
+    unchar      switch_support;                 /* see need_deinit */
+    unchar      padding;
+} gdt_eisa_sram;
+
+
+/* DPRAM ISA controllers */
+typedef struct {
+    union {
+        struct {
+            unchar      bios_used[0x3c00-32];   /* 15KB - 32Bytes BIOS */
+            ulong       magic;                  /* controller (EISA) ID */
+            ushort      need_deinit;            /* switch betw. BIOS/driver */
+            unchar      switch_support;         /* see need_deinit */
+            unchar      padding[9];
+            unchar      os_used[16];            /* OS code per service */
+        } dp_sram;
+        unchar          bios_area[0x4000];      /* 16KB reserved for BIOS */
+    } bu;
+    union {
+        gdt_dpr_if      ic;                     /* interface area */
+        unchar          if_area[0x3000];        /* 12KB for interface */
+    } u;
+    struct {
+        unchar          memlock;                /* write protection DPRAM */
+        unchar          event;                  /* release event */
+        unchar          irqen;                  /* board interrupts enable */
+        unchar          irqdel;                 /* acknowledge board int. */
+        unchar volatile Sema1;                  /* status semaphore */
+        unchar          rq;                     /* IRQ/DRQ configuration */
+    } io;
+} gdt2_dpram_str;
+
+/* DPRAM PCI controllers */
+typedef struct {
+    union {
+        gdt_dpr_if      ic;                     /* interface area */
+        unchar          if_area[0xff0-sizeof(gdt_pci_sram)];
+    } u;
+    gdt_pci_sram        gdt6sr;                 /* SRAM structure */
+    struct {
+        unchar          unused0[1];
+        unchar volatile Sema1;                  /* command semaphore */
+        unchar          unused1[3];
+        unchar          irqen;                  /* board interrupts enable */
+        unchar          unused2[2];
+        unchar          event;                  /* release event */
+        unchar          unused3[3];
+        unchar          irqdel;                 /* acknowledge board int. */
+        unchar          unused4[3];
+    } io;
+} gdt6_dpram_str;
+
+/* PLX register structure (new PCI controllers) */
+typedef struct {
+    unchar              cfg_reg;        /* DPRAM cfg.(2:below 1MB,0:anywhere)*/
+    unchar              unused1[0x3f];
+    unchar volatile     sema0_reg;              /* command semaphore */
+    unchar volatile     sema1_reg;              /* status semaphore */
+    unchar              unused2[2];
+    ushort volatile     status;                 /* command status */
+    ushort              service;                /* service */
+    ulong               info[2];                /* additional info */
+    unchar              unused3[0x10];
+    unchar              ldoor_reg;              /* PCI to local doorbell */
+    unchar              unused4[3];
+    unchar volatile     edoor_reg;              /* local to PCI doorbell */
+    unchar              unused5[3];
+    unchar              control0;               /* control0 register(unused) */
+    unchar              control1;               /* board interrupts enable */
+    unchar              unused6[0x16];
+} gdt6c_plx_regs;
+
+/* DPRAM new PCI controllers */
+typedef struct {
+    union {
+        gdt_dpr_if      ic;                     /* interface area */
+        unchar          if_area[0x4000-sizeof(gdt_pci_sram)];
+    } u;
+    gdt_pci_sram        gdt6sr;                 /* SRAM structure */
+} gdt6c_dpram_str;
+
+/* i960 register structure (PCI MPR controllers) */
+typedef struct {
+    unchar              unused1[16];
+    unchar volatile     sema0_reg;              /* command semaphore */
+    unchar              unused2;
+    unchar volatile     sema1_reg;              /* status semaphore */
+    unchar              unused3;
+    ushort volatile     status;                 /* command status */
+    ushort              service;                /* service */
+    ulong               info[2];                /* additional info */
+    unchar              ldoor_reg;              /* PCI to local doorbell */
+    unchar              unused4[11];
+    unchar volatile     edoor_reg;              /* local to PCI doorbell */
+    unchar              unused5[7];
+    unchar              edoor_en_reg;           /* board interrupts enable */
+    unchar              unused6[27];
+    ulong               unused7[1004];          /* size: 4 KB */
+} gdt6m_i960_regs;
+
+/* DPRAM PCI MPR controllers */
+typedef struct {
+    gdt6m_i960_regs     i960r;                  /* 4KB i960 registers */
+    union {
+        gdt_dpr_if      ic;                     /* interface area */
+        unchar          if_area[0x3000-sizeof(gdt_pci_sram)];
+    } u;
+    gdt_pci_sram        gdt6sr;                 /* SRAM structure */
+} gdt6m_dpram_str;
+
+
+/* PCI resources */
+typedef struct {
+    ushort      device_id;                      /* device ID (0,..,9) */
+    unchar      bus;                            /* PCI bus */
+    unchar      device_fn;                      /* PCI device/function no. */
+    ulong       dpmem;                          /* DPRAM address */
+    ulong       io;                             /* IO address */
+    ulong       io_mm;                          /* IO address mem. mapped */
+    ulong       bios;                           /* BIOS address */
+    unchar      irq;                            /* IRQ */
+} gdth_pci_str;
+
+
+/* controller information structure */
+typedef struct {
+    unchar              bus_cnt;                /* SCSI bus count */
+    unchar              type;                   /* controller class */
+    ushort              raw_feat;               /* feat. raw service (s/g,..) */
+    ushort              cache_feat;             /* feat. cache serv. (s/g,..) */
+    ulong               stype;                  /* controller subtype */
+    ulong               brd;                    /* BMIC/DPRAM address */
+    ulong               brd_phys;               /* slot number/BIOS address */
+    gdt6c_plx_regs      *plx;                   /* PLX regs (new PCI contr.) */
+    gdth_cmd_str        *pccb;                  /* address command structure */
+    gdth_scratch_str    *pscratch;
+    unchar              irq;                    /* IRQ */
+    unchar              drq;                    /* DRQ (ISA controllers) */
+    ushort              status;                 /* command status */
+    ulong               info;
+    ulong               info2;                  /* additional info */
+    Scsi_Cmnd           *req_first;             /* top of request queue */
+    struct {
+        unchar          type;                   /* device type */
+        unchar          heads;                  /* mapping */
+        unchar          secs;
+        unchar          lock;                   /* drive locked ? (hot plug) */
+        ushort          hostdrive;              /* host drive number */
+        ushort          devtype;                /* further information */
+        ulong           size;                   /* capacity */
+    } id[MAXBUS][MAXID];         
+    ushort              cmd_cnt;                /* command count in DPRAM */
+    ushort              cmd_len;                /* length of actual command */
+    ushort              cmd_offs_dpmem;         /* actual offset in DPRAM */
+    ushort              ic_all_size;            /* sizeof DPRAM interf. area */
+    unchar              reserved;
+    unchar              mode;                   /* information from /proc */
+    ushort              param_size;
+    gdth_cpar_str       cpar;                   /* controller cache par. */
+} gdth_ha_str;
+
+/* structure for scsi_register(), SCSI bus != 0 */
+typedef struct {
+    ushort      hanum;
+    ushort      busnum;
+} gdth_num_str;
+
+/* structure for scsi_register() */
+typedef struct {
+    gdth_num_str        numext;                 /* must be the first element */
+    gdth_ha_str         haext;
+    gdth_cmd_str        cmdext;
+    gdth_scratch_str    dmaext;
+} gdth_ext_str;
+
+
+/* INQUIRY data format */
+typedef struct {
+    unchar      type_qual;
+    unchar      modif_rmb;
+    unchar      version;
+    unchar      resp_aenc;
+    unchar      add_length;
+    unchar      reserved1;
+    unchar      reserved2;
+    unchar      misc;
+    unchar      vendor[8];
+    unchar      product[16];
+    unchar      revision[4];
+} gdth_inq_data;
+
+/* READ_CAPACITY data format */
+typedef struct {
+    ulong       last_block_no;
+    ulong       block_length;
+} gdth_rdcap_data;
+
+/* REQUEST_SENSE data format */
+typedef struct {
+    unchar      errorcode;
+    unchar      segno;
+    unchar      key;
+    ulong       info;
+    unchar      add_length;
+    ulong       cmd_info;
+    unchar      adsc;
+    unchar      adsq;
+    unchar      fruc;
+    unchar      key_spec[3];
+} gdth_sense_data;
+
+/* MODE_SENSE data format */
+typedef struct {
+    struct {
+        unchar  data_length;
+        unchar  med_type;
+        unchar  dev_par;
+        unchar  bd_length;
+    } hd;
+    struct {
+        unchar  dens_code;
+        unchar  block_count[3];
+        unchar  reserved;
+        unchar  block_length[3];
+    } bd;
+} gdth_modep_data;
+
+typedef struct {
+    ulong       b[10];                          /* 32 bit compiler ! */
+} gdth_stackframe;
+
+#pragma pack()
+
+/* function prototyping */
+
+int gdth_detect(Scsi_Host_Template *);
+int gdth_release(struct Scsi_Host *);
+int gdth_command(Scsi_Cmnd *);
+int gdth_queuecommand(Scsi_Cmnd *,void (*done)(Scsi_Cmnd *));
+int gdth_abort(Scsi_Cmnd *);
+#if LINUX_VERSION_CODE >= 0x010346
+int gdth_reset(Scsi_Cmnd *, unsigned int reset_flags);
+#else
+int gdth_reset(Scsi_Cmnd *);
+#endif
+const char *gdth_info(struct Scsi_Host *);
+
+
+#if LINUX_VERSION_CODE >= 0x010300
+int gdth_bios_param(Disk *,kdev_t,int *);
+extern struct proc_dir_entry proc_scsi_gdth;
+int gdth_proc_info(char *,char **,off_t,int,int,int);
+#define GDTH { NULL, NULL,                              \
+                   &proc_scsi_gdth,                     \
+                   gdth_proc_info,                      \
+                   "GDT SCSI Disk Array Controller",    \
+                   gdth_detect,                         \
+                   gdth_release,                        \
+                   gdth_info,                           \
+                   gdth_command,                        \
+                   gdth_queuecommand,                   \
+                   gdth_abort,                          \
+                   gdth_reset,                          \
+                   NULL,                                \
+                   gdth_bios_param,                     \
+                   GDTH_MAXCMDS,                        \
+                   -1,                                  \
+                   GDTH_MAXSG,                          \
+                   GDTH_MAXC_P_L,                       \
+                   0,                                   \
+                   1,                                   \
+                   ENABLE_CLUSTERING}
+#else
+int gdth_bios_param(Disk *,int,int *);
+#define GDTH { NULL, NULL,                              \
+                   "GDT SCSI Disk Array Controller",    \
+                   gdth_detect,                         \
+                   gdth_release,                        \
+                   gdth_info,                           \
+                   gdth_command,                        \
+                   gdth_queuecommand,                   \
+                   gdth_abort,                          \
+                   gdth_reset,                          \
+                   NULL,                                \
+                   gdth_bios_param,                     \
+                   GDTH_MAXCMDS,                        \
+                   -1,                                  \
+                   GDTH_MAXSG,                          \
+                   GDTH_MAXC_P_L,                       \
+                   0,                                   \
+                   1,                                   \
+                   ENABLE_CLUSTERING}
+#endif
+
+#endif
+
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/gdth_ioctl.h linux/drivers/scsi/gdth_ioctl.h
--- v2.0.30/linux/drivers/scsi/gdth_ioctl.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/gdth_ioctl.h	Mon Aug  4 17:15:07 1997
@@ -0,0 +1,86 @@
+#ifndef _GDTH_IOCTL_H
+#define _GDTH_IOCTL_H
+
+/* gdth_ioctl.h
+ * $Id: gdth_ioctl.h,v 1.1 1997/02/21 08:07:27 achim Exp $
+ */
+
+/* IOCTLs */
+#define GDTIOCTL_MASK       ('J'<<8)
+#define GDTIOCTL_GENERAL    (GDTIOCTL_MASK | 0) /* general IOCTL */
+#define GDTIOCTL_DRVERS     (GDTIOCTL_MASK | 1) /* get driver version */
+#define GDTIOCTL_CTRTYPE    (GDTIOCTL_MASK | 2) /* get controller type */
+#define GDTIOCTL_OSVERS     (GDTIOCTL_MASK | 3) /* get OS version */
+#define GDTIOCTL_CTRCNT     (GDTIOCTL_MASK | 5) /* get controller count */
+#define GDTIOCTL_LOCKDRV    (GDTIOCTL_MASK | 6) /* lock host drive */
+#define GDTIOCTL_LOCKCHN    (GDTIOCTL_MASK | 7) /* lock channel */
+#define GDTIOCTL_EVENT      (GDTIOCTL_MASK | 8) /* read controller events */
+
+#define GDTIOCTL_MAGIC      0x06030f07UL
+
+
+/* IOCTL structure (write) */
+typedef struct {
+    ulong                   magic;              /* IOCTL magic */
+    ushort                  ioctl;              /* IOCTL */
+    ushort                  ionode;             /* controller number */
+    ushort                  service;            /* controller service */
+    ushort                  timeout;            /* timeout */
+    union {
+        struct {
+            unchar          command[512];       /* controller command */
+            unchar          data[1];            /* add. data */
+        } general;
+        struct {
+            unchar          lock;               /* lock/unlock */
+            unchar          drive_cnt;          /* drive count */
+            ushort          drives[35];         /* drives */
+        } lockdrv;
+        struct {
+            unchar          lock;               /* lock/unlock */
+            unchar          channel;            /* channel */
+        } lockchn;
+        struct {
+            int             erase;              /* erase event ? */
+            int             handle;
+        } event;
+    } iu;
+} gdth_iowr_str;
+
+/* IOCTL structure (read) */
+typedef struct {
+    ulong                   size;               /* buffer size */
+    ulong                   status;             /* IOCTL error code */
+    union {
+        struct {
+            unchar          data[1];            /* data */
+        } general;
+        struct {
+            ushort          version;            /* driver version */
+        } drvers;
+        struct {
+            unchar          type;               /* controller type */
+            ushort          info;               /* slot etc. */
+            ushort          oem_id;             /* OEM ID */
+            ushort          bios_ver;           /* not used */
+            ushort          access;             /* not used */
+            ushort          ext_type;           /* extended type */
+        } ctrtype;
+        struct {
+            unchar          version;            /* OS version */
+            unchar          subversion;         /* OS subversion */
+            ushort          revision;           /* revision */
+        } osvers;
+        struct {
+            ushort          count;              /* controller count */
+        } ctrcnt;
+        struct {
+            int             handle;
+            unchar          evt[32];            /* event structure */
+        } event;
+    } iu;
+} gdth_iord_str;
+
+
+#endif
+
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/gdth_proc.c linux/drivers/scsi/gdth_proc.c
--- v2.0.30/linux/drivers/scsi/gdth_proc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/gdth_proc.c	Mon Aug  4 17:15:07 1997
@@ -0,0 +1,635 @@
+/* gdth_proc.c 
+ * $Id: gdth_proc.c,v 1.4 1997/02/25 13:33:47 achim Exp $
+ */
+
+#include "gdth_ioctl.h"
+
+int gdth_proc_info(char *buffer,char **start,off_t offset,int length,   
+                   int hostno,int inout)
+{
+    int hanum,busnum,i;
+
+    TRACE2(("gdth_proc_info() length %d ha %d offs %d inout %d\n",
+            length,hostno,offset,inout));
+
+    for (i=0; i<gdth_ctr_vcount; ++i) {
+        if (gdth_ctr_vtab[i]->host_no == hostno)
+            break;
+    }
+    if (i==gdth_ctr_vcount)
+        return(-EINVAL);
+
+    hanum = NUMDATA(gdth_ctr_vtab[i])->hanum;
+    busnum= NUMDATA(gdth_ctr_vtab[i])->busnum;
+
+    if (inout)
+        return(gdth_set_info(buffer,length,i,hanum,busnum));
+    else
+        return(gdth_get_info(buffer,start,offset,length,i,hanum,busnum));
+}
+
+static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum)
+{
+    int             ret_val;
+    Scsi_Cmnd       scp;
+    Scsi_Device     sdev;
+    gdth_iowr_str   *piowr;
+
+    TRACE2(("gdth_set_info() ha %d bus %d\n",hanum,busnum));
+    piowr = (gdth_iowr_str *)buffer;
+
+    memset(&sdev,0,sizeof(Scsi_Device));
+    memset(&scp, 0,sizeof(Scsi_Cmnd));
+    sdev.host = gdth_ctr_vtab[vh];
+    sdev.id = sdev.host->this_id;
+    scp.cmd_len = 12;
+    scp.host = gdth_ctr_vtab[vh];
+    scp.target = sdev.host->this_id;
+    scp.device = &sdev;
+    scp.use_sg = 0;
+
+    if (length >= 4) {
+        if (strncmp(buffer,"gdth",4) == 0) {
+            buffer += 5;
+            length -= 5;
+            ret_val = gdth_set_asc_info( buffer, length, hanum, scp );
+        } else if (piowr->magic == GDTIOCTL_MAGIC) {
+            ret_val = gdth_set_bin_info( buffer, length, hanum, scp );
+        } else {
+            printk("GDT: Wrong signature: %6s\n",buffer);
+            ret_val = -EINVAL;
+        }
+    } else {
+        ret_val = -EINVAL;
+    }
+    return ret_val;
+}
+         
+static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp)
+{
+    int             orig_length, drive, wb_mode;
+    char            cmnd[12];
+    int             i, j, found;
+    gdth_ha_str     *ha;
+    gdth_cmd_str    gdtcmd;
+    gdth_cpar_str   *pcpar;
+
+    TRACE2(("gdth_set_asc_info() ha %d\n",hanum));
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    memset(cmnd, 0,10);
+    orig_length = length + 5;
+    drive = -1;
+    wb_mode = 0;
+    found = FALSE;
+
+    if (length >= 5 && strncmp(buffer,"flush",5)==0) {
+        buffer += 6;
+        length -= 6;
+        if (length && *buffer>='0' && *buffer<='9') {
+            drive = (int)(*buffer-'0');
+            ++buffer; --length;
+            if (length && *buffer>='0' && *buffer<='9') {
+                drive = drive*10 + (int)(*buffer-'0');
+                ++buffer; --length;
+            }
+            printk("GDT: Flushing host drive %d .. ",drive);
+        } else {
+            printk("GDT: Flushing all host drives .. ");
+        }
+        for (i = 0; i < MAXBUS; ++i) {
+            for (j = 0; j < MAXID; ++j) {
+                if (ha->id[i][j].type == CACHE_DTYP) {
+                    if (drive != -1 &&
+                        ha->id[i][j].hostdrive != (ushort)drive)
+                        continue;
+                    found = TRUE;
+                    gdtcmd.BoardNode = LOCALBOARD;
+                    gdtcmd.Service = CACHESERVICE;
+                    gdtcmd.OpCode = GDT_FLUSH;
+                    gdtcmd.u.cache.DeviceNo = ha->id[i][j].hostdrive;
+                    gdtcmd.u.cache.BlockNo = 1;
+                    gdtcmd.u.cache.sg_canz = 0;
+                    {
+                        struct semaphore sem = MUTEX_LOCKED;
+                        scp.request.rq_status = RQ_SCSI_BUSY;
+                        scp.request.sem = &sem;
+                        scsi_do_cmd(&scp, cmnd, &gdtcmd,
+                                    sizeof(gdth_cmd_str), gdth_scsi_done,
+                                    30*HZ, 1);
+                        down(&sem);
+                    }
+                }
+            }
+        }
+        if (!found)
+            printk("\nNo host drive found !\n");
+        else
+            printk("Done.\n");
+        return(orig_length);
+    }
+
+    if (length >= 7 && strncmp(buffer,"wbp_off",7)==0) {
+        buffer += 8;
+        length -= 8;
+        printk("GDT: Disabling write back permanently .. ");
+        wb_mode = 1;
+    } else if (length >= 6 && strncmp(buffer,"wbp_on",6)==0) {
+        buffer += 7;
+        length -= 7;
+        printk("GDT: Enabling write back permanently .. ");
+        wb_mode = 2;
+    } else if (length >= 6 && strncmp(buffer,"wb_off",6)==0) {
+        buffer += 7;
+        length -= 7;
+        printk("GDT: Disabling write back commands .. ");
+        if (ha->cache_feat & GDT_WR_THROUGH) {
+            gdth_write_through = TRUE;
+            printk("Done.\n");
+        } else {
+            printk("Not supported !\n");
+        }
+        return(orig_length);
+    } else if (length >= 5 && strncmp(buffer,"wb_on",5)==0) {
+        buffer += 6;
+        length -= 6;
+        printk("GDT: Enabling write back commands .. ");
+        gdth_write_through = FALSE;
+        printk("Done.\n");
+        return(orig_length);
+    }
+
+    if (wb_mode) {
+        pcpar = (gdth_cpar_str *)kmalloc( sizeof(gdth_cpar_str),
+            GFP_ATOMIC | GFP_DMA );
+        if (pcpar == NULL) {
+            TRACE2(("gdth_set_info(): Unable to allocate memory.\n"));
+            printk("Unable to allocate memory.\n");
+            return(-EINVAL);
+        }
+        memcpy( pcpar, &ha->cpar, sizeof(gdth_cpar_str) );
+        gdtcmd.BoardNode = LOCALBOARD;
+        gdtcmd.Service = CACHESERVICE;
+        gdtcmd.OpCode = GDT_IOCTL;
+        gdtcmd.u.ioctl.p_param = (ulong)pcpar;
+        gdtcmd.u.ioctl.param_size = sizeof(gdth_cpar_str);
+        gdtcmd.u.ioctl.subfunc = CACHE_CONFIG;
+        gdtcmd.u.ioctl.channel = INVALID_CHANNEL;
+        pcpar->write_back = wb_mode==1 ? 0:1;
+        {
+            struct semaphore sem = MUTEX_LOCKED;
+            scp.request.rq_status = RQ_SCSI_BUSY;
+            scp.request.sem = &sem;
+            scsi_do_cmd(&scp, cmnd, &gdtcmd, sizeof(gdth_cmd_str),
+                        gdth_scsi_done, 30*HZ, 1);
+            down(&sem);
+        }
+        kfree( pcpar );
+        printk("Done.\n");
+        return(orig_length);
+    }
+
+    printk("GDT: Unknown command: %s  Length: %d\n",buffer,length);
+    return(-EINVAL);
+}
+
+static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp)
+{
+    char            cmnd[12];
+    int             id;
+    unchar          i, j, k, found;
+    gdth_ha_str     *ha;
+    gdth_iowr_str   *piowr;
+    gdth_iord_str   *piord;
+    gdth_cmd_str    *pcmd;
+    ulong           *ppadd;
+    ulong           add_size, flags;
+
+
+    TRACE2(("gdth_set_bin_info() ha %d\n",hanum));
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    memset(cmnd, 0,10);
+    piowr = (gdth_iowr_str *)buffer;
+    piord = NULL;
+    pcmd = NULL;
+
+    if (length < GDTOFFSOF(gdth_iowr_str,iu))
+        return(-EINVAL);
+
+    switch (piowr->ioctl) {
+      case GDTIOCTL_GENERAL:
+        if (length < GDTOFFSOF(gdth_iowr_str,iu.general.data[0]))
+            return(-EINVAL);
+        pcmd = (gdth_cmd_str *)piowr->iu.general.command;
+        pcmd->Service = piowr->service;
+        if (pcmd->OpCode == GDT_IOCTL) {
+            ppadd = &pcmd->u.ioctl.p_param;
+            add_size = pcmd->u.ioctl.param_size;
+        } else if (piowr->service == CACHESERVICE) {
+            add_size = pcmd->u.cache.BlockCnt * SECTOR_SIZE;
+            if (ha->cache_feat & SCATTER_GATHER) {
+                ppadd = &pcmd->u.cache.sg_lst[0].sg_ptr;
+                pcmd->u.cache.DestAddr = -1UL;
+                pcmd->u.cache.sg_lst[0].sg_len = add_size;
+                pcmd->u.cache.sg_canz = 1;
+            } else {
+                ppadd = &pcmd->u.cache.DestAddr;
+                pcmd->u.cache.sg_canz = 0;
+            }
+        } else if (piowr->service == SCSIRAWSERVICE) {
+            add_size = pcmd->u.raw.sdlen;
+            if (ha->raw_feat & SCATTER_GATHER) {
+                ppadd = &pcmd->u.raw.sg_lst[0].sg_ptr;
+                pcmd->u.raw.sdata = -1UL;
+                pcmd->u.raw.sg_lst[0].sg_len = add_size;
+                pcmd->u.raw.sg_ranz = 1;
+            } else {
+                ppadd = &pcmd->u.raw.sdata;
+                pcmd->u.raw.sg_ranz = 0;
+            }
+        } else {
+            return(-EINVAL);
+        }
+        id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) + add_size );
+        if (id == -1)
+            return(-EBUSY);
+        piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+
+        piord->size = sizeof(gdth_iord_str) + add_size;
+        if (add_size > 0) {
+            memcpy(piord->iu.general.data, piowr->iu.general.data, add_size);
+            *ppadd = (ulong)piord->iu.general.data;
+        }
+        /* do IOCTL */
+        {
+            struct semaphore sem = MUTEX_LOCKED;
+            scp.request.rq_status = RQ_SCSI_BUSY;
+            scp.request.sem = &sem;
+            scp.SCp.this_residual = IOCTL_PRI;
+            scsi_do_cmd(&scp, cmnd, pcmd,
+                        sizeof(gdth_cmd_str), gdth_scsi_done,
+                        piowr->timeout*HZ, 1);
+            down(&sem);
+            piord->status = (ulong)scp.SCp.Message;
+        }
+        break;
+
+      case GDTIOCTL_DRVERS:
+        id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+        if (id == -1)
+            return(-EBUSY);
+        piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+        piord->size = sizeof(gdth_iord_str);
+        piord->status = S_OK;
+        piord->iu.drvers.version = (GDTH_VERSION<<8) | GDTH_SUBVERSION;
+        break;
+
+      case GDTIOCTL_CTRTYPE:
+        id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+        if (id == -1)
+            return(-EBUSY);
+        piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+        piord->size = sizeof(gdth_iord_str);
+        piord->status = S_OK;
+        if (ha->type == GDT_ISA || ha->type == GDT_EISA) {
+            piord->iu.ctrtype.type = (unchar)((ha->stype>>20) - 10);
+        } else if (ha->type != GDT_PCIMPR) {
+            piord->iu.ctrtype.type = (unchar)((ha->stype<<8) + 6);
+        } else {
+            piord->iu.ctrtype.type = 0xfe;
+            piord->iu.ctrtype.ext_type = 0x6000 | ha->stype;
+        }
+        piord->iu.ctrtype.info = ha->brd_phys;
+        piord->iu.ctrtype.oem_id = (ushort)GDT3_ID;
+        break;
+
+      case GDTIOCTL_CTRCNT:
+        id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+        if (id == -1)
+            return(-EBUSY);
+        piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+        piord->size = sizeof(gdth_iord_str);
+        piord->status = S_OK;
+        piord->iu.ctrcnt.count = (ushort)gdth_ctr_count;
+        break;
+
+      case GDTIOCTL_OSVERS:
+        id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+        if (id == -1)
+            return(-EBUSY);
+        piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+        piord->size = sizeof(gdth_iord_str);
+        piord->status = S_OK;
+        piord->iu.osvers.version = (unchar)(LINUX_VERSION_CODE >> 16);
+        piord->iu.osvers.subversion = (unchar)(LINUX_VERSION_CODE >> 8);
+        piord->iu.osvers.revision = (ushort)(LINUX_VERSION_CODE & 0xff);
+        break;
+
+      case GDTIOCTL_LOCKDRV:
+        id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+        if (id == -1)
+            return(-EBUSY);
+        piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+        for (i = k = 0; i < piowr->iu.lockdrv.drive_cnt; ++i) {
+            found = FALSE;
+            for (j = 0; j < ha->bus_cnt; ++j) {
+                for (k = 0; k < MAXID; ++k) {
+                    if (ha->id[j][k].type == CACHE_DTYP &&
+                        ha->id[j][k].hostdrive == piowr->iu.lockdrv.drives[i]) {
+                        found = TRUE;
+                        break;
+                    }
+                }
+                if (found)
+                    break;
+            }
+            if (!found)
+                continue;
+
+            if (piowr->iu.lockdrv.lock) {
+                save_flags( flags );
+                cli();
+                ha->id[j][k].lock = 1;
+                restore_flags( flags );
+                gdth_wait_completion( hanum, j, k );
+                gdth_stop_timeout( hanum, j, k );
+            } else {
+                save_flags( flags );
+                cli();
+                ha->id[j][k].lock = 0;
+                restore_flags( flags );
+                gdth_start_timeout( hanum, j, k );
+                gdth_next( hanum );
+            }
+        }
+        piord->size = sizeof(gdth_iord_str);
+        piord->status = S_OK;
+        break;
+
+      case GDTIOCTL_LOCKCHN:
+        id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+        if (id == -1)
+            return(-EBUSY);
+        for (k = 0, j = piowr->iu.lockchn.channel; k < MAXID; ++k) {
+            if (ha->id[j][k].type != RAW_DTYP)
+                continue;
+
+            if (piowr->iu.lockchn.lock) {
+                save_flags( flags );
+                cli();
+                ha->id[j][k].lock = 1;
+                restore_flags( flags );
+                gdth_wait_completion( hanum, j, k );
+                gdth_stop_timeout( hanum, j, k );
+            } else {
+                save_flags( flags );
+                cli();
+                ha->id[j][k].lock = 0;
+                restore_flags( flags );
+                gdth_start_timeout( hanum, j, k );
+                gdth_next( hanum );
+            }
+        }
+        piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+        piord->size = sizeof(gdth_iord_str);
+        piord->status = S_OK;
+        break;
+
+      case GDTIOCTL_EVENT:
+        id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+        if (id == -1)
+            return(-EBUSY);
+        piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+        if (piowr->iu.event.erase == 0) {
+            piord->iu.event.handle = gdth_read_event( piowr->iu.event.handle,
+                (gdth_evt_str *)piord->iu.event.evt );
+        } else {
+            piord->iu.event.handle = piowr->iu.event.handle;
+            gdth_readapp_event( (unchar)piowr->iu.event.erase,
+                (gdth_evt_str *)piord->iu.event.evt );
+        }
+        piord->size = sizeof(gdth_iord_str);
+        piord->status = S_OK;
+        break;
+
+      default:
+        return(-EINVAL);
+    }
+    /* we return a buffer ID to detect the right buffer during READ-IOCTL */
+    return id;
+}
+
+static int gdth_get_info(char *buffer,char **start,off_t offset,
+                         int length,int vh,int hanum,int busnum)
+{
+    int size = 0,len = 0;
+    off_t begin = 0,pos = 0;
+    gdth_ha_str *ha;
+    gdth_iord_str *piord;
+    int id;
+
+    TRACE2(("gdth_get_info() ha %d bus %d\n",hanum,busnum));
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    id = length;
+
+    /* look for buffer ID in length */
+    if (id > 4) {
+#if LINUX_VERSION_CODE >= 0x010400
+        size = sprintf(buffer+len,
+                       "%s SCSI Disk Array Controller\n",
+                       gdth_ctr_name(hanum));
+#else
+        size = sprintf(buffer+len,
+                       "%s SCSI Disk Array Controller (SCSI Bus %d)\n",
+                       gdth_ctr_name(hanum),busnum);
+#endif
+        len += size;  pos = begin + len;
+        size = sprintf(buffer+len,
+                       "Firmware Version: %d.%2d\tDriver Version: %s\n",
+                       (unchar)(ha->cpar.version>>8),
+                       (unchar)(ha->cpar.version),GDTH_VERSION_STR);
+        len += size;  pos = begin + len;
+ 
+        if (pos < offset) {
+            len = 0;
+            begin = pos;
+        }
+        if (pos > offset + length)
+            goto stop_output;
+
+    } else {
+        piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+        if (piord == NULL)
+            goto stop_output;
+        length = piord->size;
+        memcpy(buffer+len, (char *)piord, length);
+        gdth_ioctl_free(hanum, id);
+        len += length; pos = begin + len;
+
+        if (pos < offset) {
+            len = 0;
+            begin = pos;
+        }
+        if (pos > offset + length)
+            goto stop_output;
+    }
+
+stop_output:
+    *start = buffer +(offset-begin);
+    len -= (offset-begin);
+    if (len > length)
+        len = length;
+    TRACE2(("get_info() len %d pos %d begin %d offset %d length %d size %d\n",
+            len,pos,begin,offset,length,size));
+    return(len);
+}
+
+
+void gdth_scsi_done(Scsi_Cmnd *scp)
+{
+    TRACE2(("gdth_scsi_done()\n"));
+
+    scp->request.rq_status = RQ_SCSI_DONE;
+
+    if (scp->request.sem != NULL)
+        up(scp->request.sem);
+}
+
+static int gdth_ioctl_alloc(int hanum, ushort size)
+{
+    ulong flags;
+    int i;
+
+    if (size == 0)
+        return -1;
+
+    save_flags(flags);
+    cli();
+
+    for (i = 0; i < 4; ++i) {
+        if (gdth_ioctl_tab[i][hanum] == NULL) {
+            gdth_ioctl_tab[i][hanum] = kmalloc( size, GFP_ATOMIC | GFP_DMA );
+            break;
+        }
+    }
+
+    restore_flags(flags);
+    if (i == 4 || gdth_ioctl_tab[i][hanum] == NULL)
+        return -1;
+    return (i+1);
+}
+
+static void gdth_ioctl_free(int hanum, int idx)
+{
+    ulong flags;
+
+    save_flags(flags);
+    cli();
+
+    kfree( gdth_ioctl_tab[idx-1][hanum] );
+    gdth_ioctl_tab[idx-1][hanum] = NULL;
+
+    restore_flags(flags);
+}
+
+static void gdth_wait_completion(int hanum, int busnum, int id)
+{
+    ulong flags;
+    int i;
+    Scsi_Cmnd *scp;
+
+    save_flags(flags);
+    cli();
+
+    for (i = 0; i < GDTH_MAXCMDS; ++i) {
+        scp = gdth_cmd_tab[i][hanum].cmnd;
+        if (!SPECIAL_SCP(scp) && scp->target == (unchar)id &&
+#if LINUX_VERSION_CODE >= 0x010400
+            scp->channel == (unchar)busnum)
+#else
+            NUMDATA(scp->host)->busnum == (unchar)busnum)
+#endif
+        {
+            restore_flags(flags);
+            while (!scp->SCp.have_data_in)
+                barrier();
+            save_flags(flags);
+            cli();
+        }
+    }
+    restore_flags(flags);
+}
+
+static void gdth_stop_timeout(int hanum, int busnum, int id)
+{
+    ulong flags;
+    Scsi_Cmnd *scp;
+    gdth_ha_str *ha;
+
+    save_flags(flags);
+    cli();
+    ha = HADATA(gdth_ctr_tab[hanum]);
+
+    for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) {
+        if (scp->target == (unchar)id &&
+#if LINUX_VERSION_CODE >= 0x010400
+            scp->channel == (unchar)busnum)
+#else
+            NUMDATA(scp->host)->busnum == (unchar)busnum)
+#endif
+        {
+            TRACE2(("gdth_stop_timeout(): update_timeout()\n"));
+            scp->SCp.buffers_residual = gdth_update_timeout(scp, 0);
+        }
+    }
+    restore_flags(flags);
+}
+
+static void gdth_start_timeout(int hanum, int busnum, int id)
+{
+    ulong flags;
+    Scsi_Cmnd *scp;
+    gdth_ha_str *ha;
+
+    save_flags(flags);
+    cli();
+    ha = HADATA(gdth_ctr_tab[hanum]);
+
+    for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) {
+        if (scp->target == (unchar)id &&
+#if LINUX_VERSION_CODE >= 0x010400
+            scp->channel == (unchar)busnum)
+#else
+            NUMDATA(scp->host)->busnum == (unchar)busnum)
+#endif
+        {
+            TRACE2(("gdth_start_timeout(): update_timeout()\n"));
+            gdth_update_timeout(scp, scp->SCp.buffers_residual);
+        }
+    }
+    restore_flags(flags);
+}
+
+static int gdth_update_timeout(Scsi_Cmnd *scp, int timeout)
+{
+    ulong flags;
+    int oldto;
+
+    save_flags(flags);
+    cli();
+
+    oldto = scp->timeout;
+    scp->timeout = timeout;
+    if (timeout > 0) {
+        if (timer_table[SCSI_TIMER].expires == 0) {
+            timer_table[SCSI_TIMER].expires = jiffies + timeout;
+            timer_active |= 1 << SCSI_TIMER;
+        } else {
+            if (jiffies + timeout < timer_table[SCSI_TIMER].expires)
+                timer_table[SCSI_TIMER].expires = jiffies + timeout;
+        }
+    }
+
+    restore_flags(flags);
+    return oldto;
+}
+
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/gdth_proc.h linux/drivers/scsi/gdth_proc.h
--- v2.0.30/linux/drivers/scsi/gdth_proc.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/gdth_proc.h	Mon Aug  4 17:15:07 1997
@@ -0,0 +1,24 @@
+#ifndef _GDTH_PROC_H
+#define _GDTH_PROC_H
+
+/* gdth_proc.h 
+ * $Id: gdth_proc.h,v 1.2 1997/02/21 08:08:51 achim Exp $
+ */
+
+static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum);
+static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp);
+static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp);
+static int gdth_get_info(char *buffer,char **start,off_t offset,
+                         int length,int vh,int hanum,int busnum);
+
+static int gdth_ioctl_alloc(int hanum, ushort size);
+static void gdth_ioctl_free(int hanum, int id);
+static void gdth_wait_completion(int hanum, int busnum, int id);
+static void gdth_stop_timeout(int hanum, int busnum, int id);
+static void gdth_start_timeout(int hanum, int busnum, int id);
+static int gdth_update_timeout(Scsi_Cmnd *scp, int timeout);
+
+void gdth_scsi_done(Scsi_Cmnd *scp);
+
+#endif
+
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- v2.0.30/linux/drivers/scsi/hosts.c	Wed Oct 30 02:45:53 1996
+++ linux/drivers/scsi/hosts.c	Thu Aug 14 10:31:20 1997
@@ -153,6 +153,10 @@
 #include "NCR53c406a.h"
 #endif
 
+#ifdef CONFIG_SCSI_DC390T
+#include "dc390.h"
+#endif
+
 #ifdef CONFIG_SCSI_AM53C974
 #include "AM53C974.h"
 #endif
@@ -165,6 +169,14 @@
 #include "esp.h"
 #endif
 
+#ifdef CONFIG_BLK_DEV_IDESCSI
+#include "ide-scsi.h"
+#endif
+
+#ifdef CONFIG_SCSI_GDTH
+#include "gdth.h"
+#endif
+
 #ifdef CONFIG_SCSI_DEBUG
 #include "scsi_debug.h"
 #endif
@@ -276,6 +288,9 @@
 #ifdef CONFIG_SCSI_DTC3280
         DTC3x80,
 #endif
+#ifdef CONFIG_SCSI_DC390T
+    DC390_T,
+#endif
 #ifdef CONFIG_SCSI_NCR53C7xx
     NCR53c7xx,
 #endif
@@ -302,6 +317,12 @@
 #endif
 #ifdef CONFIG_SCSI_SUNESP
     SCSI_SPARC_ESP,
+#endif
+#ifdef CONFIG_SCSI_GDTH
+    GDTH,
+#endif
+#ifdef CONFIG_BLK_DEV_IDESCSI
+    IDESCSI,
 #endif
 #ifdef CONFIG_SCSI_DEBUG
     SCSI_DEBUG,
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/ide-scsi.c linux/drivers/scsi/ide-scsi.c
--- v2.0.30/linux/drivers/scsi/ide-scsi.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/ide-scsi.c	Mon Aug  4 11:45:55 1997
@@ -0,0 +1,601 @@
+/*
+ * linux/drivers/scsi/ide-scsi.c	Version 0.3 - ALPHA	Jul  24, 1997
+ *
+ * Copyright (C) 1996, 1997 Gadi Oxman <gadio@netvision.net.il>
+ */
+
+/*
+ * Emulation of a SCSI host adapter for IDE ATAPI devices.
+ *
+ * With this driver, one can use the Linux SCSI drivers instead of the
+ * native IDE ATAPI drivers.
+ *
+ * Ver 0.1   Dec  3 96   Initial version.
+ * Ver 0.2   Jan 26 97   Fixed bug in cleanup_module() and added emulation
+ *                        of MODE_SENSE_6/MODE_SELECT_6 for cdroms. Thanks
+ *                        to Janos Farkas for pointing this out.
+ *                       Avoid using bitfields in structures for m68k.
+ *                       Added Scather/Gather and DMA support.
+ * Ver 0.3   Jul 24 97   Add support for ATAPI PD/CD drives.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/malloc.h>
+
+#include <asm/io.h>
+#include <asm/bitops.h>
+
+#include "../block/ide.h"
+
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+#include "ide-scsi.h"
+
+#define IDESCSI_DEBUG_LOG		0
+
+typedef struct idescsi_pc_s {
+	u8 c[12];				/* Actual packet bytes */
+	int request_transfer;			/* Bytes to transfer */
+	int actually_transferred;		/* Bytes actually transferred */
+	int buffer_size;			/* Size of our data buffer */
+	struct request *rq;			/* The corresponding request */
+	byte *buffer;				/* Data buffer */
+	byte *current_position;			/* Pointer into the above buffer */
+	struct scatterlist *sg;			/* Scather gather table */
+	int b_count;				/* Bytes transferred from current entry */
+	Scsi_Cmnd *scsi_cmd;			/* SCSI command */
+	void (*done)(Scsi_Cmnd *);		/* Scsi completion routine */
+	unsigned int flags;			/* Status/Action flags */
+} idescsi_pc_t;
+
+/*
+ *	Packet command status bits.
+ */
+#define PC_DMA_IN_PROGRESS		0	/* 1 while DMA in progress */
+#define PC_WRITING			1	/* Data direction */
+
+typedef struct {
+	ide_drive_t *drive;
+	idescsi_pc_t *pc;			/* Current packet command */
+	unsigned int flags;			/* Status/Action flags */
+	int media;
+} idescsi_scsi_t;
+
+/*
+ *	Per ATAPI device status bits.
+ */
+#define IDESCSI_DRQ_INTERRUPT		0	/* DRQ interrupt device */
+
+/*
+ *	ide-scsi requests.
+ */
+#define IDESCSI_PC_RQ			90
+
+/*
+ *	Bits of the interrupt reason register.
+ */
+#define IDESCSI_IREASON_COD	0x1		/* Information transferred is command */
+#define IDESCSI_IREASON_IO	0x2		/* The device requests us to read */
+
+#define IDE_MIN(a,b)      ((a)<(b) ? (a):(b))
+#define IDE_MAX(a,b)      ((a)>(b) ? (a):(b))
+
+static void idescsi_discard_data (ide_drive_t *drive, unsigned int bcount)
+{
+	while (bcount--)
+		IN_BYTE (IDE_DATA_REG);
+}
+
+static void idescsi_output_zeros (ide_drive_t *drive, unsigned int bcount)
+{
+	while (bcount--)
+		OUT_BYTE (0, IDE_DATA_REG);
+}
+
+/*
+ *	PIO data transfer routines using the scather gather table.
+ */
+static void idescsi_input_buffers (ide_drive_t *drive, idescsi_pc_t *pc, unsigned int bcount)
+{
+	int count;
+
+	while (bcount) {
+		if (pc->sg - (struct scatterlist *) pc->scsi_cmd->request_buffer > pc->scsi_cmd->use_sg) {
+			printk (KERN_ERR "ide-scsi: scather gather table too small, discarding data\n");
+			idescsi_discard_data (drive, bcount);
+			return;
+		}
+		count = IDE_MIN (pc->sg->length - pc->b_count, bcount);
+		atapi_input_bytes (drive, pc->sg->address + pc->b_count, count);
+		bcount -= count; pc->b_count += count;
+		if (pc->b_count == pc->sg->length) {
+			pc->sg++;
+			pc->b_count = 0;
+		}
+	}
+}
+
+static void idescsi_output_buffers (ide_drive_t *drive, idescsi_pc_t *pc, unsigned int bcount)
+{
+	int count;
+
+	while (bcount) {
+		if (pc->sg - (struct scatterlist *) pc->scsi_cmd->request_buffer > pc->scsi_cmd->use_sg) {
+			printk (KERN_ERR "ide-scsi: scather gather table too small, padding with zeros\n");
+			idescsi_output_zeros (drive, bcount);
+			return;
+		}
+		count = IDE_MIN (pc->sg->length - pc->b_count, bcount);
+		atapi_output_bytes (drive, pc->sg->address + pc->b_count, count);
+		bcount -= count; pc->b_count += count;
+		if (pc->b_count == pc->sg->length) {
+			pc->sg++;
+			pc->b_count = 0;
+		}
+	}
+}
+
+/*
+ *	Most of the SCSI commands are supported directly by ATAPI devices.
+ *	idescsi_transform_pc handles the few exceptions.
+ */
+static inline void idescsi_transform_pc1 (ide_drive_t *drive, idescsi_pc_t *pc)
+{
+	idescsi_scsi_t *scsi = drive->scsi;
+	u8 *c = pc->c, *buf = pc->buffer, *sc = pc->scsi_cmd->cmnd;
+	int i;
+
+	if (scsi->media == TYPE_ROM) {
+		if (c[0] == READ_6 || c[0] == WRITE_6) {
+			c[8] = c[4];		c[5] = c[3];		c[4] = c[2];
+			c[3] = c[1] & 0x1f;	c[2] = 0;		c[1] &= 0xe0;
+			c[0] += (READ_10 - READ_6);
+		}
+		if (c[0] == MODE_SENSE || (c[0] == MODE_SELECT && buf[3] == 8)) {
+			pc->request_transfer -= 4;
+			memset (c, 0, 12);
+			c[0] = sc[0] | 0x40;	c[2] = sc[2];		c[8] = sc[4] - 4;
+			if (c[0] == MODE_SENSE_10) return;
+			for (i = 0; i <= 7; i++) buf[i] = 0;
+			for (i = 8; i < pc->buffer_size - 4; i++) buf[i] = buf[i + 4];
+		}
+	}
+}
+
+static inline void idescsi_transform_pc2 (ide_drive_t *drive, idescsi_pc_t *pc)
+{
+	idescsi_scsi_t *scsi = drive->scsi;
+	u8 *buf = pc->buffer;
+	int i;
+
+	if (scsi->media == TYPE_ROM) {
+		if (pc->c[0] == MODE_SENSE_10 && pc->scsi_cmd->cmnd[0] == MODE_SENSE) {
+			buf[0] = buf[1];	buf[1] = buf[2];
+			buf[2] = 0;		buf[3] = 8;
+			for (i = pc->buffer_size - 1; i >= 12; i--) buf[i] = buf[i - 4];
+			for (i = 11; i >= 4; i--) buf[i] = 0;
+		}
+		if (pc->c[0] == INQUIRY)
+			buf[2] |= 2;
+	}
+}
+
+static inline void idescsi_free_bh (struct buffer_head *bh)
+{
+	struct buffer_head *bhp;
+
+	while (bh) {
+		bhp = bh;
+		bh = bh->b_reqnext;
+		kfree (bhp);
+	}
+}
+
+void idescsi_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
+{
+	ide_drive_t *drive = hwgroup->drive;
+	idescsi_scsi_t *scsi = drive->scsi;
+	struct request *rq = hwgroup->rq;
+	idescsi_pc_t *pc = (idescsi_pc_t *) rq->buffer;
+
+	if (rq->cmd != IDESCSI_PC_RQ) {
+		ide_end_request (uptodate, hwgroup);
+		return;
+	}
+	ide_end_drive_cmd (drive, 0, 0);
+	if (rq->errors >= ERROR_MAX) {
+#if IDESCSI_DEBUG_LOG
+		printk ("ide-scsi: %s: I/O error for %lu\n", drive->name, pc->scsi_cmd->serial_number);
+#endif /* IDESCSI_DEBUG_LOG */
+		pc->scsi_cmd->result = DID_ERROR << 16;
+	} else if (rq->errors) {
+#if IDESCSI_DEBUG_LOG
+		printk ("ide-scsi: %s: check condition for %lu\n", drive->name, pc->scsi_cmd->serial_number);
+#endif /* IDESCSI_DEBUG_LOG */
+		pc->scsi_cmd->result = (CHECK_CONDITION << 1) | (DID_OK << 16);
+	} else {
+#if IDESCSI_DEBUG_LOG
+		printk ("ide-scsi: %s: success for %lu\n", drive->name, pc->scsi_cmd->serial_number);
+#endif /* IDESCSI_DEBUG_LOG */
+		pc->scsi_cmd->result = DID_OK << 16;
+		idescsi_transform_pc2 (drive, pc);
+	}
+	pc->done(pc->scsi_cmd);
+	idescsi_free_bh (rq->bh);
+	kfree(pc); kfree(rq);
+	scsi->pc = NULL;
+}
+
+/*
+ *	Our interrupt handler.
+ */
+static void idescsi_pc_intr (ide_drive_t *drive)
+{
+	idescsi_scsi_t *scsi = drive->scsi;
+	byte status, ireason;
+	int bcount;
+	idescsi_pc_t *pc=scsi->pc;
+	struct request *rq = pc->rq;
+	unsigned int temp;
+
+#if IDESCSI_DEBUG_LOG
+	printk (KERN_INFO "ide-scsi: Reached idescsi_pc_intr interrupt handler\n");
+#endif /* IDESCSI_DEBUG_LOG */
+
+	if (clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
+#if IDESCSI_DEBUG_LOG
+		printk ("ide-scsi: %s: DMA complete\n", drive->name);
+#endif /* IDESCSI_DEBUG_LOG */
+		pc->actually_transferred=pc->request_transfer;
+		(void) (HWIF(drive)->dmaproc(ide_dma_abort, drive));
+	}
+
+	status = GET_STAT();						/* Clear the interrupt */
+
+	if ((status & DRQ_STAT) == 0) {					/* No more interrupts */
+#if IDESCSI_DEBUG_LOG
+		printk (KERN_INFO "Packet command completed, %d bytes transferred\n", pc->actually_transferred);
+#endif /* IDESCSI_DEBUG_LOG */
+		sti();
+		if (status & ERR_STAT)
+			rq->errors++;
+		idescsi_end_request (1, HWGROUP(drive));
+		return;
+	}
+	bcount = IN_BYTE (IDE_BCOUNTH_REG) << 8 | IN_BYTE (IDE_BCOUNTL_REG);
+	ireason = IN_BYTE (IDE_IREASON_REG);
+
+	if (ireason & IDESCSI_IREASON_COD) {
+		printk (KERN_ERR "ide-scsi: CoD != 0 in idescsi_pc_intr\n");
+		ide_do_reset (drive);
+		return;
+	}
+	if (ireason & IDESCSI_IREASON_IO) {
+		temp = pc->actually_transferred + bcount;
+		if ( temp > pc->request_transfer) {
+			if (temp > pc->buffer_size) {
+				printk (KERN_ERR "ide-scsi: The scsi wants to send us more data than expected - discarding data\n");
+				idescsi_discard_data (drive,bcount);
+				ide_set_handler (drive,&idescsi_pc_intr,WAIT_CMD);
+				return;
+			}
+#if IDESCSI_DEBUG_LOG
+			printk (KERN_NOTICE "ide-scsi: The scsi wants to send us more data than expected - allowing transfer\n");
+#endif /* IDESCSI_DEBUG_LOG */
+		}
+	}
+	if (ireason & IDESCSI_IREASON_IO) {
+		if (pc->sg)
+			idescsi_input_buffers (drive, pc, bcount);
+		else
+			atapi_input_bytes (drive,pc->current_position,bcount);
+	} else {
+		if (pc->sg)
+			idescsi_output_buffers (drive, pc, bcount);
+		else
+			atapi_output_bytes (drive,pc->current_position,bcount);
+	}
+	pc->actually_transferred+=bcount;				/* Update the current position */
+	pc->current_position+=bcount;
+
+	ide_set_handler (drive,&idescsi_pc_intr,WAIT_CMD);		/* And set the interrupt handler again */
+}
+
+static void idescsi_transfer_pc (ide_drive_t *drive)
+{
+	idescsi_scsi_t *scsi = drive->scsi;
+	byte ireason;
+
+	if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
+		printk (KERN_ERR "ide-scsi: Strange, packet command initiated yet DRQ isn't asserted\n");
+		return;
+	}
+	ireason = IN_BYTE (IDE_IREASON_REG);
+	if ((ireason & (IDESCSI_IREASON_IO | IDESCSI_IREASON_COD)) != IDESCSI_IREASON_COD) {
+		printk (KERN_ERR "ide-scsi: (IO,CoD) != (0,1) while issuing a packet command\n");
+		ide_do_reset (drive);
+		return;
+	}
+	ide_set_handler (drive, &idescsi_pc_intr, WAIT_CMD);	/* Set the interrupt routine */
+	atapi_output_bytes (drive, scsi->pc->c, 12);		/* Send the actual packet */
+}
+
+/*
+ *	Issue a packet command
+ */
+static void idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *pc)
+{
+	idescsi_scsi_t *scsi = drive->scsi;
+	int bcount;
+	struct request *rq = pc->rq;
+	int dma_ok = 0;
+
+	scsi->pc=pc;							/* Set the current packet command */
+	pc->actually_transferred=0;					/* We haven't transferred any data yet */
+	pc->current_position=pc->buffer;
+	bcount = IDE_MIN (pc->request_transfer, 63 * 1024);		/* Request to transfer the entire buffer at once */
+
+	if (drive->using_dma && rq->bh)
+		dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive);
+
+	OUT_BYTE (drive->ctl,IDE_CONTROL_REG);
+	OUT_BYTE (dma_ok,IDE_FEATURE_REG);
+	OUT_BYTE (bcount >> 8,IDE_BCOUNTH_REG);
+	OUT_BYTE (bcount & 0xff,IDE_BCOUNTL_REG);
+	OUT_BYTE (drive->select.all,IDE_SELECT_REG);
+
+	if (dma_ok) {
+		set_bit (PC_DMA_IN_PROGRESS, &pc->flags);
+		(void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
+	}
+	if (test_bit (IDESCSI_DRQ_INTERRUPT, &scsi->flags)) {
+		ide_set_handler (drive, &idescsi_transfer_pc, WAIT_CMD);
+		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);		/* Issue the packet command */
+	} else {
+		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);
+		idescsi_transfer_pc (drive);
+	}
+}
+
+/*
+ *	idescsi_do_request is our request handling function.
+ */
+void idescsi_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+#if IDESCSI_DEBUG_LOG
+	printk (KERN_INFO "rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors);
+	printk (KERN_INFO "sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
+#endif /* IDESCSI_DEBUG_LOG */
+
+	if (rq->cmd == IDESCSI_PC_RQ) {
+		idescsi_issue_pc (drive, (idescsi_pc_t *) rq->buffer);
+		return;
+	}
+	printk (KERN_ERR "ide-scsi: %s: unsupported command in request queue (%x)\n", drive->name, rq->cmd);
+	idescsi_end_request (0,HWGROUP (drive));
+}
+
+int idescsi_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+void idescsi_ide_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+static ide_drive_t *idescsi_drives[MAX_HWIFS * MAX_DRIVES];
+static int idescsi_initialized = 0;
+
+int idescsi_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	return -EINVAL;
+}
+
+static struct proc_dir_entry idescsi_proc_dir = {PROC_SCSI_IDESCSI, 8, "ide-scsi", S_IFDIR | S_IRUGO | S_IXUGO, 2};
+
+/*
+ *	idescsi_setup will register the driver for each scsi.
+ */
+void idescsi_setup (ide_drive_t *drive)
+{
+	idescsi_scsi_t *scsi;
+	int i, id;
+
+	if (!idescsi_initialized) {
+		idescsi_initialized = 1;
+		for (i = 0; i < MAX_HWIFS * MAX_DRIVES; i++)
+			idescsi_drives[i] = NULL;
+	}
+	if ((scsi = (idescsi_scsi_t *) kmalloc (sizeof (idescsi_scsi_t), GFP_KERNEL)) == NULL) {
+		printk (KERN_ERR "ide-scsi: %s: Can't allocate a scsi structure\n", drive->name);
+		return;
+	}
+	for (id = 0; id < MAX_HWIFS * MAX_DRIVES && idescsi_drives[id]; id++);
+	idescsi_drives[id] = drive;
+	drive->scsi = scsi;
+	drive->ready_stat = 0;
+	memset (scsi, 0, sizeof (idescsi_scsi_t));
+	scsi->drive = drive;
+	scsi->media = (drive->id->config >> 8) & 0x1f;
+	if (drive->id && (drive->id->config & 0x0060) == 0x20)
+		set_bit (IDESCSI_DRQ_INTERRUPT, &scsi->flags);
+}
+
+int idescsi_detect (Scsi_Host_Template *host_template)
+{
+	struct Scsi_Host *host;
+	int id;
+
+	host_template->proc_dir = &idescsi_proc_dir;
+	host = scsi_register(host_template, 0);
+	for (id = 0; id < MAX_HWIFS * MAX_DRIVES && idescsi_drives[id]; id++);
+	host->max_id = id;
+	host->can_queue = host->cmd_per_lun * id;
+	return 1;
+}
+
+int idescsi_release (struct Scsi_Host *host)
+{
+	return 0;
+}
+
+const char *idescsi_info (struct Scsi_Host *host)
+{
+	return "SCSI host adapter emulation for IDE ATAPI devices";
+}
+
+static inline struct buffer_head *idescsi_kmalloc_bh (int count)
+{
+	struct buffer_head *bh, *bhp, *first_bh;
+
+	if ((first_bh = bhp = bh = kmalloc (sizeof(struct buffer_head), GFP_ATOMIC)) == NULL)
+		goto abort;
+	memset (bh, 0, sizeof (struct buffer_head));
+	bh->b_reqnext = NULL;
+	while (--count) {
+		if ((bh = kmalloc (sizeof(struct buffer_head), GFP_ATOMIC)) == NULL)
+			goto abort;
+		memset (bh, 0, sizeof (struct buffer_head));
+		bhp->b_reqnext = bh;
+		bhp = bh;
+		bh->b_reqnext = NULL;
+	}
+	return first_bh;
+abort:
+	idescsi_free_bh (first_bh);
+	return NULL;
+}
+
+static inline int idescsi_set_direction (idescsi_pc_t *pc)
+{
+	switch (pc->c[0]) {
+		case READ_6: case READ_10: case READ_12:
+			clear_bit (PC_WRITING, &pc->flags);
+			return 0;
+		case WRITE_6: case WRITE_10: case WRITE_12:
+			set_bit (PC_WRITING, &pc->flags);
+			return 0;
+		default:
+			return 1;
+	}
+}
+
+static inline struct buffer_head *idescsi_dma_bh (ide_drive_t *drive, idescsi_pc_t *pc)
+{
+	struct buffer_head *bh = NULL, *first_bh = NULL;
+	int segments = pc->scsi_cmd->use_sg;
+	struct scatterlist *sg = pc->scsi_cmd->request_buffer;
+
+	if (!drive->using_dma || pc->request_transfer % 1024)
+		return NULL;
+	if (idescsi_set_direction(pc))
+		return NULL;
+	if (segments) {
+		if ((first_bh = bh = idescsi_kmalloc_bh (segments)) == NULL)
+			return NULL;
+#if IDESCSI_DEBUG_LOG
+		printk ("ide-scsi: %s: building DMA table, %d segments, %dkB total\n", drive->name, segments, pc->request_transfer >> 10);
+#endif /* IDESCSI_DEBUG_LOG */
+		while (segments--) {
+			bh->b_data = sg->address;
+			bh->b_size = sg->length;
+			bh = bh->b_reqnext;
+			sg++;
+		}
+	} else {
+		if ((first_bh = bh = idescsi_kmalloc_bh (1)) == NULL)
+			return NULL;
+#if IDESCSI_DEBUG_LOG
+		printk ("ide-scsi: %s: building DMA table for a single buffer (%dkB)\n", drive->name, pc->request_transfer >> 10);
+#endif /* IDESCSI_DEBUG_LOG */
+		bh->b_data = pc->scsi_cmd->request_buffer;
+		bh->b_size = pc->request_transfer;
+	}
+	return first_bh;
+}
+
+int idescsi_queue (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+{
+	ide_drive_t *drive = idescsi_drives[cmd->target];
+	struct request *rq = NULL;
+	idescsi_pc_t *pc = NULL;
+
+#if IDESCSI_DEBUG_LOG
+	printk ("idescsi_queue called, serial = %lu, cmd[0] = %x, id = %d\n", cmd->serial_number, cmd->cmnd[0], cmd->target);
+#endif	/* IDESCSI_DEBUG_LOG */
+
+	if (!drive) {
+		printk (KERN_ERR "ide-scsi: drive id %d not present\n", cmd->target);
+		goto abort;
+	}
+	pc = kmalloc (sizeof (idescsi_pc_t), GFP_ATOMIC);
+	rq = kmalloc (sizeof (struct request), GFP_ATOMIC);
+	if (rq == NULL || pc == NULL) {
+		printk (KERN_ERR "ide-scsi: %s: out of memory\n", drive->name);
+		goto abort;
+	}
+
+	memset (pc->c, 0, 12);
+	pc->flags = 0;
+	pc->rq = rq;
+	memcpy (pc->c, cmd->cmnd, cmd->cmd_len);
+	if (cmd->use_sg) {
+		pc->buffer = NULL;
+		pc->sg = cmd->request_buffer;
+	} else {
+		pc->buffer = cmd->request_buffer;
+		pc->sg = NULL;
+	}
+	pc->b_count = 0;
+	pc->request_transfer = pc->buffer_size = cmd->request_bufflen;
+	pc->scsi_cmd = cmd;
+	pc->done = done;
+	idescsi_transform_pc1 (drive, pc);
+
+	ide_init_drive_cmd (rq);
+	rq->buffer = (char *) pc;
+	rq->bh = idescsi_dma_bh (drive, pc);
+	rq->cmd = IDESCSI_PC_RQ;
+	(void) ide_do_drive_cmd (drive, rq, ide_end);
+	return 0;
+abort:
+	if (pc) kfree (pc);
+	if (rq) kfree (rq);
+	cmd->result = DID_ERROR << 16;
+	done(cmd);
+	return 0;
+}
+
+int idescsi_abort (Scsi_Cmnd *cmd)
+{
+	return SCSI_ABORT_SNOOZE;
+}
+
+int idescsi_reset (Scsi_Cmnd *cmd, unsigned int resetflags)
+{
+	return SCSI_RESET_PUNT;
+}
+
+int idescsi_bios (Disk *disk, kdev_t dev, int *parm)
+{
+	ide_drive_t *drive = idescsi_drives[disk->device->id];
+
+	if (drive->cyl && drive->head && drive->sect) {
+		parm[0] = drive->head;
+		parm[1] = drive->sect;
+		parm[2] = drive->cyl;
+	}
+	return 0;
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/ide-scsi.h linux/drivers/scsi/ide-scsi.h
--- v2.0.30/linux/drivers/scsi/ide-scsi.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/ide-scsi.h	Mon Aug  4 11:45:55 1997
@@ -0,0 +1,42 @@
+/*
+ * linux/drivers/scsi/ide-scsi.h
+ *
+ * Copyright (C) 1996, 1997 Gadi Oxman <gadio@netvision.net.il>
+ */
+
+#ifndef IDESCSI_H
+#define IDESCSI_H
+
+extern int idescsi_detect (Scsi_Host_Template *host_template);
+extern int idescsi_release (struct Scsi_Host *host);
+extern const char *idescsi_info (struct Scsi_Host *host);
+extern int idescsi_queue (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *));
+extern int idescsi_abort (Scsi_Cmnd *cmd);
+extern int idescsi_reset (Scsi_Cmnd *cmd, unsigned int resetflags);
+extern int idescsi_bios (Disk *disk, kdev_t dev, int *parm);
+
+#define IDESCSI								\
+{	NULL,			/* next		*/			\
+	NULL,			/* module	*/			\
+	NULL,			/* proc_dir	*/			\
+	NULL,			/* proc_info	*/			\
+	"idescsi",		/* name		*/			\
+	idescsi_detect,		/* detect	*/			\
+	idescsi_release,	/* release	*/			\
+	idescsi_info,		/* info		*/			\
+	NULL,			/* command	*/			\
+	idescsi_queue,		/* queuecommand */			\
+	idescsi_abort,		/* abort	*/			\
+	idescsi_reset,		/* reset	*/			\
+	NULL,			/* slave_attach	*/			\
+	idescsi_bios,		/* bios_param	*/			\
+	10,			/* can_queue	*/			\
+	-1,			/* this_id	*/			\
+	256,			/* sg_tablesize	*/			\
+	5,			/* cmd_per_lun	*/			\
+	0,			/* present	*/			\
+	0,			/* isa_dma	*/			\
+	DISABLE_CLUSTERING	/* clustering	*/			\
+}
+
+#endif /* IDESCSI_H */
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/in2000.c linux/drivers/scsi/in2000.c
--- v2.0.30/linux/drivers/scsi/in2000.c	Thu May  9 06:40:24 1996
+++ linux/drivers/scsi/in2000.c	Tue Aug  5 13:26:17 1997
@@ -104,7 +104,7 @@
  *
  */
 
-
+#include <linux/module.h>
 
 #include <asm/system.h>
 #include <linux/sched.h>
@@ -115,49 +115,44 @@
 #include <linux/ioport.h>
 #include <linux/blkdev.h>
 
-#include "scsi.h"
-#include "sd.h"
-#include "hosts.h"
-#include "in2000.h"
-
 #include <linux/blk.h>
 #include <linux/stat.h>
 
-#ifdef MODULE
-#include <linux/module.h>
-#endif
+#include "scsi.h"
+#include "sd.h"
+#include "hosts.h"
 
 
-#define IN2000_VERSION    "1.28"
-#define IN2000_DATE       "07/May/1996"
+#define IN2000_VERSION    "1.31"
+#define IN2000_DATE       "06/July/1997"
 
-#define PROC_INTERFACE     /* add code for /proc/scsi/in2000/xxx interface */
-#define SYNC_DEBUG         /* extra info on sync negotiation printed */
-#define DEBUGGING_ON       /* enable command-line debugging bitmask */
-#define DEBUG_DEFAULTS 0   /* default bitmask - change from command-line */
+/*
+ * Note - the following defines have been moved to 'in2000.h':
+ *
+ *    PROC_INTERFACE
+ *    PROC_STATISTICS
+ *    SYNC_DEBUG
+ *    DEBUGGING_ON
+ *    DEBUG_DEFAULTS
+ *    FAST_READ_IO
+ *    FAST_WRITE_IO
+ *
+ */
 
-#define FAST_READ_IO       /* No problems with these on my machine */
-#define FAST_WRITE_IO
 
-#ifdef DEBUGGING_ON
-#define DB(f,a) if (hostdata->args & (f)) a;
-#define CHECK_NULL(p,s) /* if (!(p)) {printk("\n"); while (1) printk("NP:%s\r",(s));} */
-#else
-#define DB(f,a)
-#define CHECK_NULL(p,s)
-#endif
+#include "in2000.h"
 
-#define IS_DIR_OUT(cmd) ((cmd)->cmnd[0] == WRITE_6  || \
-                         (cmd)->cmnd[0] == WRITE_10 || \
-                         (cmd)->cmnd[0] == WRITE_12)
 
 
 /*
- * setup_strings is an array of strings that define some of the operating
- * parameters and settings for this driver. It is used unless a LILO
- * or insmod command line has been specified, in which case those settings
- * are combined with the ones here. The driver recognizes the following
- * keywords (lower case required) and arguments:
+ * 'setup_strings' is a single string used to pass operating parameters and
+ * settings from the kernel/module command-line to the driver. 'setup_args[]'
+ * is an array of strings that define the compile-time default values for
+ * these settings. If Linux boots with a LILO or insmod command-line, those
+ * settings are combined with 'setup_args[]'. Note that LILO command-lines
+ * are prefixed with "in2000=" while insmod uses a "setup_strings=" prefix.
+ * The driver recognizes the following keywords (lower case required) and
+ * arguments:
  *
  * -  ioport:addr    -Where addr is IO address of a (usually ROM-less) card.
  * -  noreset        -No optional args. Prevents SCSI bus reset at boot time.
@@ -182,13 +177,11 @@
  *    _must_ be a colon between a keyword and its numeric argument, with no
  *    spaces.
  * -  Keywords are separated by commas, no spaces, in the standard kernel
- *    command-line manner, except in the case of 'setup_strings[]' (see
- *    below), which is simply a C array of pointers to char. Each element
- *    in the array is a string comprising one keyword & argument.
+ *    command-line manner.
  * -  A keyword in the 'nth' comma-separated command-line member will overwrite
- *    the 'nth' element of setup_strings[]. A blank command-line member (in
+ *    the 'nth' element of setup_args[]. A blank command-line member (in
  *    other words, a comma with no preceding keyword) will _not_ overwrite
- *    the corresponding setup_strings[] element.
+ *    the corresponding setup_args[] element.
  *
  * A few LILO examples (for insmod, use 'setup_strings' instead of 'in2000'):
  * -  in2000=ioport:0x220,noreset
@@ -197,57 +190,23 @@
  * -  in2000=proc:3
  */
 
-static char *setup_strings[] =
-      {"","","","","","","","","","","",""};
+/* Normally, no defaults are specified... */
+static char *setup_args[] =
+      {"","","","","","","","",""};
 
-static struct Scsi_Host *instance_list = 0;
+/* filled in by 'insmod' */
+static char *setup_strings = 0;
 
-#ifdef PROC_INTERFACE
-unsigned long disc_allowed_total;
-unsigned long disc_taken_total;
+#ifdef MODULE_PARM
+MODULE_PARM(setup_strings, "s");
 #endif
 
 
-#define read1_io(a)     (inb(hostdata->io_base+(a)))
-#define read2_io(a)     (inw(hostdata->io_base+(a)))
-#define write1_io(b,a)  (outb((b),hostdata->io_base+(a)))
-#define write2_io(w,a)  (outw((w),hostdata->io_base+(a)))
-
-/* These inline assembly defines are derived from a patch
- * sent to me by Bill Earnest. He's done a lot of very
- * valuable thinking, testing, and coding during his effort
- * to squeeze more speed out of this driver. I really think
- * that we are doing IO at close to the maximum now with
- * the fifo. (And yes, insw uses 'edi' while outsw uses
- * 'esi'. Thanks Bill!)
- */
-
-#define FAST_READ2_IO()    \
-   __asm__ __volatile__ ("\n \
-   cld                    \n \
-   orl %%ecx, %%ecx       \n \
-   jz 1f                  \n \
-   rep                    \n \
-   insw %%dx              \n \
-1: "                       \
-   : "=D" (sp)                   /* output */   \
-   : "d" (f), "D" (sp), "c" (i)  /* input */    \
-   : "edx", "ecx", "edi" )       /* trashed */
-
-#define FAST_WRITE2_IO()   \
-   __asm__ __volatile__ ("\n \
-   cld                    \n \
-   orl %%ecx, %%ecx       \n \
-   jz 1f                  \n \
-   rep                    \n \
-   outsw %%dx             \n \
-1: "                       \
-   : "=S" (sp)                   /* output */   \
-   : "d" (f), "S" (sp), "c" (i)  /* input */    \
-   : "edx", "ecx", "esi" )       /* trashed */
+static struct Scsi_Host *instance_list = 0;
+
 
 
-inline uchar read_3393(struct IN2000_hostdata *hostdata, uchar reg_num)
+static inline uchar read_3393(struct IN2000_hostdata *hostdata, uchar reg_num)
 {
    write1_io(reg_num,IO_WD_ADDR);
    return read1_io(IO_WD_DATA);
@@ -257,14 +216,14 @@
 #define READ_AUX_STAT() read1_io(IO_WD_ASR)
 
 
-inline void write_3393(struct IN2000_hostdata *hostdata, uchar reg_num, uchar value)
+static inline void write_3393(struct IN2000_hostdata *hostdata, uchar reg_num, uchar value)
 {
    write1_io(reg_num,IO_WD_ADDR);
    write1_io(value,IO_WD_DATA);
 }
 
 
-inline void write_3393_cmd(struct IN2000_hostdata *hostdata, uchar cmd)
+static inline void write_3393_cmd(struct IN2000_hostdata *hostdata, uchar cmd)
 {
 /*   while (READ_AUX_STAT() & ASR_CIP)
       printk("|");*/
@@ -273,7 +232,7 @@
 }
 
 
-uchar read_1_byte(struct IN2000_hostdata *hostdata)
+static uchar read_1_byte(struct IN2000_hostdata *hostdata)
 {
 uchar asr, x = 0;
 
@@ -288,7 +247,7 @@
 }
 
 
-void write_3393_count(struct IN2000_hostdata *hostdata, unsigned long value)
+static void write_3393_count(struct IN2000_hostdata *hostdata, unsigned long value)
 {
    write1_io(WD_TRANSFER_COUNT_MSB,IO_WD_ADDR);
    write1_io((value >> 16),IO_WD_DATA);
@@ -297,7 +256,7 @@
 }
 
 
-unsigned long read_3393_count(struct IN2000_hostdata *hostdata)
+static unsigned long read_3393_count(struct IN2000_hostdata *hostdata)
 {
 unsigned long value;
 
@@ -309,6 +268,32 @@
 }
 
 
+/* The 33c93 needs to be told which direction a command transfers its
+ * data; we use this function to figure it out. Returns true if there
+ * will be a DATA_OUT phase with this command, false otherwise.
+ * (Thanks to Joerg Dorchain for the research and suggestion.)
+ */
+static int is_dir_out(Scsi_Cmnd *cmd)
+{
+   switch (cmd->cmnd[0]) {
+      case WRITE_6:           case WRITE_10:          case WRITE_12:
+      case WRITE_LONG:        case WRITE_SAME:        case WRITE_BUFFER:
+      case WRITE_VERIFY:      case WRITE_VERIFY_12:      
+      case COMPARE:           case COPY:              case COPY_VERIFY:
+      case SEARCH_EQUAL:      case SEARCH_HIGH:       case SEARCH_LOW:
+      case SEARCH_EQUAL_12:   case SEARCH_HIGH_12:    case SEARCH_LOW_12:      
+      case FORMAT_UNIT:       case REASSIGN_BLOCKS:   case RESERVE:
+      case MODE_SELECT:       case MODE_SELECT_10:    case LOG_SELECT:
+      case SEND_DIAGNOSTIC:   case CHANGE_DEFINITION: case UPDATE_BLOCK:
+      case SET_WINDOW:        case MEDIUM_SCAN:       case SEND_VOLUME_TAG:
+      case 0xea:
+         return 1;
+      default:
+         return 0;
+      }
+}
+
+
 
 static struct sx_period sx_table[] = {
    {  1, 0x20},
@@ -321,7 +306,7 @@
    {1000,0x00},
    {0,   0} };
 
-int round_period(unsigned int period)
+static int round_period(unsigned int period)
 {
 int x;
 
@@ -334,7 +319,7 @@
    return 7;
 }
 
-uchar calc_sync_xfer(unsigned int period, unsigned int offset)
+static uchar calc_sync_xfer(unsigned int period, unsigned int offset)
 {
 uchar result;
 
@@ -346,7 +331,7 @@
 
 
 
-void in2000_execute(struct Scsi_Host *instance);
+static void in2000_execute(struct Scsi_Host *instance);
 
 int in2000_queuecommand (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
 {
@@ -354,7 +339,6 @@
 Scsi_Cmnd *tmp;
 unsigned long flags;
 
-
    hostdata = (struct IN2000_hostdata *)cmd->host->hostdata;
 
 DB(DB_QUEUE_COMMAND,printk("Q-%d-%02x-%ld(",cmd->target,cmd->cmnd[0],cmd->pid))
@@ -372,7 +356,7 @@
 /* We use the Scsi_Pointer structure that's included with each command
  * as a scratchpad (as it's intended to be used!). The handy thing about
  * the SCp.xxx fields is that they're always associated with a given
- * cmd, and are preserved across disconnect-reconnect. This means we
+ * cmd, and are preserved across disconnect-reselect. This means we
  * can pretty much ignore SAVE_POINTERS and RESTORE_POINTERS messages
  * if we keep all the critical pointers and counters in SCp:
  *  - SCp.ptr is the pointer into the RAM buffer
@@ -400,9 +384,24 @@
 
 /* We don't set SCp.phase here - that's done in in2000_execute() */
 
-/* Preset the command status to GOOD, since that's the normal case */
+/* WD docs state that at the conclusion of a "LEVEL2" command, the
+ * status byte can be retrieved from the LUN register. Apparently,
+ * this is the case only for *uninterrupted* LEVEL2 commands! If
+ * there are any unexpected phases entered, even if they are 100%
+ * legal (different devices may choose to do things differently),
+ * the LEVEL2 command sequence is exited. This often occurs prior
+ * to receiving the status byte, in which case the driver does a
+ * status phase interrupt and gets the status byte on its own.
+ * While such a command can then be "resumed" (ie restarted to
+ * finish up as a LEVEL2 command), the LUN register will NOT be
+ * a valid status byte at the command's conclusion, and we must
+ * use the byte obtained during the earlier interrupt. Here, we
+ * preset SCp.Status to an illegal value (0xff) so that when
+ * this command finally completes, we can tell where the actual
+ * status byte is stored.
+ */
 
-   cmd->SCp.Status = GOOD;
+   cmd->SCp.Status = ILLEGAL_STATUS_BYTE;
 
    save_flags(flags);
    cli();
@@ -444,7 +443,7 @@
  * the input_Q, using the first command we find that's intended
  * for a currently non-busy target/lun.
  */
-void in2000_execute (struct Scsi_Host *instance)
+static void in2000_execute (struct Scsi_Host *instance)
 {
 struct IN2000_hostdata *hostdata;
 Scsi_Cmnd *cmd, *prev;
@@ -500,11 +499,15 @@
    else
       hostdata->input_Q = (Scsi_Cmnd *)cmd->host_scribble;
 
+#ifdef PROC_STATISTICS
+   hostdata->cmd_cnt[cmd->target]++;
+#endif
+
 /*
  * Start the selection process
  */
 
-   if (IS_DIR_OUT(cmd))
+   if (is_dir_out(cmd))
       write_3393(hostdata,WD_DESTINATION_ID, cmd->target);
    else
       write_3393(hostdata,WD_DESTINATION_ID, cmd->target | DSTID_DPD);
@@ -557,8 +560,8 @@
 yes:
    cmd->SCp.phase = 1;
 
-#ifdef PROC_INTERFACE
-   disc_allowed_total++;
+#ifdef PROC_STATISTICS
+   hostdata->disc_allowed_cnt[cmd->target]++;
 #endif
 
 no:
@@ -654,7 +657,7 @@
          write_3393(hostdata,WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_BUS);
          write1_io(0, IO_FIFO_WRITE);  /* clear fifo counter, write mode */
 
-         if (IS_DIR_OUT(cmd)) {
+         if (is_dir_out(cmd)) {
             hostdata->fifo = FI_FIFO_WRITING;
             if ((i = cmd->SCp.this_residual) > (IN2000_FIFO_SIZE - 16) )
                i = IN2000_FIFO_SIZE - 16;
@@ -718,7 +721,7 @@
 
 
 
-void transfer_pio(uchar *buf, int cnt,
+static void transfer_pio(uchar *buf, int cnt,
                   int data_in_dir, struct IN2000_hostdata *hostdata)
 {
 uchar asr;
@@ -754,7 +757,7 @@
 
 
 
-void transfer_bytes(Scsi_Cmnd *cmd, int data_in_dir)
+static void transfer_bytes(Scsi_Cmnd *cmd, int data_in_dir)
 {
 struct IN2000_hostdata *hostdata;
 unsigned short *sp;
@@ -791,7 +794,8 @@
 
    if (data_in_dir) {
       write1_io(0,IO_FIFO_READ);
-      if ((hostdata->level2 >= L2_DATA) || (cmd->SCp.phase == 0)) {
+      if ((hostdata->level2 >= L2_DATA) ||
+          (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) {
          write_3393(hostdata,WD_COMMAND_PHASE,0x45);
          write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER);
          hostdata->state = S_RUNNING_LEVEL2;
@@ -808,7 +812,8 @@
  * write any bytes that don't make it at this stage.
  */
 
-   if ((hostdata->level2 >= L2_DATA) || (cmd->SCp.phase == 0)) {
+      if ((hostdata->level2 >= L2_DATA) ||
+          (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) {
       write_3393(hostdata,WD_COMMAND_PHASE,0x45);
       write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER);
       hostdata->state = S_RUNNING_LEVEL2;
@@ -847,7 +852,7 @@
  * re-think the multiple card capability....
  */
 
-void in2000_intr (int irqnum, void * dev_id, struct pt_regs *ptregs)
+static void in2000_intr (int irqnum, void * dev_id, struct pt_regs *ptregs)
 {
 struct Scsi_Host *instance;
 struct IN2000_hostdata *hostdata;
@@ -874,6 +879,10 @@
    save_flags(flags);
    sti();
 
+#ifdef PROC_STATISTICS
+   hostdata->int_cnt++;
+#endif
+
 /* The IN2000 card has 2 interrupt sources OR'ed onto its IRQ line - the
  * WD3393 chip and the 2k fifo (which is actually a dual-port RAM combined
  * with a big logic array, so it's a little different than what you might
@@ -1181,9 +1190,10 @@
       case CSR_XFER_DONE|PHS_STATUS:
       case CSR_UNEXP    |PHS_STATUS:
       case CSR_SRV_REQ  |PHS_STATUS:
-DB(DB_INTR,printk("STATUS"))
+DB(DB_INTR,printk("STATUS="))
 
          cmd->SCp.Status = read_1_byte(hostdata);
+DB(DB_INTR,printk("%02x",cmd->SCp.Status))
          if (hostdata->level2 >= L2_BASIC) {
             sr = read_3393(hostdata,WD_SCSI_STATUS);  /* clear interrupt */
             hostdata->state = S_RUNNING_LEVEL2;
@@ -1191,7 +1201,6 @@
             write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER);
             }
          else {
-DB(DB_INTR,printk("=%02x",cmd->SCp.Status))
             hostdata->state = S_CONNECTED;
             }
          break;
@@ -1362,15 +1371,16 @@
 DB(DB_INTR,printk("SX-DONE-%ld",cmd->pid))
             cmd->SCp.Message = COMMAND_COMPLETE;
             lun = read_3393(hostdata,WD_TARGET_LUN);
-            if (cmd->SCp.Status == GOOD)
-               cmd->SCp.Status = lun;
+DB(DB_INTR,printk(":%d.%d",cmd->SCp.Status,lun))
             hostdata->connected = NULL;
-            if (cmd->cmnd[0] != REQUEST_SENSE)
-               cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
-            else if (cmd->SCp.Status != GOOD)
-               cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
             hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
             hostdata->state = S_UNCONNECTED;
+            if (cmd->SCp.Status == ILLEGAL_STATUS_BYTE)
+               cmd->SCp.Status = lun;
+            if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)
+               cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+            else
+               cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
             cmd->scsi_done(cmd);
 
 /* We are no longer connected to a target - check to see if
@@ -1452,10 +1462,10 @@
          hostdata->connected = NULL;
          hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
          hostdata->state = S_UNCONNECTED;
-         if (cmd->cmnd[0] != REQUEST_SENSE)
-            cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
-         else if (cmd->SCp.Status != GOOD)
+         if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)
             cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+         else
+            cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
          cmd->scsi_done(cmd);
 
 /* We are no longer connected to a target - check to see if
@@ -1485,10 +1495,11 @@
                hostdata->connected = NULL;
                hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
                hostdata->state = S_UNCONNECTED;
-               if (cmd->cmnd[0] != REQUEST_SENSE)
-                  cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
-               else if (cmd->SCp.Status != GOOD)
+DB(DB_INTR,printk(":%d",cmd->SCp.Status))
+               if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)
                   cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+               else
+                  cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
                cmd->scsi_done(cmd);
                break;
             case S_PRE_TMP_DISC:
@@ -1498,8 +1509,8 @@
                hostdata->connected = NULL;
                hostdata->state = S_UNCONNECTED;
 
-#ifdef PROC_INTERFACE
-               disc_taken_total++;
+#ifdef PROC_STATISTICS
+               hostdata->disc_done_cnt[cmd->target]++;
 #endif
 
                break;
@@ -1600,7 +1611,7 @@
     * But we DO need to fix the DPD bit so it's correct for this command.
     */
 
-         if (IS_DIR_OUT(cmd))
+         if (is_dir_out(cmd))
             write_3393(hostdata,WD_DESTINATION_ID,cmd->target);
          else
             write_3393(hostdata,WD_DESTINATION_ID,cmd->target | DSTID_DPD);
@@ -1633,7 +1644,7 @@
 #define RESET_CARD_AND_BUS 1
 #define B_FLAG 0x80
 
-int reset_hardware(struct Scsi_Host *instance, int type)
+static int reset_hardware(struct Scsi_Host *instance, int type)
 {
 struct IN2000_hostdata *hostdata;
 int qt,x;
@@ -1855,10 +1866,11 @@
 
 
 #define MAX_IN2000_HOSTS 3
-#define MAX_SETUP_STRINGS (sizeof(setup_strings) / sizeof(char *))
+#define MAX_SETUP_ARGS (sizeof(setup_args) / sizeof(char *))
 #define SETUP_BUFFER_SIZE 200
 static char setup_buffer[SETUP_BUFFER_SIZE];
-static char setup_used[MAX_SETUP_STRINGS];
+static char setup_used[MAX_SETUP_ARGS];
+static int done_setup = 0;
 
 void in2000_setup (char *str, int *ints)
 {
@@ -1869,43 +1881,44 @@
    setup_buffer[SETUP_BUFFER_SIZE - 1] = '\0';
    p1 = setup_buffer;
    i = 0;
-   while (*p1 && (i < MAX_SETUP_STRINGS)) {
+   while (*p1 && (i < MAX_SETUP_ARGS)) {
       p2 = strchr(p1, ',');
       if (p2) {
          *p2 = '\0';
          if (p1 != p2)
-            setup_strings[i] = p1;
+            setup_args[i] = p1;
          p1 = p2 + 1;
          i++;
          }
       else {
-         setup_strings[i] = p1;
+         setup_args[i] = p1;
          break;
          }
       }
-   for (i=0; i<MAX_SETUP_STRINGS; i++)
+   for (i=0; i<MAX_SETUP_ARGS; i++)
       setup_used[i] = 0;
+   done_setup = 1;
 }
 
 
-/* check_setup_strings() returns index if key found, 0 if not
+/* check_setup_args() returns index if key found, 0 if not
  */
 
-int check_setup_strings(char *key, int *flags, int *val, char *buf)
+static int check_setup_args(char *key, int *flags, int *val, char *buf)
 {
 int x;
 char *cp;
 
-   for  (x=0; x<MAX_SETUP_STRINGS; x++) {
+   for  (x=0; x<MAX_SETUP_ARGS; x++) {
       if (setup_used[x])
          continue;
-      if (!strncmp(setup_strings[x], key, strlen(key)))
+      if (!strncmp(setup_args[x], key, strlen(key)))
          break;
       }
-   if (x == MAX_SETUP_STRINGS)
+   if (x == MAX_SETUP_ARGS)
       return 0;
    setup_used[x] = 1;
-   cp = setup_strings[x] + strlen(key);
+   cp = setup_args[x] + strlen(key);
    *val = -1;
    if (*cp != ':')
       return ++x;
@@ -1918,33 +1931,33 @@
 
 
 
-struct proc_dir_entry proc_scsi_in2000 = {
-   PROC_SCSI_IN2000, 6, "in2000",
-   S_IFDIR | S_IRUGO | S_IXUGO, 2
-   };
-
-
-const unsigned int *bios_tab[] = {
+/* The "correct" (ie portable) way to access memory-mapped hardware
+ * such as the IN2000 EPROM and dip switch is through the use of
+ * special macros declared in 'asm/io.h'. We use readb() and readl()
+ * when reading from the card's BIOS area in in2000_detect().
+ */
+static const unsigned int *bios_tab[] = {
    (unsigned int *)0xc8000,
    (unsigned int *)0xd0000,
    (unsigned int *)0xd8000,
    0
    };
 
-const unsigned short base_tab[] = {
+static const unsigned short base_tab[] = {
    0x220,
    0x200,
    0x110,
    0x100,
    };
 
-const int int_tab[] = {
+static const int int_tab[] = {
    15,
    14,
    11,
    10
    };
 
+
 int in2000_detect(Scsi_Host_Template * tpnt)
 {
 struct Scsi_Host *instance;
@@ -1960,36 +1973,40 @@
 char buf[32];
 
 /* Thanks to help from Bill Earnest, probing for IN2000 cards is a
- * pretty straightforward and fool-proof operation. We do require
- * that cards have their BIOS enabled, although I hope to be able
- * to detect and use BIOS-less cards in the future. There are 3
+ * pretty straightforward and fool-proof operation. There are 3
  * possible locations for the IN2000 EPROM in memory space - if we
  * find a BIOS signature, we can read the dip switch settings from
  * the byte at BIOS+32 (shadowed in by logic on the card). From 2
  * of the switch bits we get the card's address in IO space. There's
  * an image of the dip switch there, also, so we have a way to back-
- * check that this really is an IN2000 card. Very nifty.
- *
- * There have been a couple of BIOS versions with different layouts
- * for the obvious ID strings. We look for the 2 most common ones and
- * hope that they cover all the cases...
+ * check that this really is an IN2000 card. Very nifty. Use the
+ * 'ioport:xx' command-line parameter if your BIOS EPROM is absent
+ * or disabled.
  */
 
+   if (!done_setup && setup_strings)
+      in2000_setup(setup_strings,0);
+
    detect_count = 0;
    for (bios = 0; bios_tab[bios]; bios++) {
-      if (check_setup_strings("ioport",&flags,&val,buf)) {
+      if (check_setup_args("ioport",&flags,&val,buf)) {
          base = val;
          switches = ~inb(base + IO_SWITCHES) & 0xff;
          printk("Forcing IN2000 detection at IOport 0x%x ",base);
          bios = 2;
          }
-      else if (*(bios_tab[bios]+0x04) == 0x41564f4e ||
-          *(bios_tab[bios]+0x0c) == 0x61776c41) {
+/*
+ * There have been a couple of BIOS versions with different layouts
+ * for the obvious ID strings. We look for the 2 most common ones and
+ * hope that they cover all the cases...
+ */
+      else if (readl(bios_tab[bios]+0x04) == 0x41564f4e ||
+               readl(bios_tab[bios]+0x0c) == 0x61776c41) {
          printk("Found IN2000 BIOS at 0x%x ",(unsigned int)bios_tab[bios]);
 
 /* Read the switch image that's mapped into EPROM space */
 
-         switches = ~((*(bios_tab[bios]+0x08) & 0xff));
+         switches = ~((readb(bios_tab[bios]+0x08) & 0xff));
 
 /* Find out where the IO space is */
 
@@ -2066,6 +2083,11 @@
          hostdata->busy[x] = 0;
          hostdata->sync_xfer[x] = calc_sync_xfer(DEFAULT_SX_PER/4,DEFAULT_SX_OFF);
          hostdata->sync_stat[x] = SS_UNSET;  /* using default sync values */
+#ifdef PROC_STATISTICS
+         hostdata->cmd_cnt[x] = 0;
+         hostdata->disc_allowed_cnt[x] = 0;
+         hostdata->disc_done_cnt[x] = 0;
+#endif
          }
       hostdata->input_Q = NULL;
       hostdata->selecting = NULL;
@@ -2082,41 +2104,47 @@
 
 /* Older BIOS's had a 'sync on/off' switch - use its setting */
 
-      if (*(bios_tab[bios]+0x04) == 0x41564f4e && (switches & SW_SYNC_DOS5))
+      if (readl(bios_tab[bios]+0x04) == 0x41564f4e && (switches & SW_SYNC_DOS5))
          hostdata->sync_off = 0x00;    /* sync defaults to on */
       else
          hostdata->sync_off = 0xff;    /* sync defaults to off */
 
-      hostdata->proc = PR_VERSION|PR_INFO|PR_TOTALS|
+#ifdef PROC_INTERFACE
+      hostdata->proc = PR_VERSION|PR_INFO|PR_STATISTICS|
                        PR_CONNECTED|PR_INPUTQ|PR_DISCQ|
                        PR_STOP;
-
-#ifdef PROC_INTERFACE
-      disc_allowed_total = 0;
-      disc_taken_total = 0;
+#ifdef PROC_STATISTICS
+      hostdata->int_cnt = 0;
+#endif
 #endif
 
-      if (check_setup_strings("nosync",&flags,&val,buf))
+      if (check_setup_args("nosync",&flags,&val,buf))
          hostdata->sync_off = val;
 
-      if (check_setup_strings("period",&flags,&val,buf))
+      if (check_setup_args("period",&flags,&val,buf))
          hostdata->default_sx_per = sx_table[round_period((unsigned int)val)].period_ns;
 
-      if (check_setup_strings("disconnect",&flags,&val,buf)) {
+      if (check_setup_args("disconnect",&flags,&val,buf)) {
          if ((val >= DIS_NEVER) && (val <= DIS_ALWAYS))
             hostdata->disconnect = val;
          else
             hostdata->disconnect = DIS_ADAPTIVE;
          }
 
-      if (check_setup_strings("noreset",&flags,&val,buf))
+      if (check_setup_args("noreset",&flags,&val,buf))
          hostdata->args ^= A_NO_SCSI_RESET;
 
-      if (check_setup_strings("debug",&flags,&val,buf))
+      if (check_setup_args("level2",&flags,&val,buf))
+         hostdata->level2 = val;
+
+      if (check_setup_args("debug",&flags,&val,buf))
          hostdata->args = (val & DB_MASK);
 
-      if (check_setup_strings("proc",&flags,&val,buf))
+#ifdef PROC_INTERFACE
+      if (check_setup_args("proc",&flags,&val,buf))
          hostdata->proc = val;
+#endif
+
 
       x = reset_hardware(instance,(hostdata->args & A_NO_SCSI_RESET)?RESET_CARD:RESET_CARD_AND_BUS);
 
@@ -2142,9 +2170,9 @@
                   (hostdata->chip==C_WD33C93B)?"WD33c93B":"unknown",
                   hostdata->microcode);
 #ifdef DEBUGGING_ON
-      printk("setup_strings = ");
-      for (x=0; x<8; x++)
-         printk("%s,",setup_strings[x]);
+      printk("setup_args = ");
+      for (x=0; x<MAX_SETUP_ARGS; x++)
+         printk("%s,",setup_args[x]);
       printk("\n");
 #endif
       if (hostdata->sync_off == 0xff)
@@ -2188,31 +2216,16 @@
       iinfo[0] = 255;
       iinfo[1] = 63;
       iinfo[2] = disk->capacity / (iinfo[0] * iinfo[1]);
-      if (iinfo[2] > 1023)
-         iinfo[2] = 1023;
       }
     return 0;
 }
 
 
-#ifdef PROC_INTERFACE
-
-/* Certain older compilers (such as a.out 2.5.8) choke and give a
- * "Too many reloads" error when there are a lot of calls to 'strcat()'
- * in one function. Modern kernels define 'strcat()' as an inline
- * function - I _guess_ this is related to the problem. Regardless,
- * we can make everyone happy by doing some macro fudging to force
- * gcc to do calls instead of inline expansion.
- */
-
-char * in2000_strcat(char * dest, const char * src)
-{
-   return strcat(dest,src);
-}
 
-#define strcat(d,s) (in2000_strcat((d),(s)))
-
-#endif
+struct proc_dir_entry proc_scsi_in2000 = {
+   PROC_SCSI_IN2000, 6, "in2000",
+   S_IFDIR | S_IRUGO | S_IXUGO, 2
+   };
 
 
 int in2000_proc_info(char *buf, char **start, off_t off, int len, int hn, int in)
@@ -2278,6 +2291,10 @@
          bp += 5;
          hd->proc = simple_strtoul(bp,NULL,0);
          }
+      else if (!strncmp(bp,"level2:",7)) {
+         bp += 7;
+         hd->level2 = simple_strtoul(bp,NULL,0);
+         }
       return len;
       }
 
@@ -2296,12 +2313,38 @@
                   (hd->dip_switch & 0x40)?"Yes":"No",
                   (hd->dip_switch & 0x20)?"Yes":"No");
       strcat(bp,tbuf);
-      }
-   if (hd->proc & PR_TOTALS) {
-      sprintf(tbuf,"\n%ld disc_allowed, %ld disc_taken",
-            disc_allowed_total,disc_taken_total);
+      strcat(bp,"\nsync_xfer[] =       ");
+      for (x=0; x<7; x++) {
+         sprintf(tbuf,"\t%02x",hd->sync_xfer[x]);
+         strcat(bp,tbuf);
+         }
+      strcat(bp,"\nsync_stat[] =       ");
+      for (x=0; x<7; x++) {
+         sprintf(tbuf,"\t%02x",hd->sync_stat[x]);
+         strcat(bp,tbuf);
+         }
+      }
+#ifdef PROC_STATISTICS
+   if (hd->proc & PR_STATISTICS) {
+      strcat(bp,"\ncommands issued:    ");
+      for (x=0; x<7; x++) {
+         sprintf(tbuf,"\t%ld",hd->cmd_cnt[x]);
+         strcat(bp,tbuf);
+         }
+      strcat(bp,"\ndisconnects allowed:");
+      for (x=0; x<7; x++) {
+         sprintf(tbuf,"\t%ld",hd->disc_allowed_cnt[x]);
+         strcat(bp,tbuf);
+         }
+      strcat(bp,"\ndisconnects done:   ");
+      for (x=0; x<7; x++) {
+         sprintf(tbuf,"\t%ld",hd->disc_done_cnt[x]);
+         strcat(bp,tbuf);
+         }
+      sprintf(tbuf,"\ninterrupts:      \t%ld",hd->int_cnt);
       strcat(bp,tbuf);
       }
+#endif
    if (hd->proc & PR_CONNECTED) {
       strcat(bp,"\nconnected:     ");
       if (hd->connected) {
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/in2000.h linux/drivers/scsi/in2000.h
--- v2.0.30/linux/drivers/scsi/in2000.h	Thu May  9 06:40:24 1996
+++ linux/drivers/scsi/in2000.h	Tue Aug  5 13:26:17 1997
@@ -2,7 +2,7 @@
  *    in2000.h -  Linux device driver definitions for the
  *                Always IN2000 ISA SCSI card.
  *
- *    IMPORTANT: This file is for version 1.28 - 07/May/1996
+ *    IMPORTANT: This file is for version 1.31 - 06/Jul/1997
  *
  * Copyright (c) 1996 John Shifflett, GeoLog Consulting
  *    john@geolog.com
@@ -25,9 +25,66 @@
 
 #include <asm/io.h>
 
+#define PROC_INTERFACE     /* add code for /proc/scsi/in2000/xxx interface */
+#ifdef  PROC_INTERFACE
+#define PROC_STATISTICS    /* add code for keeping various real time stats */
+#endif
+
+#define SYNC_DEBUG         /* extra info on sync negotiation printed */
+#define DEBUGGING_ON       /* enable command-line debugging bitmask */
+#define DEBUG_DEFAULTS 0   /* default bitmask - change from command-line */
+
+#define FAST_READ_IO       /* No problems with these on my machine */
+#define FAST_WRITE_IO
+
+#ifdef DEBUGGING_ON
+#define DB(f,a) if (hostdata->args & (f)) a;
+#define CHECK_NULL(p,s) /* if (!(p)) {printk("\n"); while (1) printk("NP:%s\r",(s));} */
+#else
+#define DB(f,a)
+#define CHECK_NULL(p,s)
+#endif
 
 #define uchar unsigned char
 
+#define read1_io(a)     (inb(hostdata->io_base+(a)))
+#define read2_io(a)     (inw(hostdata->io_base+(a)))
+#define write1_io(b,a)  (outb((b),hostdata->io_base+(a)))
+#define write2_io(w,a)  (outw((w),hostdata->io_base+(a)))
+
+/* These inline assembly defines are derived from a patch
+ * sent to me by Bill Earnest. He's done a lot of very
+ * valuable thinking, testing, and coding during his effort
+ * to squeeze more speed out of this driver. I really think
+ * that we are doing IO at close to the maximum now with
+ * the fifo. (And yes, insw uses 'edi' while outsw uses
+ * 'esi'. Thanks Bill!)
+ */
+
+#define FAST_READ2_IO()    \
+   __asm__ __volatile__ ("\n \
+   cld                    \n \
+   orl %%ecx, %%ecx       \n \
+   jz 1f                  \n \
+   rep                    \n \
+   insw %%dx              \n \
+1: "                       \
+   : "=D" (sp)                   /* output */   \
+   : "d" (f), "D" (sp), "c" (i)  /* input */    \
+   : "edx", "ecx", "edi" )       /* trashed */
+
+#define FAST_WRITE2_IO()   \
+   __asm__ __volatile__ ("\n \
+   cld                    \n \
+   orl %%ecx, %%ecx       \n \
+   jz 1f                  \n \
+   rep                    \n \
+   outsw %%dx             \n \
+1: "                       \
+   : "=S" (sp)                   /* output */   \
+   : "d" (f), "S" (sp), "c" (i)  /* input */    \
+   : "edx", "ecx", "esi" )       /* trashed */
+
 
 /* IN2000 io_port offsets */
 #define IO_WD_ASR       0x00     /* R - 3393 auxstat reg */
@@ -194,6 +251,9 @@
 
 
 
+#define ILLEGAL_STATUS_BYTE   0xff
+
+
 #define DEFAULT_SX_PER     500   /* (ns) fairly safe */
 #define DEFAULT_SX_OFF     0     /* aka async */
 
@@ -231,7 +291,15 @@
     uchar            sync_xfer[8];     /* sync_xfer reg settings per target */
     uchar            sync_stat[8];     /* status of sync negotiation per target */
     uchar            sync_off;         /* bit mask: don't use sync with these targets */
+#ifdef PROC_INTERFACE
     uchar            proc;             /* bit mask: what's in proc output */
+#ifdef PROC_STATISTICS
+    unsigned long    cmd_cnt[8];       /* # of commands issued per target */
+    unsigned long    int_cnt;          /* # of interrupts serviced */
+    unsigned long    disc_allowed_cnt[8]; /* # of disconnects allowed per target */
+    unsigned long    disc_done_cnt[8]; /* # of disconnects done per target*/
+#endif
+#endif
     };
 
 
@@ -301,7 +369,7 @@
 
 #define PR_VERSION   1<<0
 #define PR_INFO      1<<1
-#define PR_TOTALS    1<<2
+#define PR_STATISTICS 1<<2
 #define PR_CONNECTED 1<<3
 #define PR_INPUTQ    1<<4
 #define PR_DISCQ     1<<5
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/ncr53c8xx.c linux/drivers/scsi/ncr53c8xx.c
--- v2.0.30/linux/drivers/scsi/ncr53c8xx.c	Wed Dec 11 07:02:43 1996
+++ linux/drivers/scsi/ncr53c8xx.c	Thu Aug 21 15:13:05 1997
@@ -36,11 +36,35 @@
 **  And has been ported to NetBSD by
 **          Charles M. Hannum           <mycroft@gnu.ai.mit.edu>
 **
+**-----------------------------------------------------------------------------
+**
+**                     Brief history
+**
+**  December 10 1995 by Gerard Roudier:
+**     Initial port to Linux.
+**
+**  June 23 1996 by Gerard Roudier:
+**     Support for 64 bits architectures (Alpha).
+**
+**  November 30 1996 by Gerard Roudier:
+**     Support for Fast-20 scsi.
+**     Support for large DMA fifo and 128 dwords bursting.
+**
+**  February 27 1997 by Gerard Roudier:
+**     Support for Fast-40 scsi.
+**     Support for on-Board RAM.
+**
+**  May 3 1997 by Gerard Roudier:
+**     Full support for scsi scripts instructions pre-fetching.
+**
+**  May 19 1997 by Richard Waltham <dormouse@farsrobt.demon.co.uk>:
+**     Support for NvRAM detection and reading.
+**
 *******************************************************************************
 */
 
 /*
-**	30 August 1996, version 1.12c
+**	21 August 1997, version 2.4a
 **
 **	Supported SCSI-II features:
 **	    Synchronous negotiation
@@ -51,26 +75,22 @@
 **	    Etc...
 **
 **	Supported NCR chips:
-**		53C810		(NCR BIOS in flash-bios required) 
-**		53C815		(~53C810 with on board rom BIOS)
-**		53C820		(Wide, NCR BIOS in flash bios required)
-**		53C825		(Wide, ~53C820 with on board rom BIOS)
-**		53C860		(not yet tested)
-**		53C875		(not yet tested)
+**		53C810		(8 bits, Fast SCSI-2, no rom BIOS) 
+**		53C815		(8 bits, Fast SCSI-2, on board rom BIOS)
+**		53C820		(Wide,   Fast SCSI-2, no rom BIOS)
+**		53C825		(Wide,   Fast SCSI-2, on board rom BIOS)
+**		53C860		(8 bits, Fast 20,     no rom BIOS)
+**		53C875		(Wide,   Fast 20,     on board rom BIOS)
+**		53C895		(Wide,   Fast 40,     on board rom BIOS)
 **
 **	Other features:
-**		Memory mapped IO (linux-1.3.X only)
+**		Memory mapped IO (linux-1.3.X and above only)
 **		Module
 **		Shared IRQ (since linux-1.3.72)
 */
 
-#define SCSI_NCR_DEBUG
 #define SCSI_NCR_DEBUG_FLAGS	(0)		
 
-#define NCR_DATE "pl23 95/09/07"
-
-#define NCR_VERSION	(2)
-
 #define NCR_GETCC_WITHMSG
 
 /*==========================================================
@@ -105,11 +125,22 @@
 
 #include <linux/version.h>
 #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
-#include "linux/blk.h"
+#include <linux/blk.h>
 #else
 #include "../block/blk.h"
 #endif
 
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,35)
+#include <linux/init.h>
+#else
+#ifndef	__initdata
+#define	__initdata
+#endif
+#ifndef	__initfunc
+#define	__initfunc(__arginit) __arginit
+#endif
+#endif
+
 #include "scsi.h"
 #include "hosts.h"
 #include "constants.h"
@@ -132,15 +163,6 @@
 */
 
 /*
-**	Proc info and user command support
-*/
-
-#ifdef SCSI_NCR_PROC_INFO_SUPPORT
-#define SCSI_NCR_PROFILE
-#define SCSI_NCR_USER_COMMAND
-#endif
-
-/*
 **    SCSI address of this device.
 **    The boot routines should have set it.
 **    If not, use this.
@@ -151,24 +173,6 @@
 #endif
 
 /*
-**    The maximal synchronous frequency in kHz.
-**    (0=asynchronous)
-*/
-
-#ifndef SCSI_NCR_MAX_SYNC
-#define SCSI_NCR_MAX_SYNC   (10000)
-#endif
-
-/*
-**    The maximal bus with (in log2 byte)
-**    (0=8 bit, 1=16 bit)
-*/
-
-#ifndef SCSI_NCR_MAX_WIDE
-#define SCSI_NCR_MAX_WIDE   (1)
-#endif
-
-/*
 **    The maximum number of tags per logic unit.
 **    Used only for disk devices that support tags.
 */
@@ -177,13 +181,6 @@
 #define SCSI_NCR_MAX_TAGS    (4)
 #endif
 
-/*==========================================================
-**
-**      Configuration and Debugging
-**
-**==========================================================
-*/
-
 /*
 **    Number of targets supported by the driver.
 **    n permits target numbers 0..n-1.
@@ -209,7 +206,14 @@
 #else
 #define MAX_LUN    (1)
 #endif
+
+/*
+**    Asynchronous pre-scaler (ns). Shall be 40
+*/
  
+#ifndef SCSI_NCR_MIN_ASYNC
+#define SCSI_NCR_MIN_ASYNC (40)
+#endif
 
 /*
 **    The maximum number of jobs scheduled for starting.
@@ -231,12 +235,11 @@
 #define MAX_SCATTER (SCSI_NCR_MAX_SCATTER)
 
 /*
-**    The maximum transfer length (should be >= 64k).
-**    MUST NOT be greater than (MAX_SCATTER-1) * NBPG.
+**    Io mapped or memory mapped.
 */
 
-#if 0
-#define MAX_SIZE  ((MAX_SCATTER-1) * (long) NBPG)
+#if defined(SCSI_NCR_IOMAPPED)
+#define NCR_IOMAPPED
 #endif
 
 /*
@@ -245,10 +248,6 @@
 
 #define NCR_SNOOP_TIMEOUT (1000000)
 
-#if defined(SCSI_NCR_IOMAPPED) || defined(__alpha__)
-#define NCR_IOMAPPED
-#endif
-
 /*==========================================================
 **
 **	Defines for Linux.
@@ -287,7 +286,7 @@
 **	virtual memory addresses of the kernel data segment into
 **	IO bus adresses.
 **	On i386 architecture, IO bus addresses match the physical
-**	addresses. But on Alpha architecture they are different.
+**	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().
@@ -299,27 +298,42 @@
 /*
 **	Memory mapped IO
 **
-**	Linux 1.3.X allow to remap physical pages addresses greater than
-**	the highest physical memory address to kernel virtual pages.
-**	We must use vremap() to map the page and vfree() to unmap it.
-**	The memory base of ncr chips is set by the bios at a high physical
-**	address. Also we can map it, and MMIO is possible.
+**	Since linux-2.1, we must use ioremap() to map the io memory space.
+**	iounmap() to unmap it. That allows portability.
+**	Linux 1.3.X and 2.0.X allow to remap physical pages addresses greater 
+**	than the highest physical memory address to kernel virtual pages with 
+**	vremap() / vfree(). That was not portable but worked with i386 
+**	architecture.
 */
 
-static inline vm_offset_t remap_pci_mem(u_long base, u_long size)
+__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);
 }
-static inline void unmap_pci_mem(vm_offset_t vaddr, u_long size)
-{
-	if (vaddr) vfree((void *) (vaddr & PAGE_MASK));
-}
 
+__initfunc(
+static void unmap_pci_mem(vm_offset_t vaddr, u_long size)
+)
+{
+	if (vaddr)
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
+		iounmap((void *) (vaddr & PAGE_MASK));
 #else
+		vfree((void *) (vaddr & PAGE_MASK));
+#endif
+}
+
+#else /* linux-1.2.13 */
 
 /*
 **	Linux 1.2.X assumes that addresses (virtual, physical, bus)
@@ -332,6 +346,10 @@
 #define vtophys(p)	((u_long) (p))
 #endif
 
+/*
+**	Insert a delay in micro-seconds.
+*/
+
 static void DELAY(long us)
 {
 	for (;us>1000;us-=1000) udelay(1000);
@@ -348,31 +366,81 @@
 **	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.
+**	The NCR need better alignment for cache line bursting.
+**	The global header is moved betewen the NCB and CCBs and need 
+**	origin and destination addresses to have same lower four bits.
+**
+**	We use 32 boundary alignment for NCB and CCBs and offset multiple 
+**	of 32 for global header fields. That's too much but at least enough.
+*/
+
+#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)
+
+static void *m_alloc(int size, int a_shift)
+{
+	u_long addr;
+	void *ptr;
+	u_long a_size, a_mask;
+
+	if (a_shift < 3)
+		a_shift = 3;
+
+	a_size	= ALIGN_SIZE(a_shift);
+	a_mask	= ALIGN_MASK(a_shift);
+
+	ptr = (void *) kmalloc(size + a_size, GFP_ATOMIC);
+	if (ptr) {
+		addr	= (((u_long) ptr) + a_size) & a_mask;
+		*((void **) (addr - sizeof(void *))) = ptr;
+		ptr	= (void *) addr;
+	}
 
-static inline void *m_alloc(int size)
-{
-	void *ptr = (void *) kmalloc(size, GFP_ATOMIC);
-	if (((unsigned long) ptr) & 3)
-		panic("ncr53c8xx: kmalloc returns misaligned address %lx\n", (unsigned long) ptr);
 	return ptr;
 }
 
-static inline void m_free(void *ptr, int size)
-	{ kfree(ptr); }
+#ifdef MODULE
+static void m_free(void *ptr, int size)
+{
+	u_long addr;
+
+	if (ptr) {
+		addr	= (u_long) ptr;
+		ptr	= *((void **) (addr - sizeof(void *)));
+
+		kfree(ptr);
+	}
+}
+#endif
 
 /*
 **	Transfer direction
 **
-**	The middle scsi driver of Linux does not provide the transfer
-**	direction in the command structure.
-**	FreeBsd ncr driver require this information.
-**
-**	I spent some hours to read the scsi2 documentation to see if
-**	it was possible to deduce the direction of transfer from the opcode
-**	of the command. It seems that it's OK.
-**	guess_xfer_direction() seems to work. If it's wrong we will
-**	get a phase mismatch on some opcode.
+**	Low-level scsi drivers under Linux do not receive the expected 
+**	data transfer direction from upper scsi drivers.
+**	The driver will only check actual data direction for common 
+**	scsi opcodes. Other ones may cause problem, since they may 
+**	depend on device type or be vendor specific.
+**	I would prefer to never trust the device for data direction, 
+**	but that is not possible.
+**
+**	The original driver requires the expected direction to be known.
+**	The Linux version of the driver has been enhanced in order to 
+**	be able to transfer data in the direction choosen by the target. 
 */
 
 #define XferNone	0
@@ -385,6 +453,8 @@
 **	Head of list of NCR boards
 **
 **	Host is retrieved by its irq level.
+**	If interrupts are shared, the internal host control block 
+**	address (struct ncb) is used as device id.
 */
 
 static struct Scsi_Host		*first_host	= NULL;
@@ -419,6 +489,41 @@
 	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 : 1;
+	unsigned ultra_scsi	: 2;
+	unsigned force_sync_nego: 1;
+	unsigned reverse_probe: 1;
+	unsigned pci_fix_up: 4;
+	u_char	use_nvram;
+	u_char	verbose;
+	u_char	default_tags;
+	u_short	default_sync;
+	u_short	debug;
+	u_char	burst_max;
+	u_char	led_pin;
+	u_char	max_wide;
+	u_char	settle_delay;
+	u_char	diff_support;
+	u_char	irqm;
+};
+
+static struct ncr_driver_setup
+	driver_setup			= SCSI_NCR_DRIVER_SETUP;
+
+#ifdef	SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
+static struct ncr_driver_setup
+	driver_safe_setup __initdata	= SCSI_NCR_DRIVER_SAFE_SETUP;
+#endif
 
 /*
 **	Other Linux definitions
@@ -438,7 +543,149 @@
 
 static void ncr53c8xx_timeout(unsigned long np);
 
-#define bootverbose 1
+#define initverbose (driver_setup.verbose)
+#define bootverbose (np->verbose)
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+/*
+**	Symbios NvRAM data format
+*/
+#define SYMBIOS_NVRAM_SIZE 368
+#define SYMBIOS_NVRAM_ADDRESS 0x100
+
+struct Symbios_nvram {
+/* Header 6 bytes */
+	u_short start_marker;	/* 0x0000 */
+	u_short byte_count;	/* excluding header/trailer */
+	u_short checksum;
+
+/* Controller set up 20 bytes */
+	u_short	word0;		/* 0x3000 */
+	u_short	word2;		/* 0x0000 */
+	u_short	word4;		/* 0x0000 */
+	u_short	flags;
+#define SYMBIOS_SCAM_ENABLE	(1)
+#define SYMBIOS_PARITY_ENABLE	(1<<1)
+#define SYMBIOS_VERBOSE_MSGS	(1<<2)
+	u_short	flags1;
+#define SYMBIOS_SCAN_HI_LO	(1)
+	u_short	word10;		/* 0x00 */
+	u_short	word12;		/* 0x00 */
+	u_char	host_id;
+	u_char	byte15;		/* 0x04 */
+	u_short	word16;		/* 0x0410 */
+	u_short	word18;		/* 0x0000 */
+
+/* Boot order 14 bytes * 4 */
+	struct Symbios_host{
+		u_char	word0;		/* 0x0004:ok / 0x0000:nok */
+		u_short	device_id;	/* PCI device id */
+		u_short	vendor_id;	/* PCI vendor id */
+		u_char	byte6;		/* 0x00 */
+		u_char	device_fn;	/* PCI device/function number << 3*/
+		u_short	word8;
+		u_short	flags;
+#define	SYMBIOS_INIT_SCAN_AT_BOOT	(1)
+		u_short	io_port;	/* PCI io_port address */
+	} host[4];
+
+/* Targets 8 bytes * 16 */
+	struct Symbios_target {
+		u_short	flags;
+#define SYMBIOS_DISCONNECT_ENABLE	(1)
+#define SYMBIOS_SCAN_AT_BOOT_TIME	(1<<1)
+#define SYMBIOS_SCAN_LUNS		(1<<2)
+#define SYMBIOS_QUEUE_TAGS_ENABLED	(1<<3)
+		u_char	bus_width;	/* 0x08/0x10 */
+		u_char	sync_offset;
+		u_char	sync_period;	/* 4*period factor */
+		u_char	byte6;		/* 0x00 */
+		u_short	timeout;
+	} target[16];
+	u_char	spare_devices[19*8];
+	u_char	trailer[6];		/* 0xfe 0xfe 0x00 0x00 0x00 0x00 */
+};
+typedef struct Symbios_nvram	Symbios_nvram;
+typedef struct Symbios_host	Symbios_host;
+typedef struct Symbios_target	Symbios_target;
+
+/*
+**	Tekram NvRAM data format.
+*/
+#define TEKRAM_NVRAM_SIZE 64
+#define TEKRAM_NVRAM_ADDRESS 0
+
+struct Tekram_nvram {
+	struct Tekram_target {
+		u_char	flags;
+#define	TEKRAM_PARITY_CHECK		(1)
+#define TEKRAM_SYNC_NEGO		(1<<1)
+#define TEKRAM_DISCONNECT_ENABLE	(1<<2)
+#define	TEKRAM_START_CMD		(1<<3)
+#define TEKRAM_TAGGED_COMMANDS		(1<<4)
+#define TEKRAM_WIDE_NEGO		(1<<5)
+		u_char	sync_index;
+		u_short	word2;
+	} target[16];
+	u_char	host_id;
+	u_char	flags;
+#define TEKRAM_MORE_THAN_2_DRIVES	(1)
+#define TEKRAM_DRIVES_SUP_1GB		(1<<1)
+#define	TEKRAM_RESET_ON_POWER_ON	(1<<2)
+#define TEKRAM_ACTIVE_NEGATION		(1<<3)
+#define TEKRAM_IMMEDIATE_SEEK		(1<<4)
+#define	TEKRAM_SCAN_LUNS		(1<<5)
+#define	TEKRAM_REMOVABLE_FLAGS		(3<<6)	/* 0: disable; 1: boot device; 2:all */
+	u_char	boot_delay_index;
+	u_char	max_tags_index;
+	u_short	flags1;
+#define TEKRAM_F2_F6_ENABLED		(1)
+	u_short	spare[29];
+};
+typedef struct Tekram_nvram	Tekram_nvram;
+typedef struct Tekram_target	Tekram_target;
+
+static u_char Tekram_sync[12] __initdata = {25,31,37,43,50,62,75,125,12,15,18,21};
+
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
+/*
+**	Structures used by ncr53c8xx_detect/ncr53c8xx_pci_init to 
+**	transmit device configuration to the ncr_attach() function.
+*/
+typedef struct {
+	int	bus;
+	u_char	device_fn;
+	u_int	base;
+	u_int	io_port;
+	int	irq;
+/* port and reg fields to use INB, OUTB macros */
+	u_int	port;
+	volatile struct	ncr_reg	*reg;
+} ncr_slot;
+
+typedef struct {
+	int type;
+#define	SCSI_NCR_SYMBIOS_NVRAM	(1)
+#define	SCSI_NCR_TEKRAM_NVRAM	(2)
+#ifdef	SCSI_NCR_NVRAM_SUPPORT
+	union {
+		Symbios_nvram Symbios;
+		Tekram_nvram Tekram;
+	} data;
+#endif
+} ncr_nvram;
+
+/*
+**	Structure used by ncr53c8xx_detect/ncr53c8xx_pci_init
+**	to save data on each detected board for ncr_attach().
+*/
+typedef struct {
+	ncr_slot  slot;
+	ncr_chip  chip;
+	ncr_nvram *nvram;
+	int attached;
+} ncr_device;
 
 /*==========================================================
 **
@@ -466,7 +713,7 @@
 **    Can be changed at runtime too.
 */
 
-#ifdef SCSI_NCR_DEBUG
+#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
 	#define DEBUG_FLAGS ncr_debug
 #else
 	#define DEBUG_FLAGS	SCSI_NCR_DEBUG_FLAGS
@@ -499,49 +746,45 @@
 **	Access to the controller chip.
 **
 **	If NCR_IOMAPPED is defined, only IO are used by the driver.
-**	Else, we begins initialisations by using MMIO.
-**		If cache test fails, we retry using IO mapped.
-**	The flag "use_mmio" in the ncb structure is set to 1 if
-**	mmio is possible.
 **
 **==========================================================
 */
 
 /*
-**	IO mapped input / ouput
+**	IO mapped only input / ouput
 */
 
-#define	IOM_INB(r)         inb (np->port + offsetof(struct ncr_reg, r))
-#define	IOM_INB_OFF(o)     inb (np->port + (o))
-#define	IOM_INW(r)         inw (np->port + offsetof(struct ncr_reg, r))
-#define	IOM_INL(r)         inl (np->port + offsetof(struct ncr_reg, r))
-#define	IOM_INL_OFF(o)     inl (np->port + (o))
-
-#define	IOM_OUTB(r, val)     outb ((val), np->port+offsetof(struct ncr_reg,r))
-#define	IOM_OUTW(r, val)     outw ((val), np->port+offsetof(struct ncr_reg,r))
-#define	IOM_OUTL(r, val)     outl ((val), np->port+offsetof(struct ncr_reg,r))
-#define	IOM_OUTL_OFF(o, val) outl ((val), np->port + (o))
+#define	IOM_INB(r)		inb (np->port + offsetof(struct ncr_reg, r))
+#define	IOM_INB_OFF(o)		inb (np->port + (o))
+#define	IOM_INW(r)		inw (np->port + offsetof(struct ncr_reg, r))
+#define	IOM_INL(r)		inl (np->port + offsetof(struct ncr_reg, r))
+#define	IOM_INL_OFF(o)		inl (np->port + (o))
+
+#define	IOM_OUTB(r, val)	outb ((val), np->port+offsetof(struct ncr_reg,r))
+#define	IOM_OUTW(r, val)	outw ((val), np->port+offsetof(struct ncr_reg,r))
+#define	IOM_OUTL(r, val)	outl ((val), np->port+offsetof(struct ncr_reg,r))
+#define	IOM_OUTL_OFF(o, val)	outl ((val), np->port + (o))
 
 /*
 **	MEMORY mapped IO input / output
 */
 
-#define MMIO_INB(r)        readb(&np->reg_remapped->r)
-#define MMIO_INB_OFF(o)    readb((char *)np->reg_remapped + (o))
-#define MMIO_INW(r)        readw(&np->reg_remapped->r)
-#define MMIO_INL(r)        readl(&np->reg_remapped->r)
-#define MMIO_INL_OFF(o)    readl((char *)np->reg_remapped + (o))
-
-#define MMIO_OUTB(r, val)     writeb((val), &np->reg_remapped->r)
-#define MMIO_OUTW(r, val)     writew((val), &np->reg_remapped->r)
-#define MMIO_OUTL(r, val)     writel((val), &np->reg_remapped->r)
-#define MMIO_OUTL_OFF(o, val) writel((val), (char *)np->reg_remapped + (o))
+#define MMIO_INB(r)		readb(&np->reg->r)
+#define MMIO_INB_OFF(o)		readb((char *)np->reg + (o))
+#define MMIO_INW(r)		readw(&np->reg->r)
+#define MMIO_INL(r)		readl(&np->reg->r)
+#define MMIO_INL_OFF(o)		readl((char *)np->reg + (o))
+
+#define MMIO_OUTB(r, val)	writeb((val), &np->reg->r)
+#define MMIO_OUTW(r, val)	writew((val), &np->reg->r)
+#define MMIO_OUTL(r, val)	writel((val), &np->reg->r)
+#define MMIO_OUTL_OFF(o, val)	writel((val), (char *)np->reg + (o))
 
 /*
-**	IO mapped only input / output
+**	IO mapped input / output
 */
 
-#ifdef NCR_IOMAPPED
+#if defined(NCR_IOMAPPED)
 
 #define INB(r)             IOM_INB(r)
 #define INB_OFF(o)         IOM_INB_OFF(o)
@@ -555,24 +798,35 @@
 #define OUTL_OFF(o, val)   IOM_OUTL_OFF(o, val)
 
 /*
-**	IO mapped or MEMORY mapped depending on flag "use_mmio"
+**	MEMORY mapped only input / output
 */
 
 #else
 
-#define	INB(r)             (np->use_mmio ? MMIO_INB(r) : IOM_INB(r))
-#define	INB_OFF(o)         (np->use_mmio ? MMIO_INB_OFF(o) : IOM_INB_OFF(o))
-#define	INW(r)             (np->use_mmio ? MMIO_INW(r) : IOM_INW(r))
-#define	INL(r)             (np->use_mmio ? MMIO_INL(r) : IOM_INL(r))
-#define	INL_OFF(o)         (np->use_mmio ? MMIO_INL_OFF(o) : IOM_INL_OFF(o))
-
-#define	OUTB(r, val)       (np->use_mmio ? MMIO_OUTB(r, val) : IOM_OUTB(r, val))
-#define	OUTW(r, val)       (np->use_mmio ? MMIO_OUTW(r, val) : IOM_OUTW(r, val))
-#define	OUTL(r, val)       (np->use_mmio ? MMIO_OUTL(r, val) : IOM_OUTL(r, val))
-#define	OUTL_OFF(o, val)   (np->use_mmio ? MMIO_OUTL_OFF(o, val) : IOM_OUTL_OFF(o, val))
+#define INB(r)             MMIO_INB(r)
+#define INB_OFF(o)         MMIO_INB_OFF(o)
+#define INW(r)             MMIO_INW(r)
+#define INL(r)             MMIO_INL(r)
+#define INL_OFF(o)         MMIO_INL_OFF(o)
+
+#define OUTB(r, val)       MMIO_OUTB(r, val)
+#define OUTW(r, val)       MMIO_OUTW(r, val)
+#define OUTL(r, val)       MMIO_OUTL(r, val)
+#define OUTL_OFF(o, val)   MMIO_OUTL_OFF(o, val)
 
 #endif
 
+/*
+**	Set bit field ON, OFF 
+*/
+
+#define OUTONB(r, m)	OUTB(r, INB(r) | (m))
+#define OUTOFFB(r, m)	OUTB(r, INB(r) & ~(m))
+#define OUTONW(r, m)	OUTW(r, INW(r) | (m))
+#define OUTOFFW(r, m)	OUTW(r, INW(r) & ~(m))
+#define OUTONL(r, m)	OUTL(r, INL(r) | (m))
+#define OUTOFFL(r, m)	OUTL(r, INL(r) & ~(m))
+
 /*==========================================================
 **
 **	Command control block states.
@@ -614,7 +868,9 @@
 #define	SIR_REJECT_SENT		(10)
 #define	SIR_IGN_RESIDUE		(11)
 #define	SIR_MISSING_SAVE	(12)
-#define	SIR_MAX			(12)
+#define	SIR_DATA_IO_IS_OUT	(13)
+#define	SIR_DATA_IO_IS_IN	(14)
+#define	SIR_MAX			(14)
 
 /*==========================================================
 **
@@ -673,7 +929,6 @@
 */
 
 #define CCB_MAGIC	(0xf2691ad2)
-#define	MAX_TAGS	(16)		/* hard limit */
 
 /*==========================================================
 **
@@ -713,7 +968,13 @@
 #define UC_SETFLAG	15
 #define UC_CLEARPROF	16
 
+#ifdef	SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+#define UC_DEBUG_ERROR_RECOVERY 17
+#endif
+
 #define	UF_TRACE	(0x01)
+#define	UF_NODISC	(0x02)
+#define	UF_NOSCAN	(0x04)
 
 /*---------------------------------------
 **
@@ -727,7 +988,6 @@
 	u_long end;
 	u_long select;
 	u_long command;
-	u_long data;
 	u_long status;
 	u_long disconnect;
 	u_long reselect;
@@ -812,6 +1072,14 @@
 	ccb_p   hold_cp;
 
 	/*
+	**	pointer to ccb used for negotiating.
+	**	Avoid to start a nego for all queued commands 
+	**	when tagged command queuing is enabled.
+	*/
+
+	ccb_p   nego_cp;
+
+	/*
 	**	statistical data
 	*/
 
@@ -821,13 +1089,18 @@
 	/*
 	**	user settable limits for sync transfer
 	**	and tagged commands.
+	**	These limits are read from the NVRAM if present.
 	*/
 
 	u_char	usrsync;
-	u_char	usrtags;
 	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.
@@ -911,6 +1184,15 @@
 	u_char		usetags;
 	u_char		lasttag;
 
+	/*
+	**	Linux specific fields:
+	**	Number of active commands and current credit.
+	**	Should be managed by the generic scsi driver
+	*/
+
+	u_char		active;
+	u_char		opennings;
+
 	/*-----------------------------------------------
 	**	Flag to force M_ORDERED_TAG on next command
 	**	in order to avoid spurious timeout when
@@ -960,9 +1242,9 @@
 	**	the last transfer command.
 	*/
 
-	u_long		savep;
-	u_long		lastp;
-	u_long		goalp;
+	u_int32		savep;
+	u_int32		lastp;
+	u_int32		goalp;
 
 	/*
 	**	The virtual address of the ccb
@@ -1093,6 +1375,14 @@
 
 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.
@@ -1199,6 +1489,14 @@
 	*/
 
 	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;
 };
 
 #define CCB_PHYS(cp,lbl)	(cp->p_ccb + offsetof(struct ccb, lbl))
@@ -1211,32 +1509,69 @@
 */
 
 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.
+	*/
+	struct head     header;
+
 	/*-----------------------------------------------
 	**	Specific Linux fields
 	**-----------------------------------------------
 	*/
 	int    unit;			/* Unit number                       */
-	int    chip;			/* Chip 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               */
-	int	release_stage;		/* Synchronisation stage on release  */
 	Scsi_Cmnd *waiting_list;	/* Waiting list header for commands  */
 					/* that we can't put into the squeue */
-#ifndef NCR_IOMAPPED
-	volatile struct ncr_reg*
-		reg_remapped;		/* Virtual address of the memory     */
-					/* base of the ncr chip              */
-	int	use_mmio;		/* Indicate mmio is OK               */
+	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;
-#define ChipDevice	((np)->device_id)
-#define ChipVersion	((np)->revision_id & 0xf0)
+
+	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 ..
@@ -1262,6 +1597,9 @@
 	vm_offset_t     vaddr;
 	vm_offset_t     paddr;
 
+	vm_offset_t     vaddr2;
+	vm_offset_t     paddr2;
+
 	/*
 	**	pointer to the chip's registers.
 	*/
@@ -1269,14 +1607,22 @@
 	struct ncr_reg* reg;
 
 	/*
-	**	A copy of the script, relocated for this ncb.
+	**	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;
 
 	/*
-	**	Physical address of this instance of ncb->script
+	**	Scripts instance physical address.
 	*/
 	u_long		p_script;
+	u_long		p_scripth;
 
 	/*
 	**	The SCSI address of the host adapter.
@@ -1284,11 +1630,21 @@
 	u_char	  myaddr;
 
 	/*
+	**	Max dwords burst supported by the adapter.
+	*/
+	u_char		maxburst;	/* log base 2 of dwords burst	*/
+
+	/*
 	**	timing parameters
 	*/
-	u_char		ns_async;
-	u_char		ns_sync;
-	u_char		rv_scntl3;
+	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
@@ -1314,7 +1670,7 @@
 	/*
 	**	Start queue.
 	*/
-	u_long		squeue [MAX_START];
+	u_int32		squeue [MAX_START];
 	u_short		squeueput;
 	u_short		actccbs;
 
@@ -1343,19 +1699,12 @@
 	u_long		disc_ref;
 
 	/*
-	**	The global header.
-	**	Accessible to both the host and the
-	**	script-processor.
-	*/
-	struct head     header;
-
-	/*
 	**	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;
+	struct ccb      *ccb;
 
 	/*
 	**	message buffers.
@@ -1365,7 +1714,7 @@
 	*/
 	u_char		msgout[8];
 	u_char		msgin [8];
-	u_long		lastmsg;
+	u_int32		lastmsg;
 
 	/*
 	**	Buffer for STATUS_IN phase.
@@ -1393,7 +1742,8 @@
 	u_short		irq;
 };
 
-#define NCB_SCRIPT_PHYS(np,lbl)	(np->p_script + offsetof (struct script, lbl))
+#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))
 
 /*==========================================================
 **
@@ -1416,12 +1766,15 @@
 **----------------------------------------------------------
 */
 
+/*
+**	Script fragments which are loaded into the on-board RAM 
+**	of 825A, 875 and 895 chips.
+*/
 struct script {
 	ncrcmd	start		[  7];
 	ncrcmd	start0		[  2];
 	ncrcmd	start1		[  3];
 	ncrcmd  startpos	[  1];
-	ncrcmd  tryloop		[MAX_START*5+2];
 	ncrcmd  trysel		[  8];
 	ncrcmd	skip		[  8];
 	ncrcmd	skip2		[  3];
@@ -1439,14 +1792,6 @@
 	ncrcmd  status		[ 27];
 	ncrcmd  msg_in		[ 26];
 	ncrcmd  msg_bad		[  6];
-	ncrcmd  msg_parity	[  6];
-	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  complete	[ 13];
 	ncrcmd	cleanup		[ 12];
 	ncrcmd	cleanup0	[ 11];
@@ -1458,6 +1803,30 @@
 	ncrcmd  disconnect1	[ 23];
 	ncrcmd	msg_out		[  9];
 	ncrcmd	msg_out_done	[  7];
+	ncrcmd  badgetcc	[  6];
+	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];
+};
+
+/*
+**	Script fragments which stay in main memory for all chips.
+*/
+struct scripth {
+	ncrcmd  tryloop		[MAX_START*5+2];
+	ncrcmd  msg_parity	[  6];
+	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_out_abort	[ 10];
 	ncrcmd  getcc		[  4];
 	ncrcmd  getcc1		[  5];
@@ -1467,14 +1836,7 @@
 	ncrcmd	getcc2		[ 14];
 #endif
 	ncrcmd	getcc3		[ 10];
-	ncrcmd  badgetcc	[  6];
-	ncrcmd	reselect	[ 12];
-	ncrcmd	reselect2	[  6];
-	ncrcmd	resel_tmp	[  5];
-	ncrcmd  resel_lun	[ 18];
-	ncrcmd	resel_tag	[ 24];
-	ncrcmd  data_in		[MAX_SCATTER * 4 + 7];
-	ncrcmd  data_out	[MAX_SCATTER * 4 + 7];
+	ncrcmd  data_out	[MAX_SCATTER * 4 + 4];
 	ncrcmd	aborttag	[  4];
 	ncrcmd	abort		[ 22];
 	ncrcmd	snooptest	[  9];
@@ -1493,51 +1855,59 @@
 static	void	ncr_alloc_ccb	(ncb_p np, u_long t, u_long l);
 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);
-static	void	ncr_getclock	(ncb_p np, u_char scntl3);
+static	void	ncr_free_ccb	(ncb_p np, ccb_p cp, u_long t, u_long l);
+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	void	ncr_init	(ncb_p np, char * msg, u_long code);
-static	int	ncr_intr	(ncb_p np);
+static	int	ncr_int_sbmc	(ncb_p np);
+static	int	ncr_int_par	(ncb_p np);
 static	void	ncr_int_ma	(ncb_p np);
 static	void	ncr_int_sir	(ncb_p np);
 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
-static	int	ncr_delta	(u_long from, u_long to);
+#ifdef SCSI_NCR_PROFILE_SUPPORT
 static	void	ncb_profile	(ncb_p np, ccb_p cp);
 #endif
 
 static	void	ncr_script_copy_and_bind
-				(struct script * script, ncb_p np);
-static  void    ncr_script_fill (struct script * scr);
+				(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 usrtags);
-static	void	ncr_setsync	(ncb_p np, ccb_p cp, u_char sxfer);
+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_setwide	(ncb_p np, ccb_p cp, u_char wide);
+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);
 
-#ifdef SCSI_NCR_USER_COMMAND
+#ifdef SCSI_NCR_USER_COMMAND_SUPPORT
 static	void	ncr_usercmd	(ncb_p np);
 #endif
 
-static int ncr_attach (Scsi_Host_Template *tpnt, int unit, u_short device_id,
-		       u_char revision_id, int chip, u_int base, u_int io_port, 
-		       int irq, int bus, u_char device_fn);
+static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device);
 
 static void insert_into_waiting_list(ncb_p np, Scsi_Cmnd *cmd);
-static Scsi_Cmnd *remove_from_waiting_list(ncb_p np, Scsi_Cmnd *cmd);
+static Scsi_Cmnd *retrieve_from_waiting_list(int to_remove, ncb_p np, Scsi_Cmnd *cmd);
 static void process_waiting_list(ncb_p np, int sts);
 
+#define remove_from_waiting_list(np, cmd) \
+		retrieve_from_waiting_list(1, (np), (cmd))
 #define requeue_waiting_list(np) process_waiting_list((np), DID_OK)
-#define abort_waiting_list(np) process_waiting_list((np), DID_ABORT)
 #define reset_waiting_list(np) process_waiting_list((np), DID_RESET)
 
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+static	int	ncr_get_Symbios_nvram	(ncr_slot *np, Symbios_nvram *nvram);
+static	int	ncr_get_Tekram_nvram	(ncr_slot *np, Tekram_nvram *nvram);
+#endif
+
 /*==========================================================
 **
 **
@@ -1557,24 +1927,13 @@
 	+ (u_long) sizeof (struct tcb)	*  2;
 #endif
 
-#ifdef SCSI_NCR_DEBUG
+#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
 static int ncr_debug = SCSI_NCR_DEBUG_FLAGS;
 #endif
 
-/*==========================================================
-**
-**
-**      Global static data:	auto configure
-**
-**
-**==========================================================
-*/
-
-static char *ncr_name (ncb_p np)
+static inline char *ncr_name (ncb_p np)
 {
-	static char name[10];
-	sprintf(name, "ncr53c%d-%d", np->chip, np->unit);
-	return (name);
+	return np->inst_name;
 }
 
 
@@ -1601,10 +1960,12 @@
 #define	RELOC_LABEL	0x50000000
 #define	RELOC_REGISTER	0x60000000
 #define	RELOC_KVAR	0x70000000
+#define	RELOC_LABELH	0x80000000
 #define	RELOC_MASK	0xf0000000
 
 #define	NADDR(label)	(RELOC_SOFTC | offsetof(struct ncb, label))
 #define PADDR(label)    (RELOC_LABEL | offsetof(struct script, label))
+#define PADDRH(label)   (RELOC_LABELH | offsetof(struct scripth, label))
 #define	RADDR(label)	(RELOC_REGISTER | REG(label))
 #define	FADDR(label,ofs)(RELOC_REGISTER | ((REG(label))+(ofs)))
 #define	KVAR(which)	(RELOC_KVAR | (which))
@@ -1618,10 +1979,10 @@
  * Kernel variables referenced in the scripts.
  * THESE MUST ALL BE ALIGNED TO A 4-BYTE BOUNDARY.
  */
-static void *script_kvars[] =
+static void *script_kvars[] __initdata =
 	{ (void *)&jiffies };
 
-static	struct script script0 = {
+static	struct script script0 __initdata = {
 /*--------------------------< START >-----------------------*/ {
 	/*
 	**	Claim to be still alive ...
@@ -1661,33 +2022,7 @@
 	*/
 	SCR_JUMP,
 }/*-------------------------< STARTPOS >--------------------*/,{
-		PADDR(tryloop),
-}/*-------------------------< TRYLOOP >---------------------*/,{
-/*
-**	Load an entry of the start queue into dsa
-**	and try to start it by jumping to TRYSEL.
-**
-**	Because the size depends on the
-**	#define MAX_START parameter, it is filled
-**	in at runtime.
-**
-**-----------------------------------------------------------
-**
-**  ##===========< I=0; i<MAX_START >===========
-**  ||	SCR_COPY (4),
-**  ||		NADDR (squeue[i]),
-**  ||		RADDR (dsa),
-**  ||	SCR_CALL,
-**  ||		PADDR (trysel),
-**  ##==========================================
-**
-**	SCR_JUMP,
-**		PADDR(tryloop),
-**
-**-----------------------------------------------------------
-*/
-0
-
+		PADDRH(tryloop),
 }/*-------------------------< TRYSEL >----------------------*/,{
 	/*
 	**	Now:
@@ -1742,7 +2077,7 @@
 	**	patch the launch field.
 	**	should look like an idle process.
 	*/
-	SCR_COPY (4),
+	SCR_COPY_F (4),
 		RADDR (dsa),
 		PADDR (skip2),
 	SCR_COPY (8),
@@ -1848,7 +2183,7 @@
 	**	We patch the address part of a
 	**	COPY command with the DSA-register.
 	*/
-	SCR_COPY (4),
+	SCR_COPY_F (4),
 		RADDR (dsa),
 		PADDR (loadpos),
 	/*
@@ -1925,7 +2260,7 @@
 	SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)),
 		PADDR (msg_in),
 	SCR_JUMP ^ IFTRUE (DATA (M_REJECT)),
-		PADDR (msg_reject),
+		PADDRH (msg_reject),
 	/*
 	**	normal processing
 	*/
@@ -2142,7 +2477,7 @@
 	SCR_FROM_REG (socl),
 		0,
 	SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
-		PADDR (msg_parity),
+		PADDRH (msg_parity),
 	SCR_FROM_REG (scratcha),
 		0,
 	/*
@@ -2157,13 +2492,13 @@
 	SCR_JUMP ^ IFTRUE (DATA (M_DISCONNECT)),
 		PADDR (disconnect),
 	SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)),
-		PADDR (msg_extended),
+		PADDRH (msg_extended),
 	SCR_JUMP ^ IFTRUE (DATA (M_NOOP)),
 		PADDR (clrack),
 	SCR_JUMP ^ IFTRUE (DATA (M_REJECT)),
-		PADDR (msg_reject),
+		PADDRH (msg_reject),
 	SCR_JUMP ^ IFTRUE (DATA (M_IGN_RESIDUE)),
-		PADDR (msg_ign_residue),
+		PADDRH (msg_ign_residue),
 	/*
 	**	Rest of the messages left as
 	**	an exercise ...
@@ -2182,301 +2517,41 @@
 	SCR_JUMP,
 		PADDR (setmsg),
 
-}/*-------------------------< MSG_PARITY >---------------*/,{
+}/*-------------------------< COMPLETE >-----------------*/,{
 	/*
-	**	count it
+	**	Complete message.
+	**
+	**	If it's not the get condition code,
+	**	copy TEMP register to LASTP in header.
 	*/
-	SCR_REG_REG (PS_REG, SCR_ADD, 0x01),
+	SCR_FROM_REG (SS_REG),
 		0,
-	/*
-	**	send a "message parity error" message.
+/*<<<*/	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.
+	**
+	**	We don't want to be told of an
+	**	"unexpected disconnect",
+	**	so we disable this feature.
 	*/
-	SCR_LOAD_REG (scratcha, M_PARITY),
+	SCR_REG_REG (scntl2, SCR_AND, 0x7f),
 		0,
-	SCR_JUMP,
-		PADDR (setmsg),
-}/*-------------------------< MSG_REJECT >---------------*/,{
 	/*
-	**	If a negotiation was in progress,
-	**	negotiation failed.
+	**	Terminate cycle ...
 	*/
-	SCR_FROM_REG (HS_REG),
+	SCR_CLR (SCR_ACK|SCR_ATN),
 		0,
-	SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)),
-		SIR_NEGO_FAILED,
 	/*
-	**	else make host log this message
-	*/
-	SCR_INT ^ IFFALSE (DATA (HS_NEGOTIATE)),
-		SIR_REJECT_RECEIVED,
-	SCR_JUMP,
-		PADDR (clrack),
-
-}/*-------------------------< MSG_IGN_RESIDUE >----------*/,{
-	/*
-	**	Terminate cycle
+	**	... and wait for the disconnect.
 	*/
-	SCR_CLR (SCR_ACK),
+	SCR_WAIT_DISC,
 		0,
-	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
-		PADDR (dispatch),
-	/*
-	**	get residue size.
-	*/
-	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)),
-		PADDR (msg_parity),
-	SCR_FROM_REG (scratcha),
-		0,
-	/*
-	**	Size is 0 .. ignore message.
-	*/
-	SCR_JUMP ^ IFTRUE (DATA (0)),
-		PADDR (clrack),
-	/*
-	**	Size is not 1 .. have to interrupt.
-	*/
-/*<<<*/	SCR_JUMPR ^ IFFALSE (DATA (1)),
-		40,
-	/*
-	**	Check for residue byte in swide register
-	*/
-	SCR_FROM_REG (scntl2),
-		0,
-/*<<<*/	SCR_JUMPR ^ IFFALSE (MASK (WSR, WSR)),
-		16,
-	/*
-	**	There IS data in the swide register.
-	**	Discard it.
-	*/
-	SCR_REG_REG (scntl2, SCR_OR, WSR),
-		0,
-	SCR_JUMP,
-		PADDR (clrack),
-	/*
-	**	Load again the size to the sfbr register.
-	*/
-/*>>>*/	SCR_FROM_REG (scratcha),
-		0,
-/*>>>*/	SCR_INT,
-		SIR_IGN_RESIDUE,
-	SCR_JUMP,
-		PADDR (clrack),
-
-}/*-------------------------< MSG_EXTENDED >-------------*/,{
-	/*
-	**	Terminate cycle
-	*/
-	SCR_CLR (SCR_ACK),
-		0,
-	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
-		PADDR (dispatch),
-	/*
-	**	get length.
-	*/
-	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)),
-		PADDR (msg_parity),
-	SCR_FROM_REG (scratcha),
-		0,
-	/*
-	*/
-	SCR_JUMP ^ IFTRUE (DATA (3)),
-		PADDR (msg_ext_3),
-	SCR_JUMP ^ IFFALSE (DATA (2)),
-		PADDR (msg_bad),
-}/*-------------------------< MSG_EXT_2 >----------------*/,{
-	SCR_CLR (SCR_ACK),
-		0,
-	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
-		PADDR (dispatch),
-	/*
-	**	get extended message code.
-	*/
-	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)),
-		PADDR (msg_parity),
-	SCR_FROM_REG (scratcha),
-		0,
-	SCR_JUMP ^ IFTRUE (DATA (M_X_WIDE_REQ)),
-		PADDR (msg_wdtr),
-	/*
-	**	unknown extended message
-	*/
-	SCR_JUMP,
-		PADDR (msg_bad)
-}/*-------------------------< MSG_WDTR >-----------------*/,{
-	SCR_CLR (SCR_ACK),
-		0,
-	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
-		PADDR (dispatch),
-	/*
-	**	get data bus width
-	*/
-	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
-		NADDR (msgin[3]),
-	SCR_FROM_REG (socl),
-		0,
-	SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
-		PADDR (msg_parity),
-	/*
-	**	let the host do the real work.
-	*/
-	SCR_INT,
-		SIR_NEGO_WIDE,
-	/*
-	**	let the target fetch our answer.
-	*/
-	SCR_SET (SCR_ATN),
-		0,
-	SCR_CLR (SCR_ACK),
-		0,
-
-	SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)),
-		SIR_NEGO_PROTO,
-	/*
-	**	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 (lastmsg),
-	SCR_JUMP,
-		PADDR (msg_out_done),
-
-}/*-------------------------< MSG_EXT_3 >----------------*/,{
-	SCR_CLR (SCR_ACK),
-		0,
-	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
-		PADDR (dispatch),
-	/*
-	**	get extended message code.
-	*/
-	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)),
-		PADDR (msg_parity),
-	SCR_FROM_REG (scratcha),
-		0,
-	SCR_JUMP ^ IFTRUE (DATA (M_X_SYNC_REQ)),
-		PADDR (msg_sdtr),
-	/*
-	**	unknown extended message
-	*/
-	SCR_JUMP,
-		PADDR (msg_bad)
-
-}/*-------------------------< MSG_SDTR >-----------------*/,{
-	SCR_CLR (SCR_ACK),
-		0,
-	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
-		PADDR (dispatch),
-	/*
-	**	get period and offset
-	*/
-	SCR_MOVE_ABS (2) ^ SCR_MSG_IN,
-		NADDR (msgin[3]),
-	SCR_FROM_REG (socl),
-		0,
-	SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
-		PADDR (msg_parity),
-	/*
-	**	let the host do the real work.
-	*/
-	SCR_INT,
-		SIR_NEGO_SYNC,
-	/*
-	**	let the target fetch our answer.
-	*/
-	SCR_SET (SCR_ATN),
-		0,
-	SCR_CLR (SCR_ACK),
-		0,
-
-	SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)),
-		SIR_NEGO_PROTO,
-	/*
-	**	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 (lastmsg),
-	SCR_JUMP,
-		PADDR (msg_out_done),
-
-}/*-------------------------< COMPLETE >-----------------*/,{
-	/*
-	**	Complete message.
-	**
-	**	If it's not the get condition code,
-	**	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.
-	**
-	**	We don't want to be told of an
-	**	"unexpected disconnect",
-	**	so we disable this feature.
-	*/
-	SCR_REG_REG (scntl2, SCR_AND, 0x7f),
-		0,
-	/*
-	**	Terminate cycle ...
-	*/
-	SCR_CLR (SCR_ACK|SCR_ATN),
-		0,
-	/*
-	**	... and wait for the disconnect.
-	*/
-	SCR_WAIT_DISC,
-		0,
-}/*-------------------------< CLEANUP >-------------------*/,{
+}/*-------------------------< CLEANUP >-------------------*/,{
 	/*
 	**      dsa:    Pointer to ccb
 	**	      or xxxxxxFF (no ccb)
@@ -2497,7 +2572,7 @@
 	/*
 	**	and copy back the header to the ccb.
 	*/
-	SCR_COPY (4),
+	SCR_COPY_F (4),
 		RADDR (dsa),
 		PADDR (cleanup0),
 	SCR_COPY (sizeof (struct head)),
@@ -2517,7 +2592,7 @@
 	SCR_FROM_REG (SS_REG),
 		0,
 	SCR_JUMP ^ IFTRUE (DATA (S_CHECK_COND)),
-		PADDR(getcc2),
+		PADDRH(getcc2),
 	/*
 	**	And make the DSA register invalid.
 	*/
@@ -2603,7 +2678,7 @@
 	**
 	**	CAUTION: only little endian architectures supported! XXX
 	*/
-	SCR_COPY (1),
+	SCR_COPY_F (1),
 		NADDR (header.savep),
 		PADDR (disconnect0),
 }/*-------------------------< DISCONNECT0 >--------------*/,{
@@ -2612,7 +2687,7 @@
 	/*
 	**	neither this
 	*/
-	SCR_COPY (1),
+	SCR_COPY_F (1),
 		NADDR (header.goalp),
 		PADDR (disconnect1),
 }/*-------------------------< DISCONNECT1 >--------------*/,{
@@ -2672,7 +2747,7 @@
 	**	If it was no ABORT message ...
 	*/
 	SCR_JUMP ^ IFTRUE (DATA (M_ABORT)),
-		PADDR (msg_out_abort),
+		PADDRH (msg_out_abort),
 	/*
 	**	... wait for the next phase
 	**	if it's a message out, send it again, ...
@@ -2693,180 +2768,24 @@
 	*/
 	SCR_JUMP,
 		PADDR (dispatch),
-}/*-------------------------< MSG_OUT_ABORT >-------------*/,{
-	/*
-	**	After ABORT message,
-	**
-	**	expect an immediate disconnect, ...
-	*/
-	SCR_REG_REG (scntl2, SCR_AND, 0x7f),
-		0,
-	SCR_CLR (SCR_ACK|SCR_ATN),
-		0,
-	SCR_WAIT_DISC,
-		0,
-	/*
-	**	... and set the status to "ABORTED"
-	*/
-	SCR_LOAD_REG (HS_REG, HS_ABORTED),
-		0,
-	SCR_JUMP,
-		PADDR (cleanup),
-
-}/*-------------------------< GETCC >-----------------------*/,{
-	/*
-	**	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 ...
-	*/
-	SCR_COPY (4),
-		RADDR (dsa),
-		PADDR (getcc1),
-	/*
-	**	... then we do the actual copy.
-	*/
-	SCR_COPY (sizeof (struct head)),
-}/*-------------------------< GETCC1 >----------------------*/,{
-		0,
-		NADDR (header),
-	/*
-	**	Initialize the status registers
-	*/
-	SCR_COPY (4),
-		NADDR (header.status),
-		RADDR (scr0),
-}/*-------------------------< GETCC2 >----------------------*/,{
-	/*
-	**	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.
-	*/
-/*<<<*/	SCR_CALLR,
-		24,
-	/*
-	**	Get the condition code.
-	*/
-	SCR_MOVE_TBL ^ SCR_DATA_IN,
-		offsetof (struct dsb, sense),
-	/*
-	**	No data phase may follow!
-	*/
-	SCR_CALL,
-		PADDR (checkatn),
-	SCR_JUMP,
-		PADDR (no_data),
-/*>>>*/
-
-	/*
-	**	The CALL jumps to this point.
-	**	Prepare for a RESTORE_POINTER message.
-	**	Save the TEMP register into the saved pointer.
-	*/
-	SCR_COPY (4),
-		RADDR (temp),
-		NADDR (header.savep),
-	/*
-	**	Load scratcha, because in case of a selection timeout,
-	**	the host will expect a new value for startpos in
-	**	the scratcha register.
-	*/
-	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)),
-		PADDR(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),
-	/*
-	**	save target id.
-	*/
-	SCR_FROM_REG (sdid),
-		0,
-	SCR_TO_REG (ctest0),
-		0,
-	/*
-	**	Send the IDENTIFY message.
-	**	In case of short transfer, remove ATN.
-	*/
-	SCR_MOVE_TBL ^ SCR_MSG_OUT,
-		offsetof (struct dsb, smsg2),
-	SCR_CLR (SCR_ATN),
-		0,
-	/*
-	**	save the first byte of the message.
-	*/
-	SCR_COPY (1),
-		RADDR (sfbr),
-		NADDR (lastmsg),
-	SCR_JUMP,
-		PADDR (prepare2),
-
-#endif
-}/*-------------------------< GETCC3 >----------------------*/,{
-	/*
-	**	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.
-	*/
-	SCR_SEL_TBL ^ offsetof (struct dsb, select),
-		PADDR(badgetcc),
-	/*
-	**	save target id.
-	*/
-	SCR_FROM_REG (sdid),
-		0,
-	SCR_TO_REG (ctest0),
-		0,
-	/*
-	**	Force error if selection timeout
-	*/
-	SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_IN)),
-		0,
-	/*
-	**	don't negotiate.
-	*/
-	SCR_JUMP,
-		PADDR (prepare2),
-
-}/*------------------------< BADGETCC >---------------------*/,{
+}/*------------------------< BADGETCC >---------------------*/,{
 	/*
 	**	If SIGP was set, clear it and try again.
 	*/
 	SCR_FROM_REG (ctest2),
 		0,
 	SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)),
-		PADDR (getcc2),
+		PADDRH (getcc2),
 	SCR_INT,
 		SIR_SENSE_FAILED,
 }/*-------------------------< RESELECT >--------------------*/,{
 	/*
+	**	This NOP will be patched with LED OFF
+	**	SCR_REG_REG (gpreg, SCR_OR, 0x01)
+	*/
+	SCR_NO_OP,
+		0,
+	/*
 	**	make the DSA invalid.
 	*/
 	SCR_LOAD_REG (dsa, 0xff),
@@ -2881,6 +2800,13 @@
 	*/
 	SCR_WAIT_RESEL,
 		PADDR(reselect2),
+}/*-------------------------< RESELECT1 >--------------------*/,{
+	/*
+	**	This NOP will be patched with LED ON
+	**	SCR_REG_REG (gpreg, SCR_AND, 0xfe)
+	*/
+	SCR_NO_OP,
+		0,
 	/*
 	**	... zu nichts zu gebrauchen ?
 	**
@@ -2894,7 +2820,7 @@
 	**	- struct ccb
 	**	to understand what's going on.
 	*/
-	SCR_REG_SFBR (ssid, SCR_AND, 0x87),
+	SCR_REG_SFBR (ssid, SCR_AND, 0x8F),
 		0,
 	SCR_TO_REG (ctest0),
 		0,
@@ -2902,6 +2828,12 @@
 		NADDR (jump_tcb),
 }/*-------------------------< RESELECT2 >-------------------*/,{
 	/*
+	**	This NOP will be patched with LED ON
+	**	SCR_REG_REG (gpreg, SCR_AND, 0xfe)
+	*/
+	SCR_NO_OP,
+		0,
+	/*
 	**	If it's not connected :(
 	**	-> interrupted by SIGP bit.
 	**	Jump to start.
@@ -2984,80 +2916,530 @@
 /*<<<*/	SCR_JUMPR ^ IFFALSE (DATA (M_SIMPLE_TAG)),
 		48,
 	/*
-	**	It WAS a SIMPLE_TAG message.
-	**	get it and ack it!
+	**	It WAS a SIMPLE_TAG message.
+	**	get it and ack it!
+	*/
+	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+		NADDR (msgin),
+	SCR_CLR (SCR_ACK),
+		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),
+		0,
+	SCR_RETURN,
+		0,
+	/*
+	**	No message phase or no SIMPLE_TAG message
+	**	or no second byte: return 0.
+	*/
+/*>>>*/	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_JUMP,
+		PADDR (no_data),
+
+}/*-------------------------< DATA_IN >--------------------*/,{
+/*
+**	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_IN)),
+**  ||		PADDR (checkatn),
+**  ||	SCR_MOVE_TBL ^ SCR_DATA_IN,
+**  ||		offsetof (struct dsb, data[ i]),
+**  ##==========================================
+**
+**	SCR_CALL,
+**		PADDR (checkatn),
+**	SCR_JUMP,
+**		PADDR (no_data),
+*/
+0
+}/*--------------------------------------------------------*/
+};
+
+static	struct scripth scripth0 __initdata = {
+/*-------------------------< TRYLOOP >---------------------*/{
+/*
+**	Load an entry of the start queue into dsa
+**	and try to start it by jumping to TRYSEL.
+**
+**	Because the size depends on the
+**	#define MAX_START parameter, it is filled
+**	in at runtime.
+**
+**-----------------------------------------------------------
+**
+**  ##===========< I=0; i<MAX_START >===========
+**  ||	SCR_COPY (4),
+**  ||		NADDR (squeue[i]),
+**  ||		RADDR (dsa),
+**  ||	SCR_CALL,
+**  ||		PADDR (trysel),
+**  ##==========================================
+**
+**	SCR_JUMP,
+**		PADDRH(tryloop),
+**
+**-----------------------------------------------------------
+*/
+0
+},/*-------------------------< MSG_PARITY >---------------*/{
+	/*
+	**	count it
+	*/
+	SCR_REG_REG (PS_REG, SCR_ADD, 0x01),
+		0,
+	/*
+	**	send a "message parity error" message.
+	*/
+	SCR_LOAD_REG (scratcha, M_PARITY),
+		0,
+	SCR_JUMP,
+		PADDR (setmsg),
+}/*-------------------------< MSG_REJECT >---------------*/,{
+	/*
+	**	If a negotiation was in progress,
+	**	negotiation failed.
+	*/
+	SCR_FROM_REG (HS_REG),
+		0,
+	SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)),
+		SIR_NEGO_FAILED,
+	/*
+	**	else make host log this message
+	*/
+	SCR_INT ^ IFFALSE (DATA (HS_NEGOTIATE)),
+		SIR_REJECT_RECEIVED,
+	SCR_JUMP,
+		PADDR (clrack),
+
+}/*-------------------------< MSG_IGN_RESIDUE >----------*/,{
+	/*
+	**	Terminate cycle
+	*/
+	SCR_CLR (SCR_ACK),
+		0,
+	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+		PADDR (dispatch),
+	/*
+	**	get residue size.
+	*/
+	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)),
+		PADDR (clrack),
+	/*
+	**	Size is not 1 .. have to interrupt.
+	*/
+/*<<<*/	SCR_JUMPR ^ IFFALSE (DATA (1)),
+		40,
+	/*
+	**	Check for residue byte in swide register
+	*/
+	SCR_FROM_REG (scntl2),
+		0,
+/*<<<*/	SCR_JUMPR ^ IFFALSE (MASK (WSR, WSR)),
+		16,
+	/*
+	**	There IS data in the swide register.
+	**	Discard it.
+	*/
+	SCR_REG_REG (scntl2, SCR_OR, WSR),
+		0,
+	SCR_JUMP,
+		PADDR (clrack),
+	/*
+	**	Load again the size to the sfbr register.
+	*/
+/*>>>*/	SCR_FROM_REG (scratcha),
+		0,
+/*>>>*/	SCR_INT,
+		SIR_IGN_RESIDUE,
+	SCR_JUMP,
+		PADDR (clrack),
+
+}/*-------------------------< MSG_EXTENDED >-------------*/,{
+	/*
+	**	Terminate cycle
+	*/
+	SCR_CLR (SCR_ACK),
+		0,
+	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+		PADDR (dispatch),
+	/*
+	**	get length.
+	*/
+	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),
+	SCR_JUMP ^ IFFALSE (DATA (2)),
+		PADDR (msg_bad),
+}/*-------------------------< MSG_EXT_2 >----------------*/,{
+	SCR_CLR (SCR_ACK),
+		0,
+	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+		PADDR (dispatch),
+	/*
+	**	get extended message code.
+	*/
+	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),
+	/*
+	**	unknown extended message
+	*/
+	SCR_JUMP,
+		PADDR (msg_bad)
+}/*-------------------------< MSG_WDTR >-----------------*/,{
+	SCR_CLR (SCR_ACK),
+		0,
+	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+		PADDR (dispatch),
+	/*
+	**	get data bus width
+	*/
+	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.
+	*/
+	SCR_INT,
+		SIR_NEGO_WIDE,
+	/*
+	**	let the target fetch our answer.
+	*/
+	SCR_SET (SCR_ATN),
+		0,
+	SCR_CLR (SCR_ACK),
+		0,
+
+	SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+		SIR_NEGO_PROTO,
+	/*
+	**	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 (lastmsg),
+	SCR_JUMP,
+		PADDR (msg_out_done),
+
+}/*-------------------------< MSG_EXT_3 >----------------*/,{
+	SCR_CLR (SCR_ACK),
+		0,
+	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+		PADDR (dispatch),
+	/*
+	**	get extended message code.
+	*/
+	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),
+	/*
+	**	unknown extended message
+	*/
+	SCR_JUMP,
+		PADDR (msg_bad)
+
+}/*-------------------------< MSG_SDTR >-----------------*/,{
+	SCR_CLR (SCR_ACK),
+		0,
+	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+		PADDR (dispatch),
+	/*
+	**	get period and offset
+	*/
+	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.
+	*/
+	SCR_INT,
+		SIR_NEGO_SYNC,
+	/*
+	**	let the target fetch our answer.
+	*/
+	SCR_SET (SCR_ATN),
+		0,
+	SCR_CLR (SCR_ACK),
+		0,
+
+	SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+		SIR_NEGO_PROTO,
+	/*
+	**	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 (lastmsg),
+	SCR_JUMP,
+		PADDR (msg_out_done),
+
+}/*-------------------------< MSG_OUT_ABORT >-------------*/,{
+	/*
+	**	After ABORT message,
+	**
+	**	expect an immediate disconnect, ...
+	*/
+	SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+		0,
+	SCR_CLR (SCR_ACK|SCR_ATN),
+		0,
+	SCR_WAIT_DISC,
+		0,
+	/*
+	**	... and set the status to "ABORTED"
+	*/
+	SCR_LOAD_REG (HS_REG, HS_ABORTED),
+		0,
+	SCR_JUMP,
+		PADDR (cleanup),
+
+}/*-------------------------< GETCC >-----------------------*/,{
+	/*
+	**	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 ...
+	*/
+	SCR_COPY_F (4),
+		RADDR (dsa),
+		PADDRH (getcc1),
+	/*
+	**	... then we do the actual copy.
+	*/
+	SCR_COPY (sizeof (struct head)),
+}/*-------------------------< GETCC1 >----------------------*/,{
+		0,
+		NADDR (header),
+	/*
+	**	Initialize the status registers
+	*/
+	SCR_COPY (4),
+		NADDR (header.status),
+		RADDR (scr0),
+}/*-------------------------< GETCC2 >----------------------*/,{
+	/*
+	**	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.
+	*/
+/*<<<*/	SCR_CALLR,
+		24,
+	/*
+	**	Get the condition code.
+	*/
+	SCR_MOVE_TBL ^ SCR_DATA_IN,
+		offsetof (struct dsb, sense),
+	/*
+	**	No data phase may follow!
+	*/
+	SCR_CALL,
+		PADDR (checkatn),
+	SCR_JUMP,
+		PADDR (no_data),
+/*>>>*/
+
+	/*
+	**	The CALL jumps to this point.
+	**	Prepare for a RESTORE_POINTER message.
+	**	Save the TEMP register into the saved pointer.
+	*/
+	SCR_COPY (4),
+		RADDR (temp),
+		NADDR (header.savep),
+	/*
+	**	Load scratcha, because in case of a selection timeout,
+	**	the host will expect a new value for startpos in
+	**	the scratcha register.
+	*/
+	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),
+	/*
+	**	save target id.
+	*/
+	SCR_FROM_REG (sdid),
+		0,
+	SCR_TO_REG (ctest0),
+		0,
+	/*
+	**	Send the IDENTIFY message.
+	**	In case of short transfer, remove ATN.
+	*/
+	SCR_MOVE_TBL ^ SCR_MSG_OUT,
+		offsetof (struct dsb, smsg2),
+	SCR_CLR (SCR_ATN),
+		0,
+	/*
+	**	save the first byte of the message.
 	*/
-	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
-		NADDR (msgin),
-	SCR_CLR (SCR_ACK),
-		0,
+	SCR_COPY (1),
+		RADDR (sfbr),
+		NADDR (lastmsg),
+	SCR_JUMP,
+		PADDR (prepare2),
+
+#endif
+}/*-------------------------< GETCC3 >----------------------*/,{
 	/*
-	**	Wait for the second byte (the tag)
+	**	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.
 	*/
-/*<<<*/	SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
-		24,
+	SCR_SEL_TBL ^ offsetof (struct dsb, select),
+		PADDR(badgetcc),
 	/*
-	**	Get it and ack it!
+	**	save target id.
 	*/
-	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
-		NADDR (msgin),
-	SCR_CLR (SCR_ACK|SCR_CARRY),
+	SCR_FROM_REG (sdid),
 		0,
-	SCR_RETURN,
+	SCR_TO_REG (ctest0),
 		0,
 	/*
-	**	No message phase or no SIMPLE_TAG message
-	**	or no second byte: return 0.
+	**	Force error if selection timeout
 	*/
-/*>>>*/	SCR_LOAD_SFBR (0),
-		0,
-	SCR_SET (SCR_CARRY),
-		0,
-	SCR_RETURN,
+	SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_IN)),
 		0,
+	/*
+	**	don't negotiate.
+	*/
+	SCR_JUMP,
+		PADDR (prepare2),
 
-}/*-------------------------< DATA_IN >--------------------*/,{
-/*
-**	Because the size depends on the
-**	#define MAX_SCATTER parameter,
-**	it is filled in at runtime.
-**
-**	SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
-**		PADDR (no_data),
-**	SCR_COPY (sizeof (u_long)),
-**		KVAR(SCRIPT_KVAR_JIFFIES),
-**		NADDR (header.stamp.data),
-**	SCR_MOVE_TBL ^ SCR_DATA_IN,
-**		offsetof (struct dsb, data[ 0]),
-**
-**  ##===========< i=1; i<MAX_SCATTER >=========
-**  ||	SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)),
-**  ||		PADDR (checkatn),
-**  ||	SCR_MOVE_TBL ^ SCR_DATA_IN,
-**  ||		offsetof (struct dsb, data[ i]),
-**  ##==========================================
-**
-**	SCR_CALL,
-**		PADDR (checkatn),
-**	SCR_JUMP,
-**		PADDR (no_data),
-*/
-0
 }/*-------------------------< DATA_OUT >-------------------*/,{
 /*
 **	Because the size depends on the
 **	#define MAX_SCATTER parameter,
 **	it is filled in at runtime.
 **
-**	SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
-**		PADDR (no_data),
-**	SCR_COPY (sizeof (u_long)),
-**		KVAR(SCRIPT_KVAR_JIFFIES),
-**		NADDR (header.stamp.data),
-**	SCR_MOVE_TBL ^ SCR_DATA_OUT,
-**		offsetof (struct dsb, data[ 0]),
-**
-**  ##===========< i=1; i<MAX_SCATTER >=========
+**  ##===========< i=0; i<MAX_SCATTER >=========
 **  ||	SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)),
 **  ||		PADDR (dispatch),
 **  ||	SCR_MOVE_TBL ^ SCR_DATA_OUT,
@@ -3071,8 +3453,7 @@
 **
 **---------------------------------------------------------
 */
-0	/* was (u_long)&ident ? */
-
+0
 }/*-------------------------< ABORTTAG >-------------------*/,{
 	/*
 	**      Abort a bad reselection.
@@ -3146,12 +3527,14 @@
 **==========================================================
 */
 
-void ncr_script_fill (struct script * scr)
+__initfunc(
+void ncr_script_fill (struct script * scr, struct scripth * scrh)
+)
 {
 	int	i;
 	ncrcmd	*p;
 
-	p = scr->tryloop;
+	p = scrh->tryloop;
 	for (i=0; i<MAX_START; i++) {
 		*p++ =SCR_COPY (4);
 		*p++ =NADDR (squeue[i]);
@@ -3160,21 +3543,13 @@
 		*p++ =PADDR (trysel);
 	};
 	*p++ =SCR_JUMP;
-	*p++ =PADDR(tryloop);
+	*p++ =PADDRH(tryloop);
 
-	assert ((u_long)p == (u_long)&scr->tryloop + sizeof (scr->tryloop));
+	assert ((u_long)p == (u_long)&scrh->tryloop + sizeof (scrh->tryloop));
 
 	p = scr->data_in;
 
-	*p++ =SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN));
-	*p++ =PADDR (no_data);
-	*p++ =SCR_COPY (sizeof (u_long));
-	*p++ =KVAR(SCRIPT_KVAR_JIFFIES);
-	*p++ =NADDR (header.stamp.data);
-	*p++ =SCR_MOVE_TBL ^ SCR_DATA_IN;
-	*p++ =offsetof (struct dsb, data[ 0]);
-
-	for (i=1; i<MAX_SCATTER; i++) {
+	for (i=0; i<MAX_SCATTER; i++) {
 		*p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN));
 		*p++ =PADDR (checkatn);
 		*p++ =SCR_MOVE_TBL ^ SCR_DATA_IN;
@@ -3188,17 +3563,9 @@
 
 	assert ((u_long)p == (u_long)&scr->data_in + sizeof (scr->data_in));
 
-	p = scr->data_out;
-
-	*p++ =SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_OUT));
-	*p++ =PADDR (no_data);
-	*p++ =SCR_COPY (sizeof (u_long));
-	*p++ =KVAR(SCRIPT_KVAR_JIFFIES);
-	*p++ =NADDR (header.stamp.data);
-	*p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT;
-	*p++ =offsetof (struct dsb, data[ 0]);
+	p = scrh->data_out;
 
-	for (i=1; i<MAX_SCATTER; i++) {
+	for (i=0; i<MAX_SCATTER; i++) {
 		*p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT));
 		*p++ =PADDR (dispatch);
 		*p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT;
@@ -3210,7 +3577,7 @@
 	*p++ =SCR_JUMP;
 	*p++ =PADDR (no_data);
 
-	assert ((u_long)p == (u_long)&scr->data_out + sizeof (scr->data_out));
+	assert ((u_long)p == (u_long)&scrh->data_out + sizeof (scrh->data_out));
 }
 
 /*==========================================================
@@ -3222,19 +3589,17 @@
 **==========================================================
 */
 
-static void ncr_script_copy_and_bind (struct script *script, ncb_p np)
+__initfunc(
+static void ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len)
+)
 {
 	ncrcmd  opcode, new, old, tmp1, tmp2;
-	ncrcmd	*src, *dst, *start, *end;
+	ncrcmd	*start, *end;
 	int relocs;
-
-	np->p_script = vtophys(np->script);
-
-	src = script->start;
-	dst = np->script->start;
+	int opchanged = 0;
 
 	start = src;
-	end = src + (sizeof (struct script) / 4);
+	end = src + len/4;
 
 	while (src < end) {
 
@@ -3278,6 +3643,14 @@
 					ncr_name(np), (int) (src-start-1));
 				DELAY (1000000);
 			}
+			/*
+			**	If PREFETCH feature not enabled, remove 
+			**	the NO FLUSH bit if present.
+			*/
+			if ((opcode & SCR_NO_FLUSH) && !(np->features & FE_PFEN)) {
+				dst[-1] = (opcode & ~SCR_NO_FLUSH);
+				++opchanged;
+			}
 			break;
 
 		case 0x0:
@@ -3321,6 +3694,9 @@
 				case RELOC_LABEL:
 					new = (old & ~RELOC_MASK) + np->p_script;
 					break;
+				case RELOC_LABELH:
+					new = (old & ~RELOC_MASK) + np->p_scripth;
+					break;
 				case RELOC_SOFTC:
 					new = (old & ~RELOC_MASK) + vtophys(np);
 					break;
@@ -3351,6 +3727,9 @@
 			*dst++ = *src++;
 
 	};
+	if (bootverbose > 1 && opchanged)
+		printf("%s: NO FLUSH bit removed from %d script instructions\n",
+			ncr_name(np), opchanged); 
 }
 
 /*==========================================================
@@ -3362,10 +3741,6 @@
 **==========================================================
 */
 
-#define	MIN_ASYNC_PD	40
-#define	MIN_SYNC_PD	20
-
-
 /*
 **	Linux host data structure
 **
@@ -3375,25 +3750,526 @@
 */
 
 struct host_data {
-     struct ncb ncb_data;
+     struct ncb *ncb;
+
+     char ncb_align[NCB_ALIGN_SIZE-1];	/* Filler for alignment */
+     struct ncb _ncb_data;
+
+     char ccb_align[CCB_ALIGN_SIZE-1];	/* Filler for alignment */
+     struct ccb _ccb_data;
+
+     char scr_align[SCR_ALIGN_SIZE-1];	/* Filler for alignment */
      struct script script_data;
+
+     struct scripth scripth_data;
 };
 
 /*
-**	Print something which allow to retreive the controler type, unit,
+**	Print something which allow to retrieve the controler type, unit,
 **	target, lun concerned by a kernel message.
 */
 
 #define PRINT_LUN(np, target, lun) \
-printf("%s-<target %d, lun %d>: ", ncr_name(np), (int) (target), (int) (lun))
+printf(KERN_INFO "%s-<%d,%d>: ", ncr_name(np), (int) (target), (int) (lun))
 
-static inline void PRINT_ADDR(Scsi_Cmnd *cmd)
+static void PRINT_ADDR(Scsi_Cmnd *cmd)
 {
 	struct host_data *host_data = (struct host_data *) cmd->host->hostdata;
-	ncb_p np                    = &host_data->ncb_data;
+	ncb_p np                    = host_data->ncb;
 	if (np) PRINT_LUN(np, cmd->target, cmd->lun);
 }
 
+/*==========================================================
+**
+**	NCR chip clock divisor table.
+**	Divisors are multiplied by 10,000,000 in order to make 
+**	calculations more simple.
+**
+**==========================================================
+*/
+
+#define _5M 5000000
+static u_long div_10M[] =
+	{2*_5M, 3*_5M, 4*_5M, 6*_5M, 8*_5M, 12*_5M, 16*_5M};
+
+
+/*===============================================================
+**
+**	Prepare io register values used by ncr_init() according 
+**	to selected and supported features.
+**
+**	NCR chips allow burst lengths of 2, 4, 8, 16, 32, 64, 128 
+**	transfers. 32,64,128 are only supported by 875 and 895 chips.
+**	We use log base 2 (burst length) as internal code, with 
+**	value 0 meaning "burst disabled".
+**
+**===============================================================
+*/
+
+/*
+ *	Burst length from burst code.
+ */
+#define burst_length(bc) (!(bc))? 0 : 1 << (bc)
+
+/*
+ *	Burst code from io register bits.
+ */
+#define burst_code(dmode, ctest4, ctest5) \
+	(ctest4) & 0x80? 0 : (((dmode) & 0xc0) >> 6) + ((ctest5) & 0x04) + 1
+
+/*
+ *	Set initial io register bits from burst code.
+ */
+static inline void ncr_init_burst(ncb_p np, u_char bc)
+{
+	np->rv_ctest4	&= ~0x80;
+	np->rv_dmode	&= ~(0x3 << 6);
+	np->rv_ctest5	&= ~0x4;
+
+	if (!bc) {
+		np->rv_ctest4	|= 0x80;
+	}
+	else {
+		--bc;
+		np->rv_dmode	|= ((bc & 0x3) << 6);
+		np->rv_ctest5	|= (bc & 0x4);
+	}
+}
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+
+/*
+**	Get target set-up from Symbios format NVRAM.
+*/
+
+__initfunc(
+static void
+	ncr_Symbios_setup_target(ncb_p np, int target, Symbios_nvram *nvram)
+)
+{
+	tcb_p tp = &np->target[target];
+	Symbios_target *tn = &nvram->target[target];
+
+	tp->usrsync = tn->sync_period ? (tn->sync_period + 3) / 4 : 255;
+	tp->usrwide = tn->bus_width == 0x10 ? 1 : 0;
+	tp->usrtags =
+		(tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? SCSI_NCR_MAX_TAGS : 0;
+
+	if (!(tn->flags & SYMBIOS_DISCONNECT_ENABLE))
+		tp->usrflag |= UF_NODISC;
+	if (!(tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME))
+		tp->usrflag |= UF_NOSCAN;
+}
+
+/*
+**	Get target set-up from Tekram format NVRAM.
+*/
+
+__initfunc(
+static void
+	ncr_Tekram_setup_target(ncb_p np, int target, Tekram_nvram *nvram)
+)
+{
+	tcb_p tp = &np->target[target];
+	struct Tekram_target *tn = &nvram->target[target];
+	int i;
+
+	if (tn->flags & TEKRAM_SYNC_NEGO) {
+		i = tn->sync_index & 0xf;
+		tp->usrsync = i < 12 ? Tekram_sync[i] : 255;
+	}
+
+	tp->usrwide = (tn->flags & TEKRAM_WIDE_NEGO) ? 1 : 0;
+
+	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))
+		tp->usrflag = UF_NODISC;
+ 
+	/* If any device does not support parity, we will not use this option */
+	if (!(tn->flags & TEKRAM_PARITY_CHECK))
+		np->rv_scntl0  &= ~0x0a; /* SCSI parity checking disabled */
+}
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
+__initfunc(
+static int ncr_prepare_setting(ncb_p np, ncr_nvram *nvram)
+)
+{
+	u_char	burst_max;
+	u_long	period;
+	int i;
+
+	/*
+	**	Save assumed BIOS setting
+	*/
+
+	np->sv_scntl0	= INB(nc_scntl0) & 0x0a;
+	np->sv_scntl3	= INB(nc_scntl3) & 0x07;
+	np->sv_dmode	= INB(nc_dmode)  & 0xce;
+	np->sv_dcntl	= INB(nc_dcntl)  & 0xa8;
+	np->sv_ctest3	= INB(nc_ctest3) & 0x01;
+	np->sv_ctest4	= INB(nc_ctest4) & 0x80;
+	np->sv_ctest5	= INB(nc_ctest5) & 0x24;
+	np->sv_gpcntl	= INB(nc_gpcntl);
+	np->sv_stest2	= INB(nc_stest2) & 0x20;
+	np->sv_stest4	= INB(nc_stest4);
+
+	/*
+	**	Wide ?
+	*/
+
+	np->maxwide	= (np->features & FE_WIDE)? 1 : 0;
+
+	/*
+	**	Get the frequency of the chip's clock.
+	**	Find the right value for scntl3.
+	*/
+
+	if	(np->features & FE_QUAD)
+		np->multiplier	= 4;
+	else if	(np->features & FE_DBLR)
+		np->multiplier	= 2;
+	else
+		np->multiplier	= 1;
+
+	np->clock_khz	= (np->features & FE_CLK80)? 80000 : 40000;
+	np->clock_khz	*= np->multiplier;
+
+	if (np->clock_khz != 40000)
+		ncr_getclock(np, np->multiplier);
+
+	/*
+	 * Divisor to be used for async (timer pre-scaler).
+	 */
+	i = np->clock_divn - 1;
+	while (i >= 0) {
+		--i;
+		if (10ul * SCSI_NCR_MIN_ASYNC * np->clock_khz > div_10M[i]) {
+			++i;
+			break;
+		}
+	}
+	np->rv_scntl3 = i+1;
+
+	/*
+	 * Minimum synchronous period factor supported by the chip.
+	 * Btw, 'period' is in tenths of nanoseconds.
+	 */
+
+	period = (4 * div_10M[0] + np->clock_khz - 1) / np->clock_khz;
+	if	(period <= 250)		np->minsync = 10;
+	else if	(period <= 303)		np->minsync = 11;
+	else if	(period <= 500)		np->minsync = 12;
+	else				np->minsync = (period + 40 - 1) / 40;
+
+	/*
+	 * Check against chip SCSI standard support (SCSI-2,ULTRA,ULTRA2).
+	 */
+
+	if	(np->minsync < 25 && !(np->features & (FE_ULTRA|FE_ULTRA2)))
+		np->minsync = 25;
+	else if	(np->minsync < 12 && !(np->features & FE_ULTRA2))
+		np->minsync = 12;
+
+	/*
+	 * Maximum synchronous period factor supported by the chip.
+	 */
+
+	period = (11 * div_10M[np->clock_divn - 1]) / (4 * np->clock_khz);
+	np->maxsync = period > 2540 ? 254 : period / 10;
+
+	/*
+	**	Get on-board RAM bus address when supported
+	*/
+	if (np->features & FE_RAM) {
+		OUTONB(nc_ctest2, 0x8);
+		np->paddr2 = INL(nc_scr0);
+		OUTOFFB(nc_ctest2, 0x8);
+	}
+
+	/*
+	**	Prepare initial value of other IO registers
+	*/
+#if defined SCSI_NCR_TRUST_BIOS_SETTING
+	np->rv_scntl0	= np->sv_scntl0;
+	np->rv_dmode	= np->sv_dmode;
+	np->rv_dcntl	= np->sv_dcntl;
+	np->rv_ctest3	= np->sv_ctest3;
+	np->rv_ctest4	= np->sv_ctest4;
+	np->rv_ctest5	= np->sv_ctest5;
+	burst_max	= burst_code(np->sv_dmode, np->sv_ctest4, np->sv_ctest5);
+#else
+
+	/*
+	**	Select burst length (dwords)
+	*/
+	burst_max	= driver_setup.burst_max;
+	if (burst_max == 255)
+		burst_max = burst_code(np->sv_dmode, np->sv_ctest4, np->sv_ctest5);
+	if (burst_max > 7)
+		burst_max = 7;
+	if (burst_max > np->maxburst)
+		burst_max = np->maxburst;
+
+	/*
+	**	Select all supported special features
+	*/
+	if (np->features & FE_ERL)
+		np->rv_dmode	|= ERL;		/* Enable Read Line */
+	if (np->features & FE_BOF)
+		np->rv_dmode	|= BOF;		/* Burst Opcode Fetch */
+	if (np->features & FE_ERMP)
+		np->rv_dmode	|= ERMP;	/* Enable Read Multiple */
+	if (np->features & FE_PFEN)
+		np->rv_dcntl	|= PFEN;	/* Prefetch Enable */
+	if (np->features & FE_CLSE)
+		np->rv_dcntl	|= CLSE;	/* Cache Line Size Enable */
+	if (np->features & FE_WRIE)
+		np->rv_ctest3	|= WRIE;	/* Write and Invalidate */
+	if (np->features & FE_DFS)
+		np->rv_ctest5	|= DFS;		/* Dma Fifo Size */
+
+	/*
+	**	Select some other
+	*/
+	if (driver_setup.master_parity)
+		np->rv_ctest4	|= MPEE;	/* Master parity checking */
+	if (driver_setup.scsi_parity)
+		np->rv_scntl0	|= 0x0a;	/*  full arb., ena parity, par->ATN  */
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+	/*
+	**	Get parity checking, host ID and verbose mode from NVRAM
+	**/
+	if (nvram) {
+		switch(nvram->type) {
+		case SCSI_NCR_TEKRAM_NVRAM:
+			np->myaddr = nvram->data.Tekram.host_id & 0x0f;
+			break;
+		case SCSI_NCR_SYMBIOS_NVRAM:
+			if (!(nvram->data.Symbios.flags & SYMBIOS_PARITY_ENABLE))
+				np->rv_scntl0  &= ~0x0a;
+			np->myaddr = nvram->data.Symbios.host_id & 0x0f;
+			if (nvram->data.Symbios.flags & SYMBIOS_VERBOSE_MSGS)
+				np->verbose += 1;
+			break;
+		}
+	}
+#endif
+	/*
+	**  Get SCSI addr of host adapter (set by bios?).
+	*/
+	if (!np->myaddr) np->myaddr = INB(nc_scid) & 0x07;
+	if (!np->myaddr) np->myaddr = SCSI_NCR_MYADDR;
+
+
+#endif /* SCSI_NCR_TRUST_BIOS_SETTING */
+
+	/*
+	 *	Prepare initial io register bits for burst length
+	 */
+	ncr_init_burst(np, burst_max);
+
+	/*
+	**	Set differential mode and LED support.
+	**	Ignore these features for boards known to use a 
+	**	specific GPIO wiring (Tekram only for now).
+	**	Probe initial setting of GPREG and GPCNTL for 
+	**	other ones.
+	*/
+	if (!nvram || nvram->type != SCSI_NCR_TEKRAM_NVRAM) {
+		switch(driver_setup.diff_support) {
+		case 3:
+			if (INB(nc_gpreg) & 0x08)
+			break;
+		case 2:
+			np->rv_stest2	|= 0x20;
+			break;
+		case 1:
+			np->rv_stest2	|= (np->sv_stest2 & 0x20);
+			break;
+		default:
+			break;
+		}
+	}
+	if ((driver_setup.led_pin ||
+	     (nvram && nvram->type == SCSI_NCR_SYMBIOS_NVRAM)) &&
+	    !(np->sv_gpcntl & 0x01))
+		np->features |= FE_LED0;
+
+	/*
+	**	Set irq mode.
+	*/
+	switch(driver_setup.irqm) {
+	case 2:
+		np->rv_dcntl	|= IRQM;
+		break;
+	case 1:
+		np->rv_dcntl	|= (np->sv_dcntl & IRQM);
+		break;
+	default:
+		break;
+	}
+
+	/*
+	**	Configure targets according to driver setup.
+	**	If NVRAM present get targets setup from NVRAM.
+	**	Allow to override sync, wide and NOSCAN from 
+	**	boot command line.
+	*/
+	for (i = 0 ; i < MAX_TARGET ; i++) {
+		tcb_p tp = &np->target[i];
+
+		tp->usrsync = 255;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+		if (nvram) {
+			switch(nvram->type) {
+			case SCSI_NCR_TEKRAM_NVRAM:
+				ncr_Tekram_setup_target(np, i, &nvram->data.Tekram);
+				break;
+			case SCSI_NCR_SYMBIOS_NVRAM:
+				ncr_Symbios_setup_target(np, i, &nvram->data.Symbios);
+				break;
+			}
+			if (driver_setup.use_nvram & 0x2)
+				tp->usrsync = driver_setup.default_sync;
+			if (driver_setup.use_nvram & 0x4)
+				tp->usrwide = driver_setup.max_wide;
+			if (driver_setup.use_nvram & 0x8)
+				tp->usrflag &= ~UF_NOSCAN;
+		}
+		else {
+#else
+		if (1) {
+#endif
+			tp->usrsync = driver_setup.default_sync;
+			tp->usrwide = driver_setup.max_wide;
+			tp->usrtags = driver_setup.default_tags;
+			if (!driver_setup.disconnection)
+				np->target[i].usrflag = UF_NODISC;
+		}
+	}
+
+	/*
+	**	Announce all that stuff to user.
+	*/
+
+	i = nvram ? nvram->type : 0;
+	printf(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,
+		np->minsync < 12 ? 40 : (np->minsync < 25 ? 20 : 10),
+		(np->rv_scntl0 & 0xa)	? ", Parity Checking"	: ", NO Parity",
+		(np->rv_stest2 & 0x20)	? ", Differential"	: "");
+
+	if (bootverbose > 1) {
+		printf ("%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 = "
+			"(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",
+			ncr_name(np), np->paddr2);
+
+	return 0;
+}
+
+
+#ifdef SCSI_NCR_DEBUG_NVRAM
+
+__initfunc(
+void ncr_display_Symbios_nvram(ncb_p np, Symbios_nvram *nvram)
+)
+{
+	int i;
+
+	/* display Symbios nvram host data */
+	printf("%s: HOST ID=%d%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->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",
+		ncr_name(np), i,
+		(tn->flags & SYMBIOS_DISCONNECT_ENABLE)	? " DISC"	: "",
+		(tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME)	? " SCAN_BOOT"	: "",
+		(tn->flags & SYMBIOS_SCAN_LUNS)		? " SCAN_LUNS"	: "",
+		(tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? " TCQ"	: "",
+		tn->bus_width,
+		tn->sync_period / 4,
+		tn->timeout);
+	}
+}
+
+static u_char Tekram_boot_delay[7] __initdata = {3, 5, 10, 20, 30, 60, 120};
+
+__initfunc(
+void ncr_display_Tekram_nvram(ncb_p np, Tekram_nvram *nvram)
+)
+{
+	int i, tags, boot_delay;
+	char *rem;
+
+	/* display Tekram nvram host data */
+	tags = 2 << nvram->max_tags_index;
+	boot_delay = 0;
+	if (nvram->boot_delay_index < 6)
+		boot_delay = Tekram_boot_delay[nvram->boot_delay_index];
+	switch((nvram->flags & TEKRAM_REMOVABLE_FLAGS) >> 6) {
+	default:
+	case 0:	rem = "";			break;
+	case 1: rem = " REMOVABLE=boot device";	break;
+	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",
+		ncr_name(np), nvram->host_id & 0x0f,
+		(nvram->flags1 & SYMBIOS_SCAM_ENABLE)	? " SCAM"	:"",
+		(nvram->flags & TEKRAM_MORE_THAN_2_DRIVES) ? " >2DRIVES"	:"",
+		(nvram->flags & TEKRAM_DRIVES_SUP_1GB)	? " >1GB"	:"",
+		(nvram->flags & TEKRAM_RESET_ON_POWER_ON) ? " RESET"	:"",
+		(nvram->flags & TEKRAM_ACTIVE_NEGATION)	? " ACT_NEG"	:"",
+		(nvram->flags & TEKRAM_IMMEDIATE_SEEK)	? " IMM_SEEK"	:"",
+		(nvram->flags & TEKRAM_SCAN_LUNS)	? " SCAN_LUNS"	:"",
+		(nvram->flags1 & TEKRAM_F2_F6_ENABLED)	? " F2_F6"	:"",
+		rem, boot_delay, tags);
+
+	/* display Tekram nvram drive data */
+	for (i = 0; i <= 15; i++) {
+		int sync, j;
+		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",
+		ncr_name(np), i,
+		(tn->flags & TEKRAM_PARITY_CHECK)	? " PARITY"	: "",
+		(tn->flags & TEKRAM_SYNC_NEGO)		? " SYNC"	: "",
+		(tn->flags & TEKRAM_DISCONNECT_ENABLE)	? " DISC"	: "",
+		(tn->flags & TEKRAM_START_CMD)		? " START"	: "",
+		(tn->flags & TEKRAM_TAGGED_COMMANDS)	? " TCQ"	: "",
+		(tn->flags & TEKRAM_WIDE_NEGO)		? " WIDE"	: "",
+		sync);
+	}
+}
+#endif /* SCSI_NCR_DEBUG_NVRAM */
 
 /*
 **	Host attach and initialisations.
@@ -3401,24 +4277,23 @@
 **	Allocate host data and ncb structure.
 **	Request IO region and remap MMIO region.
 **	Do chip initialization.
-**	Try with mmio.
-**	If mmio not possible (misconfigured cache),
-**	retry with io mapped.
 **	If all is OK, install interrupt handling and
 **	start the timer daemon.
 */
 
-static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ushort device_id,
-		       u_char revision_id, int chip, u_int base, u_int io_port, 
-		       int irq, int bus, u_char device_fn)
-
+__initfunc(
+static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
+)
 {
         struct host_data *host_data;
 	ncb_p np;
         struct Scsi_Host *instance = 0;
 	u_long flags = 0;
+	ncr_nvram *nvram = device->nvram;
 
-printf("ncr_attach: unit=%d chip=%d base=%x, io_port=%x, irq=%d\n", unit, chip, base, io_port, irq);
+printf(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%x, io_port=0x%x, irq=%d\n",
+	device->chip.name, unit, device->chip.revision_id, device->slot.base,
+	device->slot.io_port, device->slot.irq);
 
 	/*
 	**	Allocate host_data structure
@@ -3429,16 +4304,36 @@
 	/*
 	**	Initialize structure.
 	*/
-	instance->irq = irq;
 	host_data = (struct host_data *) instance->hostdata;
 
-	np        = &host_data->ncb_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);
+	host_data->ncb = np;
 	bzero (np, sizeof (*np));
-	np->unit = unit;
-	np->chip = chip;
-	np->device_id	= device_id;
-	np->revision_id	= revision_id;
-	np->script = &host_data->script_data;
+
+	np->ccb   = (ccb_p) (((u_long) &host_data->_ccb_data) & CCB_ALIGN_MASK);
+	bzero (np->ccb, sizeof (*np->ccb));
+
+	/*
+	**	Store input informations in the host data structure.
+	*/
+	strncpy(np->chip_name, device->chip.name, sizeof(np->chip_name) - 1);
+	np->unit	= unit;
+	np->verbose	= driver_setup.verbose;
+	sprintf(np->inst_name, "ncr53c%s-%d", np->chip_name, np->unit);
+	np->device_id	= device->chip.device_id;
+	np->revision_id	= device->chip.revision_id;
+	np->features	= device->chip.features;
+	np->clock_divn	= device->chip.nr_divisor;
+	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->scripth0 = &host_data->scripth_data;
 
 	/*
 	**    Initialize timer structure
@@ -3453,38 +4348,74 @@
 	**	virtual and physical memory.
 	*/
 
-	np->paddr = base;
-	np->vaddr = base;
+	np->paddr = device->slot.base;
 
 #ifndef NCR_IOMAPPED
-	np->reg_remapped = (struct ncr_reg *) remap_pci_mem((u_long) base, (u_long) 128);
-	if (!np->reg_remapped) {
+	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));
-		np->use_mmio = 0;
+		goto attach_error;
 	}
-	printf("%s: using memory mapped IO at virtual address 0x%lx\n", ncr_name(np), (u_long) np->reg_remapped);
-		np->use_mmio = 1;
-#endif
+	else
+		if (bootverbose > 1)
+			printf("%s: using memory mapped IO at virtual address 0x%lx\n", ncr_name(np), (u_long) np->vaddr);
+
+	/*
+	**	Make the controller's registers available.
+	**	Now the INB INW INL OUTB OUTW OUTL macros
+	**	can be used safely.
+	*/
+
+	np->reg = (struct ncr_reg*) np->vaddr;
+
+#endif /* !defined NCR_IOMAPPED */
+
 	/*
 	**	Try to map the controller chip into iospace.
 	*/
 
-	request_region(io_port, 128, "ncr53c8xx");
-	np->port = io_port;
+	request_region(device->slot.io_port, 128, "ncr53c8xx");
+	np->port = device->slot.io_port;
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+	if (nvram) {
+		switch(nvram->type) {
+		case SCSI_NCR_SYMBIOS_NVRAM:
+#ifdef SCSI_NCR_DEBUG_NVRAM
+			ncr_display_Symbios_nvram(np, &nvram->data.Symbios);
+#endif
+			break;
+		case SCSI_NCR_TEKRAM_NVRAM:
+#ifdef SCSI_NCR_DEBUG_NVRAM
+			ncr_display_Tekram_nvram(np, &nvram->data.Tekram);
+#endif
+			break;
+		default:
+			nvram = 0;
+#ifdef SCSI_NCR_DEBUG_NVRAM
+			printf("%s: NVRAM: None or invalid data.\n", ncr_name(np));
+#endif
+		}
+	}
+#endif
 
 	/*
 	**	Do chip dependent initialization.
 	*/
+	(void)ncr_prepare_setting(np, nvram);
 
-	switch (device_id) {
-	case PCI_DEVICE_ID_NCR_53C825:
-	case PCI_DEVICE_ID_NCR_53C875:
-		np->maxwide = 1;
-		break;
-	default:
-		np->maxwide = 0;
-		break;
+#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);
 	}
+#endif /* !defined NCR_IOMAPPED */
 
 	/*
 	**	Fill Linux host instance structure
@@ -3495,9 +4426,10 @@
 	instance->max_lun	= SCSI_NCR_MAX_LUN;
 #endif
 #ifndef NCR_IOMAPPED
-	instance->base		= (char *) np->reg_remapped;
+	instance->base		= (char *) np->reg;
 #endif
-	instance->io_port	= io_port;
+	instance->irq		= device->slot.irq;
+	instance->io_port	= device->slot.io_port;
 	instance->n_io_port	= 128;
 	instance->dma_channel	= 0;
 #if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0)
@@ -3507,58 +4439,39 @@
 	/*
 	**	Patch script to physical addresses
 	*/
-	ncr_script_fill (&script0);
-	ncr_script_copy_and_bind (&script0, np);
-	np->ccb.p_ccb		= vtophys (&np->ccb);
+	ncr_script_fill (&script0, &scripth0);
 
-	/*
-	**	init data structure
-	*/
-
-	np->jump_tcb.l_cmd	= SCR_JUMP;
-	np->jump_tcb.l_paddr	= NCB_SCRIPT_PHYS (np, abort);
-
-	/*
-	**	Make the controller's registers available.
-	**	Now the INB INW INL OUTB OUTW OUTL macros
-	**	can be used safely.
-	*/
+	np->scripth	= np->scripth0;
+	np->p_scripth	= vtophys(np->scripth);
 
-	np->reg = (struct ncr_reg*) np->vaddr;
+	np->script	= (np->vaddr2) ? (struct script *) np->vaddr2 : np->script0;
+	np->p_script	= (np->vaddr2) ? np->paddr2 : vtophys(np->script0);
 
-#ifndef NCR_IOMAPPED
-retry_chip_init:
-#endif
+	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));
+	np->ccb->p_ccb		= vtophys (np->ccb);
 
 	/*
-	**  Get SCSI addr of host adapter (set by bios?).
+	**    Patch the script for LED support.
 	*/
 
-	np->myaddr = INB(nc_scid) & 0x07;
-	if (!np->myaddr) np->myaddr = SCSI_NCR_MYADDR;
+	if (np->features & FE_LED0) {
+		np->script0->reselect[0]  = SCR_REG_REG(gpreg, SCR_OR,  0x01);
+		np->script0->reselect1[0] = SCR_REG_REG(gpreg, SCR_AND, 0xfe);
+		np->script0->reselect2[0] = SCR_REG_REG(gpreg, SCR_AND, 0xfe);
+	}
 
 	/*
-	**	Get the value of the chip's clock.
-	**	Find the right value for scntl3.
+	**	init data structure
 	*/
 
-	ncr_getclock (np, INB(nc_scntl3));
+	np->jump_tcb.l_cmd	= SCR_JUMP;
+	np->jump_tcb.l_paddr	= NCB_SCRIPTH_PHYS (np, abort);
 
 	/*
 	**	Reset chip.
 	*/
 
-	OUTW (nc_sien , 0);	/* Disable scsi interrupts */
-	OUTB (nc_dien , 0);	/* Disable dma interrupts */
-
-	OUTB (nc_istat,  SRST);
-	DELAY (1000);
-	OUTB (nc_istat,  0   );
-
-	/*
-	**	Reset chip, once again.
-	*/
-
 	OUTB (nc_istat,  SRST);
 	DELAY (1000);
 	OUTB (nc_istat,  0   );
@@ -3568,14 +4481,6 @@
 	*/
 
 	if (ncr_snooptest (np)) {
-#ifndef NCR_IOMAPPED
-		if (np->use_mmio) {
-printf("%s: cache misconfigured, retrying with IO mapped at 0x%lx\n",
-	ncr_name(np), (u_long) np->port);
-			np->use_mmio = 0;
-			goto retry_chip_init;
-		}
-#endif
 		printf ("CACHE INCORRECTLY CONFIGURED.\n");
 		goto attach_error;
 	};
@@ -3584,55 +4489,50 @@
 	**	Install the interrupt handler.
 	*/
 #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
-#   ifdef SCSI_NCR_SHARE_IRQ
-	printf("%s: requesting shared irq %d (dev_id=0x%lx)\n",
-	        ncr_name(np), irq, (u_long) np);
-	if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT|SA_SHIRQ, "53c8xx", np)) {
-#   else
-	if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT, "53c8xx", NULL)) {
-#   endif
+#ifdef SCSI_NCR_SHARE_IRQ
+	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)) {
+#else
+	if (request_irq(device->slot.irq, ncr53c8xx_intr,
+			SA_INTERRUPT, "ncr53c8xx", NULL)) {
+#endif
 #else
-	if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT, "53c8xx")) {
+	if (request_irq(device->slot.irq, ncr53c8xx_intr,
+			SA_INTERRUPT, "ncr53c8xx")) {
 #endif
-		printf("%s: request irq %d failure\n", ncr_name(np), irq);
+		printf("%s: request irq %d failure\n", ncr_name(np), device->slot.irq);
 		goto attach_error;
 	}
-	np->irq = irq;
+	np->irq = device->slot.irq;
 
 	/*
 	**	After SCSI devices have been opened, we cannot
 	**	reset the bus safely, so we do it here.
 	**	Interrupt handler does the real work.
-	*/
-
-	OUTB (nc_scntl1, CRST);
-	DELAY (1000);
-
-	/*
 	**	Process the reset exception,
 	**	if interrupts are not enabled yet.
 	**	Then enable disconnects.
 	*/
 	save_flags(flags); cli();
+	ncr_start_reset(np, driver_setup.settle_delay);
 	ncr_exception (np);
 	restore_flags(flags);
 
-#ifndef SCSI_NCR_NO_DISCONNECT
 	np->disc = 1;
-#endif
 
 	/*
 	**	The middle-level SCSI driver does not
 	**	wait devices to settle.
+	**	Wait synchronously if more than 2 seconds.
 	*/
-#ifdef SCSI_NCR_SETTLE_TIME
-#if    SCSI_NCR_SETTLE_TIME > 2
-	printf("%s: waiting for scsi devices to settle...\n", ncr_name(np));
-#endif
-#if    SCSI_NCR_SETTLE_TIME > 0
-	DELAY(SCSI_NCR_SETTLE_TIME*1000000);
-#endif
-#endif
+	if (driver_setup.settle_delay > 2) {
+		printf("%s: waiting %d seconds for scsi devices to settle...\n",
+			ncr_name(np), driver_setup.settle_delay);
+		DELAY(1000000UL * driver_setup.settle_delay);
+	}
 
 	/*
 	**	Now let the generic SCSI driver
@@ -3642,8 +4542,8 @@
 	/*
 	**	start the timeout daemon
 	*/
-	ncr_timeout (np);
 	np->lasttime=0;
+	ncr_timeout (np);
 
 	/*
 	**  use SIMPLE TAG messages by default
@@ -3664,14 +4564,25 @@
 
 attach_error:
 	if (!instance) return -1;
+	printf("%s: detaching...\n", ncr_name(np));
 #ifndef NCR_IOMAPPED
-	if (np->reg_remapped) {
-		printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->reg_remapped, 128);
-		unmap_pci_mem((vm_offset_t) np->reg_remapped, (u_long) 128);
+	if (np->vaddr) {
+#ifdef DEBUG_NCR53C8XX
+		printf("%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
 	if (np->port) {
+#ifdef DEBUG_NCR53C8XX
 		printf("%s: releasing IO region %x[%d]\n", ncr_name(np), np->port, 128);
+#endif
 		release_region(np->port, 128);
 	}
 	scsi_unregister(instance);
@@ -3682,47 +4593,6 @@
 /*==========================================================
 **
 **
-**	Process pending device interrupts.
-**
-**
-**==========================================================
-*/
-int ncr_intr(np)
-	ncb_p np;
-{
-	int n = 0;
-	u_long flags;
-
-	save_flags(flags); cli();
-
-	if (DEBUG_FLAGS & DEBUG_TINY) printf ("[");
-
-#ifdef SCSI_NCR_PARANOIA
-	if (INB(nc_istat) & (INTF|SIP|DIP)) {
-		/*
-		**	Repeat until no outstanding ints
-		*/
-		do {
-#endif
-			ncr_exception (np);
-#ifdef SCSI_NCR_PARANOIA
-		} while (INB(nc_istat) & (INTF|SIP|DIP));
-
-		n=1;
-		np->ticks = 5 * HZ;
-	};
-#endif
-
-	if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n");
-
-	restore_flags(flags);
-
-	return (n);
-}
-
-/*==========================================================
-**
-**
 **	Start execution of a SCSI command.
 **	This is called from the generic SCSI driver.
 **
@@ -3734,7 +4604,7 @@
         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_data;
+	ncb_p np                      = host_data->ncb;
 	tcb_p tp                      = &np->target[cmd->target];
 
 	ccb_p cp;
@@ -3742,7 +4612,7 @@
 
 	int	segments;
 	u_char	qidx, nego, idmsg, *msgptr;
-	u_long  msglen, msglen2;
+	u_int  msglen, msglen2;
 	u_long	flags;
 	int	xfer_direction;
 
@@ -3753,22 +4623,6 @@
 
 	/*---------------------------------------------
 	**
-	**   Reset SCSI bus
-	**
-	**	Interrupt handler does the real work.
-	**
-	**---------------------------------------------
-	*/
-#if 0
-	if (flags & SCSI_RESET) {
-		OUTB (nc_scntl1, CRST);
-		DELAY (1000);
-		return(COMPLETE);
-	}
-#endif
-
-	/*---------------------------------------------
-	**
 	**      Some shortcuts ...
 	**
 	**---------------------------------------------
@@ -3779,6 +4633,19 @@
 		return(DID_BAD_TARGET);
         }
 
+	/*---------------------------------------------
+	**
+	**	Complete the 1st TEST UNIT READY command
+	**	with error condition if the device is 
+	**	flagged NOSCAN, in order to speed up 
+	**	the boot.
+	**
+	**---------------------------------------------
+	*/
+	if (cmd->cmnd[0] == 0 && (tp->usrflag & UF_NOSCAN)) {
+		tp->usrflag &= ~UF_NOSCAN;
+		return DID_BAD_TARGET;
+	}
 
 	if (DEBUG_FLAGS & DEBUG_TINY) {
 		PRINT_ADDR(cmd);
@@ -3788,13 +4655,14 @@
 	/*---------------------------------------------------
 	**
 	**	Assign a ccb / bind cmd
-	**	If no free ccb, insert cmd into the waiting list.
+	**	If resetting or no free ccb,
+	**	insert cmd into the waiting list.
 	**
 	**----------------------------------------------------
 	*/
 	save_flags(flags); cli();
 
-        if (!(cp=ncr_get_ccb (np, cmd->target, cmd->lun))) {
+        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);
@@ -3803,16 +4671,14 @@
 
 	/*---------------------------------------------------
 	**
-	**	Enable tagged queue if asked by user
+	**	Enable tagged queue if asked by scsi ioctl
 	**
 	**----------------------------------------------------
 	*/
-#ifdef SCSI_NCR_TAGGED_QUEUE_DISABLED
- 	if (cmd->device && cmd->device->tagged_queue &&
-	    (lp = tp->lp[cmd->lun]) && (!lp->usetags)) {
+	if (!tp->usrtags && cmd->device && cmd->device->tagged_queue) {
+		tp->usrtags = SCSI_NCR_MAX_TAGS;
 		ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS);
 	}
-#endif
 
 	/*---------------------------------------------------
 	**
@@ -3820,9 +4686,10 @@
 	**
 	**----------------------------------------------------
 	*/
-
+#ifdef SCSI_NCR_PROFILE_SUPPORT
 	bzero (&cp->phys.header.stamp, sizeof (struct tstamp));
 	cp->phys.header.stamp.start = jiffies;
+#endif
 
 	/*----------------------------------------------------
 	**
@@ -3857,7 +4724,8 @@
 
 	nego = 0;
 
-	if (cmd->lun == 0 && (tp->inqdata[2] & 0x7) >= 2 && tp->inqdata[7]) {
+	if (cmd->lun == 0 && !tp->nego_cp && 
+	    (tp->inqdata[2] & 0x7) >= 2 && tp->inqdata[7]) {
 		/*
 		**	negotiate wide transfers ?
 		*/
@@ -3874,7 +4742,7 @@
 		*/
 
 		if (!nego && !tp->period) {
-			if (SCSI_NCR_MAX_SYNC 
+			if ( 1
 #if defined (CDROM_ASYNC)
 			    && ((tp->inqdata[0] & 0x1f) != 5)
 #endif
@@ -3887,6 +4755,15 @@
 				printf ("asynchronous.\n");
 			};
 		};
+
+		/*
+		**	remember nego is pending for the target.
+		**	Avoid to start a nego for all queued commands 
+		**	when tagged command queuing is enabled.
+		*/
+
+		if (nego)
+			tp->nego_cp = cp;
 	};
 
 	/*---------------------------------------------------
@@ -3925,7 +4802,7 @@
 
 	idmsg = M_IDENTIFY | cmd->lun;
 
-	if ((cp!=&np->ccb) && (np->disc))
+	if (cp != np->ccb && ((np->disc && !(tp->usrflag & UF_NODISC)) || cp->tag))
 		idmsg |= 0x40;
 
 	msgptr = cp->scsi_smsg;
@@ -4013,7 +4890,7 @@
 	segments = ncr_scatter (cp, cp->cmd);
 
 	if (segments < 0) {
-		ncr_free_ccb(np, cp);
+		ncr_free_ccb(np, cp, cmd->target, cmd->lun);
 		restore_flags(flags);
 		return(DID_ERROR);
 	}
@@ -4028,10 +4905,12 @@
 	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:
@@ -4046,20 +4925,31 @@
 	**----------------------------------------------------
 	*/
 
+	cp->segments = segments;
+	if (!cp->data_len)
+		xfer_direction = XferNone;
+
 	switch (xfer_direction) {
+		u_long endp;
 	default:
+	case XferBoth:
+		cp->phys.header.savep = NCB_SCRIPT_PHYS (np, data_io);
+		cp->phys.header.goalp = cp->phys.header.savep;
+		break;
 	case XferIn:
-	     cp->phys.header.savep = NCB_SCRIPT_PHYS (np, data_in);
-	     cp->phys.header.goalp = cp->phys.header.savep +20 +segments*16;
-	     break;
+		endp = NCB_SCRIPT_PHYS (np, data_in) + MAX_SCATTER*16;
+		cp->phys.header.goalp = endp + 8;
+		cp->phys.header.savep = endp - segments*16;
+		break;
 	case XferOut:
-	     cp->phys.header.savep = NCB_SCRIPT_PHYS (np, data_out);
-	     cp->phys.header.goalp = cp->phys.header.savep +20 +segments*16;
-	     break;
+		endp = NCB_SCRIPTH_PHYS (np, data_out) + MAX_SCATTER*16;
+		cp->phys.header.goalp = endp + 8;
+		cp->phys.header.savep = endp - segments*16;
+		break;
 	case XferNone:
-	     cp->phys.header.savep = NCB_SCRIPT_PHYS (np, no_data);
-	     cp->phys.header.goalp = cp->phys.header.savep;
-	     break;
+		cp->phys.header.savep = NCB_SCRIPT_PHYS (np, no_data);
+		cp->phys.header.goalp = cp->phys.header.savep;
+		break;
 	}
 
 	cp->phys.header.lastp = cp->phys.header.savep;
@@ -4161,12 +5051,15 @@
 		printf ("%s: queuepos=%d tryoffset=%d.\n", ncr_name (np),
 		np->squeueput,
 		(unsigned)(np->script->startpos[0]- 
-			   (NCB_SCRIPT_PHYS (np, tryloop))));
+			   (NCB_SCRIPTH_PHYS (np, tryloop))));
 
 	/*
 	**	Script processor may be waiting for reselect.
 	**	Wake it up.
 	*/
+#ifdef	SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+	if (!np->stalling)
+#endif
 	OUTB (nc_istat, SIGP);
 
 	/*
@@ -4184,28 +5077,112 @@
 /*==========================================================
 **
 **
+**	Start reset process.
+**	If reset in progress do nothing.
+**	The interrupt handler will reinitialize the chip.
+**	The timeout handler will wait for settle_time before 
+**	clearing it and so resuming command processing.
+**
+**
+**==========================================================
+*/
+static void ncr_start_reset(ncb_p np, int settle_delay)
+{
+	u_long flags;
+
+	save_flags(flags); cli();
+
+	if (!np->settle_time) {
+		if (bootverbose > 1)
+			printf("%s: resetting, command processing suspended for %d seconds\n",
+				ncr_name(np), settle_delay);
+		np->settle_time	= jiffies + settle_delay * HZ;
+		OUTB (nc_istat, SRST);
+		DELAY (1000);
+		OUTB (nc_istat, 0);
+		OUTW (nc_sien, RST);
+		OUTB (nc_scntl1, CRST);
+		DELAY (100);
+	}
+
+	restore_flags(flags);
+}
+
+/*==========================================================
+**
+**
 **	Reset the SCSI BUS.
 **	This is called from the generic SCSI driver.
 **
 **
 **==========================================================
 */
-int ncr_reset_bus (Scsi_Cmnd *cmd)
+int ncr_reset_bus (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_data;
+	ncb_p np                      = host_data->ncb;
+	ccb_p cp;
 	u_long flags;
+	int found;
 
-	save_flags(flags); cli();
+#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;
+	}
+/*
+ * Start the reset process.
+ * The script processor is then assumed to be stopped.
+ * Commands will now be queued in the waiting list until a settle 
+ * delay of 2 seconds will be completed.
+ */
+	ncr_start_reset(np, 2);
+/*
+ * First, look in the wakeup list
+ */
+	for (found=0, cp=np->ccb; cp; cp=cp->link_ccb) {
+		/*
+		**	look for the ccb of this command.
+		*/
+		if (cp->host_status == HS_IDLE) continue;
+		if (cp->cmd == cmd) {
+			found = 1;
+			break;
+		}
+	}
+/*
+ * Then, look in the waiting list
+ */
+	if (!found && retrieve_from_waiting_list(0, np, cmd))
+		found = 1;
+/*
+ * Wake-up all awaiting commands with DID_RESET.
+ */
 	reset_waiting_list(np);
-	ncr_init(np, "scsi bus reset", HS_RESET);
-
-#ifndef SCSI_NCR_NO_DISCONNECT
-	np->disc = 1;
-#endif
+/*
+ * Wake-up all pending commands with HS_RESET -> DID_RESET.
+ */
+	ncr_wakeup(np, HS_RESET);
+/*
+ * If the involved command was not in a driver queue, and the 
+ * scsi driver told us reset is synchronous, and the command is not 
+ * currently in the waiting list, complete it with DID_RESET status,
+ * in order to keep it alive.
+ */
+	if (!found && sync_reset && !retrieve_from_waiting_list(0, np, cmd)) {
+		cmd->result = ScsiResult(DID_RESET, 0);
+		cmd->scsi_done(cmd);
+	}
 
 	restore_flags(flags);
 
@@ -4221,17 +5198,22 @@
 **
 **==========================================================
 */
-int ncr_abort_command (Scsi_Cmnd *cmd)
+static int ncr_abort_command (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_data;
+	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
@@ -4246,7 +5228,7 @@
 /*
  * Then, look in the wakeup list
  */
-	for (found=0, cp=&np->ccb; cp; cp=cp->link_ccb) {
+	for (found=0, cp=np->ccb; cp; cp=cp->link_ccb) {
 		/*
 		**	look for the ccb of this command.
 		*/
@@ -4256,14 +5238,23 @@
 			break;
 		}
 	}
+
 	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 = (SCR_JUMP);
 	if (cp->phys.header.launch.l_paddr == NCB_SCRIPT_PHYS (np, select)) {
@@ -4271,35 +5262,18 @@
 		cp->phys.header.launch.l_paddr = NCB_SCRIPT_PHYS (np, skip);
 	}
 
-	switch (cp->host_status) {
-	case HS_BUSY:
-	case HS_NEGOTIATE:
-		/*
-		** still in start queue ?
-		*/
-		if (cp->phys.header.launch.l_paddr == NCB_SCRIPT_PHYS (np, skip)) {
-			retv = SCSI_ABORT_BUSY;
-			break;
-		}
-	/* fall through */
-	case HS_DISCONNECT:
-		cp->host_status=HS_ABORTED;
-		cp->tag = 0;
-		/*
-		**	wakeup this ccb.
-		*/
-		ncr_complete (np, cp);
-		retv = SCSI_ABORT_SUCCESS;
-		break;
-	default:
-		cp->tag = 0;
-		/*
-		**	wakeup this ccb.
-		*/
-		ncr_complete (np, cp);
-		retv = SCSI_ABORT_SUCCESS;
-		break;
-	}
+	cp->tlimit = 0;
+	retv = SCSI_ABORT_PENDING;
+
+	/*
+	**      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);
 
@@ -4325,7 +5299,6 @@
 	lcb_p lp;
 	int target, lun;
 	int i;
-	u_char scntl3;
 
 	printf("%s: releasing host resources\n", ncr_name(np));
 
@@ -4334,7 +5307,7 @@
 **	Set release_stage to 1 and wait that ncr_timeout() set it to 2.
 */
 
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
 	printf("%s: stopping the timer\n", ncr_name(np));
 #endif
 	np->release_stage = 1;
@@ -4347,7 +5320,7 @@
 **	Disable chip interrupts
 */
 
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
 	printf("%s: disabling chip interrupts\n", ncr_name(np));
 #endif
 	OUTW (nc_sien , 0);
@@ -4357,7 +5330,7 @@
 **	Free irq
 */
 
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
 	printf("%s: freeing irq %d\n", ncr_name(np), irq);
 #endif
 #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
@@ -4372,28 +5345,40 @@
 
 	/*
 	**	Reset NCR chip
-	**	Preserve scntl3 for automatic clock detection.
+	**	Restore bios setting for automatic clock detection.
 	*/
 
 	printf("%s: resetting chip\n", ncr_name(np));
-	scntl3 = INB (nc_scntl3);
 	OUTB (nc_istat,  SRST);
 	DELAY (1000);
 	OUTB (nc_istat,  0   );
-	OUTB (nc_scntl3, scntl3);
+
+	OUTB(nc_dmode,	np->sv_dmode);
+	OUTB(nc_dcntl,	np->sv_dcntl);
+	OUTB(nc_ctest3,	np->sv_ctest3);
+	OUTB(nc_ctest4,	np->sv_ctest4);
+	OUTB(nc_ctest5,	np->sv_ctest5);
+	OUTB(nc_gpcntl,	np->sv_gpcntl);
+	OUTB(nc_stest2,	np->sv_stest2);
+
+	ncr_selectclock(np, np->sv_scntl3);
 
 	/*
 	**	Release Memory mapped IO region and IO mapped region
 	*/
 
 #ifndef NCR_IOMAPPED
-#ifdef DEBUG
-	printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->reg_remapped, 128);
+#ifdef DEBUG_NCR53C8XX
+	printf("%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->reg_remapped, (u_long) 128);
+	unmap_pci_mem((vm_offset_t) np->vaddr2, (u_long) 4096);
 #endif
 
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
 	printf("%s: releasing IO region %x[%d]\n", ncr_name(np), np->port, 128);
 #endif
 	release_region(np->port, 128);
@@ -4402,13 +5387,13 @@
 	**	Free allocated ccb(s)
 	*/
 
-	while ((cp=np->ccb.link_ccb) != NULL) {
-		np->ccb.link_ccb = cp->link_ccb;
+	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",
 			ncr_name(np), cp->host_status);
 		}
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
 	printf("%s: freeing ccb (%lx)\n", ncr_name(np), (u_long) cp);
 #endif
 		m_free(cp, sizeof(*cp));
@@ -4423,7 +5408,7 @@
 		for (lun = 0 ; lun < MAX_LUN ; lun++) {
 			lp = tp->lp[lun];
 			if (lp) {
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
 	printf("%s: freeing lp (%lx)\n", ncr_name(np), (u_long) lp);
 #endif
 				m_free(lp, sizeof(*lp));
@@ -4476,7 +5461,7 @@
 	**	timestamp
 	**	Optional, spare some CPU time
 	*/
-#ifdef SCSI_NCR_PROFILE
+#ifdef SCSI_NCR_PROFILE_SUPPORT
 	ncb_profile (np, cp);
 #endif
 
@@ -4487,6 +5472,16 @@
 	cmd = cp->cmd;
 	cp->cmd = NULL;
 	tp = &np->target[cmd->target];
+	lp = tp->lp[cmd->lun];
+
+	/*
+	**	We donnot queue more than 1 ccb per target 
+	**	with negotiation at any time. If this ccb was 
+	**	used for negotiation, clear this info in the tcb.
+	*/
+
+	if (cp == tp->nego_cp)
+		tp->nego_cp = 0;
 
 	/*
 	**	Check for parity errors.
@@ -4555,11 +5550,11 @@
 		*/
 		if (cmd->lun == 0 && cmd->cmnd[0] == 0x12) {
 			if (np->unit < SCSI_NCR_MAX_HOST) {
-#ifdef SCSI_NCR_FORCE_SYNC_NEGO
-				((char *) cmd->request_buffer)[7] |= INQ7_SYNC;
-#endif
-				((char *) cmd->request_buffer)[7] &=
-				(target_capabilities[np->unit].and_map[cmd->target]);
+				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,
@@ -4568,12 +5563,7 @@
 			/*
 			**	set number of tags
 			*/
-			lp = tp->lp[cmd->lun];
-#ifndef SCSI_NCR_TAGGED_QUEUE_DISABLED
-			if (lp && !lp->usetags) {
-				ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS);
-			}
-#endif
+				ncr_setmaxtags (np, tp, driver_setup.default_tags);
 			/*
 			**	prepare negotiation of synch and wide.
 			*/
@@ -4585,8 +5575,33 @@
 			tp->quirks |= QUIRK_UPDATE;
 		}
 
+		/*
+		**	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.
+		*/
+		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");
+				}
+			}
+		}
 	} else if ((cp->host_status == HS_COMPLETE)
 		&& (cp->scsi_status == (S_SENSE|S_GOOD) ||
 		    cp->scsi_status == (S_SENSE|S_CHECK_COND))) {
@@ -4612,6 +5627,29 @@
 		*/
 		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)) {
 
@@ -4680,7 +5718,7 @@
 	/*
 	**	Free this ccb
 	*/
-	ncr_free_ccb (np, cp);
+	ncr_free_ccb (np, cp, cmd->target, cmd->lun);
 
 	/*
 	**	requeue awaiting scsi commands
@@ -4713,7 +5751,7 @@
 	**	complete all jobs that are not IDLE.
 	*/
 
-	ccb_p cp = &np->ccb;
+	ccb_p cp = np->ccb;
 	while (cp) {
 		switch (cp->host_status) {
 
@@ -4751,11 +5789,6 @@
 void ncr_init (ncb_p np, char * msg, u_long code)
 {
 	int	i;
-	u_long	usrsync;
-	u_char	usrwide;
-#if 0
-	u_char	burstlen;
-#endif
 
 	/*
 	**	Reset chip.
@@ -4768,7 +5801,7 @@
 	**	Message.
 	*/
 
-	if (msg) printf ("%s: restart (%s).\n", ncr_name (np), msg);
+	if (msg) printf (KERN_INFO "%s: restart (%s).\n", ncr_name (np), msg);
 
 	/*
 	**	Clear Start Queue
@@ -4779,10 +5812,9 @@
 	/*
 	**	Start at first entry.
 	*/
-
 	np->squeueput = 0;
-	np->script->startpos[0] = NCB_SCRIPT_PHYS (np, tryloop);
-	np->script->start0  [0] = SCR_INT ^ IFFALSE (0);
+	np->script0->startpos[0] = NCB_SCRIPTH_PHYS (np, tryloop);
+	np->script0->start0  [0] = SCR_INT ^ IFFALSE (0);
 
 	/*
 	**	Wakeup all pending jobs.
@@ -4790,122 +5822,73 @@
 	ncr_wakeup (np, code);
 
 	/*
-	**	Remove Reset, abort ...
-	*/
-	OUTB (nc_istat,  0      );
-
-	/*
 	**	Init chip.
 	*/
-/**	NCR53C810			**/
-	if (ChipDevice == PCI_DEVICE_ID_NCR_53C810 && ChipVersion == 0) {
-		OUTB(nc_dmode, 0x80);	/* Set 8-transfer burst */
-	}
-	else
-/**	NCR53C815			**/
-	if (ChipDevice == PCI_DEVICE_ID_NCR_53C815) {
-		OUTB(nc_dmode, 0x80);	/* Set 8-transfer burst */
-	}
-	else
-/**	NCR53C825			**/
-	if (ChipDevice == PCI_DEVICE_ID_NCR_53C825 && ChipVersion == 0) {
-		OUTB(nc_dmode, 0x80);	/* Set 8-transfer burst */
-	}
-	else
-/**	NCR53C810A or NCR53C860		**/
-	if ((ChipDevice == PCI_DEVICE_ID_NCR_53C810 && ChipVersion >= 0x10) ||
-	    ChipDevice == PCI_DEVICE_ID_NCR_53C860) {
-		OUTB(nc_dmode, 0xc0);	/* Set 16-transfer burst */
-#if 0
-		OUTB(nc_ctest3, 0x01);	/* Set write and invalidate */
-		OUTB(nc_dcntl, 0xa1);	/* Cache line size enable, */
-					/* pre-fetch enable and 700 comp */
-#endif
-	}
-	else
-/**	NCR53C825A or NCR53C875		**/
-	if ((ChipDevice == PCI_DEVICE_ID_NCR_53C825 && ChipVersion >= 0x10) ||
-	    ChipDevice == PCI_DEVICE_ID_NCR_53C875) {
-		OUTB(nc_dmode, 0xc0);	/* Set 16-transfer burst */
-#if 0
-		OUTB(nc_ctest5, 0x04);	/* Set DMA FIFO to 88 */
-		OUTB(nc_ctest5, 0x24);	/* Set DMA FIFO to 536 */
-		OUTB(nc_dmode, 0x40);	/* Set 64-transfer burst */
-		OUTB(nc_ctest3, 0x01);	/* Set write and invalidate */
-		OUTB(nc_dcntl, 0x81);	/* Cache line size enable and 700 comp*/
-#endif
-	}
-/**	OTHERS				**/
-	else {
-		OUTB(nc_dmode, 0xc0);	/* Set 16-transfer burst */
-	}
-#if 0
-	burstlen = 0xc0;
-#endif
-
-#ifdef SCSI_NCR_DISABLE_PARITY_CHECK
-	OUTB (nc_scntl0, 0xc0   );      /*  full arb., (no parity)           */
-#else
-	OUTB (nc_scntl0, 0xca   );      /*  full arb., ena parity, par->ATN  */
-#endif
 
-	OUTB (nc_scntl1, 0x00	);	/*  odd parity, and remove CRST!!    */
-	OUTB (nc_scntl3, np->rv_scntl3);/*  timing prescaler		     */
-	OUTB (nc_scid  , RRE|np->myaddr);/*  host adapter SCSI address       */
-	OUTW (nc_respid, 1ul<<np->myaddr);/*  id to respond to		     */
-	OUTB (nc_istat , SIGP	);	/*  Signal Process		     */
-#if 0
-	OUTB (nc_dmode , burstlen);	/*  Burst length = 2 .. 16 transfers */
-#endif
-	OUTB (nc_dcntl , NOCOM  );      /*  no single step mode, protect SFBR*/
+	OUTB (nc_istat,  0x00   );	/*  Remove Reset, abort */
+	OUTB (nc_scntl0, np->rv_scntl0 | 0xc0);
+					/*  full arb., ena parity, par->ATN  */
+	OUTB (nc_scntl1, 0x00);		/*  odd parity, and remove CRST!! */
+
+	ncr_selectclock(np, np->rv_scntl3);	/* Select SCSI clock */
+
+	OUTB (nc_scid  , RRE|np->myaddr);	/* Adapter SCSI address */
+	OUTW (nc_respid, 1ul<<np->myaddr);	/* Id to respond to */
+	OUTB (nc_istat , SIGP	);		/*  Signal Process */
+	OUTB (nc_dmode , np->rv_dmode);		/* Burst length, dma mode */
+	OUTB (nc_ctest5, np->rv_ctest5);	/* Large fifo + large burst */
+
+	OUTB (nc_dcntl , NOCOM|np->rv_dcntl);	/* Protect SFBR */
+	OUTB (nc_ctest3, np->rv_ctest3);	/* Write and invalidate */
+	OUTB (nc_ctest4, np->rv_ctest4);	/* Master parity checking */
+
+	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. */
 
-#ifdef SCSI_NCR_DISABLE_MPARITY_CHECK
-	OUTB (nc_ctest4, 0x00	);	/*  disable master parity checking   */
-#else
-	OUTB (nc_ctest4, 0x08	);	/*  enable master parity checking    */
-#endif
+	/*
+	**	Disable disconnects.
+	*/
 
-	OUTB (nc_stest2, EXT    );	/*  Extended Sreq/Sack filtering     */
-	OUTB (nc_stest3, TE     );	/*  TolerANT enable		     */
-	OUTB (nc_stime0, 0x0d	);	/*  HTH = disable  STO = 0.4 sec.    */
-					/*  0.25 sec recommended for scsi 1  */
+	np->disc = 0;
 
 	/*
-	**	Reinitialize usrsync.
-	**	Have to renegotiate synch mode.
+	**    Enable GPIO0 pin for writing if LED support.
 	*/
 
-	usrsync = 255;
+	if (np->features & FE_LED0) {
+		OUTOFFB (nc_gpcntl, 0x01);
+	}
 
-#ifndef SCSI_NCR_FORCE_ASYNCHRONOUS
-	if (SCSI_NCR_MAX_SYNC) {
-		u_long period;
-		period =1000000/SCSI_NCR_MAX_SYNC; /* ns = 10e6 / kHz */
-		if (period <= 11 * np->ns_sync) {
-			if (period < 4 * np->ns_sync)
-				usrsync = np->ns_sync;
-			else
-				usrsync = period / 4;
-		};
-	};
-#endif
+	/*
+	**    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));
+		bcopy(np->script0, np->script, sizeof(struct script));
+	}
 
 	/*
-	**	Reinitialize usrwide.
-	**	Have to renegotiate wide mode.
+	**      enable ints
 	*/
 
-	usrwide = (SCSI_NCR_MAX_WIDE);
-	if (usrwide > np->maxwide) usrwide=np->maxwide;
+	OUTW (nc_sien , STO|HTH|MA|SGE|UDC|RST);
+	OUTB (nc_dien , MDPE|BF|ABRT|SSI|SIR|IID);
 
 	/*
-	**	Disable disconnects.
+	**	For 895/6 enable SBMC interrupt and save current SCSI bus mode.
 	*/
-
-	np->disc = 0;
+	if (np->features & FE_ULTRA2) {
+		OUTONW (nc_sien, SBMC);
+		np->scsi_mode = INB (nc_stest4) & SMODE;
+	}
 
 	/*
 	**	Fill in target structure.
+	**	Reinitialize usrsync.
+	**	Reinitialize usrwide.
+	**	Prepare sync negotiation according to actual SCSI bus mode.
 	*/
 
 	for (i=0;i<MAX_TARGET;i++) {
@@ -4914,20 +5897,23 @@
 		tp->sval    = 0;
 		tp->wval    = np->rv_scntl3;
 
-		tp->usrsync = usrsync;
-		tp->usrwide = usrwide;
+		if (tp->usrsync != 255) {
+			if (tp->usrsync <= np->maxsync) {
+				if (tp->usrsync < np->minsync) {
+					tp->usrsync = np->minsync;
+				}
+			}
+			else
+				tp->usrsync = 255;
+		};
+
+		if (tp->usrwide > np->maxwide)
+			tp->usrwide = np->maxwide;
 
 		ncr_negotiate (np, tp);
 	}
 
 	/*
-	**      enable ints
-	*/
-
-	OUTW (nc_sien , STO|HTH|MA|SGE|UDC|RST);
-	OUTB (nc_dien , MDPE|BF|ABRT|SSI|SIR|IID);
-
-	/*
 	**    Start script processor.
 	*/
 
@@ -4950,7 +5936,13 @@
 
 	u_long minsync = tp->usrsync;
 
-	if (minsync < 25) minsync=25;
+	/*
+	**	SCSI bus mode limit
+	*/
+
+	if (np->scsi_mode && np->scsi_mode == SMODE_SE) {
+		if (minsync < 12) minsync = 12;
+	}
 
 	/*
 	**	if not scsi 2
@@ -4964,18 +5956,18 @@
 	**	our limit ..
 	*/
 
-	if (minsync < np->ns_sync)
-		minsync = np->ns_sync;
+	if (minsync < np->minsync)
+		minsync = np->minsync;
 
 	/*
 	**	divider limit
 	*/
 
-	if (minsync > (np->ns_sync * 11) / 4)
+	if (minsync > np->maxsync)
 		minsync = 255;
 
 	tp->minsync = minsync;
-	tp->maxoffs = (minsync<255 ? 8 : 0);
+	tp->maxoffs = (minsync<255 ? np->maxoffs : 0);
 
 	/*
 	**	period=0: has to negotiate sync transfer
@@ -4991,16 +5983,120 @@
 
 /*==========================================================
 **
+**	Get clock factor and sync divisor for a given 
+**	synchronous factor period.
+**	Returns the clock factor (in sxfer) and scntl3 
+**	synchronous divisor field.
+**
+**==========================================================
+*/
+
+static void ncr_getsync(ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p)
+{
+	u_long	clk = np->clock_khz;	/* SCSI clock frequency in kHz	*/
+	int	div = np->clock_divn;	/* Number of divisors supported	*/
+	u_long	fak;			/* Sync factor in sxfer		*/
+	u_long	per;			/* Period in tenths of ns	*/
+	u_long	kpc;			/* (per * clk)			*/
+
+	/*
+	**	Compute the synchronous period in tenths of nano-seconds
+	*/
+	if	(sfac <= 10)	per = 250;
+	else if	(sfac == 11)	per = 303;
+	else if	(sfac == 12)	per = 500;
+	else			per = 40 * sfac;
+
+	/*
+	**	Look for the greatest clock divisor that allows an 
+	**	input speed faster than the period.
+	*/
+	kpc = per * clk;
+	while (--div >= 0)
+		if (kpc >= (div_10M[div] << 2)) break;
+
+	/*
+	**	Calculate the lowest clock factor that allows an output 
+	**	speed not faster than the period.
+	*/
+	fak = (kpc - 1) / div_10M[div] + 1;
+
+#if 0	/* This optimization does not seem very usefull */
+
+	per = (fak * div_10M[div]) / clk;
+
+	/*
+	**	Why not to try the immediate lower divisor and to choose 
+	**	the one that allows the fastest output speed ?
+	**	We dont want input speed too much greater than output speed.
+	*/
+	if (div >= 1 && fak < 8) {
+		u_long fak2, per2;
+		fak2 = (kpc - 1) / div_10M[div-1] + 1;
+		per2 = (fak2 * div_10M[div-1]) / clk;
+		if (per2 < per && fak2 <= 8) {
+			fak = fak2;
+			per = per2;
+			--div;
+		}
+	}
+#endif
+
+	if (fak < 4) fak = 4;	/* Should never happen, too bad ... */
+
+	/*
+	**	Compute and return sync parameters for the ncr
+	*/
+	*fakp		= fak - 4;
+	*scntl3p	= ((div+1) << 4) + (sfac < 25 ? 0x80 : 0);
+}
+
+
+/*==========================================================
+**
+**	Set actual values, sync status and patch all ccbs of 
+**	a target according to new sync/wide agreement.
+**
+**==========================================================
+*/
+
+static void ncr_set_sync_wide_status (ncb_p np, u_char target)
+{
+	ccb_p cp;
+	tcb_p tp = &np->target[target];
+
+	/*
+	**	set actual value and sync_status
+	*/
+	OUTB (nc_sxfer, tp->sval);
+	np->sync_st = tp->sval;
+	OUTB (nc_scntl3, tp->wval);
+	np->wide_st = tp->wval;
+
+	/*
+	**	patch ALL ccbs of this target.
+	*/
+	for (cp = np->ccb; cp; cp = cp->link_ccb) {
+		if (!cp->cmd) continue;
+		if (cp->cmd->target != target) continue;
+		cp->sync_status = tp->sval;
+		cp->wide_status = tp->wval;
+	};
+}
+
+/*==========================================================
+**
 **	Switch sync mode for current job and it's target
 **
 **==========================================================
 */
 
-static void ncr_setsync (ncb_p np, ccb_p cp, u_char sxfer)
+static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer)
 {
 	Scsi_Cmnd *cmd;
 	tcb_p tp;
-	u_char target = INB (nc_ctest0)&7;
+	u_char target = INB (nc_ctest0) & 0x0f;
+	u_char idiv;
 
 	assert (cp);
 	if (!cp) return;
@@ -5011,57 +6107,81 @@
 	assert (target == (cmd->target & 0xf));
 
 	tp = &np->target[target];
-	tp->period= sxfer&0xf ? ((sxfer>>5)+4) * np->ns_sync : 0xffff;
 
-	if (tp->sval == sxfer) return;
+	if (!scntl3 || !(sxfer & 0x1f))
+		scntl3 = np->rv_scntl3;
+	scntl3 = (scntl3 & 0xf0) | (tp->wval & EWS) | (np->rv_scntl3 & 0x07);
+
+	/*
+	**	Deduce the value of controller sync period from scntl3.
+	**	period is in tenths of nano-seconds.
+	*/
+
+	idiv = ((scntl3 >> 4) & 0x7);
+	if ((sxfer & 0x1f) && idiv)
+		tp->period = (((sxfer>>5)+4)*div_10M[idiv-1])/np->clock_khz;
+	else
+		tp->period = 0xffff;
+
+	/*
+	**	 Stop there if sync parameters are unchanged
+	*/
+	if (tp->sval == sxfer && tp->wval == scntl3) return;
 	tp->sval = sxfer;
+	tp->wval = scntl3;
 
 	/*
 	**	Bells and whistles   ;-)
 	*/
 	PRINT_ADDR(cmd);
-	if (sxfer & 0x0f) {
+	if (sxfer & 0x01f) {
+		unsigned f10 = 100000 << (tp->widedone ? tp->widedone -1 : 0);
+		unsigned mb10 = (f10 + tp->period/2) / tp->period;
+		char *scsi;
+
 		/*
 		**  Disable extended Sreq/Sack filtering
 		*/
-		if (tp->period <= 200) OUTB (nc_stest2, 0);
+		if (tp->period <= 2000) OUTOFFB (nc_stest2, EXT);
 
-		printf ("%s%dns (%d Mb/sec) offset %d.\n",
-			tp->period<200 ? "FAST SCSI-2 ":"",
-			tp->period,
-			(((tp->wval & EWS)? 2:1)*1000+tp->period/2)/tp->period,
-			sxfer & 0x0f);
-	} else  printf ("asynchronous.\n");
+		/*
+		**	Bells and whistles   ;-)
+		*/
+		if	(tp->period < 500)	scsi = "FAST-40";
+		else if	(tp->period < 1000)	scsi = "FAST-20";
+		else if	(tp->period < 2000)	scsi = "FAST-10";
+		else				scsi = "SLOW";
 
-	/*
-	**	set actual value and sync_status
-	*/
-	OUTB (nc_sxfer, sxfer);
-	np->sync_st = sxfer;
+		printf ("%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 " : "");
 
 	/*
+	**	set actual value and sync_status
 	**	patch ALL ccbs of this target.
 	*/
-	for (cp = &np->ccb; cp; cp = cp->link_ccb) {
-		if (!cp->cmd) continue;
-		if (cp->cmd->target != target) continue;
-		cp->sync_status = sxfer;
-	};
+	ncr_set_sync_wide_status(np, target);
 }
 
 /*==========================================================
 **
 **	Switch wide mode for current job and it's target
+**	SCSI specs say: a SCSI device that accepts a WDTR 
+**	message shall reset the synchronous agreement to 
+**	asynchronous mode.
 **
 **==========================================================
 */
 
-static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide)
+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)&7;
+	u_short target = INB (nc_ctest0) & 0x0f;
 	tcb_p tp;
-	u_char	scntl3 = np->rv_scntl3 | (wide ? EWS : 0);
+	u_char	scntl3;
+	u_char	sxfer;
 
 	assert (cp);
 	if (!cp) return;
@@ -5073,32 +6193,33 @@
 
 	tp = &np->target[target];
 	tp->widedone  =  wide+1;
-	if (tp->wval == scntl3) return;
-	tp->wval = scntl3;
+	scntl3 = (tp->wval & (~EWS)) | (wide ? EWS : 0);
+
+	sxfer = ack ? 0 : tp->sval;
 
 	/*
-	**	Bells and whistles   ;-)
+	**	 Stop there if sync/wide parameters are unchanged
 	*/
-	PRINT_ADDR(cmd);
-	if (scntl3 & EWS)
-		printf ("WIDE SCSI (16 bit) enabled.\n");
-	else
-		printf ("WIDE SCSI disabled.\n");
+	if (tp->sval == sxfer && tp->wval == scntl3) return;
+	tp->sval = sxfer;
+	tp->wval = scntl3;
 
 	/*
-	**	set actual value and sync_status
+	**	Bells and whistles   ;-)
 	*/
-	OUTB (nc_scntl3, scntl3);
-	np->wide_st = scntl3;
+	if (bootverbose >= 2) {
+		PRINT_ADDR(cmd);
+		if (scntl3 & EWS)
+			printf ("WIDE SCSI (16 bit) enabled.\n");
+		else
+			printf ("WIDE SCSI disabled.\n");
+	}
 
 	/*
+	**	set actual value and sync_status
 	**	patch ALL ccbs of this target.
 	*/
-	for (cp = &np->ccb; cp; cp = cp->link_ccb) {
-		if (!cp->cmd) continue;
-		if (cp->cmd->target != target) continue;
-		cp->wide_status = scntl3;
-	};
+	ncr_set_sync_wide_status(np, target);
 }
 
 /*==========================================================
@@ -5108,19 +6229,32 @@
 **==========================================================
 */
 
-static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long usrtags)
+static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long numtags)
 {
 	int l;
-	tp->usrtags = usrtags;
+	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;
+
 		if (!tp) break;
 		lp=tp->lp[l];
 		if (!lp) continue;
+
+		wastags = lp->usetags;
 		ncr_settags (tp, lp);
-		if (lp->usetags > 0) {
+
+		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("using tagged command queueing, up to %d cmds/lun\n", lp->usetags);
+			printf("disabling tagged command queueing\n");
 		}
 	};
 }
@@ -5139,8 +6273,8 @@
 	*/
 	if ((  tp->inqdata[2] & 0x7) >= 2 &&
 	    (  tp->inqdata[7] & INQ7_QUEUE) && ((tp->inqdata[0] & 0x1f)==0x00)
-		&& tp->usrtags) {
-		reqtags = tp->usrtags;
+		&& tp->numtags > 1) {
+		reqtags = tp->numtags;
 		if (lp->actlink <= 1)
 			lp->usetags=reqtags;
 	} else {
@@ -5171,7 +6305,7 @@
 **----------------------------------------------------
 */
 
-#ifdef SCSI_NCR_USER_COMMAND
+#ifdef SCSI_NCR_USER_COMMAND_SUPPORT
 
 static void ncr_usercmd (ncb_p np)
 {
@@ -5196,13 +6330,13 @@
 			np->user.data = SCSI_NCR_MAX_TAGS;
 		for (t=0; t<MAX_TARGET; t++) {
 			if (!((np->user.target>>t)&1)) continue;
+			np->target[t].usrtags = np->user.data;
 			ncr_setmaxtags (np, &np->target[t], np->user.data);
 		};
-		np->disc = 1;
 		break;
 
 	case UC_SETDEBUG:
-#ifdef SCSI_NCR_DEBUG
+#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
 		ncr_debug = np->user.data;
 #endif
 		break;
@@ -5234,12 +6368,106 @@
 	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
 
 /*==========================================================
 **
@@ -5267,7 +6495,7 @@
 	**	If release process in progress, let's go
 	**	Set the release stage from 1 to 2 to synchronize
 	**	with the release process.
-	**/
+	*/
 
 	if (np->release_stage) {
 		if (np->release_stage == 1) np->release_stage = 2;
@@ -5282,7 +6510,33 @@
 
 	add_timer(&np->timer);
 
-	if (np->lasttime + HZ < thistime) {
+#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.
+	*/
+	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();
+			np->settle_time	= 0;
+			np->disc	= 1;
+			requeue_waiting_list(np);
+			restore_flags(flags);
+		}
+		return;
+	}
+
+	/*
+	**	Since the generic scsi driver only allows us 0.5 second 
+	**	to perform abort of a command, we must look at ccbs about 
+	**	every 0.25 second.
+	*/
+	if (np->lasttime + (HZ>>2) <= thistime) {
 		/*
 		**	block ncr interrupts
 		*/
@@ -5311,44 +6565,15 @@
 		t = (thistime - np->heartbeat) / HZ;
 
 		if (t<2) np->latetime=0; else np->latetime++;
-		if (np->latetime>5) {
-			/*
-			**      If there are no requests, the script
-			**      processor will sleep on SEL_WAIT_RESEL.
-			**      But we have to check whether it died.
-			**      Let's wake it up.
-			*/
-			OUTB (nc_istat, SIGP);
-		}
-		if (np->latetime>10) {
-			/*
-			**	Although we tried to wake it up,
-			**	the script processor didn't respond.
-			**
-			**	May be a target is hanging,
-			**	or another initator lets a tape device
-			**	rewind with disconnect disabled :-(
-			**
-			**	We won't accept that.
-			*/
-			if (INB (nc_sbcl) & CBSY)
-				OUTB (nc_scntl1, CRST);
-			DELAY (1000);
-			ncr_init (np, "ncr dead ?", HS_TIMEOUT);
-#ifndef SCSI_NCR_NO_DISCONNECT
-			np->disc = 1;
-#endif
-			np->heartbeat = thistime;
-		}
 
 		/*----------------------------------------------------
 		**
-		**	should handle ccb timeouts
-		**	Let the middle scsi driver manage timeouts.
+		**	handle ccb timeouts
+		**
 		**----------------------------------------------------
 		*/
 
-		for (cp=&np->ccb; cp; cp=cp->link_ccb) {
+		for (cp=np->ccb; cp; cp=cp->link_ccb) {
 			/*
 			**	look for timed out ccbs.
 			*/
@@ -5357,7 +6582,7 @@
 			/*
 			**	Have to force ordered tag to avoid timeouts
 			*/
-			if (cp->cmd && cp->tlimit <= 
+			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];
@@ -5365,24 +6590,14 @@
 					lp->force_ordered_tag = 1;
 				}
 			}
-/*
-**	Let the middle scsi driver manage timeouts
-*/
-#if 0
-			if (cp->tlimit > thistime) continue;
-
 			/*
-			**	Disable reselect.
-			**      Remove it from startqueue.
+			**	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.
 			*/
-			cp->jump_ccb.l_cmd = (SCR_JUMP);
-			if (cp->phys.header.launch.l_paddr ==
-				NCB_SCRIPT_PHYS (np, select)) {
-				printf ("%s: timeout ccb=%p (skip)\n",
-					ncr_name (np), cp);
-				cp->phys.header.launch.l_paddr
-				= NCB_SCRIPT_PHYS (np, skip);
-			};
+
+			if (cp->tlimit) continue;
 
 			switch (cp->host_status) {
 
@@ -5397,7 +6612,7 @@
 
 				/* fall through */
 			case HS_DISCONNECT:
-				cp->host_status=HS_TIMEOUT;
+				cp->host_status=HS_ABORTED;
 			};
 			cp->tag = 0;
 
@@ -5405,7 +6620,11 @@
 			**	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);
 	}
@@ -5425,43 +6644,146 @@
 #endif /* SCSI_NCR_BROKEN_INTR */
 }
 
-/*==========================================================
-**
+/*==========================================================
+**
+**	log message for real hard errors
+**
+**	"ncr0 targ 0?: ERROR (ds:si) (so-si-sd) (sxfer/scntl3) @ name (dsp:dbc)."
+**	"	      reg: r0 r1 r2 r3 r4 r5 r6 ..... rf."
+**
+**	exception register:
+**		ds:	dstat
+**		si:	sist
+**
+**	SCSI bus lines:
+**		so:	control lines as driver by NCR.
+**		si:	control lines as seen by NCR.
+**		sd:	scsi data lines as seen by NCR.
+**
+**	wide/fastmode:
+**		sxfer:	(see the manual)
+**		scntl3:	(see the manual)
+**
+**	current script command:
+**		dsp:	script adress (relative to start of script).
+**		dbc:	first word of script command.
+**
+**	First 16 register of the chip:
+**		r0..rf
+**
+**==========================================================
+*/
+
+static void ncr_log_hard_error(ncb_p np, u_short sist, u_char dstat)
+{
+	u_int32	dsp;
+	int	script_ofs;
+	int	script_size;
+	char	*script_name;
+	u_char	*script_base;
+	int	i;
+
+	dsp	= INL (nc_dsp);
+
+	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_name	= "script";
+	}
+	else {
+		script_ofs	= dsp - np->p_scripth;
+		script_size	= sizeof(struct scripth);
+		script_base	= (u_char *) np->scripth;
+		script_name	= "scripth";
+	}
+
+	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,
+		(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));
+	}
+
+        printf ("%s: regdump:", ncr_name(np));
+        for (i=0; i<16;i++)
+            printf (" %02x", (unsigned)INB_OFF(i));
+        printf (".\n");
+}
+
+/*============================================================
 **
 **	ncr chip exception handler.
 **
+**============================================================
 **
-**==========================================================
+**	In normal cases, interrupt conditions occur one at a 
+**	time. The ncr is able to stack in some extra registers 
+**	other interrupts that will occurs after the first one.
+**	But severall interrupts may occur at the same time.
+**
+**	We probably should only try to deal with the normal 
+**	case, but it seems that multiple interrupts occur in 
+**	some cases that are not abnormal at all.
+**
+**	The most frequent interrupt condition is Phase Mismatch.
+**	We should want to service this interrupt quickly.
+**	A SCSI parity error may be delivered at the same time.
+**	The SIR interrupt is not very frequent in this driver, 
+**	since the INTFLY is likely used for command completion 
+**	signaling.
+**	The Selection Timeout interrupt may be triggered with 
+**	IID and/or UDC.
+**	The SBMC interrupt (SCSI Bus Mode Change) may probably 
+**	occur at any time.
+**
+**	This handler try to deal as cleverly as possible with all
+**	the above.
+**
+**============================================================
 */
 
 void ncr_exception (ncb_p np)
 {
 	u_char	istat, dstat;
 	u_short	sist;
-	u_int32	dsp, dsa;
-	int	script_ofs;
 	int	i;
 
 	/*
 	**	interrupt on the fly ?
 	*/
 	while ((istat = INB (nc_istat)) & INTF) {
-		if (DEBUG_FLAGS & DEBUG_TINY) printf ("F");
+		if (DEBUG_FLAGS & DEBUG_TINY) printf ("F ");
+#ifdef	SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+	if (np->stalling)
+		OUTB (nc_istat, INTF);
+	else
+#endif
 		OUTB (nc_istat, (istat & SIGP) | INTF);
 		np->profile.num_fly++;
 		ncr_wakeup (np, 0);
 	};
 
-	if (!(istat & (SIP|DIP))) return;
+	if (!(istat & (SIP|DIP)))
+		return;
+
+	np->profile.num_int++;
+
+	if (istat & CABRT)
+		OUTB (nc_istat, CABRT);
 
 	/*
 	**	Steinbach's Guideline for Systems Programming:
 	**	Never test for an error condition you don't know how to handle.
 	*/
 
-	dstat = (istat & DIP) ? INB (nc_dstat) : 0;
 	sist  = (istat & SIP) ? INW (nc_sist)  : 0;
-	np->profile.num_int++;
+	dstat = (istat & DIP) ? INB (nc_dstat) : 0;
 
 	if (DEBUG_FLAGS & DEBUG_TINY)
 		printf ("<%d|%x:%x|%x:%x>",
@@ -5469,297 +6791,121 @@
 			dstat,sist,
 			(unsigned)INL(nc_dsp),
 			(unsigned)INL(nc_dbc));
-	if ((dstat==DFE) && (sist==PAR)) return;
-
-/*==========================================================
-**
-**	First the normal cases.
-**
-**==========================================================
-*/
-	/*-------------------------------------------
-	**	SCSI reset
-	**-------------------------------------------
-	*/
-
-	if (sist & RST) {
-		ncr_init (np, bootverbose ? "scsi reset" : NULL, HS_RESET);
-		return;
-	};
 
-	/*-------------------------------------------
-	**	selection timeout
+	/*========================================================
+	**	First, interrupts we want to service cleanly.
 	**
-	**	IID excluded from dstat mask!
-	**	(chip bug)
-	**-------------------------------------------
-	*/
-
-	if ((sist  & STO) &&
-		!(sist  & (GEN|HTH|MA|SGE|UDC|RST|PAR)) &&
-		!(dstat & (MDPE|BF|ABRT|SIR))) {
-		ncr_int_sto (np);
-		return;
-	};
-
-	/*-------------------------------------------
-	**      Phase mismatch.
-	**-------------------------------------------
-	*/
-
-	if ((sist  & MA) &&
-		!(sist  & (STO|GEN|HTH|SGE|UDC|RST|PAR)) &&
-		!(dstat & (MDPE|BF|ABRT|SIR|IID))) {
-		ncr_int_ma (np);
+	**	Phase mismatch is the most frequent interrupt, and 
+	**	so we have to service it as quickly and as cleanly 
+	**	as possible.
+	**	Programmed interrupts are rarely used in this driver,
+	**	but we must handle them cleanly anyway.
+	**	We try to deal with PAR and SBMC combined with 
+	**	some other interrupt(s).
+	**=========================================================
+	*/
+
+	if (!(sist  & (STO|GEN|HTH|SGE|UDC|RST)) &&
+	    !(dstat & (MDPE|BF|ABRT|IID))) {
+		if ((sist & SBMC) && ncr_int_sbmc (np))
+			return;
+		if ((sist & PAR)  && ncr_int_par  (np))
+			return;
+		if (sist & MA) {
+			ncr_int_ma (np);
+			return;
+		}
+		if (dstat & SIR) {
+			ncr_int_sir (np);
+			return;
+		}
+		if (!(sist & (SBMC|PAR)) && !(dstat & SSI))
+			printf("%s: unknown interrupt(s) ignored sist=%x dstat=%x\n",
+				ncr_name(np), sist, dstat);
+		OUTONB (nc_dcntl, (STD|NOCOM));
 		return;
 	};
 
-	/*----------------------------------------
-	**	move command with length 0
-	**----------------------------------------
+	/*========================================================
+	**	Now, interrupts that need some fixing up.
+	**	Order and multiple interrupts is so less important.
+	**
+	**	If SRST has been asserted, we just reset the chip.
+	**
+	**	Selection is intirely handled by the chip. If the 
+	**	chip says STO, we trust it. Seems some other 
+	**	interrupts may occur at the same time (UDC, IID), so 
+	**	we ignore them. In any case we do enough fix-up 
+	**	in the service routine.
+	**	We just exclude some fatal dma errors.
+	**=========================================================
 	*/
 
-	if ((dstat & IID) &&
-		!(sist  & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) &&
-		!(dstat & (MDPE|BF|ABRT|SIR)) &&
-		((INL(nc_dbc) & 0xf8000000) == SCR_MOVE_TBL)) {
-		/*
-		**      Target wants more data than available.
-		**	The "no_data" script will do it.
-		*/
-		OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, no_data));
+	if (sist & RST) {
+		ncr_init (np, bootverbose ? "scsi reset" : NULL, HS_RESET);
 		return;
 	};
 
-	/*-------------------------------------------
-	**	Programmed interrupt
-	**-------------------------------------------
-	*/
-
-	if ((dstat & SIR) &&
-		!(sist  & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) &&
-		!(dstat & (MDPE|BF|ABRT|IID)) &&
-		(INB(nc_dsps) <= SIR_MAX)) {
-		ncr_int_sir (np);
+	if ((sist & STO) &&
+		!(dstat & (MDPE|BF|ABRT))) {
+		ncr_int_sto (np);
 		return;
 	};
 
-	/*========================================
-	**	do the register dump
-	**========================================
+	/*=========================================================
+	**	Now, interrupts we are not able to recover cleanly.
+	**	(At least for the moment).
+	**
+	**	Do the register dump.
+	**	Log message for real hard errors.
+	**	Clear all fifos.
+	**	For MDPE, BF, ABORT, IID, SGE and HTH we reset the 
+	**	BUS and the chip.
+	**	We are more soft for UDC.
+	**=========================================================
 	*/
 	if (jiffies - np->regtime > 10*HZ) {
-		int i;
 		np->regtime = jiffies;
-		for (i=0; i<sizeof(np->regdump); i++)
+		for (i = 0; i<sizeof(np->regdump); i++)
 			((char*)&np->regdump)[i] = INB_OFF(i);
 		np->regdump.nc_dstat = dstat;
 		np->regdump.nc_sist  = sist;
 	};
 
-	/*=========================================
-	**	log message for real hard errors
-	**=========================================
-
-	"ncr0 targ 0?: ERROR (ds:si) (so-si-sd) (sxfer/scntl3) @ (dsp:dbc)."
-	"	      reg: r0 r1 r2 r3 r4 r5 r6 ..... rf."
-
-	exception register:
-		ds:	dstat
-		si:	sist
-
-	SCSI bus lines:
-		so:	control lines as driver by NCR.
-		si:	control lines as seen by NCR.
-		sd:	scsi data lines as seen by NCR.
-
-	wide/fastmode:
-		sxfer:	(see the manual)
-		scntl3:	(see the manual)
-
-	current script command:
-		dsp:	script adress (relative to start of script).
-		dbc:	first word of script command.
-
-	First 16 register of the chip:
-		r0..rf
-
-	=============================================
-	*/
-
-	dsp = (unsigned) INL (nc_dsp);
-	dsa = (unsigned) INL (nc_dsa);
-
-	script_ofs = dsp - np->p_script;
-
-	printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ (%x:%08x).\n",
-		ncr_name (np), (unsigned)INB (nc_ctest0)&0x0f, dstat, sist,
-		(unsigned)INB (nc_socl), (unsigned)INB (nc_sbcl), (unsigned)INB (nc_sbdl),
-		(unsigned)INB (nc_sxfer),(unsigned)INB (nc_scntl3), script_ofs,
-		(unsigned) INL (nc_dbc));
-
-	if (((script_ofs & 3) == 0) &&
-	    (unsigned)script_ofs < sizeof(struct script)) {
-		printf ("\tscript cmd = %08x\n", 
-			(int) *(ncrcmd *)((char*)np->script +script_ofs));
-	}
-
-        printf ("\treg:\t");
-        for (i=0; i<16;i++)
-            printf (" %02x", (unsigned)INB_OFF(i));
-        printf (".\n");
-
-	/*----------------------------------------
-	**	clean up the dma fifo
-	**----------------------------------------
-	*/
-
-	if ( (INB(nc_sstat0) & (ILF|ORF|OLF)   ) ||
-	     (INB(nc_sstat1) & (FF3210)	) ||
-	     (INB(nc_sstat2) & (ILF1|ORF1|OLF1)) ||	/* wide .. */
-	     !(dstat & DFE)) {
-		printf ("%s: have to clear fifos.\n", ncr_name (np));
-		OUTB (nc_stest3, TE|CSF);	/* clear scsi fifo */
-		OUTB (nc_ctest3, CLF);		/* clear dma fifo  */
-	}
+	ncr_log_hard_error(np, sist, dstat);
 
-	/*----------------------------------------
-	**	handshake timeout
-	**----------------------------------------
-	*/
+	printf ("%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, 2);
+		return;
+	};
 
 	if (sist & HTH) {
 		printf ("%s: handshake timeout\n", ncr_name(np));
-		OUTB (nc_scntl1, CRST);
-		DELAY (1000);
-		OUTB (nc_scntl1, 0x00);
-		OUTB (nc_scr0, HS_FAIL);
-		OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup));
-		return;
-	}
-
-	/*----------------------------------------
-	**	unexpected disconnect
-	**----------------------------------------
-	*/
-
-	if ((sist  & UDC) &&
-		!(sist  & (STO|GEN|HTH|MA|SGE|RST|PAR)) &&
-		!(dstat & (MDPE|BF|ABRT|SIR|IID))) {
-		OUTB (nc_scr0, HS_UNEXPECTED);
-		OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup));
+		ncr_start_reset(np, 2);
 		return;
 	};
 
-	/*----------------------------------------
-	**	cannot disconnect
-	**----------------------------------------
-	*/
-
-	if ((dstat & IID) &&
-		!(sist  & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) &&
-		!(dstat & (MDPE|BF|ABRT|SIR)) &&
-		((INL(nc_dbc) & 0xf8000000) == SCR_WAIT_DISC)) {
-		/*
-		**      Unexpected data cycle while waiting for disconnect.
-		*/
-		if (INB(nc_sstat2) & LDSC) {
-			/*
-			**	It's an early reconnect.
-			**	Let's continue ...
-			*/
-			OUTB (nc_dcntl, (STD|NOCOM));
-			/*
-			**	info message
-			*/
-			printf ("%s: INFO: LDSC while IID.\n",
-				ncr_name (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));
 		};
-		printf ("%s: target %d doesn't release the bus.\n",
-			ncr_name (np), (int)INB (nc_ctest0)&0x0f);
-		/*
-		**	return without restarting the NCR.
-		**	timeout will do the real work.
-		*/
-		return;
-	};
-
-	/*----------------------------------------
-	**	single step
-	**----------------------------------------
-	*/
-
-	if ((dstat & SSI) &&
-		!(sist  & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) &&
-		!(dstat & (MDPE|BF|ABRT|SIR|IID))) {
-		OUTB (nc_dcntl, (STD|NOCOM));
-		return;
-	};
-
-/*
-**	@RECOVER@ HTH, SGE, ABRT.
-**
-**	We should try to recover from these interrupts.
-**	They may occur if there are problems with synch transfers, or 
-**	if targets are switched on or off while the driver is running.
-*/
-
-	if (sist & SGE) {
-		OUTB (nc_ctest3, CLF);		/* clear scsi offsets */
-	}
-
-	/*
-	**	Freeze controller to be able to read the messages.
-	*/
-
-	if (DEBUG_FLAGS & DEBUG_FREEZE) {
-		unsigned char val;
-		for (i=0; i<0x60; i++) {
-			switch (i%16) {
-
-			case 0:
-				printf ("%s: reg[%d0]: ",
-					ncr_name(np),i/16);
-				break;
-			case 4:
-			case 8:
-			case 12:
-				printf (" ");
-				break;
-			};
-			val = INB_OFF(i);
-			printf (" %x%x", val/16, val%16);
-			if (i%16==15) printf (".\n");
-		}
-
-		del_timer(&np->timer);
-
-		printf ("%s: halted!\n", ncr_name(np));
-		/*
-		**	don't restart controller ...
-		*/
-		OUTB (nc_istat,  SRST);
+		ncr_start_reset(np, 2);
 		return;
 	};
 
-#ifdef NCR_FREEZE
-	/*
-	**	Freeze system to be able to read the messages.
-	*/
-	printf ("ncr: fatal error: system halted - press reset to reboot ...");
-	cli();
-	for (;;);
-#endif
-
-	/*
-	**	sorry, have to kill ALL jobs ...
+	/*=========================================================
+	**	We just miss the cause of the interrupt. :(
+	**	Print a message. The timeout will do the real work.
+	**=========================================================
 	*/
-
-	ncr_init (np, "fatal error", HS_FAIL);
-#ifndef SCSI_NCR_NO_DISCONNECT
-	np->disc = 1;
-#endif
+	printf ("%s: unknown interrupt\n", ncr_name(np));
 }
 
 /*==========================================================
@@ -5789,7 +6935,7 @@
 	*/
 
 	dsa = INL (nc_dsa);
-	cp = &np->ccb;
+	cp = np->ccb;
 	while (cp && (CCB_PHYS (cp, phys) != dsa))
 		cp = cp->link_ccb;
 
@@ -5803,7 +6949,7 @@
 	*/
 
 	scratcha = INL (nc_scratcha);
-	diff = scratcha - NCB_SCRIPT_PHYS (np, tryloop);
+	diff = scratcha - NCB_SCRIPTH_PHYS (np, tryloop);
 
 /*	assert ((diff <= MAX_START * 20) && !(diff % 20));*/
 
@@ -5813,9 +6959,55 @@
 		return;
 	};
 	ncr_init (np, "selection timeout", HS_FAIL);
-#ifndef SCSI_NCR_NO_DISCONNECT
 	np->disc = 1;
-#endif
+}
+
+/*==========================================================
+**
+**	ncr chip exception handler for SCSI bus mode change
+**
+**==========================================================
+**
+**	spi2-r12 11.2.3 says a transceiver mode change must 
+**	generate a reset event and a device that detects a reset 
+**	event shall initiate a hard reset. It says also that a
+**	device that detects a mode change shall set data transfer 
+**	mode to eight bit asynchronous, etc...
+**	So, just resetting should be enough.
+**	 
+**
+**----------------------------------------------------------
+*/
+
+static int ncr_int_sbmc (ncb_p np)
+{
+	u_char scsi_mode = INB (nc_stest4) & SMODE;
+
+	printf("%s: SCSI bus mode change from %x to %x, resetting ...\n",
+		ncr_name(np), np->scsi_mode, scsi_mode);
+
+	np->scsi_mode = scsi_mode;
+	ncr_start_reset(np, 2);
+
+	return 1;
+}
+
+/*==========================================================
+**
+**	ncr chip exception handler for SCSI parity error.
+**
+**==========================================================
+**
+**	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;
 }
 
 /*==========================================================
@@ -5836,49 +7028,76 @@
 {
 	u_int32	dbc;
 	u_int32	rest;
-	u_int32	dsa;
 	u_int32	dsp;
+	u_int32	dsa;
 	u_int32	nxtdsp;
 	u_int32	*vdsp;
 	u_int32	oadr, olen;
 	u_int32	*tblp;
         ncrcmd *newcmd;
-	u_char	cmd, sbcl, delta, ss0, ss2;
+	u_char	cmd, sbcl;
 	ccb_p	cp;
 
-	dsp = INL (nc_dsp);
-	dsa = INL (nc_dsa);
-	dbc = INL (nc_dbc);
-	ss0 = INB (nc_sstat0);
-	ss2 = INB (nc_sstat2);
-	sbcl= INB (nc_sbcl);
-
-	cmd = dbc >> 24;
-	rest= dbc & 0xffffff;
-	delta=(INB (nc_dfifo) - rest) & 0x7f;
-
-	/*
-	**	The data in the dma fifo has not been transfered to
-	**	the target -> add the amount to the rest
-	**	and clear the data.
-	**	Check the sstat2 register in case of wide transfer.
-	*/
-
-	if (! (INB(nc_dstat) & DFE)) rest += delta;
-	if (ss0 & OLF) rest++;
-	if (ss0 & ORF) rest++;
-	if (INB(nc_scntl3) & EWS) {
-		if (ss2 & OLF1) rest++;
-		if (ss2 & ORF1) rest++;
-	};
-	OUTB (nc_ctest3, CLF   );	/* clear dma fifo  */
-	OUTB (nc_stest3, TE|CSF);	/* clear scsi fifo */
+	dsp	= INL (nc_dsp);
+	dbc	= INL (nc_dbc);
+	sbcl	= INB (nc_sbcl);
+
+	cmd	= dbc >> 24;
+	rest	= dbc & 0xffffff;
+
+	/*
+	**	Take into account dma fifo and various buffers and latches,
+	**	only if the interrupted phase is an OUTPUT phase.
+	*/
+
+	if ((cmd & 1) == 0) {
+		u_char	ctest5, ss0, ss2;
+		u_short	delta;
+
+		ctest5 = (np->rv_ctest5 & DFS) ? INB (nc_ctest5) : 0;
+		if (ctest5 & DFS)
+			delta=(((ctest5 << 8) | (INB (nc_dfifo) & 0xff)) - rest) & 0x3ff;
+		else
+			delta=(INB (nc_dfifo) - rest) & 0x7f;
+
+		/*
+		**	The data in the dma fifo has not been transfered to
+		**	the target -> add the amount to the rest
+		**	and clear the data.
+		**	Check the sstat2 register in case of wide transfer.
+		*/
+
+		rest += delta;
+		ss0  = INB (nc_sstat0);
+		if (ss0 & OLF) rest++;
+		if (ss0 & ORF) rest++;
+		if (INB(nc_scntl3) & EWS) {
+			ss2 = INB (nc_sstat2);
+			if (ss2 & OLF1) rest++;
+			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,
+				(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);
+		}
+	}
 
 	/*
 	**	locate matching cp
 	*/
 	dsa = INL (nc_dsa);
-	cp = &np->ccb;
+	cp  = np->ccb;
 	while (cp && (CCB_PHYS (cp, phys) != dsa))
 		cp = cp->link_ccb;
 
@@ -5904,19 +7123,18 @@
 	} else if (dsp == vtophys (&cp->patch[6])) {
 		vdsp = &cp->patch[4];
 		nxtdsp = vdsp[3];
-	} else {
+	} else if (dsp > np->p_script && dsp <= np->p_script + sizeof(struct script)) {
 		vdsp = (u_int32 *) ((char*)np->script - np->p_script + dsp -8);
 		nxtdsp = dsp;
+	} else {
+		vdsp = (u_int32 *) ((char*)np->scripth - np->p_scripth + dsp -8);
+		nxtdsp = dsp;
 	};
 
 	/*
 	**	log the information
 	*/
-	if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_PHASE)) {
-		printf ("P%x%x ",cmd&7, sbcl&7);
-		printf ("RL=%d D=%d SS0=%x ",
-			(unsigned) rest, (unsigned) delta, ss0);
-	};
+
 	if (DEBUG_FLAGS & DEBUG_PHASE) {
 		printf ("\nCP=%p CP2=%p DSP=%x NXT=%x VDSP=%p CMD=%x ",
 			cp, np->header.cp,
@@ -5948,7 +7166,7 @@
 	};
 
 	/*
-	**	if old phase not dataphase, leave here.
+	**	check cmd against assumed interrupted script command.
 	*/
 
 	if (cmd != (vdsp[0] >> 24)) {
@@ -5958,13 +7176,25 @@
 		
 		return;
 	}
+
+#ifdef	SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+	if ((cmd & 7) == 1 && np->assert_atn) {
+		np->assert_atn = 0;
+		OUTONB(nc_socl, CATN);
+	}
+#endif
+
+	/*
+	**	if old phase not dataphase, leave here.
+	*/
+
 	if (cmd & 0x06) {
 		PRINT_ADDR(cp->cmd);
 		printf ("phase change %x-%x %d@%08x resid=%d.\n",
 			cmd&7, sbcl&7, (unsigned)olen,
 			(unsigned)oadr, (unsigned)rest);
 
-		OUTB (nc_dcntl, (STD|NOCOM));
+		OUTONB (nc_dcntl, (STD|NOCOM));
 		return;
 	};
 
@@ -6000,7 +7230,10 @@
 	*/
 	np->profile.num_break++;
 	OUTL (nc_temp, vtophys (newcmd));
-	OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, dispatch));
+	if ((cmd & 7) == 0)
+		OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, dispatch));
+	else
+		OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, checkatn));
 }
 
 /*==========================================================
@@ -6031,11 +7264,12 @@
 
 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) & 7;
+	u_char	target = INB (nc_ctest0) & 0x0f;
 	tcb_p	tp     = &np->target[target];
 	int     i;
 	if (DEBUG_FLAGS & DEBUG_TINY) printf ("I#%d", num);
@@ -6044,13 +7278,15 @@
 	case SIR_SENSE_RESTART:
 	case SIR_STALL_RESTART:
 		break;
+	case SIR_STALL_QUEUE:	/* Ignore, just restart the script */
+		goto out;
 
 	default:
 		/*
 		**	lookup the ccb
 		*/
 		dsa = INL (nc_dsa);
-		cp = &np->ccb;
+		cp = np->ccb;
 		while (cp && (CCB_PHYS (cp, phys) != dsa))
 			cp = cp->link_ccb;
 
@@ -6063,6 +7299,32 @@
 	}
 
 	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 = endp + 8;
+			cp->phys.header.savep = endp - cp->segments*16;
+		} else {
+			endp = NCB_SCRIPT_PHYS (np, data_in)  + MAX_SCATTER*16;
+			cp->phys.header.goalp = endp + 8;
+			cp->phys.header.savep = 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,	np->header.savep);
+		OUTL (nc_dsp,	np->header.savep);
+		return;
+		/* break; */
 
 /*--------------------------------------------------------------------
 **
@@ -6099,7 +7361,7 @@
 			if (DEBUG_FLAGS & DEBUG_RESTART)
 				printf ("+ restart job ..\n");
 			OUTL (nc_dsa, CCB_PHYS (cp, phys));
-			OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, getcc));
+			OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, getcc));
 			return;
 		};
 
@@ -6230,11 +7492,11 @@
 		switch (cp->nego_status) {
 
 		case NS_SYNC:
-			ncr_setsync (np, cp, 0xe0);
+			ncr_setsync (np, cp, 0, 0xe0);
 			break;
 
 		case NS_WIDE:
-			ncr_setwide (np, cp, 0);
+			ncr_setwide (np, cp, 0, 0);
 			break;
 
 		};
@@ -6277,8 +7539,8 @@
 		**	check values against driver limits.
 		*/
 
-		if (per < np->ns_sync)
-			{chg = 1; per = np->ns_sync;}
+		if (per < np->minsync)
+			{chg = 1; per = np->minsync;}
 		if (per < tp->minsync)
 			{chg = 1; per = tp->minsync;}
 		if (ofs > tp->maxoffs)
@@ -6287,23 +7549,26 @@
 		/*
 		**	Check against controller limits.
 		*/
+		fak	= 7;
+		scntl3	= 0;
 		if (ofs != 0) {
-			fak = (4ul * per - 1) / np->ns_sync - 3;
-			if (fak>7) {
+			ncr_getsync(np, per, &fak, &scntl3);
+			if (fak > 7) {
 				chg = 1;
 				ofs = 0;
 			}
 		}
 		if (ofs == 0) {
-			fak = 7;
-			per = 0;
+			fak	= 7;
+			per	= 0;
+			scntl3	= 0;
 			tp->minsync = 0;
 		}
 
 		if (DEBUG_FLAGS & DEBUG_NEGO) {
 			PRINT_ADDR(cp->cmd);
-			printf ("sync: per=%d ofs=%d fak=%d chg=%d.\n",
-				per, ofs, fak, chg);
+			printf ("sync: per=%d scntl3=0x%x ofs=%d fak=%d chg=%d.\n",
+				per, scntl3, ofs, fak, chg);
 		}
 
 		if (INB (HS_PRT) == HS_NEGOTIATE) {
@@ -6318,19 +7583,19 @@
 					/*
 					**	Answer wasn't acceptable.
 					*/
-					ncr_setsync (np, cp, 0xe0);
+					ncr_setsync (np, cp, 0, 0xe0);
 					OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
 				} else {
 					/*
 					**	Answer is ok.
 					*/
-					ncr_setsync (np, cp, (fak<<5)|ofs);
+					ncr_setsync (np, cp, scntl3, (fak<<5)|ofs);
 					OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack));
 				};
 				return;
 
 			case NS_WIDE:
-				ncr_setwide (np, cp, 0);
+				ncr_setwide (np, cp, 0, 0);
 				break;
 			};
 		};
@@ -6354,7 +7619,7 @@
 		**      prepare an answer message
 		*/
 
-		ncr_setsync (np, cp, (fak<<5)|ofs);
+		ncr_setsync (np, cp, scntl3, (fak<<5)|ofs);
 
 		np->msgout[0] = M_EXTENDED;
 		np->msgout[1] = 3;
@@ -6367,7 +7632,7 @@
 		if (DEBUG_FLAGS & DEBUG_NEGO) {
 			PRINT_ADDR(cp->cmd);
 			printf ("sync msgout: ");
-			(void) ncr_show_msg (np->msgin);
+			(void) ncr_show_msg (np->msgout);
 			printf (".\n");
 		}
 
@@ -6429,19 +7694,19 @@
 					/*
 					**	Answer wasn't acceptable.
 					*/
-					ncr_setwide (np, cp, 0);
+					ncr_setwide (np, cp, 0, 1);
 					OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
 				} else {
 					/*
 					**	Answer is ok.
 					*/
-					ncr_setwide (np, cp, wide);
+					ncr_setwide (np, cp, wide, 1);
 					OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack));
 				};
 				return;
 
 			case NS_SYNC:
-				ncr_setsync (np, cp, 0xe0);
+				ncr_setsync (np, cp, 0, 0xe0);
 				break;
 			};
 		};
@@ -6451,7 +7716,7 @@
 		**      prepare an answer message
 		*/
 
-		ncr_setwide (np, cp, wide);
+		ncr_setwide (np, cp, wide, 1);
 
 		np->msgout[0] = M_EXTENDED;
 		np->msgout[1] = 2;
@@ -6534,13 +7799,14 @@
 		*/
 
 		PRINT_ADDR(cp->cmd);
-		printf ("M_DISCONNECT received, but datapointer not saved:\n"
-			"\tdata=%x save=%x goal=%x.\n",
+		printf ("M_DISCONNECT received, but datapointer not saved: "
+			"data=%x save=%x goal=%x.\n",
 			(unsigned) INL (nc_temp),
 			(unsigned) np->header.savep,
 			(unsigned) np->header.goalp);
 		break;
 
+#if 0   /* This stuff does not work */
 /*--------------------------------------------------------------------
 **
 **	Processing of a "S_QUEUE_FULL" status.
@@ -6571,14 +7837,9 @@
 		np->script->start1[0] =  SCR_INT;
 
 		/*
-		**	For the moment tagged transfers cannot be disabled.
-		*/
-#if 0
-		/*
 		**	Try to disable tagged transfers.
 		*/
 		ncr_setmaxtags (np, &np->target[target], 0);
-#endif
 
 		/*
 		** @QUEUE@
@@ -6601,7 +7862,7 @@
 		/*
 		**	Look for a disconnected job.
 		*/
-		cp = &np->ccb;
+		cp = np->ccb;
 		while (cp && cp->host_status != HS_DISCONNECT)
 			cp = cp->link_ccb;
 
@@ -6623,10 +7884,11 @@
 		printf ("%s: queue empty.\n", ncr_name (np));
 		np->script->start1[0] =  SCR_INT ^ IFFALSE (0);
 		break;
+#endif   /* This stuff does not work */
 	};
 
 out:
-	OUTB (nc_dcntl, (STD|NOCOM));
+	OUTONB (nc_dcntl, (STD|NOCOM));
 }
 
 /*==========================================================
@@ -6649,7 +7911,9 @@
 	*/
 
 	lp = np->target[target].lp[lun];
-	if (lp) {
+
+	if (lp && lp->opennings && (!lp->active || lp->active < lp->reqlink)) {
+
 		cp = lp->next_ccb;
 
 		/*
@@ -6657,6 +7921,15 @@
 		*/
 
 		while (cp && cp->magic) cp = cp->next_ccb;
+
+		/*
+		**	Increment active commands and decrement credit.
+		*/
+
+		if (cp) {
+			++lp->active;
+			--lp->opennings;
+		}
 	}
 
 	/*
@@ -6668,7 +7941,7 @@
 	if ((!cp) && lp && lp->actccbs > 0)
 		return ((ccb_p) 0);
 
-	if (!cp) cp = &np->ccb;
+	if (!cp) cp = np->ccb;
 
 	/*
 	**	Wait until available.
@@ -6697,18 +7970,30 @@
 **==========================================================
 */
 
-void ncr_free_ccb (ncb_p np, ccb_p cp)
+void ncr_free_ccb (ncb_p np, ccb_p cp, u_long target, u_long lun)
 {
+	lcb_p lp;
+
 	/*
 	**    sanity
 	*/
 
 	assert (cp != NULL);
 
+	/*
+	**	Decrement active commands and increment credit.
+	*/
+
+	lp = np->target[target].lp[lun];
+	if (lp) {
+			--lp->active;
+			++lp->opennings;
+	}
+
 	cp -> host_status = HS_IDLE;
 	cp -> magic = 0;
 #if 0
-	if (cp == &np->ccb)
+	if (cp == np->ccb)
 		wakeup ((caddr_t) cp);
 #endif
 }
@@ -6743,10 +8028,12 @@
 		tp->jump_tcb.l_cmd   = (SCR_JUMP^IFFALSE (DATA (0x80 + target)));
 		tp->jump_tcb.l_paddr = np->jump_tcb.l_paddr;
 
-		tp->getscr[0] = SCR_COPY (1);
+		tp->getscr[0] =
+			(np->features & FE_PFEN)? SCR_COPY(1) : SCR_COPY_F(1);
 		tp->getscr[1] = vtophys (&tp->sval);
 		tp->getscr[2] = np->paddr + offsetof (struct ncr_reg, nc_sxfer);
-		tp->getscr[3] = SCR_COPY (1);
+		tp->getscr[3] =
+			(np->features & FE_PFEN)? SCR_COPY(1) : SCR_COPY_F(1);
 		tp->getscr[4] = vtophys (&tp->wval);
 		tp->getscr[5] = np->paddr + offsetof (struct ncr_reg, nc_scntl3);
 
@@ -6759,7 +8046,7 @@
 		tp->call_lun.l_paddr = NCB_SCRIPT_PHYS (np, resel_lun);
 
 		tp->jump_lcb.l_cmd   = (SCR_JUMP);
-		tp->jump_lcb.l_paddr = NCB_SCRIPT_PHYS (np, abort);
+		tp->jump_lcb.l_paddr = NCB_SCRIPTH_PHYS (np, abort);
 		np->jump_tcb.l_paddr = vtophys (&tp->jump_tcb);
 	}
 
@@ -6771,7 +8058,7 @@
 		/*
 		**	Allocate a lcb
 		*/
-		lp = (lcb_p) m_alloc (sizeof (struct lcb));
+		lp = (lcb_p) m_alloc (sizeof (struct lcb), LCB_ALIGN_SHIFT);
 		if (!lp) return;
 
 		if (DEBUG_FLAGS & DEBUG_ALLOC) {
@@ -6790,31 +8077,25 @@
 		lp->call_tag.l_paddr = NCB_SCRIPT_PHYS (np, resel_tag);
 
 		lp->jump_ccb.l_cmd   = (SCR_JUMP);
-		lp->jump_ccb.l_paddr = NCB_SCRIPT_PHYS (np, aborttag);
+		lp->jump_ccb.l_paddr = NCB_SCRIPTH_PHYS (np, aborttag);
 
 		lp->actlink = 1;
 
+		lp->active  = 1;
+
 		/*
 		**   Chain into LUN list
 		*/
 		tp->jump_lcb.l_paddr = vtophys (&lp->jump_lcb);
 		tp->lp[lun] = lp;
 
-#ifndef SCSI_NCR_TAGGED_QUEUE_DISABLED
-		if (!lp->usetags) {
-			ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS);
-		}
-#endif
+		ncr_setmaxtags (np, tp, driver_setup.default_tags);
 	}
 
 	/*
 	**	Allocate ccbs up to lp->reqccbs.
-	**
-	**	This modification will be reworked in a future release.
 	*/
 
-loop_alloc_ccb:
-
 	/*
 	**	Limit possible number of ccbs.
 	**
@@ -6828,7 +8109,7 @@
 	/*
 	**	Allocate a ccb
 	*/
-	cp = (ccb_p) m_alloc (sizeof (struct ccb));
+	cp = (ccb_p) m_alloc (sizeof (struct ccb), CCB_ALIGN_SHIFT);
 	if (!cp)
 		return;
 
@@ -6866,16 +8147,68 @@
 	/*
 	**	Chain into wakeup list
 	*/
-	cp->link_ccb      = np->ccb.link_ccb;
-	np->ccb.link_ccb  = cp;
+	cp->link_ccb      = np->ccb->link_ccb;
+	np->ccb->link_ccb = cp;
 
 	/*
 	**	Chain into CCB list
 	*/
-	cp->next_ccb	= lp->next_ccb;
-	lp->next_ccb	= cp;
+	cp->next_ccb	= lp->next_ccb;
+	lp->next_ccb	= cp;
+}
+
+/*==========================================================
+**
+**
+**	Announce the number of ccbs/tags to the scsi driver.
+**
+**
+**==========================================================
+*/
+
+static void ncr_opennings (ncb_p np, lcb_p lp, Scsi_Cmnd * cmd)
+{
+	/*
+	**	want to reduce the number ...
+	*/
+	if (lp->actlink > lp->reqlink) {
+
+		/*
+		**	Try to  reduce the count.
+		**	We assume to run at splbio ..
+		*/
+		u_char diff = lp->actlink - lp->reqlink;
+
+		if (!diff) return;
+
+		if (diff > lp->opennings)
+			diff = lp->opennings;
+
+		lp->opennings	-= diff;
+
+		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;
+	};
+
+	/*
+	**	want to increase the number ?
+	*/
+	if (lp->reqlink > lp->actlink) {
+		u_char diff = lp->reqlink - lp->actlink;
 
-goto loop_alloc_ccb;
+		lp->opennings	+= diff;
+
+		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);
+	};
 }
 
 /*==========================================================
@@ -6894,8 +8227,7 @@
 **----------------------------------------------------------
 */
 
-/*	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.
@@ -6903,158 +8235,49 @@
 **	we had to use a break point every 512 bytes.
 **	Of course the number of scatter/gather blocks is
 **	limited.
+**	Under Linux, the scatter/gatter blocks are provided by 
+**	the generic driver. We just have to copy addresses and 
+**	sizes to the data segment array.
 */
 
-/*
-**	The scatterlist passed by the linux middle-level scsi drivers
-**	may contain blocks of any size (Generaly < 1024 bytes blocks,
-**	can be 4096 with a 4K fs).
-*/
-
-#if defined(SCSI_NCR_SEGMENT_SIZE)
 static	int	ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd)
 {
-	struct scatterlist *scatter;
-	struct dsb *phys;
-	register u_short segment = 0;
-	register u_short o_segment = 0;
-	u_short chunk, chunk_min;
-	u_long segaddr;
-	int segsize;
-	int datalen;
-
-	phys = &cp->phys;
-	cp->data_len = 0;
-
-	/*
-	**	Compute a good value for chunk size
-	**	If SCSI_NCR_SEGMENT_SIZE is OK, we will try to use it. 
-	*/
-
-	if (!cmd->use_sg)
-		cp->data_len	= cmd->request_bufflen;
-	else {
-		scatter = (struct scatterlist *)cmd->buffer;
-		for (segment = 0 ; segment < cmd->use_sg ; segment++)
-			cp->data_len += scatter[segment].length;
-	}
-
-
-	if (!cp->data_len) {
-		bzero (&phys->data, sizeof (phys->data));
-		return 0;
-	}
-
-	chunk_min	= cp->data_len / MAX_SCATTER;
-	for (chunk = SCSI_NCR_SEGMENT_SIZE ; chunk < chunk_min ; chunk += chunk);
-
-	/*
-	**	If the linux scsi command is not a scatterlist,
-	**	the computed chunk size is OK.
-	*/
-
-	if (!cmd->use_sg) {
-		bzero (&phys->data, sizeof (phys->data));
-		datalen = cmd->request_bufflen;
-		segaddr = vtophys(cmd->request_buffer);
-		segsize = chunk;
-		o_segment = 0;
-
-if (DEBUG_FLAGS & DEBUG_SCATTER)
-	printf("ncr53c8xx: re-scattering physical=0x%x size=%d chunk=%d.\n",
-	(unsigned) segaddr, (int) datalen, (int) chunk);
-
-		while (datalen && (o_segment < MAX_SCATTER)) {
-			if (segsize > datalen) segsize	= datalen;
-			phys->data[o_segment].addr	= segaddr;
-			phys->data[o_segment].size	= segsize;
+	struct scr_tblmove *data;
+	int segment	= 0;
+	int use_sg	= (int) cmd->use_sg;
 
-			datalen -= segsize;
-
-if(DEBUG_FLAGS & DEBUG_SCATTER)
-	printf ("ncr53c8xx:     seg #%d  addr=%lx  size=%d  (rest=%d).\n",
-	o_segment, segaddr, (int) segsize, (int) datalen);
+#if 0
+	bzero (cp->phys.data, sizeof (cp->phys.data));
+#endif
+	data		= cp->phys.data;
+	cp->data_len	= 0;
 
-			segaddr	+= segsize;
-			o_segment++;
+	if (!use_sg) {
+		if (cmd->request_bufflen) {
+			data = &data[MAX_SCATTER - 1];
+			data[0].addr	= vtophys(cmd->request_buffer);
+			data[0].size	= cmd->request_bufflen;
+			cp->data_len	= data[0].size;
+			segment = 1;
 		}
-
-		return datalen ? -1 : o_segment;
 	}
+	else if (use_sg <= MAX_SCATTER) {
+		struct scatterlist *scatter = (struct scatterlist *)cmd->buffer;
 
-	/*
-	**	Else, the computed chunk size is not so good
-	**	and we have to iterate.
-	**	Rescatter the Linux scatterlist into the data block descriptor.
-	**	Loop if necessary, beginning with the not so good chunk size and
-	**	doubling it if the scatter process fails.
-	*/
-
-	scatter = (struct scatterlist *)cmd->buffer;
-	for (segment = 0; segment < cmd->use_sg; chunk += chunk) {
-		o_segment	= 0;
-		bzero (&phys->data, sizeof (phys->data));
-		for (segment = 0 ; segment < cmd->use_sg ; segment++) {
-			datalen = scatter[segment].length;
-			segaddr = vtophys(scatter[segment].address);
-			segsize = chunk;
-
-if (DEBUG_FLAGS & DEBUG_SCATTER)
-	printf("ncr53c8xx: re-scattering physical=0x%x size=%d chunk=%d.\n",
-	(unsigned) segaddr, (int) datalen, (int) chunk);
-
-			while (datalen && (o_segment < MAX_SCATTER)) {
-				if (segsize > datalen) segsize	= datalen;
-				phys->data[o_segment].addr	= segaddr;
-				phys->data[o_segment].size	= segsize;
-
-				datalen -= segsize;
-
-if(DEBUG_FLAGS & DEBUG_SCATTER)
-	printf ("ncr53c8xx:     seg #%d  addr=%lx  size=%d  (rest=%d).\n",
-	o_segment, segaddr, (int) segsize, (int) datalen);
-
-				segaddr	+= segsize;
-				o_segment++;
-			}
-
-			if (datalen) break;
+		data = &data[MAX_SCATTER - use_sg];
+		while (segment < use_sg) {
+			data[segment].addr = vtophys(scatter[segment].address);
+			data[segment].size = scatter[segment].length;
+			cp->data_len	   += data[segment].size;
+			++segment;
 		}
 	}
-
-	return segment < cmd->use_sg ? -1 : o_segment;
-}
-
-#else /* !defined SCSI_NCR_SEGMENT_SIZE */
-
-static	int	ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd)
-{
-	struct dsb *phys = &cp->phys;
-	u_short	segment  = 0;
-
-	cp->data_len = 0;
-	bzero (&phys->data, sizeof (phys->data));
-
-	if (!cmd->use_sg) {
-	     phys->data[segment].addr = vtophys(cmd->request_buffer);
-	     phys->data[segment].size = cmd->request_bufflen;
-	     cp->data_len            += phys->data[segment].size;
-	     segment++;
-	     return segment;
-	}
-
-	while (segment < cmd->use_sg && segment < MAX_SCATTER) {
-	     struct scatterlist *scatter = (struct scatterlist *)cmd->buffer;
-
-	     phys->data[segment].addr = vtophys(scatter[segment].address);
-	     phys->data[segment].size = scatter[segment].length;
-	     cp->data_len            += phys->data[segment].size;
-	     ++segment;
+	else {
+		return -1;
 	}
 
-	return segment < cmd->use_sg ? -1 : segment;
+	return segment;
 }
-#endif /* SCSI_NCR_SEGMENT_SIZE */
 
 /*==========================================================
 **
@@ -7068,7 +8291,9 @@
 */
 
 #ifndef NCR_IOMAPPED
+__initfunc(
 static int ncr_regtest (struct ncb* np)
+)
 {
 	register volatile u_long data;
 	/*
@@ -7092,12 +8317,14 @@
 }
 #endif
 
+__initfunc(
 static int ncr_snooptest (struct ncb* np)
+)
 {
 	u_long	ncr_rd, ncr_wr, ncr_bk, host_rd, host_wr, pc, err=0;
 	int	i;
 #ifndef NCR_IOMAPPED
-	if (np->use_mmio) {
+	if (np->reg) {
             err |= ncr_regtest (np);
             if (err) return (err);
 	}
@@ -7105,7 +8332,7 @@
 	/*
 	**	init
 	*/
-	pc  = NCB_SCRIPT_PHYS (np, snooptest);
+	pc  = NCB_SCRIPTH_PHYS (np, snooptest);
 	host_wr = 1;
 	ncr_wr  = 2;
 	/*
@@ -7149,8 +8376,11 @@
 	/*
 	**	Check termination position.
 	*/
-	if (pc != NCB_SCRIPT_PHYS (np, snoopend)+8) {
+	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,
+			(u_long) NCB_SCRIPTH_PHYS (np, snoopend) +8);
 		return (0x40);
 	};
 	/*
@@ -7183,23 +8413,19 @@
 **==========================================================
 */
 
-/*
-**	Compute the difference in milliseconds.
-**/
+#ifdef SCSI_NCR_PROFILE_SUPPORT
 
-#ifdef SCSI_NCR_PROFILE
+/*
+**	Compute the difference in jiffies ticks.
+*/
 
-static	int ncr_delta (u_long from, u_long to)
-{
-	if (!from) return (-1);
-	if (!to) return (-2);
-	return ((to  - from) * 1000 / HZ );
-}
+#define ncr_delta(from, to) \
+	( ((to) && (from))? (to) - (from) : -1 )
 
 #define PROFILE  cp->phys.header.stamp
 static	void ncb_profile (ncb_p np, ccb_p cp)
 {
-	int co, da, st, en, di, se, post,work,disc;
+	int co, st, en, di, se, post,work,disc;
 	u_long diff;
 
 	PROFILE.end = jiffies;
@@ -7207,9 +8433,6 @@
 	st = ncr_delta (PROFILE.start,PROFILE.status);
 	if (st<0) return;	/* status  not reached  */
 
-	da = ncr_delta (PROFILE.start,PROFILE.data);
-	if (da<0) return;	/* No data transfer phase */
-
 	co = ncr_delta (PROFILE.start,PROFILE.command);
 	if (co<0) return;	/* command not executed */
 
@@ -7246,7 +8469,7 @@
 }
 #undef PROFILE
 
-#endif /* SCSI_NCR_PROFILE */
+#endif /* SCSI_NCR_PROFILE_SUPPORT */
 
 /*==========================================================
 **
@@ -7307,7 +8530,7 @@
 /*==========================================================
 **
 **	Determine the ncr's clock frequency.
-**	This is important for the negotiation
+**	This is essential for the negotiation
 **	of the synchronous transfer rate.
 **
 **==========================================================
@@ -7315,84 +8538,161 @@
 **	Note: we have to return the correct value.
 **	THERE IS NO SAVE DEFAULT VALUE.
 **
-**	We assume that all NCR based boards are delivered
-**	with a 40Mhz clock. Because we have to divide
-**	by an integer value greater than 3, only clock
-**	frequencies of 40Mhz (/4) or 50MHz (/5) permit
-**	the FAST-SCSI rate of 10MHz.
+**	Most NCR/SYMBIOS boards are delivered with a 40 Mhz clock.
+**	53C860 and 53C875 rev. 1 support fast20 transfers but 
+**	do not have a clock doubler and so are provided with a 
+**	80 MHz clock. All other fast20 boards incorporate a doubler 
+**	and so should be delivered with a 40 MHz clock.
+**	The future fast40 chips (895/895) use a 40 Mhz base clock 
+**	and provide a clock quadrupler (160 Mhz). The code below 
+**	tries to deal as cleverly as possible with all this stuff.
 **
 **----------------------------------------------------------
 */
 
-#ifndef NCR_CLOCK
-#	define NCR_CLOCK 40
-#endif /* NCR_CLOCK */
+/*
+ *	Select NCR SCSI clock frequency
+ */
+static void ncr_selectclock(ncb_p np, u_char scntl3)
+{
+	if (np->multiplier < 2) {
+		OUTB(nc_scntl3,	scntl3);
+		return;
+	}
+
+	if (bootverbose >= 2)
+		printf ("%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);
+		if (!i)
+			printf("%s: the chip cannot lock the frequency\n", ncr_name(np));
+	} else			/* Wait 20 micro-seconds for doubler	*/
+		DELAY(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);		/* Restart scsi clock 		*/
+}
 
-static void ncr_getclock (ncb_p np, u_char scntl3)
-{
-#if 0
-	u_char	tbl[5] = {6,2,3,4,6};
-	u_char	f;
-	u_char	ns_clock = (1000/NCR_CLOCK);
-
-	/*
-	**	Compute the best value for scntl3.
-	*/
-
-	f = (2 * MIN_SYNC_PD - 1) / ns_clock;
-	if (!f ) f=1;
-	if (f>4) f=4;
-	np -> ns_sync = (ns_clock * tbl[f]) / 2;
-	np -> rv_scntl3 = f<<4;
-
-	f = (2 * MIN_ASYNC_PD - 1) / ns_clock;
-	if (!f ) f=1;
-	if (f>4) f=4;
-	np -> ns_async = (ns_clock * tbl[f]) / 2;
-	np -> rv_scntl3 |= f;
-	if (DEBUG_FLAGS & DEBUG_TIMING)
-		printf ("%s: sclk=%d async=%d sync=%d (ns) scntl3=0x%x\n",
-		ncr_name (np), ns_clock, np->ns_async, np->ns_sync, np->rv_scntl3);
-#else
-	/*
-	 * If NCR53C875 chip with clock doubler enabled,
-	 *	disable clock doubler and assume 40 MHz clock.
-	 * If NCR53C860 chip assume 80 MHz clock.
+
+/*
+ *	calculate NCR SCSI clock frequency (in KHz)
+ */
+__initfunc(
+static unsigned ncrgetfreq (ncb_p np, int gen)
+)
+{
+	unsigned ms = 0;
+
+	/*
+	 * Measure GEN timer delay in order 
+	 * to calculate SCSI clock frequency
+	 *
+	 * This code will never execute too
+	 * many loop iterations (if DELAY is 
+	 * reasonably correct). It could get
+	 * too low a delay (too high a freq.)
+	 * if the CPU is slow executing the 
+	 * loop for some reason (an NMI, for
+	 * example). For this reason we will
+	 * if multiple measurements are to be 
+	 * performed trust the higher delay 
+	 * (lower frequency returned).
 	 */
+	OUTB (nc_stest1, 0);	/* make sure clock doubler is OFF */
+	OUTW (nc_sien , 0);	/* mask all scsi interrupts */
+	(void) INW (nc_sist);	/* clear pending scsi interrupt */
+	OUTB (nc_dien , 0);	/* mask all dma interrupts */
+	(void) INW (nc_sist);	/* another one, just to be sure :) */
+	OUTB (nc_scntl3, 4);	/* set pre-scaler to divide by 3 */
+	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 */
+	OUTB (nc_stime1, 0);	/* disable general purpose timer */
+ 	/*
+ 	 * set prescaler to divide by whatever 0 means
+ 	 * 0 ought to choose divide by 2, but appears
+ 	 * to set divide by 3.5 mode in my 53c810 ...
+ 	 */
+ 	OUTB (nc_scntl3, 0);
+
+	if (bootverbose >= 2)
+		printf ("%s: Delay (GEN=%d): %u msec\n", ncr_name(np), gen, ms);
+  	/*
+ 	 * adjust for prescaler, and convert into KHz 
+  	 */
+	return ms ? ((1 << gen) * 4340) / ms : 0;
+}
 
-	switch(np->device_id) {
-	case PCI_DEVICE_ID_NCR_53C875:
-		if ((INB(nc_stest1) & (DBLEN+DBLSEL)) == DBLEN+DBLSEL) {
-			if (bootverbose)
-				printf ("%s: disabling clock doubler\n", ncr_name(np));
-			OUTB(nc_stest1, 0);
-			scntl3	= 3;
-		}
-		break;
-	case PCI_DEVICE_ID_NCR_53C860:
-		scntl3	= 5;
-		break;
+/*
+ *	Get/probe NCR SCSI clock frequency
+ */
+__initfunc(
+static void ncr_getclock (ncb_p np, int mult)
+)
+{
+	unsigned char scntl3 = INB(nc_scntl3);
+	unsigned char stest1 = INB(nc_stest1);
+	unsigned f1;
+
+	np->multiplier = 1;
+	f1 = 40000;
+
+	/*
+	**	True with 875 or 895 with clock multiplier selected
+	*/
+	if (mult > 1 && (stest1 & (DBLEN+DBLSEL)) == DBLEN+DBLSEL) {
+		if (bootverbose >= 2)
+			printf ("%s: clock multiplier found\n", ncr_name(np));
+		np->multiplier = mult;
 	}
 
 	/*
-	 *	For now just preserve the BIOS setting ...
-	 */
+	**	If multiplier not found or scntl3 not 7,5,3,
+	**	reset chip and get frequency from general purpose timer.
+	**	Otherwise trust scntl3 BIOS setting.
+	*/
+	if (np->multiplier != mult || (scntl3 & 7) < 3 || !(scntl3 & 1)) {
+		unsigned f2;
 
-	if ((scntl3 & 7) < 3) {
-		printf ("%s: assuming 40MHz clock\n", ncr_name(np));
-		scntl3 = 3; /* assume 40MHz if no value supplied by BIOS */
-	}
+		OUTB(nc_istat, SRST); DELAY(5); OUTB(nc_istat, 0);
 
-	np->ns_sync   = 25;
-	np->ns_async  = 50;
-	np->rv_scntl3 = ((scntl3 & 0x7) << 4) -0x20 + (scntl3 & 0x7);
-
-	if (bootverbose) {
-		printf ("%s: initial value of SCNTL3 = %02x, final = %02x\n",
-			ncr_name(np), scntl3, np->rv_scntl3);
+		(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);
+
+		if (f1 > f2) f1 = f2;		/* trust lower result	*/
+
+		if	(f1 <	45000)		f1 =  40000;
+		else if (f1 <	55000)		f1 =  50000;
+		else				f1 =  80000;
+
+		if (f1 < 80000 && mult > 1) {
+			if (bootverbose >= 2)
+				printf ("%s: clock multiplier assumed\n", ncr_name(np));
+			np->multiplier	= mult;
+		}
+	} else {
+		if	((scntl3 & 7) == 3)	f1 =  40000;
+		else if	((scntl3 & 7) == 5)	f1 =  80000;
+		else 				f1 = 160000;
+
+		f1 /= np->multiplier;
 	}
-#endif
+
+	/*
+	**	Compute controller synchronous parameters.
+	*/
+	f1		*= np->multiplier;
+	np->clock_khz	= f1;
 }
 
 /*===================== LINUX ENTRY POINTS SECTION ==========================*/
@@ -7409,29 +8709,108 @@
 #define ulong unsigned long
 #endif
 
-static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, int unit, int board, int chip,
-	     uchar bus, uchar device_fn, int options);
-
-/*
-**   NCR53C8XX devices description table
+/* ---------------------------------------------------------------------
+**
+**	Driver setup from the boot command line
+**
+** ---------------------------------------------------------------------
 */
 
-static struct {
-     ushort pci_device_id;
-     int chip;
-     int max_revision;
-     int min_revision;
-} pci_chip_ids[] = { 
-     {PCI_DEVICE_ID_NCR_53C810,   810, -1, -1}, 
-/*   {PCI_DEVICE_ID_NCR_53C810AP, 810, -1, -1}, */
-     {PCI_DEVICE_ID_NCR_53C815,   815, -1, -1},
-     {PCI_DEVICE_ID_NCR_53C820,   820, -1, -1},
-     {PCI_DEVICE_ID_NCR_53C825,   825, -1, -1},
-     {PCI_DEVICE_ID_NCR_53C860,   860, -1, -1},
-     {PCI_DEVICE_ID_NCR_53C875,   875, -1, -1}
-};
+__initfunc(
+void ncr53c8xx_setup(char *str, int *ints)
+)
+{
+#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
+	char *cur = str;
+	char *pc, *pv;
+	int val;
+	int base;
+	int c;
+
+	while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
+		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;
+			}
+			else if (c >= '0' && c <= '9')
+				base = 10;
+			else
+				break;
+#endif
+			val = (int) simple_strtoul(pv, NULL, base);
+		}
 
-#define NPCI_CHIP_IDS (sizeof (pci_chip_ids) / sizeof(pci_chip_ids[0]))
+		if	(!strncmp(cur, "mpar:", 5))
+			driver_setup.master_parity	= val;
+		else if	(!strncmp(cur, "spar:", 5))
+			driver_setup.scsi_parity	= val;
+		else if	(!strncmp(cur, "disc:", 5))
+			driver_setup.disconnection	= val;
+		else if	(!strncmp(cur, "specf:", 6))
+			driver_setup.special_features = val;
+		else if	(!strncmp(cur, "ultra:", 6))
+			driver_setup.ultra_scsi	= val;
+		else if	(!strncmp(cur, "fsn:", 4))
+			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))
+			driver_setup.verbose	= val;
+		else if	(!strncmp(cur, "debug:", 6))
+			driver_setup.debug	= val;
+		else if	(!strncmp(cur, "burst:", 6))
+			driver_setup.burst_max	= val;
+		else if	(!strncmp(cur, "led:", 4))
+			driver_setup.led_pin	= val;
+		else if	(!strncmp(cur, "wide:", 5))
+			driver_setup.max_wide	= val? 1:0;
+		else if	(!strncmp(cur, "settle:", 7))
+			driver_setup.settle_delay= val;
+		else if	(!strncmp(cur, "diff:", 5))
+			driver_setup.diff_support= val;
+		else if	(!strncmp(cur, "irqm:", 5))
+			driver_setup.irqm	= val;
+		else if	(!strncmp(cur, "pcifix:", 7))
+			driver_setup.pci_fix_up	= val;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+		else if	(!strncmp(cur, "nvram:", 6))
+			driver_setup.use_nvram	= val;
+#endif
+
+		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);
+
+		if ((cur = strchr(cur, ',')) != NULL)
+			++cur;
+	}
+#endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */
+}
+
+static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
+	     uchar bus, uchar device_fn, ncr_device *device);
 
 /*
 **   Linux entry point for NCR53C8XX devices detection routine.
@@ -7442,15 +8821,156 @@
 **   Read the PCI configuration and try to attach each
 **   detected NCR board.
 **
+**   If NVRAM is present, try to attach boards according to 
+**   the used defined boot order.
+**
 **   Returns the number of boards successfully attached.
 */
 
+__initfunc(
+static void ncr_print_driver_setup(void)
+)
+{
+#define YesNo(y)	y ? 'y' : 'n'
+	printk("ncr53c8xx: setup=disc:%c,specf:%c,ultra:%c,tags:%d,sync:%d,burst:%d,wide:%c,diff:%d\n",
+			YesNo(driver_setup.disconnection),
+			YesNo(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);
+#undef YesNo
+}
+
+/*
+**   NCR53C8XX devices description table and chip ids list.
+*/
+
+static ncr_chip	ncr_chip_table[] __initdata	= SCSI_NCR_CHIP_TABLE;
+static ushort	ncr_chip_ids[]   __initdata	= SCSI_NCR_CHIP_IDS;
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+__initfunc(
+static int
+ncr_attach_using_nvram(Scsi_Host_Template *tpnt, int nvram_index, int count, ncr_device device[])
+)
+{
+	int i, j;
+	int attach_count = 0;
+	ncr_nvram  *nvram;
+	ncr_device *devp;
+
+	if (!nvram_index)
+		return 0;
+
+	/* find first Symbios NVRAM if there is one as we need to check it for host boot order */
+	for (i = 0, nvram_index = -1; i < count; i++) {
+		devp  = &device[i];
+		nvram = devp->nvram;
+		if (!nvram)
+			continue;
+		if (nvram->type == SCSI_NCR_SYMBIOS_NVRAM) {
+			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",
+				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",
+				j,		h->device_id,	h->vendor_id,
+				h->device_fn,	h->io_port,
+				(h->flags & SYMBIOS_INIT_SCAN_AT_BOOT) ? "SCAN AT BOOT" : "");
+			}
+		}
+		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",
+				devp->chip.name, devp->slot.bus, 
+				(int) (devp->slot.device_fn & 0xf8) >> 3, 
+				(int) devp->slot.device_fn & 7);
+#endif
+		}
+	}
+
+	if (nvram_index >= 0 && nvram_index < count)
+		nvram = device[nvram_index].nvram;
+	else
+		nvram = 0;
+
+	if (!nvram)
+		goto out;
+
+	/* 
+	** check devices in the boot record against devices detected. 
+	** attach devices if we find a match. boot table records that 
+	** do not match any detected devices will be ignored. 
+	** devices that do not match any boot table will not be attached
+	** here but will attempt to be attached during the device table 
+	** rescan.
+	*/
+     	for (i = 0; i < 4; i++) {
+		Symbios_host *h = &nvram->data.Symbios.host[i];
+		for (j = 0 ; j < count ; j++) {
+			devp = &device[j];
+			if (h->device_fn == devp->slot.device_fn &&
+#if 0	/* bus number location in nvram ? */
+			    h->bus	 == devp->slot.bus	 &&
+#endif
+			    h->device_id == devp->chip.device_id)
+				break;
+		}
+		if (j < count && !devp->attached &&
+		    !ncr_attach (tpnt, attach_count, devp)) {
+			attach_count++;
+			devp->attached = 1;
+		}
+	}
+
+out:
+	return attach_count;
+}
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
+__initfunc(
 int ncr53c8xx_detect(Scsi_Host_Template *tpnt)
+)
 {
-     int i;
-     int count = 0;			/* Number of boards detected */
-     uchar pci_bus, pci_device_fn;
-     short pci_index;	/* Device index to PCI BIOS calls */
+	int i, j;
+	int chips;
+	int count = 0;
+	uchar bus, device_fn;
+	short index;
+	int attach_count = 0;
+	ncr_device device[8];
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+	ncr_nvram  nvram[4];
+	int k, nvrams;
+#endif
+	int hosts;
+
+#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;
@@ -7459,131 +8979,363 @@
 # endif
 #endif
 
-     if (pcibios_present()) {
-	  for (i = 0; i < NPCI_CHIP_IDS; ++i) 
-	       for (pci_index = 0;
-		    !pcibios_find_device(PCI_VENDOR_ID_NCR, 
-					 pci_chip_ids[i].pci_device_id, pci_index, &pci_bus, 
-					 &pci_device_fn);
-                    ++pci_index)
-		    if (!ncr53c8xx_pci_init(tpnt, count, 0, pci_chip_ids[i].chip, 
-			      pci_bus, pci_device_fn, /* no options */ 0))
-		    ++count;
-     }
+	/* 
+	** Detect all 53c8xx hosts and then attach them.
+	**
+	** If we are using NVRAM, once all hosts are detected, we need to check
+	** any NVRAM for boot order in case detect and boot order differ and
+	** attach them using the order in the NVRAM.
+	**
+	** If no NVRAM is found or data appears invalid attach boards in the 
+	** the order they are detected.
+	*/
 
-     return count;
-}
+	if (!pcibios_present())
+		return 0;
+
+	chips	= sizeof(ncr_chip_ids)	/ sizeof(ncr_chip_ids[0]);
+	hosts	= sizeof(device)	/ sizeof(device[0]);
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+	k = 0;
+	if (driver_setup.use_nvram & 0x1)
+		nvrams	= sizeof(nvram)	/ sizeof(nvram[0]);
+	else
+		nvrams	= 0;
+#endif
+
+	for (j = 0; j < chips ; ++j) {
+		i = driver_setup.reverse_probe ? chips-1 - j : j;
+		for (index = 0; ; index++) {
+			char *msg = "";
+			if ((pcibios_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i],
+						index, &bus, &device_fn)) ||
+			    (count == hosts))
+				break;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+			device[count].nvram = k < nvrams ? &nvram[k] : 0;
+#else
+			device[count].nvram = 0;
+#endif
+			if (ncr53c8xx_pci_init(tpnt, bus, device_fn, &device[count])) {
+				device[count].nvram = 0;
+				continue;
+			}
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+			if (device[count].nvram) {
+				++k;
+				nvram_index |= device[count].nvram->type;
+				switch (device[count].nvram->type) {
+				case SCSI_NCR_TEKRAM_NVRAM:
+					msg = "with Tekram NVRAM";
+					break;
+				case SCSI_NCR_SYMBIOS_NVRAM:
+					msg = "with Symbios NVRAM";
+					break;
+				default:
+					msg = "";
+					device[count].nvram = 0;
+					--k;
+				}
+			}
+#endif
+			printf(KERN_INFO "ncr53c8xx: 53c%s detected %s\n",
+				device[count].chip.name, msg);
+
+			device[count].attached = 0;
+			++count;
+		}
+	}
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+	attach_count = ncr_attach_using_nvram(tpnt, nvram_index, count, device);
+#endif
+	/* 
+	** rescan device list to make sure all boards attached.
+	** devices without boot records will not be attached yet
+	** so try to attach them here.
+	*/
+	for (i= 0; i < count; i++) {
+		if ((!device[i].attached) && (!ncr_attach (tpnt, attach_count, &device[i]))) {
+ 			attach_count++;
+			device[i].attached = 1;
+		}
+	}
 
+	return attach_count;
+}
 
 /*
-**   Read the PCI configuration of a found NCR board and
-**   try yo attach it.
+**   Read and check the PCI configuration for any detected NCR 
+**   boards and save data for attaching after all boards have 
+**   been detected.
 */
 
-static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, int unit, int board, int chip,
-		    uchar bus, uchar device_fn, int options)
-{
-     ushort vendor_id, device_id, command;
-#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
-     uint base, io_port; 
+__initfunc(
+static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
+			      uchar bus, uchar device_fn, ncr_device *device)
+)
+{
+	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, io_port; 
+#else
+	ulong base, io_port; 
+#endif
+	int i, error;
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+	ncr_nvram *nvram = device->nvram;
+#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
+	 */
+	if (
+		(error=pcibios_read_config_word(bus, device_fn, PCI_VENDOR_ID, &vendor_id))	||
+		(error=pcibios_read_config_word(bus, device_fn, PCI_DEVICE_ID, &device_id))	||
+		(error=pcibios_read_config_word( bus, device_fn, PCI_COMMAND, &command))	||
+		(error=pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0,&io_port))	|| 
+		(error=pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, &base))	||
+		(error=pcibios_read_config_byte(bus, device_fn, PCI_CLASS_REVISION,&revision))	||
+		(error=pcibios_read_config_byte(bus, device_fn, PCI_INTERRUPT_LINE, &irq))	||
+		(error=pcibios_read_config_byte(bus, device_fn, PCI_CACHE_LINE_SIZE, &cache_line_size)) ||
+		(error=pcibios_read_config_byte(bus, device_fn, PCI_LATENCY_TIMER, &latency_timer))
+	)
+		goto err_pcibios;
+
+	/*
+	 *	Check if the chip is supported
+	 */
+	chip = 0;
+	for (i = 0; i < sizeof(ncr_chip_table)/sizeof(ncr_chip_table[0]); i++) {
+		if (device_id != ncr_chip_table[i].device_id)
+			continue;
+		if (revision > ncr_chip_table[i].revision_id)
+			continue;
+		chip = &device->chip;
+		memcpy(chip, &ncr_chip_table[i], sizeof(*chip));
+		chip->revision_id = revision;
+		break;
+	}
+	if (!chip) {
+		printk("ncr53c8xx: not initializing, device not supported\n");
+		return -1;
+	}
+
+	/*
+	 * Check availability of IO space, memory space and master capability.
+	 */
+	if (command & PCI_COMMAND_IO) { 
+		if ((io_port & 3) != 1) {
+			printk("ncr53c8xx: disabling I/O mapping since base address 0 (0x%x)\n"
+				"           bits 0..1 indicate a non-IO mapping\n", (int) io_port);
+			io_port = 0;
+		}
+		else
+			io_port &= PCI_BASE_ADDRESS_IO_MASK;
+	}
+	else
+		io_port = 0;
+
+	if (command & PCI_COMMAND_MEMORY) {
+		if ((base & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) {
+			printk("ncr53c8xx: disabling memory mapping since base address 1\n"
+				"            contains a non-memory mapping\n");
+			base = 0;
+		}
+		else 
+			base &= PCI_BASE_ADDRESS_MEM_MASK;
+	}
+	else
+		base = 0;
+	
+	if (!io_port && !base) {
+		printk("ncr53c8xx: not initializing, both I/O and memory mappings disabled\n");
+		return -1;
+	}
+
+	if (io_port && check_region (io_port, 128)) {
+		printk("ncr53c8xx: IO region 0x%x to 0x%x is in use\n",
+			(int) io_port, (int) (io_port + 127));
+		return -1;
+	}
+	
+	if (!(command & PCI_COMMAND_MASTER)) {
+		printk("ncr53c8xx: not initializing, BUS MASTERING was disabled\n");
+		return -1;
+	}
+
+	/*
+	 * Fix some features according to driver setup.
+	 */
+	if (!driver_setup.special_features)
+		chip->features &= ~FE_SPECIAL_SET;
+	if (driver_setup.ultra_scsi < 2 && (chip->features & FE_ULTRA2)) {
+		chip->features |=  FE_ULTRA;
+		chip->features &= ~FE_ULTRA2;
+	}
+	if (driver_setup.ultra_scsi < 1)
+		chip->features &= ~FE_ULTRA;
+	if (!driver_setup.max_wide)
+		chip->features &= ~FE_WIDE;
+
+
+#ifdef	SCSI_NCR_PCI_FIX_UP_SUPPORT
+
+	/*
+	 * Try to fix up PCI config according to wished features.
+	 */
+#if defined(__i386) && !defined(MODULE)
+	if ((driver_setup.pci_fix_up & 1) &&
+	    (chip->features & FE_CLSE) && cache_line_size == 0) {
+		extern char x86;
+		switch(x86) {
+		case 4:	cache_line_size = 4; break;
+		case 5:	cache_line_size = 8; break;
+		}
+		if (cache_line_size)
+			error = pcibios_write_config_byte(bus, device_fn, PCI_CACHE_LINE_SIZE, cache_line_size);
+		if (error)
+			goto err_pcibios;
+		if (initverbose)
+			printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fix-up).\n", cache_line_size);
+	}
+
+	if ((driver_setup.pci_fix_up & 2) && cache_line_size &&
+	    (chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
+		command |= PCI_COMMAND_INVALIDATE;
+		error=pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
+		if (error)
+			goto err_pcibios;
+		if (initverbose)
+			printk("ncr53c8xx: setting PCI_COMMAND_INVALIDATE bit (fix-up).\n");
+	}
+#endif
+	/*
+	 * Fix up for old chips that support READ LINE but not CACHE LINE SIZE.
+	 * - If CACHE LINE SIZE is unknown, set burst max to 32 bytes = 8 dwords
+	 *   and donnot enable READ LINE.
+	 * - Otherwise set it to the CACHE LINE SIZE (power of 2 assumed). 
+	 */
+
+	if (!(chip->features & FE_CLSE)) {
+		int burst_max = chip->burst_max;
+		if (cache_line_size == 0) {
+			chip->features	&= ~FE_ERL;
+			if (burst_max > 3)
+				burst_max = 3;
+		}
+		else {
+			while (cache_line_size < (1 << burst_max))
+				--burst_max;
+		}
+		chip->burst_max = burst_max;
+	}
+
+	/*
+	 * Tune PCI LATENCY TIMER according to burst max length transfer.
+	 * (latency timer >= burst length + 6, we add 10 to be quite sure)
+	 * If current value is zero, the device has probably been configured 
+	 * for no bursting due to some broken hardware.
+	 */
+
+	if (latency_timer == 0 && chip->burst_max)
+		printk("ncr53c8xx: PCI_LATENCY_TIMER=0, bursting should'nt be allowed.\n");
+
+	if ((driver_setup.pci_fix_up & 4) && chip->burst_max) {
+		uchar lt = (1 << chip->burst_max) + 6 + 10;
+		if (latency_timer < lt) {
+			latency_timer = lt;
+			if (initverbose)
+				printk("ncr53c8xx: setting PCI_LATENCY_TIMER to %d bus clocks (fix-up).\n", latency_timer);
+			error = pcibios_write_config_byte(bus, device_fn,
+					PCI_LATENCY_TIMER, latency_timer);
+			if (error)
+				goto err_pcibios;
+		}
+	}
+
+	/*
+	 * Fix up for recent chips that support CACHE LINE SIZE.
+	 * If PCI config space is not OK, remove features that shall not be 
+	 * used by the chip. No need to trigger possible chip bugs.
+	 */
+
+	if ((chip->features & FE_CLSE) && cache_line_size == 0) {
+		chip->features &= ~FE_CACHE_SET;
+		printk("ncr53c8xx: PCI_CACHE_LINE_SIZE not set, features based on CACHE LINE SIZE not used.\n");
+	}
+
+	if ((chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
+		chip->features &= ~FE_WRIE;
+		printk("ncr53c8xx: PCI_COMMAND_INVALIDATE not set, WRITE AND INVALIDATE not used\n");
+	}
+
+#endif	/* SCSI_NCR_PCI_FIX_UP_SUPPORT */
+
+ 	/* initialise ncr_device structure with items required by ncr_attach */
+	device->slot.bus	= bus;
+	device->slot.device_fn	= device_fn;
+	device->slot.base	= base;
+	device->slot.io_port	= io_port;
+	device->slot.irq	= irq;
+	device->attached	= 0;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+	if (!nvram)
+		goto out;
+
+	/*
+	** Get access to chip IO registers
+	*/
+#ifdef NCR_IOMAPPED
+	request_region(io_port, 128, "ncr53c8xx");
+	device->slot.port = io_port;
+#else
+	device->slot.reg = (struct ncr_reg *) remap_pci_mem((ulong) base, 128);
+	if (!device->slot.reg)
+		goto out;
+#endif
+
+	/*
+	** Try to read SYMBIOS nvram.
+	** Data can be used to order booting of boards.
+	**
+	** Data is saved in ncr_device structure if NVRAM found. This
+	** is then used to find drive boot order for ncr_attach().
+	**
+	** NVRAM data is passed to Scsi_Host_Template later during ncr_attach()
+	** for any device set up.
+	**
+	** Try to read TEKRAM nvram if Symbios nvram not found.
+	*/
+
+	if	(!ncr_get_Symbios_nvram(&device->slot, &nvram->data.Symbios))
+		nvram->type = SCSI_NCR_SYMBIOS_NVRAM;
+	else if	(!ncr_get_Tekram_nvram(&device->slot, &nvram->data.Tekram))
+		nvram->type = SCSI_NCR_TEKRAM_NVRAM;
+	else
+		nvram->type = 0;
+out:
+	/*
+	** Release access to chip IO registers
+	*/
+#ifdef NCR_IOMAPPED
+	release_region(device->slot.port, 128);
 #else
-     ulong base, io_port; 
+	unmap_pci_mem((vm_offset_t) device->slot.reg, (u_long) 128);
 #endif
-     uchar irq, revision;
-     int error, expected_chip;
-     int expected_id = -1, max_revision = -1, min_revision = -1;
-     int i;
-
-     printk("ncr53c8xx : at PCI bus %d, device %d, function %d\n",
-	    bus, (int) (device_fn & 0xf8) >> 3, (int) device_fn & 7);
-
-     if (!pcibios_present()) {
-	  printk("ncr53c8xx : not initializing due to lack of PCI BIOS,\n");
-	  return -1;
-     }
-
-     if ((error = pcibios_read_config_word( bus, device_fn, PCI_VENDOR_ID,      &vendor_id)) ||
-	 (error = pcibios_read_config_word( bus, device_fn, PCI_DEVICE_ID,      &device_id)) ||
-	 (error = pcibios_read_config_word( bus, device_fn, PCI_COMMAND,        &command))   ||
-	 (error = pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0, &io_port))   || 
-	 (error = pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, &base))      ||
-	 (error = pcibios_read_config_byte (bus, device_fn, PCI_CLASS_REVISION, &revision))  ||
-	 (error = pcibios_read_config_byte (bus, device_fn, PCI_INTERRUPT_LINE, &irq))) {
-	  printk("ncr53c8xx : error %s not initializing due to error reading configuration space\n",
-		 pcibios_strerror(error));
-	  return -1;
-     }
-
-     if (vendor_id != PCI_VENDOR_ID_NCR) {
-	  printk("ncr53c8xx : not initializing, 0x%04x is not NCR vendor ID\n", (int) vendor_id);
-	  return -1;
-     }
-
-
-     if (command & PCI_COMMAND_IO) { 
-	  if ((io_port & 3) != 1) {
-	       printk("ncr53c8xx : disabling I/O mapping since base address 0 (0x%x)\n"
-		      "            bits 0..1 indicate a non-IO mapping\n", (int) io_port);
-	       io_port = 0;
-	  }
-	  else
-	       io_port &= PCI_BASE_ADDRESS_IO_MASK;
-     }
-     else
-	  io_port = 0;
 
-     if (command & PCI_COMMAND_MEMORY) {
-	  if ((base & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) {
-	       printk("ncr53c8xx : disabling memory mapping since base address 1\n"
-		      "            contains a non-memory mapping\n");
-	       base = 0;
-	  }
-	  else 
-	       base &= PCI_BASE_ADDRESS_MEM_MASK;
-     }
-     else
-	  base = 0;
-	
-     if (!io_port && !base) {
-	  printk("ncr53c8xx : not initializing, both I/O and memory mappings disabled\n");
-	  return -1;
-     }
-	
-     if (!(command & PCI_COMMAND_MASTER)) {
-	  printk ("ncr53c8xx : not initializing, BUS MASTERING was disabled\n");
-	  return -1;
-     }
-
-     for (i = 0; i < NPCI_CHIP_IDS; ++i) {
-	  if (device_id == pci_chip_ids[i].pci_device_id) {
-	       max_revision  = pci_chip_ids[i].max_revision;
-	       min_revision  = pci_chip_ids[i].min_revision;
-	       expected_chip = pci_chip_ids[i].chip;
-	  }
-	  if (chip == pci_chip_ids[i].chip)
-	       expected_id = pci_chip_ids[i].pci_device_id;
-     }
-
-     if (chip && device_id != expected_id) 
-	  printk("ncr53c8xx : warning : device id of 0x%04x doesn't\n"
-		 "            match expected 0x%04x\n",
-		  (unsigned int) device_id, (unsigned int) expected_id );
-    
-     if (max_revision != -1 && revision > max_revision) 
-	  printk("ncr53c8xx : warning : revision %d is greater than expected.\n",
-		 (int) revision);
-     else if (min_revision != -1 && revision < min_revision)
-	  printk("ncr53c8xx : warning : revision %d is lower than expected.\n",
-		 (int) revision);
-
-     if (io_port && check_region (io_port, 128)) {
-	  printk("ncr53c8xx : IO region 0x%x to 0x%x is in use\n",
-		 (int) io_port, (int) (io_port + 127));
-	  return -1;
-     }
+#endif	/* SCSI_NCR_NVRAM_SUPPORT */
+	return 0;     
 
-     return ncr_attach (tpnt, unit, device_id, revision, chip, base, io_port, 
-		       (int) irq, bus, (uchar) device_fn);
+err_pcibios:
+	printk("ncr53c8xx: error %s reading configuration space\n",
+		pcibios_strerror(error));
+	return -1;
 }
 
 #if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0)
@@ -7596,13 +9348,18 @@
 
 	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 = 1;
+				device->queue_depth = 2;
 			}
-#ifdef DEBUG
+#else
+			device->queue_depth = 1;
+#endif
+
+#ifdef DEBUG_NCR53C8XX
 printk("ncr53c8xx_select_queue_depth: id=%d, lun=%d, queue_depth=%d\n",
 	device->id, device->lun, device->queue_depth);
 #endif
@@ -7618,19 +9375,19 @@
 int ncr53c8xx_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
 {
      int sts;
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
 printk("ncr53c8xx_queue_command\n");
 #endif
 
      if ((sts = ncr_queue_command(cmd, done)) != DID_OK) {
 	  cmd->result = ScsiResult(sts, 0);
 	  done(cmd);
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
 printk("ncr53c8xx : command not queued - result=%d\n", sts);
 #endif
           return sts;
      }
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
 printk("ncr53c8xx : command successfully queued\n");
 #endif
      return sts;
@@ -7648,8 +9405,13 @@
 {
      struct Scsi_Host *host;
      struct host_data *host_data;
+#if 0
+     u_long flags;
+
+     save_flags(flags); cli();
+#endif
 
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
 printk("ncr53c8xx : interrupt received\n");
 #endif
 
@@ -7658,12 +9420,20 @@
 	       host_data = (struct host_data *) host->hostdata;
 #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
 #   ifdef SCSI_NCR_SHARE_IRQ
-               if (dev_id == &host_data->ncb_data)
+               if (dev_id == host_data->ncb) {
+#else
+               if (1) {
 #   endif
 #endif
-	       ncr_intr(&host_data->ncb_data);
+                    if (DEBUG_FLAGS & DEBUG_TINY) printf ("[");
+	            ncr_exception(host_data->ncb);
+                    if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n");
+               }
 	  }
      }
+#if 0
+     restore_flags(flags);
+#endif
 }
 
 /*
@@ -7679,40 +9449,106 @@
 **   Linux entry point of reset() function
 */
 
-#if	LINUX_VERSION_CODE >= LinuxVersionCode(1,3,98)
+#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS
+
 int ncr53c8xx_reset(Scsi_Cmnd *cmd, unsigned int reset_flags)
+{
+	int sts;
+	unsigned long flags;
+
+	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);
+
+	save_flags(flags); cli();
+
+	/*
+	 * We have to just ignore reset requests in some situations.
+	 */
+#if defined SCSI_RESET_NOT_RUNNING
+	if (cmd->serial_number != cmd->serial_number_at_timeout) {
+		sts = SCSI_RESET_NOT_RUNNING;
+		goto out;
+	}
+#endif
+	/*
+	 * If the mid-level driver told us reset is synchronous, it seems 
+	 * that we must call the done() callback for the involved command, 
+	 * even if this command was not queued to the low-level driver, 
+	 * before returning SCSI_RESET_SUCCESS.
+	 */
+
+	sts = ncr_reset_bus(cmd,
+	(reset_flags & (SCSI_RESET_SYNCHRONOUS | SCSI_RESET_ASYNCHRONOUS)) == SCSI_RESET_SYNCHRONOUS);
+	/*
+	 * Since we always reset the controller, when we return success, 
+	 * we add this information to the return code.
+	 */
+#if defined SCSI_RESET_HOST_RESET
+	if (sts == SCSI_RESET_SUCCESS)
+		sts |= SCSI_RESET_HOST_RESET;
+#endif
+
+out:
+	restore_flags(flags);
+	return sts;
+}
 #else
 int ncr53c8xx_reset(Scsi_Cmnd *cmd)
-#endif
 {
-#ifdef DEBUG
-printk("ncr53c8xx_reset : reset call\n");
-#endif
-	return ncr_reset_bus(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)
+{
+	int sts;
+	unsigned long flags;
+
+	printk("ncr53c8xx_abort: pid=%lu serial_number=%ld serial_number_at_timeout=%ld\n",
+		cmd->pid, cmd->serial_number, cmd->serial_number_at_timeout);
+
+	save_flags(flags); cli();
+
+	/*
+	 * We have to just ignore abort requests in some situations.
+	 */
+	if (cmd->serial_number != cmd->serial_number_at_timeout) {
+		sts = SCSI_ABORT_NOT_RUNNING;
+		goto out;
+	}
+
+	sts = ncr_abort_command(cmd);
+out:
+	restore_flags(flags);
+	return sts;
+}
+#else
 int ncr53c8xx_abort(Scsi_Cmnd *cmd)
 {
-printk("ncr53c8xx_abort : abort call\n");
+	printk("ncr53c8xx_abort: command pid %lu\n", cmd->pid);
 	return ncr_abort_command(cmd);
 }
+#endif
 
 #ifdef MODULE
 int ncr53c8xx_release(struct Scsi_Host *host)
 {
      struct host_data *host_data;
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
 printk("ncr53c8xx : release\n");
 #endif
 
      for (host = first_host; host; host = host->next) {
 	  if (host->hostt == the_template) {
 	       host_data = (struct host_data *) host->hostdata;
-	       ncr_detach(&host_data->ncb_data, host->irq);
+	       ncr_detach(host_data->ncb, host->irq);
 	  }
      }
 
@@ -7752,17 +9588,19 @@
 	}
 }
 
-static Scsi_Cmnd *remove_from_waiting_list(ncb_p np, Scsi_Cmnd *cmd)
+static Scsi_Cmnd *retrieve_from_waiting_list(int to_remove, ncb_p np, Scsi_Cmnd *cmd)
 {
 	Scsi_Cmnd *wcmd;
 
 	if (!(wcmd = np->waiting_list)) return 0;
 	while (wcmd->next_wcmd) {
 		if (cmd == (Scsi_Cmnd *) wcmd->next_wcmd) {
-			wcmd->next_wcmd = cmd->next_wcmd;
-			cmd->next_wcmd = 0;
+			if (to_remove) {
+				wcmd->next_wcmd = cmd->next_wcmd;
+				cmd->next_wcmd = 0;
+			}
 #ifdef DEBUG_WAITING_LIST
-	printf("%s: cmd %lx removed from waiting list\n", ncr_name(np), (u_long) cmd);
+	printf("%s: cmd %lx retrieved from waiting list\n", ncr_name(np), (u_long) cmd);
 #endif
 			return cmd;
 		}
@@ -7802,13 +9640,7 @@
 #undef next_wcmd
 
 /*
-**	In order to patch the SCSI script for SAVE/RESTORE DATA POINTER,
-**	we need the direction of transfer.
-**	Linux middle-level scsi driver does not provide this information.
-**	So we have to guess it.
-**	My documentation about SCSI-II standard is old. Probably some opcode
-**	are missing.
-**	If I do'nt know the command code, I assume input transfer direction.
+**	Returns data transfer direction for common op-codes.
 */
 
 static int guess_xfer_direction(int opcode)
@@ -7816,111 +9648,31 @@
 	int d;
 
 	switch(opcode) {
-	case 0x00:  /*	TEST UNIT READY			00 */
-	case 0x08:  /*	READ(6)				08 */
 	case 0x12:  /*	INQUIRY				12 */
 	case 0x4D:  /*	LOG SENSE			4D */
 	case 0x5A:  /*	MODE SENSE(10)			5A */
 	case 0x1A:  /*	MODE SENSE(6)			1A */
-	case 0x28:  /*	READ(10)			28 */
-	case 0xA8:  /*	READ(12)			A8 */
 	case 0x3C:  /*	READ BUFFER			3C */
 	case 0x1C:  /*	RECEIVE DIAGNOSTIC RESULTS	1C */
-	case 0xB7:  /*	READ DEFECT DATA(12)		B7 */
-	case 0xB8:  /*	READ ELEMENT STATUS		B8 */
-	            /*	GET WINDOW			25 */
-	case 0x25:  /*	READ CAPACITY			25 */
-	case 0x29:  /*	READ GENERATION			29 */
-	case 0x3E:  /*	READ LONG			3E */
-	            /*	GET DATA BUFFER STATUS		34 */
-	            /*	PRE-FETCH			34 */
-	case 0x34:  /*	READ POSITION			34 */
 	case 0x03:  /*	REQUEST SENSE			03 */
-	case 0x05:  /*	READ BLOCK LIMITS		05 */
-	case 0x0F:  /*	READ REVERSE			0F */
-	case 0x14:  /*	RECOVER BUFFERED DATA		14 */
-	case 0x2D:  /*	READ UPDATED BLOCK		2D */
-	case 0x37:  /*	READ DEFECT DATA(10)		37 */
-	case 0x42:  /*	READ SUB-CHANNEL		42 */
-	case 0x43:  /*	READ TOC			43 */
-	case 0x44:  /*	READ HEADER			44 */
-	case 0xC7:  /*  ???                  ???        C7 */
 		d = XferIn;
 		break;
 	case 0x39:  /*	COMPARE				39 */
 	case 0x3A:  /*	COPY AND VERIFY			3A */
-	            /*	PRINT				0A */
-	            /*	SEND MESSAGE(6)			0A */
-	case 0x0A:  /*	WRITE(6)			0A */
 	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 */
-	            /*	SEND MESSAGE(12)		AA */
-	case 0xAA:  /*	WRITE(12)			AA */
-	case 0xB6:  /*	SEND VOLUME TAG			B6 */
-	case 0x3F:  /*	WRITE LONG			3F */
-	case 0x04:  /*	FORMAT UNIT			04 */
-		    /*	INITIALIZE ELEMENT STATUS	07 */
-	case 0x07:  /*	REASSIGN BLOCKS			07 */
 	case 0x15:  /*	MODE SELECT(6)			15 */
-	case 0x24:  /*	SET WINDOW			24 */
-	case 0x2A:  /*	WRITE(10)			2A */
-	case 0x2E:  /*	WRITE AND VERIFY(10)		2E */
-	case 0xAE:  /*	WRITE AND VERIFY(12)		AE */
-	case 0xB0:  /*	SEARCH DATA HIGH(12)		B0 */
-	case 0xB1:  /*	SEARCH DATA EQUAL(12)		B1 */
-	case 0xB2:  /*	SEARCH DATA LOW(12)		B2 */
-	            /*	OBJECT POSITION			31 */
-	case 0x30:  /*	SEARCH DATA HIGH(10)		30 */
-	case 0x31:  /*	SEARCH DATA EQUAL(10)		31 */
-	case 0x32:  /*	SEARCH DATA LOW(10)		32 */
-	case 0x38:  /*	MEDIUM SCAN			38 */
-	case 0x3D:  /*	UPDATE BLOCK			3D */
-	case 0x41:  /*	WRITE SAME			41 */
-	            /*	LOAD UNLOAD			1B */
-	            /*	SCAN				1B */
-	case 0x1B:  /*	START STOP UNIT			1B */
 		d = XferOut;
 		break;
-	case 0x01:  /*	REZERO UNIT			01 */
-	            /*	SEEK(6)				0B */
-	case 0x0B:  /*	SLEW AND PRINT			0B */
-	            /*	SYNCHRONIZE BUFFER		10 */
-	case 0x10:  /*	WRITE FILEMARKS			10 */
-	case 0x11:  /*	SPACE				11 */
-	case 0x13:  /*	VERIFY				13 */
-	case 0x16:  /*	RESERVE UNIT			16 */
-	case 0x17:  /*	RELEASE UNIT			17 */
-	case 0x19:  /*	ERASE				19 */
-	            /*	LOCATE				2B */
-	            /*	POSITION TO ELEMENT		2B */
-	case 0x2B:  /*	SEEK(10)			2B */
-	case 0x1E:  /*	PREVENT ALLOW MEDIUM REMOVAL	1E */
-	case 0x2C:  /*	ERASE(10)			2C */
-	case 0xAC:  /*	ERASE(12)			AC */
-	case 0x2F:  /*	VERIFY(10)			2F */
-	case 0xAF:  /*	VERIFY(12)			AF */
-	case 0x33:  /*	SET LIMITS(10)			33 */
-	case 0xB3:  /*	SET LIMITS(12)			B3 */
-	case 0x35:  /*	SYNCHRONIZE CACHE		35 */
-	case 0x36:  /*	LOCK UNLOCK CACHE		36 */
-	case 0x45:  /*	PLAY AUDIO(10)			45 */
-	case 0x47:  /*	PLAY AUDIO MSF			47 */
-	case 0x48:  /*	PLAY AUDIO TRACK/INDEX		48 */
-	case 0x49:  /*	PLAY TRACK RELATIVE(10)		49 */
-	case 0xA9:  /*	PLAY TRACK RELATIVE(12)		A9 */
-	case 0x4B:  /*	PAUSE/RESUME			4B */
-	            /*	MOVE MEDIUM			A5 */
-	case 0xA5:  /*	PLAY AUDIO(12)			A5 */
-	case 0xA6:  /*	EXCHANGE MEDIUM			A6 */
-	case 0xB5:  /*	REQUEST VOLUME ELEMENT ADDRESS	B5 */
+	case 0x00:  /*	TEST UNIT READY			00 */
 		d = XferNone;
 		break;
 	default:
-		d = XferIn;
+		d = XferBoth;
 		break;
 	}
 
@@ -7940,6 +9692,8 @@
 **=========================================================================
 */
 
+#ifdef SCSI_NCR_USER_COMMAND_SUPPORT
+
 #define is_digit(c)	((c) >= '0' && (c) <= '9')
 #define digit_to_bin(c)	((c) - '0')
 #define is_space(c)	((c) == ' ' || (c) == '\t')
@@ -8021,6 +9775,10 @@
 		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;
 
@@ -8038,13 +9796,16 @@
 	case UC_SETWIDE:
 	case UC_SETFLAG:
 		SKIP_SPACES(1);
-		GET_INT_ARG(target);
+		if ((arg_len = is_keyword(ptr, len, "all")) != 0) {
+			ptr += arg_len; len -= arg_len;
+			uc->target = ~0;
+		} else {
+			GET_INT_ARG(target);
+			uc->target = (1<<target);
 #ifdef DEBUG_PROC_INFO
 printf("ncr_user_command: target=%ld\n", target);
 #endif
-		if (target > MAX_TARGET)
-			return -EINVAL;
-		uc->target = (1<<target);
+		}
 		break;
 	}
 
@@ -8074,6 +9835,8 @@
 			SKIP_SPACES(1);
 			if	((arg_len = is_keyword(ptr, len, "alloc")))
 				uc->data |= DEBUG_ALLOC;
+			else if	((arg_len = is_keyword(ptr, len, "phase")))
+				uc->data |= DEBUG_PHASE;
 			else if	((arg_len = is_keyword(ptr, len, "poll")))
 				uc->data |= DEBUG_POLL;
 			else if	((arg_len = is_keyword(ptr, len, "queue")))
@@ -8086,6 +9849,8 @@
 				uc->data |= DEBUG_SCRIPT;
 			else if	((arg_len = is_keyword(ptr, len, "tiny")))
 				uc->data |= DEBUG_TINY;
+			else if	((arg_len = is_keyword(ptr, len, "timing")))
+				uc->data |= DEBUG_TIMING;
 			else if	((arg_len = is_keyword(ptr, len, "nego")))
 				uc->data |= DEBUG_NEGO;
 			else if	((arg_len = is_keyword(ptr, len, "tags")))
@@ -8107,24 +9872,37 @@
 			SKIP_SPACES(1);
 			if	((arg_len = is_keyword(ptr, len, "trace")))
 				uc->data |= UF_TRACE;
+			else if	((arg_len = is_keyword(ptr, len, "no_disc")))
+				uc->data |= UF_NODISC;
 			else
 				return -EINVAL;
 			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;
 	}
 
-	/*
-	** Not allow to disable tagged queue
-	*/ 
-	if (uc->cmd == UC_SETTAGS && uc->data < 1)
-		return -EINVAL;
-
 	if (len)
 		return -EINVAL;
-#ifdef SCSI_NCR_USER_COMMAND
 	else {
 		long flags;
 
@@ -8132,10 +9910,13 @@
 		ncr_usercmd (np);
 		restore_flags(flags);
 	}
-#endif
 	return length;
 }
 
+#endif	/* SCSI_NCR_USER_COMMAND_SUPPORT */
+
+#ifdef SCSI_NCR_USER_INFO_SUPPORT
+
 struct info_str
 {
 	char *buffer;
@@ -8182,6 +9963,8 @@
 **	Copy formatted profile information into the input buffer.
 */
 
+#define to_ms(t) ((t) * 1000 / HZ)
+
 static int ncr_host_info(ncb_p np, char *ptr, off_t offset, int len)
 {
 	struct info_str info;
@@ -8192,7 +9975,7 @@
 	info.pos	= 0;
 
 	copy_info(&info, "General information:\n");
-	copy_info(&info, "  Chip NCR53C%03d, ",	np->chip);
+	copy_info(&info, "  Chip NCR53C%s, ",	np->chip_name);
 	copy_info(&info, "device id 0x%x, ",	np->device_id);
 	copy_info(&info, "revision id 0x%x\n",	np->revision_id);
 
@@ -8200,12 +9983,19 @@
 	copy_info(&info, "IRQ number %d\n", (int) np->irq);
 
 #ifndef NCR_IOMAPPED
-	if (np->use_mmio)
+	if (np->reg)
 		copy_info(&info, "  Using memory mapped IO at virtual address 0x%lx\n",
-		                  (u_long) np->reg_remapped);
+		                  (u_long) np->reg);
 #endif
+	copy_info(&info, "  Synchronous period factor %d, ", (int) np->minsync);
+	copy_info(&info, "max commands per lun %d\n", SCSI_NCR_MAX_TAGS);
+
+	if (driver_setup.debug || driver_setup.verbose > 1) {
+		copy_info(&info, "  Debug flags 0x%x, ", driver_setup.debug);
+		copy_info(&info, "verbosity level %d\n", driver_setup.verbose);
+	}
 
-#ifdef SCSI_NCR_PROFILE
+#ifdef SCSI_NCR_PROFILE_SUPPORT
 	copy_info(&info, "Profiling information:\n");
 	copy_info(&info, "  %-12s = %lu\n", "num_trans",np->profile.num_trans);
 	copy_info(&info, "  %-12s = %lu\n", "num_kbytes",np->profile.num_kbytes);
@@ -8213,15 +10003,17 @@
 	copy_info(&info, "  %-12s = %lu\n", "num_break",np->profile.num_break);
 	copy_info(&info, "  %-12s = %lu\n", "num_int",	np->profile.num_int);
 	copy_info(&info, "  %-12s = %lu\n", "num_fly",	np->profile.num_fly);
-	copy_info(&info, "  %-12s = %lu\n", "ms_setup",	np->profile.ms_setup);
-	copy_info(&info, "  %-12s = %lu\n", "ms_data",	np->profile.ms_data);
-	copy_info(&info, "  %-12s = %lu\n", "ms_disc",	np->profile.ms_disc);
-	copy_info(&info, "  %-12s = %lu\n", "ms_post",	np->profile.ms_post);
+	copy_info(&info, "  %-12s = %lu\n", "ms_setup",	to_ms(np->profile.ms_setup));
+	copy_info(&info, "  %-12s = %lu\n", "ms_data",	to_ms(np->profile.ms_data));
+	copy_info(&info, "  %-12s = %lu\n", "ms_disc",	to_ms(np->profile.ms_disc));
+	copy_info(&info, "  %-12s = %lu\n", "ms_post",	to_ms(np->profile.ms_post));
 #endif
 	
 	return info.pos > info.offset? info.pos - info.offset : 0;
 }
 
+#endif /* SCSI_NCR_USER_INFO_SUPPORT */
+
 /*
 **	Entry point of the scsi proc fs of the driver.
 **	- func = 0 means read  (returns profile data)
@@ -8243,7 +10035,7 @@
 	for (host = first_host; host; host = host->next) {
 		if (host->hostt == the_template && host->host_no == hostno) {
 			host_data = (struct host_data *) host->hostdata;
-			ncb = &host_data->ncb_data;
+			ncb = host_data->ncb;
 			break;
 		}
 	}
@@ -8252,25 +10044,485 @@
 		return -EINVAL;
 
 	if (func) {
+#ifdef	SCSI_NCR_USER_COMMAND_SUPPORT
 		retv = ncr_user_command(ncb, buffer, length);
-#ifdef DEBUG_PROC_INFO
-printf("ncr_user_command: retv=%d\n", retv);
+#else
+		retv = -EINVAL;
 #endif
 	}
 	else {
 		if (start)
 			*start = buffer;
+#ifdef SCSI_NCR_USER_INFO_SUPPORT
 		retv = ncr_host_info(ncb, buffer, offset, length);
+#else
+		retv = -EINVAL;
+#endif
 	}
 
 	return retv;
 }
 
+
 /*=========================================================================
 **	End of proc file system stuff
 **=========================================================================
 */
 #endif
+
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+
+/* ---------------------------------------------------------------------
+**
+**	Try reading Symbios format nvram
+**
+** ---------------------------------------------------------------------
+**
+** GPOI0 - data in/data out
+** GPIO1 - clock
+**
+**	return 0 if NVRAM data OK, 1 if NVRAM data not OK
+** ---------------------------------------------------------------------
+*/
+
+#define SET_BIT 0
+#define CLR_BIT 1
+#define SET_CLK 2
+#define CLR_CLK 3
+
+static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl);
+static void nvram_start(ncr_slot *np, u_char *gpreg);
+static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl);
+static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl);
+static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl);
+static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl);
+static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg);
+static void nvram_stop(ncr_slot *np, u_char *gpreg);
+static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode);
+
+__initfunc(
+static int ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram)
+)
+{
+	static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0};
+	u_char	gpcntl, gpreg;
+	u_char	old_gpcntl, old_gpreg;
+	u_short	csum;
+	u_char	ack_data;
+	int	retv = 1;
+
+	/* save current state of GPCNTL and GPREG */
+	old_gpreg	= INB (nc_gpreg);
+	old_gpcntl	= INB (nc_gpcntl);
+	gpcntl		= old_gpcntl & 0xfc;
+
+	/* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */
+	OUTB (nc_gpreg,  old_gpreg);
+	OUTB (nc_gpcntl, gpcntl);
+
+	/* this is to set NVRAM into a known state with GPIO0/1 both low */
+	gpreg = old_gpreg;
+	nvram_setBit(np, 0, &gpreg, CLR_CLK);
+	nvram_setBit(np, 0, &gpreg, CLR_BIT);
+		
+	/* now set NVRAM inactive with GPIO0/1 both high */
+	nvram_stop(np, &gpreg);
+	
+	/* activate NVRAM */
+	nvram_start(np, &gpreg);
+
+	/* write device code and random address MSB */
+	nvram_write_byte(np, &ack_data,
+		0xa0 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl);
+	if (ack_data & 0x01)
+		goto out;
+
+	/* write random address LSB */
+	nvram_write_byte(np, &ack_data,
+		(SYMBIOS_NVRAM_ADDRESS & 0x7f) << 1, &gpreg, &gpcntl);
+	if (ack_data & 0x01)
+		goto out;
+
+	/* regenerate START state to set up for reading */
+	nvram_start(np, &gpreg);
+	
+	/* rewrite device code and address MSB with read bit set (lsb = 0x01) */
+	nvram_write_byte(np, &ack_data,
+		0xa1 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl);
+	if (ack_data & 0x01)
+		goto out;
+
+	/* now set up GPIO0 for inputting data */
+	gpcntl |= 0x01;
+	OUTB (nc_gpcntl, gpcntl);
+		
+	/* input all active data - only part of total NVRAM */
+	csum = nvram_read_data(np,
+			(u_char *) nvram, sizeof(*nvram), &gpreg, &gpcntl);
+
+	/* finally put NVRAM back in inactive mode */
+	gpcntl &= 0xfe;
+	OUTB (nc_gpcntl, gpcntl);
+	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",
+	nvram->start_marker,
+	nvram->trailer[0], nvram->trailer[1], nvram->trailer[2],
+	nvram->trailer[3], nvram->trailer[4], nvram->trailer[5],
+	nvram->byte_count, sizeof(*nvram) - 12,
+	nvram->checksum, csum);
+#endif
+
+	/* check valid NVRAM signature, verify byte count and checksum */
+	if (nvram->start_marker == 0 &&
+	    !memcmp(nvram->trailer, Symbios_trailer, 6) &&
+	    nvram->byte_count == sizeof(*nvram) - 12 &&
+	    csum == nvram->checksum)
+		retv = 0;
+out:
+	/* return GPIO0/1 to original states after having accessed NVRAM */
+	OUTB (nc_gpcntl, old_gpcntl);
+	OUTB (nc_gpreg,  old_gpreg);
+
+	return retv;
+}
+
+/*
+ * Read Symbios NvRAM data and compute checksum.
+ */
+__initfunc(
+static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl)
+)
+{
+	int	x;
+	u_short	csum;
+
+	for (x = 0; x < len; x++) 
+		nvram_read_byte(np, &data[x], (x == (len - 1)), gpreg, gpcntl);
+
+	for (x = 6, csum = 0; x < len - 6; x++)
+		csum += data[x];
+
+	return csum;
+}
+
+/*
+ * Send START condition to NVRAM to wake it up.
+ */
+__initfunc(
+static void nvram_start(ncr_slot *np, u_char *gpreg)
+)
+{
+	nvram_setBit(np, 1, gpreg, SET_BIT);
+	nvram_setBit(np, 0, gpreg, SET_CLK);
+	nvram_setBit(np, 0, gpreg, CLR_BIT);
+	nvram_setBit(np, 0, gpreg, CLR_CLK);
+}
+
+/*
+ * WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK,
+ * GPIO0 must already be set as an output
+ */
+__initfunc(
+static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl)
+)
+{
+	int x;
+	
+	for (x = 0; x < 8; x++)
+		nvram_doBit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg);
+		
+	nvram_readAck(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ * READ a byte from the NVRAM and then send an ACK to say we have got it,
+ * GPIO0 must already be set as an input
+ */
+__initfunc(
+static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl)
+)
+{
+	int x;
+	u_char read_bit;
+
+	*read_data = 0;
+	for (x = 0; x < 8; x++) {
+		nvram_doBit(np, &read_bit, 1, gpreg);
+		*read_data |= ((read_bit & 0x01) << (7 - x));
+	}
+
+	nvram_writeAck(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ * Output an ACK to the NVRAM after reading,
+ * change GPIO0 to output and when done back to an input
+ */
+__initfunc(
+static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl)
+)
+{
+	OUTB (nc_gpcntl, *gpcntl & 0xfe);
+	nvram_doBit(np, 0, write_bit, gpreg);
+	OUTB (nc_gpcntl, *gpcntl);
+}
+
+/*
+ * Input an ACK from NVRAM after writing,
+ * change GPIO0 to input and when done back to an output
+ */
+__initfunc(
+static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl)
+)
+{
+	OUTB (nc_gpcntl, *gpcntl | 0x01);
+	nvram_doBit(np, read_bit, 1, gpreg);
+	OUTB (nc_gpcntl, *gpcntl);
+}
+
+/*
+ * Read or write a bit to the NVRAM,
+ * read if GPIO0 input else write if GPIO0 output
+ */
+__initfunc(
+static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg)
+)
+{
+	nvram_setBit(np, write_bit, gpreg, SET_BIT);
+	nvram_setBit(np, 0, gpreg, SET_CLK);
+	if (read_bit)
+		*read_bit = INB (nc_gpreg);
+	nvram_setBit(np, 0, gpreg, CLR_CLK);
+	nvram_setBit(np, 0, gpreg, CLR_BIT);
+}
+
+/*
+ * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!!
+ */
+__initfunc(
+static void nvram_stop(ncr_slot *np, u_char *gpreg)
+)
+{
+	nvram_setBit(np, 0, gpreg, SET_CLK);
+	nvram_setBit(np, 1, gpreg, SET_BIT);
+}
+
+/*
+ * Set/clear data/clock bit in GPIO0
+ */
+__initfunc(
+static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode)
+)
+{
+	DELAY(5);
+	switch (bit_mode){
+	case SET_BIT:
+		*gpreg |= write_bit;
+		break;
+	case CLR_BIT:
+		*gpreg &= 0xfe;
+		break;
+	case SET_CLK:
+		*gpreg |= 0x02;
+		break;
+	case CLR_CLK:
+		*gpreg &= 0xfd;
+		break;
+
+	}
+	OUTB (nc_gpreg, *gpreg);
+	DELAY(5);
+}
+
+#undef SET_BIT 0
+#undef CLR_BIT 1
+#undef SET_CLK 2
+#undef CLR_CLK 3
+
+
+/* ---------------------------------------------------------------------
+**
+**	Try reading Tekram format nvram
+**
+** ---------------------------------------------------------------------
+**
+** GPOI0 - data in
+** GPIO1 - data out
+** GPIO2 - clock
+** GPIO4 - chip select
+**
+**	return 0 if NVRAM data OK, 1 if NVRAM data not OK
+** ---------------------------------------------------------------------
+*/
+
+static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg);
+static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg);
+static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg);
+static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg);
+static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg);
+static void Tnvram_Stop(ncr_slot *np, u_char *gpreg);
+static void Tnvram_Clk(ncr_slot *np, u_char *gpreg);
+
+__initfunc(
+static int ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram)
+)
+{
+	u_char gpcntl, gpreg;
+	u_char old_gpcntl, old_gpreg;
+	u_short csum;
+
+	/* save current state of GPCNTL and GPREG */
+	old_gpreg	= INB (nc_gpreg);
+	old_gpcntl	= INB (nc_gpcntl);
+
+	/* set up GPREG & GPCNTL to set GPIO0/1/2/4 in to known state, 0 in,
+	   1/2/4 out */
+	gpreg = old_gpreg & 0xe9;
+	OUTB (nc_gpreg, gpreg);
+	gpcntl = (old_gpcntl & 0xe9) | 0x09;
+	OUTB (nc_gpcntl, gpcntl);
+
+	/* input all of NVRAM, 64 words */
+	csum = Tnvram_read_data(np, (u_short *) nvram,
+			sizeof(*nvram) / sizeof(short), &gpreg);
+	
+	/* return GPIO0/1/2/4 to original states after having accessed NVRAM */
+	OUTB (nc_gpcntl, old_gpcntl);
+	OUTB (nc_gpreg,  old_gpreg);
+
+	/* check data valid */
+	if (csum != 0x1234)
+		return 1;
+
+	return 0;
+}
+
+/*
+ * Read Tekram NvRAM data and compute checksum.
+ */
+__initfunc(
+static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg)
+)
+{
+	u_char	read_bit;
+	u_short	csum;
+	int	x;
+
+	for (x = 0, csum = 0; x < len; x++)  {
+
+		/* output read command and address */
+		Tnvram_Send_Command(np, 0x180 | x, &read_bit, gpreg);
+		if (read_bit & 0x01)
+			return 0; /* Force bad checksum */
+
+		Tnvram_Read_Word(np, &data[x], gpreg);
+		csum += data[x];
+
+		Tnvram_Stop(np, gpreg);
+	}
+
+	return csum;
+}
+
+/*
+ * Send read command and address to NVRAM
+ */
+__initfunc(
+static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg)
+)
+{
+	int x;
+
+	/* send 9 bits, start bit (1), command (2), address (6)  */
+	for (x = 0; x < 9; x++)
+		Tnvram_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg);
+
+	*read_bit = INB (nc_gpreg);
+}
+
+/*
+ * READ a byte from the NVRAM
+ */
+__initfunc(
+static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg)
+)
+{
+	int x;
+	u_char read_bit;
+
+	*nvram_data = 0;
+	for (x = 0; x < 16; x++) {
+		Tnvram_Read_Bit(np, &read_bit, gpreg);
+
+		if (read_bit & 0x01)
+			*nvram_data |=  (0x01 << (15 - x));
+		else
+			*nvram_data &= ~(0x01 << (15 - x));
+	}
+}
+
+/* 
+ * Read bit from NVRAM
+ */
+__initfunc(
+static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg)
+)
+{
+	DELAY(2);
+	Tnvram_Clk(np, gpreg);
+	*read_bit = INB (nc_gpreg);
+}
+
+/*
+ * Write bit to GPIO0
+ */
+__initfunc(
+static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg)
+)
+{
+	if (write_bit & 0x01)
+		*gpreg |= 0x02;
+	else
+		*gpreg &= 0xfd;
+		
+	*gpreg |= 0x10;
+		
+	OUTB (nc_gpreg, *gpreg);
+	DELAY(2);
+
+	Tnvram_Clk(np, gpreg);
+}
+
+/*
+ * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!!
+ */
+__initfunc(
+static void Tnvram_Stop(ncr_slot *np, u_char *gpreg)
+)
+{
+	*gpreg &= 0xef;
+	OUTB (nc_gpreg, *gpreg);
+	DELAY(2);
+
+	Tnvram_Clk(np, gpreg);
+}
+
+/*
+ * Pulse clock bit in GPIO0
+ */
+__initfunc(
+static void Tnvram_Clk(ncr_slot *np, u_char *gpreg)
+)
+{
+	OUTB (nc_gpreg, *gpreg | 0x04);
+	DELAY(2);
+	OUTB (nc_gpreg, *gpreg);
+}
+
+#endif	/* SCSI_NCR_NVRAM_SUPPORT */
 
 /*
 **	Module stuff
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/ncr53c8xx.h linux/drivers/scsi/ncr53c8xx.h
--- v2.0.30/linux/drivers/scsi/ncr53c8xx.h	Tue Mar 11 15:40:02 1997
+++ linux/drivers/scsi/ncr53c8xx.h	Thu Aug 21 15:13:04 1997
@@ -42,7 +42,10 @@
 #ifndef NCR53C8XX_H
 #define NCR53C8XX_H
 
-/*********** LINUX SPECIFIC SECTION ******************/
+/*
+**	Name and revision of the driver
+*/
+#define SCSI_NCR_DRIVER_NAME		"ncr53c8xx - revision 2.4a"
 
 /*
 **	Check supported Linux versions
@@ -65,28 +68,6 @@
 #define LINUX_VERSION_CODE LinuxVersionCode(1,2,13)
 #endif
 
-#if !defined(VERSION)
-#define VERSION		((LINUX_VERSION_CODE >> 16) & 0xff)
-#define PATCHLEVEL	((LINUX_VERSION_CODE >> 8)  & 0xff)
-#define SUBLEVEL	((LINUX_VERSION_CODE >> 0)  & 0xff)
-#endif
-
-#if	VERSION == 0 || VERSION > 3
-#	error Only Linux version 1 and probable 2 or 3 supported.
-#endif
-
-#if	VERSION == 1 && PATCHLEVEL == 2
-#	if	SUBLEVEL != 13
-#		error Only sublevel 13 of Linux 1.2 is supported.
-#	endif
-#endif
-
-#if	VERSION == 1 && PATCHLEVEL == 3
-#	if	SUBLEVEL < 45
-#		error Only sublevels >=45 of Linux 1.3 are supported.
-#	endif
-#endif
-
 /*
 **	Normal IO or memory mapped IO.
 **
@@ -108,53 +89,168 @@
 #endif
 
 /*
-**	Avoid to change these constants, unless you know what you are doing.
+**	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
+#	define	SCSI_NCR_USER_COMMAND_SUPPORT
+#	define	SCSI_NCR_USER_INFO_SUPPORT
+/* #	define	SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT */
+#endif
+
+/*==========================================================
+**
+** nvram settings - #define SCSI_NCR_NVRAM_SUPPORT to enable
+**
+**==========================================================
 */
 
+#ifdef CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT
+#define SCSI_NCR_NVRAM_SUPPORT
+/* #define SCSI_NCR_DEBUG_NVRAM */
+#endif
+
+/* ---------------------------------------------------------------------
+** Take into account kernel configured parameters.
+** Most of these options can be overridden at startup by a command line.
+** ---------------------------------------------------------------------
+*/
+
+/*
+ * For Ultra2 SCSI support option, use special features and allow 40Mhz 
+ * synchronous data transfers.
+ */
+#define	SCSI_NCR_SETUP_SPECIAL_FEATURES		(1)
+#define SCSI_NCR_SETUP_ULTRA_SCSI		(2)
+#define SCSI_NCR_MAX_SYNC			(40)
+
+/*
+ * Allow tags from 2 to 12, default 4
+ */
+#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)
+#else
+#define	SCSI_NCR_MAX_TAGS	CONFIG_SCSI_NCR53C8XX_MAX_TAGS
+#endif
+#else
 #define SCSI_NCR_MAX_TAGS	(4)
-#define SCSI_NCR_ALWAYS_SIMPLE_TAG
+#endif
 
-#ifdef CONFIG_SCSI_NCR53C8XX_IOMAPPED
-#define	SCSI_NCR_IOMAPPED
+/*
+ * Allow tagged command queuing support if configured with default number 
+ * of tags set to max (see above).
+ */
+#ifdef	CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE
+#define	SCSI_NCR_SETUP_DEFAULT_TAGS	SCSI_NCR_MAX_TAGS
+#else
+#define	SCSI_NCR_SETUP_DEFAULT_TAGS	(0)
 #endif
 
-#ifndef CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE
-#define SCSI_NCR_TAGGED_QUEUE_DISABLED
+/*
+ * Use normal IO if configured. Forced for alpha.
+ */
+#if defined(CONFIG_SCSI_NCR53C8XX_IOMAPPED) || defined(__alpha__)
+#define	SCSI_NCR_IOMAPPED
 #endif
 
-#ifdef CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT
-#define SCSI_NCR_NO_DISCONNECT
+/*
+ * Sync transfer frequency at startup.
+ * Allow from 5Mhz to 40Mhz default 10 Mhz.
+ */
+#ifndef	CONFIG_SCSI_NCR53C8XX_SYNC
+#define	CONFIG_SCSI_NCR53C8XX_SYNC	(5)
+#elif	CONFIG_SCSI_NCR53C8XX_SYNC > SCSI_NCR_MAX_SYNC
+#define	SCSI_NCR_SETUP_DEFAULT_SYNC	SCSI_NCR_MAX_SYNC
+#endif
+
+#if	CONFIG_SCSI_NCR53C8XX_SYNC == 0
+#define	SCSI_NCR_SETUP_DEFAULT_SYNC	(255)
+#elif	CONFIG_SCSI_NCR53C8XX_SYNC <= 5
+#define	SCSI_NCR_SETUP_DEFAULT_SYNC	(50)
+#elif	CONFIG_SCSI_NCR53C8XX_SYNC <= 20
+#define	SCSI_NCR_SETUP_DEFAULT_SYNC	(250/(CONFIG_SCSI_NCR53C8XX_SYNC))
+#elif	CONFIG_SCSI_NCR53C8XX_SYNC <= 33
+#define	SCSI_NCR_SETUP_DEFAULT_SYNC	(11)
+#else
+#define	SCSI_NCR_SETUP_DEFAULT_SYNC	(10)
 #endif
 
-#ifdef CONFIG_SCSI_NCR53C8XX_FORCE_ASYNCHRONOUS
-#define SCSI_NCR_FORCE_ASYNCHRONOUS
+/*
+ * Disallow disconnections at boot-up
+ */
+#ifdef CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT
+#define SCSI_NCR_SETUP_DISCONNECTION	(0)
+#else
+#define SCSI_NCR_SETUP_DISCONNECTION	(1)
 #endif
 
+/*
+ * Force synchronous negotiation for all targets
+ */
 #ifdef CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO
-#define SCSI_NCR_FORCE_SYNC_NEGO
+#define SCSI_NCR_SETUP_FORCE_SYNC_NEGO	(1)
+#else
+#define SCSI_NCR_SETUP_FORCE_SYNC_NEGO	(0)
 #endif
 
+/*
+ * Disable master parity checking (flawed hardwares need that)
+ */
 #ifdef CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK
-#define SCSI_NCR_DISABLE_MPARITY_CHECK
+#define SCSI_NCR_SETUP_MASTER_PARITY	(0)
+#else
+#define SCSI_NCR_SETUP_MASTER_PARITY	(1)
 #endif
 
+/*
+ * Disable scsi parity checking (flawed devices may need that)
+ */
 #ifdef CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK
-#define SCSI_NCR_DISABLE_PARITY_CHECK
+#define SCSI_NCR_SETUP_SCSI_PARITY	(0)
+#else
+#define SCSI_NCR_SETUP_SCSI_PARITY	(1)
 #endif
 
-#if 0
-#define SCSI_NCR_SEGMENT_SIZE	(512)
+/*
+ * Vendor specific stuff
+ */
+#ifdef CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT
+#define SCSI_NCR_SETUP_LED_PIN		(1)
+#define SCSI_NCR_SETUP_DIFF_SUPPORT	(3)
+#else
+#define SCSI_NCR_SETUP_LED_PIN		(0)
+#define SCSI_NCR_SETUP_DIFF_SUPPORT	(0)
 #endif
 
-#define SCSI_NCR_MAX_SCATTER	(128)
+/*
+ * Settle time after reset at boot-up
+ */
+#define SCSI_NCR_SETUP_SETTLE_TIME	(2)
+
+/*
+**	Other parameters not configurable with "make config"
+**	Avoid to change these constants, unless you know what you are doing.
+*/
+
+#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_SETTLE_TIME	(2)
 #define SCSI_NCR_TIMEOUT_ALERT	(3*HZ)
 
 #define SCSI_NCR_CAN_QUEUE	(7*SCSI_NCR_MAX_TAGS)
 #define SCSI_NCR_CMD_PER_LUN	(SCSI_NCR_MAX_TAGS)
-#define SCSI_NCR_SG_TABLESIZE	(SCSI_NCR_MAX_SCATTER-1)
+#define SCSI_NCR_SG_TABLESIZE	(SCSI_NCR_MAX_SCATTER)
+
+#define SCSI_NCR_TIMER_INTERVAL	((HZ+5-1)/5)
 
 #if 1 /* defined CONFIG_SCSI_MULTI_LUN */
 #define SCSI_NCR_MAX_LUN	(8)
@@ -162,8 +258,6 @@
 #define SCSI_NCR_MAX_LUN	(1)
 #endif
 
-#define SCSI_NCR_TIMER_INTERVAL	((HZ+5-1)/5)
-
 /*
 **	Define Scsi_Host_Template parameters
 **
@@ -196,7 +290,7 @@
 
 #if	LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
 
-#define NCR53C8XX {NULL,NULL,NULL,NULL,"ncr53c8xx (rel 1.12d)", ncr53c8xx_detect,\
+#define NCR53C8XX {NULL,NULL,NULL,NULL,SCSI_NCR_DRIVER_NAME, ncr53c8xx_detect,\
     	ncr53c8xx_release, /* info */ NULL, /* command, deprecated */ NULL, 		\
 	ncr53c8xx_queue_command, ncr53c8xx_abort, ncr53c8xx_reset,	\
         NULL /* slave attach */, scsicam_bios_param, /* can queue */ SCSI_NCR_CAN_QUEUE,\
@@ -207,7 +301,7 @@
 #else
 
 
-#define NCR53C8XX {NULL, NULL, "ncr53c8xx (rel 1.12d)", ncr53c8xx_detect,\
+#define NCR53C8XX {NULL, NULL, SCSI_NCR_DRIVER_NAME, ncr53c8xx_detect,\
     	ncr53c8xx_release, /* info */ NULL, /* command, deprecated */ NULL, 		\
 	ncr53c8xx_queue_command, ncr53c8xx_abort, ncr53c8xx_reset,	\
         NULL /* slave attach */, scsicam_bios_param, /* can queue */ SCSI_NCR_CAN_QUEUE,\
@@ -222,6 +316,200 @@
 #ifndef HOSTS_C
 
 /*
+**	NCR53C8XX Device Ids
+*/
+
+#ifndef PCI_DEVICE_ID_NCR_53C810
+#define PCI_DEVICE_ID_NCR_53C810 1
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C810AP
+#define PCI_DEVICE_ID_NCR_53C810AP 5
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C815
+#define PCI_DEVICE_ID_NCR_53C815 4
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C820
+#define PCI_DEVICE_ID_NCR_53C820 2
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C825
+#define PCI_DEVICE_ID_NCR_53C825 3
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C860
+#define PCI_DEVICE_ID_NCR_53C860 6
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C875
+#define PCI_DEVICE_ID_NCR_53C875 0xf
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C875J
+#define PCI_DEVICE_ID_NCR_53C875J 0x8f
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C885
+#define PCI_DEVICE_ID_NCR_53C885 0xd
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C895
+#define PCI_DEVICE_ID_NCR_53C895 0xc
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C896
+#define PCI_DEVICE_ID_NCR_53C896 0xb
+#endif
+
+/*
+**   NCR53C8XX devices features table.
+*/
+typedef struct {
+	unsigned short	device_id;
+	unsigned short	revision_id;
+	char	*name;
+	unsigned char	burst_max;
+	unsigned char	offset_max;
+	unsigned char	nr_divisor;
+	unsigned int	features;
+#define FE_LED0		(1<<0)
+#define FE_WIDE		(1<<1)
+#define FE_ULTRA	(1<<2)
+#define FE_ULTRA2	(1<<3)
+#define FE_DBLR		(1<<4)
+#define FE_QUAD		(1<<5)
+#define FE_ERL		(1<<6)
+#define FE_CLSE		(1<<7)
+#define FE_WRIE		(1<<8)
+#define FE_ERMP		(1<<9)
+#define FE_BOF		(1<<10)
+#define FE_DFS		(1<<11)
+#define FE_PFEN		(1<<12)
+#define FE_LDSTR	(1<<13)
+#define FE_RAM		(1<<14)
+#define FE_CLK80	(1<<15)
+#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)
+} ncr_chip;
+
+#define SCSI_NCR_CHIP_TABLE						\
+{									\
+ {PCI_DEVICE_ID_NCR_53C810, 0x0f, "810",  4,  8, 4,			\
+ FE_ERL}								\
+ ,									\
+ {PCI_DEVICE_ID_NCR_53C810, 0xff, "810a", 4,  8, 4,			\
+ FE_CACHE_SET|FE_LDSTR|FE_PFEN|FE_BOF}					\
+ ,									\
+ {PCI_DEVICE_ID_NCR_53C815, 0xff, "815",  4,  8, 4,			\
+ FE_ERL|FE_BOF}								\
+ ,									\
+ {PCI_DEVICE_ID_NCR_53C820, 0xff, "820",  4,  8, 4,			\
+ FE_WIDE|FE_ERL}							\
+ ,									\
+ {PCI_DEVICE_ID_NCR_53C825, 0x0f, "825",  4,  8, 4,			\
+ FE_WIDE|FE_ERL|FE_BOF}							\
+ ,									\
+ {PCI_DEVICE_ID_NCR_53C825, 0xff, "825a", 7,  8, 4,			\
+ FE_WIDE|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}		\
+ ,									\
+ {PCI_DEVICE_ID_NCR_53C860, 0xff, "860",  4,  8, 5,			\
+ FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_BOF|FE_LDSTR|FE_PFEN}		\
+ ,									\
+ {PCI_DEVICE_ID_NCR_53C875, 0x01, "875",  7, 16, 5,			\
+ FE_WIDE|FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ ,									\
+ {PCI_DEVICE_ID_NCR_53C875, 0xff, "875",  7, 16, 5,			\
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ ,									\
+ {PCI_DEVICE_ID_NCR_53C875J, 0xff, "875J",  7, 16, 5,			\
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ ,									\
+ {PCI_DEVICE_ID_NCR_53C885, 0xff, "885",  7, 16, 5,			\
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ ,									\
+ {PCI_DEVICE_ID_NCR_53C895, 0xff, "895",  7, 31, 7,			\
+ 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}\
+}
+
+/*
+ * List of supported NCR chip ids
+ */
+#define SCSI_NCR_CHIP_IDS		\
+{					\
+	PCI_DEVICE_ID_NCR_53C810,	\
+	PCI_DEVICE_ID_NCR_53C815,	\
+	PCI_DEVICE_ID_NCR_53C820,	\
+	PCI_DEVICE_ID_NCR_53C825,	\
+	PCI_DEVICE_ID_NCR_53C860,	\
+	PCI_DEVICE_ID_NCR_53C875,	\
+	PCI_DEVICE_ID_NCR_53C875J,	\
+	PCI_DEVICE_ID_NCR_53C885,	\
+	PCI_DEVICE_ID_NCR_53C895,	\
+	PCI_DEVICE_ID_NCR_53C896	\
+}
+
+/*
+**	Initial setup.
+**	Can be overriden at startup by a command line.
+*/
+#define SCSI_NCR_DRIVER_SETUP			\
+{						\
+	SCSI_NCR_SETUP_MASTER_PARITY,		\
+	SCSI_NCR_SETUP_SCSI_PARITY,		\
+	SCSI_NCR_SETUP_DISCONNECTION,		\
+	SCSI_NCR_SETUP_SPECIAL_FEATURES,	\
+	SCSI_NCR_SETUP_ULTRA_SCSI,		\
+	SCSI_NCR_SETUP_FORCE_SYNC_NEGO,		\
+	0,					\
+	0,					\
+	1,					\
+	1,					\
+	SCSI_NCR_SETUP_DEFAULT_TAGS,		\
+	SCSI_NCR_SETUP_DEFAULT_SYNC,		\
+	0x00,					\
+	7,					\
+	SCSI_NCR_SETUP_LED_PIN,			\
+	1,					\
+	SCSI_NCR_SETUP_SETTLE_TIME,		\
+	SCSI_NCR_SETUP_DIFF_SUPPORT,		\
+	0					\
+}
+
+/*
+**	Boot fail safe setup.
+**	Override initial setup from boot command line:
+**	ncr53c8xx=safe:y
+*/
+#define SCSI_NCR_DRIVER_SAFE_SETUP		\
+{						\
+	0,					\
+	1,					\
+	0,					\
+	0,					\
+	0,					\
+	0,					\
+	0,					\
+	0,					\
+	1,					\
+	2,					\
+	0,					\
+	255,					\
+	0x00,					\
+	255,					\
+	0,					\
+	0,					\
+	10,					\
+	1,					\
+	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
@@ -309,38 +597,6 @@
 #endif
 #endif
 
-/*
-**	NCR53C8XX Device Ids
-*/
-
-#ifndef PCI_DEVICE_ID_NCR_53C810
-#define PCI_DEVICE_ID_NCR_53C810 1
-#endif
-
-#ifndef PCI_DEVICE_ID_NCR_53C810AP
-#define PCI_DEVICE_ID_NCR_53C810AP 5
-#endif
-
-#ifndef PCI_DEVICE_ID_NCR_53C815
-#define PCI_DEVICE_ID_NCR_53C815 4
-#endif
-
-#ifndef PCI_DEVICE_ID_NCR_53C820
-#define PCI_DEVICE_ID_NCR_53C820 2
-#endif
-
-#ifndef PCI_DEVICE_ID_NCR_53C825
-#define PCI_DEVICE_ID_NCR_53C825 3
-#endif
-
-#ifndef PCI_DEVICE_ID_NCR_53C860
-#define PCI_DEVICE_ID_NCR_53C860 6
-#endif
-
-#ifndef PCI_DEVICE_ID_NCR_53C875
-#define PCI_DEVICE_ID_NCR_53C875 0xf
-#endif
-
 /**************** ORIGINAL CONTENT of ncrreg.h from FreeBSD ******************/
 
 /*-----------------------------------------------------------------
@@ -365,6 +621,7 @@
 
 /*03*/  u_char    nc_scntl3;    /* cnf system clock dependent       */
 	#define   EWS     0x08  /* cmd: enable wide scsi         [W]*/
+	#define   ULTRA   0x80  /* cmd: ULTRA enable                */
 
 /*04*/  u_char    nc_scid;	/* cnf host adapter scsi address    */
 	#define   RRE     0x40  /* r/w:e enable response to resel.  */
@@ -418,6 +675,7 @@
         #define   ILF1    0x80  /* sta: data in SIDL register msb[W]*/
         #define   ORF1    0x40  /* sta: data in SODR register msb[W]*/
         #define   OLF1    0x20  /* sta: data in SODL register msb[W]*/
+        #define   DM      0x04  /* sta: DIFFSENS mismatch (895/6 only) */
         #define   LDSC    0x02  /* sta: disconnect & reconnect      */
 
 /*10*/  u_int32    nc_dsa;	/* --> Base page                    */
@@ -443,13 +701,20 @@
 	#define   CSIGP   0x40
 
 /*1b*/  u_char    nc_ctest3;
-	#define   CLF	  0x04	/* clear scsi fifo		    */
+	#define   FLF     0x08  /* cmd: flush dma fifo              */
+	#define   CLF	  0x04	/* cmd: clear dma fifo		    */
+	#define   FM      0x02  /* mod: fetch pin mode              */
+	#define   WRIE    0x01  /* mod: write and invalidate enable */
 
 /*1c*/  u_int32    nc_temp;	/* ### Temporary stack              */
 
 /*20*/	u_char	  nc_dfifo;
 /*21*/  u_char    nc_ctest4;
+	#define   BDIS    0x80  /* mod: burst disable               */
+	#define   MPEE    0x08  /* mod: master parity error enable  */
+
 /*22*/  u_char    nc_ctest5;
+	#define   DFS     0x20  /* mod: dma fifo size               */
 /*23*/  u_char    nc_ctest6;
 
 /*24*/  u_int32    nc_dbc;	/* ### Byte count and command       */
@@ -459,18 +724,31 @@
 /*34*/  u_int32    nc_scratcha;  /* ??? Temporary register a         */
 
 /*38*/  u_char    nc_dmode;
+	#define   BL_2    0x80  /* mod: burst length shift value +2 */
+	#define   BL_1    0x40  /* mod: burst length shift value +1 */
+	#define   ERL     0x08  /* mod: enable read line            */
+	#define   ERMP    0x04  /* mod: enable read multiple        */
+	#define   BOF     0x02  /* mod: burst op code fetch         */
+
 /*39*/  u_char    nc_dien;
 /*3a*/  u_char    nc_dwt;
 
 /*3b*/  u_char    nc_dcntl;	/* --> Script execution control     */
-        #define   SSM     0x10  /* mod: single step mode            */
-        #define   STD     0x04  /* cmd: start dma mode              */
-	#define	  NOCOM   0x01	/* cmd: protect sfbr while reselect */
+
+	#define   CLSE    0x80  /* mod: cache line size enable      */
+	#define   PFF     0x40  /* cmd: pre-fetch flush             */
+	#define   PFEN    0x20  /* mod: pre-fetch enable            */
+	#define   SSM     0x10  /* mod: single step mode            */
+	#define   IRQM    0x08  /* mod: irq mode (1 = totem pole !) */
+	#define   STD     0x04  /* cmd: start dma mode              */
+	#define   IRQD    0x02  /* mod: irq disable                 */
+ 	#define	  NOCOM   0x01	/* cmd: protect sfbr while reselect */
 
 /*3c*/  u_int32    nc_adder;
 
 /*40*/  u_short   nc_sien;	/* -->: interrupt enable            */
 /*42*/  u_short   nc_sist;	/* <--: interrupt status            */
+        #define   SBMC    0x1000/* sta: SCSI Bus Mode Change (895/6 only) */
         #define   STO     0x0400/* sta: timeout (select)            */
         #define   GEN     0x0200/* sta: timeout (general)           */
         #define   HTH     0x0100/* sta: timeout (handshake)         */
@@ -496,6 +774,7 @@
 /*4d*/  u_char    nc_stest1;
 	#define   DBLEN   0x08	/* clock doubler running		*/
 	#define   DBLSEL  0x04	/* clock doubler selected		*/
+  
 
 /*4e*/  u_char    nc_stest2;
 	#define   ROF     0x40	/* reset scsi offset (after gross error!) */
@@ -503,10 +782,18 @@
 
 /*4f*/  u_char    nc_stest3;
 	#define   TE     0x80	/* c: tolerAnt enable */
+	#define   HSC    0x20	/* c: Halt SCSI Clock */
 	#define   CSF    0x02	/* c: clear scsi fifo */
 
 /*50*/  u_short   nc_sidl;	/* Lowlevel: latched from scsi data */
-/*52*/  u_short   nc_52_;
+/*52*/  u_char    nc_stest4;
+	#define   SMODE  0xc0	/* SCSI bus mode      (895/6 only) */
+	#define    SMODE_HVD 0x40	/* High Voltage Differential       */
+	#define    SMODE_SE  0x80	/* Single Ended                    */
+	#define    SMODE_LVD 0xc0	/* Low Voltage Differential        */
+	#define   LCKFRQ 0x20	/* Frequency Lock (895/6 only)     */
+
+/*53*/	u_char    nc_53_;
 /*54*/  u_short   nc_sodl;	/* Lowlevel: data out to scsi data  */
 /*56*/  u_short   nc_56_;
 /*58*/  u_short   nc_sbdl;	/* Lowlevel: data from scsi data    */
@@ -659,10 +946,18 @@
 **	<< source_address >>
 **	<< destination_address >>
 **
+**	SCR_COPY   sets the NO FLUSH option by default.
+**	SCR_COPY_F does not set this option.
+**
+**	For chips which do not support this option,
+**	ncr_copy_and_bind() will remove this bit.
 **-----------------------------------------------------------
 */
 
-#define SCR_COPY(n) (0xc0000000 | (n))
+#define SCR_NO_FLUSH 0x01000000
+
+#define SCR_COPY(n) (0xc0000000 | SCR_NO_FLUSH | (n))
+#define SCR_COPY_F(n) (0xc0000000 | (n))
 
 /*-----------------------------------------------------------
 **
@@ -768,6 +1063,7 @@
 **-----------------------------------------------------------
 */
 
+#define SCR_NO_OP        0x80000000
 #define SCR_JUMP        0x80080000
 #define SCR_JUMPR       0x80880000
 #define SCR_CALL        0x88080000
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v2.0.30/linux/drivers/scsi/scsi.c	Mon Mar 31 13:27:43 1997
+++ linux/drivers/scsi/scsi.c	Mon Aug 11 13:37:24 1997
@@ -114,7 +114,6 @@
 #define SCSI_BLOCK(HOST) ((HOST->block && host_active && HOST != host_active) \
 			  || (HOST->can_queue && HOST->host_busy >= HOST->can_queue))
 
-#define MAX_SCSI_DEVICE_CODE 10
 const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] =
 {
     "Direct-Access    ",
@@ -289,6 +288,7 @@
 {"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},
 /*
  * Must be at end of list...
  */
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h
--- v2.0.30/linux/drivers/scsi/scsi.h	Tue Mar 11 15:40:02 1997
+++ linux/drivers/scsi/scsi.h	Mon Aug 11 13:37:24 1997
@@ -39,11 +39,12 @@
 extern volatile int in_scan_scsis;
 extern const unsigned char scsi_command_size[8];
 #define COMMAND_SIZE(opcode) scsi_command_size[((opcode) >> 5) & 7]
-
 #define IDENTIFY_BASE       0x80
 #define IDENTIFY(can_disconnect, lun)   (IDENTIFY_BASE |\
 		     ((can_disconnect) ?  0x40 : 0) |\
 		     ((lun) & 0x07)) 
+#define MAX_SCSI_DEVICE_CODE 10
+extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE];
 
 		 
     
@@ -218,6 +219,12 @@
 
 extern Scsi_Device * scsi_devices;
 
+extern struct hd_struct * sd;
+
+#if defined(MAJOR_NR) && (MAJOR_NR == SCSI_DISK_MAJOR)
+extern struct hd_struct * sd;
+#endif
+
 /*
  *  Initializes all SCSI devices.  This scans all scsi busses.
  */ 
@@ -498,8 +505,14 @@
     req = &SCpnt->request;
     req->errors = 0;
     if (!uptodate) {
+#if defined(MAJOR_NR) && (MAJOR_NR == SCSI_DISK_MAJOR)
+	printk(DEVICE_NAME " I/O error: dev %s, sector %lu, absolute sector %lu\n",
+	       kdevname(req->rq_dev), req->sector, 
+	       req->sector + sd[MINOR(SCpnt->request.rq_dev)].start_sect);
+#else
 	printk(DEVICE_NAME " I/O error: dev %s, sector %lu\n",
 	       kdevname(req->rq_dev), req->sector);
+#endif
     }
     
     do {
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/scsi_ioctl.c linux/drivers/scsi/scsi_ioctl.c
--- v2.0.30/linux/drivers/scsi/scsi_ioctl.c	Fri Feb 28 15:14:18 1997
+++ linux/drivers/scsi/scsi_ioctl.c	Mon Aug 25 12:34:15 1997
@@ -9,6 +9,7 @@
 #include <asm/io.h>
 #include <asm/segment.h>
 #include <asm/system.h>
+#include <asm/page.h>
 
 #include <linux/errno.h>
 #include <linux/kernel.h>
@@ -21,9 +22,14 @@
 #include "hosts.h"
 #include <scsi/scsi_ioctl.h>
 
-#define MAX_RETRIES 5   
-#define MAX_TIMEOUT (9 * HZ)
-#define MAX_BUF 4096
+#define NORMAL_RETRIES 5   
+#define NORMAL_TIMEOUT (10 * HZ)
+#define FORMAT_UNIT_TIMEOUT (2 * 60 * 60 * HZ)
+#define START_STOP_TIMEOUT (60 * HZ)
+#define MOVE_MEDIUM_TIMEOUT (5 * 60 * HZ)
+#define READ_ELEMENT_STATUS_TIMEOUT (5 * 60 * HZ)
+
+#define MAX_BUF PAGE_SIZE
 
 #define max(a,b) (((a) > (b)) ? (a) : (b))
 
@@ -65,7 +71,7 @@
 /*
  * 
  * The SCSI_IOCTL_SEND_COMMAND ioctl sends a command out to the SCSI host.
- * The MAX_TIMEOUT and MAX_RETRIES  variables are used.  
+ * The NORMAL_TIMEOUT and NORMAL_RETRIES  variables are used.  
  * 
  * dev is the SCSI device struct ptr, *(int *) arg is the length of the
  * input data, if any, not including the command string & counts, 
@@ -73,12 +79,9 @@
  * 
  * *(char *) ((int *) arg)[2] the actual command byte.   
  * 
- * Note that no more than MAX_BUF data bytes will be transfered.  Since
- * SCSI block device size is 512 bytes, I figured 1K was good.
- * but (WDE) changed it to 8192 to handle large bad track buffers.
- * ERY: I changed this to a dynamic allocation using scsi_malloc - we were
- * getting a kernel stack overflow which was crashing the system when we
- * were using 8192 bytes.
+ * Note that if more than MAX_BUF bytes are requested to be transfered,
+ * the ioctl will fail with error EINVAL.  MAX_BUF can be increased in
+ * the future by increasing the size that scsi_malloc will accept.
  * 
  * This size *does not* include the initial lengths that were passed.
  * 
@@ -101,7 +104,8 @@
     }
 }   
 
-static int ioctl_internal_command(Scsi_Device *dev, char * cmd)
+static int ioctl_internal_command(Scsi_Device *dev, char * cmd,
+				  int timeout, int retries)
 {
     int result;
     Scsi_Cmnd * SCpnt;
@@ -110,9 +114,7 @@
     {
 	struct semaphore sem = MUTEX_LOCKED;
 	SCpnt->request.sem = &sem;
-	scsi_do_cmd(SCpnt,  cmd, NULL,  0,
-		    scsi_ioctl_done,  MAX_TIMEOUT,
-		    MAX_RETRIES);
+	scsi_do_cmd(SCpnt,  cmd, NULL,  0, scsi_ioctl_done,  timeout, retries);
 	down(&sem);
     }
     
@@ -201,8 +203,8 @@
      * If the user needs to transfer more data than this, they
      * should use scsi_generics instead.
      */
-    if( inlen > MAX_BUF ) inlen = MAX_BUF;
-    if( outlen > MAX_BUF ) outlen = MAX_BUF;
+    if( inlen > MAX_BUF )  return -EINVAL;
+    if( outlen > MAX_BUF )  return -EINVAL;
 
     cmd_in = (char *) ( ((int *)buffer) + 2);
     opcode = get_user(cmd_in); 
@@ -243,21 +245,24 @@
     switch (opcode)
       {
       case FORMAT_UNIT:
-	timeout =  2 * 60 * 60 * HZ; /* 2 Hours */
+	timeout = FORMAT_UNIT_TIMEOUT;
 	retries = 1;
 	break;
       case START_STOP:
-	timeout =  60 * HZ;	/* 60 seconds */
-	retries = 1;
+	timeout = START_STOP_TIMEOUT;
+	retries = NORMAL_RETRIES;
 	break;
       case MOVE_MEDIUM:
+	timeout = MOVE_MEDIUM_TIMEOUT;
+	retries = NORMAL_RETRIES;
+	break;
       case READ_ELEMENT_STATUS:
-	timeout =  5 * 60 * HZ;	/* 5 minutes */
-	retries = 1;
+	timeout = READ_ELEMENT_STATUS_TIMEOUT;
+	retries = NORMAL_RETRIES;
 	break;
       default:
-	timeout = MAX_TIMEOUT;
-	retries = MAX_RETRIES;
+	timeout = NORMAL_TIMEOUT;
+	retries = NORMAL_RETRIES;
 	break;
       }
 
@@ -366,7 +371,8 @@
 	scsi_cmd[1] = dev->lun << 5;
 	scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
 	scsi_cmd[4] = SCSI_REMOVAL_PREVENT;
-	return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd);
+	return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd,
+				      NORMAL_TIMEOUT, NORMAL_RETRIES);
 	break;
     case SCSI_IOCTL_DOORUNLOCK:
 	if (!dev->removable || !dev->lockable) return 0;
@@ -374,13 +380,31 @@
 	scsi_cmd[1] = dev->lun << 5;
 	scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
 	scsi_cmd[4] = SCSI_REMOVAL_ALLOW;
-	return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd);
+	return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd,
+				      NORMAL_TIMEOUT, NORMAL_RETRIES);
     case SCSI_IOCTL_TEST_UNIT_READY:
 	scsi_cmd[0] = TEST_UNIT_READY;
 	scsi_cmd[1] = dev->lun << 5;
 	scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
 	scsi_cmd[4] = 0;
-	return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd);
+	return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd,
+				      NORMAL_TIMEOUT, NORMAL_RETRIES);
+	break;
+    case SCSI_IOCTL_START_UNIT:
+	scsi_cmd[0] = START_STOP;
+	scsi_cmd[1] = dev->lun << 5;
+	scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+	scsi_cmd[4] = 1;
+	return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd,
+				      START_STOP_TIMEOUT, NORMAL_RETRIES);
+	break;
+    case SCSI_IOCTL_STOP_UNIT:
+	scsi_cmd[0] = START_STOP;
+	scsi_cmd[1] = dev->lun << 5;
+	scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+	scsi_cmd[4] = 0;
+	return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd,
+				      START_STOP_TIMEOUT, NORMAL_RETRIES);
 	break;
     default :           
 	return -EINVAL;
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/scsi_proc.c linux/drivers/scsi/scsi_proc.c
--- v2.0.30/linux/drivers/scsi/scsi_proc.c	Sat Nov 11 03:11:19 1995
+++ linux/drivers/scsi/scsi_proc.c	Mon Aug 11 13:37:24 1997
@@ -239,21 +239,6 @@
     return(cmdIndex);
 }
 
-#define MAX_SCSI_DEVICE_CODE 10
-const char *const scsi_dev_types[MAX_SCSI_DEVICE_CODE] =
-{
-    "Direct-Access    ",
-    "Sequential-Access",
-    "Printer          ",
-    "Processor        ",
-    "WORM             ",
-    "CD-ROM           ",
-    "Scanner          ",
-    "Optical Device   ",
-    "Medium Changer   ",
-    "Communications   "
-};
-
 void proc_print_scsidevice(Scsi_Device *scd, char *buffer, int *size, int len)
 {	    
     int x, y = *size;
@@ -285,7 +270,7 @@
     
     y += sprintf(buffer + len + y, "  Type:   %s ",
 		     scd->type < MAX_SCSI_DEVICE_CODE ? 
-		     scsi_dev_types[(int)scd->type] : "Unknown          " );
+		     scsi_device_types[(int)scd->type] : "Unknown          " );
     y += sprintf(buffer + len + y, "               ANSI"
 		     " SCSI revision: %02x", (scd->scsi_level < 3)?1:2);
     if (scd->scsi_level == 2)
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/scsi_syms.c linux/drivers/scsi/scsi_syms.c
--- v2.0.30/linux/drivers/scsi/scsi_syms.c	Wed May  1 21:48:54 1996
+++ linux/drivers/scsi/scsi_syms.c	Sun Aug 17 12:49:03 1997
@@ -63,6 +63,7 @@
     X(print_Scsi_Cmnd),
     X(scsi_mark_host_reset),
     X(scsi_mark_bus_reset),
+    X(scsi_device_types),
 #if defined(CONFIG_PROC_FS)
     X(proc_print_scsidevice),
 #endif
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/scsiiom.c linux/drivers/scsi/scsiiom.c
--- v2.0.30/linux/drivers/scsi/scsiiom.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/scsiiom.c	Thu Aug 14 10:31:20 1997
@@ -0,0 +1,1540 @@
+/***********************************************************************
+ *	FILE NAME : SCSIIOM.C					       *
+ *	     BY   : C.L. Huang,    ching@tekram.com.tw		       *
+ *	Description: Device Driver for Tekram DC-390 (T) PCI SCSI      *
+ *		     Bus Master Host Adapter			       *
+ ***********************************************************************/
+
+
+static USHORT
+DC390_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB )
+{
+    USHORT ioport, rc;
+    UCHAR  bval, bval1, i, cnt;
+    PUCHAR ptr;
+    ULONG  wlval;
+
+    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);
+
+    pSRB->ScsiPhase = SCSI_NOP0;
+    bval = pDCB->IdentifyMsg;
+    if( !(pDCB->SyncMode & EN_ATN_STOP) )
+    {
+	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;
+	    pSRB->SRBState = SRB_START_;
+	    if( pDCB->SyncMode & SYNC_ENABLE )
+	    {
+		if( !(pDCB->IdentifyMsg & 7) ||
+		    (pSRB->CmdBlock[0] != INQUIRY) )
+		{
+		    bval1 = SEL_W_ATN_STOP;
+		    pSRB->SRBState = SRB_MSGOUT;
+		}
+	    }
+	}
+	else
+	{
+	    if(pDCB->SyncMode & EN_TAG_QUEUING)
+	    {
+		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);
+		pDCB->TagMask |= wlval;
+		pSRB->TagNumber = bval;
+		bval1 = SEL_W_ATN2;
+		pSRB->SRBState = SRB_START_;
+	    }
+	    else
+	    {
+		outb(bval,ioport+ScsiFifo);
+		bval1 = SELECT_W_ATN;
+		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 */
+    {
+	if( (pSRB->CmdBlock[0] == INQUIRY) ||
+	    (pSRB->CmdBlock[0] == REQUEST_SENSE) ||
+	    (pSRB->SRBFlag & AUTO_REQSENSE) )
+	{
+	    bval &= 0xBF;
+	    outb(bval,ioport+ScsiFifo);
+	    bval1 = SELECT_W_ATN;
+	    pSRB->SRBState = SRB_START_;
+	    if( pDCB->SyncMode & SYNC_ENABLE )
+	    {
+		if( !(pDCB->IdentifyMsg & 7) ||
+		    (pSRB->CmdBlock[0] != INQUIRY) )
+		{
+		    bval1 = SEL_W_ATN_STOP;
+		    pSRB->SRBState = SRB_MSGOUT;
+		}
+	    }
+	}
+	else
+	{
+	    if(pDCB->SyncMode & EN_TAG_QUEUING)
+	    {
+		outb(bval,ioport+ScsiFifo);
+		pSRB->MsgOutBuf[0] = MSG_SIMPLE_QTAG;
+		wlval = 1;
+		bval = 0;
+		while( wlval & pDCB->TagMask )
+		{
+		    wlval = wlval << 1;
+		    bval++;
+		}
+		pDCB->TagMask |= wlval;
+		pSRB->TagNumber = bval;
+		pSRB->MsgOutBuf[1] = bval;
+		pSRB->MsgCnt = 2;
+		bval1 = SEL_W_ATN_STOP;
+		pSRB->SRBState = SRB_START_;
+	    }
+	    else
+	    {
+		outb(bval,ioport+ScsiFifo);
+		pSRB->MsgOutBuf[0] = MSG_NOP;
+		pSRB->MsgCnt = 1;
+		pSRB->SRBState = SRB_START_;
+		bval1 = SEL_W_ATN_STOP;
+	    }
+	}
+    }
+    bval = inb( ioport+Scsi_Status );
+    if( bval & INTERRUPT )
+    {
+	pSRB->SRBState = SRB_READY;
+	pDCB->TagMask &= ~( 1 << pSRB->TagNumber );
+	rc = 1;
+    }
+    else
+    {
+	pSRB->ScsiPhase = SCSI_NOP1;
+	pACB->pActiveDCB = pDCB;
+	pDCB->pActiveSRB = pSRB;
+	rc = 0;
+	outb(bval1,ioport+ScsiCmd);
+    }
+    return( rc );
+}
+
+
+#ifndef  VERSION_ELF_1_2_13
+static void
+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;
+    void   (*stateV)( PACB, PSRB, PUCHAR );
+    UCHAR  istate = 0;
+    UCHAR  sstatus=0, istatus;
+
+    pACB = pACB_start;
+    if( pACB == NULL )
+	return;
+    for( i=0; i < adapterCnt; i++ )
+    {
+	if( pACB->IRQLevel == (UCHAR) irq )
+	{
+	     ioport = pACB->IOPortBase;
+	     sstatus = inb( ioport+Scsi_Status );
+	     if( sstatus & INTERRUPT )
+		 break;
+	     else
+		pACB = pACB->pNextACB;
+	}
+	else
+	{
+	    pACB = pACB->pNextACB;
+	}
+    }
+
+#ifdef DC390_DEBUG1
+	printk("sstatus=%2x,",sstatus);
+#endif
+
+    if( pACB == (PACB )-1 )
+    {
+	printk("DC390: Spurious interrupt detected!\n");
+	return;
+    }
+
+    istate = inb( ioport+Intern_State );
+    istatus = inb( ioport+INT_Status );
+
+#ifdef DC390_DEBUG1
+	printk("Istatus=%2x,",istatus);
+#endif
+
+    if(istatus &  DISCONNECTED)
+    {
+	DC390_Disconnect( pACB );
+	return;
+    }
+
+    if(istatus &  RESELECTED)
+    {
+	DC390_Reselect( pACB );
+	return;
+    }
+
+    if(istatus &  INVALID_CMD)
+    {
+	DC390_InvalidCmd( pACB );
+	return;
+    }
+
+    if(istatus &  SCSI_RESET)
+    {
+	DC390_ScsiRstDetect( pACB );
+	return;
+    }
+
+    if( istatus & (SUCCESSFUL_OP+SERVICE_REQUEST) )
+    {
+	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 );
+    }
+}
+
+
+static void
+DC390_DataOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+    UCHAR   sstatus, bval;
+    USHORT  ioport;
+    PSGL    psgl;
+    ULONG   ResidCnt, xferCnt;
+
+    ioport = pACB->IOPortBase;
+    sstatus = *psstatus;
+
+    if( !(pSRB->SRBState & SRB_XFERPAD) )
+    {
+	if( sstatus & PARITY_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);
+	    pSRB->TotalXferredLen += pSRB->SGToBeXferLen;
+	    pSRB->SGIndex++;
+	    if( pSRB->SGIndex < pSRB->SGcount )
+	    {
+		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->SGToBeXferLen = (ULONG) psgl->length;
+	    }
+	    else
+		pSRB->SGToBeXferLen = 0;
+	}
+	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;
+
+	    xferCnt = pSRB->SGToBeXferLen - ResidCnt;
+	    pSRB->SGPhysAddr += xferCnt;
+	    pSRB->TotalXferredLen += xferCnt;
+	    pSRB->SGToBeXferLen = ResidCnt;
+	}
+    }
+    bval = WRITE_DIRECTION+DMA_IDLE_CMD;
+    outb( bval, ioport+DMA_Cmd);
+}
+
+static void
+DC390_DataIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+    UCHAR   sstatus, bval;
+    USHORT  i, ioport, residual;
+    PSGL    psgl;
+    ULONG   ResidCnt, xferCnt;
+    PUCHAR  ptr;
+
+
+    ioport = pACB->IOPortBase;
+    sstatus = *psstatus;
+
+    if( !(pSRB->SRBState & SRB_XFERPAD) )
+    {
+	if( sstatus & PARITY_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);
+
+	    bval = READ_DIRECTION+DMA_IDLE_CMD;
+	    outb( bval, ioport+DMA_Cmd);
+
+	    pSRB->TotalXferredLen += pSRB->SGToBeXferLen;
+	    pSRB->SGIndex++;
+	    if( pSRB->SGIndex < pSRB->SGcount )
+	    {
+		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->SGToBeXferLen = (ULONG) psgl->length;
+	    }
+	    else
+		pSRB->SGToBeXferLen = 0;
+	}
+	else	/* phase changed */
+	{
+	    residual = 0;
+	    bval = inb(ioport+Current_Fifo);
+	    while( bval & 0x1f )
+	    {
+		if( (bval & 0x1f) == 1 )
+		{
+		    for(i=0; i< 0x100; i++)
+		    {
+			bval = inb(ioport+Current_Fifo);
+			if( !(bval & 0x1f) )
+			    goto din_1;
+			else if( i == 0x0ff )
+			{
+			    residual = 1;   /* ;1 residual byte */
+			    goto din_1;
+			}
+		    }
+		}
+		else
+		    bval = inb(ioport+Current_Fifo);
+	    }
+din_1:
+	    bval = READ_DIRECTION+DMA_BLAST_CMD;
+	    outb(bval, ioport+DMA_Cmd);
+	    for(i=0; i<0x8000; i++)
+	    {
+		bval = inb(ioport+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);
+
+	    xferCnt = pSRB->SGToBeXferLen - ResidCnt;
+	    pSRB->SGPhysAddr += 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
+		*ptr = bval;
+		pSRB->SGPhysAddr++;
+		pSRB->TotalXferredLen++;
+		pSRB->SGToBeXferLen--;
+	    }
+	}
+    }
+}
+
+static void
+DC390_Command_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+}
+
+static void
+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;
+
+    *psstatus = SCSI_NOP0;
+    pSRB->SRBState = SRB_COMPLETED;
+    bval = MSG_ACCEPTED_CMD;
+    outb(bval, ioport+ScsiCmd);
+}
+
+static void
+DC390_MsgOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+    if( pSRB->SRBState & (SRB_UNEXPECT_RESEL+SRB_ABORT_SENT) )
+	*psstatus = SCSI_NOP0;
+}
+
+static void
+DC390_MsgIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+    UCHAR  bval;
+    USHORT ioport, wval, wval1;
+    PDCB   pDCB;
+    PSRB   psrb;
+
+    ioport = pACB->IOPortBase;
+    pDCB = pACB->pActiveDCB;
+
+    bval = inb( ioport+ScsiFifo );
+    if( !(pSRB->SRBState & SRB_MSGIN_MULTI) )
+    {
+	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)) )
+	{
+	    pSRB->SRBState |= SRB_MSGIN_MULTI;
+	    pSRB->MsgInBuf[0] = bval;
+	    pSRB->MsgCnt = 1;
+	    pSRB->pMsgPtr = &pSRB->MsgInBuf[1];
+	}
+	else if(bval == MSG_REJECT_)
+	{
+	    bval = RESET_ATN_CMD;
+	    outb(bval, ioport+ScsiCmd);
+	    if( pSRB->SRBState & DO_SYNC_NEGO)
+		goto  set_async;
+	}
+	else if( bval == MSG_RESTORE_PTR)
+	    goto  min6;
+	else
+	    goto  min6;
+    }
+    else
+    {	/* minx: */
+
+	*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 );
+		}
+	    }
+	}
+	else if( (pSRB->MsgInBuf[0] == MSG_EXTENDED) && (pSRB->MsgCnt == 5) )
+	{
+	    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: */
+
+		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);
+	    }
+	}
+    }
+min6:
+    *psstatus = SCSI_NOP0;
+    bval = MSG_ACCEPTED_CMD;
+    outb(bval, ioport+ScsiCmd);
+}
+
+static void
+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);
+	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->SGToBeXferLen = (ULONG) psgl->length;
+	}
+	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);
+
+	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);
+
+	pSRB->SRBState = SRB_DATA_XFER;
+
+	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);
+    }
+    else    /* xfer pad */
+    {
+	if( pSRB->SGcount )
+	{
+	    pSRB->AdaptStatus = H_OVER_UNDER_RUN;
+	    pSRB->SRBStatus |= OVER_RUN;
+	}
+	bval = 0;
+	outb(bval,ioport+CtcReg_Low);
+	outb(bval,ioport+CtcReg_Mid);
+	outb(bval,ioport+CtcReg_High);
+
+	pSRB->SRBState |= SRB_XFERPAD;
+	bval = DMA_COMMAND+XFER_PAD_BYTE;
+	outb(bval, ioport+ScsiCmd);
+/*
+	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);
+*/
+    }
+}
+
+
+static void
+DC390_DataOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+    UCHAR  ioDir;
+
+    ioDir = WRITE_DIRECTION;
+    DataIO_Comm( pACB, pSRB, ioDir);
+}
+
+static void
+DC390_DataInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+    UCHAR  ioDir;
+
+    ioDir = READ_DIRECTION;
+    DataIO_Comm( pACB, pSRB, ioDir);
+}
+
+static void
+DC390_CommandPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+    PDCB   pDCB;
+    UCHAR  bval;
+    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);
+    if( !(pSRB->SRBFlag & AUTO_REQSENSE) )
+    {
+	cnt = (USHORT) pSRB->ScsiCmdLen;
+	ptr = (PUCHAR) pSRB->CmdBlock;
+	for(i=0; i < cnt; i++)
+	{
+	    outb(*ptr, ioport+ScsiFifo);
+	    ptr++;
+	}
+    }
+    else
+    {
+	bval = REQUEST_SENSE;
+	outb(bval, ioport+ScsiFifo);
+	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);
+    }
+    pSRB->SRBState = SRB_COMMAND;
+    bval = INFO_XFER_CMD;
+    outb(bval, ioport+ScsiCmd);
+}
+
+static void
+DC390_StatusPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+    UCHAR  bval;
+    USHORT ioport;
+
+    ioport = pACB->IOPortBase;
+    bval = CLEAR_FIFO_CMD;
+    outb(bval, ioport+ScsiCmd);
+    pSRB->SRBState = SRB_STATUS;
+    bval = INITIATOR_CMD_CMPLTE;
+    outb(bval, ioport+ScsiCmd);
+}
+
+static void
+DC390_MsgOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+    UCHAR   bval;
+    USHORT  ioport, i, cnt;
+    PUCHAR  ptr;
+    PDCB    pDCB;
+
+    ioport = pACB->IOPortBase;
+    bval = CLEAR_FIFO_CMD;
+    outb(bval, ioport+ScsiCmd);
+    pDCB = pACB->pActiveDCB;
+    if( !(pSRB->SRBState & SRB_MSGOUT) )
+    {
+	cnt = pSRB->MsgCnt;
+	if( cnt )
+	{
+	    ptr = (PUCHAR) pSRB->MsgOutBuf;
+	    for(i=0; i < cnt; i++)
+	    {
+		outb(*ptr, ioport+ScsiFifo);
+		ptr++;
+	    }
+	    pSRB->MsgCnt = 0;
+	    if( (pDCB->DCBFlag & ABORT_DEV_) &&
+		(pSRB->MsgOutBuf[0] == MSG_ABORT) )
+		pSRB->SRBState = SRB_ABORT_SENT;
+	}
+	else
+	{
+	    bval = MSG_ABORT;	/* ??? MSG_NOP */
+	    if( (pSRB->CmdBlock[0] == INQUIRY ) ||
+		(pSRB->CmdBlock[0] == REQUEST_SENSE) ||
+		(pSRB->SRBFlag & AUTO_REQSENSE) )
+	    {
+		if( pDCB->SyncMode & SYNC_ENABLE )
+		    goto  mop1;
+	    }
+	    outb(bval, ioport+ScsiFifo);
+	}
+	bval = INFO_XFER_CMD;
+	outb( bval, ioport+ScsiCmd);
+    }
+    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);
+	pSRB->SRBState |= DO_SYNC_NEGO;
+	bval = INFO_XFER_CMD;
+	outb(bval, ioport+ScsiCmd);
+    }
+}
+
+static void
+DC390_MsgInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+    UCHAR  bval;
+    USHORT ioport;
+
+    ioport = pACB->IOPortBase;
+    bval = CLEAR_FIFO_CMD;
+    outb(bval, ioport+ScsiCmd);
+    if( !(pSRB->SRBState & SRB_MSGIN) )
+    {
+	pSRB->SRBState &= SRB_DISCONNECT;
+	pSRB->SRBState |= SRB_MSGIN;
+    }
+    bval = INFO_XFER_CMD;
+    outb(bval, ioport+ScsiCmd);
+}
+
+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 )
+{
+    UCHAR  bval;
+    USHORT cnt, i;
+    PDCB   ptr;
+
+    if( !(pDCB->IdentifyMsg & 0x07) )
+    {
+	if( pACB->scan_devices )
+	{
+	    CurrSyncOffset = pDCB->SyncOffset;
+	}
+	else
+	{
+	    ptr = pACB->pLinkDCB;
+	    cnt = pACB->DeviceCnt;
+	    bval = pDCB->UnitSCSIID;
+	    for(i=0; i<cnt; i++)
+	    {
+		if( ptr->UnitSCSIID == bval )
+		{
+		    ptr->SyncPeriod = pDCB->SyncPeriod;
+		    ptr->SyncOffset = pDCB->SyncOffset;
+		    ptr->CtrlR3 = pDCB->CtrlR3;
+		    ptr->CtrlR4 = pDCB->CtrlR4;
+		    ptr->SyncMode = pDCB->SyncMode;
+		}
+		ptr = ptr->pNextDCB;
+	    }
+	}
+    }
+    return;
+}
+
+
+static void
+DC390_Disconnect( PACB pACB )
+{
+    PDCB   pDCB;
+    PSRB   pSRB, psrb;
+    ULONG  flags;
+    USHORT ioport, i, cnt;
+    UCHAR  bval;
+
+#ifdef DC390_DEBUG0
+    printk("DISC,");
+#endif
+
+    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;
+     }
+    pSRB = pDCB->pActiveSRB;
+    pACB->pActiveDCB = 0;
+    pSRB->ScsiPhase = SCSI_NOP0;
+    bval = EN_SEL_RESEL;
+    outb(bval, ioport+ScsiCmd);
+    if( pSRB->SRBState & SRB_UNEXPECT_RESEL )
+    {
+	pSRB->SRBState = 0;
+	DoWaitingSRB( pACB );
+    }
+    else if( pSRB->SRBState & SRB_ABORT_SENT )
+    {
+	pDCB->TagMask = 0;
+	pDCB->DCBFlag = 0;
+	cnt = pDCB->GoingSRBCnt;
+	pDCB->GoingSRBCnt = 0;
+	pSRB = pDCB->pGoingSRB;
+	for( i=0; i < cnt; i++)
+	{
+	    psrb = pSRB->pNextSRB;
+	    pSRB->pNextSRB = pACB->pFreeSRB;
+	    pACB->pFreeSRB = pSRB;
+	    pSRB = psrb;
+	}
+	pDCB->pGoingSRB = 0;
+	DoWaitingSRB( pACB );
+    }
+    else
+    {
+	if( (pSRB->SRBState & (SRB_START_+SRB_MSGOUT)) ||
+	   !(pSRB->SRBState & (SRB_DISCONNECT+SRB_COMPLETED)) )
+	{	/* Selection time out */
+	    if( !(pACB->scan_devices) )
+	    {
+		pSRB->SRBState = SRB_READY;
+		RewaitSRB( pDCB, pSRB);
+	    }
+	    else
+	    {
+		pSRB->TargetStatus = SCSI_STAT_SEL_TIMEOUT;
+		goto  disc1;
+	    }
+	}
+	else if( pSRB->SRBState & SRB_DISCONNECT )
+	{
+	    DoWaitingSRB( pACB );
+	}
+	else if( pSRB->SRBState & SRB_COMPLETED )
+	{
+disc1:
+	    if(pDCB->MaxCommand > 1)
+	    {
+	       bval = pSRB->TagNumber;
+	       pDCB->TagMask &= (~(1 << bval));   /* free tag mask */
+	    }
+	    pDCB->pActiveSRB = 0;
+	    pSRB->SRBState = SRB_FREE;
+	    SRBdone( pACB, pDCB, pSRB);
+	}
+    }
+    restore_flags(flags);
+    return;
+}
+
+
+static void
+DC390_Reselect( PACB pACB )
+{
+    PDCB   pDCB, pdcb;
+    PSRB   pSRB;
+    USHORT ioport, wval;
+    UCHAR  bval, bval1;
+
+
+#ifdef DC390_DEBUG0
+    printk("RSEL,");
+#endif
+    ioport = pACB->IOPortBase;
+    pDCB = pACB->pActiveDCB;
+    if( pDCB )
+    {	/* Arbitration lost but Reselection win */
+	pSRB = pDCB->pActiveSRB;
+	if( !( pACB->scan_devices ) )
+	{
+	    pSRB->SRBState = SRB_READY;
+	    RewaitSRB( pDCB, pSRB);
+	}
+    }
+    bval = inb(ioport+ScsiFifo);	/* get ID */
+    bval = bval ^ pACB->HostID_Bit;
+    wval = 0;
+    bval1 = 1;
+    for(;;)
+    {
+	if( !(bval & bval1) )
+	{
+	    bval1 = bval1 << 1;
+	    wval++;
+	}
+	else
+	    break;
+    }
+    wval |=  ( (USHORT) inb(ioport+ScsiFifo) & 7) << 8;  /* get LUN */
+    pDCB = pACB->pLinkDCB;
+    pdcb = pDCB;
+    while( wval != *((PUSHORT) &pDCB->UnitSCSIID) )
+    {
+	pDCB = pDCB->pNextDCB;
+	if( pDCB == pdcb )
+	    return;
+    }
+    pACB->pActiveDCB = pDCB;
+    if( pDCB->SyncMode & EN_TAG_QUEUING )
+    {
+	pSRB = pACB->pTmpSRB;
+	pDCB->pActiveSRB = pSRB;
+    }
+    else
+    {
+	pSRB = pDCB->pActiveSRB;
+	if( !pSRB || !(pSRB->SRBState & SRB_DISCONNECT) )
+	{
+	    pSRB= pACB->pTmpSRB;
+	    pSRB->SRBState = SRB_UNEXPECT_RESEL;
+	    pDCB->pActiveSRB = pSRB;
+	    EnableMsgOut( pACB, pSRB );
+	}
+	else
+	{
+	    if( pDCB->DCBFlag & ABORT_DEV_ )
+	    {
+		pSRB->SRBState = SRB_ABORT_SENT;
+		EnableMsgOut( pACB, pSRB );
+	    }
+	    else
+		pSRB->SRBState = SRB_DATA_XFER;
+	}
+    }
+    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);
+}
+
+
+static void
+SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB )
+{
+    PSRB   psrb;
+    UCHAR  bval, bval1, i, j, status;
+    PSCSICMD pcmd;
+    PSCSI_INQDATA  ptr;
+    USHORT disable_tag;
+    ULONG  flags;
+    PSGL   ptr2;
+    ULONG  swlval;
+
+    pcmd = pSRB->pcmd;
+    status = pSRB->TargetStatus;
+    if(pSRB->SRBFlag & AUTO_REQSENSE)
+    {
+	pSRB->SRBFlag &= ~AUTO_REQSENSE;
+	pSRB->AdaptStatus = 0;
+	pSRB->TargetStatus = SCSI_STAT_CHECKCOND;
+	if(status == SCSI_STAT_CHECKCOND)
+	{
+	    pcmd->result = DID_BAD_TARGET << 16;
+	    goto ckc_e;
+	}
+	if(pSRB->RetryCnt == 0)
+	{
+	    *((PULONG) &(pSRB->CmdBlock[0])) = pSRB->Segment0[0];
+	    pSRB->TotalXferredLen = pSRB->Segment1[1];
+	    if( (pSRB->TotalXferredLen) &&
+		(pSRB->TotalXferredLen >= pcmd->underflow) )
+		pcmd->result |= (DID_OK << 16);
+	    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
+	    goto ckc_e;
+	}
+	else
+	{
+	    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 )
+	    {
+		pcmd->result = (DRIVER_SENSE << 24) | (DRIVER_OK << 16) |
+				SCSI_STAT_CHECKCOND;
+		goto ckc_e;
+	    }
+	    pcmd->result |= (DRIVER_SENSE << 24);
+	    pSRB->SGcount		 = (UCHAR) pSRB->Segment1[0];
+	    pSRB->ScsiCmdLen		 = (UCHAR) (pSRB->Segment1[0] >> 8);
+	    pSRB->SGIndex = 0;
+	    pSRB->TotalXferredLen = 0;
+	    pSRB->SGToBeXferLen = 0;
+	    if( pcmd->use_sg )
+		pSRB->pSegmentList = (PSGL) pcmd->request_buffer;
+	    else if( pcmd->request_buffer )
+	    {
+		pSRB->pSegmentList = (PSGL) &pSRB->Segmentx;
+		pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer;
+		pSRB->Segmentx.length = pcmd->request_bufflen;
+	    }
+	    if( DC390_StartSCSI( pACB, pDCB, pSRB ) )
+		RewaitSRB( pDCB, pSRB );
+	    return;
+	}
+    }
+    if( status )
+    {
+	if( status == SCSI_STAT_CHECKCOND)
+	{
+	    if( (pSRB->SGIndex < pSRB->SGcount) && (pSRB->SGcount) && (pSRB->SGToBeXferLen) )
+	    {
+		bval = pSRB->SGcount;
+		swlval = 0;
+		ptr2 = pSRB->pSegmentList;
+		for( i=pSRB->SGIndex; i < bval; i++)
+		{
+		    swlval += ptr2->length;
+		    ptr2++;
+		}
+#ifdef	DC390_DEBUG0
+		printk("XferredLen=%8x,NotXferLen=%8x,",
+			(UINT) pSRB->TotalXferredLen, (UINT) swlval);
+#endif
+	    }
+	    RequestSense( pACB, pDCB, pSRB );
+	    return;
+	}
+	else if( status == SCSI_STAT_QUEUEFULL )
+	{
+	    bval = (UCHAR) pDCB->GoingSRBCnt;
+	    bval--;
+	    pDCB->MaxCommand = bval;
+	    RewaitSRB( pDCB, pSRB );
+	    pSRB->AdaptStatus = 0;
+	    pSRB->TargetStatus = 0;
+	    return;
+	}
+	else if(status == SCSI_STAT_SEL_TIMEOUT)
+	{
+	    pSRB->AdaptStatus = H_SEL_TIMEOUT;
+	    pSRB->TargetStatus = 0;
+	    pcmd->result = DID_BAD_TARGET << 16;
+	}
+	else
+	{
+	    pSRB->AdaptStatus = 0;
+	    if( pSRB->RetryCnt )
+	    {
+		pSRB->RetryCnt--;
+		pSRB->TargetStatus = 0;
+		pSRB->SGIndex = 0;
+		pSRB->TotalXferredLen = 0;
+		pSRB->SGToBeXferLen = 0;
+		if( pcmd->use_sg )
+		    pSRB->pSegmentList = (PSGL) pcmd->request_buffer;
+		else if( pcmd->request_buffer )
+		{
+		    pSRB->pSegmentList = (PSGL) &pSRB->Segmentx;
+		    pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer;
+		    pSRB->Segmentx.length = pcmd->request_bufflen;
+		}
+		if( DC390_StartSCSI( pACB, pDCB, pSRB ) )
+		    RewaitSRB( pDCB, pSRB );
+		return;
+	    }
+	    else
+	    {
+		pcmd->result |= (DID_ERROR << 16) | (ULONG) (pSRB->EndMessage << 8) |
+			       (ULONG) status;
+	    }
+	}
+    }
+    else
+    {
+	status = pSRB->AdaptStatus;
+	if(status & H_OVER_UNDER_RUN)
+	{
+	    pSRB->TargetStatus = 0;
+	    pcmd->result |= (DID_OK << 16) | (pSRB->EndMessage << 8);
+	}
+	else if( pSRB->SRBStatus & PARITY_ERROR)
+	{
+	    pcmd->result |= (DID_PARITY << 16) | (pSRB->EndMessage << 8);
+	}
+	else		       /* No error */
+	{
+	    pSRB->AdaptStatus = 0;
+	    pSRB->TargetStatus = 0;
+	    pcmd->result |= (DID_OK << 16);
+	}
+    }
+
+ckc_e:
+    if( pACB->scan_devices )
+    {
+	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;
+	    }
+	    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;
+			}
+		    }
+		}
+	    }
+	}
+    }
+
+    save_flags( flags );
+    cli();
+/*  ReleaseSRB( pDCB, pSRB ); */
+
+    if(pSRB == pDCB->pGoingSRB )
+    {
+	pDCB->pGoingSRB = pSRB->pNextSRB;
+    }
+    else
+    {
+	psrb = pDCB->pGoingSRB;
+	while( psrb->pNextSRB != pSRB )
+	    psrb = psrb->pNextSRB;
+	psrb->pNextSRB = pSRB->pNextSRB;
+	if( pSRB == pDCB->pGoingLast )
+	    pDCB->pGoingLast = psrb;
+    }
+    pSRB->pNextSRB = pACB->pFreeSRB;
+    pACB->pFreeSRB = pSRB;
+    pDCB->GoingSRBCnt--;
+
+    DoWaitingSRB( pACB );
+    restore_flags(flags);
+
+/*  Notify cmd done */
+    pcmd->scsi_done( pcmd );
+
+    if( pDCB->QIORBCnt )
+	DoNextCmd( pACB, pDCB );
+    return;
+}
+
+
+static void
+DoingSRB_Done( PACB pACB )
+{
+    PDCB  pDCB, pdcb;
+    PSRB  psrb, psrb2;
+    USHORT  cnt, i;
+    PSCSICMD pcmd;
+
+    pDCB = pACB->pLinkDCB;
+    pdcb = pDCB;
+    do
+    {
+	cnt = pdcb->GoingSRBCnt;
+	psrb = pdcb->pGoingSRB;
+	for( i=0; i<cnt; i++)
+	{
+	    psrb2 = psrb->pNextSRB;
+	    pcmd = psrb->pcmd;
+	    pcmd->result = DID_RESET << 16;
+
+/*	    ReleaseSRB( pDCB, pSRB ); */
+
+	    psrb->pNextSRB = pACB->pFreeSRB;
+	    pACB->pFreeSRB = psrb;
+
+	    pcmd->scsi_done( pcmd );
+	    psrb  = psrb2;
+	}
+	pdcb->GoingSRBCnt = 0;;
+	pdcb->pGoingSRB = NULL;
+	pdcb->TagMask = 0;
+	pdcb = pdcb->pNextDCB;
+    }
+    while( pdcb != pDCB );
+}
+
+
+static void
+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);
+
+    restore_flags(flags);
+    return;
+}
+
+
+static void
+DC390_ScsiRstDetect( PACB pACB )
+{
+    ULONG wlval, flags;
+    USHORT ioport;
+    UCHAR  bval;
+
+#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);
+
+    if( pACB->ACBFlag & RESET_DEV )
+	pACB->ACBFlag |= RESET_DONE;
+    else
+    {
+	pACB->ACBFlag |= RESET_DETECT;
+
+	ResetDevParam( pACB );
+/*	DoingSRB_Done( pACB ); ???? */
+	RecoverSRB( pACB );
+	pACB->pActiveDCB = NULL;
+	pACB->ACBFlag = 0;
+	DoWaitingSRB( pACB );
+    }
+    restore_flags(flags);
+    return;
+}
+
+
+static void
+RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB )
+{
+    PSCSICMD  pcmd;
+
+    pSRB->SRBFlag |= AUTO_REQSENSE;
+    pSRB->Segment0[0] = *((PULONG) &(pSRB->CmdBlock[0]));
+    pSRB->Segment0[1] = *((PULONG) &(pSRB->CmdBlock[4]));
+    pSRB->Segment1[0] = (ULONG) ((pSRB->ScsiCmdLen << 8) + pSRB->SGcount);
+    pSRB->Segment1[1] = pSRB->TotalXferredLen;
+    pSRB->AdaptStatus = 0;
+    pSRB->TargetStatus = 0;
+
+    pcmd = pSRB->pcmd;
+
+    pSRB->Segmentx.address = (PUCHAR) &(pcmd->sense_buffer);
+    pSRB->Segmentx.length = sizeof(pcmd->sense_buffer);
+    pSRB->pSegmentList = &pSRB->Segmentx;
+    pSRB->SGcount = 1;
+    pSRB->SGIndex = 0;
+
+    *((PULONG) &(pSRB->CmdBlock[0])) = 0x00000003;
+    pSRB->CmdBlock[1] = pDCB->IdentifyMsg << 5;
+    *((PUSHORT) &(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 );
+}
+
+
+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 )
+{
+    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));
+    }
+}
+
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c
--- v2.0.30/linux/drivers/scsi/sd.c	Fri Feb 28 15:14:18 1997
+++ linux/drivers/scsi/sd.c	Wed Aug 13 18:46:08 1997
@@ -58,8 +58,8 @@
  *  Time out in seconds for disks and Magneto-opticals (which are slower).
  */
 
-#define SD_TIMEOUT (15 * HZ)
-#define SD_MOD_TIMEOUT (15 * HZ)
+#define SD_TIMEOUT (20 * HZ)
+#define SD_MOD_TIMEOUT (25 * HZ)
 
 #define CLUSTERABLE_DEVICE(SC) (SC->host->use_clustering && \
 				SC->device->type != TYPE_MOD)
@@ -966,7 +966,14 @@
     if(!rscsi_disks[target].device->removable) return 0;
     
     inode.i_rdev = full_dev;  /* This is all we really need here */
-    retval = sd_ioctl(&inode, NULL, SCSI_IOCTL_TEST_UNIT_READY, 0);
+
+    /* Using Start/Stop enables differentiation between drive with
+     * no cartridge loaded - NOT READY, drive with changed cartridge -
+     * UNIT ATTENTION, or with same cartridge - GOOD STATUS.
+     * This also handles drives that auto spin down. eg iomega jaz 1GB
+     * as this will spin up the drive.
+     */
+    retval = sd_ioctl(&inode, NULL, SCSI_IOCTL_START_UNIT, 0);
     
     if(retval){ /* Unable to test, unit probably not ready.  This usually
 		 * means there is no disc in the drive.  Mark as changed,
@@ -1023,7 +1030,7 @@
     spintime = 0;
     
     /* Spin up drives, as required.  Only do this at boot time */
-    if (!MODULE_FLAG){
+    /* Spinup needs to be done for module loads too. */
 	do{
 	    retries = 0;
 	    while(retries < 3)
@@ -1096,7 +1103,6 @@
 	    else
 		printk( "ready\n" );
 	}
-    }  /* !MODULE_FLAG */
     
     retries = 3;
     do {
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/sr.c linux/drivers/scsi/sr.c
--- v2.0.30/linux/drivers/scsi/sr.c	Tue Apr  8 08:47:46 1997
+++ linux/drivers/scsi/sr.c	Mon Aug 11 13:37:24 1997
@@ -545,6 +545,7 @@
 
     case SCSI_MAN_SONY: /* Thomas QUINOT <thomas@melchior.cuivre.fdn.fr> */
     case SCSI_MAN_PIONEER:
+    case SCSI_MAN_UNKNOWN:
 #ifdef DEBUG
 	printk(KERN_DEBUG "sr_photocd: use SONY/PIONEER code\n");
 #endif
@@ -563,7 +564,7 @@
                 printk(KERN_WARNING "sr_photocd: ioctl error (SONY/PIONEER): 0x%x\n",rc);
 	    break;
 	}
-	if ((rec[0] << 8) + rec[1] != 0x0a) {
+	if ((rec[0] << 8) + rec[1] < 0x0a) {
 	    printk(KERN_INFO "sr_photocd: (SONY/PIONEER) Hmm, seems the CDROM doesn't support multisession CD's\n");
 	    no_multi = 1;
 	    break;
@@ -577,7 +578,6 @@
 	break;
 		
     case SCSI_MAN_NEC_OLDCDR:
-    case SCSI_MAN_UNKNOWN:
     default:
 	sector = 0;
 	no_multi = 1;
@@ -601,6 +601,7 @@
     if (filp->f_mode & 2)  
 	return -EROFS;
 
+    sr_ioctl(inode,filp,CDROMCLOSETRAY,0);
     check_disk_change(inode->i_rdev);
     
     if(!scsi_CDs[MINOR(inode->i_rdev)].device->access_count++)
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/sr_ioctl.c linux/drivers/scsi/sr_ioctl.c
--- v2.0.30/linux/drivers/scsi/sr_ioctl.c	Sat May 18 12:19:19 1996
+++ linux/drivers/scsi/sr_ioctl.c	Mon Sep  1 11:21:30 1997
@@ -66,7 +66,9 @@
 	    printk("CDROM not ready.  Make sure there is a disc in the drive.\n");
 	    break;
 	case ILLEGAL_REQUEST:
-	    printk("CDROM (ioctl) reports ILLEGAL REQUEST.\n");
+	  /* CDROMCLOSETRAY should not print an error for caddy drives. */
+	    if (!(sr_cmd[0] == START_STOP && sr_cmd[4] == 0x03))
+	      printk("CDROM (ioctl) reports ILLEGAL REQUEST.\n");
 	    break;
 	default:
 	    printk("SCSI CD error: host %d id %d lun %d return code = %03x\n", 
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/st.c linux/drivers/scsi/st.c
--- v2.0.30/linux/drivers/scsi/st.c	Fri Feb 28 15:14:18 1997
+++ linux/drivers/scsi/st.c	Tue Aug  5 09:03:30 1997
@@ -1120,7 +1120,8 @@
     }
 
     if (STm->do_async_writes &&
-	((STp->buffer)->buffer_bytes >= STp->write_threshold ||
+	(((STp->buffer)->buffer_bytes >= STp->write_threshold &&
+	  (STp->buffer)->buffer_bytes >= STp->block_size) ||
 	 STp->block_size == 0) ) {
       /* Schedule an asynchronous write */
       if (!SCpnt) {
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/tmscsim.c linux/drivers/scsi/tmscsim.c
--- v2.0.30/linux/drivers/scsi/tmscsim.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/tmscsim.c	Thu Aug 14 10:31:20 1997
@@ -0,0 +1,1928 @@
+/***********************************************************************
+ *	FILE NAME : TMSCSIM.C					       *
+ *	     BY   : C.L. Huang,  ching@tekram.com.tw		       *
+ *	Description: Device Driver for Tekram DC-390(T) PCI SCSI       *
+ *		     Bus Master Host Adapter			       *
+ * (C)Copyright 1995-1996 Tekram Technology Co., Ltd.		       *
+ ***********************************************************************/
+/*	Minor enhancements and bugfixes by				*
+ *	Kurt Garloff <K.Garloff@ping.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 *
+ *				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 *
+ *				pending interrupt in DC390_detect()	*
+ ***********************************************************************/
+
+
+#define DC390_DEBUG
+
+#define SCSI_MALLOC
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/delay.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#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/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>
+
+#include "dc390.h"
+
+#define PCI_DEVICE_ID_AMD53C974 	PCI_DEVICE_ID_AMD_SCSI
+
+
+#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
+       };
+#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 );
+
+#ifdef MODULE
+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 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};
+
+UCHAR  baddevname1[2][28] ={
+       "SEAGATE ST3390N         9546",
+       "HP      C3323-300       4269"};
+
+#define BADDEVCNT	2
+
+
+/***********************************************************************
+ *
+ *
+ *
+ **********************************************************************/
+static void
+QLinkcmd( PSCSICMD cmd, PDCB pDCB )
+{
+    ULONG  flags;
+    PSCSICMD  pcmd;
+
+    save_flags(flags);
+    cli();
+
+    if( !pDCB->QIORBCnt )
+    {
+	pDCB->pQIORBhead = cmd;
+	pDCB->pQIORBtail = cmd;
+	pDCB->QIORBCnt++;
+	cmd->next = NULL;
+    }
+    else
+    {
+	pcmd = pDCB->pQIORBtail;
+	pcmd->next = cmd;
+	pDCB->pQIORBtail = cmd;
+	pDCB->QIORBCnt++;
+	cmd->next = NULL;
+    }
+
+    restore_flags(flags);
+}
+
+
+static PSCSICMD
+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 )
+{
+    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 )
+{
+    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);
+}
+
+
+static void
+RewaitSRB( PDCB pDCB, PSRB pSRB )
+{
+    PSRB   psrb1;
+    ULONG  flags;
+    UCHAR  bval;
+
+    save_flags(flags);
+    cli();
+    pDCB->GoingSRBCnt--;
+    psrb1 = pDCB->pGoingSRB;
+    if( pSRB == psrb1 )
+    {
+	pDCB->pGoingSRB = psrb1->pNextSRB;
+    }
+    else
+    {
+	while( pSRB != psrb1->pNextSRB )
+	    psrb1 = psrb1->pNextSRB;
+	psrb1->pNextSRB = pSRB->pNextSRB;
+	if( pSRB == pDCB->pGoingLast )
+	    pDCB->pGoingLast = psrb1;
+    }
+    if( (psrb1 = pDCB->pWaitingSRB) )
+    {
+	pSRB->pNextSRB = psrb1;
+	pDCB->pWaitingSRB = pSRB;
+    }
+    else
+    {
+	pSRB->pNextSRB = NULL;
+	pDCB->pWaitingSRB = pSRB;
+	pDCB->pWaitLast = pSRB;
+    }
+
+    bval = pSRB->TagNumber;
+    pDCB->TagMask &= (~(1 << bval));	  /* Free TAG number */
+    restore_flags(flags);
+}
+
+
+static void
+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;
+	if( !ptr )
+	{
+	    ptr = pACB->pLinkDCB;
+	    pACB->pDCBRunRobin = ptr;
+	}
+	ptr1 = ptr;
+	for( ;ptr1; )
+	{
+	    pACB->pDCBRunRobin = ptr1->pNextDCB;
+	    if( !( ptr1->MaxCommand > ptr1->GoingSRBCnt ) ||
+		!( pSRB = ptr1->pWaitingSRB ) )
+	    {
+		if(pACB->pDCBRunRobin == ptr)
+		    break;
+		ptr1 = ptr1->pNextDCB;
+	    }
+	    else
+	    {
+		if( !DC390_StartSCSI(pACB, ptr1, pSRB) )
+		{
+		    ptr1->GoingSRBCnt++;
+		    if( ptr1->pWaitLast == pSRB )
+		    {
+			ptr1->pWaitingSRB = NULL;
+			ptr1->pWaitLast = NULL;
+		    }
+		    else
+		    {
+			ptr1->pWaitingSRB = pSRB->pNextSRB;
+		    }
+		    pSRB->pNextSRB = NULL;
+
+		    if( ptr1->pGoingSRB )
+			ptr1->pGoingLast->pNextSRB = pSRB;
+		    else
+			ptr1->pGoingSRB = pSRB;
+		    ptr1->pGoingLast = pSRB;
+		}
+		break;
+	    }
+	}
+    }
+    restore_flags(flags);
+    return;
+}
+
+
+static void
+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;
+    }
+}
+
+
+static void
+SendSRB( PSCSICMD pcmd, 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);
+	goto SND_EXIT;
+    }
+
+    if( pDCB->pWaitingSRB )
+    {
+	SRBwaiting(pDCB, pSRB);
+/*	pSRB = GetWaitingSRB(pDCB); */
+	pSRB = pDCB->pWaitingSRB;
+	pDCB->pWaitingSRB = pSRB->pNextSRB;
+	pSRB->pNextSRB = NULL;
+    }
+
+    if( !DC390_StartSCSI(pACB, pDCB, pSRB) )
+    {
+	pDCB->GoingSRBCnt++;
+	if( pDCB->pGoingSRB )
+	{
+	    pDCB->pGoingLast->pNextSRB = pSRB;
+	    pDCB->pGoingLast = pSRB;
+	}
+	else
+	{
+	    pDCB->pGoingSRB = pSRB;
+	    pDCB->pGoingLast = pSRB;
+	}
+    }
+    else
+	RewaitSRB0( pDCB, pSRB );
+
+SND_EXIT:
+    restore_flags(flags);
+    return;
+}
+
+
+/***********************************************************************
+ * Function : static int DC390_queue_command (Scsi_Cmnd *cmd,
+ *					       void (*done)(Scsi_Cmnd *))
+ *
+ * Purpose : enqueues a SCSI command
+ *
+ * Inputs : cmd - SCSI command, done - function called on completion, with
+ *	    a pointer to the command descriptor.
+ *
+ * Returns : 0
+ *
+ ***********************************************************************/
+
+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;
+
+    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
+
+    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) )
+    {
+	pACB->scan_devices = 0;
+	pPrevDCB->pNextDCB = pACB->pLinkDCB;
+    }
+
+    if ( ( cmd->target > pACB->max_id ) || (cmd->lun > pACB->max_lun) )
+    {
+/*	printk("DC390: Ignore target %d lun %d\n",
+		cmd->target, cmd->lun); */
+	cmd->result = (DID_BAD_TARGET << 16);
+	done(cmd);
+	return( 0 );
+    }
+
+    if( (pACB->scan_devices) && !(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);
+	    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);
+	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
+    }
+
+    cmd->scsi_done = done;
+    cmd->result = 0;
+
+    save_flags(flags);
+    cli();
+
+    if( pDCB->QIORBCnt )
+    {
+	QLinkcmd( cmd, pDCB );
+	pcmd = Getcmd( pDCB );
+    }
+    else
+	pcmd = cmd;
+
+    pSRB = GetSRB( pACB );
+
+    if( !pSRB )
+    {
+	QLinkcmd( pcmd, pDCB );
+	restore_flags(flags);
+	return(0);
+    }
+
+/*  BuildSRB(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++)
+    {
+	*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;
+    }
+    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);
+}
+
+
+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;
+    }
+    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;
+}
+
+
+/***********************************************************************
+ * 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 heads, sectors, cylinders;
+    PACB pACB;
+
+    pACB = (PACB) disk->device->host->hostdata;
+    heads = 64;
+    sectors = 32;
+    cylinders = disk->capacity / (heads * sectors);
+
+    if ( cylinders > 1024)
+    {
+      heads = 255;
+      sectors = 63;
+      cylinders = disk->capacity / (255 * 63);
+    }
+
+    geom[0] = heads;
+    geom[1] = sectors;
+    geom[2] = cylinders;
+
+    return (0);
+}
+
+
+/***********************************************************************
+ * Function : int DC390_abort (Scsi_Cmnd *cmd)
+ *
+ * Purpose : Abort an errant SCSI command
+ *
+ * Inputs : cmd - command to abort
+ *
+ * Returns : 0 on success, -1 on failure.
+ ***********************************************************************/
+
+int
+DC390_abort (Scsi_Cmnd *cmd)
+{
+    ULONG flags;
+    PACB  pACB;
+    PDCB  pDCB, pdcb;
+    PSRB  pSRB, psrb;
+    USHORT count, i;
+    PSCSICMD  pcmd, pcmd1;
+    int   status;
+
+
+#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->QIORBCnt )
+    {
+	pcmd = pDCB->pQIORBhead;
+	if( pcmd == cmd )
+	{
+	    pDCB->pQIORBhead = pcmd->next;
+	    pcmd->next = NULL;
+	    pDCB->QIORBCnt--;
+	    status = SCSI_ABORT_SUCCESS;
+	    goto  ABO_X;
+	}
+	for( count = pDCB->QIORBCnt, i=0; i<count-1; i++)
+	{
+	    if( pcmd->next == cmd )
+	    {
+		pcmd1 = pcmd->next;
+		pcmd->next = pcmd1->next;
+		pcmd1->next = NULL;
+		pDCB->QIORBCnt--;
+		status = SCSI_ABORT_SUCCESS;
+		goto  ABO_X;
+	    }
+	    else
+	    {
+		pcmd = pcmd->next;
+	    }
+	}
+    }
+
+    pSRB = pDCB->pWaitingSRB;
+    if( !pSRB )
+	goto  ON_GOING;
+    if( pSRB->pcmd == cmd )
+    {
+	pDCB->pWaitingSRB = pSRB->pNextSRB;
+	goto  IN_WAIT;
+    }
+    else
+    {
+	psrb = pSRB;
+	if( !(psrb->pNextSRB) )
+	    goto ON_GOING;
+	while( psrb->pNextSRB->pcmd != cmd )
+	{
+	    psrb = psrb->pNextSRB;
+	    if( !(psrb->pNextSRB) )
+		goto ON_GOING;
+	}
+	pSRB = psrb->pNextSRB;
+	psrb->pNextSRB = pSRB->pNextSRB;
+	if( pSRB == pDCB->pWaitLast )
+	    pDCB->pWaitLast = psrb; /* No check for psrb == NULL ? */
+IN_WAIT:
+	pSRB->pNextSRB = pACB->pFreeSRB;
+	pACB->pFreeSRB = pSRB;
+	cmd->next = NULL;
+	status = SCSI_ABORT_SUCCESS;
+	goto  ABO_X;
+    }
+
+ON_GOING:
+    pSRB = pDCB->pGoingSRB;
+    for( count = pDCB->GoingSRBCnt, i=0; i<count; i++)
+    {
+	if( pSRB->pcmd != cmd )
+	    pSRB = pSRB->pNextSRB;
+	else
+	{
+	    if( (pACB->pActiveDCB == pDCB) && (pDCB->pActiveSRB == pSRB) )
+	    {
+		status = SCSI_ABORT_BUSY;
+		goto  ABO_X;
+	    }
+	    else
+	    {
+		status = SCSI_ABORT_SNOOZE;
+		goto  ABO_X;
+	    }
+	}
+    }
+
+NOT_RUN:
+    status = SCSI_ABORT_NOT_RUNNING;
+
+ABO_X:
+    cmd->result = DID_ABORT << 16;
+    cmd->scsi_done(cmd);
+    restore_flags(flags);
+    return( status );
+}
+
+
+static void
+ResetDevParam( PACB pACB )
+{
+    PDCB   pDCB, pdcb;
+
+    pDCB = pACB->pLinkDCB;
+    if( pDCB == NULL )
+	return;
+    pdcb = pDCB;
+    do
+    {
+	pDCB->SyncMode &= ~SYNC_NEGO_DONE;
+	pDCB->SyncPeriod = 0;
+	pDCB->SyncOffset = 0;
+	pDCB->CtrlR3 = FAST_CLK;
+	pDCB->CtrlR4 &= NEGATE_REQACKDATA;
+	pDCB->CtrlR4 |= EATER_25NS;
+	pDCB = pDCB->pNextDCB;
+    }
+    while( pdcb != pDCB );
+}
+
+
+static void
+RecoverSRB( PACB pACB )
+{
+    PDCB   pDCB, pdcb;
+    PSRB   psrb, psrb2;
+    USHORT cnt, i;
+
+    pDCB = pACB->pLinkDCB;
+    if( pDCB == NULL )
+	return;
+    pdcb = pDCB;
+    do
+    {
+	cnt = pdcb->GoingSRBCnt;
+	psrb = pdcb->pGoingSRB;
+	for (i=0; i<cnt; i++)
+	{
+	    psrb2 = psrb;
+	    psrb = psrb->pNextSRB;
+/*	    RewaitSRB( pDCB, psrb ); */
+	    if( pdcb->pWaitingSRB )
+	    {
+		psrb2->pNextSRB = pdcb->pWaitingSRB;
+		pdcb->pWaitingSRB = psrb2;
+	    }
+	    else
+	    {
+		pdcb->pWaitingSRB = psrb2;
+		pdcb->pWaitLast = psrb2;
+		psrb2->pNextSRB = NULL;
+	    }
+	}
+	pdcb->GoingSRBCnt = 0;
+	pdcb->pGoingSRB = NULL;
+	pdcb->TagMask = 0;
+	pdcb = pdcb->pNextDCB;
+    }
+    while( pdcb != pDCB );
+}
+
+
+/***********************************************************************
+ * Function : int DC390_reset (Scsi_Cmnd *cmd, ...)
+ *
+ * Purpose : perform a hard reset on the SCSI bus
+ *
+ * Inputs : cmd - command which caused the SCSI RESET
+ *
+ * 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;
+
+
+#ifdef DC390_DEBUG1
+    printk("DC390: RESET,");
+#endif
+
+    pACB = (PACB ) cmd->host->hostdata;
+    ioport = pACB->IOPortBase;
+    save_flags(flags);
+    cli();
+    bval = inb(ioport+CtrlReg1);
+    bval |= DIS_INT_ON_SCSI_RST;
+    outb(bval,ioport+CtrlReg1);  /* disable interrupt */
+    DC390_ResetSCSIBus( pACB );
+    for( i=0; i<500; 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);
+
+    ResetDevParam( pACB );
+    DoingSRB_Done( pACB );
+    pACB->pActiveDCB = NULL;
+
+    pACB->ACBFlag = 0;
+    DoWaitingSRB( pACB );
+
+    restore_flags(flags);
+#ifdef DC390_DEBUG1
+    printk("DC390: RESET1,");
+#endif
+    return( SCSI_RESET_SUCCESS );
+}
+
+
+#include "scsiiom.c"
+
+
+/***********************************************************************
+ * 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 )
+{
+    PEEprom	prom;
+    UCHAR	bval;
+    USHORT	index;
+
+    if( pACB->DeviceCnt == 0 )
+    {
+	pACB->pLinkDCB = pDCB;
+	pACB->pDCBRunRobin = pDCB;
+	pDCB->pNextDCB = pDCB;
+	pPrevDCB = pDCB;
+    }
+    else
+	pPrevDCB->pNextDCB = pDCB;
+
+    pDCB->pDCBACB = pACB;
+    pDCB->QIORBCnt = 0;
+    pDCB->UnitSCSIID = cmd->target;
+    pDCB->UnitSCSILUN = cmd->lun;
+    pDCB->pWaitingSRB = NULL;
+    pDCB->pGoingSRB = NULL;
+    pDCB->GoingSRBCnt = 0;
+    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];
+    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;
+    }
+
+    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->CtrlR3 = FAST_CLK;
+
+    pDCB->CtrlR4 = EATER_25NS;
+    if( pDCB->AdpMode & ACTIVE_NEGATION)
+	pDCB->CtrlR4 |= NEGATE_REQACKDATA;
+}
+
+
+/***********************************************************************
+ * 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 )
+{
+#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
+}
+
+
+void DC390_linkSRB( PACB pACB )
+{
+    USHORT  count, i;
+    PSRB    psrb;
+
+    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 );
+    }
+}
+
+
+/***********************************************************************
+ * Function : static void DC390_initACB
+ *
+ * Purpose :  initialize the internal structures for a given SCSI host
+ *
+ * Inputs : psh - pointer to this host adapter's structure
+ *
+ ***********************************************************************/
+void DC390_initACB( PSH psh, ULONG io_port, UCHAR Irq, USHORT index )
+{
+    PACB    pACB;
+    USHORT  i;
+
+    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->io_port = io_port;
+    psh->n_io_port = 0x80;
+    psh->irq = Irq;
+
+    pACB = (PACB) psh->hostdata;
+
+#ifndef VERSION_ELF_1_2_13
+    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;
+
+    pACB->pScsiHost = psh;
+    pACB->IOPortBase = (USHORT) io_port;
+    pACB->pLinkDCB = NULL;
+    pACB->pDCBRunRobin = NULL;
+    pACB->pActiveDCB = NULL;
+    pACB->pFreeSRB = pACB->SRB_array;
+    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;
+    pACB->DeviceCnt = 0;
+    pACB->IRQLevel = Irq;
+    pACB->TagMaxNum = eepromBuf[index][EE_TAG_CMD_NUM] << 2;
+    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->pTmpSRB = &pACB->TmpSRB;
+    DC390_initSRB( pACB->pTmpSRB );
+    for(i=0; i<MAX_SCSI_ID; i++)
+	pACB->DCBmap[i] = 0;
+}
+
+
+/***********************************************************************
+ * Function : static int DC390_initAdapter
+ *
+ * Purpose :  initialize the SCSI chip ctrl registers
+ *
+ * Inputs : psh - pointer to this host adapter's structure
+ *
+ ***********************************************************************/
+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;
+	}
+    }
+
+    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, "tmscsim", NULL))
+#endif
+	{
+	    printk("DC390: register IRQ 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;
+    }
+}
+
+
+void
+DC390_ReadEEprom( USHORT mechnum, USHORT index )
+{
+    UCHAR   regval,cmd;
+    PUSHORT ptr;
+    USHORT  i;
+
+    ptr = (PUSHORT) &eepromBuf[index][0];
+    cmd = EEPROM_READ;
+    for(i=0; i<0x40; i++)
+    {
+	DC390_EnDisableCE(ENABLE_CE, mechnum, &regval);
+	DC390_Prepare(mechnum, &regval, cmd);
+	*ptr = EEpromGetData1(mechnum);
+	ptr++;
+	cmd++;
+	DC390_EnDisableCE(DISABLE_CE,mechnum,&regval);
+    }
+}
+
+
+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;
+    else
+	rc = -1;
+    return( rc );
+}
+
+
+USHORT
+DC390_ToMech( USHORT Mechnum, USHORT BusDevFunNum )
+{
+    USHORT devnum;
+
+    devnum = BusDevFunNum;
+
+    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)
+ *
+ * Purpose :  initialize the internal structures for a given SCSI host
+ *
+ * Inputs : host - pointer to this host adapter's structure/
+ *
+ * Preconditions : when this function is called, the chip_type
+ *	field of the pACB structure MUST have been set.
+ ***********************************************************************/
+
+static int
+DC390_init (PSHT psht, ULONG io_port, UCHAR Irq, USHORT index, USHORT MechNum)
+{
+    PSH   psh;
+    PACB  pACB;
+
+    if( !DC390_CheckEEpromCheckSum( MechNum, 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 DC390_DEBUG0
+	printk("DC390: pSH = %8x,", (UINT) psh);
+	printk("DC390: Index %02i,", index);
+#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;
+	    }
+
+#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
+
+	}
+	else
+	{
+	    pSH_start = NULL;
+	    scsi_unregister( psh );
+	    return( -1 );
+	}
+	return( 0 );
+    }
+    else
+    {
+	printk("DC390_init: EEPROM reading error!\n");
+	return( -1 );
+    }
+}
+
+
+/***********************************************************************
+ * Function : int DC390_detect(Scsi_Host_Template *psht)
+ *
+ * Purpose : detects and initializes AMD53C974 SCSI chips
+ *	     that were autoprobed, overridden on the LILO command line,
+ *	     or specified at compile time.
+ *
+ * Inputs : psht - template for this SCSI adapter
+ *
+ * Returns : number of host adapters detected
+ *
+ ***********************************************************************/
+
+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
+
+    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
+
+    InitialTime = 1;
+    pSHT_start = psht;
+    pACB_start = NULL;
+
+    MechNum = 1;
+    for( ; (MechNum < 3) && (!adaptCnt); MechNum++)
+    {
+	BusDevFunNum = 0;
+	for (; adaptCnt < MAX_ADAPTER_NUM ;)
+	{
+	    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;
+	}
+    }
+
+#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) )
+	    {
+		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;
+	}
+    }
+#endif
+
+    InitialTime = 0;
+    adapterCnt = adaptCnt;
+    return( adaptCnt );
+}
+
+
+#ifndef VERSION_ELF_1_2_13
+
+/********************************************************************
+ * Function: tmscsim_set_info()
+ *
+ * Purpose: Set adapter info (!)
+ *
+ * Not yet implemented
+ *
+ *******************************************************************/
+
+int tmscsim_set_info(char *buffer, int length, struct Scsi_Host *shpnt)
+{
+  return(-ENOSYS);  /* Currently this is a no-op */
+}
+
+/********************************************************************
+ * Function: tmscsim_proc_info(char* buffer, char **start,
+ *			     off_t offset, int length, int hostno, int inout)
+ *
+ * Purpose: return SCSI Adapter/Device Info
+ *
+ * Input: buffer: Pointer to a buffer where to write info
+ *	  start :
+ *	  offset:
+ *	  hostno: Host adapter index
+ *	  inout : Read (=0) or set(!=0) info
+ *
+ * Output: buffer: contains info
+ *	   length; length of info in buffer
+ *
+ * return value: length
+ *
+ ********************************************************************/
+
+/* 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  ")
+
+int tmscsim_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; */
+
+  acbpnt = pACB_start;
+
+  while(acbpnt != (PACB)-1)
+     {
+	shpnt = acbpnt->pScsiHost;
+	if (shpnt->host_no == hostno) break;
+	acbpnt = acbpnt->pNextACB;
+     }
+
+  if (acbpnt == (PACB)-1) return(-ESRCH);
+  if(!shpnt) return(-ESRCH);
+
+  if(inout) // Has data been written to the file ?
+    return(tmscsim_set_info(buffer, length, shpnt));
+
+  SPRINTF("Tekram DC390(T) PCI SCSI Host Adadpter, ");
+  SPRINTF("Driver Version 1.10, 1996/12/05\n");
+
+  save_flags(flags);
+  cli();
+
+  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("Un ID LUN Prty Sync DsCn SndS TagQ NegoPeriod SyncSpeed SyncOffs\n");
+
+  dcbpnt = acbpnt->pLinkDCB;
+  for (dev = 0; dev < acbpnt->DeviceCnt; 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)
+      {
+	 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));
+      }
+      else SPRINTF("\n");
+      /* Add more info ...*/
+      dcbpnt = dcbpnt->pNextDCB;
+     }
+
+  restore_flags(flags);
+  *start = buffer + offset;
+
+  if (pos - buffer < offset)
+    return 0;
+  else if (pos - buffer - offset < length)
+    return pos - buffer - offset;
+  else
+    return length;
+}
+#endif /* VERSION_ELF_1_2_13 */
+
+
+#ifdef MODULE
+
+/***********************************************************************
+ * 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)
+{
+    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
+
+    bval = inb(ioport+CtrlReg1);
+    bval |= DIS_INT_ON_SCSI_RST;
+    outb(bval,ioport+CtrlReg1);  /* disable interrupt */
+    DC390_ResetSCSIBus( pACB );
+
+    restore_flags (flags);
+    return( 0 );
+}
+
+
+int DC390_release(struct Scsi_Host *host)
+{
+    int irq_count;
+    struct Scsi_Host *tmp;
+
+    DC390_shutdown (host);
+
+    if (host->irq != IRQ_NONE)
+    {
+	for (irq_count = 0, tmp = pSH_start; tmp; tmp = tmp->next)
+	{
+	    if ( tmp->irq == 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
+	    free_irq(host->irq,NULL);
+#else
+	    free_irq(host->irq);
+#endif
+	 }
+    }
+
+    release_region(host->io_port,host->n_io_port);
+
+    return( 1 );
+}
+
+Scsi_Host_Template driver_template = DC390_T;
+#include "scsi_module.c"
+#endif /* def MODULE */
+
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/tmscsim.h linux/drivers/scsi/tmscsim.h
--- v2.0.30/linux/drivers/scsi/tmscsim.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/tmscsim.h	Thu Aug 14 10:31:20 1997
@@ -0,0 +1,680 @@
+/***********************************************************************
+;*	File Name : TMSCSIM.H					       *
+;*		    TEKRAM DC-390(T) PCI SCSI Bus Master Host Adapter  *
+;*		    Device Driver				       *
+;***********************************************************************/
+
+#ifndef TMSCSIM_H
+#define TMSCSIM_H
+
+#define IRQ_NONE 255
+
+typedef unsigned char	UCHAR;
+typedef unsigned short	USHORT;
+typedef unsigned long	ULONG;
+typedef unsigned int	UINT;
+
+typedef UCHAR		*PUCHAR;
+typedef USHORT		*PUSHORT;
+typedef ULONG		*PULONG;
+typedef Scsi_Host_Template  *PSHT;
+typedef struct Scsi_Host    *PSH;
+typedef Scsi_Device	*PSCSIDEV;
+typedef Scsi_Cmnd	*PSCSICMD;
+typedef void		*PVOID;
+typedef struct scatterlist  *PSGL, SGL;
+
+
+/*;-----------------------------------------------------------------------*/
+typedef  struct  _SyncMsg
+{
+UCHAR		ExtendMsg;
+UCHAR		ExtMsgLen;
+UCHAR		SyncXferReq;
+UCHAR		Period;
+UCHAR		ReqOffset;
+} SyncMsg;
+/*;-----------------------------------------------------------------------*/
+typedef  struct  _Capacity
+{
+ULONG		BlockCount;
+ULONG		BlockLength;
+} Capacity;
+/*;-----------------------------------------------------------------------*/
+typedef  struct  _SGentry
+{
+ULONG		SGXferDataPtr;
+ULONG		SGXferDataLen;
+} SGentry;
+
+typedef  struct  _SGentry1
+{
+ULONG		SGXLen;
+ULONG		SGXPtr;
+} 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
+;-----------------------------------------------------------------------
+*/
+struct	_SRB
+{
+UCHAR		CmdBlock[12];
+
+struct _SRB	*pNextSRB;
+struct _DCB	*pSRBDCB;
+PSCSICMD	pcmd;
+PSGL		pSegmentList;
+
+ULONG		PhysSRB;
+ULONG		TotalXferredLen;
+ULONG		SGPhysAddr;	/*;a segment starting address */
+ULONG		SGToBeXferLen;	/*; to be xfer length */
+
+SGL		Segmentx;	/* make a one entry of S/G list table */
+
+PUCHAR		pMsgPtr;
+USHORT		SRBState;
+USHORT		Revxx2; 	/* ??? */
+
+UCHAR		MsgInBuf[6];
+UCHAR		MsgOutBuf[6];
+
+UCHAR		AdaptStatus;
+UCHAR		TargetStatus;
+UCHAR		MsgCnt;
+UCHAR		EndMessage;
+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];
+};
+
+typedef  struct  _SRB	 DC390_SRB, *PSRB;
+
+/*
+;-----------------------------------------------------------------------
+; Device Control Block
+;-----------------------------------------------------------------------
+*/
+struct	_DCB
+{
+struct _DCB	*pNextDCB;
+struct _ACB	*pDCBACB;
+
+PSCSICMD	pQIORBhead;
+PSCSICMD	pQIORBtail;
+PSCSICMD	AboIORBhead;
+PSCSICMD	AboIORBtail;
+USHORT		QIORBCnt;
+USHORT		AboIORBcnt;
+
+PSRB		pWaitingSRB;
+PSRB		pWaitLast;
+PSRB		pGoingSRB;
+PSRB		pGoingLast;
+PSRB		pActiveSRB;
+USHORT		GoingSRBCnt;
+USHORT		WaitSRBCnt;	/* ??? */
+
+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		IdentifyMsg;
+UCHAR		CtrlR1;
+UCHAR		CtrlR3;
+UCHAR		CtrlR4;
+
+UCHAR		InqDataBuf[8];
+UCHAR		CapacityBuf[8];
+UCHAR		DevMode;
+UCHAR		AdpMode;
+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 */
+};
+
+typedef  struct  _DCB	 DC390_DCB, *PDCB;
+/*
+;-----------------------------------------------------------------------
+; Adapter Control Block
+;-----------------------------------------------------------------------
+*/
+struct	_ACB
+{
+ULONG		PhysACB;
+PSH		pScsiHost;
+struct _ACB	*pNextACB;
+USHORT		IOPortBase;
+USHORT		Revxx1; 	/* ??? */
+
+PDCB		pLinkDCB;
+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;
+
+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=	*/
+DC390_SRB	TmpSRB;
+};
+
+typedef  struct  _ACB	 DC390_ACB, *PACB;
+
+/*;-----------------------------------------------------------------------*/
+
+
+#define BIT31	0x80000000
+#define BIT30	0x40000000
+#define BIT29	0x20000000
+#define BIT28	0x10000000
+#define BIT27	0x08000000
+#define BIT26	0x04000000
+#define BIT25	0x02000000
+#define BIT24	0x01000000
+#define BIT23	0x00800000
+#define BIT22	0x00400000
+#define BIT21	0x00200000
+#define BIT20	0x00100000
+#define BIT19	0x00080000
+#define BIT18	0x00040000
+#define BIT17	0x00020000
+#define BIT16	0x00010000
+#define BIT15	0x00008000
+#define BIT14	0x00004000
+#define BIT13	0x00002000
+#define BIT12	0x00001000
+#define BIT11	0x00000800
+#define BIT10	0x00000400
+#define BIT9	0x00000200
+#define BIT8	0x00000100
+#define BIT7	0x00000080
+#define BIT6	0x00000040
+#define BIT5	0x00000020
+#define BIT4	0x00000010
+#define BIT3	0x00000008
+#define BIT2	0x00000004
+#define BIT1	0x00000002
+#define BIT0	0x00000001
+
+/*;---UnitCtrlFlag */
+#define UNIT_ALLOCATED	BIT0
+#define UNIT_INFO_CHANGED BIT1
+#define FORMATING_MEDIA BIT2
+#define UNIT_RETRY	BIT3
+
+/*;---UnitFlags */
+#define DASD_SUPPORT	BIT0
+#define SCSI_SUPPORT	BIT1
+#define ASPI_SUPPORT	BIT2
+
+/*;----SRBState machine definition */
+#define SRB_FREE	0
+#define SRB_WAIT	BIT0
+#define SRB_READY	BIT1
+#define SRB_MSGOUT	BIT2	/*;arbitration+msg_out 1st byte*/
+#define SRB_MSGIN	BIT3
+#define SRB_MSGIN_MULTI BIT4
+#define SRB_COMMAND	BIT5
+#define SRB_START_	BIT6	/*;arbitration+msg_out+command_out*/
+#define SRB_DISCONNECT	BIT7
+#define SRB_DATA_XFER	BIT8
+#define SRB_XFERPAD	BIT9
+#define SRB_STATUS	BIT10
+#define SRB_COMPLETED	BIT11
+#define SRB_ABORT_SENT	BIT12
+#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
+#define OVER_RUN	BIT2
+#define UNDER_RUN	BIT3
+#define PARITY_ERROR	BIT4
+#define SRB_ERROR	BIT5
+
+/*;---SRBFlag */
+#define DATAOUT 	BIT7
+#define DATAIN		BIT6
+#define RESIDUAL_VALID	BIT5
+#define ENABLE_TIMER	BIT4
+#define RESET_DEV0	BIT2
+#define ABORT_DEV	BIT1
+#define AUTO_REQSENSE	BIT0
+
+/*;---Adapter status */
+#define H_STATUS_GOOD	 0
+#define H_SEL_TIMEOUT	 0x11
+#define H_OVER_UNDER_RUN 0x12
+#define H_UNEXP_BUS_FREE 0x13
+#define H_TARGET_PHASE_F 0x14
+#define H_INVALID_CCB_OP 0x16
+#define H_LINK_CCB_BAD	 0x17
+#define H_BAD_TARGET_DIR 0x18
+#define H_DUPLICATE_CCB  0x19
+#define H_BAD_CCB_OR_SG  0x1A
+#define H_ABORT 	 0x0FF
+
+/*; SCSI Status byte codes*/
+#define SCSI_STAT_GOOD		0x0	/*;  Good status */
+#define SCSI_STAT_CHECKCOND	0x02	/*;  SCSI Check Condition */
+#define SCSI_STAT_CONDMET	0x04	/*;  Condition Met */
+#define SCSI_STAT_BUSY		0x08	/*;  Target busy status */
+#define SCSI_STAT_INTER 	0x10	/*;  Intermediate status */
+#define SCSI_STAT_INTERCONDMET	0x14	/*;  Intermediate condition met */
+#define SCSI_STAT_RESCONFLICT	0x18	/*;  Reservation conflict */
+#define SCSI_STAT_CMDTERM	0x22	/*;  Command Terminated */
+#define SCSI_STAT_QUEUEFULL	0x28	/*;  Queue Full */
+
+#define SCSI_STAT_UNEXP_BUS_F	0xFD	/*;  Unexpect Bus Free */
+#define SCSI_STAT_BUS_RST_DETECT 0xFE	/*;  Scsi Bus Reset detected */
+#define SCSI_STAT_SEL_TIMEOUT	0xFF	/*;  Selection Time out */
+
+/*;---Sync_Mode */
+#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 EN_ATN_STOP	BIT5
+
+#define SYNC_NEGO_OFFSET 15
+
+/*;---SCSI bus phase*/
+#define SCSI_DATA_OUT	0
+#define SCSI_DATA_IN	1
+#define SCSI_COMMAND	2
+#define SCSI_STATUS_	3
+#define SCSI_NOP0	4
+#define SCSI_NOP1	5
+#define SCSI_MSG_OUT	6
+#define SCSI_MSG_IN	7
+
+/*;----SCSI MSG BYTE*/
+#define MSG_COMPLETE		0x00
+#define MSG_EXTENDED		0x01
+#define MSG_SAVE_PTR		0x02
+#define MSG_RESTORE_PTR 	0x03
+#define MSG_DISCONNECT		0x04
+#define MSG_INITIATOR_ERROR	0x05
+#define MSG_ABORT		0x06
+#define MSG_REJECT_		0x07
+#define MSG_NOP 		0x08
+#define MSG_PARITY_ERROR	0x09
+#define MSG_LINK_CMD_COMPL	0x0A
+#define MSG_LINK_CMD_COMPL_FLG	0x0B
+#define MSG_BUS_RESET		0x0C
+#define MSG_ABORT_TAG		0x0D
+#define MSG_SIMPLE_QTAG 	0x20
+#define MSG_HEAD_QTAG		0x21
+#define MSG_ORDER_QTAG		0x22
+#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
+#define RETURN_MASK		0xFF0000
+
+/*
+**  Inquiry Data format
+*/
+
+typedef struct	_SCSIInqData { /* INQ */
+
+	UCHAR	 DevType;		/* Periph Qualifier & Periph Dev Type*/
+	UCHAR	 RMB_TypeMod;		/* rem media bit & Dev Type Modifier */
+	UCHAR	 Vers;			/* ISO, ECMA, & ANSI versions	     */
+	UCHAR	 RDF;			/* AEN, TRMIOP, & response data format*/
+	UCHAR	 AddLen;		/* length of additional data	     */
+	UCHAR	 Res1;			/* reserved			     */
+	UCHAR	 Res2;			/* reserved			     */
+	UCHAR	 Flags; 		/* RelADr,Wbus32,Wbus16,Sync,etc.    */
+	UCHAR	 VendorID[8];		/* Vendor Identification	     */
+	UCHAR	 ProductID[16]; 	/* Product Identification	     */
+	UCHAR	 ProductRev[4]; 	/* Product Revision		     */
+
+
+} SCSI_INQDATA, *PSCSI_INQDATA;
+
+
+/*  Inquiry byte 0 masks */
+
+
+#define SCSI_DEVTYPE	    0x1F      /* Peripheral Device Type 	    */
+#define SCSI_PERIPHQUAL     0xE0      /* Peripheral Qualifier		    */
+
+
+/*  Inquiry byte 1 mask */
+
+#define SCSI_REMOVABLE_MEDIA  0x80    /* Removable Media bit (1=removable)  */
+
+
+/*  Peripheral Device Type definitions */
+
+#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    */
+
+/*
+** Inquiry flag definitions (Inq data byte 7)
+*/
+
+#define SCSI_INQ_RELADR       0x80    /* device supports relative addressing*/
+#define SCSI_INQ_WBUS32       0x40    /* device supports 32 bit data xfers  */
+#define SCSI_INQ_WBUS16       0x20    /* device supports 16 bit data xfers  */
+#define SCSI_INQ_SYNC	      0x10    /* device supports synchronous xfer   */
+#define SCSI_INQ_LINKED       0x08    /* device supports linked commands    */
+#define SCSI_INQ_CMDQUEUE     0x02    /* device supports command queueing   */
+#define SCSI_INQ_SFTRE	      0x01    /* device supports soft resets */
+
+
+/*
+;==========================================================
+; EEPROM byte offset
+;==========================================================
+*/
+typedef  struct  _EEprom
+{
+UCHAR	EE_MODE1;
+UCHAR	EE_SPEED;
+UCHAR	xx1;
+UCHAR	xx2;
+} EEprom, *PEEprom;
+
+#define EE_ADAPT_SCSI_ID 64
+#define EE_MODE2	65
+#define EE_DELAY	66
+#define EE_TAG_CMD_NUM	67
+
+/*; EE_MODE1 bits definition*/
+#define PARITY_CHK_	BIT0
+#define SYNC_NEGO_	BIT1
+#define EN_DISCONNECT_	BIT2
+#define SEND_START_	BIT3
+#define TAG_QUEUING_	BIT4
+
+/*; EE_MODE2 bits definition*/
+#define MORE2_DRV	BIT0
+#define GREATER_1G	BIT1
+#define RST_SCSI_BUS	BIT2
+#define ACTIVE_NEGATION BIT3
+#define NO_SEEK 	BIT4
+#define LUN_CHECK	BIT5
+
+#define ENABLE_CE	1
+#define DISABLE_CE	0
+#define EEPROM_READ	0x80
+
+/*
+;==========================================================
+;	AMD 53C974 Registers bit Definition
+;==========================================================
+*/
+/*
+;====================
+; SCSI Register
+;====================
+*/
+
+/*; Command Reg.(+0CH) */
+#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_W_ATN_STOP		0x43
+#define EN_SEL_RESEL		0x44
+#define SEL_W_ATN2		0x46
+#define DATA_XFER_CMD		INFO_XFER_CMD
+
+
+/*; SCSI Status Reg.(+10H) */
+#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)
+
+/*; Interrupt Status Reg.(+14H) */
+#define SCSI_RESET		BIT7
+#define INVALID_CMD		BIT6
+#define DISCONNECTED		BIT5
+#define SERVICE_REQUEST 	BIT4
+#define SUCCESSFUL_OP		BIT3
+#define RESELECTED		BIT2
+#define SEL_ATTENTION		BIT1
+#define SELECTED		BIT0
+
+/*; Internal State Reg.(+18H) */
+#define SYNC_OFFSET_FLAG	BIT3
+#define INTRN_STATE_MASK	(BIT2+BIT1+BIT0)
+
+/*; Clock Factor Reg.(+24H) */
+#define CLK_FREQ_40MHZ		0
+#define CLK_FREQ_35MHZ		(BIT2+BIT1+BIT0)
+#define CLK_FREQ_30MHZ		(BIT2+BIT1)
+#define CLK_FREQ_25MHZ		(BIT2+BIT0)
+#define CLK_FREQ_20MHZ		BIT2
+#define CLK_FREQ_15MHZ		(BIT1+BIT0)
+#define CLK_FREQ_10MHZ		BIT1
+
+/*; Control Reg. 1(+20H) */
+#define EXTENDED_TIMING 	BIT7
+#define DIS_INT_ON_SCSI_RST	BIT6
+#define PARITY_ERR_REPO 	BIT4
+#define SCSI_ID_ON_BUS		(BIT2+BIT1+BIT0)
+
+/*; Control Reg. 2(+2CH) */
+#define EN_FEATURE		BIT6
+#define EN_SCSI2_CMD		BIT3
+
+/*; Control Reg. 3(+30H) */
+#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) */
+#define EATER_12NS		0
+#define EATER_25NS		BIT7
+#define EATER_35NS		BIT6
+#define EATER_0NS		(BIT7+BIT6)
+#define NEGATE_REQACKDATA	BIT2
+#define NEGATE_REQACK		BIT3
+/*
+;====================
+; DMA Register
+;====================
+*/
+/*; DMA Command Reg.(+40H) */
+#define READ_DIRECTION		BIT7
+#define WRITE_DIRECTION 	0
+#define EN_DMA_INT		BIT6
+#define MAP_TO_MDL		BIT5
+#define DIAGNOSTIC		BIT4
+#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) */
+#define PCI_MS_ABORT		BIT6
+#define BLAST_COMPLETE		BIT5
+#define SCSI_INTERRUPT		BIT4
+#define DMA_XFER_DONE		BIT3
+#define DMA_XFER_ABORT		BIT2
+#define DMA_XFER_ERROR		BIT1
+#define POWER_DOWN		BIT0
+
+/*
+; DMA SCSI Bus and Ctrl.(+70H)
+;EN_INT_ON_PCI_ABORT
+*/
+
+/*
+;==========================================================
+; SCSI Chip register address offset
+;==========================================================
+*/
+#define CtcReg_Low	0x00
+#define CtcReg_Mid	0x04
+#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 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))
+
+
+#endif /* TMSCSIM_H */
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/u14-34f.c linux/drivers/scsi/u14-34f.c
--- v2.0.30/linux/drivers/scsi/u14-34f.c	Wed Feb 26 10:56:51 1997
+++ linux/drivers/scsi/u14-34f.c	Wed Aug  6 11:41:23 1997
@@ -1,6 +1,11 @@
 /*
  *      u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters.
  *
+ *      17 May 1997 rev. 3.10 for linux 2.0.30 and 2.1.38
+ *          Use of serial_number_at_timeout in abort and reset processing.
+ *          Use of the __initfunc and __initdata macro in setup code.
+ *          Minor cleanups in the list_statistics code.
+ *
  *      24 Feb 1997 rev. 3.00 for linux 2.0.29 and 2.1.26
  *          When loading as a module, parameter passing is now supported
  *          both in 2.0 and in 2.1 style.
@@ -220,10 +225,12 @@
  *  between increasing or decreasing by minimizing the seek distance between
  *  the sector of the commands just completed and the sector of the first 
  *  command in the list to be sorted. 
- *  Trivial math assures that if there are (Q-1) outstanding request for
- *  random seeks over S sectors, the unsorted average seek distance is S/2,
- *  while the sorted average seek distance is S/(Q-1). The seek distance is
- *  hence divided by a factor (Q-1)/2.
+ *  Trivial math assures that the unsorted average seek distance when doing
+ *  random seeks over S sectors is S/3.
+ *  When (Q-1) requests are uniformly distributed over S sectors, the average
+ *  distance between two adjacent requests is S/((Q-1) + 1), so the sorted
+ *  average seek distance for (Q-1) random requests over S sectors is S/Q.
+ *  The elevator sorting hence divides the seek distance by a factor Q/3.
  *  The above pure geometric remarks are valid in all cases and the 
  *  driver effectively reduces the seek distance by the predicted factor
  *  when there are Q concurrent read i/o operations on the device, but this
@@ -273,8 +280,16 @@
 #include <asm/dma.h>
 #include <asm/irq.h>
 #include "u14-34f.h"
-#include<linux/stat.h>
-#include<linux/config.h>
+#include <linux/stat.h>
+#include <linux/config.h>
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,36)
+#include <linux/init.h>
+#else
+#define __initfunc(A) A
+#define __initdata
+#define __init
+#endif
 
 struct proc_dir_entry proc_scsi_u14_34f = {
     PROC_SCSI_U14_34F, 6, "u14_34f",
@@ -419,7 +434,7 @@
 static const char *driver_name = "Ux4F";
 static unsigned int irqlist[MAX_IRQ], calls[MAX_IRQ];
 
-static unsigned int io_port[] = { 
+static unsigned int io_port[] __initdata = { 
 
    /* Space for MAX_INT_PARAM ports usable while loading as a module */
    SKIP,    SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
@@ -534,8 +549,7 @@
    return;
 }
 
-static inline int wait_on_busy(unsigned int iobase) {
-   unsigned int loop = MAXLOOP;
+static inline int wait_on_busy(unsigned int iobase, unsigned int loop) {
 
    while (inb(iobase + REG_LCL_INTR) & BSY_ASSERTED)
       if (--loop == 0) return TRUE;
@@ -556,7 +570,7 @@
    cpp->scsi_cdbs_len = 6;
    cpp->scsi_cdbs[0] = HA_CMD_INQUIRY;
 
-   if (wait_on_busy(sh[j]->io_port)) {
+   if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
       printk("%s: board_inquiry, adapter busy.\n", BN(j));
       return TRUE;
       }
@@ -586,8 +600,8 @@
    return FALSE;
 }
 
-static inline int port_detect(unsigned int port_base, unsigned int j, 
-                              Scsi_Host_Template *tpnt) {
+__initfunc (static inline int port_detect \
+      (unsigned int port_base, unsigned int j, Scsi_Host_Template *tpnt)) {
    unsigned char irq, dma_channel, subversion, i;
    unsigned char in_byte;
    char *bus_type, dma_name[16];
@@ -748,7 +762,7 @@
 	 }
       }
 
-   if (dma_channel == NO_DMA) sprintf(dma_name, "%s", "NO DMA");
+   if (dma_channel == NO_DMA) sprintf(dma_name, "%s", "BMST");
    else                       sprintf(dma_name, "DMA %u", dma_channel);
 
    for (i = 0; i < sh[j]->can_queue; i++)
@@ -781,7 +795,7 @@
    return TRUE;
 }
 
-void u14_34f_setup(char *str, int *ints) {
+__initfunc (void u14_34f_setup(char *str, int *ints)) {
    int i, argc = ints[0];
    char *cur = str, *pc;
 
@@ -813,7 +827,7 @@
    return;
 }
 
-int u14_34f_detect(Scsi_Host_Template *tpnt) {
+__initfunc (int u14_34f_detect(Scsi_Host_Template *tpnt)) {
    unsigned long flags;
    unsigned int j = 0, k;
 
@@ -983,12 +997,12 @@
    if (linked_comm && SCpnt->device->queue_depth > 2
                                      && TLDEV(SCpnt->device->type)) {
       HD(j)->cp_stat[i] = READY;
-      flush_dev(SCpnt->device, 0, j, FALSE);
+      flush_dev(SCpnt->device, SCpnt->request.sector, j, FALSE);
       restore_flags(flags);
       return 0;
       }
 
-   if (wait_on_busy(sh[j]->io_port)) {
+   if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
       SCpnt->result = DID_ERROR << 16;
       SCpnt->host_scribble = NULL;
       printk("%s: qcomm, target %d.%d:%d, pid %ld, adapter busy, DID_ERROR,"\
@@ -1018,7 +1032,8 @@
    cli();
    j = ((struct hostdata *) SCarg->host->hostdata)->board_number;
 
-   if (SCarg->host_scribble == NULL) {
+   if (SCarg->host_scribble == NULL
+       || SCarg->serial_number != SCarg->serial_number_at_timeout) {
       printk("%s: abort, target %d.%d:%d, pid %ld inactive.\n",
 	     BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
       restore_flags(flags);
@@ -1032,7 +1047,7 @@
    if (i >= sh[j]->can_queue)
       panic("%s: abort, invalid SCarg->host_scribble.\n", BN(j));
 
-   if (wait_on_busy(sh[j]->io_port)) {
+   if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
       printk("%s: abort, timeout error.\n", BN(j));
       restore_flags(flags);
       return SCSI_ABORT_ERROR;
@@ -1101,13 +1116,19 @@
    if (SCarg->host_scribble == NULL)
       printk("%s: reset, pid %ld inactive.\n", BN(j), SCarg->pid);
 
+   if (SCarg->serial_number != SCarg->serial_number_at_timeout) {
+      printk("%s: reset, pid %ld, reset not running.\n", BN(j), SCarg->pid);
+      restore_flags(flags);
+      return SCSI_RESET_NOT_RUNNING;
+      }
+
    if (HD(j)->in_reset) {
       printk("%s: reset, exit, already in reset.\n", BN(j));
       restore_flags(flags);
       return SCSI_RESET_ERROR;
       }
 
-   if (wait_on_busy(sh[j]->io_port)) {
+   if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
       printk("%s: reset, exit, timeout error.\n", BN(j));
       restore_flags(flags);
       return SCSI_RESET_ERROR;
@@ -1158,7 +1179,7 @@
       if (SCpnt == SCarg) arg_done = TRUE;
       }
 
-   if (wait_on_busy(sh[j]->io_port)) {
+   if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
       printk("%s: reset, cannot reset, timeout error.\n", BN(j));
       restore_flags(flags);
       return SCSI_RESET_ERROR;
@@ -1271,7 +1292,7 @@
    unsigned int rev = FALSE, s = TRUE, r = TRUE;
    unsigned int input_only = TRUE, overlap = FALSE;
    unsigned long sl[n_ready], pl[n_ready], ll[n_ready];
-   unsigned long maxsec = 0, minsec = ULONG_MAX, seek = 0;
+   unsigned long maxsec = 0, minsec = ULONG_MAX, seek = 0, iseek = 0;
 
    static unsigned int flushcount = 0, batchcount = 0, sortcount = 0;
    static unsigned int readycount = 0, ovlcount = 0, inputcount = 0;
@@ -1282,8 +1303,8 @@
       printk("fc %d bc %d ic %d oc %d rc %d rs %d sc %d re %d"\
              " av %ldK as %ldK.\n", flushcount, batchcount, inputcount,
              ovlcount, readycount, readysorted, sortcount, revcount,
-             seeknosort / (readycount - batchcount + 1), 
-             seeksorted / (readycount - batchcount + 1));
+             seeknosort / (readycount + 1), 
+             seeksorted / (readycount + 1));
 
    if (n_ready <= 1) return;
 
@@ -1311,6 +1332,10 @@
 
       }
 
+   if (link_statistics) {
+      if (cursec > sl[0]) seek += cursec - sl[0]; else seek += sl[0] - cursec;
+      }
+
    if (cursec > ((maxsec + minsec) / 2)) rev = TRUE;
 
    if (!((rev && r) || (!rev && s))) sort(sl, il, n_ready, rev);
@@ -1328,10 +1353,11 @@
    if (overlap) sort(pl, il, n_ready, FALSE);
 
    if (link_statistics) {
+      if (cursec > sl[0]) iseek = cursec - sl[0]; else iseek = sl[0] - cursec;
       batchcount++; readycount += n_ready, seeknosort += seek / 1024; 
       if (input_only) inputcount++;
       if (overlap) { ovlcount++; seeksorted += seek / 1024; }
-      else seeksorted += (maxsec - minsec) / 1024;
+      else seeksorted += (iseek + maxsec - minsec) / 1024;
       if (rev && !r)     {  revcount++; readysorted += n_ready; }
       if (!rev && !s)    { sortcount++; readysorted += n_ready; }
       }
@@ -1375,7 +1401,7 @@
    for (n = 0; n < n_ready; n++) {
       k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
 
-      if (wait_on_busy(sh[j]->io_port)) {
+      if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
          printk("%s: %s, target %d.%d:%d, pid %ld, Mbox %d, adapter"\
                 " busy, will abort.\n", BN(j), (ihdlr ? "ihdlr" : "qcomm"),
                 SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid, k);
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/u14-34f.h linux/drivers/scsi/u14-34f.h
--- v2.0.30/linux/drivers/scsi/u14-34f.h	Wed Feb 26 10:56:51 1997
+++ linux/drivers/scsi/u14-34f.h	Wed Aug  6 11:41:23 1997
@@ -11,7 +11,7 @@
 int u14_34f_reset(Scsi_Cmnd *, unsigned int);
 int u14_34f_biosparam(Disk *, kdev_t, int *);
 
-#define U14_34F_VERSION "3.00.09"
+#define U14_34F_VERSION "3.10.00"
 
 #define ULTRASTOR_14_34F {                                            \
 		NULL, /* Ptr for modules */                           \
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/ultrastor.c linux/drivers/scsi/ultrastor.c
--- v2.0.30/linux/drivers/scsi/ultrastor.c	Sun May 12 23:01:23 1996
+++ linux/drivers/scsi/ultrastor.c	Tue Aug 12 20:27:06 1997
@@ -1144,8 +1144,13 @@
     else
 	printk("US14F: interrupt: unexpected interrupt\n");
 
-    if (config.slot ? inb(config.icm_address - 1) : (inb(SYS_DOORBELL_INTR(config.doorbell_address)) & 1))
+    if (config.slot ? inb(config.icm_address - 1) :
+       (inb(SYS_DOORBELL_INTR(config.doorbell_address)) & 1))
+#if (ULTRASTOR_DEBUG & UD_MULTI_CMD)
       printk("Ux4F: multiple commands completed\n");
+#else
+      ;
+#endif
 
 #if (ULTRASTOR_DEBUG & UD_INTERRUPT)
     printk("USx4F: interrupt: returning\n");
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/wd7000.c linux/drivers/scsi/wd7000.c
--- v2.0.30/linux/drivers/scsi/wd7000.c	Fri Sep 20 07:00:34 1996
+++ linux/drivers/scsi/wd7000.c	Mon Aug 11 13:37:24 1997
@@ -127,6 +127,10 @@
  *
  *  Thanks to Roger Scott for driver debugging.
  *
+ *  06/07/1997
+ *
+ *  Added support for /proc file system (/proc/scsi/wd7000/[0...] files).
+ *  Now, driver can handle hard disks with capacity >1GB.
  */
 
 #ifdef MODULE
@@ -149,17 +153,23 @@
 #include "scsi.h"
 #include "hosts.h"
 #include "sd.h"
+#include <scsi/scsicam.h>
 
 #define ANY2SCSI_INLINE		/* undef this to use old macros */
-#undef DEBUG
+#undef BIOSPARAM_DEBUG
+#undef DEBUG                    /* general debug */
 
 #include "wd7000.h"
-
 #include<linux/stat.h>
 
-struct proc_dir_entry proc_scsi_wd7000 = {
-    PROC_SCSI_7000FASST, 6, "wd7000",
-    S_IFDIR | S_IRUGO | S_IXUGO, 2
+
+struct proc_dir_entry proc_scsi_wd7000 =
+{
+    PROC_SCSI_7000FASST,
+    6,
+    "wd7000",
+    S_IFDIR | S_IRUGO | S_IXUGO,
+    2
 };
 
 
@@ -210,12 +220,19 @@
 } Adapter;
 
 /*
+ * possible irq range
+ */
+#define IRQ_MIN   3
+#define IRQ_MAX   15
+#define IRQS      (IRQ_MAX-IRQ_MIN + 1)
+
+/*
  * The following is set up by wd7000_detect, and used thereafter by
  * wd7000_intr_handle to map the irq level to the corresponding Adapter.
  * Note that if SA_INTERRUPT is not used, wd7000_intr_handle must be
  * changed to pick up the IRQ level correctly.
  */
-Adapter *irq2host[16] = {NULL};	/* Possible IRQs are 0-15 */
+static struct Scsi_Host *wd7000_host[IRQS];
 
 /*
  * (linear) base address for ROM BIOS
@@ -828,7 +845,8 @@
     cli();
 
     memset(scb, 0, sizeof(Scb));
-    scb->next = scbfree;  scbfree = scb;
+    scb->next = scbfree;
+    scbfree = scb;
     freescbs++;
 
     restore_flags(flags);
@@ -969,6 +987,7 @@
 #ifdef DEBUG
     printk ("wd7000_scsi_done: 0x%06lx\n", (long) SCpnt);
 #endif
+
     SCpnt->SCp.phase = 0;
 }
 
@@ -982,7 +1001,7 @@
     register Scb *scb;             /* for SCSI commands */
     register IcbAny *icb;          /* for host commands */
     register Scsi_Cmnd *SCpnt;
-    Adapter *host = irq2host[irq];  /* This MUST be set!!! */
+    Adapter *host = (Adapter *) wd7000_host[irq - IRQ_MIN]->hostdata; /* This MUST be set!!! */
     Mailbox *icmbs = host->mb.icmb;
 
 #ifdef DEBUG
@@ -990,6 +1009,7 @@
 #endif
 
     flag = inb(host->iobase+ASC_INTR_STAT);
+
 #ifdef DEBUG
     printk("wd7000_intr_handle: intr stat = 0x%02x\n",flag);
 #endif
@@ -1264,6 +1284,144 @@
 }
 
 
+#undef SPRINTF
+#define SPRINTF(args...) { if (pos < (buffer + length)) pos += sprintf (pos, ## args); }
+
+int wd7000_set_info (char *buffer, int length, struct Scsi_Host *host)
+{
+    unsigned long flags;
+
+    save_flags (flags);
+    cli ();
+
+#ifdef DEBUG
+    printk ("Buffer = <%.*s>, length = %d\n", length, buffer, length);
+#endif
+
+    /*
+     * Currently this is a no-op
+     */
+    printk ("Sorry, this function is currently out of order...\n");
+
+    restore_flags (flags);
+
+    return (length);
+}
+
+
+int wd7000_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout)
+{
+    struct Scsi_Host *host = NULL;
+    Scsi_Device *scd = scsi_devices;
+    Adapter *adapter;
+    Mailbox *ogmbs, *icmbs;
+    unsigned long flags;
+    char *pos = buffer;
+    short i;
+
+#ifdef DEBUG
+    short count;
+#endif
+
+    /*
+     * Find the specified host board.
+     */
+    for (i = 0; i < IRQS; i++)
+        if (wd7000_host[i] && (wd7000_host[i]->host_no == hostno)) {
+            host = wd7000_host[i];
+
+	    break;
+	}
+
+    /*
+     * Host not found!
+     */
+    if (! host)
+        return (-ESRCH);
+
+    /*
+     * Has data been written to the file ?
+     */ 
+    if (inout)
+        return (wd7000_set_info (buffer, length, host));
+
+    adapter = (Adapter *) host->hostdata;
+    ogmbs = adapter->mb.ogmb;
+    icmbs = adapter->mb.icmb;
+
+    save_flags (flags);
+    cli ();
+
+    SPRINTF ("Host scsi%d: Western Digital WD-7000 (rev %d.%d)\n", hostno, adapter->rev1, adapter->rev2);
+    SPRINTF ("  IO base:     0x%x\n", adapter->iobase);
+    SPRINTF ("  IRQ:         %d\n", adapter->irq);
+    SPRINTF ("  DMA channel: %d\n", adapter->dma);
+
+#ifdef DEBUG
+    SPRINTF ("Control port value: 0x%x\n", adapter->control);
+    SPRINTF ("Incoming mailbox:\n");
+    SPRINTF ("  size: %d\n", ICMB_CNT);
+    SPRINTF ("  queued messages: ");
+
+    for (i = count = 0; i < ICMB_CNT; i++)
+        if (icmbs[i].status) {
+	    count++;
+	    SPRINTF ("0x%x ", i);
+	}
+
+    SPRINTF (count ? "\n" : "none\n");
+
+    SPRINTF ("Outgoing mailbox:\n");
+    SPRINTF ("  size: %d\n", OGMB_CNT);
+    SPRINTF ("  next message: 0x%x\n", adapter->next_ogmb);
+    SPRINTF ("  queued messages: ");
+
+    for (i = count = 0; i < OGMB_CNT; i++)
+        if (ogmbs[i].status) {
+	    count++;
+	    SPRINTF ("0x%x ", i);
+	}
+
+    SPRINTF (count ? "\n" : "none\n");
+#endif
+
+    /*
+     * Display driver information for each device attached to the board.
+     */
+    SPRINTF ("Attached devices: %s\n", scd ? "" : "none");
+
+    for ( ; scd; scd = scd->next)
+        if (scd->host->host_no == hostno) {
+	    SPRINTF ("  [Channel: %02d, Id: %02d, Lun: %02d]  ",
+		     scd->channel, scd->id, scd->lun);
+	    SPRINTF ("%s ", (scd->type < MAX_SCSI_DEVICE_CODE) ?
+		     scsi_device_types[(short) scd->type] : "Unknown device");
+
+	    for (i = 0; (i < 8) && (scd->vendor[i] >= 0x20); i++)
+	        SPRINTF ("%c", scd->vendor[i]);
+	    SPRINTF (" ");
+
+	    for (i = 0; (i < 16) && (scd->model[i] >= 0x20); i++)
+	        SPRINTF ("%c", scd->model[i]);
+	    SPRINTF ("\n");
+        }
+
+    restore_flags (flags);
+
+    /*
+     * Calculate start of next buffer, and return value.
+     */
+    *start = buffer + offset;
+
+    if ((pos - buffer) < offset)
+        return (0);
+    else if ((pos - buffer - offset) < length)
+        return (pos - buffer - offset);
+    else
+        return (length);
+}
+
+
 /*
  *  Returns the number of adapters this driver is supporting.
  *
@@ -1282,9 +1440,11 @@
     Adapter *host = NULL;
     struct Scsi_Host *sh;
 
+    for (i = 0; i < IRQS; wd7000_host[i++] = NULL);
     for (i = 0; i < NUM_CONFIGS; biosptr[i++] = -1);
 
     tpnt->proc_dir = &proc_scsi_wd7000;
+    tpnt->proc_info = &wd7000_proc_info;
 
     /*
      * Set up SCB free list, which is shared by all adapters
@@ -1292,6 +1452,9 @@
     init_scbs ();
 
     for (pass = 0, cfg_ptr = 0; pass < NUM_CONFIGS; pass++) {
+        /*
+	 * First, search for BIOS SIGNATURE...
+	 */
         for (biosaddr_ptr = 0; biosaddr_ptr < NUM_ADDRS; biosaddr_ptr++)
 	    for (sig_ptr = 0; sig_ptr < NUM_SIGNATURES; sig_ptr++) {
 	        for (i = 0; i < pass; i++)
@@ -1306,7 +1469,9 @@
 	    }
 
 bios_matched:
-
+	/*
+	 * BIOS SIGNATURE has been found.
+	 */
 #ifdef DEBUG
 	printk ("wd7000_detect: pass %d\n", pass + 1);
 
@@ -1335,7 +1500,6 @@
 #ifdef DEBUG
  	    printk ("wd7000_detect: ASC reset (IO 0x%x) ...", iobase);
 #endif
-
  	    /*
   	     * ASC reset...
  	     */
@@ -1367,8 +1531,7 @@
 		host = (Adapter *) sh->hostdata;
 
 #ifdef DEBUG
-		printk ("wd7000_detect: adapter allocated at 0x%x\n",
-			(int) host);
+		printk ("wd7000_detect: adapter allocated at 0x%x\n", (int) host);
 #endif
 
 		memset (host, 0, sizeof (Adapter));
@@ -1382,9 +1545,8 @@
 		    host->dma = configs[cfg_ptr - 1].dma;
 		}
 
-	    	host->sh = sh;
+	    	host->sh = wd7000_host[host->irq - IRQ_MIN] = sh;
     		host->iobase = iobase;
-		irq2host[host->irq] = host;
 
 #ifdef DEBUG
 		printk ("wd7000_detect: Trying init WD-7000 card at IO "
@@ -1409,8 +1571,7 @@
 		request_region (host->iobase, 4, "wd7000");
 
 		/*
-		 *  For boards before rev 6.0, scatter/gather
-		 *  isn't supported.
+		 *  For boards before rev 6.0, scatter/gather isn't supported.
 		 */
 		if (host->rev1 < 6)
 		    sh->sg_tablesize = SG_NONE;
@@ -1429,8 +1590,7 @@
 
 #ifdef DEBUG
 	else
- 	    printk ("wd7000_detect: IO 0x%x region already allocated!\n",
-		    iobase);
+ 	    printk ("wd7000_detect: IO 0x%x region already allocated!\n", iobase);
 #endif
 
     }
@@ -1469,18 +1629,55 @@
 
 
 /*
- *  This was borrowed directly from aha1542.c, but my disks are organized
- *  this way, so I think it will work OK.  Someone who is ambitious can
- *  borrow a newer or more complete version from another driver.
- */
-int wd7000_biosparam(Disk * disk, kdev_t dev, int* ip)
-{
-  int size = disk->capacity;
-  ip[0] = 64;
-  ip[1] = 32;
-  ip[2] = size >> 11;
-/*  if (ip[2] >= 1024) ip[2] = 1024; */
-  return 0;
+ *  This was borrowed directly from aha1542.c. (Zaga)
+ */
+int wd7000_biosparam (Disk *disk, kdev_t dev, int *ip)
+{
+#ifdef BIOSPARAM_DEBUG
+    printk("wd7000_biosparam: dev=%s, size=%d, ", kdevname (dev), disk->capacity);
+#endif
+
+    /*
+     * try default translation
+     */
+    ip[0] = 64;
+    ip[1] = 32;
+    ip[2] = disk->capacity / (64 * 32);
+
+    /*
+     * for disks >1GB do some guessing
+     */
+    if (ip[2] >= 1024) {
+	int info[3];
+
+	/*
+	 * try to figure out the geometry from the partition table
+	 */
+	if ((scsicam_bios_param (disk, dev, info) < 0) ||
+	    !(((info[0] == 64) && (info[1] == 32)) ||
+	    ((info[0] == 255) && (info[1] == 63)))) {
+	    printk ("wd7000_biosparam: unable to verify geometry for disk with >1GB.\n"
+		    "                  using extended translation.\n");
+
+	    ip[0] = 255;
+	    ip[1] = 63;
+	    ip[2] = disk->capacity / (255 * 63);
+	} else {
+	    ip[0] = info[0];
+	    ip[1] = info[1];
+	    ip[2] = info[2];
+
+	    if (info[0] == 255)
+		printk ("wd7000_biosparam: current partition table is using extended translation.\n");
+	}
+    }
+
+#ifdef BIOSPARAM_DEBUG
+    printk ("bios geometry: head=%d, sec=%d, cyl=%d\n", ip[0], ip[1], ip[2]);
+    printk ("WARNING: check, if the bios geometry is correct.\n");
+#endif
+
+    return (0);
 }
 
 #ifdef MODULE
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/wd7000.h linux/drivers/scsi/wd7000.h
--- v2.0.30/linux/drivers/scsi/wd7000.h	Tue Mar 11 15:40:02 1997
+++ linux/drivers/scsi/wd7000.h	Mon Aug 11 13:37:24 1997
@@ -8,17 +8,20 @@
  * This file has been reduced to only the definitions needed for the
  * WD7000 host structure.
  *
+ * Revision by Miroslav Zagorac <zaga@fly.cc.fer.hr>  Jun 1997.
  */
 
 #include <linux/types.h>
 #include <linux/kdev_t.h>
 
-int wd7000_detect(Scsi_Host_Template *);
-int wd7000_command(Scsi_Cmnd *);
-int wd7000_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
-int wd7000_abort(Scsi_Cmnd *);
-int wd7000_reset(Scsi_Cmnd *);
-int wd7000_biosparam(Disk *, kdev_t, int *);
+int wd7000_set_info (char *buffer, int length, struct Scsi_Host *host);
+int wd7000_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout);
+int wd7000_detect (Scsi_Host_Template *);
+int wd7000_command (Scsi_Cmnd *);
+int wd7000_queuecommand (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int wd7000_abort (Scsi_Cmnd *);
+int wd7000_reset (Scsi_Cmnd *);
+int wd7000_biosparam (Disk *, kdev_t, int *);
 
 #ifndef NULL
 #define NULL 0L
diff -u --recursive --new-file v2.0.30/linux/drivers/sound/.blurb linux/drivers/sound/.blurb
--- v2.0.30/linux/drivers/sound/.blurb	Sun Jun 30 01:43:44 1996
+++ linux/drivers/sound/.blurb	Sun Aug 31 08:49:57 1997
@@ -1,9 +1,8 @@
 *********************************************************
 * Readme.cards (this directory) contains some card	*
 * specific instructions.				*
-* See http://www.4front-tech.com/usslite for most up    *
+* See http://www.4front-tech.com/ossfree for most up    *
 * to date info.                                         *
-* (European mirror http://personal.eunet.fi/pp/voxware) *
 *							*
 * DON'T USE PROGRAMS FROM SND_UTIL PACKAGE EARLIER THAN *
 * snd-util-3.5 WITH THIS SOUND DRIVER VERSION.          *
diff -u --recursive --new-file v2.0.30/linux/drivers/sound/Config.in linux/drivers/sound/Config.in
--- v2.0.30/linux/drivers/sound/Config.in	Wed Jul 10 04:34:03 1996
+++ linux/drivers/sound/Config.in	Fri Sep  5 20:43:58 1997
@@ -2,10 +2,10 @@
 # Sound driver configuration
 #
 #--------
-# There is another confic script which is compatible with rest of
+# There is another config script which is compatible with rest of
 # the kernel. It can be activated by running 'make mkscript' in this
 # directory. Please note that this is an _experimental_ feature which
-# doesn't work with all cards (PSS, SM Wave, AudioTriX Pro, Maui).
+# doesn't work with all cards (PSS, SM Wave, AudioTrix Pro, Maui).
 #--------
 #
 $MAKE -C drivers/sound config || exit 1
diff -u --recursive --new-file v2.0.30/linux/drivers/sound/Readme linux/drivers/sound/Readme
--- v2.0.30/linux/drivers/sound/Readme	Sat Jul  6 01:31:43 1996
+++ linux/drivers/sound/Readme	Sun Aug 31 08:36:50 1997
@@ -2,8 +2,7 @@
 ------------------------------------
 
 Most up to date information about this driver is available from 
-http://www.4front-tech.com/usslite or http://personal.eunet.fi/pp/voxware
-(European mirror).
+http://www.4front-tech.com/ossfree.
 
 
 
@@ -24,7 +23,7 @@
 
 Packages "snd-util-3.5.tar.gz" and "snd-data-0.1.tar.Z"
 contain useful utilities to be used with this driver.
-See http://www.4front-tech.com/usslite/getting.html for
+See http://www.4front-tech.com/ossfree/getting.html for
 download instructions.
 
 If you are looking for the installation instructions, please
@@ -35,7 +34,7 @@
 
 See Readme.cards.
 
-Please check http://www.4front-tech.com/usslite if you don't find
+Please check http://www.4front-tech.com/ossfree if you don't find
 your soundcard there.
 
 Contributors
@@ -113,7 +112,7 @@
 =========================
 
 Read the sound HOWTO (sunsite.unc.edu:/pub/Linux/docs/...?).
-Also look at the home page (http://www.4front-tech.com/usslite). It may
+Also look at the home page (http://www.4front-tech.com/ossfree). It may
 contain info about some recent bug fixes.
 
 It's likely that you have some problems when trying to use the sound driver
@@ -167,8 +166,8 @@
 Hannu
 
 Hannu Savolainen
-hannu@voxware.pp.fi, hannu@4front-tech.com	
-(Please check http://www.4front-tech.com/usslite before mailing me).
+hannu@4front-tech.com	
+(Please check http://www.4front-tech.com/ossfree before mailing me).
 
 Snail mail:	Hannu Savolainen
 		Hiekkalaiturintie 3 A 8
diff -u --recursive --new-file v2.0.30/linux/drivers/sound/Readme.cards linux/drivers/sound/Readme.cards
--- v2.0.30/linux/drivers/sound/Readme.cards	Fri Sep 20 07:00:35 1996
+++ linux/drivers/sound/Readme.cards	Sun Aug 31 08:47:02 1997
@@ -3,7 +3,7 @@
 
 IMPORTANT!	This document covers only cards that were "known" when
 		this driver version was released. Please look at
-		http://www.4front-tech.com/usslite for info about
+		http://www.4front-tech.com/ossfree for info about
 		cards introduced recently.
 
 		The following covers mainly the "old" configuration
@@ -25,18 +25,18 @@
 There are many soundcards which don't work with this driver
 version (v3.5). Support for some of them is expected to be
 available during/after summer 1996 (in version 3.6). Please check
-http://www.4front-tech.com/usslite for latest news. Please don't
+http://www.4front-tech.com/ossfree for latest news. Please don't
 mail me and ask about these cards. The unsupported cards are:
 
 	- All PnP soundcards (SB PnP, GUS PnP, Soundscape PnP etc.)
 		Schedule for availability of PnP soundcard support in
-		USS/Lite depends on progress made by kernel PnP team
+		OSS/Free depends on progress made by kernel PnP team
 		(probably in Linux 2.1.xx versions). With Linux 2.0.x
 		versions there are two ways to get PnP soundcards to work:
 	 	- Use isapnptools, DOS, Win95 or PnP aware BIOS to wake up the
 		card before starting the sound driver. See "Configuring PnP 
 		soundcards" below for some hints.
-		- Support for SB PnP and GUS PnP is present in USS/Linux (the
+		- Support for SB PnP and GUS PnP is present in OSS/Linux (the
 		commercial version of this driver).
 	- Mwave soundcards and motherboards
 		(Version 3.6 or 3.7. Depends on how fast I get
@@ -49,7 +49,7 @@
 	- Compaq Deskpro
 		(Version 3.5.4-beta6 (already released))
 	- Sound Galaxy Washington/Waverider
-		(Audio features already in USS/Linux (USS/Lite soon). 
+		(Audio features already in OSS/Linux (OSS/Free soon). 
 		Can't promise the waverider synth since
 		availability of chip specs is uncertain).
 	- Yamaha OPL4 (on cards having _RAM_ for samples)
@@ -77,7 +77,7 @@
 PnP soundcards (as well as most other PnP ISA cards) are not supported
 by version 3.5 of this driver (Linux 1.3.xx and Linux 2.0.x). Proper
 support for them should be released during spring 96 
-(see http://www.4front-tech.com/usslite for latest info).
+(see http://www.4front-tech.com/ossfree for latest info).
 
 There is a method to get most of the PnP cards to work. The basic method
 is the following:
@@ -719,7 +719,7 @@
 !!!!!	    purposes. It WAS required to change /dev/dsp (a symlink) to !!!!
 !!!!!	    point to /dev/dsp1.						!!!!
 !!!!!                                                                   !!!!
-!!!!!	    This is not required with USS versions 3.5-beta6 and later	!!!!
+!!!!!	    This is not required with OSS versions 3.5-beta6 and later	!!!!
 !!!!!	    since there is now just one audio device file. Please 	!!!!
 !!!!!	    change /dev/dsp to point back to /dev/dsp0 if you are	!!!!
 !!!!!	    upgrading from an earlier driver version using		!!!!
@@ -999,7 +999,7 @@
 Writing a driver for a new card is not possible if there are no 
 programming information available about the card. If you don't
 find your new card from this file, look from the home page 
-(http://www.4front-tech.com/usslite). Then please contact
+(http://www.4front-tech.com/ossfree). Then please contact
 manufacturer of the card and ask if they have (or are willing to)
 released technical details of the card. Do this before contacting me. I
 can only answer 'no' if there are no programming information available.
@@ -1020,7 +1020,7 @@
 There are some common audio chipsets that are not supported yet. For example
 Sierra Aria and IBM Mwave. It's possible that these architectures
 get some support in future but I can't make any promises. Just look
-at the home page (http://www.4front-tech.com/usslite/new_cards.html)
+at the home page (http://www.4front-tech.com/ossfree/new_cards.html)
 for latest info.
 
 Information about unsupported soundcards and chipsets is welcome as well
@@ -1029,12 +1029,11 @@
 If you have any corrections and/or comments, please contact me.
 
 Hannu Savolainen
-hannu@voxware.pp.fi
+hannu@4front-tech.com
 
-Personal home page:	   http://personal.eunet.fi/pp/voxware/hannu.html
-www home page of USS/Lite: http://www.4front-tech.com/usslite
-  European/Finnish mirror: http://personal.eunet.fi/pp/voxware
+Personal home page:	   http://www.4front-tech.com/hannu.html
+www home page of OSS/Free: http://www.4front-tech.com/ossfree
 
 www home page of commercial
-Open Sound System drivers: http://www.4front-tech.com/uss.html
+Open Sound System drivers: http://www.4front-tech.com/oss.html
 
diff -u --recursive --new-file v2.0.30/linux/drivers/sound/Readme.linux linux/drivers/sound/Readme.linux
--- v2.0.30/linux/drivers/sound/Readme.linux	Sat Jul  6 01:31:43 1996
+++ linux/drivers/sound/Readme.linux	Sun Aug 31 08:47:09 1997
@@ -67,13 +67,18 @@
 check for possible boot (insmod) time error messages in /var/adm/messages.
 
 - Other messages or problems
-Please check http://www.4front-tech.com/usslite for more info.
+Please check http://www.4front-tech.com/ossfree for more info.
 
 Hannu Savolainen
-hannu@voxware.pp.fi
+hannu@4front-tech.com
 
 ----------------- cut here ------------------------------
 #!/bin/sh
+echo Error: Read the notice in the beginning of this script before executing.
+exit -1
+Notice! Remove this notice and the above two lines before trying to execute.
+        Trying to run this script is normally useless. The device files are
+        almost certainly already set up correctly. 
 # *****************************************
 # * NOTICE!
 # *
diff -u --recursive --new-file v2.0.30/linux/drivers/sound/Readme.v30 linux/drivers/sound/Readme.v30
--- v2.0.30/linux/drivers/sound/Readme.v30	Sat Jul  6 01:31:43 1996
+++ linux/drivers/sound/Readme.v30	Sun Aug 31 08:42:32 1997
@@ -137,4 +137,4 @@
 Don't hesitate to contact me in case you have questions or comments.
 
 Hannu Savolainen
-hannu@voxware.pp.fi
+hannu@4front-tech.com
diff -u --recursive --new-file v2.0.30/linux/drivers/sound/sb_common.c linux/drivers/sound/sb_common.c
--- v2.0.30/linux/drivers/sound/sb_common.c	Wed Sep 11 07:57:14 1996
+++ linux/drivers/sound/sb_common.c	Sun Aug  3 15:25:08 1997
@@ -583,6 +583,20 @@
  * Detect the device
  */
 
+  cli();			/* Some ESS1688 cards need this */
+  inb (devc->base + 0x9);
+  inb (devc->base + 0x9);
+  inb (devc->base + 0x9);
+  inb (devc->base + 0xb);
+  inb (devc->base + 0x9);
+  inb (devc->base + 0xb);
+  inb (devc->base + 0x9);
+  inb (devc->base + 0x9);
+  inb (devc->base + 0xb);
+  inb (devc->base + 0x9);
+  inb (devc->base);
+  sti();
+
   if (sb_dsp_reset (devc))
     dsp_get_vers (devc);
   else
diff -u --recursive --new-file v2.0.30/linux/drivers/sound/sequencer.c linux/drivers/sound/sequencer.c
--- v2.0.30/linux/drivers/sound/sequencer.c	Tue Aug 20 23:18:09 1996
+++ linux/drivers/sound/sequencer.c	Mon Aug 25 13:25:04 1997
@@ -1592,7 +1592,7 @@
 	return -(EIO);
 
       midi_dev = get_user ((int *) arg);
-      if (midi_dev >= max_mididev)
+      if (midi_dev < 0 || midi_dev >= max_mididev)
 	return -(ENXIO);
 
       if (!midi_opened[midi_dev])
@@ -2038,7 +2038,7 @@
   iqueue = (unsigned char *) (sound_mem_blocks[sound_nblocks] = vmalloc (SEQ_MAX_QUEUE * IEV_SZ));
   if (sound_nblocks < 1024)
     sound_nblocks++;;
-  if (queue == NULL)
+  if (iqueue == NULL)
     {
       printk ("Sound: Can't allocate memory for sequencer input queue\n");
       return;
diff -u --recursive --new-file v2.0.30/linux/fs/Config.in linux/fs/Config.in
--- v2.0.30/linux/fs/Config.in	Sun Dec  1 05:58:05 1996
+++ linux/fs/Config.in	Fri Sep  5 20:43:59 1997
@@ -5,7 +5,6 @@
 comment 'Filesystems'
 
 bool	 'Quota support' CONFIG_QUOTA
-bool	 'Mandatory lock support' CONFIG_LOCK_MANDATORY
 tristate 'Minix fs support' CONFIG_MINIX_FS
 tristate 'Extended fs support' CONFIG_EXT_FS
 tristate 'Second extended fs support' CONFIG_EXT2_FS
@@ -38,6 +37,9 @@
 tristate 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS
 tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS
 tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  tristate 'Kernel automounter support (experimental)' CONFIG_AUTOFS_FS
+fi
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
   tristate 'Amiga FFS filesystem support (EXPERIMENTAL)' CONFIG_AFFS_FS
   if [ "$CONFIG_AFFS_FS" != "n" ]; then
diff -u --recursive --new-file v2.0.30/linux/fs/Makefile linux/fs/Makefile
--- v2.0.30/linux/fs/Makefile	Wed May  8 08:28:01 1996
+++ linux/fs/Makefile	Fri Sep  5 20:43:59 1997
@@ -13,11 +13,11 @@
 O_OBJS    = open.o read_write.o inode.o devices.o file_table.o buffer.o \
 		super.o  block_dev.o stat.o exec.o pipe.o namei.o fcntl.o \
 		ioctl.o readdir.o select.o fifo.o locks.o filesystems.o \
-		dcache.o $(BINFMTS) 
+		dcache.o bad_inode.o $(BINFMTS) 
 
 MOD_LIST_NAME := FS_MODULES
 ALL_SUB_DIRS = minix ext ext2 fat msdos vfat proc isofs nfs xiafs umsdos \
-		hpfs sysv smbfs ncpfs ufs affs
+		hpfs sysv smbfs ncpfs ufs affs autofs
 
 ifeq ($(CONFIG_QUOTA),y)
 O_OBJS += dquot.o
@@ -154,6 +154,14 @@
 else
   ifeq ($(CONFIG_AFFS_FS),m)
   MOD_SUB_DIRS += affs
+  endif
+endif
+
+ifeq ($(CONFIG_AUTOFS_FS),y)
+SUB_DIRS += autofs
+else
+  ifeq ($(CONFIG_AUTOFS_FS),m)
+  MOD_SUB_DIRS += autofs
   endif
 endif
 
diff -u --recursive --new-file v2.0.30/linux/fs/autofs/Makefile linux/fs/autofs/Makefile
--- v2.0.30/linux/fs/autofs/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/fs/autofs/Makefile	Fri Sep  5 20:43:59 1997
@@ -0,0 +1,35 @@
+#
+# Makefile for the linux autofs-filesystem routines.
+#
+# We can build this either out of the kernel tree or the autofs tools tree.
+#
+
+O_TARGET := autofs.o
+O_OBJS   := dir.o dirhash.o init.o inode.o root.o symlink.o waitq.o
+
+M_OBJS   := $(O_TARGET)
+
+ifdef TOPDIR
+#
+# Part of the kernel code
+#
+include $(TOPDIR)/Rules.make
+else
+#
+# Standalone (handy for development)
+#
+include ../Makefile.rules
+
+CFLAGS += -D__KERNEL__ -DMODULE $(KFLAGS) -I../include -I$(KINCLUDE) $(MODFLAGS)
+
+all: $(O_TARGET)
+
+$(O_TARGET): $(O_OBJS)
+	$(LD) -r -o $(O_TARGET) $(O_OBJS)
+
+install: $(O_TARGET)
+	install -c $(O_TARGET) /lib/modules/`uname -r`/fs
+
+clean:
+	rm -f *.o *.s
+endif
diff -u --recursive --new-file v2.0.30/linux/fs/autofs/autofs_i.h linux/fs/autofs/autofs_i.h
--- v2.0.30/linux/fs/autofs/autofs_i.h	Wed Dec 31 16:00:00 1969
+++ linux/fs/autofs/autofs_i.h	Fri Sep  5 20:43:59 1997
@@ -0,0 +1,187 @@
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *   
+ * linux/fs/autofs/autofs_i.h
+ *
+ *   Copyright 1997 Transmeta Corporation - All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/* Internal header file for autofs */
+
+#include <linux/auto_fs.h>
+
+/* This is the range of ioctl() numbers we claim as ours */
+#define AUTOFS_IOC_FIRST     AUTOFS_IOC_READY
+#define AUTOFS_IOC_COUNT     32
+
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/wait.h>
+
+#define kver(a,b,c) (((a) << 16) + ((b) << 8) + (c)) 
+
+#if LINUX_VERSION_CODE < kver(2,1,0)
+
+/* Segmentation stuff for pre-2.1 kernels */
+#include <asm/segment.h>
+
+static inline int copy_to_user(void *dst, void *src, unsigned long len)
+{
+	int rv = verify_area(VERIFY_WRITE, dst, len);
+	if ( rv )
+		return -1;
+	memcpy_tofs(dst,src,len);
+	return 0;
+}
+
+static inline int copy_from_user(void *dst, void *src, unsigned long len)
+{
+	int rv = verify_area(VERIFY_READ, src, len);
+	if ( rv )
+		return -1;
+	memcpy_fromfs(dst,src,len);
+	return 0;
+}
+
+#else
+
+/* Segmentation stuff for post-2.1 kernels */
+#include <asm/uaccess.h>
+#define register_symtab(x)	((void)0)
+
+#endif
+
+#ifdef DEBUG
+#define DPRINTK(D) printk D;
+#else
+#define DPRINTK(D)
+#endif
+
+#define AUTOFS_SUPER_MAGIC 0x0187
+
+/* Structures associated with the root directory hash */
+
+#define AUTOFS_HASH_SIZE 67
+
+typedef u32 autofs_hash_t;	/* Type returned by autofs_hash() */
+
+struct autofs_dir_ent {
+	autofs_hash_t hash;
+	struct autofs_dir_ent *next;
+	struct autofs_dir_ent **back;
+	char *name;
+	int len;
+	ino_t ino;
+	/* The following entries are for the expiry system */
+	unsigned long last_usage;
+	struct autofs_dir_ent *exp_next;
+	struct autofs_dir_ent *exp_prev;
+};
+
+struct autofs_dirhash {
+	struct autofs_dir_ent *h[AUTOFS_HASH_SIZE];
+	struct autofs_dir_ent expiry_head;
+};
+
+struct autofs_wait_queue {
+	unsigned long wait_queue_token;
+	struct wait_queue *queue;
+	struct autofs_wait_queue *next;
+	/* We use the following to see what we are waiting for */
+	autofs_hash_t hash;
+	int len;
+	char *name;
+	/* This is for status reporting upon return */
+	int status;
+	int wait_ctr;
+};
+
+struct autofs_symlink {
+	int len;
+	char *data;
+	time_t mtime;
+};
+
+#define AUTOFS_MAX_SYMLINKS 256
+
+#define AUTOFS_ROOT_INO      1
+#define AUTOFS_FIRST_SYMLINK 2
+#define AUTOFS_FIRST_DIR_INO (AUTOFS_FIRST_SYMLINK+AUTOFS_MAX_SYMLINKS)
+
+#define AUTOFS_SYMLINK_BITMAP_LEN ((AUTOFS_MAX_SYMLINKS+31)/32)
+
+#ifndef END_OF_TIME
+#define END_OF_TIME ((time_t)((unsigned long)((time_t)(~0UL)) >> 1))
+#endif
+
+#define AUTOFS_SBI_MAGIC 0x6d4a556d
+
+struct autofs_sb_info {
+	u32 magic;
+	struct file *pipe;
+	pid_t oz_pgrp;
+	int catatonic;
+	unsigned long exp_timeout;
+	ino_t next_dir_ino;
+	struct autofs_wait_queue *queues; /* Wait queue pointer */
+	struct autofs_dirhash dirhash; /* Root directory hash */
+	struct autofs_symlink symlink[AUTOFS_MAX_SYMLINKS];
+	u32 symlink_bitmap[AUTOFS_SYMLINK_BITMAP_LEN];
+};
+
+/* autofs_oz_mode(): do we see the man behind the curtain? */
+static inline int autofs_oz_mode(struct autofs_sb_info *sbi) {
+	return sbi->catatonic || current->pgrp == sbi->oz_pgrp;
+}
+
+/* Debug the mysteriously disappearing wait list */
+
+#ifdef DEBUG_WAITLIST
+#define CHECK_WAITLIST(S,O) autofs_check_waitlist_integrity(S,O)
+void autofs_check_waitlist_integrity(struct autofs_sb_info *,char *);
+#else
+#define CHECK_WAITLIST(S,O)
+#endif
+
+/* Hash operations */
+
+autofs_hash_t autofs_hash(const char *,int);
+void autofs_initialize_hash(struct autofs_dirhash *);
+struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *,autofs_hash_t,const char *,int);
+void autofs_hash_insert(struct autofs_dirhash *,struct autofs_dir_ent *);
+void autofs_hash_delete(struct autofs_dir_ent *);
+struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *,off_t *);
+void autofs_hash_nuke(struct autofs_dirhash *);
+
+/* Expiration-handling functions */
+
+void autofs_update_usage(struct autofs_dirhash *,struct autofs_dir_ent *);
+struct autofs_dir_ent *autofs_expire(struct autofs_dirhash *,unsigned long);
+
+/* Operations structures */
+
+extern struct inode_operations autofs_root_inode_operations;
+extern struct inode_operations autofs_symlink_inode_operations;
+extern struct inode_operations autofs_dir_inode_operations;
+
+/* Initializing function */
+
+struct super_block *autofs_read_super(struct super_block *, void *,int);
+
+/* Queue management functions */
+
+int autofs_wait(struct autofs_sb_info *,autofs_hash_t,const char *,int);
+int autofs_wait_release(struct autofs_sb_info *,unsigned long,int);
+void autofs_catatonic_mode(struct autofs_sb_info *);
+
+#ifdef DEBUG
+void autofs_say(const char *name, int len);
+#else
+#define autofs_say(n,l)
+#endif
diff -u --recursive --new-file v2.0.30/linux/fs/autofs/dir.c linux/fs/autofs/dir.c
--- v2.0.30/linux/fs/autofs/dir.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/autofs/dir.c	Fri Sep  5 20:43:59 1997
@@ -0,0 +1,90 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/fs/autofs/dir.c
+ *
+ *  Copyright 1997 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include "autofs_i.h"
+
+static int autofs_dir_readdir(struct inode *inode, struct file *filp,
+			       void *dirent, filldir_t filldir)
+{
+	if (!inode || !S_ISDIR(inode->i_mode))
+		return -ENOTDIR;
+
+	switch((unsigned long) filp->f_pos)
+	{
+	case 0:
+		if (filldir(dirent, ".", 1, 0, inode->i_ino) < 0)
+			return 0;
+		filp->f_pos++;
+		/* fall through */
+	case 1:
+		if (filldir(dirent, "..", 2, 1, AUTOFS_ROOT_INO) < 0)
+			return 0;
+		filp->f_pos++;
+		/* fall through */
+	}
+	return 1;
+}
+
+static int autofs_dir_lookup(struct inode *dir, const char *name, int len,
+			      struct inode **result)
+{
+	*result = dir;
+	if (!len)
+		return 0;
+	if (name[0] == '.') {
+		if (len == 1)
+			return 0;
+		if (name[1] == '.' && len == 2) {
+			/* Return the root directory */
+			*result = iget(dir->i_sb,AUTOFS_ROOT_INO);
+			iput(dir);
+			return 0;
+		}
+	}
+	*result = NULL;
+	iput(dir);
+	return -ENOENT;		/* No other entries */
+}
+
+static struct file_operations autofs_dir_operations = {
+	NULL,			/* lseek */
+	NULL,			/* read */
+	NULL,			/* write */
+	autofs_dir_readdir,	/* readdir */
+	NULL,			/* select */
+	NULL,			/* ioctl */
+	NULL,			/* mmap */
+	NULL,			/* open */
+	NULL,			/* release */
+	NULL			/* fsync */
+};
+
+struct inode_operations autofs_dir_inode_operations = {
+	&autofs_dir_operations,	/* file operations */
+	NULL,			/* create */
+	autofs_dir_lookup,	/* lookup */
+	NULL,			/* link */
+	NULL,			/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	NULL,			/* read_page */
+	NULL,			/* writepage */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	NULL			/* permission */
+};
+
diff -u --recursive --new-file v2.0.30/linux/fs/autofs/dirhash.c linux/fs/autofs/dirhash.c
--- v2.0.30/linux/fs/autofs/dirhash.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/autofs/dirhash.c	Fri Sep  5 20:43:59 1997
@@ -0,0 +1,172 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/fs/autofs/dirhash.c
+ *
+ *  Copyright 1997 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include "autofs_i.h"
+
+/* Functions for maintenance of expiry queue */
+
+static void autofs_init_usage(struct autofs_dirhash *dh,
+			      struct autofs_dir_ent *ent)
+{
+	ent->exp_next = &dh->expiry_head;
+	ent->exp_prev = dh->expiry_head.exp_prev;
+	dh->expiry_head.exp_prev->exp_next = ent;
+	dh->expiry_head.exp_prev = ent;
+	ent->last_usage = jiffies;
+}
+
+static void autofs_delete_usage(struct autofs_dir_ent *ent)
+{
+	ent->exp_prev->exp_next = ent->exp_next;
+	ent->exp_next->exp_prev = ent->exp_prev;
+}
+
+void autofs_update_usage(struct autofs_dirhash *dh,
+			 struct autofs_dir_ent *ent)
+{
+	autofs_delete_usage(ent);   /* Unlink from current position */
+	autofs_init_usage(dh,ent);  /* Relink at queue tail */
+}
+
+struct autofs_dir_ent *autofs_expire(struct autofs_dirhash *dh,
+				     unsigned long timeout)
+{
+	struct autofs_dir_ent *ent;
+
+	ent = dh->expiry_head.exp_next;
+
+	if ( ent == &(dh->expiry_head) ) return NULL;
+	return (jiffies - ent->last_usage >= timeout) ? ent : NULL;
+}
+
+/* Adapted from the Dragon Book, page 436 */
+/* This particular hashing algorithm requires autofs_hash_t == u32 */
+autofs_hash_t autofs_hash(const char *name, int len)
+{
+	autofs_hash_t h = 0;
+	while ( len-- ) {
+		h = (h << 4) + (unsigned char) (*name++);
+		h ^= ((h & 0xf0000000) >> 24);
+	}
+	return h;
+}
+
+void autofs_initialize_hash(struct autofs_dirhash *dh) {
+	memset(&dh->h, 0, AUTOFS_HASH_SIZE*sizeof(struct autofs_dir_ent *));
+	dh->expiry_head.exp_next = dh->expiry_head.exp_prev =
+		&dh->expiry_head;
+}
+
+struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *dh, autofs_hash_t hash, const char *name, int len)
+{
+	struct autofs_dir_ent *dhn;
+
+	DPRINTK(("autofs_hash_lookup: hash = 0x%08x, name = ", hash));
+	autofs_say(name,len);
+
+	for ( dhn = dh->h[hash % AUTOFS_HASH_SIZE] ; dhn ; dhn = dhn->next ) {
+		if ( hash == dhn->hash &&
+		     len == dhn->len &&
+		     !memcmp(name, dhn->name, len) )
+			break;
+	}
+
+	return dhn;
+}
+
+void autofs_hash_insert(struct autofs_dirhash *dh, struct autofs_dir_ent *ent)
+{
+	struct autofs_dir_ent **dhnp;
+
+	DPRINTK(("autofs_hash_insert: hash = 0x%08x, name = ", ent->hash));
+	autofs_say(ent->name,ent->len);
+
+	autofs_init_usage(dh,ent);
+
+	dhnp = &dh->h[ent->hash % AUTOFS_HASH_SIZE];
+	ent->next = *dhnp;
+	ent->back = dhnp;
+	*dhnp = ent;
+}
+
+void autofs_hash_delete(struct autofs_dir_ent *ent)
+{
+	*(ent->back) = ent->next;
+
+	autofs_delete_usage(ent);
+
+	kfree(ent->name);
+	kfree(ent);
+}
+
+/*
+ * Used by readdir().  We must validate "ptr", so we can't simply make it
+ * a pointer.  Values below 0xffff are reserved; calling with any value
+ * <= 0x10000 will return the first entry found.
+ */
+struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *dh, off_t *ptr)
+{
+	int bucket, ecount, i;
+	struct autofs_dir_ent *ent;
+
+	bucket = (*ptr >> 16) - 1;
+	ecount = *ptr & 0xffff;
+
+	if ( bucket < 0 ) {
+		bucket = ecount = 0;
+	} 
+
+	DPRINTK(("autofs_hash_enum: bucket %d, entry %d\n", bucket, ecount));
+
+	ent = NULL;
+
+	while  ( bucket < AUTOFS_HASH_SIZE ) {
+		ent = dh->h[bucket];
+		for ( i = ecount ; ent && i ; i-- )
+			ent = ent->next;
+
+		if (ent) {
+			ecount++; /* Point to *next* entry */
+			break;
+		}
+
+		bucket++; ecount = 0;
+	}
+
+#ifdef DEBUG
+	if ( !ent )
+		printk("autofs_hash_enum: nothing found\n");
+	else {
+		printk("autofs_hash_enum: found hash %08x, name", ent->hash);
+		autofs_say(ent->name,ent->len);
+	}
+#endif
+
+	*ptr = ((bucket+1) << 16) + ecount;
+	return ent;
+}
+
+/* Delete everything.  This is used on filesystem destruction, so we
+   make no attempt to keep the pointers valid */
+void autofs_hash_nuke(struct autofs_dirhash *dh)
+{
+	int i;
+	struct autofs_dir_ent *ent, *nent;
+
+	for ( i = 0 ; i < AUTOFS_HASH_SIZE ; i++ ) {
+		for ( ent = dh->h[i] ; ent ; ent = nent ) {
+			nent = ent->next;
+			kfree(ent->name);
+			kfree(ent);
+		}
+	}
+}
diff -u --recursive --new-file v2.0.30/linux/fs/autofs/init.c linux/fs/autofs/init.c
--- v2.0.30/linux/fs/autofs/init.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/autofs/init.c	Fri Sep  5 20:43:59 1997
@@ -0,0 +1,58 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/fs/autofs/init.c
+ *
+ *  Copyright 1997 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include <linux/module.h>
+#include "autofs_i.h"
+
+#if LINUX_VERSION_CODE < kver(2,1,36)
+#define __initfunc(X) X
+#else
+#include <linux/init.h>
+#endif
+
+static struct file_system_type autofs_fs_type = {
+	autofs_read_super, "autofs", 0, NULL
+};
+
+#ifdef MODULE
+int init_module(void)
+{
+	int status;
+	
+	if ((status = register_filesystem(&autofs_fs_type)) == 0)
+		register_symtab(0);
+	return status;
+}
+
+void cleanup_module(void)
+{
+	unregister_filesystem(&autofs_fs_type);
+}
+
+#else /* MODULE */
+
+__initfunc(int init_autofs_fs(void))
+{
+	return register_filesystem(&autofs_fs_type);
+}
+
+#endif /* !MODULE */
+
+#ifdef DEBUG
+void autofs_say(const char *name, int len)
+{
+	printk("(%d: ", len);
+	while ( len-- )
+		printk("%c", *name++);
+	printk(")\n");
+}
+#endif
diff -u --recursive --new-file v2.0.30/linux/fs/autofs/inode.c linux/fs/autofs/inode.c
--- v2.0.30/linux/fs/autofs/inode.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/autofs/inode.c	Fri Sep  5 20:43:59 1997
@@ -0,0 +1,277 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/fs/autofs/inode.c
+ *
+ *  Copyright 1997 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/file.h>
+#include <linux/locks.h>
+#include <asm/bitops.h>
+#include "autofs_i.h"
+#define __NO_VERSION__
+#include <linux/module.h>
+
+static void autofs_put_inode(struct inode *inode)
+{
+        if (inode->i_nlink)
+                return;
+        inode->i_size = 0;
+}
+
+static void autofs_put_super(struct super_block *sb)
+{
+	struct autofs_sb_info *sbi =
+		(struct autofs_sb_info *) sb->u.generic_sbp;
+	unsigned int n;
+
+	if ( !sbi->catatonic )
+		autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */
+
+        lock_super(sb);
+	autofs_hash_nuke(&sbi->dirhash);
+	for ( n = 0 ; n < AUTOFS_MAX_SYMLINKS ; n++ ) {
+		if ( test_bit(n, sbi->symlink_bitmap) )
+			kfree(sbi->symlink[n].data);
+	}
+
+        sb->s_dev = 0;
+	kfree(sb->u.generic_sbp);
+        unlock_super(sb);
+
+	DPRINTK(("autofs: shutting down\n"));
+	
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+static void autofs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz);
+static void autofs_read_inode(struct inode *inode);
+static void autofs_write_inode(struct inode *inode);
+
+static struct super_operations autofs_sops = {
+        autofs_read_inode,
+        NULL,
+        autofs_write_inode,
+        autofs_put_inode,
+        autofs_put_super,
+        NULL,
+        autofs_statfs,
+        NULL
+};
+
+static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, pid_t *pgrp, int *minproto, int *maxproto)
+{
+        char *this_char, *value;
+        
+        *uid = current->uid;
+        *gid = current->gid;
+	*pgrp = current->pgrp;
+
+	*minproto = *maxproto = AUTOFS_PROTO_VERSION;
+
+        *pipefd = -1;
+
+        if ( !options ) return 1;
+        for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
+                if ((value = strchr(this_char,'=')) != NULL)
+                        *value++ = 0;
+                if (!strcmp(this_char,"fd")) {
+                        if (!value || !*value)
+                                return 1;
+                        *pipefd = simple_strtoul(value,&value,0);
+                        if (*value)
+                                return 1;
+                }
+                else if (!strcmp(this_char,"uid")) {
+                        if (!value || !*value)
+                                return 1;
+                        *uid = simple_strtoul(value,&value,0);
+                        if (*value)
+                                return 1;
+                }
+                else if (!strcmp(this_char,"gid")) {
+                        if (!value || !*value)
+                                return 1;
+                        *gid = simple_strtoul(value,&value,0);
+                        if (*value)
+                                return 1;
+                }
+                else if (!strcmp(this_char,"pgrp")) {
+                        if (!value || !*value)
+                                return 1;
+                        *pgrp = simple_strtoul(value,&value,0);
+                        if (*value)
+                                return 1;
+                }
+                else if (!strcmp(this_char,"minproto")) {
+                        if (!value || !*value)
+                                return 1;
+                        *minproto = simple_strtoul(value,&value,0);
+                        if (*value)
+                                return 1;
+                }
+                else if (!strcmp(this_char,"maxproto")) {
+                        if (!value || !*value)
+                                return 1;
+                        *maxproto = simple_strtoul(value,&value,0);
+                        if (*value)
+                                return 1;
+                }
+                else break;
+        }
+        return (*pipefd < 0);
+}
+
+struct super_block *autofs_read_super(struct super_block *s, void *data,
+                                      int silent)
+{
+        int pipefd;
+	struct autofs_sb_info *sbi;
+	int minproto, maxproto;
+
+	MOD_INC_USE_COUNT;
+
+        lock_super(s);
+        sbi = (struct autofs_sb_info *) kmalloc(sizeof(struct autofs_sb_info), GFP_KERNEL);
+	if ( !sbi ) {
+		s->s_dev = 0;
+		MOD_DEC_USE_COUNT;
+		return NULL;
+	}
+	DPRINTK(("autofs: starting up, sbi = %p\n",sbi));
+
+	s->u.generic_sbp = sbi;
+	sbi->magic = AUTOFS_SBI_MAGIC;
+	sbi->catatonic = 0;
+	sbi->exp_timeout = 0;
+	sbi->oz_pgrp = current->pgrp;
+	autofs_initialize_hash(&sbi->dirhash);
+	sbi->queues = NULL;
+	memset(sbi->symlink_bitmap, 0, sizeof(u32)*AUTOFS_SYMLINK_BITMAP_LEN);
+	sbi->next_dir_ino = AUTOFS_FIRST_DIR_INO;
+        s->s_blocksize = 1024;
+        s->s_blocksize_bits = 10;
+        s->s_magic = AUTOFS_SUPER_MAGIC;
+        s->s_op = &autofs_sops;
+        unlock_super(s);
+        if (!(s->s_mounted = iget(s, AUTOFS_ROOT_INO))) {
+                s->s_dev = 0;
+		kfree(sbi);
+                printk("autofs: get root inode failed\n");
+		MOD_DEC_USE_COUNT;
+                return NULL;
+        }
+
+        if ( parse_options(data,&pipefd,&s->s_mounted->i_uid,&s->s_mounted->i_gid,&sbi->oz_pgrp,&minproto,&maxproto) ) {
+		iput(s->s_mounted);
+                s->s_dev = 0;
+		kfree(sbi);
+                printk("autofs: called with bogus options\n");
+		MOD_DEC_USE_COUNT;
+                return NULL;
+        }
+
+	if ( minproto > AUTOFS_PROTO_VERSION || maxproto < AUTOFS_PROTO_VERSION ) {
+		iput(s->s_mounted);
+		s->s_dev = 0;
+		kfree(sbi);
+		printk("autofs: kernel does not match daemon version\n");
+		MOD_DEC_USE_COUNT;
+		return NULL;
+	}
+
+	DPRINTK(("autofs: pipe fd = %d, pgrp = %u\n", pipefd, sbi->oz_pgrp));
+	sbi->pipe = fget(pipefd);
+	if ( !sbi->pipe || !sbi->pipe->f_op || !sbi->pipe->f_op->write ) {
+		if ( sbi->pipe ) {
+			fput(sbi->pipe, sbi->pipe->f_inode);
+			printk("autofs: pipe file descriptor does not contain proper ops\n");
+		} else {
+			printk("autofs: could not open pipe file descriptor\n");
+		}
+		iput(s->s_mounted);
+		s->s_dev = 0;
+		kfree(sbi);
+		MOD_DEC_USE_COUNT;
+		return NULL;
+	}
+        return s;
+}
+
+static void autofs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+{
+        struct statfs tmp;
+
+        tmp.f_type = AUTOFS_SUPER_MAGIC;
+        tmp.f_bsize = 1024;
+        tmp.f_blocks = 0;
+        tmp.f_bfree = 0;
+        tmp.f_bavail = 0;
+        tmp.f_files = 0;
+        tmp.f_ffree = 0;
+        tmp.f_namelen = NAME_MAX;
+        copy_to_user(buf, &tmp, bufsiz);
+}
+
+static void autofs_read_inode(struct inode *inode)
+{
+        ino_t ino = inode->i_ino;
+	unsigned int n;
+        struct autofs_sb_info *sbi =
+		(struct autofs_sb_info *) inode->i_sb->u.generic_sbp;
+
+        inode->i_op = NULL;
+        inode->i_mode = 0;
+        inode->i_nlink = 2;
+        inode->i_size = 0;
+        inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+        inode->i_blocks = 0;
+        inode->i_blksize = 1024;
+
+        if ( ino == AUTOFS_ROOT_INO ) {
+                inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR;
+                inode->i_op = &autofs_root_inode_operations;
+		inode->i_uid = inode->i_gid = 0; /* Changed in read_super */
+                return;
+        } 
+	
+        inode->i_uid = inode->i_sb->s_mounted->i_uid;
+        inode->i_gid = inode->i_sb->s_mounted->i_gid;
+        
+	if ( ino >= AUTOFS_FIRST_SYMLINK && ino < AUTOFS_FIRST_DIR_INO ) {
+		/* Symlink inode - should be in symlink list */
+		struct autofs_symlink *sl;
+
+		n = ino - AUTOFS_FIRST_SYMLINK;
+		if ( n >= AUTOFS_MAX_SYMLINKS || !test_bit(n,sbi->symlink_bitmap)) {
+			printk("autofs: Looking for bad symlink inode 0x%08x\n", (unsigned int) ino);
+			return;
+		}
+		
+		inode->i_op = &autofs_symlink_inode_operations;
+		sl = &sbi->symlink[n];
+		inode->u.generic_ip = sl;
+		inode->i_mode = S_IFLNK | S_IRWXUGO;
+		inode->i_mtime = inode->i_ctime = sl->mtime;
+		inode->i_size = sl->len;
+		inode->i_nlink = 1;
+	} else {
+		/* All non-root directory inodes look the same */
+		inode->i_op = &autofs_dir_inode_operations;
+		inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
+	}
+}
+
+static void autofs_write_inode(struct inode *inode)
+{
+        inode->i_dirt = 0;
+}
diff -u --recursive --new-file v2.0.30/linux/fs/autofs/root.c linux/fs/autofs/root.c
--- v2.0.30/linux/fs/autofs/root.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/autofs/root.c	Fri Sep  5 20:43:59 1997
@@ -0,0 +1,445 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/fs/autofs/root.c
+ *
+ *  Copyright 1997 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/param.h>
+#include "autofs_i.h"
+
+static int autofs_root_readdir(struct inode *,struct file *,void *,filldir_t);
+static int autofs_root_lookup(struct inode *,const char *,int,struct inode **);
+static int autofs_root_symlink(struct inode *,const char *,int,const char *);
+static int autofs_root_unlink(struct inode *,const char *,int);
+static int autofs_root_rmdir(struct inode *,const char *,int);
+static int autofs_root_mkdir(struct inode *,const char *,int,int);
+static int autofs_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long);
+
+static struct file_operations autofs_root_operations = {
+        NULL,                   /* lseek */
+        NULL,                   /* read */
+        NULL,                   /* write */
+        autofs_root_readdir,    /* readdir */
+        NULL,                   /* select */
+        autofs_root_ioctl,	/* ioctl */
+        NULL,                   /* mmap */
+        NULL,                   /* open */
+        NULL,                   /* release */
+        NULL                    /* fsync */
+};
+
+struct inode_operations autofs_root_inode_operations = {
+        &autofs_root_operations, /* file operations */
+        NULL,                   /* create */
+        autofs_root_lookup,     /* lookup */
+        NULL,                   /* link */
+        autofs_root_unlink,     /* unlink */
+        autofs_root_symlink,    /* symlink */
+        autofs_root_mkdir,      /* mkdir */
+        autofs_root_rmdir,      /* rmdir */
+        NULL,                   /* mknod */
+        NULL,                   /* rename */
+        NULL,                   /* readlink */
+        NULL,                   /* follow_link */
+        NULL,                   /* readpage */
+        NULL,                   /* writepage */
+        NULL,                   /* bmap */
+        NULL,                   /* truncate */
+        NULL                    /* permission */
+};
+
+static int autofs_root_readdir(struct inode *inode, struct file *filp,
+			       void *dirent, filldir_t filldir)
+{
+	struct autofs_dir_ent *ent;
+	struct autofs_dirhash *dirhash;
+	off_t onr, nr;
+
+	if (!inode || !S_ISDIR(inode->i_mode))
+		return -ENOTDIR;
+
+	dirhash = &((struct autofs_sb_info *)inode->i_sb->u.generic_sbp)->dirhash;
+	nr = filp->f_pos;
+
+	switch(nr)
+	{
+	case 0:
+		if (filldir(dirent, ".", 1, nr, inode->i_ino) < 0)
+			return 0;
+		filp->f_pos = ++nr;
+		/* fall through */
+	case 1:
+		if (filldir(dirent, "..", 2, nr, inode->i_ino) < 0)
+			return 0;
+		filp->f_pos = ++nr;
+		/* fall through */
+	default:
+		while ( onr = nr, ent = autofs_hash_enum(dirhash,&nr) ) {
+			if (filldir(dirent,ent->name,ent->len,onr,ent->ino) < 0)
+				return 0;
+			filp->f_pos = nr;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+static int autofs_root_lookup(struct inode *dir, const char *name, int len,
+			      struct inode **result)
+{
+	struct autofs_sb_info *sbi;
+	struct autofs_dir_ent *ent;
+	struct inode *res;
+	autofs_hash_t hash;
+	int status, oz_mode;
+
+	DPRINTK(("autofs_root_lookup: name = "));
+	autofs_say(name,len);
+
+	*result = NULL;
+	if (!dir)
+		return -ENOENT;
+	if (!S_ISDIR(dir->i_mode)) {
+		iput(dir);
+		return -ENOTDIR;
+	}
+
+	/* Handle special cases: . and ..; since this is a root directory,
+	   they both point to the inode itself */
+	*result = dir;
+	if (!len)
+		return 0;
+	if (name[0] == '.') {
+		if (len == 1)
+			return 0;
+		if (name[1] == '.' && len == 2)
+			return 0;
+	}
+
+	*result = res = NULL;
+	sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
+
+	hash = autofs_hash(name,len);
+
+	oz_mode = autofs_oz_mode(sbi);
+	DPRINTK(("autofs_lookup: pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d\n", current->pid, current->pgrp, sbi->catatonic, oz_mode));
+
+	do {
+		while ( !(ent = autofs_hash_lookup(&sbi->dirhash,hash,name,len)) ) {
+			DPRINTK(("lookup failed, pid = %u, pgrp = %u\n", current->pid, current->pgrp));
+			
+			if ( oz_mode ) {
+				iput(dir);
+				return -ENOENT;
+			} else {
+				status = autofs_wait(sbi,hash,name,len);
+				DPRINTK(("autofs_wait returned %d\n", status));
+				if ( status ) {
+					iput(dir);
+					return status;
+				}
+			}
+		}
+
+		DPRINTK(("lookup successful, inode = %08x\n", (unsigned int)ent->ino));
+
+		if (!(res = iget(dir->i_sb,ent->ino))) {
+			printk("autofs: iget returned null!\n");
+			iput(dir);
+			return -EACCES;
+		}
+		
+		if ( !oz_mode && S_ISDIR(res->i_mode) && res->i_sb == dir->i_sb ) {
+			/* Not a mount point yet, call 1-800-DAEMON */
+			DPRINTK(("autofs: waiting on non-mountpoint dir, inode = %lu, pid = %u, pgrp = %u\n", res->i_ino, current->pid, current->pgrp));
+			iput(res);
+			res = NULL;
+			status = autofs_wait(sbi,hash,name,len);
+			if ( status ) {
+				iput(dir);
+				return status;
+			}
+		}
+	} while(!res);
+	autofs_update_usage(&sbi->dirhash,ent);
+	
+	*result = res;
+	iput(dir);
+	return 0;
+}
+
+static int autofs_root_symlink(struct inode *dir, const char *name, int len, const char *symname)
+{
+	struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
+	struct autofs_dirhash *dh = &sbi->dirhash;
+	autofs_hash_t hash = autofs_hash(name,len);
+	struct autofs_dir_ent *ent;
+	unsigned int n;
+	int slsize;
+	struct autofs_symlink *sl;
+
+	DPRINTK(("autofs_root_symlink: %s <- ", symname));
+	autofs_say(name,len);
+
+	if ( !autofs_oz_mode(sbi) ) {
+		iput(dir);
+		return -EPERM;
+	}
+	if ( autofs_hash_lookup(dh,hash,name,len) ) {
+		iput(dir);
+		return -EEXIST;
+	}
+	n = find_first_zero_bit(sbi->symlink_bitmap,AUTOFS_MAX_SYMLINKS);
+	if ( n >= AUTOFS_MAX_SYMLINKS ) {
+		iput(dir);
+		return -ENOSPC;
+	}
+	set_bit(n,sbi->symlink_bitmap);
+	sl = &sbi->symlink[n];
+	sl->len = strlen(symname);
+	sl->data = kmalloc(slsize = sl->len+1, GFP_KERNEL);
+	if ( !sl->data ) {
+		clear_bit(n,sbi->symlink_bitmap);
+		iput(dir);
+		return -ENOSPC;
+	}
+	ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL);
+	if ( !ent ) {
+		kfree(sl->data);
+		clear_bit(n,sbi->symlink_bitmap);
+		iput(dir);
+		return -ENOSPC;
+	}
+	ent->name = kmalloc(len, GFP_KERNEL);
+	if ( !ent->name ) {
+		kfree(sl->data);
+		kfree(ent);
+		clear_bit(n,sbi->symlink_bitmap);
+		iput(dir);
+		return -ENOSPC;
+	}
+	memcpy(sl->data,symname,slsize);
+	sl->mtime = CURRENT_TIME;
+
+	ent->ino = AUTOFS_FIRST_SYMLINK + n;
+	ent->hash = hash;
+	memcpy(ent->name,name,ent->len = len);
+
+	autofs_hash_insert(dh,ent);
+	iput(dir);
+
+	return 0;
+}
+
+static int autofs_root_unlink(struct inode *dir, const char *name, int len)
+{
+	struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
+	struct autofs_dirhash *dh = &sbi->dirhash;
+	autofs_hash_t hash = autofs_hash(name,len);
+	struct autofs_dir_ent *ent;
+	unsigned int n;
+
+	iput(dir);		/* Nothing below can sleep */
+
+	if ( !autofs_oz_mode(sbi) )
+		return -EPERM;
+
+	ent = autofs_hash_lookup(dh,hash,name,len);
+	if ( !ent )
+		return -ENOENT;
+
+	n = ent->ino - AUTOFS_FIRST_SYMLINK;
+	if ( n >= AUTOFS_MAX_SYMLINKS || !test_bit(n,sbi->symlink_bitmap) )
+		return -EINVAL;	/* Not a symlink inode, can't unlink */
+
+	autofs_hash_delete(ent);
+	clear_bit(n,sbi->symlink_bitmap);
+	kfree(sbi->symlink[n].data);
+	
+	return 0;
+}
+
+static int autofs_root_rmdir(struct inode *dir, const char *name, int len)
+{
+	struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
+	struct autofs_dirhash *dh = &sbi->dirhash;
+	autofs_hash_t hash = autofs_hash(name,len);
+	struct autofs_dir_ent *ent;
+
+	if ( !autofs_oz_mode(sbi) ) {
+		iput(dir);
+		return -EPERM;
+	}
+	ent = autofs_hash_lookup(dh,hash,name,len);
+	if ( !ent ) {
+		iput(dir);
+		return -ENOENT;
+	}
+	if ( (unsigned int)ent->ino < AUTOFS_FIRST_DIR_INO ) {
+		iput(dir);
+		return -ENOTDIR; /* Not a directory */
+	}
+	autofs_hash_delete(ent);
+	dir->i_nlink--;
+	iput(dir);
+
+	return 0;
+}
+
+static int autofs_root_mkdir(struct inode *dir, const char *name, int len, int mode)
+{
+	struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
+	struct autofs_dirhash *dh = &sbi->dirhash;
+	autofs_hash_t hash = autofs_hash(name,len);
+	struct autofs_dir_ent *ent;
+
+	if ( !autofs_oz_mode(sbi) ) {
+		iput(dir);
+		return -EPERM;
+	}
+	ent = autofs_hash_lookup(dh,hash,name,len);
+	if ( ent ) {
+		iput(dir);
+		return -EEXIST;
+	}
+	if ( sbi->next_dir_ino < AUTOFS_FIRST_DIR_INO ) {
+		printk("autofs: Out of inode numbers -- what the heck did you do??\n");
+		iput(dir);
+		return -ENOSPC;
+	}
+	ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL);
+	if ( !ent ) {
+		iput(dir);
+		return -ENOSPC;
+	}
+	ent->name = kmalloc(len, GFP_KERNEL);
+	if ( !ent->name ) {
+		kfree(ent);
+		iput(dir);
+		return -ENOSPC;
+	}
+	ent->hash = hash;
+	memcpy(ent->name, name, ent->len = len);
+	ent->ino = sbi->next_dir_ino++;
+	autofs_hash_insert(dh,ent);
+	dir->i_nlink++;
+	iput(dir);
+
+	return 0;
+}
+
+/* Get/set timeout ioctl() operation */
+static inline int autofs_get_set_timeout(struct autofs_sb_info *sbi,
+					 unsigned long *p)
+{
+	int rv;
+	unsigned long ntimeout;
+
+#if LINUX_VERSION_CODE < kver(2,1,0)
+	if ( (rv = verify_area(VERIFY_WRITE, p, sizeof(unsigned long))) )
+		return rv;
+	ntimeout = get_user(p);
+	put_user(sbi->exp_timeout/HZ, p);
+#else
+	if ( (rv = get_user(ntimeout, p)) ||
+	     (rv = put_user(sbi->exp_timeout/HZ, p)) )
+		return rv;
+#endif
+
+	if ( ntimeout > ULONG_MAX/HZ )
+		sbi->exp_timeout = 0;
+	else
+		sbi->exp_timeout = ntimeout * HZ;
+
+	return 0;
+}
+
+/* Return protocol version */
+static inline int autofs_get_protover(int *p)
+{
+#if LINUX_VERSION_CODE < kver(2,1,0)
+	int rv;
+	if ( (rv = verify_area(VERIFY_WRITE, p, sizeof(int))) )
+		return rv;
+	put_user(AUTOFS_PROTO_VERSION, p);
+	return 0;
+#else
+	return put_user(AUTOFS_PROTO_VERSION, p);
+#endif
+}
+
+/* Perform an expiry operation */
+static inline int autofs_expire_run(struct autofs_sb_info *sbi,
+				    struct autofs_packet_expire *pkt_p)
+{
+	struct autofs_dir_ent *ent;
+	struct autofs_packet_expire pkt;
+	struct autofs_dirhash *dh = &(sbi->dirhash);
+	
+	memset(&pkt,0,sizeof pkt);
+
+	pkt.hdr.proto_version = AUTOFS_PROTO_VERSION;
+	pkt.hdr.type = autofs_ptype_expire;
+
+	if ( !sbi->exp_timeout ||
+	     !(ent = autofs_expire(dh,sbi->exp_timeout)) )
+		return -EAGAIN;
+
+	pkt.len = ent->len;
+	memcpy(pkt.name, ent->name, pkt.len);
+	pkt.name[pkt.len] = '\0';
+
+	if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) )
+		return -EFAULT;
+	
+	autofs_update_usage(dh,ent);
+
+	return 0;
+}
+
+/*
+ * ioctl()'s on the root directory is the chief method for the daemon to
+ * generate kernel reactions
+ */
+static int autofs_root_ioctl(struct inode *inode, struct file *filp,
+			     unsigned int cmd, unsigned long arg)
+{
+	struct autofs_sb_info *sbi =
+		(struct autofs_sb_info *)inode->i_sb->u.generic_sbp;
+
+	DPRINTK(("autofs_ioctl: cmd = 0x%08x, arg = 0x%08lx, sbi = %p, pgrp = %u\n",cmd,arg,sbi,current->pgrp));
+
+	if ( _IOC_TYPE(cmd) != _IOC_TYPE(AUTOFS_IOC_FIRST) ||
+	     _IOC_NR(cmd) - _IOC_NR(AUTOFS_IOC_FIRST) >= AUTOFS_IOC_COUNT )
+		return -ENOTTY;
+	
+	if ( !autofs_oz_mode(sbi) && !fsuser() )
+		return -EPERM;
+	
+	switch(cmd) {
+	case AUTOFS_IOC_READY:	/* Wait queue: go ahead and retry */
+		return autofs_wait_release(sbi,arg,0);
+	case AUTOFS_IOC_FAIL:	/* Wait queue: fail with ENOENT */
+		return autofs_wait_release(sbi,arg,-ENOENT);
+	case AUTOFS_IOC_CATATONIC: /* Enter catatonic mode (daemon shutdown) */
+		autofs_catatonic_mode(sbi);
+		return 0;
+	case AUTOFS_IOC_PROTOVER: /* Get protocol version */
+		return autofs_get_protover((int *)arg);
+	case AUTOFS_IOC_SETTIMEOUT:
+		return autofs_get_set_timeout(sbi,(unsigned long *)arg);
+	case AUTOFS_IOC_EXPIRE:
+		return autofs_expire_run(sbi,(struct autofs_packet_expire *)arg);
+	default:
+		return -ENOSYS;
+	}
+}
diff -u --recursive --new-file v2.0.30/linux/fs/autofs/symlink.c linux/fs/autofs/symlink.c
--- v2.0.30/linux/fs/autofs/symlink.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/autofs/symlink.c	Fri Sep  5 20:43:59 1997
@@ -0,0 +1,85 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/fs/autofs/symlink.c
+ *
+ *  Copyright 1997 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include <linux/string.h>
+#include <linux/sched.h>
+#include "autofs_i.h"
+
+static int autofs_follow_link(struct inode *dir, struct inode *inode,
+			      int flag, int mode, struct inode **res_inode)
+{
+	int error;
+	char *link;
+
+	*res_inode = NULL;
+	if (!dir) {
+		dir = current->fs->root;
+		dir->i_count++;
+	}
+	if (!inode) {
+		iput(dir);
+		return -ENOENT;
+	}
+	if (!S_ISLNK(inode->i_mode)) {
+		iput(dir);
+		*res_inode = inode;
+		return 0;
+	}
+	if (current->link_count > 5) {
+		iput(dir);
+		iput(inode);
+		return -ELOOP;
+	}
+	link = ((struct autofs_symlink *)inode->u.generic_ip)->data;
+	current->link_count++;
+	error = open_namei(link,flag,mode,res_inode,dir);
+	current->link_count--;
+	iput(inode);
+	return error;
+}
+
+static int autofs_readlink(struct inode *inode, char *buffer, int buflen)
+{
+	struct autofs_symlink *sl;
+	int len;
+
+	if (!S_ISLNK(inode->i_mode)) {
+		iput(inode);
+		return -EINVAL;
+	}
+	sl = (struct autofs_symlink *)inode->u.generic_ip;
+	len = sl->len;
+	if (len > buflen) len = buflen;
+	copy_to_user(buffer,sl->data,len);
+	iput(inode);
+	return len;
+}
+
+struct inode_operations autofs_symlink_inode_operations = {
+	NULL,			/* file operations */
+	NULL,			/* create */
+	NULL,			/* lookup */
+	NULL,			/* link */
+	NULL,			/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	autofs_readlink,	/* readlink */
+	autofs_follow_link,	/* follow_link */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	NULL			/* permission */
+};
diff -u --recursive --new-file v2.0.30/linux/fs/autofs/waitq.c linux/fs/autofs/waitq.c
--- v2.0.30/linux/fs/autofs/waitq.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/autofs/waitq.c	Fri Sep  5 20:43:59 1997
@@ -0,0 +1,171 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/fs/autofs/waitq.c
+ *
+ *  Copyright 1997 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include <linux/malloc.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/file.h>
+#include "autofs_i.h"
+
+/* We make this a static variable rather than a part of the superblock; it
+   is better if we don't reassign numbers easily even across filesystems */
+static int autofs_next_wait_queue = 1;
+
+void autofs_catatonic_mode(struct autofs_sb_info *sbi)
+{
+	struct autofs_wait_queue *wq, *nwq;
+
+	DPRINTK(("autofs: entering catatonic mode\n"));
+
+	sbi->catatonic = 1;
+	wq = sbi->queues;
+	sbi->queues = NULL;	/* Erase all wait queues */
+	while ( wq ) {
+		nwq = wq->next;
+		wq->status = -ENOENT; /* Magic is gone - report failure */
+		kfree(wq->name);
+		wq->name = NULL;
+		wake_up(&wq->queue);
+		wq = nwq;
+	}
+	fput(sbi->pipe, sbi->pipe->f_inode);	/* Close the pipe */
+}
+
+static int autofs_write(struct file *file, const void *addr, int bytes)
+{
+	unsigned short fs;
+	unsigned long old_signal;
+	const char *data = (const char *)addr;
+	int written = 0;
+
+	/** WARNING: this is not safe for writing more than PIPE_BUF bytes! **/
+
+	/* Save pointer to user space and point back to kernel space */
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+
+	old_signal = current->signal;
+
+	while ( bytes && (written = file->f_op->write(file->f_inode,file,data,bytes)) > 0 ) {
+		data += written;
+		bytes -= written;
+	}
+
+	if ( written == -EPIPE && !(old_signal & (1 << (SIGPIPE-1))) ) {
+		/* Keep the currently executing process from receiving a
+		   SIGPIPE unless it was already supposed to get one */
+		current->signal &= ~(1 << (SIGPIPE-1));
+	}
+	set_fs(fs);
+
+	return (bytes > 0);
+}
+	
+static void autofs_notify_daemon(struct autofs_sb_info *sbi, struct autofs_wait_queue *wq)
+{
+	struct autofs_packet_missing pkt;
+
+	DPRINTK(("autofs_wait: wait id = 0x%08lx, name = ", wq->wait_queue_token));
+	autofs_say(wq->name,wq->len);
+
+	memset(&pkt,0,sizeof pkt); /* For security reasons */
+
+	pkt.hdr.proto_version = AUTOFS_PROTO_VERSION;
+	pkt.hdr.type = autofs_ptype_missing;
+	pkt.wait_queue_token = wq->wait_queue_token;
+	pkt.len = wq->len;
+        memcpy(pkt.name, wq->name, pkt.len);
+	pkt.name[pkt.len] = '\0';
+
+	if ( autofs_write(sbi->pipe,&pkt,sizeof(struct autofs_packet_missing)) )
+		autofs_catatonic_mode(sbi);
+}
+
+int autofs_wait(struct autofs_sb_info *sbi, autofs_hash_t hash, const char *name, int len)
+{
+	struct autofs_wait_queue *wq;
+	int status;
+
+	for ( wq = sbi->queues ; wq ; wq = wq->next ) {
+		if ( wq->hash == hash &&
+		     wq->len == len &&
+		     wq->name && !memcmp(wq->name,name,len) )
+			break;
+	}
+	
+	if ( !wq ) {
+		/* Create a new wait queue */
+		wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL);
+		if ( !wq )
+			return -ENOMEM;
+
+		wq->name = kmalloc(len,GFP_KERNEL);
+		if ( !wq->name ) {
+			kfree(wq);
+			return -ENOMEM;
+		}
+		wq->wait_queue_token = autofs_next_wait_queue++;
+		init_waitqueue(&wq->queue);
+		wq->hash = hash;
+		wq->len = len;
+		wq->status = -EINTR; /* Status return if interrupted */
+		memcpy(wq->name, name, len);
+		wq->next = sbi->queues;
+		sbi->queues = wq;
+
+		/* autofs_notify_daemon() may block */
+		wq->wait_ctr = 2;
+		autofs_notify_daemon(sbi,wq);
+	} else
+		wq->wait_ctr++;
+
+	if ( wq->name ) {
+		/* wq->name is NULL if and only if the lock is released */
+		interruptible_sleep_on(&wq->queue);
+	} else {
+		DPRINTK(("autofs_wait: skipped sleeping\n"));
+	}
+
+	status = wq->status;
+
+	if ( ! --wq->wait_ctr )	/* Are we the last process to need status? */
+		kfree(wq);
+
+	return status;
+}
+
+
+int autofs_wait_release(struct autofs_sb_info *sbi, unsigned long wait_queue_token, int status)
+{
+	struct autofs_wait_queue *wq, **wql;
+
+	for ( wql = &sbi->queues ; (wq = *wql) ; wql = &wq->next ) {
+		if ( wq->wait_queue_token == wait_queue_token )
+			break;
+	}
+	if ( !wq )
+		return -EINVAL;
+
+	*wql = wq->next;	/* Unlink from chain */
+	kfree(wq->name);
+	wq->name = NULL;	/* Do not wait on this queue */
+
+	wq->status = status;
+
+	if ( ! --wq->wait_ctr )	/* Is anyone still waiting for this guy? */
+		kfree(wq);
+	else
+		wake_up(&wq->queue);
+
+	return 0;
+}
+
diff -u --recursive --new-file v2.0.30/linux/fs/bad_inode.c linux/fs/bad_inode.c
--- v2.0.30/linux/fs/bad_inode.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/bad_inode.c	Sat Aug 16 16:23:19 1997
@@ -0,0 +1,77 @@
+/*
+ *  linux/fs/bad_inode.c
+ *
+ *  Copyright (C) 1997, Stephen Tweedie
+ *
+ *  Provide stub functions for unreadable inodes
+ */
+
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/sched.h>
+
+static int return_EIO()
+{
+	return -EIO;
+}
+
+#define EIO_ERROR ((void *) (return_EIO))
+
+static struct file_operations bad_file_ops =
+{
+	EIO_ERROR,		/* lseek */
+	EIO_ERROR,		/* read */
+	EIO_ERROR,		/* write */
+	EIO_ERROR,		/* readdir */
+	EIO_ERROR,		/* select */
+	EIO_ERROR,		/* ioctl */
+	EIO_ERROR,		/* mmap */
+	EIO_ERROR,		/* open */
+	EIO_ERROR,		/* release */
+	EIO_ERROR,		/* fsync */
+	EIO_ERROR,		/* fasync */
+	EIO_ERROR,		/* check_media_change */
+	EIO_ERROR		/* revalidate */
+};
+
+static struct inode_operations bad_inode_ops =
+{
+	&bad_file_ops,		/* default file operations */
+	EIO_ERROR,		/* create */
+	EIO_ERROR,		/* lookup */
+	EIO_ERROR,		/* link */
+	EIO_ERROR,		/* unlink */
+	EIO_ERROR,		/* symlink */
+	EIO_ERROR,		/* mkdir */
+	EIO_ERROR,		/* rmdir */
+	EIO_ERROR,		/* mknod */
+	EIO_ERROR,		/* rename */
+	EIO_ERROR,		/* readlink */
+	EIO_ERROR,		/* follow_link */
+	EIO_ERROR,		/* readpage */
+	EIO_ERROR,		/* writepage */
+	EIO_ERROR,		/* bmap */
+	EIO_ERROR,		/* truncate */
+	EIO_ERROR,		/* permission */
+	EIO_ERROR		/* smap */
+};
+
+
+/* 
+ * When a filesystem is unable to read an inode due to an I/O error in
+ * its read_inode() function, it can call make_bad_inode() to return a
+ * set of stubs which will return EIO errors as required. 
+ *
+ * We only need to do limited initialisation: all other fields are
+ * preinitialised to zero automatically.
+ */
+void make_bad_inode(struct inode * inode) 
+{
+	inode->i_mode = S_IFREG;
+	inode->i_flags = S_BAD_INODE;
+	inode->i_atime = CURRENT_TIME;
+	inode->i_mtime = CURRENT_TIME;
+	inode->i_ctime = CURRENT_TIME;
+	inode->i_op = &bad_inode_ops;	
+}
+
diff -u --recursive --new-file v2.0.30/linux/fs/binfmt_elf.c linux/fs/binfmt_elf.c
--- v2.0.30/linux/fs/binfmt_elf.c	Thu Jan  2 10:29:30 1997
+++ linux/fs/binfmt_elf.c	Tue Aug 12 14:06:35 1997
@@ -880,7 +880,7 @@
  */
 static inline int maydump(struct vm_area_struct *vma)
 {
-	if (!(vma->vm_flags & (VM_READ|VM_WRITE|VM_EXEC)))
+	if (!(vma->vm_flags & (VM_READ|VM_EXEC)))
 		return 0;
 #if 1
 	if (vma->vm_flags & (VM_WRITE|VM_GROWSUP|VM_GROWSDOWN))
@@ -1140,7 +1140,7 @@
 		set_fs(fs);
 		
 		len = current->mm->arg_end - current->mm->arg_start;
-		len = len >= ELF_PRARGSZ ? ELF_PRARGSZ : len;
+		len = (len >= ELF_PRARGSZ-1) ? ELF_PRARGSZ-1 : len;
 		memcpy_fromfs(&psinfo.pr_psargs,
 			      (const char *)current->mm->arg_start, len);
 		for(i = 0; i < len; i++)
diff -u --recursive --new-file v2.0.30/linux/fs/buffer.c linux/fs/buffer.c
--- v2.0.30/linux/fs/buffer.c	Tue Apr  8 08:47:46 1997
+++ linux/fs/buffer.c	Tue Aug 19 15:18:16 1997
@@ -524,7 +524,9 @@
 			if (bh->b_size == size)
 				 continue;
 			
+			bhnext->b_count++;
 			wait_on_buffer(bh);
+			bhnext->b_count--;
 			if (bh->b_dev == dev && bh->b_size != size) {
 				clear_bit(BH_Dirty, &bh->b_state);
 				clear_bit(BH_Uptodate, &bh->b_state);
@@ -541,14 +543,11 @@
 static inline int can_reclaim(struct buffer_head *bh, int size)
 {
 	if (bh->b_count || 
-	    buffer_protected(bh) || buffer_locked(bh))
+	    buffer_protected(bh) ||
+	    buffer_locked(bh) ||
+	    mem_map[MAP_NR((unsigned long) bh->b_data)].count != 1 ||
+	    buffer_dirty(bh))
 		return 0;
-			 
-	if (mem_map[MAP_NR((unsigned long) bh->b_data)].count != 1 ||
-	    buffer_dirty(bh)) {
-		refile_buffer(bh);
-		return 0;
-	}
 
 	if (bh->b_size != size)
 		return 0;
@@ -557,38 +556,41 @@
 }
 
 /* find a candidate buffer to be reclaimed */
-static struct buffer_head *find_candidate(struct buffer_head *list,int *list_len,int size)
+static struct buffer_head *find_candidate(struct buffer_head *bh,
+					  int *list_len, int size)
 {
-	struct buffer_head *bh;
+	int behind = 0;
+
+	if (!bh)
+		goto no_candidate;
 	
-	for (bh = list; 
-	     bh && (*list_len) > 0; 
-	     bh = bh->b_next_free, (*list_len)--) {
+	for (; (*list_len) > 0; bh = bh->b_next_free, (*list_len)--) {
 		if (size != bh->b_size) {
 			/* this provides a mechanism for freeing blocks
 			   of other sizes, this is necessary now that we
 			   no longer have the lav code. */
 			try_to_free_buffer(bh,&bh,1);
+			if (!bh)
+				break;
 			continue;
 		}
-
-		if (buffer_locked(bh) && 
-		    (bh->b_list == BUF_LOCKED || bh->b_list == BUF_LOCKED1)) {
-			/* Buffers are written in the order they are placed 
-			   on the locked list. If we encounter a locked
-			   buffer here, this means that the rest of them
-			   are also locked */
-			(*list_len) = 0;
-			return NULL;
+		else if (buffer_locked(bh) && 
+			 (bh->b_list == BUF_LOCKED || bh->b_list == BUF_LOCKED1)) {
+			if (behind++ > 10) {
+				(*list_len) = 0;
+				goto no_candidate;
+			}
 		}
-
-		if (can_reclaim(bh,size))
-		    return bh;
+		else if (can_reclaim(bh,size))
+			return bh;
 	}
 
+no_candidate:
 	return NULL;
 }
 
+extern void allow_interrupts(void);
+
 static void refill_freelist(int size)
 {
 	struct buffer_head * bh;
@@ -612,14 +614,16 @@
 	}
 
 repeat:
+	allow_interrupts();
+	if(needed <= 0)
+		return;
+
 	/* OK, we cannot grow the buffer cache, now try to get some
 	   from the lru list */
 
 	/* First set the candidate pointers to usable buffers.  This
 	   should be quick nearly all of the time. */
 
-	if(needed <= 0) return;
-
 	for(i=0; i<BUF_DIRTY; i++){
 		buffers[i] = nr_buffers_type[i];
 		candidate[i] = find_candidate(lru_list[i], &buffers[i], size);
@@ -652,11 +656,14 @@
 			if (candidate[i] && !can_reclaim(candidate[i],size))
 				candidate[i] = find_candidate(candidate[i],&buffers[i], size);
 		}
-		if (needed >= 0)
-			goto repeat;
+		goto repeat;
 	}
-	
-	if(needed <= 0) return;
+
+	/* Dirty buffers should not overtake, wakeup_bdflush(1) calls
+	   bdflush and sleeps, therefore kswapd does his important work. */
+	if ((nr_buffers_type[BUF_DIRTY] > nr_buffers * bdf_prm.b_un.nfract/100) ||
+	    (nr_free_pages < min_free_pages))
+		wakeup_bdflush(1);
 	
 	/* Too bad, that was not enough. Try a little harder to grow some. */
 	
@@ -668,8 +675,9 @@
 	}
 
 	/* and repeat until we find something good */
-	if (!grow_buffers(GFP_ATOMIC, size))
-		wakeup_bdflush(1);
+	wakeup_bdflush(1);
+
+	/* decrease needed even if there is no success */
 	needed -= PAGE_SIZE;
 	goto repeat;
 }
@@ -693,6 +701,7 @@
 	   now so as to ensure that there are still clean buffers available
 	   for user processes to use (and dirty) */
 repeat:
+	allow_interrupts();
 	bh = get_hash_table(dev, block, size);
 	if (bh) {
 		if (!buffer_dirty(bh)) {
@@ -705,6 +714,7 @@
 	}
 
 	while(!free_list[isize]) {
+		allow_interrupts();
 		refill_freelist(size);
 	}
 	
@@ -919,6 +929,34 @@
 	wake_up(&buffer_wait);
 }
 
+/* 
+ * We can't put completed temporary IO buffer_heads directly onto the
+ * unused_list when they become unlocked, since the device driver
+ * end_request routines still expect access to the buffer_head's
+ * fields after the final unlock.  So, the device driver puts them on
+ * the reuse_list instead once IO completes, and we recover these to
+ * the unused_list here.
+ *
+ * The reuse_list receives buffers from interrupt routines, so we need
+ * to be IRQ-safe here (but note that interrupts only _add_ to the
+ * reuse_list, never take away. So we don't need to worry about the
+ * reuse_list magically emptying).
+ */
+static inline void recover_reusable_buffer_heads(void)
+{
+	if (reuse_list) {
+		struct buffer_head *head;
+
+		head = xchg(&reuse_list, NULL);
+	
+		do {
+			struct buffer_head *bh = head;
+			head = head->b_next_free;
+			put_unused_buffer_head(bh);
+		} while (head);
+	}
+}
+
 static void get_more_buffer_heads(void)
 {
 	struct buffer_head * bh;
@@ -946,38 +984,14 @@
 		 */
 		run_task_queue(&tq_disk);
 		sleep_on(&buffer_wait);
+		/*
+		 * After we wake up, check for released async buffer heads.
+		 */
+		recover_reusable_buffer_heads();
 	}
 
 }
 
-/* 
- * We can't put completed temporary IO buffer_heads directly onto the
- * unused_list when they become unlocked, since the device driver
- * end_request routines still expect access to the buffer_head's
- * fields after the final unlock.  So, the device driver puts them on
- * the reuse_list instead once IO completes, and we recover these to
- * the unused_list here.
- *
- * The reuse_list receives buffers from interrupt routines, so we need
- * to be IRQ-safe here (but note that interrupts only _add_ to the
- * reuse_list, never take away. So we don't need to worry about the
- * reuse_list magically emptying).
- */
-static inline void recover_reusable_buffer_heads(void)
-{
-	if (reuse_list) {
-		struct buffer_head *head;
-
-		head = xchg(&reuse_list, NULL);
-	
-		do {
-			struct buffer_head *bh = head;
-			head = head->b_next_free;
-			put_unused_buffer_head(bh);
-		} while (head);
-	}
-}
-
 static struct buffer_head * get_unused_buffer_head(void)
 {
 	struct buffer_head * bh;
@@ -1158,6 +1172,8 @@
 		free_async_buffers(bh);
 		restore_flags(flags);
 		after_unlock_page(page);
+		if (waitqueue_active(&buffer_wait))
+			wake_up(&buffer_wait);
 	}
 	++current->maj_flt;
 	return 0;
@@ -1496,6 +1512,7 @@
 		ndirty = 0;
 		nwritten = 0;
 	repeat:
+		allow_interrupts();
 
 		bh = lru_list[nlist];
 		if(bh) 
@@ -1520,6 +1537,7 @@
 				 ndirty++;
 				 if(bh->b_flushtime > jiffies) continue;
 				 nwritten++;
+				 next->b_count++;
 				 bh->b_count++;
 				 bh->b_flushtime = 0;
 #ifdef DEBUG
@@ -1527,8 +1545,10 @@
 #endif
 				 ll_rw_block(WRITE, 1, &bh);
 				 bh->b_count--;
+				 next->b_count--;
 			 }
 	}
+	run_task_queue(&tq_disk);
 #ifdef DEBUG
 	if (ncount) printk("sync_old_buffers: %d dirty buffers not on dirty list\n", ncount);
 	printk("Wrote %d/%d buffers\n", nwritten, ndirty);
@@ -1633,6 +1653,7 @@
 			 ndirty = 0;
 			 refilled = 0;
 		 repeat:
+			 allow_interrupts();
 
 			 bh = lru_list[nlist];
 			 if(bh) 
@@ -1660,6 +1681,7 @@
 					     currently dirty buffers are not shared, so it does not matter */
 					  if (refilled && major == LOOP_MAJOR)
 						   continue;
+					  next->b_count++;
 					  bh->b_count++;
 					  ndirty++;
 					  bh->b_flushtime = 0;
@@ -1675,6 +1697,7 @@
 					  if(nlist != BUF_DIRTY) ncount++;
 #endif
 					  bh->b_count--;
+					  next->b_count--;
 				  }
 		 }
 #ifdef DEBUG
@@ -1685,16 +1708,16 @@
 		 * dirty buffers, then make the next write to a
 		 * loop device to be a blocking write.
 		 * This lets us block--which we _must_ do! */
-		if (ndirty == 0 && nr_buffers_type[BUF_DIRTY] > 0) {
+		if (ndirty == 0 && nr_buffers_type[BUF_DIRTY] > 0 && wrta_cmd != WRITE) {
 			wrta_cmd = WRITE;
 			continue;
 		}
 		run_task_queue(&tq_disk);
-		wake_up(&bdflush_done);
 		
 		/* If there are still a lot of dirty buffers around, skip the sleep
 		   and flush some more */
-		if(nr_buffers_type[BUF_DIRTY] <= nr_buffers * bdf_prm.b_un.nfract/100) {
+		if(ndirty == 0 || nr_buffers_type[BUF_DIRTY] <= nr_buffers * bdf_prm.b_un.nfract/100) {
+			wake_up(&bdflush_done);
 			current->signal = 0;
 			interruptible_sleep_on(&bdflush_wait);
 		}
diff -u --recursive --new-file v2.0.30/linux/fs/dquot.c linux/fs/dquot.c
--- v2.0.30/linux/fs/dquot.c	Sun May 12 21:36:19 1996
+++ linux/fs/dquot.c	Sun Aug  3 15:31:34 1997
@@ -19,6 +19,8 @@
  * 
  * Fixes:   Dmitry Gorodchanin <begemot@bgm.rosprint.net>, 11 Feb 96
  *	    removed race conditions in dqput(), dqget() and iput(). 
+ *          Nick Kralevich <nickkral@cal.alumni.berkeley.edu>, 21 Jul 97
+ *          Fixed a condition where user and group quotas could get mixed up.
  *
  * (C) Copyright 1994, 1995 Marco van Wieringen 
  *
@@ -549,12 +551,14 @@
 repeat:
 	dquot = *(hash(dev, id, type));
 	while (dquot) {
-		if (dquot->dq_dev != dev || dquot->dq_id != id) {
+		if (dquot->dq_dev != dev || dquot->dq_id != id ||
+		    dquot->dq_type != type) {
 			dquot = dquot->dq_hash_next;
 			continue;
 		}
 		wait_on_dquot(dquot);
-		if (dquot->dq_dev != dev || dquot->dq_id != id)
+		if (dquot->dq_dev != dev || dquot->dq_id != id ||
+		    dquot->dq_type != type)
 			goto repeat;
 		if (!dquot->dq_count)
 			nr_free_dquots--;
diff -u --recursive --new-file v2.0.30/linux/fs/ext2/balloc.c linux/fs/ext2/balloc.c
--- v2.0.30/linux/fs/ext2/balloc.c	Thu Jan  4 04:14:20 1996
+++ linux/fs/ext2/balloc.c	Sat Aug 16 16:23:19 1997
@@ -43,6 +43,10 @@
 	unsigned long desc;
 	struct ext2_group_desc * gdp;
 
+	/*
+	 * This panic should never trigger on a bad filesystem: the caller
+	 * should have verified the block/group number already.
+	 */
 	if (block_group >= sb->u.ext2_sb.s_groups_count)
 		ext2_panic (sb, "get_group_desc",
 			    "block_group >= groups_count - "
@@ -63,22 +67,37 @@
 	return gdp + desc;
 }
 
-static void read_block_bitmap (struct super_block * sb,
-			       unsigned int block_group,
-			       unsigned long bitmap_nr)
+/*
+ * Read the bitmap for a given block_group, reading into the specified 
+ * slot in the superblock's bitmap cache.
+ *
+ * Return >=0 on success or a -ve error code.
+ */
+
+static int read_block_bitmap (struct super_block * sb,
+			      unsigned int block_group,
+			      unsigned long bitmap_nr)
 {
 	struct ext2_group_desc * gdp;
 	struct buffer_head * bh;
+	int retval = 0;
 	
 	gdp = get_group_desc (sb, block_group, NULL);
 	bh = bread (sb->s_dev, gdp->bg_block_bitmap, sb->s_blocksize);
-	if (!bh)
-		ext2_panic (sb, "read_block_bitmap",
+	if (!bh) {
+		ext2_error (sb, "read_block_bitmap",
 			    "Cannot read block bitmap - "
 			    "block_group = %d, block_bitmap = %lu",
 			    block_group, (unsigned long) gdp->bg_block_bitmap);
+		retval = -EIO;
+	}
+	/*
+	 * On IO error, just leave a zero in the superblock's block pointer for
+	 * this group.  The IO will be retried next time.
+	 */
 	sb->u.ext2_sb.s_block_bitmap_number[bitmap_nr] = block_group;
 	sb->u.ext2_sb.s_block_bitmap[bitmap_nr] = bh;
+	return retval;
 }
 
 /*
@@ -91,11 +110,13 @@
  * 1/ There is one cache per mounted file system.
  * 2/ If the file system contains less than EXT2_MAX_GROUP_LOADED groups,
  *    this function reads the bitmap without maintaining a LRU cache.
+ *
+ * Return the slot used to store the bitmap, or a -ve error code.
  */
 static int load__block_bitmap (struct super_block * sb,
 			       unsigned int block_group)
 {
-	int i, j;
+	int i, j, retval = 0;
 	unsigned long block_bitmap_number;
 	struct buffer_head * block_bitmap;
 
@@ -114,7 +135,9 @@
 			else
 				return block_group;
 		} else {
-			read_block_bitmap (sb, block_group, block_group);
+			retval = read_block_bitmap (sb, block_group, block_group);
+			if (retval < 0)
+				return retval;
 			return block_group;
 		}
 	}
@@ -134,6 +157,14 @@
 		}
 		sb->u.ext2_sb.s_block_bitmap_number[0] = block_bitmap_number;
 		sb->u.ext2_sb.s_block_bitmap[0] = block_bitmap;
+
+		/*
+		 * There's still one special case here --- if block_bitmap == 0
+		 * then our last attempt to read the bitmap failed and we have
+		 * just ended up caching that failure.  Try again to read it.
+		 */
+		if (!block_bitmap)
+			retval = read_block_bitmap (sb, block_group, 0);
 	} else {
 		if (sb->u.ext2_sb.s_loaded_block_bitmaps < EXT2_MAX_GROUP_LOADED)
 			sb->u.ext2_sb.s_loaded_block_bitmaps++;
@@ -145,24 +176,71 @@
 			sb->u.ext2_sb.s_block_bitmap[j] =
 				sb->u.ext2_sb.s_block_bitmap[j - 1];
 		}
-		read_block_bitmap (sb, block_group, 0);
+		retval = read_block_bitmap (sb, block_group, 0);
 	}
-	return 0;
+	return retval;
 }
 
+/*
+ * Load the block bitmap for a given block group.  First of all do a couple
+ * of fast lookups for common cases and then pass the request onto the guts
+ * of the bitmap loader.
+ *
+ * Return the slot number of the group in the superblock bitmap cache's on
+ * success, or a -ve error code.
+ *
+ * There is still one inconsistancy here --- if the number of groups in this
+ * filesystems is <= EXT2_MAX_GROUP_LOADED, then we have no way of 
+ * differentiating between a group for which we have never performed a bitmap
+ * IO request, and a group for which the last bitmap read request failed.
+ */
 static inline int load_block_bitmap (struct super_block * sb,
 				     unsigned int block_group)
 {
-	if (sb->u.ext2_sb.s_loaded_block_bitmaps > 0 &&
-	    sb->u.ext2_sb.s_block_bitmap_number[0] == block_group)
-		return 0;
+	int slot;
 	
-	if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED && 
-	    sb->u.ext2_sb.s_block_bitmap_number[block_group] == block_group &&
-	    sb->u.ext2_sb.s_block_bitmap[block_group]) 
-		return block_group;
+	/*
+	 * Do the lookup for the slot.  First of all, check if we're asking
+	 * for the same slot as last time, and did we succeed that last time?
+	 */
+	if (sb->u.ext2_sb.s_loaded_block_bitmaps > 0 &&
+	    sb->u.ext2_sb.s_block_bitmap_number[0] == block_group &&
+	    sb->u.ext2_sb.s_block_bitmap[block_group]) {
+		slot = 0;
+	}
+	/*
+	 * Or can we do a fast lookup based on a loaded group on a filesystem
+	 * small enough to be mapped directly into the superblock?
+	 */
+	else if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED && 
+		 sb->u.ext2_sb.s_block_bitmap_number[block_group] == block_group &&
+		 sb->u.ext2_sb.s_block_bitmap[block_group]) {
+		slot = block_group;
+	} 
+	/*
+	 * If not, then do a full lookup for this block group.
+	 */
+	else {
+		slot = load__block_bitmap (sb, block_group);
+	}
 
-	return load__block_bitmap (sb, block_group);
+	/*
+	 * <0 means we just got an error
+	 */
+	if (slot < 0)
+		return slot;
+	
+	/*
+	 * If it's a valid slot, we may still have cached a previous IO error,
+	 * in which case the bh in the superblock cache will be zero.
+	 */
+	if (!sb->u.ext2_sb.s_block_bitmap[slot])
+		return -EIO;
+	
+	/*
+	 * Must have been read in OK to get this far.
+	 */
+	return slot;
 }
 
 void ext2_free_blocks (const struct inode * inode, unsigned long block,
@@ -199,12 +277,19 @@
 	block_group = (block - es->s_first_data_block) /
 		      EXT2_BLOCKS_PER_GROUP(sb);
 	bit = (block - es->s_first_data_block) % EXT2_BLOCKS_PER_GROUP(sb);
-	if (bit + count > EXT2_BLOCKS_PER_GROUP(sb))
-		ext2_panic (sb, "ext2_free_blocks",
+	if (bit + count > EXT2_BLOCKS_PER_GROUP(sb)) {
+		ext2_error (sb, "ext2_free_blocks",
 			    "Freeing blocks across group boundary - "
 			    "Block = %lu, count = %lu",
 			    block, count);
+		unlock_super (sb);
+		return;
+	}
+	
 	bitmap_nr = load_block_bitmap (sb, block_group);
+	if (bitmap_nr < 0)
+		goto error_return;
+		
 	bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
 	gdp = get_group_desc (sb, block_group, &bh2);
 
@@ -214,11 +299,14 @@
 	     in_range (block, gdp->bg_inode_table,
 		       sb->u.ext2_sb.s_itb_per_group) ||
 	     in_range (block + count - 1, gdp->bg_inode_table,
-		       sb->u.ext2_sb.s_itb_per_group)))
-		ext2_panic (sb, "ext2_free_blocks",
+		       sb->u.ext2_sb.s_itb_per_group))) {
+		ext2_error (sb, "ext2_free_blocks",
 			    "Freeing blocks in system zones - "
 			    "Block = %lu, count = %lu",
 			    block, count);
+		unlock_super (sb);
+		return;
+	}
 
 	for (i = 0; i < count; i++) {
 		if (!clear_bit (bit + i, bh->b_data))
@@ -242,6 +330,7 @@
 		wait_on_buffer (bh);
 	}
 	sb->s_dirt = 1;
+error_return:
 	unlock_super (sb);
 	return;
 }
@@ -301,6 +390,12 @@
 			goal_attempts++;
 #endif
 		bitmap_nr = load_block_bitmap (sb, i);
+		if (bitmap_nr < 0) {
+			*err = -EIO;
+			unlock_super (sb);
+			return 0;
+		}
+		
 		bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
 
 		ext2_debug ("goal is at %d:%d.\n", i, j);
@@ -372,7 +467,14 @@
 		unlock_super (sb);
 		return 0;
 	}
+
 	bitmap_nr = load_block_bitmap (sb, i);
+	if (bitmap_nr < 0) {
+		*err = -EIO;
+		unlock_super (sb);
+		return 0;
+	}
+		
 	bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
 	r = memscan(bh->b_data, 0, EXT2_BLOCKS_PER_GROUP(sb) >> 3);
 	j = (r - bh->b_data) << 3;
@@ -415,10 +517,14 @@
 	if (test_opt (sb, CHECK_STRICT) &&
 	    (tmp == gdp->bg_block_bitmap ||
 	     tmp == gdp->bg_inode_bitmap ||
-	     in_range (tmp, gdp->bg_inode_table, sb->u.ext2_sb.s_itb_per_group)))
-		ext2_panic (sb, "ext2_new_block",
+	     in_range (tmp, gdp->bg_inode_table, sb->u.ext2_sb.s_itb_per_group))) {
+		ext2_error (sb, "ext2_new_block",
 			    "Allocating block in system zone - "
 			    "block = %u", tmp);
+		unlock_super (sb);
+		*err = -EIO;
+		return 0;
+	}
 
 	if (set_bit (j, bh->b_data)) {
 		ext2_warning (sb, "ext2_new_block",
@@ -511,7 +617,11 @@
 	for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
 		gdp = get_group_desc (sb, i, NULL);
 		desc_count += gdp->bg_free_blocks_count;
+
 		bitmap_nr = load_block_bitmap (sb, i);
+		if (bitmap_nr < 0)
+			continue;
+		
 		x = ext2_count_free (sb->u.ext2_sb.s_block_bitmap[bitmap_nr],
 				     sb->s_blocksize);
 		printk ("group %d: stored = %d, counted = %lu\n",
@@ -556,6 +666,9 @@
 		gdp = get_group_desc (sb, i, NULL);
 		desc_count += gdp->bg_free_blocks_count;
 		bitmap_nr = load_block_bitmap (sb, i);
+		if (bitmap_nr < 0)
+			continue;
+
 		bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
 
 		if (!test_bit (0, bh->b_data))
diff -u --recursive --new-file v2.0.30/linux/fs/ext2/dir.c linux/fs/ext2/dir.c
--- v2.0.30/linux/fs/ext2/dir.c	Fri Dec 22 03:00:19 1995
+++ linux/fs/ext2/dir.c	Wed Aug  6 17:52:01 1997
@@ -206,9 +206,6 @@
 		offset = 0;
 		brelse (bh);
 	}
-	if (!IS_RDONLY(inode)) {
-		inode->i_atime = CURRENT_TIME;
-		inode->i_dirt = 1;
-	}
+	UPDATE_ATIME(inode);
 	return 0;
 }
diff -u --recursive --new-file v2.0.30/linux/fs/ext2/ialloc.c linux/fs/ext2/ialloc.c
--- v2.0.30/linux/fs/ext2/ialloc.c	Sat Nov 30 02:21:20 1996
+++ linux/fs/ext2/ialloc.c	Sat Aug 16 16:23:19 1997
@@ -62,22 +62,37 @@
 	return gdp + desc;
 }
 
-static void read_inode_bitmap (struct super_block * sb,
-			       unsigned long block_group,
-			       unsigned int bitmap_nr)
+/*
+ * Read the inode allocation bitmap for a given block_group, reading
+ * into the specified slot in the superblock's bitmap cache.
+ *
+ * Return >=0 on success or a -ve error code.
+ */
+
+static int read_inode_bitmap (struct super_block * sb,
+			      unsigned long block_group,
+			      unsigned int bitmap_nr)
 {
 	struct ext2_group_desc * gdp;
 	struct buffer_head * bh;
+	int retval = 0;
 
 	gdp = get_group_desc (sb, block_group, NULL);
 	bh = bread (sb->s_dev, gdp->bg_inode_bitmap, sb->s_blocksize);
-	if (!bh)
-		ext2_panic (sb, "read_inode_bitmap",
+	if (!bh) {
+		ext2_error (sb, "read_inode_bitmap",
 			    "Cannot read inode bitmap - "
 			    "block_group = %lu, inode_bitmap = %lu",
 			    block_group, (unsigned long) gdp->bg_inode_bitmap);
+		retval = -EIO;
+	}
+	/*
+	 * On IO error, just leave a zero in the superblock's block pointer for
+	 * this group.  The IO will be retried next time.
+	 */
 	sb->u.ext2_sb.s_inode_bitmap_number[bitmap_nr] = block_group;
 	sb->u.ext2_sb.s_inode_bitmap[bitmap_nr] = bh;
+	return retval;
 }
 
 /*
@@ -90,11 +105,13 @@
  * 1/ There is one cache per mounted file system.
  * 2/ If the file system contains less than EXT2_MAX_GROUP_LOADED groups,
  *    this function reads the bitmap without maintaining a LRU cache.
+ *
+ * Return the slot used to store the bitmap, or a -ve error code.
  */
 static int load_inode_bitmap (struct super_block * sb,
 			      unsigned int block_group)
 {
-	int i, j;
+	int i, j, retval = 0;
 	unsigned long inode_bitmap_number;
 	struct buffer_head * inode_bitmap;
 
@@ -114,7 +131,9 @@
 			else
 				return block_group;
 		} else {
-			read_inode_bitmap (sb, block_group, block_group);
+			retval = read_inode_bitmap (sb, block_group, block_group);
+			if (retval < 0)
+				return retval;
 			return block_group;
 		}
 	}
@@ -135,6 +154,14 @@
 		}
 		sb->u.ext2_sb.s_inode_bitmap_number[0] = inode_bitmap_number;
 		sb->u.ext2_sb.s_inode_bitmap[0] = inode_bitmap;
+
+		/*
+		 * There's still one special case here --- if inode_bitmap == 0
+		 * then our last attempt to read the bitmap failed and we have
+		 * just ended up caching that failure.  Try again to read it.
+		 */
+		if (!inode_bitmap)
+			retval = read_inode_bitmap (sb, block_group, 0);
 	} else {
 		if (sb->u.ext2_sb.s_loaded_inode_bitmaps < EXT2_MAX_GROUP_LOADED)
 			sb->u.ext2_sb.s_loaded_inode_bitmaps++;
@@ -146,9 +173,9 @@
 			sb->u.ext2_sb.s_inode_bitmap[j] =
 				sb->u.ext2_sb.s_inode_bitmap[j - 1];
 		}
-		read_inode_bitmap (sb, block_group, 0);
+		retval = read_inode_bitmap (sb, block_group, 0);
 	}
-	return 0;
+	return retval;
 }
 
 void ext2_free_inode (struct inode * inode)
@@ -198,6 +225,11 @@
 	block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(sb);
 	bit = (inode->i_ino - 1) % EXT2_INODES_PER_GROUP(sb);
 	bitmap_nr = load_inode_bitmap (sb, block_group);
+	if (bitmap_nr < 0) {
+		unlock_super (sb);
+		return;
+	}
+	
 	bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr];
 	if (!clear_bit (bit, bh->b_data))
 		ext2_warning (sb, "ext2_free_inode",
@@ -352,6 +384,13 @@
 		return NULL;
 	}
 	bitmap_nr = load_inode_bitmap (sb, i);
+	if (bitmap_nr < 0) {
+		unlock_super (sb);
+		iput(inode);
+		*err = -EIO;
+		return NULL;
+	}
+	
 	bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr];
 	if ((j = find_first_zero_bit ((unsigned long *) bh->b_data,
 				      EXT2_INODES_PER_GROUP(sb))) <
@@ -465,6 +504,9 @@
 		gdp = get_group_desc (sb, i, NULL);
 		desc_count += gdp->bg_free_inodes_count;
 		bitmap_nr = load_inode_bitmap (sb, i);
+		if (bitmap_nr < 0)
+			continue;
+		
 		x = ext2_count_free (sb->u.ext2_sb.s_inode_bitmap[bitmap_nr],
 				     EXT2_INODES_PER_GROUP(sb) / 8);
 		printk ("group %d: stored = %d, counted = %lu\n",
@@ -497,6 +539,9 @@
 		gdp = get_group_desc (sb, i, NULL);
 		desc_count += gdp->bg_free_inodes_count;
 		bitmap_nr = load_inode_bitmap (sb, i);
+		if (bitmap_nr < 0)
+			continue;
+		
 		x = ext2_count_free (sb->u.ext2_sb.s_inode_bitmap[bitmap_nr],
 				     EXT2_INODES_PER_GROUP(sb) / 8);
 		if (gdp->bg_free_inodes_count != x)
diff -u --recursive --new-file v2.0.30/linux/fs/ext2/inode.c linux/fs/ext2/inode.c
--- v2.0.30/linux/fs/ext2/inode.c	Tue Mar 11 13:46:41 1997
+++ linux/fs/ext2/inode.c	Sat Aug 16 16:23:19 1997
@@ -432,9 +432,12 @@
 	group_desc = block_group >> EXT2_DESC_PER_BLOCK_BITS(inode->i_sb);
 	desc = block_group & (EXT2_DESC_PER_BLOCK(inode->i_sb) - 1);
 	bh = inode->i_sb->u.ext2_sb.s_group_desc[group_desc];
-	if (!bh)
-		ext2_panic (inode->i_sb, "ext2_read_inode",
+	if (!bh) {
+		ext2_error (inode->i_sb, "ext2_read_inode",
 			    "Descriptor not loaded");
+		goto bad_inode;
+	}
+	
 	gdp = (struct ext2_group_desc *) bh->b_data;
 	/*
 	 * Figure out the offset within the block group inode table
@@ -443,10 +446,13 @@
 		EXT2_INODE_SIZE(inode->i_sb);
 	block = gdp[desc].bg_inode_table +
 		(offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb));
-	if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize)))
-		ext2_panic (inode->i_sb, "ext2_read_inode",
-			    "unable to read i-node block - "
+	if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize))) {
+		ext2_error (inode->i_sb, "ext2_read_inode",
+			    "unable to read inode block - "
 			    "inode=%lu, block=%lu", inode->i_ino, block);
+		goto bad_inode;
+	}
+	
 	offset &= (EXT2_BLOCK_SIZE(inode->i_sb) - 1);
 	raw_inode = (struct ext2_inode *) (bh->b_data + offset);
 
@@ -504,6 +510,13 @@
 		inode->i_flags |= S_APPEND;
 	if (inode->u.ext2_i.i_flags & EXT2_IMMUTABLE_FL)
 		inode->i_flags |= S_IMMUTABLE;
+	if (inode->u.ext2_i.i_flags & EXT2_NOATIME_FL)
+		inode->i_flags |= MS_NOATIME;
+	return;
+	
+bad_inode:
+	make_bad_inode(inode);
+	return;
 }
 
 static int ext2_update_inode(struct inode * inode, int do_sync)
@@ -543,10 +556,23 @@
 		EXT2_INODE_SIZE(inode->i_sb);
 	block = gdp[desc].bg_inode_table +
 		(offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb));
-	if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize)))
-		ext2_panic (inode->i_sb, "ext2_write_inode",
-			    "unable to read i-node block - "
+	if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize))) {
+		ext2_error (inode->i_sb, "ext2_write_inode",
+			    "unable to read inode block - "
 			    "inode=%lu, block=%lu", inode->i_ino, block);
+		/*
+		 * Unfortunately we're in a lose-lose situation.  I think that
+		 * keeping the inode in-core with the dirty bit set is 
+		 * the worse option, since that will soak up inodes until
+		 * the end of the world.  Clearing the dirty bit is nasty if
+		 * we haven't succeeded in writing out, but it's less nasty
+		 * than the alternative. -- sct
+		 */
+		inode->i_dirt = 0;
+		
+		return -EIO;
+	}
+	
 	offset &= EXT2_BLOCK_SIZE(inode->i_sb) - 1;
 	raw_inode = (struct ext2_inode *) (bh->b_data + offset);
 
@@ -577,10 +603,11 @@
 		ll_rw_block (WRITE, 1, &bh);
 		wait_on_buffer (bh);
 		if (buffer_req(bh) && !buffer_uptodate(bh)) {
-			printk ("IO error syncing ext2 inode ["
-				"%s:%08lx]\n",
-				kdevname(inode->i_dev), inode->i_ino);
-			err = -1;
+			ext2_error (inode->i_sb, 
+				    "IO error syncing ext2 inode ["
+				    "%s:%08lx]\n",
+				    kdevname(inode->i_dev), inode->i_ino);
+			err = -EIO;
 		}
 	}
 	brelse (bh);
diff -u --recursive --new-file v2.0.30/linux/fs/ext2/ioctl.c linux/fs/ext2/ioctl.c
--- v2.0.30/linux/fs/ext2/ioctl.c	Tue Jul  2 09:08:42 1996
+++ linux/fs/ext2/ioctl.c	Wed Aug  6 17:52:01 1997
@@ -60,6 +60,10 @@
 			inode->i_flags |= S_IMMUTABLE;
 		else
 			inode->i_flags &= ~S_IMMUTABLE;
+		if (flags & EXT2_NOATIME_FL)
+			inode->i_flags |= MS_NOATIME;
+		else
+			inode->i_flags &= ~MS_NOATIME;
 		inode->i_ctime = CURRENT_TIME;
 		inode->i_dirt = 1;
 		return 0;
diff -u --recursive --new-file v2.0.30/linux/fs/ext2/namei.c linux/fs/ext2/namei.c
--- v2.0.30/linux/fs/ext2/namei.c	Sat Nov 30 02:21:20 1996
+++ linux/fs/ext2/namei.c	Mon Aug  4 12:30:53 1997
@@ -1015,10 +1015,15 @@
 			goto end_rename;
 	}
 	retval = -EPERM;
-	if (new_inode && (new_dir->i_mode & S_ISVTX) &&
-	    current->fsuid != new_inode->i_uid &&
-	    current->fsuid != new_dir->i_uid && !fsuser())
-		goto end_rename;
+	if (new_inode) {
+		if ((new_dir->i_mode & S_ISVTX) &&
+		    current->fsuid != new_inode->i_uid &&
+		    current->fsuid != new_dir->i_uid && !fsuser())
+			goto end_rename;
+		if (IS_APPEND(new_inode) || IS_IMMUTABLE(new_inode))
+			goto end_rename;
+	}
+	
 	if (S_ISDIR(old_inode->i_mode)) {
 		retval = -ENOTDIR;
 		if (new_inode && !S_ISDIR(new_inode->i_mode))
diff -u --recursive --new-file v2.0.30/linux/fs/ext2/symlink.c linux/fs/ext2/symlink.c
--- v2.0.30/linux/fs/ext2/symlink.c	Fri Apr 12 00:33:26 1996
+++ linux/fs/ext2/symlink.c	Wed Aug  6 17:52:01 1997
@@ -86,10 +86,7 @@
 		link = bh->b_data;
 	} else
 		link = (char *) inode->u.ext2_i.i_data;
-	if (!IS_RDONLY(inode)) {
-		inode->i_atime = CURRENT_TIME;
-		inode->i_dirt = 1;
-	}
+	UPDATE_ATIME(inode);
 	current->link_count++;
 	error = open_namei (link, flag, mode, res_inode, dir);
 	current->link_count--;
@@ -127,10 +124,7 @@
 		i++;
 		put_user (c, buffer++);
 	}
-	if (!IS_RDONLY(inode)) {
-		inode->i_atime = CURRENT_TIME;
-		inode->i_dirt = 1;
-	}
+	UPDATE_ATIME(inode);
 	iput (inode);
 	if (bh)
 		brelse (bh);
diff -u --recursive --new-file v2.0.30/linux/fs/file_table.c linux/fs/file_table.c
--- v2.0.30/linux/fs/file_table.c	Mon Aug  5 00:13:54 1996
+++ linux/fs/file_table.c	Fri Aug  8 19:30:47 1997
@@ -5,6 +5,7 @@
  */
 
 #include <linux/config.h>
+#include <linux/stddef.h>
 #include <linux/fs.h>
 #include <linux/string.h>
 #include <linux/mm.h>
@@ -129,11 +130,12 @@
 	do {
 		for (f = first_file, i=0; i < nr_files; i++, f = f->f_next)
 			if (!f->f_count) {
-				remove_file_free(f);
-				memset(f,0,sizeof(*f));
-				put_last_free(f);
+				/* The f_next pointer is followed by the f_prev pointer */
+				memset(f, 0, offsetof(struct file, f_next));
+				memset(&f->f_prev + 1, 0, sizeof(*f) - sizeof(f->f_prev) - offsetof(struct file, f_prev));
 				f->f_count = 1;
 				f->f_version = ++event;
+				first_file = f->f_next;
 				return f;
 			}
 	} while (nr_files < max && grow_files());
diff -u --recursive --new-file v2.0.30/linux/fs/filesystems.c linux/fs/filesystems.c
--- v2.0.30/linux/fs/filesystems.c	Thu Apr 25 02:32:39 1996
+++ linux/fs/filesystems.c	Fri Sep  5 20:43:59 1997
@@ -24,6 +24,7 @@
 #include <linux/ncp_fs.h>
 #include <linux/affs_fs.h>
 #include <linux/ufs_fs.h>
+#include <linux/auto_fs.h>
 #include <linux/major.h>
 
 extern void device_setup(void);
@@ -110,6 +111,9 @@
 	init_ufs_fs();
 #endif
 
+#ifdef CONFIG_AUTOFS_FS
+	init_autofs_fs();
+#endif
 	mount_root();
 	return 0;
 }
diff -u --recursive --new-file v2.0.30/linux/fs/isofs/inode.c linux/fs/isofs/inode.c
--- v2.0.30/linux/fs/isofs/inode.c	Sat Aug 17 11:19:28 1996
+++ linux/fs/isofs/inode.c	Fri Aug 15 11:11:45 1997
@@ -133,13 +133,7 @@
 			  !strcmp(this_char,"uid") ||
 			  !strcmp(this_char,"gid"))) {
 		  char * vpnt = value;
-		  unsigned int ivalue;
-		  ivalue = 0;
-		  while(*vpnt){
-		    if(*vpnt <  '0' || *vpnt > '9') break;
-		    ivalue = ivalue * 10 + (*vpnt - '0');
-		    vpnt++;
-		  }
+		  unsigned int ivalue = simple_strtoul(vpnt, &vpnt, 0);
 		  if (*vpnt) return 0;
 		  switch(*this_char) {
 		  case 'b':
@@ -193,6 +187,11 @@
   vol_desc_start=0;
   if (get_blkfops(MAJOR(dev))->ioctl!=NULL)
     {
+      /* Whoops.  We must save the old FS, since otherwise
+       * we would destroy the kernels idea about FS on root
+       * mount in read_super... [chexum]
+       */
+      unsigned long old_fs=get_fs();
       inode_fake.i_rdev=dev;
       ms_info.addr_format=CDROM_LBA;
       set_fs(KERNEL_DS);
@@ -200,7 +199,7 @@
 				       NULL,
 				       CDROMMULTISESSION,
 				       (unsigned long) &ms_info);
-      set_fs(USER_DS);
+      set_fs(old_fs);
 #if 0 
       printk("isofs.inode: CDROMMULTISESSION: rc=%d\n",i);
       if (i==0)
@@ -476,6 +475,34 @@
 		printk("_isofs_bmap: block<0");
 		return 0;
 	}
+
+	/*
+	 * If we are beyond the end of this file, don't give out any
+	 * blocks.
+	 */
+	if( (block << ISOFS_BUFFER_BITS(inode)) > inode->i_size )
+	  {
+	    off_t	max_legal_read_offset;
+
+	    /*
+	     * If we are *way* beyond the end of the file, print a message.
+	     * Access beyond the end of the file up to the next page boundary
+	     * is normal because of the way the page cache works.
+	     * In this case, we just return 0 so that we can properly fill
+	     * the page with useless information without generating any
+	     * I/O errors.
+	     */
+	    max_legal_read_offset = (inode->i_size + PAGE_SIZE - 1) 
+	      & ~(PAGE_SIZE - 1);
+	    if( (block << ISOFS_BUFFER_BITS(inode)) >= max_legal_read_offset )
+	      {
+
+		printk("_isofs_bmap: block>= EOF(%d, %ld)", block, 
+		       inode->i_size);
+	      }
+	    return 0;
+	  }
+
 	return (inode->u.isofs_i.i_first_extent >> ISOFS_BUFFER_BITS(inode)) + block;
 }
 
diff -u --recursive --new-file v2.0.30/linux/fs/isofs/namei.c linux/fs/isofs/namei.c
--- v2.0.30/linux/fs/isofs/namei.c	Sat Nov 30 02:21:20 1996
+++ linux/fs/isofs/namei.c	Tue Aug 12 14:06:35 1997
@@ -242,9 +242,12 @@
 	if (!ino) {
 		char *lcname;
 
-		/* If mounted with check=relaxed (and most likely norock),
-		   then first convert this name to lower case. */
-		if (dir->i_sb->u.isofs_sb.s_name_check == 'r'
+		/* First try the original name. If that doesn't work and the fs
+		 * was mounted with check=relaxed, convert the name to lower
+		 * case and try again.
+		 */
+		if (!(bh = isofs_find_entry(dir,name,len, &ino, &ino_back))
+		    && dir->i_sb->u.isofs_sb.s_name_check == 'r'
 		    && (lcname = kmalloc(len, GFP_KERNEL)) != NULL) {
 			int i;
 			char c;
@@ -256,8 +259,7 @@
 			}
 			bh = isofs_find_entry(dir,lcname,len, &ino, &ino_back);
 			kfree(lcname);
-		} else
-			bh = isofs_find_entry(dir,name,len, &ino, &ino_back);
+		}
 
 		if (!bh) {
 			iput(dir);
diff -u --recursive --new-file v2.0.30/linux/fs/isofs/rock.c linux/fs/isofs/rock.c
--- v2.0.30/linux/fs/isofs/rock.c	Sat May 18 01:15:09 1996
+++ linux/fs/isofs/rock.c	Fri Aug 15 11:10:51 1997
@@ -287,7 +287,7 @@
 	CHECK_CE;
 	break;
       case SIG('E','R'):
-	printk("ISO9660 Extensions: ");
+	printk(KERN_DEBUG"ISO9660 Extensions: ");
 	{ int p;
 	  for(p=0;p<rr->u.ER.len_id;p++) printk("%c",rr->u.ER.data[p]);
 	};
diff -u --recursive --new-file v2.0.30/linux/fs/locks.c linux/fs/locks.c
--- v2.0.30/linux/fs/locks.c	Fri Sep 20 07:00:35 1996
+++ linux/fs/locks.c	Mon Sep  1 11:20:42 1997
@@ -28,22 +28,22 @@
  *  dynamically with kmalloc()/kfree().
  *  Andy Walker (andy@lysaker.kvaerner.no), February 21, 1995
  *
- *  Implemented two lock personalities - F_FLOCK and F_POSIX.
+ *  Implemented two lock personalities - FL_FLOCK and FL_POSIX.
  *
- *  F_POSIX locks are created with calls to fcntl() and lockf() through the
+ *  FL_POSIX locks are created with calls to fcntl() and lockf() through the
  *  fcntl() system call. They have the semantics described above.
  *
- *  F_FLOCK locks are created with calls to flock(), through the flock()
+ *  FL_FLOCK locks are created with calls to flock(), through the flock()
  *  system call, which is new. Old C libraries implement flock() via fcntl()
  *  and will continue to use the old, broken implementation.
  *
- *  F_FLOCK locks follow the 4.4 BSD flock() semantics. They are associated
+ *  FL_FLOCK locks follow the 4.4 BSD flock() semantics. They are associated
  *  with a file pointer (filp). As a result they can be shared by a parent
  *  process and its children after a fork(). They are removed when the last
  *  file descriptor referring to the file pointer is closed (unless explicitly
  *  unlocked). 
  *
- *  F_FLOCK locks never deadlock, an existing lock is always removed before
+ *  FL_FLOCK locks never deadlock, an existing lock is always removed before
  *  upgrading from shared to exclusive (or vice versa). When this happens
  *  any processes blocked by the current lock are woken up and allowed to
  *  run before the new lock is applied.
@@ -76,22 +76,32 @@
  *  flock() and fcntl().
  *  Andy Walker (andy@lysaker.kvaerner.no), April 29, 1996.
  *
- *  Allow only one type of locking scheme (F_POSIX or F_FLOCK) to be in use
- *  for a given file at a time. Changed the CONFIG_MANDATORY_OPTION scheme to
+ *  Allow only one type of locking scheme (FL_POSIX or FL_FLOCK) to be in use
+ *  for a given file at a time. Changed the CONFIG_LOCK_MANDATORY scheme to
  *  guarantee sensible behaviour in the case where file system modules might
  *  be compiled with different options than the kernel itself.
  *  Andy Walker (andy@lysaker.kvaerner.no), May 15, 1996.
  *
- *  Added a couple of missing wake_up() calls.
+ *  Added a couple of missing wake_up() calls. Thanks to Thomas Meckel
+ *  (Thomas.Meckel@mni.fh-giessen.de) for spotting this.
  *  Andy Walker (andy@lysaker.kvaerner.no), May 15, 1996.
  *
- *  TODO: Do not honour mandatory locks on remote file systems. This matches
- *        the SVR4 semantics and neatly sidesteps a pile of awkward issues that
- *        would otherwise have to be addressed.
+ *  Changed FL_POSIX locks to use the block list in the same way as FL_FLOCK
+ *  locks. Changed process synchronisation to avoid dereferencing locks that
+ *  have already been freed.
+ *  Andy Walker (andy@lysaker.kvaerner.no), Sep 21, 1996.
+ *
+ *  Made the block list a circular list to minimise searching in the list.
+ *  Andy Walker (andy@lysaker.kvaerner.no), Sep 25, 1996.
+ *
+ *  Made mandatory locking a mount option. Default is not to allow mandatory
+ *  locking.
+ *  Andy Walker (andy@lysaker.kvaerner.no), Oct 04, 1996.
+ *
+ *  Fixed /proc/locks interface so that we can't overrun the buffer we are handed.
+ *  Andy Walker (andy@lysaker.kvaerner.no), May 12, 1997.
  */
 
-#include <linux/config.h>
-
 #include <linux/malloc.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
@@ -118,76 +128,117 @@
 			   unsigned int wait);
 static int posix_locks_deadlock(struct task_struct *my_task,
 				struct task_struct *blocked_task);
-static int locks_overlap(struct file_lock *fl1, struct file_lock *fl2);
 static void posix_remove_locks(struct file_lock **before, struct task_struct *task);
 static void flock_remove_locks(struct file_lock **before, struct file *filp);
 
 static struct file_lock *locks_alloc_lock(struct file_lock *fl);
 static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl);
-static void locks_delete_lock(struct file_lock **fl, unsigned int wait);
-static char *lock_get_status(struct file_lock *fl, char *p, int id, char *pfx);
+static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait);
+static char *lock_get_status(struct file_lock *fl, int id, char *pfx);
+
+static void locks_insert_block(struct file_lock *blocker, struct file_lock *waiter);
+static void locks_delete_block(struct file_lock *blocker, struct file_lock *waiter);
+static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait);
 
 static struct file_lock *file_lock_table = NULL;
-static struct file_lock *unused_file_locks = NULL;
 
-/*
- * Free lock not inserted in any queue
- *
- * Careful! We can't just "kfree()" it: there may be other processes
- * that have yet to remove themselves from the wait queues. Thus the
- * internal memory management.
+/* Free lock not inserted in any queue.
  */
 static inline void locks_free_lock(struct file_lock *fl)
 {
-	struct file_lock *next = unused_file_locks;
-	unused_file_locks = fl;
-	fl->fl_next = next;
-}
-
-/* Add lock fl to the blocked list pointed to by block.
- * We search to the end of the existing list and insert the the new
- * struct. This ensures processes will be woken up in the order they
- * blocked.
- * NOTE: nowhere does the documentation insist that processes be woken
- * up in this order, but it seems like the reasonable thing to do.
- * If the blocked list gets long then this search could get expensive,
- * in which case we could consider waking the processes up in reverse
- * order, or making the blocked list a doubly linked circular list.
- * 
- * This functions are called only from one place (flock_lock_file)
- * so they are inlined now. -- Dmitry Gorodchanin 02/09/96.
- */
-
-static inline void locks_insert_block(struct file_lock *bfl, 
-				      struct file_lock *fl)
+	if (waitqueue_active(&fl->fl_wait))
+		panic("Aarggh: attempting to free lock with active wait queue - shoot Andy");
+
+	if (fl->fl_nextblock != NULL || fl->fl_prevblock != NULL)
+		panic("Aarggh: attempting to free lock with active block list - shoot Andy");
+		
+	kfree(fl);
+	return;
+}
+
+/* Check if two locks overlap each other.
+ */
+static inline int locks_overlap(struct file_lock *fl1, struct file_lock *fl2)
 {
-	while (bfl->fl_block != NULL) {
-		bfl = bfl->fl_block;
-	}
+	return ((fl1->fl_end >= fl2->fl_start) &&
+		(fl2->fl_end >= fl1->fl_start));
+}
+
+/* Insert waiter into blocker's block list.
+ * We use a circular list so that processes can be easily woken up in
+ * the order they blocked. The documentation doesn't require this but
+ * it seems seems like the reasonable thing to do.
+ */
+static void locks_insert_block(struct file_lock *blocker, 
+			       struct file_lock *waiter)
+{
+	struct file_lock *prevblock;
+
+	if (blocker->fl_prevblock == NULL)
+		/* No previous waiters - list is empty */
+		prevblock = blocker;
+	else
+		/* Previous waiters exist - add to end of list */
+		prevblock = blocker->fl_prevblock;
 
-	bfl->fl_block = fl;
-	fl->fl_block = NULL;
+	prevblock->fl_nextblock = waiter;
+	blocker->fl_prevblock = waiter;
+	waiter->fl_nextblock = blocker;
+	waiter->fl_prevblock = prevblock;
 	
 	return;
 }
 
-static inline void locks_delete_block(struct file_lock *bfl,
-				      struct file_lock *fl)
+/* Remove waiter from blocker's block list.
+ * When blocker ends up pointing to itself then the list is empty.
+ */
+static void locks_delete_block(struct file_lock *blocker,
+			       struct file_lock *waiter)
 {
-	struct file_lock *tfl;
+	struct file_lock *nextblock;
+	struct file_lock *prevblock;
 	
-	while ((tfl = bfl->fl_block) != NULL) {
-		if (tfl == fl) {
-			bfl->fl_block = fl->fl_block;
-			fl->fl_block = NULL;
-			return;
-		}
-		bfl = tfl;
+	nextblock = waiter->fl_nextblock;
+	prevblock = waiter->fl_prevblock;
+
+	if (nextblock == NULL)
+		return;
+	
+	nextblock->fl_prevblock = prevblock;
+	prevblock->fl_nextblock = nextblock;
+
+	waiter->fl_prevblock = waiter->fl_nextblock = NULL;
+	if (blocker->fl_nextblock == blocker)
+		/* No more locks on blocker's blocked list */
+		blocker->fl_prevblock = blocker->fl_nextblock = NULL;
+	return;
+}
+
+/* Wake up processes blocked waiting for blocker.
+ * If told to wait then schedule the processes until the block list
+ * is empty, otherwise empty the block list ourselves.
+ */
+static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait)
+{
+	struct file_lock *waiter;
+
+	while ((waiter = blocker->fl_nextblock) != NULL) {
+		wake_up(&waiter->fl_wait);
+		if (wait)
+			/* Let the blocked process remove waiter from the
+			 * block list when it gets scheduled.
+			 */
+			schedule();
+		else
+			/* Remove waiter from the block list, because by the
+			 * time it wakes up blocker won't exist any more.
+			 */
+			locks_delete_block(blocker, waiter);
 	}
 	return;
 }
 
-/* flock() system call entry point. Apply a FLOCK style lock to
+/* flock() system call entry point. Apply a FL_FLOCK style lock to
  * an open file descriptor.
  */
 asmlinkage int sys_flock(unsigned int fd, unsigned int cmd)
@@ -203,8 +254,8 @@
 	
 	if ((file_lock.fl_type != F_UNLCK) && !(filp->f_mode & 3))
 		return (-EBADF);
-	
-	return (flock_lock_file(filp, &file_lock, cmd & LOCK_UN ? 0 : cmd & LOCK_NB ? 0 : 1));
+
+	return (flock_lock_file(filp, &file_lock, (cmd & (LOCK_UN | LOCK_NB)) ? 0 : 1));
 }
 
 /* Report the first existing lock that would conflict with l.
@@ -224,26 +275,24 @@
 		return (error);
 
 	memcpy_fromfs(&flock, l, sizeof(flock));
-	if ((flock.l_type == F_UNLCK) || (flock.l_type == F_EXLCK) ||
-	    (flock.l_type == F_SHLCK))
+	if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK))
 		return (-EINVAL);
 
 	if (!filp->f_inode || !posix_make_lock(filp, &file_lock, &flock))
 		return (-EINVAL);
 
-	if ((fl = filp->f_inode->i_flock) && (fl->fl_flags & F_POSIX)) { 
-		while (fl != NULL) {
-			if (posix_locks_conflict(&file_lock, fl)) {
-				flock.l_pid = fl->fl_owner->pid;
-				flock.l_start = fl->fl_start;
-				flock.l_len = fl->fl_end == OFFSET_MAX ? 0 :
-					fl->fl_end - fl->fl_start + 1;
-				flock.l_whence = 0;
-				flock.l_type = fl->fl_type;
-				memcpy_tofs(l, &flock, sizeof(flock));
-				return (0);
-			}
-			fl = fl->fl_next;
+	for (fl = filp->f_inode->i_flock; fl != NULL; fl = fl->fl_next) {
+		if (!(fl->fl_flags & FL_POSIX))
+			break;
+		if (posix_locks_conflict(&file_lock, fl)) {
+			flock.l_pid = fl->fl_owner->pid;
+			flock.l_start = fl->fl_start;
+			flock.l_len = fl->fl_end == OFFSET_MAX ? 0 :
+				fl->fl_end - fl->fl_start + 1;
+			flock.l_whence = 0;
+			flock.l_type = fl->fl_type;
+			memcpy_tofs(l, &flock, sizeof(flock));
+			return (0);
 		}
 	}
 
@@ -265,8 +314,7 @@
 	struct flock flock;
 	struct inode *inode;
 
-	/*
-	 * Get arguments and validate them ...
+	/* Get arguments and validate them ...
 	 */
 
 	if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd]))
@@ -279,11 +327,12 @@
 	if (!(inode = filp->f_inode))
 		return (-EINVAL);
 	
-#ifdef CONFIG_LOCK_MANDATORY
 	/* Don't allow mandatory locks on files that may be memory mapped
 	 * and shared.
 	 */
-	if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID && inode->i_mmap) {
+	if (IS_MANDLOCK(inode) &&
+	    (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID &&
+	    inode->i_mmap) {
 		struct vm_area_struct *vma = inode->i_mmap;
 		do {
 			if (vma->vm_flags & VM_MAYSHARE)
@@ -291,23 +340,24 @@
 			vma = vma->vm_next_share;
 		} while (vma != inode->i_mmap);
 	}
-#endif
 
 	memcpy_fromfs(&flock, l, sizeof(flock));
 	if (!posix_make_lock(filp, &file_lock, &flock))
 		return (-EINVAL);
 	
 	switch (flock.l_type) {
-	case F_RDLCK :
+	case F_RDLCK:
 		if (!(filp->f_mode & 1))
 			return (-EBADF);
 		break;
-	case F_WRLCK :
+	case F_WRLCK:
 		if (!(filp->f_mode & 2))
 			return (-EBADF);
 		break;
-	case F_SHLCK :
-	case F_EXLCK :
+	case F_UNLCK:
+		break;
+	case F_SHLCK:
+	case F_EXLCK:
 #if 1
 /* warn a bit for now, but don't overdo it */
 {
@@ -323,10 +373,8 @@
 		if (!(filp->f_mode & 3))
 			return (-EBADF);
 		break;
-	case F_UNLCK :
-		break;
 	default:
-		return -EINVAL;
+		return (-EINVAL);
 	}
 	
 	return (posix_lock_file(filp, &file_lock, cmd == F_SETLKW));
@@ -343,7 +391,7 @@
 	 * close on that file.
 	 */
 	if ((fl = filp->f_inode->i_flock) != NULL) {
-		if (fl->fl_flags & F_POSIX)
+		if (fl->fl_flags & FL_POSIX)
 			posix_remove_locks(&filp->f_inode->i_flock, task);
 		else
 			flock_remove_locks(&filp->f_inode->i_flock, filp);
@@ -370,11 +418,11 @@
 {
 	struct file_lock *fl;
 
-	while ((fl = *before) != NULL) {
+ 	while ((fl = *before) != NULL) {
 		if ((fl->fl_file == filp) && (filp->f_count == 1))
-			locks_delete_lock(before, 0);
-		else
-			before = &fl->fl_next;
+ 			locks_delete_lock(before, 0);
+ 		else
+ 			before = &fl->fl_next;
 	}
 
 	return;
@@ -382,46 +430,43 @@
 
 int locks_verify_locked(struct inode *inode)
 {
-#ifdef CONFIG_LOCK_MANDATORY
 	/* Candidates for mandatory locking have the setgid bit set
 	 * but no group execute bit -  an otherwise meaningless combination.
 	 */
-	if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+	if (IS_MANDLOCK(inode) &&
+	    (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
 		return (locks_mandatory_locked(inode));
-#endif
 	return (0);
 }
 
 int locks_verify_area(int read_write, struct inode *inode, struct file *filp,
 		      unsigned int offset, unsigned int count)
 {
-#ifdef CONFIG_LOCK_MANDATORY	 
 	/* Candidates for mandatory locking have the setgid bit set
 	 * but no group execute bit -  an otherwise meaningless combination.
 	 */
-	if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+	if (IS_MANDLOCK(inode) &&
+	    (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
 		return (locks_mandatory_area(read_write, inode, filp, offset,
 					     count));
-#endif					     
 	return (0);
 }
 
 int locks_mandatory_locked(struct inode *inode)
 {
-#ifdef CONFIG_LOCK_MANDATORY	
 	struct file_lock *fl;
 
+	/* If there are no FL_POSIX locks then go ahead. */
+	if (!(fl = inode->i_flock) || !(fl->fl_flags & FL_POSIX))
+		return (0);
+
 	/* Search the lock list for this inode for any POSIX locks.
 	 */
-	if ((fl = inode->i_flock) && (fl->fl_flags & F_FLOCK))
-		return (0);
-	
 	while (fl != NULL) {
 		if (fl->fl_owner != current)
 			return (-EAGAIN);
 		fl = fl->fl_next;
 	}
-#endif
 	return (0);
 }
 
@@ -429,51 +474,53 @@
 			 struct file *filp, unsigned int offset,
 			 unsigned int count)
 {
-#ifdef CONFIG_LOCK_MANDATORY	
 	struct file_lock *fl;
+	struct file_lock tfl;
+
+	memset(&tfl, 0, sizeof(tfl));
+
+	tfl.fl_file = filp;
+	tfl.fl_flags = FL_POSIX | FL_ACCESS;
+	tfl.fl_owner = current;
+	tfl.fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK;
+	tfl.fl_start = offset;
+	tfl.fl_end = offset + count - 1;
 
 repeat:
-	/* Check that there are locks, and that they're not F_FLOCK locks.
-	 */
-	if ((fl = inode->i_flock) && (fl->fl_flags & F_FLOCK))
+	/* If there are no FL_POSIX locks then go ahead. */
+	if (!(fl = inode->i_flock) || !(fl->fl_flags & FL_POSIX))
 		return (0);
-	
-	/*
-	 * Search the lock list for this inode for locks that conflict with
+
+	/* Search the lock list for this inode for locks that conflict with
 	 * the proposed read/write.
 	 */
 	while (fl != NULL) {
-		if (fl->fl_owner == current ||
-		    fl->fl_end < offset || fl->fl_start >= offset + count)
-			goto next_lock;
-
-		/*
-		 * Block for writes against a "read" lock,
+		/* Block for writes against a "read" lock,
 		 * and both reads and writes against a "write" lock.
 		 */
-		if ((read_write == FLOCK_VERIFY_WRITE) ||
-		    (fl->fl_type == F_WRLCK)) {
+		if (posix_locks_conflict(fl, &tfl)) {
 			if (filp && (filp->f_flags & O_NONBLOCK))
 				return (-EAGAIN);
 			if (current->signal & ~current->blocked)
 				return (-ERESTARTSYS);
 			if (posix_locks_deadlock(current, fl->fl_owner))
 				return (-EDEADLK);
-			interruptible_sleep_on(&fl->fl_wait);
+
+			locks_insert_block(fl, &tfl);
+			interruptible_sleep_on(&tfl.fl_wait);
+			locks_delete_block(fl, &tfl);
+
 			if (current->signal & ~current->blocked)
 				return (-ERESTARTSYS);
-			/*
-			 * If we've been sleeping someone might have
+			/* If we've been sleeping someone might have
 			 * changed the permissions behind our back.
 			 */
 			if ((inode->i_mode & (S_ISGID | S_IXGRP)) != S_ISGID)
 				break;
 			goto repeat;
 		}
-	next_lock:
 		fl = fl->fl_next;
 	}
-#endif
 	return (0);
 }
 
@@ -485,37 +532,39 @@
 {
 	off_t start;
 
-	fl->fl_flags = F_POSIX;
+	memset(fl, 0, sizeof(*fl));
+	
+	fl->fl_flags = FL_POSIX;
 
 	switch (l->l_type) {
-	case F_RDLCK :
-	case F_WRLCK :
-	case F_UNLCK :
+	case F_RDLCK:
+	case F_WRLCK:
+	case F_UNLCK:
 		fl->fl_type = l->l_type;
 		break;
 	case F_SHLCK :
 		fl->fl_type = F_RDLCK;
-		fl->fl_flags |= F_BROKEN;
+		fl->fl_flags |= FL_BROKEN;
 		break;
 	case F_EXLCK :
 		fl->fl_type = F_WRLCK;
-		fl->fl_flags |= F_BROKEN;
+		fl->fl_flags |= FL_BROKEN;
 		break;
-	default :
+	default:
 		return (0);
 	}
 
 	switch (l->l_whence) {
-	case 0 : /*SEEK_SET*/
+	case 0: /*SEEK_SET*/
 		start = 0;
 		break;
-	case 1 : /*SEEK_CUR*/
+	case 1: /*SEEK_CUR*/
 		start = filp->f_pos;
 		break;
-	case 2 : /*SEEK_END*/
+	case 2: /*SEEK_END*/
 		start = filp->f_inode->i_size;
 		break;
-	default :
+	default:
 		return (0);
 	}
 
@@ -527,8 +576,7 @@
 	
 	fl->fl_file = filp;
 	fl->fl_owner = current;
-	fl->fl_wait = NULL;		/* just for cleanliness */
-	
+
 	return (1);
 }
 
@@ -538,29 +586,30 @@
 static int flock_make_lock(struct file *filp, struct file_lock *fl,
 			   unsigned int cmd)
 {
+	memset(fl, 0, sizeof(*fl));
+
 	if (!filp->f_inode)	/* just in case */
 		return (0);
 
 	switch (cmd & ~LOCK_NB) {
-	case LOCK_SH :
+	case LOCK_SH:
 		fl->fl_type = F_RDLCK;
 		break;
-	case LOCK_EX :
+	case LOCK_EX:
 		fl->fl_type = F_WRLCK;
 		break;
-	case LOCK_UN :
+	case LOCK_UN:
 		fl->fl_type = F_UNLCK;
 		break;
-	default :
+	default:
 		return (0);
 	}
 
-	fl->fl_flags = F_FLOCK;
+	fl->fl_flags = FL_FLOCK;
 	fl->fl_start = 0;
 	fl->fl_end = OFFSET_MAX;
 	fl->fl_file = filp;
 	fl->fl_owner = NULL;
-	fl->fl_wait = NULL;		/* just for cleanliness */
 	
 	return (1);
 }
@@ -602,10 +651,10 @@
 		return (0);
 
 	switch (caller_fl->fl_type) {
-	case F_RDLCK :
+	case F_RDLCK:
 		return (sys_fl->fl_type == F_WRLCK);
 		
-	case F_WRLCK :
+	case F_WRLCK:
 		return (1);
 
 	default:
@@ -616,14 +665,6 @@
 	return (0);	/* This should never happen */
 }
 
-/* Check if two locks overlap each other.
- */
-static int locks_overlap(struct file_lock *fl1, struct file_lock *fl2)
-{
-	return ((fl1->fl_end >= fl2->fl_start) &&
-		(fl2->fl_end >= fl1->fl_start));
-}
-
 /* This function tests for deadlock condition before putting a process to
  * sleep. The detection scheme is no longer recursive. Recursive was neat,
  * but dangerous - we risked stack corruption if the lock data was bad, or
@@ -637,34 +678,31 @@
 static int posix_locks_deadlock(struct task_struct *my_task,
 				struct task_struct *blocked_task)
 {
-	struct wait_queue *dlock_wait;
 	struct file_lock *fl;
+	struct file_lock *bfl;
 
 next_task:
 	if (my_task == blocked_task)
 		return (1);
 	for (fl = file_lock_table; fl != NULL; fl = fl->fl_nextlink) {
-		struct wait_queue *head;
-		if (fl->fl_owner == NULL || fl->fl_wait == NULL)
+		if (fl->fl_owner == NULL || fl->fl_nextblock == NULL)
 			continue;
-		head = WAIT_QUEUE_HEAD(&fl->fl_wait);
-		dlock_wait = fl->fl_wait;
-		while (dlock_wait != head) {
-			if (dlock_wait->task == blocked_task) {
+		for (bfl = fl->fl_nextblock; bfl != fl; bfl = bfl->fl_nextblock) {
+			if (bfl->fl_owner == blocked_task) {
 				if (fl->fl_owner == my_task) {
 					return (1);
 				}
 				blocked_task = fl->fl_owner;
 				goto next_task;
 			}
-			dlock_wait = dlock_wait->next;
 		}
 	}
 	return (0);
 }
 
-/* Try to create a FLOCK lock on filp. We always insert new locks at
- * the head of the list.
+/* Try to create a FLOCK lock on filp. We always insert new FLOCK locks at
+ * the head of the list, but that's secret knowledge known only to the next
+ * two functions.
  */
 static int flock_lock_file(struct file *filp, struct file_lock *caller,
 			   unsigned int wait)
@@ -676,7 +714,7 @@
 
 	before = &filp->f_inode->i_flock;
 
-	if ((fl = *before) && (fl->fl_flags & F_POSIX))
+	if ((fl = *before) && (fl->fl_flags & FL_POSIX))
 		return (-EBUSY);
 
 	while ((fl = *before) != NULL) {
@@ -698,51 +736,48 @@
 	if ((new_fl = locks_alloc_lock(caller)) == NULL)
 		return (-ENOLCK);
 repeat:
-	if ((fl = filp->f_inode->i_flock) && (fl->fl_flags & F_POSIX)) {
+	if ((fl = filp->f_inode->i_flock) && (fl->fl_flags & FL_POSIX)) {
 		locks_free_lock(new_fl);
 		return (-EBUSY);
 	}
 
 	while (fl != NULL) {
-		if (flock_locks_conflict(new_fl, fl)) {
-			if (!wait) {
-				locks_free_lock(new_fl);
-				return (-EAGAIN);
-			}
-			if (current->signal & ~current->blocked) {
-				/* Note: new_fl is not in any queue at this
-				 * point, so we must use locks_free_lock()
-				 * instead of locks_delete_lock()
-				 * 	Dmitry Gorodchanin 09/02/96.
-				 */
-				locks_free_lock(new_fl);
-				return (-ERESTARTSYS);
-			}
-			locks_insert_block(fl, new_fl);
-			interruptible_sleep_on(&new_fl->fl_wait);
-			wake_up(&new_fl->fl_wait);
-			if (current->signal & ~current->blocked) {
-				/* If we are here, than we were awakened
-				 * by a signal, so new_fl is still in the
-				 * block queue of fl. We need to remove 
-				 * new_fl and then free it.
-				 * 	Dmitry Gorodchanin 09/02/96.
-				 */
-				locks_delete_block(fl, new_fl);
-				locks_free_lock(new_fl);
-				return (-ERESTARTSYS);
-			}
-			goto repeat;
+		if (!flock_locks_conflict(new_fl, fl)) {
+			fl = fl->fl_next;
+			continue;
 		}
-		fl = fl->fl_next;
+		if (!wait) {
+			locks_free_lock(new_fl);
+			return (-EAGAIN);
+		}
+		if (current->signal & ~current->blocked) {
+			/* Note: new_fl is not in any queue at this
+			 * point, so we must use locks_free_lock()
+			 * instead of locks_delete_lock()
+			 * 	Dmitry Gorodchanin 09/02/96.
+			 */
+			locks_free_lock(new_fl);
+			return (-ERESTARTSYS);
+		}
+		locks_insert_block(fl, new_fl);
+		interruptible_sleep_on(&new_fl->fl_wait);
+		locks_delete_block(fl, new_fl);
+		if (current->signal & ~current->blocked) {
+			/* Awakened by a signal. Free the new
+			 * lock and return an error.
+			 */
+			locks_free_lock(new_fl);
+			return (-ERESTARTSYS);
+		}
+		goto repeat;
 	}
 	locks_insert_lock(&filp->f_inode->i_flock, new_fl);
 	return (0);
 }
 
 /* Add a POSIX style lock to a file.
- * We merge adjacent locks whenever possible. POSIX locks come after FLOCK
- * locks in the list and are sorted by owner task, then by starting address
+ * We merge adjacent locks whenever possible. POSIX locks are sorted by owner
+ * task, then by starting address
  *
  * Kai Petzke writes:
  * To make freeing a lock much faster, we keep a pointer to the lock before the
@@ -762,39 +797,44 @@
 	struct file_lock **before;
 	int added = 0;
 
-repeat:
-	if ((fl = filp->f_inode->i_flock) && (fl->fl_flags & F_FLOCK))
-		return (-EBUSY);
-
 	if (caller->fl_type != F_UNLCK) {
+  repeat:
+		if ((fl = filp->f_inode->i_flock) && (fl->fl_flags & FL_FLOCK))
+			return (-EBUSY);
+
 		while (fl != NULL) {
-			if (posix_locks_conflict(caller, fl)) {
-				if (!wait)
-					return (-EAGAIN);
-				if (current->signal & ~current->blocked)
-					return (-ERESTARTSYS);
-				if (posix_locks_deadlock(caller->fl_owner, fl->fl_owner))
-					return (-EDEADLK);
-				interruptible_sleep_on(&fl->fl_wait);
-				if (current->signal & ~current->blocked)
-					return (-ERESTARTSYS);
-				goto repeat;
+			if (!posix_locks_conflict(caller, fl)) {
+				fl = fl->fl_next;
+				continue;
 			}
-			fl = fl->fl_next;
+			if (!wait)
+				return (-EAGAIN);
+			if (current->signal & ~current->blocked)
+				return (-ERESTARTSYS);
+			if (posix_locks_deadlock(caller->fl_owner, fl->fl_owner))
+				return (-EDEADLK);
+			locks_insert_block(fl, caller);
+			interruptible_sleep_on(&caller->fl_wait);
+			locks_delete_block(fl, caller);
+			if (current->signal & ~current->blocked)
+				return (-ERESTARTSYS);
+			goto repeat;
   		}
   	}
-	/*
-	 * Find the first old lock with the same owner as the new lock.
+
+	/* Find the first old lock with the same owner as the new lock.
 	 */
 	
 	before = &filp->f_inode->i_flock;
 
-	/* First skip FLOCK locks and locks owned by other processes.
+	if ((*before != NULL) && ((*before)->fl_flags & FL_FLOCK))
+		return (-EBUSY);
+
+	/* First skip locks owned by other processes.
 	 */
 	while ((fl = *before) && (caller->fl_owner != fl->fl_owner)) {
 		before = &fl->fl_next;
 	}
-	
 
 	/* Process locks with this owner.
 	 */
@@ -862,7 +902,7 @@
 				 * as the change in lock type might satisfy
 				 * their needs.
 				 */
-				wake_up(&fl->fl_wait);
+				locks_wake_up_blocks(fl, 0);
 				fl->fl_start = caller->fl_start;
 				fl->fl_end = caller->fl_end;
 				fl->fl_type = caller->fl_type;
@@ -897,52 +937,43 @@
 			locks_insert_lock(before, left);
 		}
 		right->fl_start = caller->fl_end + 1;
-		wake_up(&right->fl_wait);
+		locks_wake_up_blocks(right, 0);
 	}
 	if (left) {
 		left->fl_end = caller->fl_start - 1;
-		wake_up(&left->fl_wait);
+		locks_wake_up_blocks(left, 0);
 	}
 	return (0);
 }
 
-/* Allocate memory for a new lock and initialize its fields from
- * fl. The lock is not inserted into any lists until locks_insert_lock()
- * or locks_insert_block() are called.
+/* Allocate new lock.
+ * Initialize its fields from fl. The lock is not inserted into any
+ * lists until locks_insert_lock() or locks_insert_block() are called.
  */
-
 static struct file_lock *locks_alloc_lock(struct file_lock *fl)
 {
-	struct file_lock *retval;
+	struct file_lock *tmp;
 
-	retval = unused_file_locks;
-	if (retval) {
-		unused_file_locks = retval->fl_next;
-		goto init_file_lock;
-	}
-	retval = (struct file_lock *)
-		kmalloc(sizeof(struct file_lock), GFP_ATOMIC);
-	if (retval) {
-		retval->fl_wait = NULL;
-init_file_lock:
-		retval->fl_next = NULL;
-		retval->fl_nextlink = NULL;
-		retval->fl_prevlink = NULL;
-		retval->fl_block = NULL;
-		retval->fl_owner = fl->fl_owner;
-		retval->fl_file = fl->fl_file;
-		retval->fl_flags = fl->fl_flags;
-		retval->fl_type = fl->fl_type;
-		retval->fl_start = fl->fl_start;
-		retval->fl_end = fl->fl_end;		
-	}
-	return retval;
+	/* Okay, let's make a new file_lock structure... */
+	if ((tmp = (struct file_lock *)kmalloc(sizeof(struct file_lock),
+					       GFP_ATOMIC)) == NULL)
+		return (tmp);
+
+	memset(tmp, 0, sizeof(*tmp));
+
+	tmp->fl_flags = fl->fl_flags;
+	tmp->fl_owner = fl->fl_owner;
+	tmp->fl_file = fl->fl_file;
+	tmp->fl_type = fl->fl_type;
+	tmp->fl_start = fl->fl_start;
+	tmp->fl_end = fl->fl_end;
+
+	return (tmp);
 }
 
 /* Insert file lock fl into an inode's lock list at the position indicated
  * by pos. At the same time add the lock to the global file lock list.
  */
-
 static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl)
 {
 	fl->fl_nextlink = file_lock_table;
@@ -957,63 +988,53 @@
 }
 
 /* Delete a lock and free it.
- * First remove our lock from the lock lists. Then remove all the blocked
- * locks from our blocked list, waking up the processes that own them. If
- * told to wait, then sleep on each of these lock's wait queues. Each
- * blocked process will wake up and immediately wake up its own wait queue
- * allowing us to be scheduled again. Lastly, wake up our own wait queue
- * before freeing the file_lock structure.
+ * First remove our lock from the active lock lists. Then call
+ * locks_wake_up_blocks() to wake up processes that are blocked
+ * waiting for this lock. Finally free the lock structure.
  */
-
-static void locks_delete_lock(struct file_lock **fl_p, unsigned int wait)
+static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait)
 {
-	struct file_lock *fl;
-	struct file_lock *pfl;
-	struct file_lock *nfl;
+	struct file_lock *thisfl;
+	struct file_lock *prevfl;
+	struct file_lock *nextfl;
 	
-	fl = *fl_p;
-	*fl_p = fl->fl_next;
-	pfl = fl->fl_prevlink;
-	nfl = fl->fl_nextlink;
+	thisfl = *thisfl_p;
+	*thisfl_p = thisfl->fl_next;
 
-	if (nfl != NULL)
-		nfl->fl_prevlink = pfl;
+	prevfl = thisfl->fl_prevlink;
+	nextfl = thisfl->fl_nextlink;
 
-	if (pfl != NULL)
-		pfl->fl_nextlink = nfl;
+	if (nextfl != NULL)
+		nextfl->fl_prevlink = prevfl;
+
+	if (prevfl != NULL)
+		prevfl->fl_nextlink = nextfl;
 	else
-		file_lock_table = nfl;
+		file_lock_table = nextfl;
 	
-	while ((nfl = fl->fl_block) != NULL) {
-		fl->fl_block = nfl->fl_block;
-		nfl->fl_block = NULL;
-		wake_up(&nfl->fl_wait);
-		if (wait)
-			sleep_on(&nfl->fl_wait);
-	}
-
-	wake_up(&fl->fl_wait);
-	locks_free_lock(fl);
+	locks_wake_up_blocks(thisfl, wait);
+	locks_free_lock(thisfl);
 
 	return;
 }
 
 
-static char *lock_get_status(struct file_lock *fl, char *p, int id, char *pfx)
+static char *lock_get_status(struct file_lock *fl, int id, char *pfx)
 {
-	struct wait_queue *wt;
+	static char temp[129];
+	char *p = temp;
+	struct inode *inode;
+
+	inode = fl->fl_file->f_inode;
 
 	p += sprintf(p, "%d:%s ", id, pfx);
-	if (fl->fl_flags & F_POSIX) {
-#ifdef CONFIG_LOCK_MANDATORY	 
-		p += sprintf(p, "%s %s ",
-			     (fl->fl_flags & F_BROKEN) ? "BROKEN" : "POSIX ",
-			     ((fl->fl_file->f_inode->i_mode & (S_IXGRP | S_ISGID))
-			      == S_ISGID) ? "MANDATORY" : "ADVISORY ");
-#else
-		p += sprintf(p, "%s ADVISORY ",
-			     (fl->fl_flags & F_BROKEN) ? "BROKEN" : "POSIX ");
-#endif
+	if (fl->fl_flags & FL_POSIX) {
+		p += sprintf(p, "%6s %s ",
+			     (fl->fl_flags & FL_BROKEN) ? "BROKEN" :
+			     (fl->fl_flags & FL_ACCESS) ? "ACCESS" : "POSIX ",
+			     (IS_MANDLOCK(inode) &&
+			      (inode->i_mode & (S_IXGRP | S_ISGID)) == S_ISGID) ?
+			     "MANDATORY" : "ADVISORY ");
 	}
 	else {
 		p += sprintf(p, "FLOCK  ADVISORY  ");
@@ -1021,36 +1042,70 @@
 	p += sprintf(p, "%s ", (fl->fl_type == F_RDLCK) ? "READ " : "WRITE");
 	p += sprintf(p, "%d %s:%ld %ld %ld ",
 		     fl->fl_owner ? fl->fl_owner->pid : 0,
-		     kdevname(fl->fl_file->f_inode->i_dev),
-		     fl->fl_file->f_inode->i_ino, fl->fl_start,
+		     kdevname(inode->i_dev), inode->i_ino, fl->fl_start,
 		     fl->fl_end);
-	p += sprintf(p, "%08lx %08lx %08lx %08lx %08lx\n%d:%s",
-		     (long)fl, (long)fl->fl_prevlink, (long)fl->fl_nextlink,
-		     (long)fl->fl_next, (long)fl->fl_block, id, pfx);
-	if ((wt = fl->fl_wait) != NULL) {
-		struct wait_queue *head = WAIT_QUEUE_HEAD(&fl->fl_wait);
-		while (wt != head) {
-			p += sprintf(p, " %d", wt->task->pid);
-			wt = wt->next;
+	sprintf(p, "%08lx %08lx %08lx %08lx %08lx\n",
+		(long)fl, (long)fl->fl_prevlink, (long)fl->fl_nextlink,
+		(long)fl->fl_next, (long)fl->fl_nextblock);
+	return (temp);
+}
+
+static inline int copy_lock_status(char *p, char **q, off_t pos, int len,
+				   off_t offset, int length)
+{
+	int i;
+
+	i = pos - offset;
+	if (i > 0) {
+		if (i >= length) {
+			i = len + length - i;
+			memcpy(*q, p, i);
+			*q += i;
+			return (0);
 		}
+		if (i < len) {
+			p += len - i;
+		}
+		else
+			i = len;
+		memcpy(*q, p, i);
+		*q += i;
 	}
-	p += sprintf(p, "\n");
-	return (p);
+	
+	return (1);
 }
 
-int get_locks_status(char *buf)
+int get_locks_status(char *buffer, char **start, off_t offset, int length)
 {
 	struct file_lock *fl;
 	struct file_lock *bfl;
 	char *p;
+	char *q = buffer;
 	int i;
+	int len;
+	off_t pos = 0;
 
-	p = buf;
 	for (fl = file_lock_table, i = 1; fl != NULL; fl = fl->fl_nextlink, i++) {
-		p = lock_get_status(fl, p, i, "");
-		for (bfl = fl; bfl->fl_block != NULL; bfl = bfl->fl_block)
-			p = lock_get_status(bfl->fl_block, p, i, " ->");
-	}
-	return  (p - buf);
+		p = lock_get_status(fl, i, "");
+		len = strlen(p);
+		pos += len;
+		if (!copy_lock_status(p, &q, pos, len, offset, length))
+			goto done;
+		if ((bfl = fl->fl_nextblock) == NULL)
+			continue;
+		do {
+			p = lock_get_status(bfl, i, " ->");
+			len = strlen(p);
+			pos += len;
+			if (!copy_lock_status(p, &q, pos, len, offset, length))
+				goto done;
+		} while ((bfl = bfl->fl_nextblock) != fl);
+	}
+done:
+	if (q != buffer)
+		*start = buffer;
+	return (q - buffer);
 }
+
+
 
diff -u --recursive --new-file v2.0.30/linux/fs/namei.c linux/fs/namei.c
--- v2.0.30/linux/fs/namei.c	Mon Mar 17 17:28:54 1997
+++ linux/fs/namei.c	Sat Aug 16 16:23:19 1997
@@ -6,6 +6,8 @@
 
 /*
  * Some corrections by tytso.
+ *
+ * Aug 97 - cevans - fix security problem with O_TRUNC and append only files
  */
 
 #include <asm/segment.h>
@@ -282,6 +284,10 @@
 			return error;
 	} else
 		iput(base);
+	if ((inode->i_flags & S_BAD_INODE) != 0) {
+		iput(inode);
+		return -EIO;
+	}
 	*res_inode = inode;
 	return 0;
 }
@@ -424,8 +430,9 @@
 	}
 	/*
 	 * An append-only file must be opened in append mode for writing
+	 * Additionally, we must disallow O_TRUNC -- cevans
 	 */
-	if (IS_APPEND(inode) && ((flag & FMODE_WRITE) && !(flag & O_APPEND))) {
+	if (IS_APPEND(inode) && (((flag & FMODE_WRITE) && !(flag & O_APPEND)) || (flag & O_TRUNC))) {
 		iput(inode);
 		return -EPERM;
 	}
diff -u --recursive --new-file v2.0.30/linux/fs/ncpfs/dir.c linux/fs/ncpfs/dir.c
--- v2.0.30/linux/fs/ncpfs/dir.c	Sat Nov 30 02:21:21 1996
+++ linux/fs/ncpfs/dir.c	Thu Aug 14 21:02:34 1997
@@ -1067,7 +1067,6 @@
 	}
         if (ncp_find_dir_inode(dir, name) != NULL)
 	{
-		iput(dir);
                 error = -EBUSY;
         }
 	else
@@ -1115,7 +1114,6 @@
 	}
         if (ncp_find_dir_inode(dir, name) != NULL)
 	{
-		iput(dir);
                 error = -EBUSY;
         }
 	else
@@ -1228,13 +1226,13 @@
 static int
 utc2local(int time)
 {
-        return time - sys_tz.tz_minuteswest*60;
+        return time - sys_tz.tz_minuteswest*60 + sys_tz.tz_dsttime*3600;
 }
 
 static int
 local2utc(int time)
 {
-        return time + sys_tz.tz_minuteswest*60;
+        return time + sys_tz.tz_minuteswest*60 - sys_tz.tz_dsttime*3600;
 }
 
 /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
diff -u --recursive --new-file v2.0.30/linux/fs/ncpfs/inode.c linux/fs/ncpfs/inode.c
--- v2.0.30/linux/fs/ncpfs/inode.c	Sat Nov 30 02:21:21 1996
+++ linux/fs/ncpfs/inode.c	Tue Aug  5 09:33:12 1997
@@ -125,27 +125,36 @@
 	}
 }
 
+/*
+ * Defer release of inode_info and file_info structures until the inode
+ * has been cleared.  This avoids a race condition allowing the inode to 
+ * be put back in use before being cleared. Also, temporarily increment
+ * i_count after clear_inode() so that the inode can't be reused.
+ */
 static void
 ncp_put_inode(struct inode *inode)
 {
-        struct nw_file_info *finfo = NCP_FINFO(inode);
-	struct super_block *sb = inode->i_sb;
+	struct super_block	*sb	= inode->i_sb;
+	struct ncp_server	*server	= NCP_SERVER(inode);
+	struct ncp_inode_info	*iinfo	= NCP_INOP(inode);
+        struct nw_file_info	*finfo	= NCP_FINFO(inode);
 
+	/*
+	 * This operation may block, so we lock before checking the count.
+	 */
 	lock_super(sb);
-        if (finfo->opened != 0)
-	{
-                if (ncp_close_file(NCP_SERVER(inode), finfo->file_handle)!=0)
-		{
-                        /* We can't do anything but complain. */
-                        printk("ncp_put_inode: could not close\n");
-                }
-        }
 
+	if (inode->i_count > 1) {
+printk("ncp_put_inode: inode in use device %s, inode %ld, count=%d\n", 
+kdevname(inode->i_dev), inode->i_ino, inode->i_count);
+		goto unlock;
+	}
+	
 	DDPRINTK("ncp_put_inode: put %s\n",
-		finfo->i.entryName);
-
-        ncp_free_inode_info(NCP_INOP(inode));
-
+		 finfo->i.entryName);
+	/*
+	 * This operation should never block.
+	 */
         if (S_ISDIR(inode->i_mode))
 	{
                 DDPRINTK("ncp_put_inode: put directory %ld\n",
@@ -154,6 +163,28 @@
         }                
 
 	clear_inode(inode);
+
+	/*
+	 * After clearing the inode i_count will be 0 in 2.0.xx kernels.
+	 * To keep the inode from being reused as free if we block while
+	 * closing the file, increment i_count temporarily.
+	 */
+	inode->i_count++;
+
+	if (finfo->opened != 0)
+	{
+		if (ncp_close_file(server, finfo->file_handle) != 0)
+		{
+			/* We can't do anything but complain. */
+			printk("ncp_put_inode: could not close %s\n",
+		 		finfo->i.entryName);
+		}
+	}
+
+	ncp_free_inode_info(iinfo);
+	inode->i_count--;
+		
+unlock:
 	unlock_super(sb);
 }
 
diff -u --recursive --new-file v2.0.30/linux/fs/pipe.c linux/fs/pipe.c
--- v2.0.30/linux/fs/pipe.c	Sun Jul  7 10:27:04 1996
+++ linux/fs/pipe.c	Wed Aug  6 17:52:01 1997
@@ -68,7 +68,7 @@
 	PIPE_LOCK(*inode)--;
 	wake_up_interruptible(&PIPE_WAIT(*inode));
 	if (read) {
-	        inode->i_atime = CURRENT_TIME;
+		UPDATE_ATIME(inode);
 		return read;
 	}
 	if (PIPE_WRITERS(*inode))
diff -u --recursive --new-file v2.0.30/linux/fs/proc/array.c linux/fs/proc/array.c
--- v2.0.30/linux/fs/proc/array.c	Tue Oct 29 17:42:41 1996
+++ linux/fs/proc/array.c	Sun Aug  3 13:59:07 1997
@@ -975,7 +975,7 @@
 extern int get_pci_list(char*);
 extern int get_md_status (char *);
 extern int get_rtc_status (char *);
-extern int get_locks_status (char *);
+extern int get_locks_status (char *, char **, off_t, int);
 #ifdef __SMP_PROF__
 extern int get_smp_prof_list(char *);
 #endif
@@ -1051,7 +1051,7 @@
 			return get_rtc_status(page);
 #endif
 		case PROC_LOCKS:
-			return get_locks_status(page);
+			return get_locks_status(page, start, offset, length);
 	}
 	return -EBADF;
 }
diff -u --recursive --new-file v2.0.30/linux/fs/smbfs/dir.c linux/fs/smbfs/dir.c
--- v2.0.30/linux/fs/smbfs/dir.c	Sun Dec  1 05:58:05 1996
+++ linux/fs/smbfs/dir.c	Wed Aug 13 09:57:46 1997
@@ -400,10 +400,10 @@
 
 	SMB_INOP(dir)->nused += 1;
 
-	/* We have to link the new inode_info into the doubly linked
-	   list of inode_infos to make a complete linear search
-	   possible. */
-
+	/*
+	 * We have to link the new inode_info into the doubly linked
+	 * list of inode_infos to make a complete linear search possible.
+	 */
 	root = &(SMB_SERVER(dir)->root);
 
 	new_inode_info->prev = root;
@@ -413,13 +413,15 @@
 
 	if (!(inode = iget(dir->i_sb, smb_info_ino(new_inode_info))))
 	{
-		new_inode_info->next->prev = new_inode_info->prev;
-		new_inode_info->prev->next = new_inode_info->next;
-		SMB_INOP(dir)->nused -= 1;
-
 		printk("smb_iget: iget failed!");
+		/*
+		 * If we blocked in iget(), another task may have referenced
+		 * the info structure ... clean up with smb_free_inode_info.
+		 */
+		smb_free_inode_info(new_inode_info);
 		return NULL;
 	}
+
 	return inode;
 }
 
@@ -660,13 +662,18 @@
 	{
 		goto in_tree;
 	}
+
+	if (new_inode_info == NULL)
+	{
+		iput(dir);
+		return -ENOMEM;
+	}
 	new_inode_info->finfo = finfo;
 
 	DPRINTK("attr: %x\n", finfo.attr);
 
 	if ((*result = smb_iget(dir, new_inode_info)) == NULL)
 	{
-		smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info));
 		iput(dir);
 		return -EACCES;
 	}
@@ -719,7 +726,6 @@
 
 	if ((*result = smb_iget(dir, new_inode_info)) == NULL)
 	{
-		smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info));
 		iput(dir);
 		return error;
 	}
diff -u --recursive --new-file v2.0.30/linux/fs/smbfs/inode.c linux/fs/smbfs/inode.c
--- v2.0.30/linux/fs/smbfs/inode.c	Sun Dec  1 05:58:05 1996
+++ linux/fs/smbfs/inode.c	Sat Aug 16 11:21:49 1997
@@ -59,7 +59,8 @@
 	{
 		/* Ok, now we're in trouble. The inode info is not
 		   there. What should we do now??? */
-		printk("smb_read_inode: inode info not found\n");
+		printk("smb_read_inode: inode %ld info not found\n",
+			inode->i_ino);
 		return;
 	}
 	inode_info->state = SMB_INODE_VALID;
@@ -92,24 +93,43 @@
 static void
 smb_put_inode(struct inode *inode)
 {
-	struct smb_dirent *finfo = SMB_FINFO(inode);
 	struct smb_server *server = SMB_SERVER(inode);
 	struct smb_inode_info *info = SMB_INOP(inode);
+	struct smb_dirent *finfo;
+	__u32 mtime = inode->i_mtime;
+
+	if (inode->i_count > 1) {
+		printk("smb_put_inode: in use device %s, inode %ld count=%d\n",
+			kdevname(inode->i_dev), inode->i_ino, inode->i_count);
+		return;
+	}
 
 	if (S_ISDIR(inode->i_mode))
 	{
 		smb_invalid_dir_cache(inode->i_ino);
 	}
-	if (finfo->opened != 0)
-	{
-		if (smb_proc_close(server, finfo->fileid, inode->i_mtime))
+	clear_inode(inode);
+
+	/*
+	 * We don't want the inode to be reused as free if we block here,
+	 * so temporarily increment i_count.
+	 */
+	inode->i_count++;
+	if (info) {
+		finfo = &info->finfo;
+		if (finfo->opened != 0)
 		{
-			/* We can't do anything but complain. */
-			DPRINTK("smb_put_inode: could not close\n");
+			if (smb_proc_close(server, finfo->fileid, mtime))
+			{
+				/* We can't do anything but complain. */
+				printk("smb_put_inode: could not close\n");
+			}
 		}
-	}
-	smb_free_inode_info(info);
-	clear_inode(inode);
+		smb_free_inode_info(info);
+	} else
+		printk("smb_put_inode: no inode info??\n");
+
+	inode->i_count--;
 }
 
 static void
@@ -362,7 +382,7 @@
 		return -EPERM;
 
 	if (((attr->ia_valid & ATTR_GID) &&
-	     (attr->ia_uid != SMB_SERVER(inode)->m.gid)))
+	     (attr->ia_gid != SMB_SERVER(inode)->m.gid)))
 		return -EPERM;
 
 	if (((attr->ia_valid & ATTR_MODE) &&
diff -u --recursive --new-file v2.0.30/linux/fs/smbfs/proc.c linux/fs/smbfs/proc.c
--- v2.0.30/linux/fs/smbfs/proc.c	Mon Dec 30 01:51:29 1996
+++ linux/fs/smbfs/proc.c	Wed Aug 13 09:57:46 1997
@@ -1685,6 +1685,7 @@
 	if (server->packet != NULL)
 	{
 		smb_vfree(server->packet);
+		server->packet = NULL;
 		server->packet_size = 0;
 	}
 	server->packet = smb_vmalloc(max_xmit);
@@ -1920,6 +1921,7 @@
 
 	/* Now make a new packet with the correct size. */
 	smb_vfree(server->packet);
+	server->packet = NULL;
 
 	server->packet = smb_vmalloc(server->max_xmit);
 	if (server->packet == NULL)
diff -u --recursive --new-file v2.0.30/linux/fs/smbfs/sock.c linux/fs/smbfs/sock.c
--- v2.0.30/linux/fs/smbfs/sock.c	Mon Dec 16 03:07:42 1996
+++ linux/fs/smbfs/sock.c	Wed Aug 13 09:57:46 1997
@@ -338,6 +338,8 @@
 		DPRINTK("smb_receive: Increase packet size from %d to %d\n",
 			server->packet_size, len + 4);
 		smb_vfree(server->packet);
+		server->packet = NULL;
+
 		server->packet_size = 0;
 		server->packet = smb_vmalloc(len + 4);
 		if (server->packet == NULL)
@@ -373,7 +375,6 @@
 	int total_data = 0;
 	int total_param = 0;
 	int result;
-	unsigned char *inbuf = server->packet;
 	unsigned char *rcv_buf;
 	int buf_len;
 	int data_len = 0;
@@ -389,8 +390,8 @@
 		*ldata = *lparam = 0;
 		return 0;
 	}
-	total_data = WVAL(inbuf, smb_tdrcnt);
-	total_param = WVAL(inbuf, smb_tprcnt);
+	total_data = WVAL(server->packet, smb_tdrcnt);
+	total_param = WVAL(server->packet, smb_tprcnt);
 
 	DDPRINTK("smb_receive_trans2: td=%d,tp=%d\n", total_data, total_param);
 
@@ -415,6 +416,8 @@
 
 	while (1)
 	{
+		unsigned char *inbuf = server->packet;
+
 		if (WVAL(inbuf, smb_prdisp) + WVAL(inbuf, smb_prcnt)
 		    > total_param)
 		{
diff -u --recursive --new-file v2.0.30/linux/fs/stat.c linux/fs/stat.c
--- v2.0.30/linux/fs/stat.c	Fri Feb 16 23:19:41 1996
+++ linux/fs/stat.c	Wed Aug  6 17:52:01 1997
@@ -235,5 +235,6 @@
 		iput(inode);
 		return -EINVAL;
 	}
+	UPDATE_ATIME(inode);
 	return inode->i_op->readlink(inode,buf,bufsiz);
 }
diff -u --recursive --new-file v2.0.30/linux/fs/super.c linux/fs/super.c
--- v2.0.30/linux/fs/super.c	Tue Apr  8 08:47:46 1997
+++ linux/fs/super.c	Mon Aug 18 13:01:15 1997
@@ -277,6 +277,8 @@
 	{ MS_NOSUID, ",nosuid" },
 	{ MS_NODEV, ",nodev" },
 	{ MS_SYNCHRONOUS, ",sync" },
+	{ MS_MANDLOCK, ",mand" },
+	{ MS_NOATIME, ",noatime" },
 #ifdef MS_NOSUB			/* Can't find this except in mount.c */
 	{ MS_NOSUB, ",nosub" },
 #endif
@@ -762,8 +764,7 @@
 		if (retval)
 			return retval;
 	}
-	sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) |
-		(flags & MS_RMT_MASK);
+	sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) | (flags & MS_RMT_MASK);
 	vfsmnt = lookup_vfsmnt(sb->s_dev);
 	if (vfsmnt)
 		vfsmnt->mnt_flags = sb->s_flags;
diff -u --recursive --new-file v2.0.30/linux/include/asm-alpha/fcntl.h linux/include/asm-alpha/fcntl.h
--- v2.0.30/linux/include/asm-alpha/fcntl.h	Mon Aug  5 00:13:54 1996
+++ linux/include/asm-alpha/fcntl.h	Sun Aug  3 13:59:07 1997
@@ -49,12 +49,6 @@
 				   blocking */
 #define LOCK_UN		8	/* remove lock */
 
-#ifdef __KERNEL__
-#define F_POSIX		1
-#define F_FLOCK		2
-#define F_BROKEN	4	/* broken flock() emulation */
-#endif /* __KERNEL__ */
-
 struct flock {
 	short l_type;
 	short l_whence;
diff -u --recursive --new-file v2.0.30/linux/include/asm-alpha/fpu.h linux/include/asm-alpha/fpu.h
--- v2.0.30/linux/include/asm-alpha/fpu.h	Thu Dec 21 22:22:06 1995
+++ linux/include/asm-alpha/fpu.h	Sun Aug  3 10:58:38 1997
@@ -57,8 +57,29 @@
 				 IEEE_STATUS_OVF | IEEE_STATUS_UNF |	\
 				 IEEE_STATUS_INE)
 
+#define IEEE_SW_MASK		(IEEE_TRAP_ENABLE_MASK | IEEE_STATUS_MASK)
+
 #define IEEE_STATUS_TO_EXCSUM_SHIFT	16
 
 #define IEEE_INHERIT    (1UL<<63)	/* inherit on thread create? */
+
+/*
+ * Convert the software IEEE trap enables and status bits into the 
+ * hardware fpcr format.
+ */
+
+static inline unsigned long
+ieee_sw_to_fpcr(unsigned long sw)
+{
+	unsigned long fpcw;
+	fpcw = (sw & IEEE_STATUS_MASK) << 35;
+	fpcw |= sw & IEEE_STATUS_MASK ? FPCR_SUM : 0;
+	fpcw |= (~sw & (IEEE_TRAP_ENABLE_INV
+			| IEEE_TRAP_ENABLE_DZE
+			| IEEE_TRAP_ENABLE_OVF)) << 48;
+	fpcw |= (~sw & (IEEE_TRAP_ENABLE_UNF | IEEE_TRAP_ENABLE_INE)) << 57;
+	return fpcw;
+}
+
 
 #endif /* __ASM_ALPHA_FPU_H */
diff -u --recursive --new-file v2.0.30/linux/include/asm-alpha/processor.h linux/include/asm-alpha/processor.h
--- v2.0.30/linux/include/asm-alpha/processor.h	Sun Sep 15 00:34:18 1996
+++ linux/include/asm-alpha/processor.h	Sun Aug  3 10:58:38 1997
@@ -43,8 +43,9 @@
 
 	/* the fields below are Linux-specific: */
 	/*
-	 * bit 0:    perform syscall argument validation (get/set_fs)
-	 * bit 1..5: IEEE_TRAP_ENABLE bits (see fpu.h)
+	 * bit 0:	perform syscall argument validation (get/set_fs)
+	 * bit 1..5:	IEEE_TRAP_ENABLE bits (see fpu.h)
+	 * bit 17..21:	IEEE_STATUS bits (see fpu.h)
 	 */
 	unsigned long flags;
 };
diff -u --recursive --new-file v2.0.30/linux/include/asm-alpha/socket.h linux/include/asm-alpha/socket.h
--- v2.0.30/linux/include/asm-alpha/socket.h	Sun Mar 24 02:09:37 1996
+++ linux/include/asm-alpha/socket.h	Tue Aug 12 13:57:23 1997
@@ -33,4 +33,6 @@
 #define SO_PRIORITY	12
 #define SO_BSDCOMPAT	14
 
+#define SO_BINDTODEVICE 25
+
 #endif /* _ASM_SOCKET_H */
diff -u --recursive --new-file v2.0.30/linux/include/asm-i386/fcntl.h linux/include/asm-i386/fcntl.h
--- v2.0.30/linux/include/asm-i386/fcntl.h	Wed Jul 17 05:10:03 1996
+++ linux/include/asm-i386/fcntl.h	Sun Aug  3 13:59:08 1997
@@ -48,12 +48,6 @@
 				   blocking */
 #define LOCK_UN		8	/* remove lock */
 
-#ifdef __KERNEL__
-#define F_POSIX		1
-#define F_FLOCK		2
-#define F_BROKEN	4	/* broken flock() emulation */
-#endif /* __KERNEL__ */
-
 struct flock {
 	short l_type;
 	short l_whence;
diff -u --recursive --new-file v2.0.30/linux/include/asm-i386/irq.h linux/include/asm-i386/irq.h
--- v2.0.30/linux/include/asm-i386/irq.h	Wed Mar  5 11:20:45 1997
+++ linux/include/asm-i386/irq.h	Mon Aug 11 13:42:11 1997
@@ -144,12 +144,16 @@
 	"movl 32(%edx), %eax\n\t" \
 	"shrl $24,%eax\n\t" \
 	"andb $0x0F,%al\n"
-	
+
 #define	ENTER_KERNEL \
 	"pushl %eax\n\t" \
+	"pushl %ebx\n\t" \
+	"pushl %ecx\n\t" \
 	"pushl %edx\n\t" \
 	"pushfl\n\t" \
 	"cli\n\t" \
+	"movl $6000, %ebx\n\t" \
+	"movl "SYMBOL_NAME_STR(smp_loops_per_tick)", %ecx\n\t" \
 	GET_PROCESSOR_ID \
 	"btsl $" STR(SMP_FROM_INT) ","SYMBOL_NAME_STR(smp_proc_in_lock)"(,%eax,4)\n\t" \
 	"1: " \
@@ -158,6 +162,7 @@
 	"jnc 3f\n\t" \
 	"cmpb "SYMBOL_NAME_STR(active_kernel_processor)", %al\n\t" \
 	"je 4f\n\t" \
+	"movb $1, "SYMBOL_NAME_STR(smp_blocked_interrupt_pending)"\n\t" \
 	"2: " \
         SMP_PROF_INT_SPINS \
 	"btl %al, "SYMBOL_NAME_STR(smp_invalidate_needed)"\n\t" \
@@ -168,14 +173,28 @@
 	"movl %cr3,%edx\n\t" \
 	"movl %edx,%cr3\n" \
 	"5: btl $0, "SYMBOL_NAME_STR(kernel_flag)"\n\t" \
-	"jc 2b\n\t" \
-	"jmp 1b\n\t" \
+	"jnc 1b\n\t" \
+	"cmpb "SYMBOL_NAME_STR(active_kernel_processor)", %al\n\t" \
+	"je 4f\n\t" \
+	"decl %ecx\n\t" \
+	"jne 2b\n\t" \
+	"decl %ebx\n\t" \
+	"jne 6f\n\t" \
+	"call "SYMBOL_NAME_STR(irq_deadlock_detected)"\n\t" \
+	"6: movl "SYMBOL_NAME_STR(smp_loops_per_tick)", %ecx\n\t" \
+	"cmpb "SYMBOL_NAME_STR(boot_cpu_id)", %al\n\t" \
+	"jne 2b\n\t" \
+	"incl "SYMBOL_NAME_STR(jiffies)"\n\t" \
+	"jmp 2b\n\t" \
 	"3: " \
 	"movb %al, "SYMBOL_NAME_STR(active_kernel_processor)"\n\t" \
 	"4: " \
 	"incl "SYMBOL_NAME_STR(kernel_counter)"\n\t" \
+	"movb $0, "SYMBOL_NAME_STR(smp_blocked_interrupt_pending)"\n\t" \
 	"popfl\n\t" \
 	"popl %edx\n\t" \
+	"popl %ecx\n\t" \
+	"popl %ebx\n\t" \
 	"popl %eax\n\t"
 
 #define	LEAVE_KERNEL \
@@ -185,7 +204,10 @@
 	"cli\n\t" \
 	"decl "SYMBOL_NAME_STR(kernel_counter)"\n\t" \
 	"jnz 1f\n\t" \
-	"movb $" STR (NO_PROC_ID) ", "SYMBOL_NAME_STR(active_kernel_processor)"\n\t" \
+	"movb "SYMBOL_NAME_STR(saved_active_kernel_processor)",%al\n\t" \
+	"movb %al,"SYMBOL_NAME_STR(active_kernel_processor)"\n\t" \
+	"cmpb $" STR (NO_PROC_ID) ",%al\n\t" \
+	"jne 1f\n\t" \
 	"lock\n\t" \
 	"btrl $0, "SYMBOL_NAME_STR(kernel_flag)"\n\t" \
 	"1: " \
diff -u --recursive --new-file v2.0.30/linux/include/asm-i386/pgtable.h linux/include/asm-i386/pgtable.h
--- v2.0.30/linux/include/asm-i386/pgtable.h	Fri Mar 28 16:08:17 1997
+++ linux/include/asm-i386/pgtable.h	Sat Aug 23 10:54:51 1997
@@ -375,6 +375,8 @@
 	free_page((unsigned long) pte);
 }
 
+extern const char bad_pmd_string[];
+
 extern inline pte_t * pte_alloc_kernel(pmd_t * pmd, unsigned long address)
 {
 	address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1);
@@ -391,7 +393,7 @@
 		free_page((unsigned long) page);
 	}
 	if (pmd_bad(*pmd)) {
-		printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
+		printk(bad_pmd_string, pmd_val(*pmd));
 		pmd_val(*pmd) = _PAGE_TABLE | (unsigned long) BAD_PAGETABLE;
 		return NULL;
 	}
@@ -444,7 +446,7 @@
 }
 
 fix:
-	printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
+	printk(bad_pmd_string, pmd_val(*pmd));
 oom:
 	pmd_val(*pmd) = _PAGE_TABLE | (unsigned long) BAD_PAGETABLE;
 	return NULL;
diff -u --recursive --new-file v2.0.30/linux/include/asm-i386/shmparam.h linux/include/asm-i386/shmparam.h
--- v2.0.30/linux/include/asm-i386/shmparam.h	Mon Sep 11 23:49:24 1995
+++ linux/include/asm-i386/shmparam.h	Tue Aug 12 16:23:22 1997
@@ -33,7 +33,7 @@
  * SHMMAX <= (PAGE_SIZE << _SHM_IDX_BITS).
  */
 
-#define SHMMAX 0x1000000		/* max shared seg size (bytes) */
+#define SHMMAX 0x2000000		/* max shared seg size (bytes) */
 #define SHMMIN 1 /* really PAGE_SIZE */	/* min shared seg size (bytes) */
 #define SHMMNI (1<<_SHM_ID_BITS)	/* max num of segs system wide */
 #define SHMALL				/* max shm system wide (pages) */ \
diff -u --recursive --new-file v2.0.30/linux/include/asm-i386/socket.h linux/include/asm-i386/socket.h
--- v2.0.30/linux/include/asm-i386/socket.h	Sun Mar 24 02:47:39 1996
+++ linux/include/asm-i386/socket.h	Tue Aug 12 13:57:23 1997
@@ -22,4 +22,6 @@
 #define SO_BSDCOMPAT	14
 /* To add :#define SO_REUSEPORT 15 */
 
+#define SO_BINDTODEVICE 25
+
 #endif /* _ASM_SOCKET_H */
diff -u --recursive --new-file v2.0.30/linux/include/asm-m68k/fcntl.h linux/include/asm-m68k/fcntl.h
--- v2.0.30/linux/include/asm-m68k/fcntl.h	Tue Apr 30 03:09:45 1996
+++ linux/include/asm-m68k/fcntl.h	Sun Aug  3 13:59:08 1997
@@ -48,12 +48,6 @@
 				   blocking */
 #define LOCK_UN		8	/* remove lock */
 
-#ifdef __KERNEL__
-#define F_POSIX		1
-#define F_FLOCK		2
-#define F_BROKEN	4	/* broken flock() emulation */
-#endif /* __KERNEL__ */
-
 struct flock {
 	short l_type;
 	short l_whence;
diff -u --recursive --new-file v2.0.30/linux/include/asm-mips/fcntl.h linux/include/asm-mips/fcntl.h
--- v2.0.30/linux/include/asm-mips/fcntl.h	Tue Apr 30 03:09:45 1996
+++ linux/include/asm-mips/fcntl.h	Sun Aug  3 13:59:08 1997
@@ -49,12 +49,6 @@
 				   blocking */
 #define LOCK_UN		8	/* remove lock */
 
-#ifdef __KERNEL__
-#define F_POSIX		1
-#define F_FLOCK		2
-#define F_BROKEN	4	/* broken flock() emulation */
-#endif /* __KERNEL__ */
-
 typedef struct flock {
 	short l_type;
 	short l_whence;
diff -u --recursive --new-file v2.0.30/linux/include/asm-ppc/fcntl.h linux/include/asm-ppc/fcntl.h
--- v2.0.30/linux/include/asm-ppc/fcntl.h	Tue Apr 30 03:09:45 1996
+++ linux/include/asm-ppc/fcntl.h	Sun Aug  3 13:59:08 1997
@@ -48,12 +48,6 @@
 				   blocking */
 #define LOCK_UN		8	/* remove lock */
 
-#ifdef __KERNEL__
-#define F_POSIX		1
-#define F_FLOCK		2
-#define F_BROKEN	4	/* broken flock() emulation */
-#endif /* __KERNEL__ */
-
 struct flock {
 	short l_type;
 	short l_whence;
diff -u --recursive --new-file v2.0.30/linux/include/asm-sparc/fcntl.h linux/include/asm-sparc/fcntl.h
--- v2.0.30/linux/include/asm-sparc/fcntl.h	Tue Apr 30 03:09:45 1996
+++ linux/include/asm-sparc/fcntl.h	Sun Aug  3 13:59:08 1997
@@ -48,12 +48,6 @@
 				   blocking */
 #define LOCK_UN		8	/* remove lock */
 
-#ifdef __KERNEL__
-#define F_POSIX		1
-#define F_FLOCK		2
-#define F_BROKEN	4	/* broken flock() emulation */
-#endif /* __KERNEL__ */
-
 struct flock {
 	short l_type;
 	short l_whence;
diff -u --recursive --new-file v2.0.30/linux/include/linux/atalk.h linux/include/linux/atalk.h
--- v2.0.30/linux/include/linux/atalk.h	Tue Apr  8 08:47:46 1997
+++ linux/include/linux/atalk.h	Sat Aug 23 10:55:26 1997
@@ -8,6 +8,8 @@
 #ifndef __LINUX_ATALK_H__
 #define __LINUX_ATALK_H__
 
+#define SIOCATALKDIFADDR      (SIOCPROTOPRIVATE + 0)
+
 #define ATPORT_FIRST	1
 #define ATPORT_RESERVED	128
 #define ATPORT_LAST	255
diff -u --recursive --new-file v2.0.30/linux/include/linux/auto_fs.h linux/include/linux/auto_fs.h
--- v2.0.30/linux/include/linux/auto_fs.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/auto_fs.h	Fri Sep  5 20:43:59 1997
@@ -0,0 +1,62 @@
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *   
+ * linux/include/linux/auto_fs.h
+ *
+ *   Copyright 1997 Transmeta Corporation - All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+
+#ifndef _LINUX_AUTO_FS_H
+#define _LINUX_AUTO_FS_H
+
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <linux/limits.h>
+#include <linux/ioctl.h>
+#include <asm/types.h>
+
+#define AUTOFS_PROTO_VERSION 3
+
+enum autofs_packet_type {
+	autofs_ptype_missing,	/* Missing entry (mount request) */
+	autofs_ptype_expire,	/* Expire entry (umount request) */
+};
+
+struct autofs_packet_hdr {
+	int proto_version;	      /* Protocol version */
+	enum autofs_packet_type type; /* Type of packet */
+};
+
+struct autofs_packet_missing {
+	struct autofs_packet_hdr hdr;
+        unsigned long wait_queue_token;
+	int len;
+	char name[NAME_MAX+1];
+};	
+
+struct autofs_packet_expire {
+	struct autofs_packet_hdr hdr;
+	int len;
+	char name[NAME_MAX+1];
+};
+
+#define AUTOFS_IOC_READY      _IO(0x93,0x60)
+#define AUTOFS_IOC_FAIL       _IO(0x93,0x61)
+#define AUTOFS_IOC_CATATONIC  _IO(0x93,0x62)
+#define AUTOFS_IOC_PROTOVER   _IOR(0x93,0x63,int)
+#define AUTOFS_IOC_SETTIMEOUT _IOWR(0x93,0x64,unsigned long)
+#define AUTOFS_IOC_EXPIRE     _IOR(0x93,0x65,struct autofs_packet_expire)
+
+#ifdef __KERNEL__
+
+/* Init function */
+int init_autofs_fs(void);
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_AUTO_FS_H */
diff -u --recursive --new-file v2.0.30/linux/include/linux/b1lli.h linux/include/linux/b1lli.h
--- v2.0.30/linux/include/linux/b1lli.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/b1lli.h	Mon Aug  4 17:34:01 1997
@@ -0,0 +1,141 @@
+/*
+ * $Id: b1lli.h,v 1.1 1997/03/04 21:27:32 calle Exp $
+ *
+ * ISDN lowlevel-module for AVM B1-card.
+ *
+ * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de)
+ *
+ * $Log: b1lli.h,v $
+ * Revision 1.1  1997/03/04 21:27:32  calle
+ * First version in isdn4linux
+ *
+ * Revision 2.2  1997/02/12 09:31:39  calle
+ * new version
+ *
+ * Revision 1.1  1997/01/31 10:32:20  calle
+ * Initial revision
+ *
+ */
+
+#ifndef _B1LLI_H_
+#define _B1LLI_H_
+/*
+ * struct for loading t4 file 
+ */
+typedef struct avmb1_t4file {
+	int len;
+	unsigned char *data;
+} avmb1_t4file;
+
+typedef struct avmb1_loaddef {
+	int contr;
+	avmb1_t4file t4file;
+} avmb1_loaddef;
+
+typedef struct avmb1_resetdef {
+	int contr;
+} avmb1_resetdef;
+
+/*
+ * struct for adding new cards 
+ */
+typedef struct avmb1_carddef {
+	int port;
+	int irq;
+} avmb1_carddef;
+
+#define	AVMB1_LOAD	0	/* load image to card */
+#define AVMB1_ADDCARD	1	/* add a new card */
+#define AVMB1_RESETCARD	2	/* reset a card */
+
+
+
+#ifdef __KERNEL__
+
+/*
+ * card states for startup
+ */
+
+#define CARD_NONE	0
+#define CARD_DETECTED	1
+#define CARD_LOADING	2
+#define CARD_INITSTATE	4
+#define CARD_RUNNING	5
+#define CARD_ACTIVE	6
+
+#define	AVMB1_PORTLEN	0x1f
+
+#define AVM_MAXVERSION	8
+#define AVM_NBCHAN	2
+
+#define AVM_NAPPS	30
+#define AVM_NPLCI	5
+#define AVM_NNCCI	6
+
+/*
+ * Main driver data
+ */
+
+typedef struct avmb1_card {
+	struct avmb1_card *next;
+	int cnr;
+	unsigned short port;
+	unsigned irq;
+	volatile unsigned short cardstate;
+	int interrupt;
+	int blocked;
+	int versionlen;
+	char versionbuf[1024];
+	char *version[AVM_MAXVERSION];
+	char msgbuf[128];	/* capimsg msg part */
+	char databuf[2048];	/* capimsg data part */
+	capi_version cversion;
+	char name[10];
+} avmb1_card;
+
+/*
+ * Versions
+ */
+
+#define	VER_DRIVER	0
+#define	VER_CARDTYPE	1
+#define	VER_HWID	2
+#define	VER_SERIAL	3
+#define	VER_OPTION	4
+#define	VER_PROTO	5
+#define	VER_PROFILE	6
+#define	VER_CAPI	7
+
+
+/* b1lli.c */
+int B1_detect(unsigned short base);
+void B1_reset(unsigned short base);
+int B1_load_t4file(unsigned short base, avmb1_t4file * t4file);
+int B1_loaded(unsigned short base);
+unsigned char B1_assign_irq(unsigned short base, unsigned irq);
+unsigned char B1_enable_irq(unsigned short base);
+unsigned char B1_disable_irq(unsigned short base);
+int B1_valid_irq(unsigned irq);
+void B1_handle_interrupt(avmb1_card * card);
+void B1_send_init(unsigned short port,
+	    unsigned int napps, unsigned int nncci, unsigned int cardnr);
+void B1_send_register(unsigned short port,
+		      __u16 appid, __u32 nmsg,
+		      __u32 nb3conn, __u32 nb3blocks, __u32 b3bsize);
+void B1_send_release(unsigned short port, __u16 appid);
+void B1_send_message(unsigned short port, struct sk_buff *skb);
+
+/* b1capi.c */
+void avmb1_handle_new_ncci(avmb1_card * card,
+			   __u16 appl, __u32 ncci, __u32 winsize);
+void avmb1_handle_free_ncci(avmb1_card * card,
+			    __u16 appl, __u32 ncci);
+void avmb1_handle_capimsg(avmb1_card * card, __u16 appl, struct sk_buff *skb);
+void avmb1_card_ready(avmb1_card * card);
+
+int avmb1_addcard(int port, int irq);
+int avmb1_probecard(int port, int irq);
+
+#endif				/* __KERNEL__ */
+
+#endif				/* _B1LLI_H_ */
diff -u --recursive --new-file v2.0.30/linux/include/linux/capi.h linux/include/linux/capi.h
--- v2.0.30/linux/include/linux/capi.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/capi.h	Mon Aug  4 17:34:01 1997
@@ -0,0 +1,127 @@
+/*
+ * $Id: capi.h,v 1.1 1997/03/04 21:27:33 calle Exp $
+ * 
+ * CAPI 2.0 Interface for Linux
+ * 
+ * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
+ * 
+ * $Log: capi.h,v $
+ * Revision 1.1  1997/03/04 21:27:33  calle
+ * First version in isdn4linux
+ *
+ * Revision 2.2  1997/02/12 09:31:39  calle
+ * new version
+ *
+ * Revision 1.1  1997/01/31 10:32:20  calle
+ * Initial revision
+ *
+ * 
+ */
+
+#ifndef __LINUX_CAPI_H__
+#define __LINUX_CAPI_H__
+
+#include <asm/types.h>
+#include <linux/ioctl.h>
+#ifndef __KERNEL__
+#include <linux/kernelcapi.h>
+#endif
+
+/*
+ * CAPI_REGISTER
+ */
+
+typedef struct capi_register_params {	/* CAPI_REGISTER */
+	__u32 level3cnt;	/* No. of simulatneous user data connections */
+	__u32 datablkcnt;	/* No. of buffered data messages */
+	__u32 datablklen;	/* Size of buffered data messages */
+} capi_register_params;
+
+#define	CAPI_REGISTER	_IOW('C',0x01,struct capi_register_params)
+
+/*
+ * CAPI_GET_MANUFACTURER
+ */
+
+#define CAPI_MANUFACTURER_LEN		64
+
+#define	CAPI_GET_MANUFACTURER	_IOWR('C',0x06,CAPI_MANUFACTURER_LEN)
+
+/*
+ * CAPI_GET_VERSION
+ */
+
+typedef struct capi_version {
+	__u32 majorversion;
+	__u32 minorversion;
+	__u32 majormanuversion;
+	__u32 minormanuversion;
+} capi_version;
+
+#define CAPI_GET_VERSION	_IOWR('C',0x07,struct capi_version)
+
+/*
+ * CAPI_GET_SERIAL
+ */
+
+#define CAPI_SERIAL_LEN		8
+#define CAPI_GET_SERIAL		_IOWR('C',0x08, CAPI_SERIAL_LEN)
+
+/*
+ * CAPI_GET_PROFILE
+ */
+
+typedef struct capi_profile {
+	__u16 ncontroller;	/* number of installed controller */
+	__u16 nbchannel;	/* number of B-Channels */
+	__u32 goptions;		/* global options */
+	__u32 support1;		/* B1 protocols support */
+	__u32 support2;		/* B2 protocols support */
+	__u32 support3;		/* B3 protocols support */
+	__u32 reserved[6];	/* reserved */
+	__u32 manu[5];		/* manufacturer specific information */
+} capi_profile;
+
+#define CAPI_GET_PROFILE	_IOWR('C',0x09,struct capi_profile)
+
+typedef struct capi_manufacturer_cmd {
+	unsigned long cmd;
+	void *data;
+} capi_manufacturer_cmd;
+
+/*
+ * CAPI_MANUFACTURER_CMD
+ */
+
+#define CAPI_MANUFACTURER_CMD	_IOWR('C',0x20, struct capi_manufacturer_cmd)
+
+/*
+ * CAPI_GET_ERRCODE
+ * capi errcode is set, * if read, write, or ioctl returns EIO,
+ * ioctl returns errcode directly, and in arg, if != 0
+ */
+
+#define CAPI_GET_ERRCODE	_IOR('C',0x21, __u16)
+
+/*
+ * CAPI_INSTALLED
+ */
+#define CAPI_INSTALLED		_IOR('C',0x22, __u16)
+
+/*
+ * member contr is input for
+ * CAPI_GET_MANUFACTURER, CAPI_VERSION, CAPI_GET_SERIAL
+ * and CAPI_GET_PROFILE
+ */
+typedef union capi_ioctl_struct {
+	__u32 contr;
+	capi_register_params rparams;
+	__u8 manufacturer[CAPI_MANUFACTURER_LEN];
+	capi_version version;
+	__u8 serial[CAPI_SERIAL_LEN];
+	capi_profile profile;
+	capi_manufacturer_cmd cmd;
+	__u16 errcode;
+} capi_ioctl_struct;
+
+#endif				/* __LINUX_CAPI_H__ */
diff -u --recursive --new-file v2.0.30/linux/include/linux/ext2_fs.h linux/include/linux/ext2_fs.h
--- v2.0.30/linux/include/linux/ext2_fs.h	Tue Apr  8 08:47:46 1997
+++ linux/include/linux/ext2_fs.h	Wed Aug  6 17:52:01 1997
@@ -189,6 +189,7 @@
 #define EXT2_IMMUTABLE_FL		0x00000010 /* Immutable file */
 #define EXT2_APPEND_FL			0x00000020 /* writes to file may only append */
 #define EXT2_NODUMP_FL			0x00000040 /* do not dump file */
+#define EXT2_NOATIME_FL			0x00000080 /* do not update atime */
 #define EXT2_RESERVED_FL		0x80000000 /* reserved for ext2 lib */
 	
 /*
diff -u --recursive --new-file v2.0.30/linux/include/linux/fs.h linux/include/linux/fs.h
--- v2.0.30/linux/include/linux/fs.h	Fri Mar 28 16:08:17 1997
+++ linux/include/linux/fs.h	Sat Aug 23 10:54:51 1997
@@ -69,14 +69,17 @@
 #define MS_NOEXEC	 8	/* Disallow program execution */
 #define MS_SYNCHRONOUS	16	/* Writes are synced at once */
 #define MS_REMOUNT	32	/* Alter flags of a mounted FS */
+#define MS_MANDLOCK	64	/* Allow mandatory locks on an FS */
 #define S_WRITE		128	/* Write on file/directory/symlink */
 #define S_APPEND	256	/* Append-only file */
 #define S_IMMUTABLE	512	/* Immutable file */
+#define MS_NOATIME	1024	/* Do not update access times. */
+#define S_BAD_INODE	2048	/* Marker for unreadable inodes */
 
 /*
  * Flags that can be altered by MS_REMOUNT
  */
-#define MS_RMT_MASK (MS_RDONLY)
+#define MS_RMT_MASK (MS_RDONLY|MS_MANDLOCK|MS_NOATIME)
 
 /*
  * Magic mount flag number. Has to be or-ed to the flag values.
@@ -97,10 +100,18 @@
 #define IS_NODEV(inode) ((inode)->i_flags & MS_NODEV)
 #define IS_NOEXEC(inode) ((inode)->i_flags & MS_NOEXEC)
 #define IS_SYNC(inode) ((inode)->i_flags & MS_SYNCHRONOUS)
+#define IS_MANDLOCK(inode) ((inode)->i_flags & MS_MANDLOCK)
 
 #define IS_WRITABLE(inode) ((inode)->i_flags & S_WRITE)
 #define IS_APPEND(inode) ((inode)->i_flags & S_APPEND)
 #define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE)
+#define IS_NOATIME(inode) ((inode)->i_flags & MS_NOATIME)
+
+#define UPDATE_ATIME(inode) \
+	if (!IS_NOATIME(inode) && !IS_RDONLY(inode)) { \
+		inode->i_atime = CURRENT_TIME; \
+		inode->i_dirt = 1; \
+	}
 
 /* the read-only stuff doesn't really belong here, but any other place is
    probably as bad and I don't want to create yet another include file. */
@@ -335,16 +346,22 @@
 	void *private_data;	/* needed for tty driver, and maybe others */
 };
 
+#define FL_POSIX	1
+#define FL_FLOCK	2
+#define FL_BROKEN	4	/* broken flock() emulation */
+#define FL_ACCESS	8	/* for processes suspended by mandatory locking */
+
 struct file_lock {
 	struct file_lock *fl_next;	/* singly linked list for this inode  */
 	struct file_lock *fl_nextlink;	/* doubly linked list of all locks */
 	struct file_lock *fl_prevlink;	/* used to simplify lock removal */
-	struct file_lock *fl_block;
+	struct file_lock *fl_nextblock; /* circular list of blocked processes */
+	struct file_lock *fl_prevblock;
 	struct task_struct *fl_owner;
 	struct wait_queue *fl_wait;
 	struct file *fl_file;
-	char fl_flags;
-	char fl_type;
+	unsigned char fl_flags;
+	unsigned char fl_type;
 	off_t fl_start;
 	off_t fl_end;
 };
@@ -367,27 +384,25 @@
 
 extern inline int locks_verify_locked(struct inode *inode)
 {
-#ifdef CONFIG_LOCK_MANDATORY	 
 	/* Candidates for mandatory locking have the setgid bit set
 	 * but no group execute bit -  an otherwise meaningless combination.
 	 */
-	if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+	if (IS_MANDLOCK(inode) &&
+	    (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
 		return (locks_mandatory_locked(inode));
-#endif
 	return (0);
 }
 extern inline int locks_verify_area(int read_write, struct inode *inode,
 				    struct file *filp, unsigned int offset,
 				    unsigned int count)
 {
-#ifdef CONFIG_LOCK_MANDATORY	 
 	/* Candidates for mandatory locking have the setgid bit set
 	 * but no group execute bit -  an otherwise meaningless combination.
 	 */
-	if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+	if (IS_MANDLOCK(inode) &&
+	    (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
 		return (locks_mandatory_area(read_write, inode, filp, offset,
 					     count));
-#endif
 	return (0);
 }
 
@@ -621,6 +636,7 @@
 extern void insert_inode_hash(struct inode *);
 extern void clear_inode(struct inode *);
 extern struct inode * get_pipe_inode(void);
+extern void make_bad_inode(struct inode *);
 extern int get_unused_fd(void);
 extern void put_unused_fd(int);
 extern struct file * get_empty_filp(void);
diff -u --recursive --new-file v2.0.30/linux/include/linux/hdreg.h linux/include/linux/hdreg.h
--- v2.0.30/linux/include/linux/hdreg.h	Fri Mar 28 16:08:17 1997
+++ linux/include/linux/hdreg.h	Sat Aug 23 10:54:51 1997
@@ -53,6 +53,7 @@
 
 #define WIN_DOORLOCK		0xde	/* lock door on removable drives */
 #define WIN_DOORUNLOCK		0xdf	/* unlock door on removable drives */
+#define WIN_ACKMC		0xdb	/* acknowledge media change */
 
 #define WIN_MULTREAD		0xC4	/* read sectors using multiple mode */
 #define WIN_MULTWRITE		0xC5	/* write sectors using multiple mode */
@@ -67,11 +68,15 @@
 #define WIN_SRST		0x08	/* ATAPI soft reset command */
 #define WIN_PACKETCMD		0xa0	/* Send a packet command. */
 
+/* Non-standard commands */
+#define EXABYTE_ENABLE_NEST	0xf0
+
 /* Bits for HD_ERROR */
 #define MARK_ERR	0x01	/* Bad address mark */
 #define TRK0_ERR	0x02	/* couldn't find track 0 */
 #define ABRT_ERR	0x04	/* Command aborted */
 #define ID_ERR		0x10	/* ID field not found */
+#define MC_ERR		0x20	/* media changed */
 #define ECC_ERR		0x40	/* Uncorrectable ECC error */
 #define	BBD_ERR		0x80	/* block marked bad */
 
diff -u --recursive --new-file v2.0.30/linux/include/linux/if.h linux/include/linux/if.h
--- v2.0.30/linux/include/linux/if.h	Tue Oct  8 09:45:28 1996
+++ linux/include/linux/if.h	Tue Aug 12 15:00:41 1997
@@ -39,6 +39,12 @@
 #define IFF_SLAVE	0x800		/* slave of a load balancer	*/
 
 #define IFF_MULTICAST	0x1000		/* Supports multicast		*/
+#define IFF_SOFTHEADERS 0x2000		/* Device cannot construct headers
+					 * until broadcast time. Therefore
+					 * SOCK_PACKET must call header
+					 * construction. Private flag.
+					 * Never visible outside of kernel.
+					 */
 
 /*
  * The ifaddr structure contains information about one address
diff -u --recursive --new-file v2.0.30/linux/include/linux/interrupt.h linux/include/linux/interrupt.h
--- v2.0.30/linux/include/linux/interrupt.h	Thu Oct 31 04:32:07 1996
+++ linux/include/linux/interrupt.h	Thu Aug 14 13:30:58 1997
@@ -33,6 +33,7 @@
 	DIGI_BH,
 	SERIAL_BH,
 	RISCOM8_BH,
+ 	SPECIALIX_BH,
 	BAYCOM_BH,
 	NET_BH,
 	IMMEDIATE_BH,
diff -u --recursive --new-file v2.0.30/linux/include/linux/ipx.h linux/include/linux/ipx.h
--- v2.0.30/linux/include/linux/ipx.h	Mon May 13 13:39:28 1996
+++ linux/include/linux/ipx.h	Mon Aug  4 08:42:09 1997
@@ -75,5 +75,6 @@
 #define SIOCAIPXITFCRT		(SIOCPROTOPRIVATE)
 #define SIOCAIPXPRISLT		(SIOCPROTOPRIVATE+1)
 #define SIOCIPXCFGDATA		(SIOCPROTOPRIVATE+2)
+#define SIOCIPXNCPCONN		(SIOCPROTOPRIVATE+3)
 #endif
 
diff -u --recursive --new-file v2.0.30/linux/include/linux/isdn.h linux/include/linux/isdn.h
--- v2.0.30/linux/include/linux/isdn.h	Sun Dec  1 10:02:03 1996
+++ linux/include/linux/isdn.h	Thu Aug 21 15:17:27 1997
@@ -1,4 +1,4 @@
-/* $Id: isdn.h,v 1.18 1996/11/06 17:37:50 keil Exp $
+/* $Id: isdn.h,v 1.32 1997/08/21 09:49:46 fritz Exp $
  *
  * Main header for the Linux ISDN subsystem (linklevel).
  *
@@ -21,6 +21,55 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: isdn.h,v $
+ * Revision 1.32  1997/08/21 09:49:46  fritz
+ * Increased NET_DV
+ *
+ * Revision 1.31  1997/06/22 11:57:07  fritz
+ * Added ability to adjust slave triggerlevel.
+ *
+ * Revision 1.30  1997/06/17 13:07:23  hipp
+ * compression changes , MP changes
+ *
+ * Revision 1.29  1997/05/27 15:18:02  fritz
+ * Added changes for recent 2.1.x kernels:
+ *   changed return type of isdn_close
+ *   queue_task_* -> queue_task
+ *   clear/set_bit -> test_and_... where apropriate.
+ *   changed type of hard_header_cache parameter.
+ *
+ * Revision 1.28  1997/03/07 01:33:01  fritz
+ * Added proper ifdef's for CONFIG_ISDN_AUDIO
+ *
+ * Revision 1.27  1997/03/05 21:11:49  fritz
+ * Minor fixes.
+ *
+ * Revision 1.26  1997/02/28 02:37:53  fritz
+ * Added some comments.
+ *
+ * Revision 1.25  1997/02/23 16:54:23  hipp
+ * some initial changes for future PPP compresion
+ *
+ * Revision 1.24  1997/02/18 09:42:45  fritz
+ * Bugfix: Increased ISDN_MODEM_ANZREG.
+ * Increased TTY_DV.
+ *
+ * Revision 1.23  1997/02/10 22:07:13  fritz
+ * Added 2 modem registers for numbering plan and screening info.
+ *
+ * Revision 1.22  1997/02/03 23:42:08  fritz
+ * Added ISDN_TIMER_RINGING
+ * Misc. changes for Kernel 2.1.X compatibility
+ *
+ * Revision 1.21  1997/01/17 01:19:10  fritz
+ * Applied chargeint patch.
+ *
+ * Revision 1.20  1997/01/17 00:41:19  fritz
+ * Increased TTY_DV.
+ *
+ * Revision 1.19  1997/01/14 01:41:07  fritz
+ * Added ATI2 related variables.
+ * Added variables for audio support in skbuffs.
+ *
  * Revision 1.18  1996/11/06 17:37:50  keil
  * more changes for 2.1.X
  *
@@ -159,7 +208,7 @@
 #define ISDN_USAGE_EXCLUSIVE 64 /* This bit is set, if channel is exclusive */
 #define ISDN_USAGE_OUTGOING 128 /* This bit is set, if channel is outgoing  */
 
-#define ISDN_MODEM_ANZREG    21        /* Number of Modem-Registers        */
+#define ISDN_MODEM_ANZREG    23        /* Number of Modem-Registers        */
 #define ISDN_MSNLEN          20
 
 typedef struct {
@@ -175,12 +224,12 @@
 
 typedef struct {
   char name[10];
-  char phone[20];
+  char phone[ISDN_MSNLEN];
   int  outgoing;
 } isdn_net_ioctl_phone;
 
-#define NET_DV 0x01 /* Data version for net_cfg     */
-#define TTY_DV 0x01 /* Data version for iprofd etc. */
+#define NET_DV 0x04 /* Data version for net_cfg     */
+#define TTY_DV 0x04 /* Data version for iprofd etc. */
 
 typedef struct {
   char name[10];     /* Name of interface                     */
@@ -203,6 +252,8 @@
   int  callback;     /* Flag: Callback                        */
   int  cbhup;        /* Flag: Reject Call before Callback     */
   int  pppbind;      /* ippp device for bindings              */
+  int  chargeint;    /* Use fixed charge interval length      */
+  int  triggercps;   /* BogoCPS needed for triggering slave   */
 } isdn_net_ioctl_cfg;
 
 #ifdef __KERNEL__
@@ -279,6 +330,7 @@
 #define ISDN_TIMER_RES       3                     /* Main Timer-Resolution  */
 #define ISDN_TIMER_02SEC     (HZ/(ISDN_TIMER_RES+1)/5) /* Slow-Timer1 .2 sec */
 #define ISDN_TIMER_1SEC      (HZ/(ISDN_TIMER_RES+1)) /* Slow-Timer2 1 sec   */
+#define ISDN_TIMER_RINGING   5 /* tty RINGs = ISDN_TIMER_1SEC * this factor */
 #define ISDN_TIMER_MODEMREAD 1
 #define ISDN_TIMER_MODEMPLUS 2
 #define ISDN_TIMER_MODEMRING 4
@@ -317,7 +369,7 @@
 /* Phone-list-element */
 typedef struct {
   void *next;
-  char num[20];
+  char num[ISDN_MSNLEN];
 } isdn_net_phone;
 
 /* Local interface-data */
@@ -361,6 +413,7 @@
 				       /* bit0: chargeint is invalid       */
 				       /* bit1: Getting charge-interval    */
                                        /* bit2: Do charge-unit-hangup      */
+                                       /* bit3: Do hangup even on incoming */
   int                    outgoing;     /* Flag: outgoing call              */
   int                    onhtime;      /* Time to keep link up             */
   int                    chargeint;    /* Interval between charge-infos    */
@@ -370,6 +423,7 @@
   int                    sqfull;       /* Flag: netdev-queue overloaded    */
   ulong                  sqfull_stamp; /* Start-Time of overload           */
   ulong                  slavedelay;   /* Dynamic bundling delaytime       */
+  int                    triggercps;   /* BogoCPS needed for trigger slave */
   struct device          *srobin;      /* Ptr to Master device for slaves  */
   isdn_net_phone         *phone[2];    /* List of remote-phonenumbers      */
 				       /* phone[0] = Incoming Numbers      */
@@ -382,11 +436,28 @@
   struct isdn_net_dev_s  *netdev;      /* Ptr to netdev                    */
   struct sk_buff         *first_skb;   /* Ptr to skb that triggers dialing */
   struct sk_buff         *sav_skb;     /* Ptr to skb, rejected by LL-driver*/
+#if (LINUX_VERSION_CODE < 0x02010F)
                                        /* Ptr to orig. header_cache_bind   */
-  void                   (*org_hcb)(struct hh_cache **, struct device *,
-                                    unsigned short, __u32);
+  void                   (*org_hcb)(struct hh_cache **,
+				    struct device *,
+                                    unsigned short, 
+				    __u32);
+#else
+#if (LINUX_VERSION_CODE < 0x2011E)
+                                       /* Ptr to orig. hard_header_cache   */
+  int                    (*org_hhc)(struct dst_entry *dst,
+				    struct dst_entry *neigh,
+				    struct hh_cache *hh);
+#else
+                                       /* Ptr to orig. hard_header_cache   */
+  int                    (*org_hhc)(struct dst_entry *dst,
+				    struct neighbour *neigh,
+				    struct hh_cache *hh);
+#endif
+#endif
                                        /* Ptr to orig. header_cache_update */
-  void                   (*org_hcu)(struct hh_cache *, struct device *,
+  void                   (*org_hcu)(struct hh_cache *,
+				    struct device *,
                                     unsigned char *);
   int  pppbind;                        /* ippp device for bindings         */
 } isdn_net_local;
@@ -435,17 +506,35 @@
 #define ISDN_SERIAL_TYPE_NORMAL            1
 #define ISDN_SERIAL_TYPE_CALLOUT           2
 
+#ifdef CONFIG_ISDN_AUDIO
+/* For using sk_buffs with audio we need some private variables
+ * within each sk_buff. For this purpose, we declare a struct here,
+ * and put it always at skb->head. A few macros help accessing the
+ * variables. Of course, we need to check skb_headroom prior to
+ * any access.
+ */
+typedef struct isdn_audio_skb {
+  unsigned short dle_count;
+  unsigned char  lock;
+} isdn_audio_skb;
+
+#define ISDN_AUDIO_SKB_DLECOUNT(skb) (((isdn_audio_skb*)skb->head)->dle_count)
+#define ISDN_AUDIO_SKB_LOCK(skb) (((isdn_audio_skb*)skb->head)->lock)
+#endif
+
 /* Private data of AT-command-interpreter */
 typedef struct atemu {
   u_char              profile[ISDN_MODEM_ANZREG]; /* Modem-Regs. Profile 0 */
   u_char              mdmreg[ISDN_MODEM_ANZREG];  /* Modem-Registers       */
   char                pmsn[ISDN_MSNLEN]; /* EAZ/MSNs Profile 0             */
   char                msn[ISDN_MSNLEN];/* EAZ/MSN                          */
+#ifdef CONFIG_ISDN_AUDIO
   u_char              vpar[10];        /* Voice-parameters                 */
+  int                 lastDLE;         /* Flag for voice-coding: DLE seen  */
+#endif
   int                 mdmcmdl;         /* Length of Modem-Commandbuffer    */
   int                 pluscount;       /* Counter for +++ sequence         */
   int                 lastplus;        /* Timestamp of last +              */
-  int                 lastDLE;         /* Flag for voice-coding: DLE seen  */
   char                mdmcmd[255];     /* Modem-Commandbuffer              */
 } atemu;
 
@@ -462,26 +551,39 @@
   int			blocked_open;	 /* # of blocked opens             */
   long			session;	 /* Session of opening process     */
   long			pgrp;		 /* pgrp of opening process        */
-  int                   online;          /* B-Channel is up                */
-  int                   vonline;         /* Voice-channel status           */
+  int                   online;          /* 1 = B-Channel is up, drop data */
+					 /* 2 = B-Channel is up, deliver d.*/
   int                   dialing;         /* Dial in progress               */
   int                   rcvsched;        /* Receive needs schedule         */
   int                   isdn_driver;	 /* Index to isdn-driver           */
   int                   isdn_channel;    /* Index to isdn-channel          */
   int                   drv_index;       /* Index to dev->usage            */
   int                   ncarrier;        /* Flag: schedule NO CARRIER      */
+  unsigned char         last_cause[8];   /* Last cause message             */
+  unsigned char         last_num[ISDN_MSNLEN];
+	                                 /* Last phone-number              */
+  unsigned char         last_l2;         /* Last layer-2 protocol          */
+  unsigned char         last_si;         /* Last service                   */
+  unsigned char         last_lhup;       /* Last hangup local?             */
+  unsigned char         last_dir;        /* Last direction (in or out)     */
   struct timer_list     nc_timer;        /* Timer for delayed NO CARRIER   */
   int                   send_outstanding;/* # of outstanding send-requests */
   int                   xmit_size;       /* max. # of chars in xmit_buf    */
   int                   xmit_count;      /* # of chars in xmit_buf         */
   unsigned char         *xmit_buf;       /* transmit buffer                */
   struct sk_buff_head   xmit_queue;      /* transmit queue                 */
+#ifdef CONFIG_ISDN_AUDIO
+  int                   vonline;         /* Voice-channel status           */
+					 /* Bit 0 = recording              */
+					 /* Bit 1 = playback               */
+					 /* Bit 2 = playback, DLE-ETX seen */
   struct sk_buff_head   dtmf_queue;      /* queue for dtmf results         */
-  struct tty_struct 	*tty;            /* Pointer to corresponding tty   */
-  atemu                 emu;             /* AT-emulator data               */
   void                  *adpcms;         /* state for adpcm decompression  */
   void                  *adpcmr;         /* state for adpcm compression    */
   void                  *dtmf_state;     /* state for dtmf decoder         */
+#endif
+  struct tty_struct 	*tty;            /* Pointer to corresponding tty   */
+  atemu                 emu;             /* AT-emulator data               */
   struct termios	normal_termios;  /* For saving termios structs     */
   struct termios	callout_termios;
   struct wait_queue	*open_wait;
@@ -513,8 +615,8 @@
 
 struct sqqueue {
   struct sqqueue *next;
-  int sqno_start;
-  int sqno_end;
+  long sqno_start;
+  long sqno_end;
   struct sk_buff *skb;
   long timer;
 };
@@ -522,7 +624,7 @@
 struct mpqueue {
   struct mpqueue *next;
   struct mpqueue *last;
-  int    sqno;
+  long sqno;
   struct sk_buff *skb;
   int BEbyte;
   unsigned long time;
@@ -561,6 +663,8 @@
   struct slcompress *slcomp;
 #endif
   unsigned long debug;
+  struct isdn_ppp_compressor *compressor,*link_compressor;
+  void *decomp_stat,*comp_stat,*link_decomp_stat,*link_comp_stat;
 };
 
 #endif
@@ -588,7 +692,9 @@
   isdn_if            *interface;        /* Interface to driver              */
   int                *rcverr;           /* Error-counters for B-Ch.-receive */
   int                *rcvcount;         /* Byte-counters for B-Ch.-receive  */
+#ifdef CONFIG_ISDN_AUDIO
   unsigned long      DLEflag;           /* Flags: Insert DLE at next read   */
+#endif
   struct sk_buff_head *rpqueue;         /* Pointers to start of Rcv-Queue   */
   struct wait_queue  **rcv_waitq;       /* Wait-Queues for B-Channel-Reads  */
   struct wait_queue  **snd_waitq;       /* Wait-Queue for B-Channel-Send's  */
@@ -612,7 +718,8 @@
   int               chanmap[ISDN_MAX_CHANNELS];/* Map minor->device-channel  */
   int               drvmap[ISDN_MAX_CHANNELS]; /* Map minor->driver-index    */
   int               usage[ISDN_MAX_CHANNELS];  /* Used by tty/ip/voice       */
-  char              num[ISDN_MAX_CHANNELS][20];/* Remote number of active ch.*/
+  char              num[ISDN_MAX_CHANNELS][ISDN_MSNLEN];
+                                               /* Remote number of active ch.*/
   int               m_idx[ISDN_MAX_CHANNELS];  /* Index for mdm....          */
   driver            *drv[ISDN_MAX_DRIVERS];    /* Array of drivers           */
   isdn_net_dev      *netdev;		       /* Linked list of net-if's    */
diff -u --recursive --new-file v2.0.30/linux/include/linux/isdn_ppp.h linux/include/linux/isdn_ppp.h
--- v2.0.30/linux/include/linux/isdn_ppp.h	Sun May 19 05:29:31 1996
+++ linux/include/linux/isdn_ppp.h	Mon Aug  4 17:34:01 1997
@@ -4,25 +4,29 @@
 extern int isdn_ppp_dial_slave(char *);
 extern int isdn_ppp_hangup_slave(char *);
 
-struct pppinfo
+#define CALLTYPE_INCOMING 0x1
+#define CALLTYPE_OUTGOING 0x2
+#define CALLTYPE_CALLBACK 0x4
+
+struct pppcallinfo
 {
-  int type; /* set by user */
-  union {
-    char clid[32]; /* calling ID */
-    int  bundles;
-    int  linknumber;
-  } info;
+	int calltype;
+	unsigned char local_num[64];
+	unsigned char remote_num[64];
+	int charge_units;
 };
 
- 
-#define PPPIOCLINKINFO _IOWR('t',128,struct pppinfo)
+#define PPPIOCGCALLINFO _IOWR('t',128,struct pppcallinfo)
 #define PPPIOCBUNDLE   _IOW('t',129,int)
 #define PPPIOCGMPFLAGS _IOR('t',130,int)
 #define PPPIOCSMPFLAGS _IOW('t',131,int)
 #define PPPIOCSMPMTU   _IOW('t',132,int)
 #define PPPIOCSMPMRU   _IOW('t',133,int)
+#define PPPIOCGCOMPRESSORS _IOR('t',134,unsigned long)
+#define PPPIOCSCOMPRESSOR _IOW('t',135,int)
 
-#define PPP_MP         0x003d
+#define PPP_MP          0x003d
+#define PPP_LINK_COMP   0x00fb
 
 #define SC_MP_PROT       0x00000200
 #define SC_REJ_MP_PROT   0x00000400
@@ -32,4 +36,36 @@
 #define MP_END_FRAG    0x40
 #define MP_BEGIN_FRAG  0x80
 
-#endif
+#ifdef __KERNEL__
+/*
+ * this is an 'old friend' from ppp-comp.h under a new name 
+ * check the original include for more information
+ */
+struct isdn_ppp_compressor {
+	struct isdn_ppp_compressor *next,*prev;
+	int num; /* CCP compression protocol number */
+	void *(*comp_alloc) (unsigned char *options, int opt_len);
+	void (*comp_free) (void *state);
+	int  (*comp_init) (void *state, unsigned char *options, int opt_len,
+		 int unit, int opthdr, int debug);
+	void (*comp_reset) (void *state);
+	int  (*compress) (void *state,struct sk_buff *in, struct sk_buff *skb_out,
+		 int proto);
+	void (*comp_stat) (void *state, struct compstat *stats);
+	void *(*decomp_alloc) (unsigned char *options, int opt_len);
+	void (*decomp_free) (void *state);
+	int  (*decomp_init) (void *state, unsigned char *options,
+			int opt_len, int unit, int opthdr, int mru, int debug);
+	void (*decomp_reset) (void *state);
+	int  (*decompress) (void *state, unsigned char *ibuf, int isize, unsigned char *obuf, int osize);
+	void (*incomp) (void *state, unsigned char *ibuf, int icnt);
+	void (*decomp_stat) (void *state, struct compstat *stats);
+};
+
+extern int isdn_ppp_register_compressor(struct isdn_ppp_compressor *);
+extern int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *);
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_ISDN_PPP_H */
+
diff -u --recursive --new-file v2.0.30/linux/include/linux/isdnif.h linux/include/linux/isdnif.h
--- v2.0.30/linux/include/linux/isdnif.h	Sun Dec  1 10:01:37 1996
+++ linux/include/linux/isdnif.h	Mon Aug  4 17:34:01 1997
@@ -1,4 +1,4 @@
-/* $Id: isdnif.h,v 1.13 1996/11/13 02:39:59 fritz Exp $
+/* $Id: isdnif.h,v 1.20 1997/05/27 15:18:06 fritz Exp $
  *
  * Linux ISDN subsystem
  *
@@ -22,6 +22,32 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: isdnif.h,v $
+ * Revision 1.20  1997/05/27 15:18:06  fritz
+ * Added changes for recent 2.1.x kernels:
+ *   changed return type of isdn_close
+ *   queue_task_* -> queue_task
+ *   clear/set_bit -> test_and_... where apropriate.
+ *   changed type of hard_header_cache parameter.
+ *
+ * Revision 1.19  1997/03/25 23:13:56  keil
+ * NI-1 US protocol
+ *
+ * Revision 1.18  1997/03/04 22:09:18  calle
+ * Change macros copy_from_user and copy_to_user in inline function.
+ * These are now correct replacements of the functions for 2.1.xx
+ *
+ * Revision 1.17  1997/02/10 21:12:53  fritz
+ * More setup-interface changes.
+ *
+ * Revision 1.16  1997/02/10 19:42:57  fritz
+ * New interface for reporting incoming calls.
+ *
+ * Revision 1.15  1997/02/09 00:18:42  keil
+ * leased line support
+ *
+ * Revision 1.14  1997/02/03 23:43:00  fritz
+ * Misc changes for Kernel 2.1.X compatibility.
+ *
  * Revision 1.13  1996/11/13 02:39:59  fritz
  * More compatibility changes.
  *
@@ -76,6 +102,8 @@
 #define ISDN_PTYPE_UNKNOWN   0   /* Protocol undefined   */
 #define ISDN_PTYPE_1TR6      1   /* german 1TR6-protocol */
 #define ISDN_PTYPE_EURO      2   /* EDSS1-protocol       */
+#define ISDN_PTYPE_LEASED    3   /* for leased lines     */
+#define ISDN_PTYPE_NI1       4   /* US NI-1 protocol     */
 
 /*
  * Values for Layer-2-protocol-selection
@@ -156,6 +184,16 @@
 #define ISDN_FEATURE_P_UNKNOWN  (0x1000 << ISDN_PTYPE_UNKNOWN)
 #define ISDN_FEATURE_P_1TR6     (0x1000 << ISDN_PTYPE_1TR6)
 #define ISDN_FEATURE_P_EURO     (0x1000 << ISDN_PTYPE_EURO)
+#define ISDN_FEATURE_P_NI1      (0x1000 << ISDN_PTYPE_NI1)
+
+typedef struct setup_parm {
+    char phone[32];         /* Remote Phone-Number */
+    char eazmsn[32];        /* Local EAZ or MSN    */
+    unsigned char si1;      /* Service Indicator 1 */
+    unsigned char si2;      /* Service Indicator 2 */
+    unsigned char plan;     /* Numbering plan      */
+    unsigned char screen;   /* Screening info      */
+} setup_parm;
 
 /*
  * Structure for exchanging above infos
@@ -165,7 +203,10 @@
   int   driver;                  /* Lowlevel-Driver-ID                    */
   int   command;                 /* Command or Status (see above)         */
   ulong arg;                     /* Additional Data                       */
-  char  num[50];                 /* Additional Data                       */
+  union {
+	char  num[50];               /* Additional Data                       */
+	setup_parm setup;
+  } parm;
 } isdn_ctrl;
 
 /*
@@ -321,8 +362,25 @@
 #endif
 #if (LINUX_VERSION_CODE < 0x020100)
 #include <linux/mm.h>
-#define copy_from_user memcpy_fromfs
-#define copy_to_user memcpy_tofs
+
+static inline unsigned long copy_from_user(void *to, const void *from, unsigned long n)
+{
+	int i;
+	if ((i = verify_area(VERIFY_READ, from, n)) != 0)
+		return i;
+	memcpy_fromfs(to, from, n);
+	return 0;
+}
+
+static inline unsigned long copy_to_user(void *to, const void *from, unsigned long n)
+{
+	int i;
+	if ((i = verify_area(VERIFY_WRITE, to, n)) != 0)
+		return i;
+	memcpy_tofs(to, from, n);
+	return 0;
+}
+
 #define GET_USER(x, addr) ( x = get_user(addr) )
 #define RWTYPE int
 #define LSTYPE int
@@ -338,6 +396,24 @@
 #define LSARG long long
 #endif
 
+#if (LINUX_VERSION_CODE < 0x02010F)
+#define SET_SKB_FREE(x) ( x->free = 1 )
+#else
+#define SET_SKB_FREE(x)
+#endif
+
+#if (LINUX_VERSION_CODE < 0x02011F)
+#define CLOSETYPE void
+#define CLOSEVAL
+#else
+#define CLOSETYPE int
+#define CLOSEVAL (0)
+#endif
+
+#if (LINUX_VERSION_CODE < 0x020125)
+#define test_and_clear_bit clear_bit
+#define test_and_set_bit set_bit
+#endif
+
 #endif /* __KERNEL__ */
 #endif /* isdnif_h */
-
diff -u --recursive --new-file v2.0.30/linux/include/linux/kernel.h linux/include/linux/kernel.h
--- v2.0.30/linux/include/linux/kernel.h	Mon May 20 10:33:57 1996
+++ linux/include/linux/kernel.h	Thu Aug 14 10:05:47 1997
@@ -68,6 +68,16 @@
  */
 #define fsuser() (current->fsuid == 0)
 
+/*
+ *      Display an IP address in readable format. 
+ */
+  
+#define NIPQUAD(addr) \
+        (((addr) >> 0)  & 0xff), \
+        (((addr) >> 8)  & 0xff), \
+        (((addr) >> 16) & 0xff), \
+        (((addr) >> 24) & 0xff)
+
 #endif /* __KERNEL__ */
 
 #define SI_LOAD_SHIFT	16
diff -u --recursive --new-file v2.0.30/linux/include/linux/kernelcapi.h linux/include/linux/kernelcapi.h
--- v2.0.30/linux/include/linux/kernelcapi.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/kernelcapi.h	Mon Aug  4 17:34:01 1997
@@ -0,0 +1,96 @@
+/*
+ * $Id: kernelcapi.h,v 1.1 1997/03/04 21:27:33 calle Exp $
+ * 
+ * Kernel CAPI 2.0 Interface for Linux
+ * 
+ * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
+ * 
+ * $Log: kernelcapi.h,v $
+ * Revision 1.1  1997/03/04 21:27:33  calle
+ * First version in isdn4linux
+ *
+ * Revision 2.2  1997/02/12 09:31:39  calle
+ * new version
+ *
+ * Revision 1.1  1997/01/31 10:32:20  calle
+ * Initial revision
+ *
+ * 
+ */
+#ifndef __KERNEL_CAPI_H__
+#define __KERNEL_CAPI_H__
+
+#define CAPI_MAXAPPL	20	/*
+				   * maximum number of applications 
+				 */
+#define CAPI_MAXCONTR	4	/*
+				   * maximum number of controller 
+				 */
+#define CAPI_MAXDATAWINDOW	8
+
+#ifdef __KERNEL__
+
+struct capi_interface {
+	int (*capi_installed) (void);
+
+	 __u16(*capi_register) (capi_register_params * rparam, __u16 * applidp);
+	 __u16(*capi_release) (__u16 applid);
+	 __u16(*capi_put_message) (__u16 applid, struct sk_buff * msg);
+	 __u16(*capi_get_message) (__u16 applid, struct sk_buff ** msgp);
+	 __u16(*capi_set_signal) (__u16 applid,
+			      void (*signal) (__u16 applid, __u32 param),
+				  __u32 param);
+	 __u16(*capi_get_manufacturer) (__u16 contr, __u8 buf[CAPI_MANUFACTURER_LEN]);
+	 __u16(*capi_get_version) (__u16 contr, struct capi_version * verp);
+	 __u16(*capi_get_serial) (__u16 contr, __u8 serial[CAPI_SERIAL_LEN]);
+	 __u16(*capi_get_profile) (__u16 contr, struct capi_profile * profp);
+
+	/*
+	 * to init controllers, data is always in user memory
+	 */
+	int (*capi_manufacturer) (unsigned int cmd, void *data);
+
+};
+
+#define	KCI_CONTRUP	0
+#define	KCI_CONTRDOWN	1
+
+struct capi_interface_user {
+	char name[20];
+	void (*callback) (unsigned int cmd, __u16 contr, void *data);
+	struct capi_interface_user *next;
+};
+
+struct capi_interface *attach_capi_interface(struct capi_interface_user *);
+int detach_capi_interface(struct capi_interface_user *);
+
+
+#define CAPI_NOERROR                      0x0000
+
+#define CAPI_TOOMANYAPPLS		  0x1001
+#define CAPI_LOGBLKSIZETOSMALL	          0x1002
+#define CAPI_BUFFEXECEEDS64K 	          0x1003
+#define CAPI_MSGBUFSIZETOOSMALL	          0x1004
+#define CAPI_ANZLOGCONNNOTSUPPORTED	  0x1005
+#define CAPI_REGRESERVED		  0x1006
+#define CAPI_REGBUSY 		          0x1007
+#define CAPI_REGOSRESOURCEERR	          0x1008
+#define CAPI_REGNOTINSTALLED 	          0x1009
+#define CAPI_REGCTRLERNOTSUPPORTEXTEQUIP  0x100a
+#define CAPI_REGCTRLERONLYSUPPORTEXTEQUIP 0x100b
+
+#define CAPI_ILLAPPNR		          0x1101
+#define CAPI_ILLCMDORSUBCMDORMSGTOSMALL   0x1102
+#define CAPI_SENDQUEUEFULL		  0x1103
+#define CAPI_RECEIVEQUEUEEMPTY	          0x1104
+#define CAPI_RECEIVEOVERFLOW 	          0x1105
+#define CAPI_UNKNOWNNOTPAR		  0x1106
+#define CAPI_MSGBUSY 		          0x1107
+#define CAPI_MSGOSRESOURCEERR	          0x1108
+#define CAPI_MSGNOTINSTALLED 	          0x1109
+#define CAPI_MSGCTRLERNOTSUPPORTEXTEQUIP  0x110a
+#define CAPI_MSGCTRLERONLYSUPPORTEXTEQUIP 0x110b
+
+#endif				/* __KERNEL__ */
+
+#endif				/* __KERNEL_CAPI_H__ */
diff -u --recursive --new-file v2.0.30/linux/include/linux/major.h linux/include/linux/major.h
--- v2.0.30/linux/include/linux/major.h	Sat Nov 16 03:00:47 1996
+++ linux/include/linux/major.h	Tue Aug 12 13:57:23 1997
@@ -69,6 +69,9 @@
 #define APBLOCK_MAJOR   60   /* AP1000 Block device */
 #define DDV_MAJOR       61   /* AP1000 DDV block device */
 
+#define SPECIALIX_NORMAL_MAJOR 75
+#define SPECIALIX_CALLOUT_MAJOR 76
+
 /*
  * Tests for SCSI devices.
  */
diff -u --recursive --new-file v2.0.30/linux/include/linux/mm.h linux/include/linux/mm.h
--- v2.0.30/linux/include/linux/mm.h	Fri Mar 28 16:08:17 1997
+++ linux/include/linux/mm.h	Sat Aug 23 10:54:52 1997
@@ -295,7 +295,7 @@
 
 /* filemap.c */
 extern unsigned long page_unuse(unsigned long);
-extern int shrink_mmap(int, int);
+extern int shrink_mmap(int, int, int);
 extern void truncate_inode_pages(struct inode *, unsigned long);
 
 #define GFP_BUFFER	0x00
diff -u --recursive --new-file v2.0.30/linux/include/linux/net_alias.h linux/include/linux/net_alias.h
--- v2.0.30/linux/include/linux/net_alias.h	Fri Mar 28 16:09:02 1997
+++ linux/include/linux/net_alias.h	Sat Aug 23 10:55:26 1997
@@ -2,7 +2,7 @@
  *		NET_ALIAS network device aliasing definitions.
  *
  *
- * Version:	@(#)net_alias.h	0.43   12/20/95
+ * Version:	@(#)net_alias.h	0.50   4/20/97
  *
  * Author:	Juan Jose Ciarlante, <jjciarla@raiz.uncu.edu.ar>
  *
@@ -12,6 +12,10 @@
  *	as published by the Free Software Foundation; either version
  *	2 of the License, or (at your option) any later version.
  *	
+ * Fixes:
+ *	Juan Jose Ciarlante	:	Added tx/rx stats for aliases.
+ *	Juan Jose Ciarlante 	:	hash_tab size now in net_alias.c
+ *	Juan Jose Ciarlante	:	added sysctl interface
  */
 
 #ifndef _NET_ALIAS_H
@@ -21,12 +25,6 @@
 #include <linux/if.h>
 #include <linux/netdevice.h>
 
-/*
- * max. alias slot number allowed 
- */
-
-#define NET_ALIAS_MAX_SLOT  256
-
 struct net_alias;
 struct net_alias_info;
 struct net_alias_type;
@@ -47,6 +45,8 @@
   struct device *main_dev;	/* pointer to main device */
   struct net_alias_type *nat;	/* alias type object bound */
   struct net_alias *next;	/* next alias (hashed linked list) */
+  unsigned long rx_lookups;     /* 'fake' rx pkts */
+  unsigned long tx_lookups;     /* 'fake' tx pkts */
 };
 
 
@@ -58,8 +58,11 @@
 struct net_alias_info
 {
   int n_aliases;		/* num aliases */
+  int truesize;                 /* actual malloc size for struct + hashtab */
   struct device *taildev;	/* my last (alias) device */
-  struct net_alias *hash_tab[16]; /* hashed alias table */
+  int max_aliases;              /* max aliases allowed for main device */
+  unsigned hash_tab_size;       /* hash_tab size in elements */
+  struct net_alias *hash_tab[0]; /* hashed alias table */
 };
 
 /*
@@ -110,23 +113,42 @@
   return (dev->alias_info != NULL);
 }
 
-
+/*
+ *	Initialise net_alias module
+ */
 extern void net_alias_init(void);
 
+/*
+ *	dev_get() with added aliasing magic
+ */
 extern struct device * net_alias_dev_get(char *dev_name, int aliasing_ok, int *err, struct sockaddr *sa, void *data);
 extern int net_alias_dev_rehash(struct device *dev, struct sockaddr *sa);
 
+/*
+ *	PROC_FS entries
+ */
 extern int net_alias_getinfo(char *buf, char **, off_t , int , int );
 extern int net_alias_types_getinfo(char *buf, char **, off_t , int , int );
 
+/*
+ *	net_alias_type (address family) registration 
+ */
 extern int register_net_alias_type(struct net_alias_type *nat, int type);
 extern int unregister_net_alias_type(struct net_alias_type *nat);
 
+/*
+ *	get alias device _with_ specified address
+ */
 extern struct device * net_alias_dev_chk(struct device *main_dev, struct sockaddr *sa, int flags_on, int flags_off);
 extern struct device * net_alias_dev_chk32(struct device *main_dev, int family, __u32 addr32, int flags_on, int flags_off);
 
-extern struct device * net_alias_dev_rcv_sel(struct device *main_dev, struct sockaddr *sa_src, struct sockaddr *sa_dst);
-extern struct device * net_alias_dev_rcv_sel32(struct device *main_dev, int family, __u32 src, __u32 dst);
+/*
+ *	get 'closest' device to specified address (returns main_dev if
+ *	nothing better)
+ *	if succesfull, also increment rx stats.
+ */
+extern struct device * net_alias_dev_rx(struct device *main_dev, struct sockaddr *sa_src, struct sockaddr *sa_dst);
+extern struct device * net_alias_dev_rx32(struct device *main_dev, int family, __u32 src, __u32 dst);
 
 
 /*
@@ -167,6 +189,32 @@
   }
   pdev->next = nextdev;
   return nextdev;
+}
+
+/*
+ *	lookup counters (used for alias devices stats)
+ */
+static __inline__ void net_alias_inc_rx(struct net_alias *alias)
+{
+        if (alias != NULL) alias->rx_lookups++;
+}
+static __inline__ void net_alias_inc_tx(struct net_alias *alias)
+{
+        if (alias != NULL) alias->tx_lookups++;
+}
+
+/*
+ *	To be called when passing down a pkt, to _switch_ from alias device
+ *	to actual device, also incr. alias tx counter.
+ */
+static __inline__ struct device *net_alias_dev_tx(struct device *dev)
+{
+        struct net_alias *alias = dev->my_alias;
+        if (alias) {
+                net_alias_inc_tx(alias);
+                return alias->main_dev;
+        } 
+        return dev;
 }
 
 #endif  /* _NET_ALIAS_H */
diff -u --recursive --new-file v2.0.30/linux/include/linux/pagemap.h linux/include/linux/pagemap.h
--- v2.0.30/linux/include/linux/pagemap.h	Fri Mar 28 16:08:17 1997
+++ linux/include/linux/pagemap.h	Sat Aug 23 10:54:52 1997
@@ -11,6 +11,7 @@
 
 #include <linux/mm.h>
 #include <linux/fs.h>
+#include <linux/swapctl.h>
 
 static inline unsigned long page_address(struct page * page)
 {
@@ -20,7 +21,7 @@
 #define PAGE_HASH_BITS 11
 #define PAGE_HASH_SIZE (1 << PAGE_HASH_BITS)
 
-#define PAGE_AGE_VALUE 16
+#define PAGE_AGE_VALUE ((PAGE_INITIAL_AGE)+(PAGE_ADVANCE))
 
 extern unsigned long page_cache_size; /* # of pages currently in the hash table */
 extern struct page * page_hash_table[PAGE_HASH_SIZE];
diff -u --recursive --new-file v2.0.30/linux/include/linux/pci.h linux/include/linux/pci.h
--- v2.0.30/linux/include/linux/pci.h	Mon Mar 31 13:23:23 1997
+++ linux/include/linux/pci.h	Mon Aug 18 15:19:41 1997
@@ -31,14 +31,14 @@
  *	  PCI-CPU bridge or PCI-ISA bridge.
  *	- If you can't find the actual information in your hardware
  *	  booklet, try to read the references of the chip on the board.
- *	- Send all that to linux-pcisupport@cao-vlsi.ibp.fr,
+ *	- Send all that to linux-pcisupport@cck.uni-kl.de
  *	  and I'll add your device to the list as soon as possible
  *
  * BEFORE you send a mail, please check the latest linux releases
  * to be sure it has not been recently added.
  *
  *        Thanks
- *		Frederic Potter.
+ *		Jens Maurer
  */
 
 
@@ -221,6 +221,9 @@
  */
 #define PCI_VENDOR_ID_COMPAQ		0x0e11
 #define PCI_DEVICE_ID_COMPAQ_1280	0x3033
+#define PCI_DEVICE_ID_COMPAQ_NETELL100	0xae32
+#define PCI_DEVICE_ID_COMPAQ_NETELL10	0xae34
+#define PCI_DEVICE_ID_COMPAQ_NETFLEX3	0xae35
 #define PCI_DEVICE_ID_COMPAQ_THUNDER	0xf130
 
 #define PCI_VENDOR_ID_NCR		0x1000
@@ -228,18 +231,26 @@
 #define PCI_DEVICE_ID_NCR_53C820	0x0002
 #define PCI_DEVICE_ID_NCR_53C825	0x0003
 #define PCI_DEVICE_ID_NCR_53C815	0x0004
+#define PCI_DEVICE_ID_NCR_53C860	0x0006
+#define PCI_DEVICE_ID_NCR_53C896	0x000b
+#define PCI_DEVICE_ID_NCR_53C895	0x000c
+#define PCI_DEVICE_ID_NCR_53C885	0x000d
+#define PCI_DEVICE_ID_NCR_53C875	0x000f
 
 #define PCI_VENDOR_ID_ATI		0x1002
 #define PCI_DEVICE_ID_ATI_68800		0x4158
 #define PCI_DEVICE_ID_ATI_215CT222	0x4354
 #define PCI_DEVICE_ID_ATI_210888CX	0x4358
+#define PCI_DEVICE_ID_ATI_215GT		0x4754
 #define PCI_DEVICE_ID_ATI_210888GX	0x4758
+#define PCI_DEVICE_ID_ATI_264VT		0x5654
 
 #define PCI_VENDOR_ID_VLSI		0x1004
 #define PCI_DEVICE_ID_VLSI_82C592	0x0005
 #define PCI_DEVICE_ID_VLSI_82C593	0x0006
 #define PCI_DEVICE_ID_VLSI_82C594	0x0007
 #define PCI_DEVICE_ID_VLSI_82C597	0x0009
+#define PCI_DEVICE_ID_VLSI_VAS96011	0x0702
 
 #define PCI_VENDOR_ID_ADL		0x1005
 #define PCI_DEVICE_ID_ADL_2301		0x2301
@@ -252,6 +263,7 @@
 #define PCI_DEVICE_ID_TSENG_W32P_b	0x3205
 #define PCI_DEVICE_ID_TSENG_W32P_c	0x3206
 #define PCI_DEVICE_ID_TSENG_W32P_d	0x3207
+#define PCI_DEVICE_ID_TSENG_ET6000	0x3208
 
 #define PCI_VENDOR_ID_WEITEK		0x100e
 #define PCI_DEVICE_ID_WEITEK_P9000	0x9001
@@ -264,20 +276,30 @@
 #define PCI_DEVICE_ID_DEC_TULIP_FAST	0x0009
 #define PCI_DEVICE_ID_DEC_FDDI		0x000F
 #define PCI_DEVICE_ID_DEC_TULIP_PLUS	0x0014
+#define PCI_DEVICE_ID_DEC_21142		0x0019
 #define PCI_DEVICE_ID_DEC_21052		0x0021
 #define PCI_DEVICE_ID_DEC_21152		0x0024
 
 #define PCI_VENDOR_ID_CIRRUS		0x1013
+#define PCI_DEVICE_ID_CIRRUS_7548	0x0038
 #define PCI_DEVICE_ID_CIRRUS_5430	0x00a0
 #define PCI_DEVICE_ID_CIRRUS_5434_4	0x00a4
 #define PCI_DEVICE_ID_CIRRUS_5434_8	0x00a8
 #define PCI_DEVICE_ID_CIRRUS_5436	0x00ac
+#define PCI_DEVICE_ID_CIRRUS_5446	0x00b8
+#define PCI_DEVICE_ID_CIRRUS_5480	0x00bc
+#define PCI_DEVICE_ID_CIRRUS_5464	0x00d4
+#define PCI_DEVICE_ID_CIRRUS_5465	0x00d6
 #define PCI_DEVICE_ID_CIRRUS_6729	0x1100
+#define PCI_DEVICE_ID_CIRRUS_6832	0x1110
 #define PCI_DEVICE_ID_CIRRUS_7542	0x1200
 #define PCI_DEVICE_ID_CIRRUS_7543	0x1202
+#define PCI_DEVICE_ID_CIRRUS_7541	0x1204
 
 #define PCI_VENDOR_ID_IBM		0x1014
+#define PCI_DEVICE_ID_IBM_FIRE_CORAL	0x000a
 #define PCI_DEVICE_ID_IBM_82G2675	0x001d
+#define PCI_DEVICE_ID_IBM_82351		0x0022
 
 #define PCI_VENDOR_ID_WD		0x101c
 #define PCI_DEVICE_ID_WD_7197		0x3296
@@ -298,12 +320,14 @@
 #define PCI_DEVICE_ID_MATROX_MGA_2	0x0518
 #define PCI_DEVICE_ID_MATROX_MIL	0x0519
 #define PCI_DEVICE_ID_MATROX_MYS	0x051A
+#define PCI_DEVICE_ID_MATROX_MIL_2	0x051b
 #define PCI_DEVICE_ID_MATROX_MGA_IMP	0x0d10
 
 #define PCI_VENDOR_ID_CT		0x102c
 #define PCI_DEVICE_ID_CT_65545		0x00d8
 #define PCI_DEVICE_ID_CT_65548		0x00dc
 #define PCI_DEVICE_ID_CT_65550		0x00e0
+#define PCI_DEVICE_ID_CT_65554		0x00e4
 
 #define PCI_VENDOR_ID_MIRO		0x1031
 #define PCI_DEVICE_ID_MIRO_36050	0x5601
@@ -319,11 +343,15 @@
 #define PCI_DEVICE_ID_SI_501		0x0406
 #define PCI_DEVICE_ID_SI_496		0x0496
 #define PCI_DEVICE_ID_SI_601		0x0601
+#define PCI_DEVICE_ID_SI_5107		0x5107
 #define PCI_DEVICE_ID_SI_5511		0x5511
 #define PCI_DEVICE_ID_SI_5513		0x5513
+#define PCI_DEVICE_ID_SI_5571		0x5571
+#define PCI_DEVICE_ID_SI_7001		0x7001
 
 #define PCI_VENDOR_ID_HP		0x103c
 #define PCI_DEVICE_ID_HP_J2585A		0x1030
+#define PCI_DEVICE_ID_HP_J2585B		0x1031
 
 #define PCI_VENDOR_ID_PCTECH		0x1042
 #define PCI_DEVICE_ID_PCTECH_RZ1000	0x1000
@@ -348,9 +376,22 @@
 #define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER    0x1040
 #define PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT     0x8130
 
+#define PCI_VENDOR_ID_TI		0x104c
+#define PCI_DEVICE_ID_TI_PCI1130	0xac12
+#define PCI_DEVICE_ID_TI_PCI1131	0xac15
+
 #define PCI_VENDOR_ID_OAK		0x104e
 #define PCI_DEVICE_ID_OAK_OTI107	0x0107
 
+/* Winbond have two vendor IDs! See 0x10ad as well */
+#define PCI_VENDOR_ID_WINBOND2		0x1050
+#define PCI_DEVICE_ID_WINBOND2_89C940	0x0940
+
+#define PCI_VENDOR_ID_MOTOROLA		0x1057
+#define PCI_DEVICE_ID_MOTOROLA_MPC105	0x0001
+#define PCI_DEVICE_ID_MOTOROLA_MPC106	0x0002
+#define PCI_DEVICE_ID_MOTOROLA_RAVEN	0x4801
+
 #define PCI_VENDOR_ID_PROMISE		0x105a
 #define PCI_DEVICE_ID_PROMISE_5300	0x5300
 
@@ -372,6 +413,11 @@
 #define PCI_VENDOR_ID_X			0x1061
 #define PCI_DEVICE_ID_X_AGX016		0x0001
 
+#define PCI_VENDOR_ID_APPLE		0x106b
+#define PCI_DEVICE_ID_APPLE_BANDIT	0x0001
+#define PCI_DEVICE_ID_APPLE_GC		0x0002
+#define PCI_DEVICE_ID_APPLE_HYDRA	0x000e
+
 #define PCI_VENDOR_ID_NEXGEN		0x1074
 #define PCI_DEVICE_ID_NEXGEN_82C501	0x4e78
 
@@ -388,15 +434,26 @@
 #define PCI_VENDOR_ID_FOREX		0x1083
 
 #define PCI_VENDOR_ID_OLICOM		0x108d
+#define PCI_DEVICE_ID_OLICOM_OC3136	0x0001
+#define PCI_DEVICE_ID_OLICOM_OC2315	0x0011
+#define PCI_DEVICE_ID_OLICOM_OC2325	0x0012
+#define PCI_DEVICE_ID_OLICOM_OC2183	0x0013
+#define PCI_DEVICE_ID_OLICOM_OC2326	0x0014
+#define PCI_DEVICE_ID_OLICOM_OC6151	0x0021
 
 #define PCI_VENDOR_ID_CMD		0x1095
 #define PCI_DEVICE_ID_CMD_640		0x0640
+#define PCI_DEVICE_ID_CMD_643		0x0643
 #define PCI_DEVICE_ID_CMD_646		0x0646
+#define PCI_DEVICE_ID_CMD_670		0x0670
 
 #define PCI_VENDOR_ID_VISION		0x1098
 #define PCI_DEVICE_ID_VISION_QD8500	0x0001
 #define PCI_DEVICE_ID_VISION_QD8580	0x0002
 
+#define PCI_VENDOR_ID_BROOKTREE		0x109e
+#define PCI_DEVICE_ID_BROOKTREE_848	0x0350
+
 #define PCI_VENDOR_ID_SIERRA		0x10a8
 #define PCI_DEVICE_ID_SIERRA_STB	0x0000
 
@@ -406,12 +463,21 @@
 #define PCI_VENDOR_ID_WINBOND		0x10ad
 #define PCI_DEVICE_ID_WINBOND_83769	0x0001
 #define PCI_DEVICE_ID_WINBOND_82C105	0x0105
+#define PCI_DEVICE_ID_WINBOND_83C553	0x0565
+
+#define PCI_VENDOR_ID_DATABOOK		0x10b3
+#define PCI_DEVICE_ID_DATABOOK_87144	0xb106
 
 #define PCI_VENDOR_ID_3COM		0x10b7
 #define PCI_DEVICE_ID_3COM_3C590	0x5900
 #define PCI_DEVICE_ID_3COM_3C595TX	0x5950
 #define PCI_DEVICE_ID_3COM_3C595T4	0x5951
 #define PCI_DEVICE_ID_3COM_3C595MII	0x5952
+#define PCI_DEVICE_ID_3COM_3C900TPO	0x9000
+#define PCI_DEVICE_ID_3COM_3C900COMBO	0x9001
+#define PCI_DEVICE_ID_3COM_3C905TX	0x9050
+
+#define PCI_VENDOR_ID_SMC		0x10b8
 
 #define PCI_VENDOR_ID_AL		0x10b9
 #define PCI_DEVICE_ID_AL_M1445		0x1445
@@ -423,8 +489,15 @@
 #define PCI_DEVICE_ID_AL_M1513		0x1513
 #define PCI_DEVICE_ID_AL_M4803		0x5215
 
+#define PCI_VENDOR_ID_MITSUBISHI	0x10ba
+
+#define PCI_VENDOR_ID_NEOMAGIC          0x10c8
+#define PCI_DEVICE_ID_NEOMAGIC_MAGICGRAPH_NM2070 0x0001
+#define PCI_DEVICE_ID_NEOMAGIC_MAGICGRAPH_128V 0x0002
+
 #define PCI_VENDOR_ID_ASP		0x10cd
 #define PCI_DEVICE_ID_ASP_ABP940	0x1200
+#define PCI_DEVICE_ID_ASP_ABP940U	0x1300
 
 #define PCI_VENDOR_ID_CERN		0x10dc
 #define PCI_DEVICE_ID_CERN_SPSB_PMC	0x0001
@@ -436,14 +509,23 @@
 #define PCI_VENDOR_ID_TEKRAM2		0x10e1
 #define PCI_DEVICE_ID_TEKRAM2_690c	0x690c
 
+#define PCI_VENDOR_ID_TUNDRA		0x10e3
+#define PCI_DEVICE_ID_TUNDRA_CA91C042	0x0000
+
 #define PCI_VENDOR_ID_AMCC		0x10e8
 #define PCI_DEVICE_ID_AMCC_MYRINET	0x8043
+#define PCI_DEVICE_ID_AMCC_S5933	0x807d
 
 #define PCI_VENDOR_ID_INTERG		0x10ea
 #define PCI_DEVICE_ID_INTERG_1680	0x1680
+#define PCI_DEVICE_ID_INTERG_1682	0x1682
 
 #define PCI_VENDOR_ID_REALTEK		0x10ec
 #define PCI_DEVICE_ID_REALTEK_8029	0x8029
+#define PCI_DEVICE_ID_REALTEK_8129	0x8129
+
+#define PCI_VENDOR_ID_TRUEVISION	0x10fa
+#define PCI_DEVICE_ID_TRUEVISION_T1000	0x000c
 
 #define PCI_VENDOR_ID_INIT		0x1101
 #define PCI_DEVICE_ID_INIT_320P		0x9100
@@ -451,7 +533,11 @@
 #define PCI_VENDOR_ID_VIA		0x1106
 #define PCI_DEVICE_ID_VIA_82C505	0x0505
 #define PCI_DEVICE_ID_VIA_82C561	0x0561
+#define PCI_DEVICE_ID_VIA_82C586_1	0x0571
 #define PCI_DEVICE_ID_VIA_82C576	0x0576
+#define PCI_DEVICE_ID_VIA_82C585	0x0585
+#define PCI_DEVICE_ID_VIA_82C586_0	0x0586
+#define PCI_DEVICE_ID_VIA_82C926	0x0926
 #define PCI_DEVICE_ID_VIA_82C416	0x1571
 
 #define PCI_VENDOR_ID_VORTEX		0x1119
@@ -481,6 +567,9 @@
 #define PCI_VENDOR_ID_IMAGINGTECH	0x112f
 #define PCI_DEVICE_ID_IMAGINGTECH_ICPCI	0x0000
 
+#define PCI_VENDOR_ID_PHILIPS		0x1131
+#define PCI_DEVICE_ID_PHILIPS_SAA7146	0x7146
+
 #define PCI_VENDOR_ID_PLX		0x113c
 #define PCI_DEVICE_ID_PLX_9060		0x0001
 
@@ -497,16 +586,41 @@
 #define PCI_VENDOR_ID_MUTECH		0x1159
 #define PCI_DEVICE_ID_MUTECH_MV1000	0x0001
 
+#define PCI_VENDOR_ID_RENDITION		0x1163
+#define PCI_DEVICE_ID_RENDITION_VERITE	0x0001
+
 #define PCI_VENDOR_ID_TOSHIBA		0x1179
+#define PCI_DEVICE_ID_TOSHIBA_601	0x0601
+
+#define PCI_VENDOR_ID_RICOH		0x1180
+#define PCI_DEVICE_ID_RICOH_RL5C466	0x0466
 
 #define PCI_VENDOR_ID_ZEITNET		0x1193
 #define PCI_DEVICE_ID_ZEITNET_1221	0x0001
 #define PCI_DEVICE_ID_ZEITNET_1225	0x0002
 
+#define PCI_VENDOR_ID_OMEGA		0x119b
+#define PCI_DEVICE_ID_OMEGA_82C092G	0x1221
+
+#define PCI_VENDOR_ID_NP		0x11bc
+#define PCI_DEVICE_ID_NP_PCI_FDDI	0x0001
+
 #define PCI_VENDOR_ID_SPECIALIX		0x11cb
 #define PCI_DEVICE_ID_SPECIALIX_XIO	0x4000
 #define PCI_DEVICE_ID_SPECIALIX_RIO	0x8000
 
+#define PCI_VENDOR_ID_IKON		0x11d5
+#define PCI_DEVICE_ID_IKON_10115	0x0115
+#define PCI_DEVICE_ID_IKON_10117	0x0117
+
+#define PCI_VENDOR_ID_ZORAN		0x11de
+#define PCI_DEVICE_ID_ZORAN_36057	0x6057
+#define PCI_DEVICE_ID_ZORAN_36120	0x6120
+
+#define PCI_VENDOR_ID_COMPEX		0x11f6
+#define PCI_DEVICE_ID_COMPEX_ENET100VG4	0x0112
+#define PCI_DEVICE_ID_COMPEX_RL2000	0x1401
+
 #define PCI_VENDOR_ID_RP               0x11fe
 #define PCI_DEVICE_ID_RP8OCTA          0x0001
 #define PCI_DEVICE_ID_RP8INTF          0x0002
@@ -519,19 +633,40 @@
 #define PCI_DEVICE_ID_CYCLOM_Z_Lo	0x0200
 #define PCI_DEVICE_ID_CYCLOM_Z_Hi	0x0201
 
+#define PCI_VENDOR_ID_3DFX		0x121a
+#define PCI_DEVICE_ID_3DFX_VOODOO	0x0001
+
+#define PCI_VENDOR_ID_SIGMADES		0x1236
+#define PCI_DEVICE_ID_SIGMADES_6425	0x6401
+
+#define PCI_VENDOR_ID_OPTIBASE		0x1255
+#define PCI_DEVICE_ID_OPTIBASE_FORGE	0x1110
+#define PCI_DEVICE_ID_OPTIBASE_FUSION	0x1210
+#define PCI_DEVICE_ID_OPTIBASE_VPLEX	0x2110
+#define PCI_DEVICE_ID_OPTIBASE_VPLEXCC	0x2120
+#define PCI_DEVICE_ID_OPTIBASE_VQUEST	0x2130
+
 #define PCI_VENDOR_ID_SYMPHONY		0x1c1c
 #define PCI_DEVICE_ID_SYMPHONY_101	0x0001
 
 #define PCI_VENDOR_ID_TEKRAM		0x1de1
 #define PCI_DEVICE_ID_TEKRAM_DC290	0xdc29
 
-#define PCI_VENDOR_ID_3DLABS		0x3D3D
+#define PCI_VENDOR_ID_3DLABS		0x3d3d
 #define PCI_DEVICE_ID_3DLABS_300SX	0x0001
+#define PCI_DEVICE_ID_3DLABS_500TX	0x0002
+#define PCI_DEVICE_ID_3DLABS_DELTA	0x0003
+#define PCI_DEVICE_ID_3DLABS_PERMEDIA	0x0004
 
 #define PCI_VENDOR_ID_AVANCE		0x4005
+#define PCI_DEVICE_ID_AVANCE_ALG2064	0x2064
 #define PCI_DEVICE_ID_AVANCE_2302	0x2302
 
+#define PCI_VENDOR_ID_NETVIN		0x4a14
+#define PCI_DEVICE_ID_NETVIN_NV5000SC	0x5000
+
 #define PCI_VENDOR_ID_S3		0x5333
+#define PCI_DEVICE_ID_S3_PLATO_PXS	0x0551
 #define PCI_DEVICE_ID_S3_ViRGE		0x5631
 #define PCI_DEVICE_ID_S3_TRIO		0x8811
 #define PCI_DEVICE_ID_S3_AURORA64VP	0x8812
@@ -544,6 +679,9 @@
 #define PCI_DEVICE_ID_S3_964_1		0x88d0
 #define PCI_DEVICE_ID_S3_964_2		0x88d1
 #define PCI_DEVICE_ID_S3_968		0x88f0
+#define PCI_DEVICE_ID_S3_TRIO64V2	0x8901
+#define PCI_DEVICE_ID_S3_PLATO_PXG	0x8902
+#define PCI_DEVICE_ID_S3_ViRGE_DXGX	0x8a01
 
 #define PCI_VENDOR_ID_INTEL		0x8086
 #define PCI_DEVICE_ID_INTEL_82375	0x0482
@@ -551,6 +689,8 @@
 #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_82092AA_0	0x1221
+#define PCI_DEVICE_ID_INTEL_82092AA_1	0x1222
 #define PCI_DEVICE_ID_INTEL_7116	0x1223
 #define PCI_DEVICE_ID_INTEL_82596	0x1226
 #define PCI_DEVICE_ID_INTEL_82865	0x1227
@@ -558,13 +698,24 @@
 #define PCI_DEVICE_ID_INTEL_82437	0x122d
 #define PCI_DEVICE_ID_INTEL_82371_0	0x122e
 #define PCI_DEVICE_ID_INTEL_82371_1	0x1230
+#define PCI_DEVICE_ID_INTEL_82371MX	0x1234
+#define PCI_DEVICE_ID_INTEL_82437MX	0x1235
 #define PCI_DEVICE_ID_INTEL_82441	0x1237
 #define PCI_DEVICE_ID_INTEL_82439	0x1250
 #define PCI_DEVICE_ID_INTEL_82371SB_0	0x7000
 #define PCI_DEVICE_ID_INTEL_82371SB_1	0x7010
 #define PCI_DEVICE_ID_INTEL_82371SB_2	0x7020
 #define PCI_DEVICE_ID_INTEL_82437VX	0x7030
+#define PCI_DEVICE_ID_INTEL_82439TX	0x7100
+#define PCI_DEVICE_ID_INTEL_82371AB_0	0x7110
+#define PCI_DEVICE_ID_INTEL_82371AB	0x7111
+#define PCI_DEVICE_ID_INTEL_82371AB_2	0x7112
+#define PCI_DEVICE_ID_INTEL_82371AB_3	0x7113
 #define PCI_DEVICE_ID_INTEL_P6		0x84c4
+#define PCI_DEVICE_ID_INTEL_82450GX	0x84c5
+
+#define PCI_VENDOR_ID_KTI		0x8e2e
+#define PCI_DEVICE_ID_KTI_ET32P2	0x3000
 
 #define PCI_VENDOR_ID_ADAPTEC		0x9004
 #define PCI_DEVICE_ID_ADAPTEC_7850	0x5078
@@ -585,9 +736,10 @@
 #define PCI_VENDOR_ID_ATRONICS		0x907f
 #define PCI_DEVICE_ID_ATRONICS_2015	0x2015
 
-#define PCI_VENDOR_ID_HER		0xedd8
-#define PCI_DEVICE_ID_HER_STING		0xa091
-#define PCI_DEVICE_ID_HER_STINGARK	0xa099
+#define PCI_VENDOR_ID_ARK		0xedd8
+#define PCI_DEVICE_ID_ARK_STING		0xa091
+#define PCI_DEVICE_ID_ARK_STINGARK	0xa099
+#define PCI_DEVICE_ID_ARK_2000MT	0xa0a1
 
 /*
  * The PCI interface treats multi-function devices as independent
diff -u --recursive --new-file v2.0.30/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.0.30/linux/include/linux/proc_fs.h	Tue Apr  8 08:47:46 1997
+++ linux/include/linux/proc_fs.h	Sat Aug 23 10:54:52 1997
@@ -105,6 +105,7 @@
 	PROC_NET_STRIP_STATUS,
 	PROC_NET_STRIP_TRACE,
 	PROC_NET_IPAUTOFW,
+	PROC_NET_Z8530,
 	PROC_NET_LAST
 };
 
@@ -127,6 +128,7 @@
 	PROC_SCSI_QLOGICISP,
 	PROC_SCSI_SEAGATE,
 	PROC_SCSI_T128,
+	PROC_SCSI_DC390T,
 	PROC_SCSI_NCR53C7xx,
 	PROC_SCSI_NCR53C8XX,
 	PROC_SCSI_ULTRASTOR,
@@ -141,6 +143,8 @@
 	PROC_SCSI_A2091,
 	PROC_SCSI_GVP11,
 	PROC_SCSI_ATARI,
+	PROC_SCSI_GDTH,
+	PROC_SCSI_IDESCSI,
 	PROC_SCSI_SCSI_DEBUG,	
 	PROC_SCSI_NOT_PRESENT,
 	PROC_SCSI_FILE,                        /* I'm assuming here that we */
diff -u --recursive --new-file v2.0.30/linux/include/linux/route.h linux/include/linux/route.h
--- v2.0.30/linux/include/linux/route.h	Tue Oct  8 09:48:40 1996
+++ linux/include/linux/route.h	Tue Aug 12 15:01:27 1997
@@ -52,6 +52,7 @@
 #define RTF_WINDOW	0x0080		/* per route window clamping	  */
 #define RTF_IRTT	0x0100		/* Initial round trip time	  */
 #define RTF_REJECT	0x0200		/* Reject route			  */
+#define RTF_NOTCACHED	0x0400		/* this route isn't cached        */
 
 /*
  *	This structure is passed from the kernel to user space by netlink
diff -u --recursive --new-file v2.0.30/linux/include/linux/scc.h linux/include/linux/scc.h
--- v2.0.30/linux/include/linux/scc.h	Thu Apr 11 23:49:46 1996
+++ linux/include/linux/scc.h	Tue Aug 12 14:43:54 1997
@@ -1,8 +1,10 @@
-/* $Id: scc.h,v 1.15 1995/11/16 20:19:26 jreuter Exp jreuter $ */
+/* $Id: scc.h,v 1.29 1997/04/10 15:27:21 jreuter Exp jreuter $ */
 
 #ifndef	_SCC_H
 #define	_SCC_H
 
+#include <linux/if_ether.h>
+
 /* selection of hardware types */
 
 #define PA0HZP		0x00	/* hardware type for PA0HZP SCC card and compatible */
@@ -16,19 +18,38 @@
 
 #define SCC_PARANOIA_CHECK	/* tell the user if something is going wrong */
 
-/* ioctl() commands */
-
-#define TIOCSCCCFG	0x2200		/* set hardware parameters */
-#define TIOCSCCINI	0x2201		/* init driver */
-#define TIOCCHANINI	0x2202		/* init channel */
-
-#define TIOCCHANMEM	0x2210		/* adjust buffer pools */
-
-#define TIOCGKISS	0x2282		/* get kiss parameter */
-#define TIOCSKISS	0x2283		/* set kiss parameter */
-
-#define TIOCSCCSTAT	0x2284		/* get scc status */
+/* TTY ioctl() commands */
 
+#define TIOCSCCCFG	 _IOW('Z', 0, sizeof(struct scc_hw_config))	/* set hardware parameters */
+#define TIOCSCCINI	  _IO('Z', 1)					/* init driver */
+#define TIOCSCCCHANINI	 _IOW('Z', 2, sizeof(struct scc_modem))		/* init channel */
+#define TIOCSCCSMEM	 _IOW('Z', 3, sizeof(struct scc_mem_config))	/* adjust buffer pools */
+#define TIOCSCCGKISS	_IOWR('Z', 4, sizeof(struct scc_kiss_cmd))	/* get kiss parameter */
+#define TIOCSCCSKISS	 _IOW('Z', 5, sizeof(struct scc_kiss_cmd))	/* set kiss parameter */
+#define TIOCSCCGSTAT	 _IOR('Z', 6, sizeof(struct scc_stat))		/* get scc status */
+
+/* old TTY ioctl() commands */
+
+#define TIOCSCCCFG_OLD	0x2200
+#define TIOCSCCINI_OLD	0x2201
+#define TIOCCHANINI_OLD	0x2202
+#define TIOCCHANMEM_OLD 0x2210
+#define TIOCSKISS_OLD	0x2282
+#define TIOCGKISS_OLD	0x2283
+#define TIOCSCCSTAT_OLD 0x2284
+
+/* DEV ioctl() commands */
+
+enum SCC_IOCTL_CMD {
+	SIOCSCCRESERVED=SIOCDEVPRIVATE,
+	SIOCSCCCFG,
+	SIOCSCCINI,
+	SIOCSCCCHANINI,
+	SIOCSCCSMEM,
+	SIOCSCCGKISS,
+	SIOCSCCSKISS,
+	SIOCSCCGSTAT
+};
 
 /* magic number */
 
@@ -42,37 +63,60 @@
 
 /* KISS state machine */
 
-#define	KISS_IDLE	0
-#define KISS_DATA	1
-#define KISS_ESCAPE	2
-#define KISS_RXFRAME	3
+enum SCC_KISS_STATES {
+	KISS_IDLE,
+	KISS_DATA,
+	KISS_ESCAPE,
+	KISS_RXFRAME
+};
 
 /* Device parameter control (from WAMPES) */
 
-#define	PARAM_TXDELAY	1
-#define	PARAM_PERSIST	2
-#define	PARAM_SLOTTIME	3
-#define	PARAM_TXTAIL	4
-#define	PARAM_FULLDUP	5
-#define PARAM_SOFTDCD	6	/* was: PARAM_HW */
-#define PARAM_MUTE	7	/* ??? */
-#define PARAM_DTR       8
-#define PARAM_RTS	9
-#define PARAM_SPEED     10
-#define PARAM_ENDDELAY	11	/* ??? */
-#define PARAM_GROUP     12
-#define PARAM_IDLE      13
-#define PARAM_MIN       14
-#define	PARAM_MAXKEY	15
-#define PARAM_WAIT      16
-#define PARAM_MAXDEFER	17
-#define PARAM_TX        18
-#define PARAM_SLIP	19
-#define PARAM_RETURN	255	/* reset kiss mode */
+enum SCC_KISS_PARAMS {
+	PARAM_TXDELAY=1,
+	PARAM_PERSIST,
+	PARAM_SLOTTIME,
+	PARAM_TXTAIL,
+	PARAM_FULLDUP,
+	PARAM_SOFTDCD,		/* was: PARAM_HW */
+	PARAM_MUTE,		/* ??? */
+	PARAM_DTR,
+	PARAM_RTS,
+	PARAM_SPEED,
+	PARAM_ENDDELAY,		/* ??? */
+	PARAM_GROUP,
+	PARAM_IDLE,
+	PARAM_MIN,
+	PARAM_MAXKEY,
+	PARAM_WAIT,
+	PARAM_MAXDEFER,
+	PARAM_TX,
+	PARAM_HWEVENT=31,
+	PARAM_RETURN=255	/* reset kiss mode */
+};
+
+/* fulldup parameter */
+
+enum SCC_KISS_DUPLEX_MODES {
+	KISS_DUPLEX_HALF,	/* normal CSMA operation */
+	KISS_DUPLEX_FULL,	/* fullduplex, key down trx after transmission */
+	KISS_DUPLEX_LINK,	/* fullduplex, key down trx after 'idletime' sec */
+	KISS_DUPLEX_OPTIMA,	/* fullduplex, let the protocol layer control the hw */
+};
+
+/* misc. parameters */
 
 #define TIMER_OFF	65535U	/* to switch off timers */
 #define NO_SUCH_PARAM	65534U	/* param not implemented */
 
+/* HWEVENT parameter */
+
+enum SCC_HWEV_PARAMETERS {
+	HWEV_DCD_ON,
+	HWEV_DCD_OFF,
+	HWEV_ALL_SENT
+};
+
 /* channel grouping */
 
 #define RXGROUP		0x100	/* if set, only tx when all channels clear */
@@ -80,20 +124,26 @@
 
 /* Tx/Rx clock sources */
 
-#define CLK_DPLL	0	/* normal halfduplex operation */
-#define CLK_EXTERNAL	1	/* external clocking (G3RUH/DF9IC modems) */
-#define CLK_DIVIDER	2	/* Rx = DPLL, Tx = divider (fullduplex with */
-				/* modems without clock regeneration */
+enum SCC_CLK_SOURCES {
+	CLK_DPLL,	/* normal halfduplex operation */
+	CLK_EXTERNAL,	/* external clocking (G3RUH/DF9IC modems) */
+	CLK_DIVIDER	/* Rx = DPLL, Tx = divider (fullduplex with */
+			/* modems without clock regeneration */
+};
 
 /* Tx state */
 
-#define TXS_IDLE	0	/* Transmitter off, no data pending */
-#define TXS_BUSY	1	/* waiting for permission to send / tailtime */
-#define TXS_ACTIVE	2	/* Transmitter on, sending data */
-#define TXS_NEWFRAME	3	/* reset CRC and send (next) frame */
+enum SCC_TX_STATES {
+	TXS_IDLE,	/* Transmitter off, no data pending */
+	TXS_BUSY,	/* waiting for permission to send / tailtime */
+	TXS_ACTIVE,	/* Transmitter on, sending data */
+	TXS_NEWFRAME,	/* reset CRC and send (next) frame */
+	TXS_IDLE2,	/* Transmitter on, no data pending */
+	TXS_WAIT,	/* Waiting for Mintime to expire */
+	TXS_TIMEOUT	/* We had a transmission timeout */
+};
 
-#define TX_ON		1	/* command for scc_key_trx() */
-#define TX_OFF		0	/* dto */
+enum SCC_TX_KEY {TX_OFF, TX_ON}; /* command for scc_key_trx() */
 
 /* Buffer management */
 
@@ -115,13 +165,7 @@
 #define Outb(port, val)	outb(val, port)
 #endif
 
-/* some nasty macros (esp. Expired) */
-
-#define TIMER_STOPPED 65535U
-#define Running(k) (scc->k != TIMER_STOPPED)
-#define Expired(k) (scc->k != TIMER_STOPPED) && (!(scc->k) || (--(scc->k) == 0))
-#define Stop_Timer(k) scc->k = TIMER_STOPPED
-
+#define TIMER_OFF 65535U
 
 /* Basic message buffer structure */
 
@@ -140,17 +184,18 @@
 	unsigned char txdelay;		/* Transmit Delay 10 ms/cnt */
 	unsigned char persist;		/* Persistence (0-255) as a % */
 	unsigned char slottime;		/* Delay to wait on persistence hit */
-	unsigned char tailtime;		/* Delay after XMTR OFF */
+	unsigned char tailtime;		/* Delay after last byte written */
 	unsigned char fulldup;		/* Full Duplex mode 0=CSMA 1=DUP 2=ALWAYS KEYED */
 	unsigned char waittime;		/* Waittime before any transmit attempt */
 	unsigned int  maxkeyup;		/* Maximum time to transmit (seconds) */
-	unsigned char mintime;		/* Minimal offtime after MAXKEYUP timeout */
+	unsigned char mintime;		/* Minimal offtime after MAXKEYUP timeout (seconds) */
 	unsigned int  idletime;		/* Maximum idle time in ALWAYS KEYED mode (seconds) */
 	unsigned int  maxdefer;		/* Timer for CSMA channel busy limit */
 	unsigned char tx_inhibit;	/* Transmit is not allowed when set */	
-	unsigned char group;		/* group ID for AX.25 TX interlocking */
-	unsigned char not_slip;		/* set to zero: use SLIP instead of KISS */
-	unsigned char softdcd;		/* use DPLL instead of DCD pin for carrier detect */
+	unsigned char group;		/* Group ID for AX.25 TX interlocking */
+	unsigned char mode;		/* 'normal' or 'hwctrl' mode (unused) */
+
+	unsigned char softdcd;		/* Use DPLL instead of DCD pin for carrier detect */
 };
 
 
@@ -182,6 +227,8 @@
 	unsigned int rxbuffers;	/* allocated rx_buffers */
 	unsigned int txbuffers;	/* allocated tx_buffers */
 	unsigned int bufsize;	/* used buffersize */
+
+	unsigned char is_netdev;/* If set: act as network instead of character device */
 };
 
 
@@ -191,13 +238,11 @@
 	char nrz;		/* NRZ instead of NRZI */	
 };
 
-struct ioctl_command {
+struct scc_kiss_cmd {
 	int  	 command;	/* one of the KISS-Commands defined above */
 	unsigned param;		/* KISS-Param */
 };
 
-/* currently unused */
-
 struct scc_hw_config {
 	io_port data_a;		/* data port channel A */
 	io_port ctrl_a;		/* control port channel A */
@@ -214,14 +259,15 @@
 	char escc;		/* use ext. features of a 8580/85180/85280 */
 };
 
+/* (#) only one INTACK latch allowed. */
+
+
 struct scc_mem_config {
 	unsigned int rxbuffers;
 	unsigned int txbuffers;
 	unsigned int bufsize;
 };
 
-/* (#) only one INTACK latch allowed. */
-	
 
 /* SCC channel structure */
 
@@ -232,6 +278,10 @@
 	struct tty_struct *tty; /* link to tty control structure */
 	char tty_opened;	/* No. of open() calls... */
 	char throttled;		/* driver is throttled  */
+	
+	struct device *dev;	/* link to device control structure */
+	struct enet_statistics dev_stat;
+				/* device statistics */
 		
 	char brand;		/* manufacturer of the board */
 	long clock;		/* used clock */
@@ -239,6 +289,7 @@
 	io_port ctrl;		/* I/O address of CONTROL register */
 	io_port	data;		/* I/O address of DATA register */
 	io_port special;	/* I/O address of special function port */
+	int irq;		/* Number of Interrupt */
 	
 	char option;
 	char enhanced;		/* Enhanced SCC support */
@@ -250,6 +301,7 @@
         struct scc_stat stat;	/* statistical information */
         struct scc_modem modem; /* modem information */
         
+        char mempool;		/* pool empty or allocated? */
         struct mbuf *rx_buffer_pool; /* free buffers for rx/tx frames are */
         struct mbuf *tx_buffer_pool; /* linked in these ring chains */
         
@@ -264,18 +316,9 @@
 	/* Timer */
 	
 	struct timer_list tx_t;	/* tx timer for this channel */
+	struct timer_list tx_wdog;
+				/* tx watchdogs */
 	struct timer_list rx_t; /* rx timer */
-
-	/* rx timer counters */
-	
-	unsigned int t_dwait;	/* wait time (DWAIT) */
-	unsigned int t_slot;	/* channel sample frequency */
-	unsigned int t_txdel;	/* TX delay */
-	unsigned int t_tail;	/* tail time */
-	unsigned int t_maxk;	/* max. key up */
-	unsigned int t_min;	/* minimal key up */
-	unsigned int t_idle;	/* */
-	unsigned int t_mbusy;	/* time until defer if channel busy */	 	
 };
 
 
@@ -564,8 +607,4 @@
 
 /* global functions */
 
-#ifdef PREV_LINUX_1_3_33
-extern long scc_init(long kmem_start);
-#else
 extern int scc_init(void);
-#endif
diff -u --recursive --new-file v2.0.30/linux/include/linux/sched.h linux/include/linux/sched.h
--- v2.0.30/linux/include/linux/sched.h	Fri Mar 28 16:08:17 1997
+++ linux/include/linux/sched.h	Sat Aug 23 10:54:52 1997
@@ -342,7 +342,7 @@
 extern void wake_up_interruptible(struct wait_queue ** p);
 extern void wake_up_process(struct task_struct * tsk);
 
-extern void notify_parent(struct task_struct * tsk);
+extern void notify_parent(struct task_struct * tsk, int signal);
 extern void force_sig(unsigned long sig,struct task_struct * p);
 extern int send_sig(unsigned long sig,struct task_struct * p,int priv);
 extern int in_group_p(gid_t grp);
diff -u --recursive --new-file v2.0.30/linux/include/linux/sysctl.h linux/include/linux/sysctl.h
--- v2.0.30/linux/include/linux/sysctl.h	Tue Apr  8 08:47:46 1997
+++ linux/include/linux/sysctl.h	Tue Aug 12 14:06:35 1997
@@ -82,6 +82,7 @@
 #define NET_BRIDGE	10
 
 /* /proc/sys/net/core */
+#define NET_CORE_NET_ALIAS_MAX 1
 
 /* /proc/sys/net/ethernet */
 
@@ -98,6 +99,7 @@
 #define NET_IPV4_ARP_CONFIRM_INTERVAL   6
 #define NET_IPV4_ARP_CONFIRM_TIMEOUT	7
 #define NET_IPV4_FORWARD                8
+#define NET_IPV4_DYNADDR		9
 
 /* /proc/sys/net/ipx */
 
diff -u --recursive --new-file v2.0.30/linux/include/linux/tty.h linux/include/linux/tty.h
--- v2.0.30/linux/include/linux/tty.h	Fri Mar 28 16:08:17 1997
+++ linux/include/linux/tty.h	Sat Aug 23 10:54:51 1997
@@ -292,6 +292,7 @@
 extern int stl_init(void);
 extern int stli_init(void);
 extern int riscom8_init(void);
+extern int specialix_init(void);
 extern int baycom_init(void);
 
 extern int tty_paranoia_check(struct tty_struct *tty, kdev_t device,
diff -u --recursive --new-file v2.0.30/linux/include/linux/wireless.h linux/include/linux/wireless.h
--- v2.0.30/linux/include/linux/wireless.h	Thu Mar  6 10:03:51 1997
+++ linux/include/linux/wireless.h	Tue Aug 12 15:01:27 1997
@@ -63,20 +63,25 @@
  * (there is some stuff that will be added in the future...)
  * I just plan to increment with each new version.
  */
-#define WIRELESS_EXT	4
+#define WIRELESS_EXT	5
 
 /*
  * Changes :
  *
  * V2 to V3
  * --------
- *	Alan Cox start some imcompatibles changes. I've integrated a bit more.
+ *	Alan Cox start some incompatibles changes. I've integrated a bit more.
  *	- Encryption renamed to Encode to avoid US regulation problems
  *	- Frequency changed from float to struct to avoid problems on old 386
  *
  * V3 to V4
  * --------
  *	- Add sensitivity
+ *
+ * V4 to V5
+ * --------
+ *	- Missing encoding definitions in range
+ *	- Access points stuff
  */
 
 /* -------------------------- IOCTL LIST -------------------------- */
@@ -103,11 +108,16 @@
 #define SIOCSIWSPY	0x8B10		/* set spy addresses */
 #define SIOCGIWSPY	0x8B11		/* get spy info (quality of link) */
 
+/* Access Point manipulation */
+#define SIOCSIWAP	0x8B14		/* set access point hardware addresses */
+#define SIOCGIWAP	0x8B15		/* get access point hardware addresses */
+#define SIOCGIWAPLIST	0x8B17		/* get list of access point in range */
+
 /* ------------------------- IOCTL STUFF ------------------------- */
 
 /* The first and the last (range) */
 #define SIOCIWFIRST	0x8B00
-#define SIOCIWLAST	0x8B13
+#define SIOCIWLAST	0x8B17
 
 /* Even : get (world access), odd : set (root access) */
 #define IW_IS_SET(cmd)	(!((cmd) & 0x1))
@@ -185,6 +195,17 @@
 	__u32		misc;		/* Others cases */
 };
 
+/*
+ *	Encoding information (setting and so on)
+ *	Encoding might be hardware encryption, scrambing or others
+ */
+struct	iw_encoding
+{
+  __u8	method;			/* Algorithm number / key used */
+  __u64	code;			/* Data/key used for algorithm */
+};
+
+
 /* ------------------------ WIRELESS STATS ------------------------ */
 /*
  * Wireless statistics (used for /proc/net/wireless)
@@ -234,13 +255,11 @@
 					 * 0-1000 = channel
 					 * > 1000 = frequency in Hz */
 
-		struct		/* Encoding stuff */
-		{
-			__u8	method;		/* Algorithm number / off */
-			__u64	code;		/* Data used for algorithm */
-		}	encoding;
+		struct iw_encoding	encoding;	/* Encoding stuff */
 
-		__u32	sensitivity;	/* signal level threshold */
+		__u32	sensitivity;		/* signal level threshold */
+
+		struct sockaddr	ap_addr;	/* Access point address */
 
 		struct		/* For all data bigger than 16 octets */
 		{
@@ -275,16 +294,16 @@
 	__u16		num_channels;	/* Number of channels [0; num - 1] */
 	__u8		num_frequency;	/* Number of entry in the list */
 	struct iw_freq	freq[IW_MAX_FREQUENCIES];	/* list */
-
 	/* Note : this frequency list doesn't need to fit channel numbers */
 
-	/* Encoder stuff */
-
 	/* signal level threshold range */
 	__u32	sensitivity;
 
 	/* Quality of link & SNR stuff */
 	struct iw_quality	max_qual;	/* Quality of the link */
+
+	/* Encoder stuff */
+	struct iw_encoding	max_encoding;	/* Encoding max range */
 };
 
 /*
diff -u --recursive --new-file v2.0.30/linux/include/net/ip.h linux/include/net/ip.h
--- v2.0.30/linux/include/net/ip.h	Tue Apr  8 08:47:46 1997
+++ linux/include/net/ip.h	Sat Aug 23 10:56:23 1997
@@ -122,6 +122,9 @@
 
 extern struct ip_mib	ip_statistics;
 
+extern int sysctl_ip_dynaddr;
+int ip_rewrite_addrs(struct sock *sk, struct sk_buff *skb, struct device *dev);
+
 /*
  *	Functions provided by ip_fragment.o
  */
diff -u --recursive --new-file v2.0.30/linux/include/net/ip_masq.h linux/include/net/ip_masq.h
--- v2.0.30/linux/include/net/ip_masq.h	Tue Apr  8 08:47:46 1997
+++ linux/include/net/ip_masq.h	Sat Aug 23 10:56:33 1997
@@ -54,6 +54,8 @@
 #define IP_MASQ_F_CONTROL		0x100 	/* this is a control channel */
 #define IP_MASQ_F_NO_SPORT    		0x200	/* no sport set yet */
 #define IP_MASQ_F_FTP_PASV	    	0x400	/* ftp PASV command just issued */
+#define IP_MASQ_F_NO_REPLY		0x800 	/* no reply yet from outside */
+#define IP_MASQ_F_AFW_PORT	       0x1000
 
 #ifdef __KERNEL__
 
diff -u --recursive --new-file v2.0.30/linux/include/net/ipx.h linux/include/net/ipx.h
--- v2.0.30/linux/include/net/ipx.h	Fri Mar 28 16:09:02 1997
+++ linux/include/net/ipx.h	Sat Aug 23 10:55:26 1997
@@ -16,6 +16,8 @@
 #include <net/datalink.h>
 #include <linux/ipx.h>
 
+/* #define CONFIG_IPX_INTERN 1 */
+
 typedef struct
 {
 	unsigned long net;
diff -u --recursive --new-file v2.0.30/linux/include/net/route.h linux/include/net/route.h
--- v2.0.30/linux/include/net/route.h	Fri Mar 28 16:10:57 1997
+++ linux/include/net/route.h	Sat Aug 23 10:56:23 1997
@@ -13,6 +13,7 @@
  *		Alan Cox	:	Reformatted. Added ip_rt_local()
  *		Alan Cox	:	Support for TCP parameters.
  *		Alexey Kuznetsov:	Major changes for new routing code.
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  *
  *	FIXME:
  *		Make atomic ops more generic and hide them in asm/...
@@ -83,7 +84,7 @@
 extern void		ip_rt_flush(struct device *dev);
 extern void		ip_rt_update(int event, struct device *dev);
 extern void		ip_rt_redirect(__u32 src, __u32 dst, __u32 gw, struct device *dev);
-extern struct rtable	*ip_rt_slow_route(__u32 daddr, int local);
+extern struct rtable	*ip_rt_slow_route(__u32 daddr, int local, struct device *dev);
 extern int		rt_get_info(char * buffer, char **start, off_t offset, int length, int dummy);
 extern int		rt_cache_get_info(char *buffer, char **start, off_t offset, int length, int dummy);
 extern int		ip_rt_ioctl(unsigned int cmd, void *arg);
@@ -131,9 +132,9 @@
 #endif
 
 #ifdef CONFIG_KERNELD
-extern struct rtable * ip_rt_route(__u32 daddr, int local);
+extern struct rtable * ip_rt_route(__u32 daddr, int local, struct device *dev);
 #else
-extern __inline__ struct rtable * ip_rt_route(__u32 daddr, int local)
+extern __inline__ struct rtable * ip_rt_route(__u32 daddr, int local, struct device *dev)
 #ifndef MODULE
 {
 	struct rtable * rth;
@@ -142,7 +143,8 @@
 
 	for (rth=ip_rt_hash_table[ip_rt_hash_code(daddr)^local]; rth; rth=rth->rt_next)
 	{
-		if (rth->rt_dst == daddr)
+		/* If an interface is specified, make sure this route points to it. */
+		if ( (rth->rt_dst == daddr) && ((dev==NULL) || (dev==rth->rt_dev)) )
 		{
 			rth->rt_lastuse = jiffies;
 			atomic_inc(&rth->rt_use);
@@ -151,23 +153,23 @@
 			return rth;
 		}
 	}
-	return ip_rt_slow_route (daddr, local);
+	return ip_rt_slow_route (daddr, local, dev);
 }
 #else
 ;
 #endif
 #endif
 
-extern __inline__ struct rtable * ip_check_route(struct rtable ** rp,
-						       __u32 daddr, int local)
+extern __inline__ struct rtable * ip_check_route(struct rtable ** rp, __u32 daddr, 
+						 int local, struct device *dev)
 {
 	struct rtable * rt = *rp;
 
-	if (!rt || rt->rt_dst != daddr || !(rt->rt_flags&RTF_UP)
+	if (!rt || rt->rt_dst != daddr || !(rt->rt_flags&RTF_UP) || (dev!=NULL)
 	    || ((local==1)^((rt->rt_flags&RTF_LOCAL) != 0)))
 	{
 		ip_rt_put(rt);
-		rt = ip_rt_route(daddr, local);
+		rt = ip_rt_route(daddr, local, dev);
 		*rp = rt;
 	}
 	return rt;
diff -u --recursive --new-file v2.0.30/linux/include/net/sock.h linux/include/net/sock.h
--- v2.0.30/linux/include/net/sock.h	Tue Apr  8 08:47:47 1997
+++ linux/include/net/sock.h	Sat Aug 23 10:55:27 1997
@@ -23,6 +23,7 @@
  *	Pauline Middelink	:	identd support
  *		Alan Cox	:	Eliminate low level recv/recvfrom
  *		David S. Miller	:	New socket lookup architecture for ISS.
+ *              Elliot Poger    :       New field for SO_BINDTODEVICE option.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -109,7 +110,11 @@
  * know the connection this socket belongs to. 
  */
 	struct ncp_server       *ncp_server;
-	
+/* 
+ * To handle special ncp connection-handling sockets for mars_nwe,
+ * the connection number must be stored in the socket.
+ */
+	unsigned short		ipx_ncp_conn;
 };
 #endif
 
@@ -192,6 +197,7 @@
 				broadcast,
 				nonagle,
 				bsdism;
+	struct device           * bound_device;
 	unsigned long	        lingertime;
 	int			proc;
 
diff -u --recursive --new-file v2.0.30/linux/include/net/tcp.h linux/include/net/tcp.h
--- v2.0.30/linux/include/net/tcp.h	Tue Apr  8 08:47:47 1997
+++ linux/include/net/tcp.h	Sat Aug 23 10:56:24 1997
@@ -41,7 +41,23 @@
 /* These are AF independant. */
 static __inline__ int tcp_bhashfn(__u16 lport)
 {
-	return (lport ^ (lport >> 7)) & (TCP_BHTABLE_SIZE - 1);
+	return (lport ^ (lport >> 7)) & (TCP_BHTABLE_SIZE-1);
+}
+
+/* Find the next port that hashes h that is larger than lport.
+ * If you change the hash, change this function to match, or you will
+ * break TCP port selection. This function must also NOT wrap around
+ * when the next number exceeds the largest possible port (2^16-1).
+ */
+static __inline__ int tcp_bhashnext(__u16 lport, __u16 h)
+{
+        __u32 s;	/* don't change this to a smaller type! */
+
+        s = (lport ^ (h ^ tcp_bhashfn(lport)));
+        if (s > lport)
+                return s;
+        s = lport + TCP_BHTABLE_SIZE;
+        return (s ^ (h ^ tcp_bhashfn(s)));
 }
 
 static __inline__ int tcp_sk_bhashfn(struct sock *sk)
@@ -116,7 +132,7 @@
 					   stacks do signed 16bit maths! */
 #define MIN_WINDOW	2048
 #define MAX_ACK_BACKLOG	2
-#define MAX_DUP_ACKS	2
+#define MAX_DUP_ACKS	3
 #define MIN_WRITE_SPACE	2048
 #define TCP_WINDOW_DIFF	2048
 
@@ -221,6 +237,8 @@
 
 extern int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg);
 
+extern void tcp_v4_unhash(struct sock *sk);
+
 extern void tcp_read_wakeup(struct sock *);
 extern void tcp_write_xmit(struct sock *);
 extern void tcp_time_wait(struct sock *);
@@ -342,6 +360,8 @@
 		break;
 
 	case TCP_CLOSE:
+		/* Preserve the invariant */
+		tcp_v4_unhash(sk);
 		/* Should be about 2 rtt's */
 		reset_timer(sk, TIME_DONE, min(sk->rtt * 2, TCP_DONE_TIME));
 		/* fall through */
diff -u --recursive --new-file v2.0.30/linux/include/scsi/scsi_ioctl.h linux/include/scsi/scsi_ioctl.h
--- v2.0.30/linux/include/scsi/scsi_ioctl.h	Wed May  8 21:38:41 1996
+++ linux/include/scsi/scsi_ioctl.h	Mon Aug 11 13:37:24 1997
@@ -5,6 +5,8 @@
 #define SCSI_IOCTL_TEST_UNIT_READY 2
 #define SCSI_IOCTL_BENCHMARK_COMMAND 3
 #define SCSI_IOCTL_SYNC 4			/* Request synchronous parameters */
+#define SCSI_IOCTL_START_UNIT 5
+#define SCSI_IOCTL_STOP_UNIT 6
 /* The door lock/unlock constants are compatible with Sun constants for
    the cdrom */
 #define SCSI_IOCTL_DOORLOCK 0x5380		/* lock the eject mechanism */
diff -u --recursive --new-file v2.0.30/linux/init/main.c linux/init/main.c
--- v2.0.30/linux/init/main.c	Sat Nov 23 05:28:23 1996
+++ linux/init/main.c	Thu Aug 14 10:30:08 1997
@@ -59,6 +59,7 @@
 static int init(void *);
 extern int bdflush(void *);
 extern int kswapd(void *);
+extern void kswapd_setup(void);
 
 extern void init_IRQ(void);
 extern void init_modules(void);
@@ -88,9 +89,11 @@
 extern void generic_NCR53C400_setup(char *str, int *intr);
 extern void aha152x_setup(char *str, int *ints);
 extern void aha1542_setup(char *str, int *ints);
+extern void gdth_setup(char *str, int *ints);
 extern void aic7xxx_setup(char *str, int *ints);
 extern void AM53C974_setup(char *str, int *ints);
 extern void BusLogic_Setup(char *str, int *ints);
+extern void ncr53c8xx_setup(char *str, int *ints);
 extern void eata2x_setup(char *str, int *ints);
 extern void u14_34f_setup(char *str, int *ints);
 extern void fdomain_setup(char *str, int *ints);
@@ -146,8 +149,11 @@
 #ifdef CONFIG_ISDN_DRV_ICN
 extern void icn_setup(char *str, int *ints);
 #endif
-#ifdef CONFIG_ISDN_DRV_TELES
-extern void teles_setup(char *str, int *ints);
+#ifdef CONFIG_ISDN_DRV_HISAX
+extern void HiSax_setup(char *str, int *ints);
+#endif
+#ifdef CONFIG_ISDN_DRV_PCBIT
+extern void pcbit_setup(char *str, int *ints);
 #endif
 
 #ifdef CONFIG_ATARIMOUSE
@@ -165,12 +171,12 @@
 #ifdef CONFIG_DIGI
 extern void pcxx_setup(char *str, int *ints);
 #endif
-#ifdef CONFIG_ISDN_DRV_PCBIT
-extern void pcbit_setup(char *str, int *ints);
-#endif
 #ifdef CONFIG_RISCOM8
 extern void riscom8_setup(char *str, int *ints);
 #endif
+#ifdef CONFIG_SPECIALIX
+extern void specialix_setup(char *str, int *ints);
+#endif
 #ifdef CONFIG_BAYCOM
 extern void baycom_setup(char *str, int *ints);
 #endif
@@ -317,12 +323,18 @@
 #ifdef CONFIG_SCSI_AHA1542
 	{ "aha1542=", aha1542_setup},
 #endif
+#ifdef CONFIG_SCSI_GDTH
+	{ "gdth=", gdth_setup},
+#endif
 #ifdef CONFIG_SCSI_AIC7XXX
 	{ "aic7xxx=", aic7xxx_setup},
 #endif
 #ifdef CONFIG_SCSI_BUSLOGIC
 	{ "BusLogic=", BusLogic_Setup},
 #endif
+#ifdef CONFIG_SCSI_NCR53C8XX
+	{ "ncr53c8xx=", ncr53c8xx_setup},
+#endif
 #ifdef CONFIG_SCSI_EATA
 	{ "eata=", eata2x_setup},
 #endif
@@ -392,8 +404,9 @@
 #ifdef CONFIG_ISDN_DRV_ICN
 	{ "icn=", icn_setup },
 #endif
-#ifdef CONFIG_ISDN_DRV_TELES
-	{ "teles=", teles_setup },
+#ifdef CONFIG_ISDN_DRV_HISAX
+       { "hisax=", HiSax_setup },
+       { "HiSax=", HiSax_setup },
 #endif
 #ifdef CONFIG_ISDN_DRV_PCBIT
 	{ "pcbit=", pcbit_setup },
@@ -420,6 +433,9 @@
 #ifdef CONFIG_RISCOM8
 	{ "riscom8=", riscom8_setup },
 #endif
+#ifdef CONFIG_SPECIALIX
+	{ "specialix=", specialix_setup },
+#endif
 #ifdef CONFIG_BAYCOM
 	{ "baycom=", baycom_setup },
 #endif
@@ -480,6 +496,10 @@
    still work even if initially too large, it will just take slightly longer */
 unsigned long loops_per_sec = (1<<12);
 
+#if defined(__SMP__) && defined(__i386__)
+unsigned long smp_loops_per_tick = 1000000;
+#endif
+
 /* This is the number of bits of precision for the loops_per_second.  Each
    bit takes on average 1.5/HZ seconds.  This (like the original) is a little
    better than 1% */
@@ -527,6 +547,10 @@
 	printk("ok - %lu.%02lu BogoMIPS\n",
 		(loops_per_sec+2500)/500000,
 		((loops_per_sec+2500)/5000) % 100);
+
+#if defined(__SMP__) && defined(__i386__)
+	smp_loops_per_tick = loops_per_sec / 400;
+#endif
 }
 
 static void parse_root_dev(char * line)
@@ -920,6 +944,7 @@
 	/* Launch bdflush from here, instead of the old syscall way. */
 	kernel_thread(bdflush, NULL, 0);
 	/* Start the background pageout daemon. */
+	kswapd_setup();
 	kernel_thread(kswapd, NULL, 0);
 
 #ifdef CONFIG_BLK_DEV_INITRD
diff -u --recursive --new-file v2.0.30/linux/ipc/shm.c linux/ipc/shm.c
--- v2.0.30/linux/ipc/shm.c	Fri Nov 22 06:25:18 1996
+++ linux/ipc/shm.c	Mon Aug  4 15:25:11 1997
@@ -13,6 +13,7 @@
 #include <linux/stat.h>
 #include <linux/malloc.h>
 #include <linux/swap.h>
+#include <linux/swapctl.h>
 
 #include <asm/segment.h>
 #include <asm/pgtable.h>
@@ -672,6 +673,11 @@
 			shm_swp--;
 		}
 		shm_rss++;
+
+		/* Give the physical reallocated page a bigger start */
+		if (shm_rss < (MAP_NR(high_memory) >> 3))
+			mem_map[MAP_NR(page)].age = (PAGE_INITIAL_AGE + PAGE_ADVANCE);
+
 		pte = pte_mkdirty(mk_pte(page, PAGE_SHARED));
 		shp->shm_pages[idx] = pte_val(pte);
 	} else
diff -u --recursive --new-file v2.0.30/linux/kernel/exit.c linux/kernel/exit.c
--- v2.0.30/linux/kernel/exit.c	Tue Oct 29 17:42:42 1996
+++ linux/kernel/exit.c	Fri Aug 29 10:38:46 1997
@@ -100,11 +100,9 @@
 	return 0;
 }
 
-void notify_parent(struct task_struct * tsk)
+void notify_parent(struct task_struct * tsk, int signal)
 {
-	if (tsk->p_pptr == task[smp_num_cpus])		/* Init */
-		tsk->exit_signal = SIGCHLD;
-	send_sig(tsk->exit_signal, tsk->p_pptr, 1);
+	send_sig(signal, tsk->p_pptr, 1);
 	wake_up_interruptible(&tsk->p_pptr->wait_chldexit);
 }
 
@@ -385,11 +383,10 @@
 	struct task_struct * p;
 
 	for_each_task(p) {
-		if (p->p_opptr == father)
-			if (task[smp_num_cpus])	/* init */
-				p->p_opptr = task[smp_num_cpus];
-			else
-				p->p_opptr = task[0];
+		if (p->p_opptr == father) {
+			p->exit_signal = SIGCHLD;
+			p->p_opptr = task[smp_num_cpus] ? : task[0];	/* init */
+		}
 	}
 }
 
@@ -519,7 +516,7 @@
 		kill_pg(current->pgrp,SIGCONT,1);
 	}
 	/* Let father know we died */
-	notify_parent(current);
+	notify_parent(current, current->exit_signal);
 	
 	/*
 	 * This loop does two things:
@@ -533,15 +530,15 @@
 		current->p_cptr = p->p_osptr;
 		p->p_ysptr = NULL;
 		p->flags &= ~(PF_PTRACED|PF_TRACESYS);
-		if (task[smp_num_cpus] && task[smp_num_cpus] != current) /* init */
-			p->p_pptr = task[smp_num_cpus];
-		else
-			p->p_pptr = task[0];
+
+		p->p_pptr = p->p_opptr;
 		p->p_osptr = p->p_pptr->p_cptr;
-		p->p_osptr->p_ysptr = p;
+		if (p->p_osptr)
+			p->p_osptr->p_ysptr = p;
 		p->p_pptr->p_cptr = p;
 		if (p->state == TASK_ZOMBIE)
-			notify_parent(p);
+			notify_parent(p, p->exit_signal);
+
 		/*
 		 * process group orphan check
 		 * Case ii: Our child is in a different pgrp 
@@ -672,7 +669,7 @@
 					REMOVE_LINKS(p);
 					p->p_pptr = p->p_opptr;
 					SET_LINKS(p);
-					notify_parent(p);
+					notify_parent(p, p->exit_signal);
 				} else
 					release(p);
 #ifdef DEBUG_PROC_TREE
diff -u --recursive --new-file v2.0.30/linux/kernel/fork.c linux/kernel/fork.c
--- v2.0.30/linux/kernel/fork.c	Thu Apr  3 20:02:28 1997
+++ linux/kernel/fork.c	Tue Aug 12 14:24:08 1997
@@ -122,14 +122,17 @@
 		*mm = *current->mm;
 		mm->count = 1;
 		mm->def_flags = 0;
+		mm->mmap_sem = MUTEX;
 		tsk->mm = mm;
 		tsk->min_flt = tsk->maj_flt = 0;
 		tsk->cmin_flt = tsk->cmaj_flt = 0;
 		tsk->nswap = tsk->cnswap = 0;
 		if (new_page_tables(tsk))
-			return -1;
+			goto free_mm;
 		if (dup_mmap(mm)) {
 			free_page_tables(mm);
+free_mm:
+			kfree(mm);
 			return -1;
 		}
 		return 0;
diff -u --recursive --new-file v2.0.30/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v2.0.30/linux/kernel/ksyms.c	Tue Mar 11 14:37:16 1997
+++ linux/kernel/ksyms.c	Fri Sep  5 20:43:59 1997
@@ -46,6 +46,7 @@
 #include <linux/genhd.h>
 #include <linux/swap.h>
 #include <linux/ctype.h>
+#include <linux/file.h>
 
 extern unsigned char aux_device_present, kbd_read_mask;
 
@@ -170,6 +171,7 @@
 	X(generic_file_read),
 	X(generic_file_mmap),
 	X(generic_readpage),
+	X(__fput),
 
 	/* device registration */
 	X(register_chrdev),
diff -u --recursive --new-file v2.0.30/linux/kernel/panic.c linux/kernel/panic.c
--- v2.0.30/linux/kernel/panic.c	Thu Apr 11 23:49:47 1996
+++ linux/kernel/panic.c	Mon Aug  4 17:15:07 1997
@@ -17,6 +17,7 @@
 asmlinkage void sys_sync(void);	/* it's really int */
 extern void hard_reset_now(void);
 extern void do_unblank_screen(void);
+extern void gdth_halt(void);
 extern int C_A_D;
 
 int panic_timeout = 0;
@@ -53,6 +54,9 @@
 		printk(KERN_EMERG "Rebooting in %d seconds..",panic_timeout);
 		for(i = 0; i < (panic_timeout*1000); i++)
 			udelay(1000);
+#ifdef CONFIG_SCSI_GDTH
+		gdth_halt();
+#endif
 		hard_reset_now();
 	}
 	for(;;);
diff -u --recursive --new-file v2.0.30/linux/kernel/sched.c linux/kernel/sched.c
--- v2.0.30/linux/kernel/sched.c	Tue Apr  8 08:47:47 1997
+++ linux/kernel/sched.c	Mon Aug 11 13:42:11 1997
@@ -283,6 +283,87 @@
 	return weight;
 }
 
+
+/*
+  The following allow_interrupts function is used to workaround a rare but
+  nasty deadlock situation that is possible for 2.0.x Intel SMP because it uses
+  a single kernel lock and interrupts are only routed to the boot CPU.  There
+  are two deadlock scenarios this code protects against.
+
+  The first scenario is that if a CPU other than the boot CPU holds the kernel
+  lock and needs to wait for an operation to complete that itself requires an
+  interrupt, there is a deadlock since the boot CPU may be able to accept the
+  interrupt but will not be able to acquire the kernel lock to process it.
+
+  The workaround for this deadlock requires adding calls to allow_interrupts to
+  places where this deadlock is possible.  These places are known to be present
+  in buffer.c and keyboard.c.  It is also possible that there are other such
+  places which have not been identified yet.  In order to break the deadlock,
+  the code in allow_interrupts temporarily yields the kernel lock directly to
+  the boot CPU to allow the interrupt to be processed.  The boot CPU interrupt
+  entry code indicates that it is spinning waiting for the kernel lock by
+  setting the smp_blocked_interrupt_pending variable.  This code notices that
+  and manipulates the active_kernel_processor variable to yield the kernel lock
+  without ever clearing it.  When the interrupt has been processed, the
+  saved_active_kernel_processor variable contains the value for the interrupt
+  exit code to restore, either the APICID of the CPU that granted it the kernel
+  lock, or NO_PROC_ID in the normal case where no yielding occurred.  Restoring
+  active_kernel_processor from saved_active_kernel_processor returns the kernel
+  lock back to the CPU that yielded it.
+
+  The second form of deadlock is even more insidious.  Suppose the boot CPU
+  takes a page fault and then the previous scenario ensues.  In this case, the
+  boot CPU would spin with interrupts disabled waiting to acquire the kernel
+  lock.  To resolve this deadlock, the kernel lock acquisition code must enable
+  interrupts briefly so that the pending interrupt can be handled as in the
+  case above.
+
+  An additional form of deadlock is where kernel code running on a non-boot CPU
+  waits for the jiffies variable to be incremented.  This deadlock is avoided
+  by having the spin loops in ENTER_KERNEL increment jiffies approximately
+  every 10 milliseconds.  Finally, if approximately 60 seconds elapse waiting
+  for the kernel lock, a message will be printed if possible to indicate that a
+  deadlock has been detected.
+
+		Leonard N. Zubkoff
+		   4 August 1997
+*/
+
+#if defined(__SMP__) && defined(__i386__)
+
+volatile unsigned char smp_blocked_interrupt_pending = 0;
+
+volatile unsigned char saved_active_kernel_processor = NO_PROC_ID;
+
+void allow_interrupts(void)
+{
+  if (smp_processor_id() == boot_cpu_id) return;
+  if (smp_blocked_interrupt_pending)
+    {
+      long timeout_counter = loops_per_sec;
+      unsigned long saved_kernel_counter;
+      saved_active_kernel_processor = active_kernel_processor;
+      saved_kernel_counter = kernel_counter;
+      kernel_counter = 0;
+      active_kernel_processor = boot_cpu_id;
+      while (active_kernel_processor != saved_active_kernel_processor &&
+	     --timeout_counter >= 0)
+	barrier();
+      if (timeout_counter < 0)
+	panic("FORWARDED INTERRUPT TIMEOUT (AKP = %d, Saved AKP = %d)\n",
+	      active_kernel_processor, saved_active_kernel_processor);
+      kernel_counter = saved_kernel_counter;
+      saved_active_kernel_processor = NO_PROC_ID;
+    }
+}
+
+#else
+
+void allow_interrupts(void) {}
+
+#endif
+
+
 /*
  *  'schedule()' is the scheduler function. It's a very simple and nice
  * scheduler: it's not perfect, but certainly works for most things.
@@ -303,6 +384,8 @@
 
 /* check alarm, wake up any interruptible tasks that have got a signal */
 
+	allow_interrupts();
+
 	if (intr_count)
 		goto scheduling_in_interrupt;
 
@@ -1460,8 +1543,9 @@
 {
 	cli();
 	move_last_runqueue(current);
-	sti();
+	current->counter = 0;
 	need_resched = 1;
+	sti();
 	return 0;
 }
 
@@ -1499,10 +1583,10 @@
 	error = verify_area(VERIFY_WRITE, interval, sizeof(struct timespec));
 	if (error)
 		return error;
-	
+
+	/* Values taken from 2.1.38 */	
 	t.tv_sec = 0;
-	t.tv_nsec = 0;   /* <-- Linus, please fill correct value in here */
-	return -ENOSYS;  /* and then delete this line. Thanks!           */
+	t.tv_nsec = 150000;   /* is this right for non-intel architecture too?*/
 	memcpy_tofs(interval, &t, sizeof(struct timespec));
 
 	return 0;
diff -u --recursive --new-file v2.0.30/linux/kernel/sys.c linux/kernel/sys.c
--- v2.0.30/linux/kernel/sys.c	Tue Apr  8 08:47:47 1997
+++ linux/kernel/sys.c	Mon Aug  4 17:15:07 1997
@@ -34,6 +34,7 @@
 int C_A_D = 1;
 
 extern void adjust_clock(void);
+extern void gdth_halt(void);
 
 asmlinkage int sys_ni_syscall(void)
 {
@@ -186,9 +187,12 @@
 		return -EPERM;
 	if (magic != 0xfee1dead || magic_too != 672274793)
 		return -EINVAL;
-	if (flag == 0x01234567)
+	if (flag == 0x01234567) {
+#ifdef CONFIG_SCSI_GDTH
+		gdth_halt();
+#endif
 		hard_reset_now();
-	else if (flag == 0x89ABCDEF)
+	} else if (flag == 0x89ABCDEF)
 		C_A_D = 1;
 	else if (!flag)
 		C_A_D = 0;
@@ -211,9 +215,12 @@
  */
 void ctrl_alt_del(void)
 {
-	if (C_A_D)
+	if (C_A_D) {
+#ifdef CONFIG_SCSI_GDTH
+		gdth_halt();
+#endif
 		hard_reset_now();
-	else
+	} else
 		kill_proc(1, SIGINT, 1);
 }
 	
diff -u --recursive --new-file v2.0.30/linux/kernel/sysctl.c linux/kernel/sysctl.c
--- v2.0.30/linux/kernel/sysctl.c	Mon Mar 31 13:22:37 1997
+++ linux/kernel/sysctl.c	Wed Aug 13 15:02:42 1997
@@ -139,7 +139,7 @@
 #ifdef CONFIG_ROOT_NFS
 	{KERN_NFSRNAME, "nfs-root-name", nfs_root_name, NFS_ROOT_NAME_LEN,
 	 0644, NULL, &proc_dostring, &sysctl_string },
-	{KERN_NFSRNAME, "nfs-root-addrs", nfs_root_addrs, NFS_ROOT_ADDRS_LEN,
+	{KERN_NFSRADDRS, "nfs-root-addrs", nfs_root_addrs, NFS_ROOT_ADDRS_LEN,
 	 0644, NULL, &proc_dostring, &sysctl_string },
 #endif
 #ifdef CONFIG_BINFMT_JAVA
@@ -201,7 +201,7 @@
 	do {
 		context = 0;
 		error = parse_table(name, nlen, oldval, oldlenp, 
-				    newval, newlen, root_table, &context);
+				    newval, newlen, tmp->ctl_table, &context);
 		if (context)
 			kfree(context);
 		if (error != -ENOTDIR)
diff -u --recursive --new-file v2.0.30/linux/kernel/time.c linux/kernel/time.c
--- v2.0.30/linux/kernel/time.c	Wed Apr 24 23:59:33 1996
+++ linux/kernel/time.c	Fri Sep  5 08:53:13 1997
@@ -15,7 +15,7 @@
  * 1993-10-08    Torsten Duwe
  *      adjtime interface update and CMOS clock write code
  * 1995-08-13    Torsten Duwe
- *      kernel PLL updated to 1994-12-13 specs (rfc-1489)
+ *      kernel PLL updated to 1994-12-13 specs (rfc-1589)
  */
 
 #include <linux/errno.h>
diff -u --recursive --new-file v2.0.30/linux/mm/filemap.c linux/mm/filemap.c
--- v2.0.30/linux/mm/filemap.c	Wed Sep 11 07:57:19 1996
+++ linux/mm/filemap.c	Sat Aug 23 10:54:08 1997
@@ -41,18 +41,7 @@
  * Simple routines for both non-shared and shared mappings.
  */
 
-/*
- * This is a special fast page-free routine that _only_ works
- * on page-cache pages that we are currently using. We can
- * just decrement the page count, because we know that the page
- * has a count > 1 (the page cache itself counts as one, and
- * we're currently using it counts as one). So we don't need
- * the full free_page() stuff..
- */
-static inline void release_page(struct page * page)
-{
-	atomic_dec(&page->count);
-}
+#define release_page(page) __free_page((page))
 
 /*
  * Invalidate the pages of an inode, removing all pages that aren't
@@ -100,7 +89,7 @@
 		/* page wholly truncated - free it */
 		if (offset >= start) {
 			if (PageLocked(page)) {
-				wait_on_page(page);
+				__wait_on_page(page);
 				goto repeat;
 			}
 			inode->i_nrpages--;
@@ -125,7 +114,7 @@
 	}
 }
 
-int shrink_mmap(int priority, int dma)
+int shrink_mmap(int priority, int dma, int can_do_io)
 {
 	static int clock = 0;
 	struct page * page;
@@ -169,8 +158,12 @@
 		switch (page->count) {
 			case 1:
 				/* If it has been referenced recently, don't free it */
-				if (clear_bit(PG_referenced, &page->flags))
+				if (clear_bit(PG_referenced, &page->flags)) {
+					/* age this page potential used */
+					if (priority < 4)
+						age_page(page);
 					break;
+				}
 
 				/* is it a page cache page? */
 				if (page->inode) {
@@ -181,7 +174,7 @@
 				}
 
 				/* is it a buffer cache page? */
-				if (bh && try_to_free_buffer(bh, &bh, 6))
+				if (can_do_io && bh && try_to_free_buffer(bh, &bh, 6))
 					return 1;
 				break;
 
@@ -448,7 +441,7 @@
 
 #define PageAlignSize(size) (((size) + PAGE_SIZE -1) & PAGE_MASK)
 
-#if 0  /* small readahead */
+#ifdef CONFIG_READA_SMALL  /* small readahead */
 #define MAX_READAHEAD PageAlignSize(4096*7)
 #define MIN_READAHEAD PageAlignSize(4096*2)
 #else /* large readahead */
@@ -743,10 +736,7 @@
 	filp->f_reada = 1;
 	if (page_cache)
 		free_page(page_cache);
-	if (!IS_RDONLY(inode)) {
-		inode->i_atime = CURRENT_TIME;
-		inode->i_dirt = 1;
-	}
+	UPDATE_ATIME(inode)
 	if (!read)
 		read = error;
 	return read;
@@ -784,8 +774,15 @@
 found_page:
 	/*
 	 * Ok, found a page in the page cache, now we need to check
-	 * that it's up-to-date
+	 * that it's up-to-date.  First check whether we'll need an
+	 * extra page -- better to overlap the allocation with the I/O.
 	 */
+	if (no_share && !new_page) {
+		new_page = __get_free_page(GFP_KERNEL);
+		if (!new_page)
+			goto failure;
+	}
+
 	if (PageLocked(page))
 		goto page_locked_wait;
 	if (!PageUptodate(page))
@@ -810,13 +807,8 @@
 	}
 
 	/*
-	 * Check that we have another page to copy it over to..
+	 * No sharing ... copy to the new page.
 	 */
-	if (!new_page) {
-		new_page = __get_free_page(GFP_KERNEL);
-		if (!new_page)
-			goto failure;
-	}
 	memcpy((void *) new_page, (void *) old_page, PAGE_SIZE);
 	flush_page_to_ram(new_page);
 	release_page(page);
@@ -880,6 +872,8 @@
 	 */
 failure:
 	release_page(page);
+	if (new_page)
+		free_page(new_page);
 no_page:
 	return 0;
 }
@@ -1003,6 +997,8 @@
 	unsigned long page;
 	int error;
 
+	if (pte_none(pte))
+		return 0;
 	if (!(flags & MS_INVALIDATE)) {
 		if (!pte_present(pte))
 			return 0;
@@ -1015,8 +1011,6 @@
 		page = pte_page(pte);
 		mem_map[MAP_NR(page)].count++;
 	} else {
-		if (pte_none(pte))
-			return 0;
 		flush_cache_page(vma, address);
 		pte_clear(ptep);
 		flush_tlb_page(vma, address);
@@ -1178,10 +1172,7 @@
 		return -EACCES;
 	if (!inode->i_op || !inode->i_op->readpage)
 		return -ENOEXEC;
-	if (!IS_RDONLY(inode)) {
-		inode->i_atime = CURRENT_TIME;
-		inode->i_dirt = 1;
-	}
+	UPDATE_ATIME(inode)
 	vma->vm_inode = inode;
 	inode->i_count++;
 	vma->vm_ops = ops;
diff -u --recursive --new-file v2.0.30/linux/mm/mlock.c linux/mm/mlock.c
--- v2.0.30/linux/mm/mlock.c	Wed Sep 11 07:57:19 1996
+++ linux/mm/mlock.c	Mon Aug  4 15:25:11 1997
@@ -202,7 +202,7 @@
 
 	/* we may lock at most half of physical memory... */
 	/* (this check is pretty bogus, but doesn't hurt) */
-	if (locked > MAP_NR(high_memory)/2)
+	if (locked > (MAP_NR(high_memory) >> 1))
 		return -ENOMEM;
 
 	return do_mlock(start, len, 1);
@@ -259,7 +259,7 @@
 
 	/* we may lock at most half of physical memory... */
 	/* (this check is pretty bogus, but doesn't hurt) */
-	if (current->mm->total_vm > MAP_NR(high_memory)/2)
+	if (current->mm->total_vm > (MAP_NR(high_memory) >> 1))
 		return -ENOMEM;
 
 	return do_mlockall(flags);
diff -u --recursive --new-file v2.0.30/linux/mm/page_alloc.c linux/mm/page_alloc.c
--- v2.0.30/linux/mm/page_alloc.c	Sat Aug 17 11:19:29 1996
+++ linux/mm/page_alloc.c	Sat Aug 23 10:54:08 1997
@@ -214,7 +214,7 @@
 		return 0;
 	}
 	restore_flags(flags);
-	if (priority != GFP_BUFFER && try_to_free_page(priority, dma, 1))
+	if (try_to_free_page(priority, dma, 1))
 		goto repeat;
 	return 0;
 }
@@ -264,11 +264,11 @@
 
 	/*
 	 * select nr of pages we try to keep free for important stuff
-	 * with a minimum of 16 pages. This is totally arbitrary
+	 * with a minimum of 24 pages. This is totally arbitrary
 	 */
 	i = (end_mem - PAGE_OFFSET) >> (PAGE_SHIFT+7);
-	if (i < 16)
-		i = 16;
+	if (i < 24)
+		i = 24;
 	min_free_pages = i;
 	free_pages_low = i + (i>>1);
 	free_pages_high = i + i;
@@ -311,7 +311,8 @@
 	unsigned long page = __get_free_page(GFP_KERNEL);
 
 	if (pte_val(*page_table) != entry) {
-		free_page(page);
+		if (page)
+			free_page(page);
 		return;
 	}
 	if (!page) {
@@ -327,6 +328,11 @@
 	}
 	vma->vm_mm->rss++;
 	tsk->maj_flt++;
+
+	/* Give the physical reallocated page a bigger start */
+	if (vma->vm_mm->rss < (MAP_NR(high_memory) >> 2))
+		mem_map[MAP_NR(page)].age = (PAGE_INITIAL_AGE + PAGE_ADVANCE);
+
 	if (!write_access && add_to_swap_cache(MAP_NR(page), entry)) {
 		/* keep swap page allocated for the moment (swap cache) */
 		set_pte(page_table, mk_pte(page, vma->vm_page_prot));
diff -u --recursive --new-file v2.0.30/linux/mm/vmscan.c linux/mm/vmscan.c
--- v2.0.30/linux/mm/vmscan.c	Sat Dec 14 04:24:31 1996
+++ linux/mm/vmscan.c	Tue Aug 26 09:46:04 1997
@@ -19,6 +19,7 @@
 #include <linux/swap.h>
 #include <linux/fs.h>
 #include <linux/swapctl.h>
+#include <linux/pagemap.h>
 #include <linux/smp_lock.h>
 
 #include <asm/dma.h>
@@ -27,11 +28,22 @@
 #include <asm/bitops.h>
 #include <asm/pgtable.h>
 
+/*
+ * To check memory consuming code elsewhere set this to 1
+ */
+/* #define MM_DEBUG */
+
 /* 
  * When are we next due for a page scan? 
  */
 static int next_swap_jiffies = 0;
 
+/*
+ * Was the last kswapd wakeup caused by
+ *     nr_free_pages < free_pages_low
+ */
+static int last_wakeup_low = 0;
+
 /* 
  * How often do we do a pageout scan during normal conditions?
  * Default is four times a second.
@@ -68,7 +80,7 @@
  * have died while we slept).
  */
 static inline int try_to_swap_out(struct task_struct * tsk, struct vm_area_struct* vma,
-	unsigned long address, pte_t * page_table, int dma, int wait)
+	unsigned long address, pte_t * page_table, int dma, int wait, int can_do_io)
 {
 	pte_t pte;
 	unsigned long entry;
@@ -100,6 +112,8 @@
 	if (page_map->age)
 		return 0;
 	if (pte_dirty(pte)) {
+		if(!can_do_io)
+			return 0;
 		if (vma->vm_ops && vma->vm_ops->swapout) {
 			pid_t pid = tsk->pid;
 			vma->vm_mm->rss--;
@@ -157,7 +171,8 @@
  */
 
 static inline int swap_out_pmd(struct task_struct * tsk, struct vm_area_struct * vma,
-	pmd_t *dir, unsigned long address, unsigned long end, int dma, int wait)
+	pmd_t *dir, unsigned long address, unsigned long end, int dma, int wait,
+	int can_do_io)
 {
 	pte_t * pte;
 	unsigned long pmd_end;
@@ -179,7 +194,8 @@
 	do {
 		int result;
 		tsk->swap_address = address + PAGE_SIZE;
-		result = try_to_swap_out(tsk, vma, address, pte, dma, wait);
+		result = try_to_swap_out(tsk, vma, address, pte, dma, wait,
+					 can_do_io);
 		if (result)
 			return result;
 		address += PAGE_SIZE;
@@ -189,7 +205,8 @@
 }
 
 static inline int swap_out_pgd(struct task_struct * tsk, struct vm_area_struct * vma,
-	pgd_t *dir, unsigned long address, unsigned long end, int dma, int wait)
+	pgd_t *dir, unsigned long address, unsigned long end, int dma, int wait,
+	int can_do_io)
 {
 	pmd_t * pmd;
 	unsigned long pgd_end;
@@ -209,7 +226,8 @@
 		end = pgd_end;
 	
 	do {
-		int result = swap_out_pmd(tsk, vma, pmd, address, end, dma, wait);
+		int result = swap_out_pmd(tsk, vma, pmd, address, end, dma, wait,
+					  can_do_io);
 		if (result)
 			return result;
 		address = (address + PMD_SIZE) & PMD_MASK;
@@ -219,7 +237,7 @@
 }
 
 static int swap_out_vma(struct task_struct * tsk, struct vm_area_struct * vma,
-	pgd_t *pgdir, unsigned long start, int dma, int wait)
+	pgd_t *pgdir, unsigned long start, int dma, int wait, int can_do_io)
 {
 	unsigned long end;
 
@@ -230,7 +248,8 @@
 
 	end = vma->vm_end;
 	while (start < end) {
-		int result = swap_out_pgd(tsk, vma, pgdir, start, end, dma, wait);
+		int result = swap_out_pgd(tsk, vma, pgdir, start, end, dma, wait,
+					  can_do_io);
 		if (result)
 			return result;
 		start = (start + PGDIR_SIZE) & PGDIR_MASK;
@@ -239,7 +258,7 @@
 	return 0;
 }
 
-static int swap_out_process(struct task_struct * p, int dma, int wait)
+static int swap_out_process(struct task_struct * p, int dma, int wait, int can_do_io)
 {
 	unsigned long address;
 	struct vm_area_struct* vma;
@@ -260,7 +279,8 @@
 		address = vma->vm_start;
 
 	for (;;) {
-		int result = swap_out_vma(p, vma, pgd_offset(p->mm, address), address, dma, wait);
+		int result = swap_out_vma(p, vma, pgd_offset(p->mm, address), address, dma, wait,
+					  can_do_io);
 		if (result)
 			return result;
 		vma = vma->vm_next;
@@ -272,13 +292,45 @@
 	return 0;
 }
 
-static int swap_out(unsigned int priority, int dma, int wait)
+static int swap_out(unsigned int priority, int dma, int wait, int can_do_io)
 {
 	static int swap_task;
-	int loop, counter;
+	int loop, counter, shfrv;
 	struct task_struct *p;
 
-	counter = ((PAGEOUT_WEIGHT * nr_tasks) >> 10) >> priority;
+#ifdef MM_DEBUG
+	shfrv = 10;
+#else
+	/*
+	 * Trouble due ageing pages: In some situations it is possible that we cross only tasks
+	 * which are swapped out or which have only physical pages with age >= 3.
+	 * High values of swap_cnt for memory consuming tasks do aggravate such situations.
+	 *
+	 * If PAGEOUT_WEIGHT has a value of 8192 a right shift value of 10 leads to
+	 *     (8 * nr_tasks) >> priority
+	 * Together with a high number of tasks, say 100, we have counters (due priority)
+	 *     12(6) + 25(5) + 50(4) + 100(3) + 200(2) + 400(1) + 800(0)
+	 * and as total result 1587 scans of swap_out() to swap out a task page.
+	 *
+	 * Just assume 80 tasks are swapped out and the remaining tasks have a swap_cnt value >= 40
+	 * together with pages with age >= 3.  Then we need approx 20*40*2 = 1600 scans to get a
+	 * free page.
+	 * And now assume that the amount of cached pages, buffers, and ipc pages are really low.
+	 */
+	switch (priority) {
+		case 6: case 5: case 4:  /* be friendly */
+			shfrv = 10;
+			break;
+		case 3: case 2: case 1:  /* more intensive */
+			shfrv =  9;
+			break;
+		case 0: default:         /* sorry we need a page */
+			shfrv =  8;
+			break;
+	}
+#endif
+
+	counter = ((PAGEOUT_WEIGHT * nr_tasks) >> shfrv) >> priority;
 	for(; counter >= 0; counter--) {
 		/*
 		 * Check that swap_task is suitable for swapping.  If not, look for
@@ -305,14 +357,19 @@
 		 * Determine the number of pages to swap from this process.
 		 */
 		if (!p->swap_cnt) {
- 			/* Normalise the number of pages swapped by
-			   multiplying by (RSS / 1MB) */
+ 			/*
+			 * Normalise the number of pages swapped by
+			 * multiplying by (RSS / 1MB)
+			 */
 			p->swap_cnt = AGE_CLUSTER_SIZE(p->mm->rss);
 		}
 		if (!--p->swap_cnt)
 			swap_task++;
-		switch (swap_out_process(p, dma, wait)) {
+		switch (swap_out_process(p, dma, wait, can_do_io)) {
 			case 0:
+				if (p->state == TASK_STOPPED)
+					/* Stopped task occupy nonused ram */
+					break;
 				if (p->swap_cnt)
 					swap_task++;
 				break;
@@ -322,6 +379,14 @@
 				break;
 		}
 	}
+#ifdef MM_DEBUG
+	if (!priority) {
+		printk("swap_out: physical ram %6dkB, min pages   %6dkB\n",
+			(int)(high_memory>>10), min_free_pages<<(PAGE_SHIFT-10));
+		printk("swap_out:   free pages %6dkB, async pages %6dkB\n",
+			nr_free_pages<<(PAGE_SHIFT-10), nr_async_pages<<(PAGE_SHIFT-10));
+	}
+#endif
 	return 0;
 }
 
@@ -334,24 +399,30 @@
 {
 	static int state = 0;
 	int i=6;
-	int stop;
+	int stop, can_do_io;
 
 	/* we don't try as hard if we're not waiting.. */
 	stop = 3;
+	can_do_io = 1;
 	if (wait)
 		stop = 0;
+	if (priority == GFP_BUFFER) {
+		/* bdflush() should do the rest if we fail */
+		stop = 3;
+		can_do_io = 0;
+	}
 	switch (state) {
 		do {
 		case 0:
-			if (shrink_mmap(i, dma))
+			if (shrink_mmap(i, dma, can_do_io))
 				return 1;
 			state = 1;
 		case 1:
-			if (shm_swap(i, dma))
+			if (can_do_io && shm_swap(i, dma))
 				return 1;
 			state = 2;
 		default:
-			if (swap_out(i, dma, wait))
+			if (swap_out(i, dma, wait, can_do_io))
 				return 1;
 			state = 0;
 		i--;
@@ -360,6 +431,24 @@
 	return 0;
 }
 
+/*
+ * Before we start the kernel thread, print out the 
+ * kswapd initialization message (otherwise the init message 
+ * may be printed in the middle of another driver's init 
+ * message).  It looks very bad when that happens.
+ */
+void kswapd_setup(void)
+{
+       int i;
+       char *revision="$Revision: 1.4.2.2 $", *s, *e;
+
+       if ((s = strchr(revision, ':')) &&
+           (e = strchr(s, '$')))
+               s++, i = e - s;
+       else
+               s = revision, i = -1;
+       printk ("Starting kswapd v%.*s\n", i, s);
+}
 
 /*
  * The background pageout daemon.
@@ -368,7 +457,6 @@
 int kswapd(void *unused)
 {
 	int i;
-	char *revision="$Revision: 1.4.2.2 $", *s, *e;
 	
 	current->session = 1;
 	current->pgrp = 1;
@@ -394,14 +482,10 @@
 
 	init_swap_timer();
 	
-	if ((s = strchr(revision, ':')) &&
-	    (e = strchr(s, '$')))
-		s++, i = e - s;
-	else
-		s = revision, i = -1;
-	printk ("Started kswapd v%.*s\n", i, s);
-
 	while (1) {
+		/* low on memory, we need to start swapping soon */
+		next_swap_jiffies = jiffies +
+			(last_wakeup_low ? swapout_interval >> 1 : swapout_interval);
 		kswapd_awake = 0;
 		current->signal = 0;
 		run_task_queue(&tq_disk);
@@ -410,7 +494,8 @@
 		swapstats.wakeups++;
 		/* Do the background pageout: */
 		for (i=0; i < kswapd_ctl.maxpages; i++)
-			try_to_free_page(GFP_KERNEL, 0, 0);
+			try_to_free_page(GFP_KERNEL, 0,
+					 (nr_free_pages < min_free_pages));
 	}
 }
 
@@ -421,16 +506,15 @@
 void swap_tick(void)
 {
 	int	want_wakeup = 0;
-	static int	last_wakeup_low = 0;
 
 	if ((nr_free_pages + nr_async_pages) < free_pages_low) {
 		if (last_wakeup_low)
-			want_wakeup = jiffies >= next_swap_jiffies;
+			want_wakeup = (jiffies >= next_swap_jiffies);
 		else
 			last_wakeup_low = want_wakeup = 1;
 	}
 	else if (((nr_free_pages + nr_async_pages) < free_pages_high) && 
-	         jiffies >= next_swap_jiffies) {
+	         (jiffies >= next_swap_jiffies)) {
 		last_wakeup_low = 0;
 		want_wakeup = 1;
 	}
@@ -440,7 +524,6 @@
 			wake_up(&kswapd_wait);
 			need_resched = 1;
 		}
-		next_swap_jiffies = jiffies + swapout_interval;
 	}
 	timer_active |= (1<<SWAP_TIMER);
 }
diff -u --recursive --new-file v2.0.30/linux/net/Config.in linux/net/Config.in
--- v2.0.30/linux/net/Config.in	Mon Jul  8 00:21:46 1996
+++ linux/net/Config.in	Tue Aug 12 11:30:22 1997
@@ -11,9 +11,6 @@
 fi
 comment ' '
 tristate 'The IPX protocol' CONFIG_IPX
-if [ "$CONFIG_IPX" != "n" ]; then
-  bool 'Full internal IPX network' CONFIG_IPX_INTERN
-fi
 tristate 'Appletalk DDP' CONFIG_ATALK
 bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25
 if [ "$CONFIG_AX25" = "y" ]; then
diff -u --recursive --new-file v2.0.30/linux/net/appletalk/ddp.c linux/net/appletalk/ddp.c
--- v2.0.30/linux/net/appletalk/ddp.c	Thu Nov 14 05:20:10 1996
+++ linux/net/appletalk/ddp.c	Wed Aug  6 11:20:34 1997
@@ -828,7 +828,7 @@
 			else 
 			{
 				limit=ntohs(nr->nr_lastnet);
-				if(limit-ntohs(nr->nr_firstnet) > 256)
+				if(limit-ntohs(nr->nr_firstnet) > 4096)
 				{
 					printk(KERN_WARNING "Too many routes/iface.\n");
 					return -EINVAL;
@@ -854,6 +854,16 @@
 			((struct sockaddr_at *)(&atreq.ifr_addr))->sat_addr.s_net=atif->address.s_net;
 			((struct sockaddr_at *)(&atreq.ifr_addr))->sat_addr.s_node=ATADDR_BCAST;
 			break;
+	        case SIOCATALKDIFADDR:
+			if(!suser())
+				return -EPERM;
+			if(sa->sat_family!=AF_APPLETALK)
+				return -EINVAL;
+			if(atif==NULL)
+				return -EADDRNOTAVAIL;
+			atrtr_device_down(atif->dev);
+			atif_drop_device(atif->dev);
+			break;			
 	}
 	memcpy_tofs(arg,&atreq,sizeof(atreq));
 	return 0;
@@ -1144,6 +1154,7 @@
 	MOD_INC_USE_COUNT;
 
 	sk->no_check=0;		/* Checksums on by default */
+	sk->no_check=1;		/* Checksums off by default */
 	sk->allocation=GFP_KERNEL;
 	sk->rcvbuf=SK_RMEM_MAX;
 	sk->sndbuf=SK_WMEM_MAX;
@@ -1623,6 +1634,67 @@
 	return atalk_rcv(skb,dev,pt);
 }
 
+
+/*
+ *	This is slower, and copies the whole data area 
+ */
+ 
+static struct sk_buff *ddp_skb_copy(struct sk_buff *skb, int priority)
+{
+	struct sk_buff *n;
+	unsigned long offset;
+
+	/*
+	 *	Allocate the copy buffer
+	 */
+	 
+	IS_SKB(skb);
+	
+	n=alloc_skb(skb->truesize-sizeof(struct sk_buff),priority);
+	if(n==NULL)
+		return NULL;
+
+	/*
+	 *	Shift between the two data areas in bytes
+	 */
+	 
+	offset=n->head-skb->head;
+
+	/* Set the data pointer */
+	skb_reserve(n,skb->data-skb->head);
+	/* Set the tail pointer and length */
+	skb_put(n,skb->len);
+	/* Copy the bytes */
+	memcpy(n->head,skb->head,skb->end-skb->head);
+	n->link3=NULL;
+	n->list=NULL;
+	n->sk=NULL;
+	n->when=skb->when;
+	n->dev=skb->dev;
+	n->h.raw=skb->h.raw+offset;
+	n->mac.raw=skb->mac.raw+offset;
+	n->ip_hdr=(struct iphdr *)(((char *)skb->ip_hdr)+offset);
+	n->saddr=skb->saddr;
+	n->daddr=skb->daddr;
+	n->raddr=skb->raddr;
+	n->seq=skb->seq;
+	n->end_seq=skb->end_seq;
+	n->ack_seq=skb->ack_seq;
+	n->acked=skb->acked;
+	memcpy(n->proto_priv, skb->proto_priv, sizeof(skb->proto_priv));
+	n->used=skb->used;
+	n->free=1;
+	n->arp=skb->arp;
+	n->tries=0;
+	n->lock=0;
+	n->users=0;
+	n->pkt_type=skb->pkt_type;
+	n->stamp=skb->stamp;
+	
+	IS_SKB(n);
+	return n;
+}
+
 static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nonblock, int flags)
 {
 	atalk_socket *sk=(atalk_socket *)sock->data;
@@ -1763,16 +1835,27 @@
 	{
 		if((!(rt->flags&RTF_GATEWAY))&&(!(dev->flags&IFF_LOOPBACK)))
 		{
-			struct sk_buff *skb2=skb_clone(skb, GFP_KERNEL);
-			if(skb2)
-			{
-				loopback=1;
-				if(sk->debug)
-					printk("SK %p: send out(copy).\n", sk);
-				if(aarp_send_ddp(dev,skb2,&usat->sat_addr, NULL)==-1)
-					kfree_skb(skb2, FREE_WRITE);
-				/* else queued/sent above in the aarp queue */
-			}
+		  struct sk_buff *skb2;
+
+		  /* Make a copy of the skbuf so that the loopback does not trash it
+		   * in the next block.
+		   * Added by Peter Skarpetis, Serendipity Software 24 June 1997
+		   */
+		  skb2 = ddp_skb_copy(skb, GFP_ATOMIC);
+		  if (skb2 == NULL) {
+			printk("ddp.c: cannot allocate skb copy buffer\n");
+			return -1;
+		  }
+		  
+		  if(skb2)
+		    {
+		      loopback=1;
+		      if(sk->debug)
+			printk("SK %p: send out(copy).\n", sk);
+		      if(aarp_send_ddp(dev, skb2, &usat->sat_addr, NULL)==-1)
+			kfree_skb(skb2, FREE_WRITE);
+		      /* else queued/sent above in the aarp queue */
+		    }
 		}
 	}
 
@@ -1922,6 +2005,7 @@
 		case SIOCGIFADDR:
 		case SIOCSIFADDR:
 		case SIOCGIFBRDADDR:
+		case SIOCATALKDIFADDR:		  
 			return atif_ioctl(cmd,(void *)arg);
 		/*
 		 *	Physical layer ioctl calls
diff -u --recursive --new-file v2.0.30/linux/net/bridge/br.c linux/net/bridge/br.c
--- v2.0.30/linux/net/bridge/br.c	Thu Nov  7 22:44:27 1996
+++ linux/net/bridge/br.c	Tue Aug 26 11:05:34 1997
@@ -1418,7 +1418,7 @@
 		return;
 	}
 		
-	bpdu = (Tcn_bpdu *)skb->data + ETH_HLEN;
+	bpdu = (Tcn_bpdu *) (skb->data + ETH_HLEN);
 	switch (bpdu->type) {
 		case BPDU_TYPE_CONFIG:
 			received_config_bpdu(port, (Config_bpdu *)bpdu);
diff -u --recursive --new-file v2.0.30/linux/net/core/dev.c linux/net/core/dev.c
--- v2.0.30/linux/net/core/dev.c	Tue Apr  8 08:47:47 1997
+++ linux/net/core/dev.c	Tue Aug 12 14:15:56 1997
@@ -392,7 +392,7 @@
 
 #ifdef CONFIG_NET_ALIAS
 	if (net_alias_is(dev))
-	  	skb->dev = dev = net_alias_main_dev(dev);
+	  	skb->dev = dev = net_alias_dev_tx(dev);
 #endif
 
 	/*
@@ -436,7 +436,18 @@
 					struct sk_buff *skb2;
 					if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL)
 						break;
+					/* FIXME?: Wrong when the hard_header_len
+					 * is an upper bound. Is this even
+					 * used anywhere?
+					 */
 					skb2->h.raw = skb2->data + dev->hard_header_len;
+					/* On soft header devices we
+					 * yank the header before mac.raw
+					 * back off. This is set by
+					 * dev->hard_header().
+					 */
+					if (dev->flags&IFF_SOFTHEADERS)
+						skb_pull(skb2,skb2->mac.raw-skb2->data);
 					skb2->mac.raw = skb2->data;
 					ptype->func(skb2, skb->dev, ptype);
 				}
@@ -1095,7 +1106,7 @@
 	switch(getset) 
 	{
 		case SIOCGIFFLAGS:	/* Get interface flags */
-			ifr.ifr_flags = dev->flags;
+			ifr.ifr_flags = (dev->flags & ~IFF_SOFTHEADERS);
 			goto rarok;
 
 		case SIOCSIFFLAGS:	/* Set interface flags */
@@ -1117,7 +1128,7 @@
 					IFF_BROADCAST | IFF_DEBUG | IFF_LOOPBACK |
 					IFF_POINTOPOINT | IFF_NOTRAILERS | IFF_RUNNING |
 					IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI | IFF_SLAVE | IFF_MASTER
-					| IFF_MULTICAST)) | (dev->flags & IFF_UP);
+					| IFF_MULTICAST)) | (dev->flags & (IFF_SOFTHEADERS|IFF_UP));
 				/*
 				 *	Load in the correct multicast list now the flags have changed.
 				 */				
@@ -1427,7 +1438,6 @@
 		case SIOCGIFMTU:
 		case SIOCGIFMEM:
 		case SIOCGIFHWADDR:
-		case SIOCSIFHWADDR:
 		case SIOCGIFSLAVE:
 		case SIOCGIFMAP:
 			return dev_ifsioc(arg, cmd);
@@ -1444,6 +1454,7 @@
 		case SIOCSIFMETRIC:
 		case SIOCSIFMTU:
 		case SIOCSIFMEM:
+		case SIOCSIFHWADDR:
 		case SIOCSIFMAP:
 		case SIOCSIFSLAVE:
 		case SIOCADDMULTI:
diff -u --recursive --new-file v2.0.30/linux/net/core/net_alias.c linux/net/core/net_alias.c
--- v2.0.30/linux/net/core/net_alias.c	Sat Aug 31 23:15:34 1996
+++ linux/net/core/net_alias.c	Tue Aug 12 14:15:56 1997
@@ -2,7 +2,8 @@
  *		NET_ALIAS network device aliasing module.
  *
  *
- * Version:	@(#)net_alias.c	0.43   12/20/95
+ * Version:	@(#)net_alias.c	0.50   4/20/97
+ *
  *
  * Authors:	Juan Jose Ciarlante, <jjciarla@raiz.uncu.edu.ar>
  *		Marcelo Fabian Roccasalva, <mfroccas@raiz.uncu.edu.ar>
@@ -14,13 +15,16 @@
  *	-	fast hashed alias address lookup
  *	-	net_alias_type objs registration/unreg., module-ables.
  *	-	/proc/net/aliases & /proc/net/alias_types entries
+ *	-	tx and rx stats
+ *	-	/proc/sys/net/core/net_alias_max entry
  * Fixes:
- *			JJC	:	several net_alias_type func. renamed.
- *			JJC	:	net_alias_type object methods now pass 
+ *	Juan Jose Ciarlante	:	several net_alias_type func. renamed.
+ *	Juan Jose Ciarlante	:	net_alias_type object methods now pass 
  *					*this.
- *			JJC	:	xxx_rcv device selection based on <src,dst> 
- *					addrs
+ *	Juan Jose Ciarlante	:	xxx_rcv device selection based on <src,dst> addrs
  *		Andreas Schultz	:	Kerneld support.
+ *	Juan Jose Ciarlante	:	Added tx/rx stats for aliases.
+ *	Juan Jose Ciarlante	:	Added sysctl interface for max aliases per device
  *
  * FIXME:
  *	- User calls sleep/wake_up locking.
@@ -39,10 +43,12 @@
 #include <linux/netdevice.h>
 #include <linux/notifier.h>
 #include <linux/if.h>
+#include <linux/if_ether.h>
 #include <linux/inet.h>
 #include <linux/in.h>
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
+#include <linux/sysctl.h>
 
 #ifdef ALIAS_USER_LAND_DEBUG
 #include "net_alias.h"
@@ -55,11 +61,35 @@
 #include <linux/kerneld.h>
 #endif
 
+
 /*
- * Only allow the following flags to pass from main device to aliases
+ * 	NET_ALIAS_MAX_DEFAULT: max. alias slot number allowed by default,
+ *		can be changed by sysctl
+ *	NET_ALIAS_HASH_TAB_SIZE: hash table size (addr lookup), 1 per aliased 
+ *		device, due to hash optimizations, MUST be 16 or 256 only.
  */
 
-#define  NET_ALIAS_IFF_MASK   (IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_NOARP|IFF_LOOPBACK|IFF_POINTOPOINT)
+#define NET_ALIAS_MAX_DEFAULT		256
+
+/* DO NOT CHANGE the line below ! */
+#define NET_ALIAS_HASH_TAB_SIZE(n)  ( ((n)>=NET_ALIAS_MAX_DEFAULT) ? 256 : 16 )
+
+/*
+ *	set default max_aliases per device
+ */
+int sysctl_net_alias_max = NET_ALIAS_MAX_DEFAULT;
+
+/*
+ * 	Only allow the following flags to pass from main device to aliases
+ *	Note that IFF_BROADCAST is not passed by default, this make sense
+ *	because:
+ *	a) if same-net alias: broadcasts are already handled by main device
+ *	b) if diff-net alias: bcasts will be set by 'broadcast' ifconfig opt.
+ *	I prefer this approach instead of setting '-broadcast' for each
+ *	same-net alias device  --JJC.
+ */
+
+#define  NET_ALIAS_IFF_MASK   (IFF_SOFTHEADERS|IFF_NOARP|IFF_LOOPBACK|IFF_POINTOPOINT)
 
 static struct net_alias_type * nat_getbytype(int type);
 static int nat_attach_chg(struct net_alias_type *nat, int delta);
@@ -68,6 +98,7 @@
 
 
 static int net_alias_devinit(struct device *dev);
+static struct enet_statistics *net_alias_dev_stats(struct device *dev);
 static int net_alias_hard_start_xmit(struct sk_buff *skb, struct device *dev);
 static int net_alias_devsetup(struct net_alias *alias, struct net_alias_type *nat, struct sockaddr *sa);
 static struct net_alias **net_alias_slow_findp(struct net_alias_info *alias_info, struct net_alias *alias);
@@ -79,7 +110,22 @@
  * net_alias_type base array, will hold net_alias_type obj hashed list heads.
  */
 
-struct net_alias_type *nat_base[16];
+struct net_alias_type *net_alias_type_base[16];
+
+
+/*
+ *	get_stats function: return rx/tx pkts based on lookups
+ */
+static struct enet_statistics *net_alias_dev_stats(struct device *dev)
+{
+        static struct enet_statistics alias_stats;
+        struct net_alias *alias = dev->my_alias;
+        
+        memset (&alias_stats, 0, sizeof (alias_stats));
+        alias_stats.rx_packets	      = alias->rx_lookups;
+        alias_stats.tx_packets	      = alias->tx_lookups;
+        return &alias_stats;
+}
 
 
 /*
@@ -90,7 +136,7 @@
 nat_getbytype(int type)
 {
   struct net_alias_type *nat;
-  for(nat = nat_base[type & 0x0f]; nat ; nat = nat->next)
+  for(nat = net_alias_type_base[type & 0x0f]; nat ; nat = nat->next)
   {
     if (nat->type == type) return nat;
   }
@@ -119,11 +165,14 @@
  */
 
 static __inline__ unsigned
-HASH(__u32 addr, int af)
+hash_key(unsigned hsize, __u32 addr)
 {
   unsigned tmp = addr ^ (addr>>16); /* 4 -> 2 */
   tmp ^= (tmp>>8);                  /* 2 -> 1 */
-  return (tmp^(tmp>>4)^af) & 0x0f;	    /* 1 -> 1/2 */
+  if (hsize == 256)
+    return (tmp & 0xff);
+  else
+    return (tmp^(tmp>>4)) & 0x0f;	    /* 1 -> 1/2 */
 }
 
 
@@ -135,9 +184,9 @@
  */
 
 static __inline__ int
-nat_hash_key(struct net_alias_type *nat, struct sockaddr *sa)
+nat_hash_key(struct net_alias_type *nat, unsigned hsize, struct sockaddr *sa)
 {
-  return HASH(nat_addr32(nat,sa), sa->sa_family);
+  return hash_key(hsize, nat_addr32(nat,sa));
 }
 
 
@@ -236,13 +285,13 @@
 
 
 static int
-net_alias_open(struct device * dev)
+net_alias_dev_open(struct device * dev)
 {
   return 0;
 }
 
 static int
-net_alias_close(struct device * dev)
+net_alias_dev_close(struct device * dev)
 {
   return 0;
 }
@@ -271,13 +320,16 @@
   dev = &alias->dev;
   memset(dev, '\0', sizeof(struct device));
   family = (sa)? sa->sa_family : main_dev->family;
-
+  alias->rx_lookups = 0;
+  alias->tx_lookups = 0;
   dev->alias_info = NULL;	/* no aliasing recursion */
   dev->my_alias = alias;	/* point to alias */
   dev->name = alias->name;
   dev->type = main_dev->type;
-  dev->open = net_alias_open;
-  dev->stop = net_alias_close;
+  dev->open = net_alias_dev_open;
+  dev->stop = net_alias_dev_close;
+  dev->get_stats = net_alias_dev_stats;
+  
   dev->hard_header_len = main_dev->hard_header_len;
   memcpy(dev->broadcast, main_dev->broadcast, MAX_ADDR_LEN);
   memcpy(dev->dev_addr, main_dev->dev_addr, MAX_ADDR_LEN);
@@ -328,7 +380,7 @@
    */
   
   n_aliases = alias_info->n_aliases;
-  for (idx=0; idx < 16 ; idx++)
+  for (idx=0; idx < alias_info->hash_tab_size ; idx++)
     for (aliasp = &alias_info->hash_tab[idx];*aliasp;aliasp = &(*aliasp)->next)
       if (*aliasp == alias)
 	return aliasp;
@@ -353,6 +405,7 @@
   unsigned long flags;
   int family;
   __u32 addr32;
+  int max_aliases;
   
   /* FIXME: lock */
   alias_info = main_dev->alias_info;
@@ -387,6 +440,19 @@
 #endif
   }
   
+  *err = -EINVAL;
+  
+  if (!alias_info)
+  	/*
+         *	At this point we take the sysctl value(s)
+         */
+  	max_aliases = sysctl_net_alias_max;
+  else
+  	max_aliases = alias_info->max_aliases;
+  
+  if (slot >= max_aliases )     /* 0-based (eth0:0 _IS_ valid) */
+  	return NULL;
+  
   /*
    * do not allow creation over downed devices
    */
@@ -403,10 +469,25 @@
   *err = -ENOMEM;
 
   if (!alias_info)
-  { 
-    alias_info = kmalloc(sizeof(struct net_alias_info), GFP_KERNEL);
-    if (!alias_info) return NULL; /* ENOMEM */
-    memset(alias_info, 0, sizeof(struct net_alias_info));
+  {
+          /*
+           *	Allocate space for struct net_alias_info plus hash table
+           */
+          int truesize;
+
+          /* net_alias_info size */
+          truesize = sizeof(struct net_alias_info);
+          /* add   hash_tab size * sizeof(elem)  */
+          truesize += NET_ALIAS_HASH_TAB_SIZE(max_aliases) * sizeof (struct net_alias *);
+          
+          alias_info = kmalloc( truesize , GFP_KERNEL);
+  
+          if (!alias_info) return NULL; /* ENOMEM */
+          
+          memset(alias_info, 0, truesize);
+          alias_info->truesize = truesize;
+          alias_info->max_aliases = max_aliases;
+          alias_info->hash_tab_size = NET_ALIAS_HASH_TAB_SIZE(max_aliases);
   }
   
   if (!(alias = kmalloc(sizeof(struct net_alias), GFP_KERNEL)))
@@ -453,7 +534,7 @@
    * store hash key in alias: will speed-up rehashing and deletion
    */
   
-  alias->hash = HASH(addr32, family);
+  alias->hash = hash_key(alias_info->hash_tab_size, addr32);
 
   /*
    * insert alias in hashed linked list
@@ -595,7 +676,7 @@
   
   kfree_s(alias, sizeof(struct net_alias));
   if (main_dev->alias_info == NULL)
-    kfree_s(alias_info, sizeof(struct net_alias_info));
+    kfree_s(alias_info, alias_info->truesize);
 
   /*
    * deletion ok (*err=0), NULL device returned.
@@ -723,14 +804,12 @@
   if (!(dev=dev_get(dev_name)))
     return NULL;
   *sptr++=':';
-  
+
   /*
    * fetch slot number
    */
   
   slot = simple_strtoul(sptr,&eptr,10);
-  if (slot >= NET_ALIAS_MAX_SLOT)
-    return NULL;
 
   /*
    * if last char is '-', it is a deletion request
@@ -829,7 +908,7 @@
    * new hash key. if same as old AND same type (family) return;
    */
   
-  n_hash = nat_hash_key(n_nat, sa);
+  n_hash = nat_hash_key(n_nat, alias_info->hash_tab_size, sa);
   if (n_hash == alias->hash && o_nat == n_nat )
     return 0;
 
@@ -905,7 +984,7 @@
   unsigned idx;
   len=sprintf(buffer,"type    name            n_attach\n");
   for (idx=0 ; idx < 16 ; idx++)
-    for (nat = nat_base[idx]; nat ; nat = nat->next)
+    for (nat = net_alias_type_base[idx]; nat ; nat = nat->next)
     {
       len += sprintf(buffer+len, "%-7d %-15s %-7d\n",
 		     nat->type, nat->name,nat->n_attach);
@@ -1027,7 +1106,9 @@
 nat_addr_chk(struct net_alias_type *nat, struct net_alias_info *alias_info, struct sockaddr *sa, int flags_on, int flags_off)
 {
   struct net_alias *alias;
-  for(alias = alias_info->hash_tab[nat_hash_key(nat,sa)];
+  unsigned hsize = alias_info->hash_tab_size;
+  
+  for(alias = alias_info->hash_tab[nat_hash_key(nat,hsize,sa)];
       alias; alias = alias->next)
   {
     if (alias->dev.family != sa->sa_family) continue;
@@ -1052,7 +1133,9 @@
 nat_addr_chk32(struct net_alias_type *nat, struct net_alias_info *alias_info, int family, __u32 addr32, int flags_on, int flags_off)
 {
   struct net_alias *alias;
-  for (alias=alias_info->hash_tab[HASH(addr32,family)];
+  unsigned hsize = alias_info->hash_tab_size;
+  
+  for (alias=alias_info->hash_tab[hash_key(hsize, addr32)];
        alias; alias=alias->next)
   {
     if (alias->dev.family != family) continue;
@@ -1126,7 +1209,7 @@
  */
 
 struct device *
-net_alias_dev_rcv_sel(struct device *main_dev, struct sockaddr *sa_src, struct sockaddr *sa_dst)
+net_alias_dev_rx(struct device *main_dev, struct sockaddr *sa_src, struct sockaddr *sa_dst)
 {
   int family;
   struct net_alias_type *nat;
@@ -1166,7 +1249,10 @@
 
     dev = nat_addr_chk(nat, alias_info, sa_dst, IFF_UP, 0);
 
-    if (dev != NULL) return dev;
+    if (dev != NULL) {
+            net_alias_inc_rx(dev->my_alias);
+            return dev;
+    }
   }
 
   /*
@@ -1181,24 +1267,29 @@
   /*
    * dev ok only if it is alias of main_dev
    */
-  
-  dev = net_alias_is(dev)?
-    ( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL;
 
+  if (net_alias_is(dev)) {
+          struct net_alias *alias=dev->my_alias;
+          if (alias->main_dev == main_dev) {
+                  net_alias_inc_rx(alias);
+                  return dev;
+          }
+  }
+  
   /*
    * do not return NULL.
    */
   
-  return (dev)? dev : main_dev;
+  return main_dev;
 
 }
 
 /*
- * dev_rcv_sel32: dev_rcv_sel for 'pa_addr' protocols.
+ * dev_rx32: dev_rx selection for 'pa_addr' protocols.
  */
 
 struct device *
-net_alias_dev_rcv_sel32(struct device *main_dev, int family, __u32 src, __u32 dst)
+net_alias_dev_rx32(struct device *main_dev, int family, __u32 src, __u32 dst)
 {
   struct net_alias_type *nat;
   struct net_alias_info *alias_info;
@@ -1236,7 +1327,10 @@
   if (dst)
   {
     dev = nat_addr_chk32(nat, alias_info, family, dst, IFF_UP, 0);
-    if (dev) return dev;
+    if (dev) {
+            net_alias_inc_rx(dev->my_alias);
+            return dev;
+    }
   }
   
   /*
@@ -1255,19 +1349,23 @@
   /*
    * dev ok only if it is alias of main_dev
    */
-  
-  dev = net_alias_is(dev)?
-    ( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL;
+
+  if (net_alias_is(dev)) {
+          struct net_alias *alias=dev->my_alias;
+          if (alias->main_dev == main_dev) {
+                  net_alias_inc_rx(alias);
+                  return dev;
+          }
+  }
 
   /*
    * do not return NULL.
    */
   
-  return (dev)? dev : main_dev;
+  return main_dev;
   
 }
 
-
 /*
  * device event hook
  */
@@ -1333,8 +1431,8 @@
   hash = nat->type & 0x0f;
   save_flags(flags);
   cli();
-  nat->next = nat_base[hash];
-  nat_base[hash] = nat;
+  nat->next = net_alias_type_base[hash];
+  net_alias_type_base[hash] = nat;
   restore_flags(flags);
   return 0;
 }
@@ -1366,7 +1464,7 @@
   hash = nat->type & 0x0f;
   save_flags(flags);
   cli();
-  for (natp = &nat_base[hash]; *natp ; natp = &(*natp)->next)
+  for (natp = &net_alias_type_base[hash]; *natp ; natp = &(*natp)->next)
   {
     if (nat==(*natp))
     {
@@ -1380,3 +1478,26 @@
   return -EINVAL;
 }
 
+/*
+ *	Log sysctl's net_alias_max changes.
+ */
+int proc_do_net_alias_max(ctl_table *ctl, int write, struct file *filp,
+			    void *buffer, size_t *lenp) 
+{
+        int old = sysctl_net_alias_max;
+        int ret;
+        
+        ret = proc_dointvec(ctl, write, filp, buffer, lenp);
+        if (write) {
+                if (sysctl_net_alias_max != old) {
+                        printk(KERN_INFO "sysctl: net_alias_max changed (max.aliases=%d, hashsize=%d).\n",
+                               sysctl_net_alias_max,
+                               NET_ALIAS_HASH_TAB_SIZE(sysctl_net_alias_max));
+                        if (!sysctl_net_alias_max)
+                                printk(KERN_INFO "sysctl: net_alias creation disabled.\n");
+                        if (!old)
+                                printk(KERN_INFO "sysctl: net_alias creation enabled.\n");
+                }
+        }
+        return ret; 
+}
diff -u --recursive --new-file v2.0.30/linux/net/core/sock.c linux/net/core/sock.c
--- v2.0.30/linux/net/core/sock.c	Tue Oct 29 17:42:42 1996
+++ linux/net/core/sock.c	Fri Aug 15 12:23:22 1997
@@ -71,6 +71,7 @@
  *		Alan Cox	: 	Generic socket allocation to make hooks
  *					easier (suggested by Craig Metz).
  *		Michael Pall	:	SO_ERROR returns positive errno again
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  *
  * To Fix:
  *
@@ -128,6 +129,7 @@
 	int valbool;
 	int err;
 	struct linger ling;
+	struct ifreq req;
 
 	/*
 	 *	Options without arguments
@@ -232,7 +234,37 @@
 		case SO_BSDCOMPAT:
 			sk->bsdism = valbool;
 			return 0;
-			
+
+#ifdef CONFIG_NET	
+ 	        case SO_BINDTODEVICE:
+ 			/* Bind this socket to a particular device like "eth0",
+ 			 * as specified in an ifreq structure.  If the device 
+ 			 * is "", socket is NOT bound to a device. */
+ 			if (!valbool) {
+ 				sk->bound_device = NULL;
+ 			} else {
+ 				err=verify_area(VERIFY_READ,optval,sizeof(req));
+ 				if(err)
+ 					return err;
+ 				memcpy_fromfs(&req,optval,sizeof(req));
+
+				/* Remove any cached route for this socket. */
+				if (sk->ip_route_cache) {
+					ip_rt_put(sk->ip_route_cache);
+					sk->ip_route_cache=NULL;
+				}
+
+ 				if (*(req.ifr_name) == '\0') {
+ 					sk->bound_device = NULL;
+ 				} else {
+ 					sk->bound_device = dev_get(req.ifr_name);
+ 					if (sk->bound_device == NULL)
+ 						return -EINVAL;
+ 				}
+ 			}
+  			return 0;
+#endif
+
 		default:
 		  	return(-ENOPROTOOPT);
   	}
@@ -315,6 +347,28 @@
 			val = sk->bsdism;
 			break;
 
+#ifdef CONFIG_NET
+                case SO_BINDTODEVICE:
+		{
+			struct ifreq req;
+
+                        /* Return the bound device (if any) */
+                        err=verify_area(VERIFY_WRITE,optval,sizeof(req));
+                        if(err)
+                                return err;
+
+                        memset((char *) &req, 0, sizeof(req));
+
+                        if (sk->bound_device) {
+                            strncpy(req.ifr_name, sk->bound_device->name, sizeof(req.ifr_name));
+                            (*(struct sockaddr_in *) &req.ifr_addr).sin_family = sk->bound_device->family;
+                            (*(struct sockaddr_in *) &req.ifr_addr).sin_addr.s_addr = sk->bound_device->pa_addr;
+                        }
+                        memcpy_tofs(optval, &req, sizeof(req));
+                        return 0;
+		}
+#endif
+
 		default:
 			return(-ENOPROTOOPT);
 	}
@@ -462,9 +516,9 @@
 			return NULL;
 		}
 		
-		
+
 		mem=sk->wmem_alloc;
-		
+
 		if(!fallback)
 			skb = sock_wmalloc(sk, size, 0, sk->allocation);
 		else
diff -u --recursive --new-file v2.0.30/linux/net/core/sysctl_net_core.c linux/net/core/sysctl_net_core.c
--- v2.0.30/linux/net/core/sysctl_net_core.c	Mon Apr  1 22:03:35 1996
+++ linux/net/core/sysctl_net_core.c	Tue Aug 12 11:30:22 1997
@@ -7,7 +7,17 @@
 
 #include <linux/mm.h>
 #include <linux/sysctl.h>
+#include <linux/config.h>
+
+#ifdef CONFIG_NET_ALIAS
+extern int sysctl_net_alias_max;
+extern int proc_do_net_alias_max(ctl_table *, int, struct file *, void *, size_t *);
+#endif
 
 ctl_table core_table[] = {
+#ifdef CONFIG_NET_ALIAS
+	{NET_CORE_NET_ALIAS_MAX, "net_alias_max", &sysctl_net_alias_max, sizeof(int),
+	 0644, NULL, &proc_do_net_alias_max },
+#endif  
 	{0}
 };
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/Config.in linux/net/ipv4/Config.in
--- v2.0.30/linux/net/ipv4/Config.in	Tue Apr  8 08:47:47 1997
+++ linux/net/ipv4/Config.in	Tue Aug 12 11:30:25 1997
@@ -14,7 +14,9 @@
       bool 'IP: masquerading' CONFIG_IP_MASQUERADE
       if [ "$CONFIG_IP_MASQUERADE" != "n" ]; then
         comment 'Protocol-specific masquerading support will be built as modules.'
-        bool 'IP: ipautofw masq support' CONFIG_IP_MASQUERADE_IPAUTOFW
+        if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then	
+          bool 'IP: ipautofw masq support' CONFIG_IP_MASQUERADE_IPAUTOFW
+        fi
         bool 'IP: ICMP masquerading' CONFIG_IP_MASQUERADE_ICMP
       fi
       if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then	
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v2.0.30/linux/net/ipv4/af_inet.c	Tue Apr  8 08:47:47 1997
+++ linux/net/ipv4/af_inet.c	Fri Aug 15 12:23:23 1997
@@ -620,8 +620,12 @@
 #endif		 
 	if (snum == 0) 
 		snum = sk->prot->good_socknum();
-	if (snum < PROT_SOCK && !suser()) 
+        if (snum < PROT_SOCK) {
+		if (!suser()) 
 		return(-EACCES);
+		if (snum == 0)
+			return(-EAGAIN);
+	}
 	
 	chk_addr_ret = ip_chk_addr(addr->sin_addr.s_addr);
 	if (addr->sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR &&
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v2.0.30/linux/net/ipv4/arp.c	Tue Apr  8 08:47:47 1997
+++ linux/net/ipv4/arp.c	Tue Aug 12 17:12:26 1997
@@ -1704,7 +1704,7 @@
 	skb->arp = 1;
 	skb->dev = dev;
 	skb->free = 1;
-	skb->protocol = htons (ETH_P_IP);
+	skb->protocol = htons (ETH_P_ARP);
 
 	/*
 	 *	Fill the device header for the ARP frame
@@ -1920,16 +1920,14 @@
 	if (tip != dev->pa_addr && net_alias_has(skb->dev)) 
 	{
 		/*
-		 *	net_alias_dev_rcv_sel32 returns main dev if it fails to found other.
+		 *	net_alias_dev_rx32 returns main dev if it fails to found other.
 		 */
-		if (ip_chk_addr(tip) == IS_MYADDR) {
-			dev = net_alias_dev_rcv_sel32(dev, AF_INET, sip, tip);
+		dev = net_alias_dev_rx32(dev, AF_INET, sip, tip);
 
-			if (dev->type != ntohs(arp->ar_hrd) || dev->flags & IFF_NOARP)
-			{
-				kfree_skb(skb, FREE_READ);
-				return 0;
-			}
+		if (dev->type != ntohs(arp->ar_hrd) || dev->flags & IFF_NOARP)
+		{
+			kfree_skb(skb, FREE_READ);
+			return 0;
 		}
 	}
 #endif
@@ -1975,7 +1973,7 @@
 				memcpy(ha, proxy_entry->ha, dev->addr_len);
 				arp_unlock();
 
-				rt = ip_rt_route(tip, 0);
+				rt = ip_rt_route(tip, 0, NULL);
 				if (rt  && rt->rt_dev != dev)
 					arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha,sha);
 				ip_rt_put(rt);
@@ -2048,7 +2046,7 @@
 		if (!dev)
 		{
 			struct rtable * rt;
-			rt = ip_rt_route(ip, 0);
+			rt = ip_rt_route(ip, 0, NULL);
 			if (!rt)
 				return -ENETUNREACH;
 			dev = rt->rt_dev;
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/icmp.c linux/net/ipv4/icmp.c
--- v2.0.30/linux/net/ipv4/icmp.c	Tue Apr  8 08:47:47 1997
+++ linux/net/ipv4/icmp.c	Wed Aug 13 17:15:54 1997
@@ -39,6 +39,7 @@
  *		Thomas Quinot	:	ICMP Dest Unreach codes up to 15 are
  *					valid (RFC 1812).
  *		Alan Cox	:	Spoofing and junk icmp protections.
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  *
  *
  * RFC1122 (Host Requirements -- Comm. Layer) Status:
@@ -748,7 +749,7 @@
 				break;
 		}
 		if(icmph->code>NR_ICMP_UNREACH)	/* Invalid type */
-			return;
+			goto flush_it;
 	}
 	
 	/*
@@ -831,8 +832,9 @@
 	NETDEBUG(printk(KERN_INFO "icmp: ICMP redirect ignored. dest = %lX, "
 	       "orig gw = %lX, \"new\" gw = %lX, device = %s.\n", ntohl(ip),
 		ntohl(source), ntohl(icmph->un.gateway), dev->name));
+		goto flush_it;
 	}
-#else	
+#endif
 	switch(icmph->code & 7) 
 	{
 		case ICMP_REDIR_NET:
@@ -870,7 +872,7 @@
 		default:
 			break;
   	}
-#endif  	
+
   	/*
   	 *	Discard the original packet
   	 */
@@ -992,8 +994,14 @@
  */
 
 /* This should work with the new hashes now. -DaveM */
-extern struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport);
-extern struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport);
+extern struct sock *tcp_v4_proxy_lookup(unsigned short num, unsigned long raddr,
+					unsigned short rnum, unsigned long laddr,
+					unsigned long paddr, unsigned short pnum,
+					struct device *dev);
+extern struct sock *udp_v4_proxy_lookup(unsigned short num, unsigned long raddr,
+					unsigned short rnum, unsigned long laddr,
+					unsigned long paddr, unsigned short pnum,
+					struct device *dev);
 
 int icmp_chkaddr(struct sk_buff *skb)
 {
@@ -1009,7 +1017,8 @@
 			{
 			struct tcphdr *th = (struct tcphdr *)(((unsigned char *)iph)+(iph->ihl<<2));
 
-			sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest);
+			sk = tcp_v4_proxy_lookup(th->source, iph->daddr, th->dest,
+						 iph->saddr, 0, 0, skb->dev);
 			if (!sk) return 0;
 			if (sk->saddr != iph->saddr) return 0;
 			if (sk->daddr != iph->daddr) return 0;
@@ -1023,7 +1032,8 @@
 			{
 			struct udphdr *uh = (struct udphdr *)(((unsigned char *)iph)+(iph->ihl<<2));
 
-			sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest);
+			sk = udp_v4_proxy_lookup(uh->source, iph->daddr, uh->dest,
+						 iph->saddr, 0, 0, skb->dev);
 			if (!sk) return 0;
 			if (sk->saddr != iph->saddr && ip_chk_addr(iph->saddr) != IS_MYADDR)
 				return 0;
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/ip_alias.c linux/net/ipv4/ip_alias.c
--- v2.0.30/linux/net/ipv4/ip_alias.c	Mon May 13 02:15:24 1996
+++ linux/net/ipv4/ip_alias.c	Tue Aug 12 11:30:25 1997
@@ -102,7 +102,7 @@
 	 *	net_alias module will check if returned device is main_dev's alias
 	 */
 
-	rt = ip_rt_route(addr, 0);
+	rt = ip_rt_route(addr, 0, NULL);
 	if(rt)
 	{
 		dev=rt->rt_dev;
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/ip_forward.c linux/net/ipv4/ip_forward.c
--- v2.0.30/linux/net/ipv4/ip_forward.c	Tue Apr  8 08:47:47 1997
+++ linux/net/ipv4/ip_forward.c	Tue Aug 12 11:30:25 1997
@@ -177,7 +177,7 @@
 		 * and give it to the IP sender for further processing.
 		 */
 
-		rt = ip_rt_route(target_addr, 0);
+		rt = ip_rt_route(target_addr, 0, NULL);
 
 		if (rt == NULL)
 		{
@@ -419,7 +419,7 @@
 			skb2->dev=dev2;
 #ifdef CONFIG_IP_MROUTE
 			if(is_frag&IPFWD_MULTITUNNEL)
-				ip_encap(skb,skb->len, dev2, raddr);
+				ip_encap(skb, 0, dev2, target_addr);
 			else
 			{
 #endif
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/ip_fragment.c linux/net/ipv4/ip_fragment.c
--- v2.0.30/linux/net/ipv4/ip_fragment.c	Fri Nov 22 01:58:34 1996
+++ linux/net/ipv4/ip_fragment.c	Tue Aug 12 11:30:25 1997
@@ -388,6 +388,8 @@
 		fp = fp->next;
 	}
 
+	skb->pkt_type = qp->fragments->skb->pkt_type;
+	skb->protocol = qp->fragments->skb->protocol;
 	/* We glued together all fragments, so remove the queue entry. */
 	ip_free(qp);
 
@@ -521,7 +523,7 @@
 	prev = NULL;
 	for(next = qp->fragments; next != NULL; next = next->next)
 	{
-		if (next->offset > offset)
+		if (next->offset >= offset)
 			break;	/* bingo! */
 		prev = next;
 	}
@@ -567,7 +569,7 @@
 			else
 				qp->fragments = tmp->next;
 
-			if (tfp->next != NULL)
+			if (tmp->next != NULL)
 				tmp->next->prev = tmp->prev;
 			
 			next=tfp;	/* We have killed the original next frame */
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/ip_fw.c linux/net/ipv4/ip_fw.c
--- v2.0.30/linux/net/ipv4/ip_fw.c	Tue Apr  8 08:47:47 1997
+++ linux/net/ipv4/ip_fw.c	Tue Aug 12 11:30:28 1997
@@ -206,9 +206,9 @@
 int ip_fw_chk(struct iphdr *ip, struct device *rif, __u16 *redirport, struct ip_fw *chain, int policy, int mode)
 {
 	struct ip_fw *f;
-	struct tcphdr		*tcp=(struct tcphdr *)((unsigned long *)ip+ip->ihl);
-	struct udphdr		*udp=(struct udphdr *)((unsigned long *)ip+ip->ihl);
-	struct icmphdr		*icmp=(struct icmphdr *)((unsigned long *)ip+ip->ihl);
+	struct tcphdr		*tcp=(struct tcphdr *)((__u32 *)ip+ip->ihl);
+	struct udphdr		*udp=(struct udphdr *)((__u32 *)ip+ip->ihl);
+	struct icmphdr		*icmp=(struct icmphdr *)((__u32 *)ip+ip->ihl);
 	__u32			src, dst;
 	__u16			src_port=0xFFFF, dst_port=0xFFFF, icmp_type=0xFF;
 	unsigned short		f_prt=0, prt;
@@ -895,6 +895,14 @@
 	struct ip_autofw * newaf;
 	init_timer(&af->timer);
 	newaf = kmalloc( sizeof(struct ip_autofw), GFP_ATOMIC );
+	if ( newaf == NULL ) 
+	{
+#ifdef DEBUG_IP_FIREWALL
+		printk("ip_autofw_add:  malloc said no\n");
+#endif
+		return( ENOMEM );
+	}
+
 	memcpy(newaf, af, sizeof(struct ip_autofw));
 	newaf->timer.data = (unsigned long) newaf;
 	newaf->timer.function = ip_autofw_expire;
@@ -1183,7 +1191,7 @@
 			ntohl(i->fw_dst.s_addr),ntohl(i->fw_dmsk.s_addr),
 			(i->fw_vianame)[0] ? i->fw_vianame : "-",
 			ntohl(i->fw_via.s_addr),i->fw_flg);
-		len+=sprintf(buffer+len,"%u %u %-9lu %-9lu",
+		len+=sprintf(buffer+len,"%u %u %-10lu %-10lu",
 			i->fw_nsp,i->fw_ndp, i->fw_pcnt,i->fw_bcnt);
 		for (p = 0; p < IP_FW_MAX_PORTS; p++)
 			len+=sprintf(buffer+len, " %u", i->fw_pts[p]);
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c
--- v2.0.30/linux/net/ipv4/ip_input.c	Tue Apr  8 08:47:47 1997
+++ linux/net/ipv4/ip_input.c	Tue Aug 12 17:12:40 1997
@@ -297,14 +297,13 @@
 
 	/*
 	 *	Try to select closest <src,dst> alias device, if any.
-	 *	net_alias_dev_rcv_sel32 returns main device if it 
+	 *	net_alias_dev_rx32 returns main device if it 
 	 *	fails to found other.
 	 */
 
 #ifdef CONFIG_NET_ALIAS
 	if (iph->daddr != skb->dev->pa_addr && net_alias_has(skb->dev)) {
-		if (ip_chk_addr(iph->daddr) == IS_MYADDR)
-			skb->dev = dev = net_alias_dev_rcv_sel32(skb->dev, AF_INET, iph->saddr, iph->daddr);
+		skb->dev = dev = net_alias_dev_rx32(skb->dev, AF_INET, iph->saddr, iph->daddr);
 	}
 #endif
 
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/ip_masq.c linux/net/ipv4/ip_masq.c
--- v2.0.30/linux/net/ipv4/ip_masq.c	Tue Apr  8 08:47:47 1997
+++ linux/net/ipv4/ip_masq.c	Tue Aug  5 10:44:26 1997
@@ -19,7 +19,7 @@
  *	Keith Owens		:	Keep control channels alive if any related data entries.
  *	Delian Delchev		:	Added support for ICMP requests and replys
  *	Nigel Metheringham	:	ICMP in ICMP handling, tidy ups, bug fixes, made ICMP optional
- *
+ *	Juan Jose Ciarlante	:	re-assign maddr if no packet received from outside
  *	
  */
 
@@ -632,13 +632,21 @@
         ms->app_data	   = NULL;
 	ms->control	   = NULL;
 
-        if (proto == IPPROTO_UDP)
+        if (proto == IPPROTO_UDP && !matchport)
                 ms->flags |= IP_MASQ_F_NO_DADDR;
         
         /* get masq address from rif */
         ms->maddr	   = dev->pa_addr;
+        /*
+         *	Setup new entry as not replied yet.
+         *	This flag will allow masq. addr (ms->maddr)
+         *	to follow forwarding interface address.
+         */
+        ms->flags         |= IP_MASQ_F_NO_REPLY;
 
-        for (ports_tried = 0; ports_tried < *free_ports_p; ports_tried++){
+        for (ports_tried = 0; 
+	     (*free_ports_p && (ports_tried <= (PORT_MASQ_END - PORT_MASQ_BEGIN)));
+	     ports_tried++){
                 save_flags(flags);
                 cli();
                 
@@ -659,7 +667,7 @@
                  */
                 
                 mst = ip_masq_getbym(proto, ms->maddr, ms->mport);
-                if (mst == NULL) {
+                if (mst == NULL || matchport) {
                         save_flags(flags);
                         cli();
                 
@@ -752,7 +760,27 @@
         ms = ip_masq_out_get(iph);
 	if (ms!=NULL) {
                 ip_masq_set_expire(ms,0);
-
+                
+                /*
+                 *	If sysctl !=0 and no pkt has been received yet
+                 *	in this tunnel and routing iface address has changed...
+                 *	 "You are welcome, diald".
+                 */
+                if ( sysctl_ip_dynaddr && ms->flags & IP_MASQ_F_NO_REPLY && dev->pa_addr != ms->maddr) {
+                        unsigned long flags;
+                        if (sysctl_ip_dynaddr > 1) {
+                                printk(KERN_INFO "ip_fw_masquerade(): 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);
+                }
+                
 		/*
 		 *      Set sport if not defined yet (e.g. ftp PASV).  Because
 		 *	masq entries are hashed on sport, unhash with old value
@@ -784,7 +812,6 @@
 	/*
 	 *	Nope, not found, create a new entry for it
 	 */
-	
 	if (ms==NULL)
 	{
 #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW
@@ -942,6 +969,27 @@
 		}
 		ip_masq_set_expire(ms, 0);
 		/* Rewrite source address */
+                
+                /*
+                 *	If sysctl !=0 and no pkt has been received yet
+                 *	in this tunnel and routing iface address has changed...
+                 *	 "You are welcome, diald".
+                 */
+                if ( sysctl_ip_dynaddr && ms->flags & IP_MASQ_F_NO_REPLY && dev->pa_addr != ms->maddr) {
+                        unsigned long flags;
+#ifdef DEBUG_CONFIG_IP_MASQUERADE
+                        printk(KERN_INFO "ip_fw_masq_icmp(): change masq.addr %s",
+                               in_ntoa(ms->maddr));
+                        printk("-> %s\n", in_ntoa(dev->pa_addr));
+#endif
+                        save_flags(flags);
+                        cli();
+                        ip_masq_unhash(ms);
+                        ms->maddr = dev->pa_addr;
+                        ip_masq_hash(ms);
+                        restore_flags(flags);
+                }
+                
 		iph->saddr = ms->maddr;
 		ip_send_check(iph);
 		/* Rewrite port (id) */
@@ -1042,7 +1090,7 @@
 	if (ip_compute_csum((unsigned char *) icmph, len)) 
 	{
 		/* Failed checksum! */
-		printk(KERN_INFO "MASQ: forward ICMP: failed checksum from %s!\n", 
+		printk(KERN_DEBUG "MASQ: forward ICMP: failed checksum from %s!\n", 
 		       in_ntoa(iph->saddr));
 		return(-1);
 	}
@@ -1133,6 +1181,11 @@
 			return 0;
 
 		ip_masq_set_expire(ms,0);
+                
+                /*
+                 *	got reply, so clear flag
+                 */
+                ms->flags &= ~IP_MASQ_F_NO_REPLY;
 
 		/* Reset source address */
 		iph->daddr = ms->saddr;
@@ -1238,7 +1291,7 @@
 	if (ip_compute_csum((unsigned char *) icmph, len)) 
 	{
 		/* Failed checksum! */
-		printk(KERN_INFO "MASQ: reverse ICMP: failed checksum from %s!\n", 
+		printk(KERN_DEBUG "MASQ: reverse ICMP: failed checksum from %s!\n", 
 		       in_ntoa(iph->saddr));
 		return(-1);
 	}
@@ -1338,7 +1391,7 @@
 				if (csum_tcpudp_magic(iph->saddr, iph->daddr, len,
 						      iph->protocol, skb->csum))
 				{
-					printk(KERN_INFO "MASQ: failed TCP/UDP checksum from %s!\n", 
+					printk(KERN_DEBUG "MASQ: failed TCP/UDP checksum from %s!\n", 
 					       in_ntoa(iph->saddr));
 					return -1;
 				}
@@ -1364,20 +1417,27 @@
         ms = ip_masq_in_get(iph);
 
 #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW 
-        if (ms == NULL && (af=ip_autofw_check_range(iph->saddr, portptr[1],
-						    iph->protocol, 0))) {
+
+        if (ms == NULL && (af=ip_autofw_check_range(iph->saddr, portptr[1], iph->protocol, 0))) 
+	{
+#ifdef DEBUG_CONFIG_IP_MASQUERADE
+		printk("ip_autofw_check_range\n");
+#endif
         	ms = ip_masq_new_enh(dev, iph->protocol,
         			     af->where, portptr[1],
         			     iph->saddr, portptr[0],
         			     0,
         			     portptr[1]);
         }
-        if ( ms == NULL && (af=ip_autofw_check_port(portptr[1], 
-						    iph->protocol)) ) {
+        if ( ms == NULL && (af=ip_autofw_check_port(portptr[1], iph->protocol)) ) 
+	{
+#ifdef DEBUG_CONFIG_IP_MASQUERADE
+		printk("ip_autofw_check_port\n");
+#endif
         	ms = ip_masq_new_enh(dev, iph->protocol,
         			     af->where, htons(af->hidden),
         			     iph->saddr, portptr[0],
-        			     0,
+        			     IP_MASQ_F_AFW_PORT,
         			     htons(af->visible));
         }
 #endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
@@ -1392,6 +1452,11 @@
 		ip_masq_set_expire(ms,0);
 
                 /*
+                 *	got reply, so clear flag
+                 */
+                ms->flags &= ~IP_MASQ_F_NO_REPLY;
+                
+                /*
                  *	Set dport if not defined yet.
                  */
 
@@ -1543,9 +1608,15 @@
 	
 	if (offset < 128) 
 	{
+#ifdef CONFIG_IP_MASQUERADE_ICMP
+		sprintf(temp,
+			"Prc FromIP   FPrt ToIP     TPrt Masq Init-seq  Delta PDelta Expires (free=%d,%d,%d)",
+			ip_masq_free_ports[0], ip_masq_free_ports[1], ip_masq_free_ports[2]); 
+#else /* !defined(CONFIG_IP_MASQUERADE_ICMP) */
 		sprintf(temp,
 			"Prc FromIP   FPrt ToIP     TPrt Masq Init-seq  Delta PDelta Expires (free=%d,%d)",
 			ip_masq_free_ports[0], ip_masq_free_ports[1]); 
+#endif /* CONFIG_IP_MASQUERADE_ICMP */
 		len = sprintf(buffer, "%-127s\n", temp);
 	}
 	pos = 128;
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/ip_masq_app.c linux/net/ipv4/ip_masq_app.c
--- v2.0.30/linux/net/ipv4/ip_masq_app.c	Fri Jun 28 08:49:55 1996
+++ linux/net/ipv4/ip_masq_app.c	Tue Aug 12 11:30:33 1997
@@ -15,6 +15,7 @@
  * Fixes:
  *	JJC			: Implemented also input pkt hook
  *	Miquel van Smoorenburg	: Copy more stuff when resizing skb
+ *	Harald Hoyer/James R. Leu: Additional ipautofw support
  *
  *
  * FIXME:
@@ -186,7 +187,11 @@
 struct ip_masq_app * ip_masq_bind_app(struct ip_masq *ms)
 {
         struct ip_masq_app * mapp;
-        mapp = ip_masq_app_get(ms->protocol, ms->dport);
+	mapp = ip_masq_app_get(ms->protocol, ms->dport);
+#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW
+	if (mapp == NULL)
+		mapp = ip_masq_app_get(ms->protocol, ms->sport);
+#endif
         if (mapp != NULL) {
                 /*
                  *	don't allow binding if already bound
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/ip_masq_cuseeme.c linux/net/ipv4/ip_masq_cuseeme.c
--- v2.0.30/linux/net/ipv4/ip_masq_cuseeme.c	Tue Apr  8 08:47:47 1997
+++ linux/net/ipv4/ip_masq_cuseeme.c	Tue Aug 12 11:30:35 1997
@@ -2,7 +2,7 @@
  *		IP_MASQ_FTP CUSeeMe masquerading module
  *
  *
- * Version:	@(#)ip_masq_cuseeme.c 0.02   07/23/96
+ * Version:	@(#)$Id: ip_masq_cuseeme.c,v 1.1.2.2 1997/05/29 18:27:41 davem Exp $
  *
  * Author:	Richard Lynch
  *		
@@ -11,7 +11,13 @@
  *	Richard Lynch     	:	Updated patch to conform to new module
  *					specifications
  *	Nigel Metheringham	:	Multiple port support
- *	
+ *	Michael Owings		:	Fixed broken init code
+ *					Added code to update inbound
+ *					packets with correct local addresses.
+ *					Fixes audio and "chat" problems
+ *					Thanx to the CU-SeeMe Consortium for
+ *					technical docs
+ *
  *
  *
  *	This program is free software; you can redistribute it and/or
@@ -47,7 +53,38 @@
 #define DEBUG_CONFIG_IP_MASQ_CUSEEME 0
 #endif
 
-/* 
+#pragma pack(1)
+/* CU-SeeMe Data Header */
+typedef struct {
+	u_short 	dest_family;
+	u_short 	dest_port;
+	u_long  	dest_addr;
+	short 		family;
+	u_short 	port;
+	u_long 		addr;
+	u_long 		seq;
+	u_short 	msg;
+	u_short		data_type;
+	u_short		packet_len;
+} cu_header;
+
+/* Open Continue Header */
+typedef struct	{
+	cu_header	cu_head;
+	u_short 	client_count; /* Number of client info structs */
+	u_long		seq_no;
+	char		user_name[20];
+	char		stuff[4]; /* flags,  version stuff,  etc */
+}oc_header;
+
+/* client info structures */
+typedef struct {
+	u_long		address; /* Client address */
+	char	       	stuff[8]; /* Flags, pruning bitfield,  packet counts etc */
+} client_info;
+#pragma pack()
+
+/*
  * List of ports (up to MAX_MASQ_APP_PORTS) to be handled by helper
  * First port is set to the default port.
  */
@@ -74,20 +111,61 @@
 	struct sk_buff *skb = *skb_p;
 	struct iphdr *iph = skb->h.iph;
 	struct udphdr *uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]);
-	struct cu_header {
-		char dest[8];
-		short family;
-		u_short port;
-		u_long addr;
-	} *cu_head;
+	cu_header *cu_head;
 	char *data=(char *)&uh[1];
-	
-	if (skb->len - ((unsigned char *) data - skb->h.raw) > 16)
+
+	if (skb->len - ((unsigned char *) data - skb->h.raw) >= sizeof(cu_header))
 	{
-		cu_head         = (struct cu_header *) data;
-/*		printk("CUSeeMe orig: %lX:%X\n",ntohl(cu_head->addr),ntohs(cu_head->port));*/
-		cu_head->port   = ms->mport;
+		cu_head         = (cu_header *) data;
+		/* cu_head->port   = ms->mport; */
+	        if( cu_head->addr )
 		cu_head->addr = (u_long) dev->pa_addr;
+#if DEBUG_CONFIG_IP_MASQ_CUSEEME	  
+	        if(ntohs(cu_head->data_type) == 257)
+	           printk(KERN_INFO "Sending talk packet!\n");
+#endif	  
+	}
+	return 0;
+}
+
+int
+masq_cuseeme_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, struct device *dev)
+{
+	struct sk_buff *skb = *skb_p;
+	struct iphdr *iph = skb->h.iph;
+	struct udphdr *uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]);
+	cu_header *cu_head;
+	oc_header	*oc;
+	client_info	*ci;
+	char *data=(char *)&uh[1];
+	u_short len = skb->len - ((unsigned char *) data - skb->h.raw);
+	int		i, off;
+
+	if (len >= sizeof(cu_header))
+	{
+		cu_head         = (cu_header *) data;
+		if(cu_head->dest_addr) /* Correct destination address */
+			cu_head->dest_addr = (u_long) ms->saddr;
+		if(ntohs(cu_head->data_type)==101 && len > sizeof(oc_header))
+		{
+			oc = (oc_header * ) data;
+			/* Spin (grovel) thru client_info structs till we find our own */
+		        off=sizeof(oc_header);
+			for(i=0;
+			    (i < oc->client_count && off+sizeof(client_info) <= len);
+			    i++)		    
+			{
+			        ci=(client_info *)(data+off);
+				if(ci->address==(u_long) dev->pa_addr)
+				{
+				        /* Update w/ our real ip address and exit */
+					ci->address = (u_long) ms->saddr;
+					break;
+				}
+			        else
+				   off+=sizeof(client_info);
+			}
+		}
 	}
 	return 0;
 }
@@ -100,7 +178,7 @@
         masq_cuseeme_init_1,	/* ip_masq_init_1 */
         masq_cuseeme_done_1,	/* ip_masq_done_1 */
         masq_cuseeme_out,	/* pkt_out */
-        NULL                    /* pkt_in */
+        masq_cuseeme_in    	/* pkt_in */
 };
 
 
@@ -119,12 +197,12 @@
 				return -ENOMEM;
 			memcpy(masq_incarnations[i], &ip_masq_cuseeme, sizeof(struct ip_masq_app));
 			if ((j = register_ip_masq_app(masq_incarnations[i], 
-						      IPPROTO_TCP, 
+						      IPPROTO_UDP,
 						      ports[i]))) {
 				return j;
 			}
 #if DEBUG_CONFIG_IP_MASQ_CUSEEME
-			printk("CuSeeMe: loaded support on port[%d] = %d\n",
+			printk(KERN_INFO "CuSeeMe: loaded support on port[%d] = %d\n",
 			       i, ports[i]);
 #endif
 		} else {
@@ -152,7 +230,7 @@
 				kfree(masq_incarnations[i]);
 				masq_incarnations[i] = NULL;
 #if DEBUG_CONFIG_IP_MASQ_CUSEEME
-				printk("CuSeeMe: unloaded support on port[%d] = %d\n",
+				printk(KERN_INFO "CuSeeMe: unloaded support on port[%d] = %d\n",
 				       i, ports[i]);
 #endif
 			}
@@ -174,7 +252,7 @@
 void cleanup_module(void)
 {
         if (ip_masq_cuseeme_done() != 0)
-                printk("ip_masq_cuseeme: can't remove module");
+                printk(KERN_INFO "ip_masq_cuseeme: can't remove module");
 }
 
 #endif /* MODULE */
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- v2.0.30/linux/net/ipv4/ip_output.c	Tue Oct 29 17:42:42 1996
+++ linux/net/ipv4/ip_output.c	Tue Aug 12 11:30:35 1997
@@ -26,6 +26,8 @@
  *		Alexander Demenshin:	Missing sk/skb free in ip_queue_xmit
  *					(in case if packet not accepted by
  *					output firewall rules)
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
+ *		Juan Jose Ciarlante:	sk/skb source address rewriting
  */
 
 #include <asm/segment.h>
@@ -66,6 +68,78 @@
 #include <net/netlink.h>
 
 /*
+ *	Allows dynamic re-writing of packet's addresses.
+ *	if value > 1, be verbose about addr rewriting.
+ *	Currently implemented:
+ *		tcp_output.c   if sk->state!=TCP_SYN_SENT
+ *		ip_masq.c      if no packet has been received by tunnel
+ */
+int sysctl_ip_dynaddr = 0;
+
+/*
+ *	Very Promisc source address re-assignation.
+ *	ONLY acceptable if socket is NOT connected yet.
+ *      Caller already checked sysctl_ip_dynaddr != 0 and consistent sk->state
+ *	 (TCP_SYN_SENT for tcp, udp-connect sockets are set TCP_ESTABLISHED)
+ */
+
+int ip_rewrite_addrs (struct sock *sk, struct sk_buff *skb, struct device *dev)
+{
+	u32 new_saddr = dev->pa_addr;
+        struct iphdr *iph;
+        
+        /*
+         *	Be carefull: new_saddr must be !0
+         */
+        if (!new_saddr) {
+                printk(KERN_WARNING "ip_rewrite_addrs(): NULL device \"%s\" addr\n",
+                       dev->name);
+                return 0;
+        }
+        
+        /*
+         *	Ouch!, this should not happen.
+         */
+        if (!sk->saddr || !sk->rcv_saddr) {
+                printk(KERN_WARNING "ip_rewrite_addrs(): not valid sock addrs: saddr=%08lX rcv_saddr=%08lX",
+                       ntohl(sk->saddr), ntohl(sk->rcv_saddr));
+                return 0;
+        }
+        
+        /*
+         *	Be verbose if sysctl value > 1
+         */
+        if (sysctl_ip_dynaddr > 1) {
+                printk(KERN_INFO "ip_rewrite_addrs(): shifting saddr from %s",
+                       in_ntoa(skb->saddr));
+                printk(" to %s\n", in_ntoa(new_saddr));
+        }
+        
+        iph = skb->ip_hdr;
+
+        if (new_saddr != iph->saddr) {
+                iph->saddr = new_saddr;
+                skb->saddr = new_saddr;
+                ip_send_check(iph);
+        } else if (sysctl_ip_dynaddr > 1) {
+                printk(KERN_WARNING "ip_rewrite_addrs(): skb already changed (???).\n");
+                return 0;
+        }
+        
+        /*
+         *	Maybe whe are in a skb chain loop and socket address has
+         *	yet been 'damaged'.
+         */
+        if (new_saddr != sk->saddr) {
+                sk->saddr = new_saddr;
+                sk->rcv_saddr = new_saddr;
+                sk->prot->rehash(sk);
+        } else if (sysctl_ip_dynaddr > 1)
+                printk(KERN_NOTICE "ip_rewrite_addrs(): no change needed for sock\n");
+        return 1;
+}
+
+/*
  *	Loop a packet back to the sender.
  */
  
@@ -218,7 +292,7 @@
 #endif
 	if (rp)
 	{
-		rt = ip_check_route(rp, daddr, skb->localroute);
+		rt = ip_check_route(rp, daddr, skb->localroute, *dev);
 		/*
 		 * If rp != NULL rt_put following below should not
 		 * release route, so that...
@@ -227,7 +301,7 @@
 			atomic_inc(&rt->rt_refcnt);
 	}
 	else
-		rt = ip_rt_route(daddr, skb->localroute);
+		rt = ip_rt_route(daddr, skb->localroute, *dev);
 
 
 	if (*dev == NULL)
@@ -590,7 +664,7 @@
 #endif	
 		rt = ip_check_route(&sk->ip_route_cache, daddr,
 				    sk->localroute || (flags&MSG_DONTROUTE) ||
-				    (opt && opt->is_strictroute));
+				    (opt && opt->is_strictroute), sk->bound_device);
 		if (rt == NULL) 
 		{
 			ip_statistics.IpOutNoRoutes++;
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/ip_sockglue.c linux/net/ipv4/ip_sockglue.c
--- v2.0.30/linux/net/ipv4/ip_sockglue.c	Tue Apr  8 08:47:47 1997
+++ linux/net/ipv4/ip_sockglue.c	Tue Aug 12 11:30:35 1997
@@ -12,6 +12,7 @@
  *		Martin Mares	:	TOS setting fixed.
  *		Alan Cox	:	Fixed a couple of oopses in Martin's 
  *					TOS tweaks.
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  */
 
 #include <linux/config.h>
@@ -293,7 +294,7 @@
 				/*
 				 *	Not set so scan.
 				 */
-				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL)
+				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0,sk->bound_device))!=NULL)
 				{
 					dev=rt->rt_dev;
 					atomic_dec(&rt->rt_use);
@@ -345,7 +346,7 @@
  
 			if(mreq.imr_interface.s_addr==INADDR_ANY) 
 			{
-				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL)
+				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0,sk->bound_device))!=NULL)
 			        {
 					dev=rt->rt_dev;
 					atomic_dec(&rt->rt_use);
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/ipip.c linux/net/ipv4/ipip.c
--- v2.0.30/linux/net/ipv4/ipip.c	Mon May 13 02:15:24 1996
+++ linux/net/ipv4/ipip.c	Sun Aug  3 14:02:55 1997
@@ -60,6 +60,7 @@
 	 *	Discard the original IP header
 	 */
 	 
+	skb->mac.raw = skb->data;
 	skb_pull(skb, ((struct iphdr *)skb->data)->ihl<<2);
 	
 	/*
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/packet.c linux/net/ipv4/packet.c
--- v2.0.30/linux/net/ipv4/packet.c	Tue Apr  8 08:47:47 1997
+++ linux/net/ipv4/packet.c	Tue Aug 12 14:15:56 1997
@@ -169,10 +169,11 @@
 	 *	raw protocol and you must do your own fragmentation at this level.
 	 */
 	 
-	if(len>dev->mtu+dev->hard_header_len)
+	if ((dev->flags & IFF_SOFTHEADERS && len>dev->mtu)
+	|| len>dev->mtu+dev->hard_header_len)
   		return -EMSGSIZE;
 
-	skb = sock_wmalloc(sk, len, 0, GFP_KERNEL);
+	skb = sock_wmalloc(sk, len+dev->hard_header_len, 0, GFP_KERNEL);
 
 	/*
 	 *	If the write buffer is full, then tough. At this level the user gets to
@@ -191,6 +192,16 @@
 	 
 	skb->sk = sk;
 	skb->free = 1;
+	if (dev->flags & IFF_SOFTHEADERS) {
+        	/* Do the hard header calls for drivers that must set the
+		 * headers at transmission time by themselves. PPP and ISDN
+	 	 * are the notable offenders here. A proper fix requires some
+         	 * changes at the device driver level, but that is a 2.1 issue.
+         	 */
+        	skb_reserve(skb,dev->hard_header_len);
+		if (dev->hard_header)
+			dev->hard_header(skb,dev,ETH_P_IP,NULL,NULL,len);
+	}
 	memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
 	skb->arp = 1;		/* No ARP needs doing on this (complete) frame */
 	skb->protocol = proto;
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/rarp.c linux/net/ipv4/rarp.c
--- v2.0.30/linux/net/ipv4/rarp.c	Tue Oct 29 17:42:42 1996
+++ linux/net/ipv4/rarp.c	Tue Aug 12 11:30:35 1997
@@ -354,7 +354,7 @@
  *	Is it reachable directly ?
  */
   
-	rt = ip_rt_route(ip, 0);
+	rt = ip_rt_route(ip, 0, NULL);
 	if (rt == NULL)
 		return -ENETUNREACH;
 	dev = rt->rt_dev;
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v2.0.30/linux/net/ipv4/route.c	Tue Apr  8 08:47:47 1997
+++ linux/net/ipv4/route.c	Thu Sep  4 11:18:06 1997
@@ -42,6 +42,9 @@
  *		Bjorn Ekwall	:	Kerneld route support.
  *		Alan Cox	:	Multicast fixed (I hope)
  * 		Pavel Krauz	:	Limited broadcast fixed
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
+ *		Andi Kleen	:	Don't send multicast addresses to
+ *					kerneld.	
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -127,9 +130,9 @@
  * Backlogging.
  */
 
-#define RT_BH_REDIRECT		0
-#define RT_BH_GARBAGE_COLLECT 	1
-#define RT_BH_FREE	 	2
+#define RT_BH_REDIRECT		1
+#define RT_BH_GARBAGE_COLLECT 	2
+#define RT_BH_FREE	 	4
 
 struct rt_req
 {
@@ -203,6 +206,7 @@
 			fi->fib_prev->fib_next = fi->fib_next;
 		if (fi == fib_info_list)
 			fib_info_list = fi->fib_next;
+		kfree_s(fi, sizeof(struct fib_info));
 	}
 	kfree_s(f, sizeof(struct fib_node));
 }
@@ -225,10 +229,9 @@
 		
 		for ( ; f; f = f->fib_next)
 		{
-			if ((dst ^ f->fib_dst) & fz->fz_mask)
+			if (((dst ^ f->fib_dst) & fz->fz_mask) ||
+			    (f->fib_info->fib_flags & RTF_GATEWAY))
 				continue;
-			if (f->fib_info->fib_flags & RTF_GATEWAY)
-				return NULL;
 			return f;
 		}
 	}
@@ -247,9 +250,11 @@
  *	  Host 193.233.7.129 is locally unreachable,
  *	  but old (<=1.3.37) code will send packets destined for it to eth1.
  *
+ * Calling routine can specify a particular interface by setting dev.  If dev==NULL,
+ * any interface will do.
  */
 
-static struct fib_node * fib_lookup_local(__u32 dst)
+static struct fib_node * fib_lookup_local(__u32 dst, struct device *dev)
 {
 	struct fib_zone * fz;
 	struct fib_node * f;
@@ -267,6 +272,8 @@
 		{
 			if ((dst ^ f->fib_dst) & fz->fz_mask)
 				continue;
+			if ( (dev != NULL) && (dev != f->fib_info->fib_dev) )
+				continue;
 			if (!(f->fib_info->fib_flags & RTF_GATEWAY))
 				return f;
 			longest_match_found = 1;
@@ -289,7 +296,7 @@
  *		route add -host 193.233.7.255 eth0
  */
 
-static struct fib_node * fib_lookup(__u32 dst)
+static struct fib_node * fib_lookup(__u32 dst, struct device *dev)
 {
 	struct fib_zone * fz;
 	struct fib_node * f;
@@ -305,6 +312,8 @@
 		{
 			if ((dst ^ f->fib_dst) & fz->fz_mask)
 				continue;
+			if ( (dev != NULL) && (dev != f->fib_info->fib_dev) )
+				continue;
 			return f;
 		}
 	}
@@ -915,6 +924,8 @@
 {
 	struct rtable *rt, **rtp;
 
+	ip_rt_bh_mask &= ~RT_BH_FREE;
+
 	rtp = &rt_free_queue;
 
 	while ((rt = *rtp) != NULL)
@@ -1254,7 +1265,7 @@
 	struct rt_req * rtr;
 	struct rtable * rt;
 
-	rt = ip_rt_route(dst, 0);
+	rt = ip_rt_route(dst, 0, NULL);
 	if (!rt)
 		return;
 
@@ -1323,7 +1334,7 @@
 		if (rth->rt_gateway != daddr)
 		{
 			ip_rt_fast_unlock();
-			rtg = ip_rt_route(rth->rt_gateway, 0);
+			rtg = ip_rt_route(rth->rt_gateway, 0, NULL);
 			ip_rt_fast_lock();
 		}
 
@@ -1395,7 +1406,7 @@
    
  */
 
-struct rtable * ip_rt_slow_route (__u32 daddr, int local)
+struct rtable * ip_rt_slow_route (__u32 daddr, int local, struct device *dev)
 {
 	unsigned hash = ip_rt_hash_code(daddr)^local;
 	struct rtable * rth;
@@ -1415,9 +1426,9 @@
 	}
 
 	if (local)
-		f = fib_lookup_local(daddr);
+		f = fib_lookup_local(daddr, dev);
 	else
-		f = fib_lookup (daddr);
+		f = fib_lookup (daddr, dev);
 
 	if (f)
 	{
@@ -1436,6 +1447,8 @@
 		ip_rt_unlock();
 		kfree_s(rth, sizeof(struct rtable));
 #ifdef CONFIG_KERNELD		
+		if (MULTICAST(daddr)) 
+			return NULL; 
 		daddr=ntohl(daddr);
 		sprintf(wanted_route, "%d.%d.%d.%d",
 			(int)(daddr >> 24) & 0xff, (int)(daddr >> 16) & 0xff,
@@ -1490,7 +1503,15 @@
 		rth->rt_gateway = rth->rt_dst;
 
 	if (ip_rt_lock == 1)
-		rt_cache_add(hash, rth);
+	{
+		/* Don't add this to the rt_cache if a device was specified,
+		 * because we might have skipped better routes which didn't
+		 * point at the right device. */
+		if (dev != NULL)
+			rth->rt_flags |= RTF_NOTCACHED;
+		else
+			rt_cache_add(hash, rth);
+	}
 	else
 	{
 		rt_free(rth);
@@ -1507,9 +1528,14 @@
 {
 	if (rt)
 		atomic_dec(&rt->rt_refcnt);
+	
+	/* If this rtable entry is not in the cache, we'd better free it once the
+	 * refcnt goes to zero, because nobody else will... */
+	if ( rt && (rt->rt_flags & RTF_NOTCACHED) && (!rt->rt_refcnt) )
+		rt_free(rt);
 }
 
-struct rtable * ip_rt_route(__u32 daddr, int local)
+struct rtable * ip_rt_route(__u32 daddr, int local, struct device *dev)
 {
 	struct rtable * rth;
 
@@ -1517,7 +1543,8 @@
 
 	for (rth=ip_rt_hash_table[ip_rt_hash_code(daddr)^local]; rth; rth=rth->rt_next)
 	{
-		if (rth->rt_dst == daddr)
+		/* If a network device is specified, make sure this route points to it. */
+		if ( (rth->rt_dst == daddr) && ((dev==NULL) || (dev==rth->rt_dev)) )
 		{
 			rth->rt_lastuse = jiffies;
 			atomic_inc(&rth->rt_use);
@@ -1526,7 +1553,7 @@
 			return rth;
 		}
 	}
-	return ip_rt_slow_route (daddr, local);
+	return ip_rt_slow_route (daddr, local, dev);
 }
 
 /*
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/sysctl_net_ipv4.c linux/net/ipv4/sysctl_net_ipv4.c
--- v2.0.30/linux/net/ipv4/sysctl_net_ipv4.c	Tue Apr  8 08:47:47 1997
+++ linux/net/ipv4/sysctl_net_ipv4.c	Tue Aug 12 11:30:35 1997
@@ -18,7 +18,8 @@
 extern int sysctl_arp_confirm_interval;
 extern int sysctl_arp_confirm_timeout;
 
-extern int sysctl_ip_forward; 
+extern int sysctl_ip_forward;
+extern int sysctl_ip_dynaddr;
 static int proc_doipforward(ctl_table *ctl, int write, struct file *filp,
 			    void *buffer, size_t *lenp) 
 {
@@ -58,5 +59,7 @@
          &proc_dointvec},
 	{NET_IPV4_FORWARD, "ip_forward", &sysctl_ip_forward, sizeof(int),
 	 0644, NULL, &proc_doipforward },
+        {NET_IPV4_DYNADDR, "ip_dynaddr",
+         &sysctl_ip_dynaddr, sizeof(int), 0644, NULL, &proc_dointvec},
 	{0}
 };
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v2.0.30/linux/net/ipv4/tcp.c	Tue Apr  8 08:47:47 1997
+++ linux/net/ipv4/tcp.c	Sun Aug 17 13:35:10 1997
@@ -205,6 +205,7 @@
  *		Theodore Ts'o	:	Do secure TCP sequence numbers.
  *		David S. Miller	:	New socket lookup architecture for ISS.
  *					This code is dedicated to John Dyson.
+ *		Elliot Poger	:	Added support for SO_BINDTODEVICE.
  *					
  * To Fix:
  *		Fast path the code. Two things here - fix the window calculation
@@ -471,6 +472,11 @@
 			unsigned char state = sk2->state;
 			int sk2_reuse = sk2->reuse;
 
+			/* Two sockets can be bound to the same port if they're
+			 * bound to different interfaces... */
+			if (sk->bound_device != sk2->bound_device)
+				continue;
+
 			if(!sk2->rcv_saddr || !sk->rcv_saddr) {
 				if((!sk2_reuse)			||
 				   (!sk_reuse)			||
@@ -527,45 +533,56 @@
 	end = i + TCP_BHTABLE_SIZE;
 	bc = binding_contour;
 	do {
-		struct sock *sk = tcp_bound_hash[tcp_bhashfn(i)];
+		struct sock *sk = tcp_bound_hash[i&(TCP_BHTABLE_SIZE-1)];
 		if(!sk) {
-			retval = (start + i);
-			start  = (retval + 1);
+			/* find the smallest value no smaller than start
+			 * that has this hash value.
+			 */
+			retval = tcp_bhashnext(start-1,i&(TCP_BHTABLE_SIZE-1));
 
 			/* Check for decreasing load. */
-			if(bc != 0)
+			if (bc != 0)
 				binding_contour = 0;
 			goto done;
 		} else {
 			int j = 0;
-			do { sk = sk->bind_next; } while(++j < size && sk);
-			if(j < size) {
-				best = (start + i);
+			do { sk = sk->bind_next; } while (++j < size && sk);
+			if (j < size) {
+				best = i&(TCP_BHTABLE_SIZE-1);
 				size = j;
-				if(bc && size <= bc) {
-					start = best + 1;
+				if (bc && size <= bc) {
+					i = best;
 					goto verify;
 				}
 			}
 		}
 	} while(++i != end);
+	i = best;
 
 	/* Socket load is increasing, adjust our load average. */
 	binding_contour = size;
 verify:
-	if(size < binding_contour)
+	if (size < binding_contour)
 		binding_contour = size;
 
-	if(best > 32767)
-		best -= (32768 - PROT_SOCK);
+	retval = tcp_bhashnext(start-1,i);
 
-	while(tcp_lport_inuse(best))
-		best += TCP_BHTABLE_SIZE;
-	retval = best;
-done:
-	if(start > 32767)
-		start -= (32768 - PROT_SOCK);
+	best = retval;	/* mark the starting point to avoid infinite loops */
+	while(tcp_lport_inuse(retval)) {
+		retval = tcp_bhashnext(retval,i);
+		if (retval > 32767)	/* Upper bound */
+			retval = tcp_bhashnext(PROT_SOCK,i);
+		if (retval == best) {
+			/* This hash chain is full. No answer. */
+			retval = 0;
+			break;
+		}
+	}
 
+done:
+	start = (retval + 1);
+	if (start > 32767 || start < PROT_SOCK)
+		start = PROT_SOCK;
 	SOCKHASH_UNLOCK();
 
 	return retval;
@@ -731,6 +748,7 @@
 		 * here as well.
 		 */
 		sk->cong_window = 1;
+		sk->cong_count = 0;
 		sk->high_seq = sk->sent_seq;
 		return;
 	}
@@ -1254,7 +1272,10 @@
 					sk->write_seq += copy;
 					seglen -= copy;
 				}
-				if (tcp_size >= sk->mss || (flags & MSG_OOB) || !sk->packets_out)
+				/* If we have a full packet or a new OOB
+				 * message, we have to force this packet out.
+				 */
+				if (tcp_size >= sk->mss || (flags & MSG_OOB))
 					tcp_send_skb(sk, skb);
 				else
 					tcp_enqueue_partial(skb, sk);
@@ -1290,8 +1311,14 @@
 
 			delay = 0;
 			tmp = copy + sk->prot->max_header + 15;
-			if (copy < sk->mss && !(flags & MSG_OOB) && sk->packets_out)
-			{
+			/* If won't fill the current packet, and it's not an OOB message,
+			 * then we might want to delay to allow data in the later parts
+			 * of iov to fill this packet out. Note that if we aren't
+			 * Nagling or there are no packets currently out then the top
+			 * level code in tcp_sendmsg() will force any partial packets out
+			 * after we finish building the largest packets this write allows.
+			 */
+			if (copy < sk->mss && !(flags & MSG_OOB)) {
 				tmp = tmp - copy + sk->mtu + 128;
 				delay = 1;
 			}
@@ -1838,7 +1865,10 @@
 		case TCP_CLOSE:
 		case TCP_LISTEN:
 			break;
-		case TCP_LAST_ACK:	/* Could have shutdown() then close()!*/
+		case TCP_LAST_ACK:	/* Could have shutdown() then close().
+					   Be careful not to send double fin. */
+			ns=TCP_LAST_ACK;
+			break;
 		case TCP_CLOSE_WAIT:	/* They have FIN'd us. We send our FIN and
 					   wait only for the ACK */
 			ns=TCP_LAST_ACK;
@@ -2014,7 +2044,7 @@
 
 	/* Now that the socket is dead, if we are in the FIN_WAIT2 state
 	 * we may need to set up a timer.
-         */
+	 */
 	if (sk->state==TCP_FIN_WAIT2)
 	{
 		int timer_active=del_timer(&sk->timer);
@@ -2024,8 +2054,8 @@
 			tcp_reset_msl_timer(sk, TIME_CLOSE, TCP_FIN_TIMEOUT);
 	}
 
-	release_sock(sk);
 	sk->dead = 1;
+	release_sock(sk);
 
 	if(sk->state == TCP_CLOSE)
 		tcp_v4_unhash(sk);
@@ -2198,6 +2228,9 @@
 	buff->free = 0;
 	buff->localroute = sk->localroute;
 
+	/* If this socket is bound to a particular device, make sure we use it. */
+	dev = sk->bound_device;
+
 	/*
 	 *	Put in the IP header and routing stuff.
 	 */
@@ -2252,10 +2285,14 @@
 	 *	but not bigger than device MTU
 	 */
 
-	if(sk->mtu <32)
-		sk->mtu = 32;	/* Sanity limit */
-
 	sk->mtu = min(sk->mtu, dev->mtu - sizeof(struct iphdr) - sizeof(struct tcphdr));
+
+	/* Must check it here, just to be absolutely safe.  If we end up
+	 * with an sk->mtu of zero, we can thus end up with an sk->mss
+	 * of zero, which causes us to bomb out in tcp_do_sendmsg. -DaveM
+	 */
+	if(sk->mtu < 32)
+		sk->mtu = 32;	/* Sanity limit */
 
 	/*
 	 *	Put in the TCP options to say MTU.
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.0.30/linux/net/ipv4/tcp_input.c	Tue Apr  8 08:47:47 1997
+++ linux/net/ipv4/tcp_input.c	Sun Aug 17 13:27:49 1997
@@ -33,6 +33,7 @@
  *				:	with SYN flooding attacks.
  *		David S. Miller	:	New socket lookup architecture for ISS.
  *					This code is dedicated to John Dyson.
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  */
 
 #include <linux/config.h>
@@ -154,13 +155,13 @@
 
 /*
  * This code needs to be a bit more clever.
- * Does 2 minute timeouts now. Still just a circular buffer.
+ * Does 300 second timeouts now. Still just a circular buffer.
  * At most 32 validations stored. New validations are ignored
  * if all 32 validations are currently valid. To do otherwise
  * allows a situation in which clearances are forgotten before
  * they can be used (provided valid traffic is coming fast enough).
  * The buffer should really be as long as the number of valid
- * connections we want to accept in an 2 minute period.
+ * connections we want to accept in an 300 second period.
  * 32 is maybe to small. On the other hand, the validation check
  * algorithm has to walk the whole table, which is also stupid.
  * It would be better to have a combined hash/circular buffer.
@@ -185,7 +186,7 @@
 	int i;
 	for (i = 0; i < 32; i++)
 		if (clearances[i].saddr == saddr
-		&& clearances[i].tstamp > jiffies-HZ*120)
+		&& clearances[i].tstamp > jiffies-HZ*300)
 			return 1;
 	return 0;
 }
@@ -207,10 +208,12 @@
 #ifdef CONFIG_SYN_COOKIES
 /*
  *	MTU values we can represent in fall back mode.
- *	FIXME. I sort of picked these out of a hat. I should
- *	probably look around for docs on what common values are.
+ *	These values are partially borrowed from Jeff Weisberg's SunOS
+ *	implementation of SYNCOOKIES. I have added an extra limiting
+ * 	value of 64 to deal with the case of very small MTU values.
+ *	(e.g. long delay packet radio links, 1200 baud modems.)
  */
-static __u32 cookie_mtu[8] = { 64, 128, 256, 296, 512, 576, 1024, 1500 };
+static __u32 cookie_mtu[8] = { 64, 256, 512, 536, 1024, 1440, 1460, 4312 };
 #endif
 
 extern void tcp_v4_hash(struct sock *sk);
@@ -222,27 +225,43 @@
  * to specify the remote port nor the remote address for the
  * connection.  So always assume those are both wildcarded
  * during the search since they can never be otherwise.
- *
- * XXX Later on, hash on both local port _and_ local address,
- * XXX to handle a huge IP alias'd box.  Keep in mind that
- * XXX such a scheme will require us to run through the listener
- * XXX hash twice, once for local addresses bound, and once for
- * XXX the local address wildcarded (because the hash is different).
  */
-static struct sock *tcp_v4_lookup_longway(u32 daddr, unsigned short hnum)
+static struct sock *tcp_v4_lookup_longway(u32 daddr, unsigned short hnum,
+					  struct device *dev)
 {
 	struct sock *sk = tcp_listening_hash[tcp_lhashfn(hnum)];
 	struct sock *result = NULL;
+	int score, hiscore = 0;
 
 	for(; sk; sk = sk->next) {
 		if(sk->num == hnum) {
 			__u32 rcv_saddr = sk->rcv_saddr;
+			score = 1;
 
+			/* If this socket is bound to a particular IP address,
+			 * does the dest IPaddr of the packet match it?
+			 */
 			if(rcv_saddr) {
-				if(rcv_saddr == daddr)
-					return sk; /* Best possible match. */
-			} else if(!result)
+				if(rcv_saddr != daddr)
+					continue;
+				score++;
+			}
+
+			/* If this socket is bound to a particular interface,
+			 * did the packet come in on it? */
+			if (sk->bound_device) {
+				if (dev != sk->bound_device)
+					continue;
+				score++;
+			}
+
+			/* Check the score--max is 3. */
+			if (score == 3)
+				return sk; /* Best possible match. */
+			if (score > hiscore) {
+				hiscore = score;
 				result = sk;
+			}
 		}
 	}
 	return result;
@@ -252,7 +271,8 @@
  * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
  */
 static inline struct sock *__tcp_v4_lookup(struct tcphdr *th,
-					   u32 saddr, u16 sport, u32 daddr, u16 dport)
+					   u32 saddr, u16 sport, u32 daddr, 
+					   u16 dport, struct device *dev)
 {
 	unsigned short hnum = ntohs(dport);
 	struct sock *sk;
@@ -266,21 +286,23 @@
 		if(sk->daddr		== saddr		&& /* remote address */
 		   sk->dummy_th.dest	== sport		&& /* remote port    */
 		   sk->num		== hnum			&& /* local port     */
-		   sk->rcv_saddr	== daddr)		   /* local address  */
+		   sk->rcv_saddr	== daddr		&& /* local address  */
+		   ((sk->bound_device==NULL) || (sk->bound_device==dev))  )
 			goto hit; /* You sunk my battleship! */
-	sk = tcp_v4_lookup_longway(daddr, hnum);
+	sk = tcp_v4_lookup_longway(daddr, hnum, dev);
 hit:
 	return sk;
 }
  
-__inline__ struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport)
+__inline__ struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport,
+				      struct device *dev)
 {
-	return __tcp_v4_lookup(0, saddr, sport, daddr, dport);
+	return __tcp_v4_lookup(0, saddr, sport, daddr, dport, dev);
 }
 
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
 #define secondlist(hpnum, sk, fpass) \
-({ struct sock *s1; if(!(sk) && (fpass)--) \
+({ struct sock *s1; if((hpnum) && !(sk) && (fpass)--) \
 	s1 = tcp_bound_hash[tcp_bhashfn(hpnum)]; \
    else \
 	s1 = (sk); \
@@ -295,7 +317,8 @@
 
 struct sock *tcp_v4_proxy_lookup(unsigned short num, unsigned long raddr,
 				 unsigned short rnum, unsigned long laddr,
-				 unsigned long paddr, unsigned short pnum)
+				 unsigned long paddr, unsigned short pnum,
+				 struct device *dev)
 {
 	struct sock *s, *result = NULL;
 	int badness = -1;
@@ -327,7 +350,12 @@
 					continue;
 				score++;
 			}
-			if(score == 3 && s->num == hnum) {
+			if(s->bound_device) {
+				if (s->bound_device != dev)
+					continue;
+				score++;
+			}
+			if(score == 4 && s->num == hnum) {
 				result = s;
 				break;
 			} else if(score > badness && (s->num == hpnum || s->rcv_saddr)) {
@@ -473,7 +501,7 @@
 	  	switch(opcode)
 	  	{
 	  		case TCPOPT_EOL:
-	  			return;
+	  			goto ende;
 	  		case TCPOPT_NOP:	/* Ref: RFC 793 section 3.1 */
 	  			length--;
 	  			ptr--;		/* the opsize=*ptr++ above was a mistake */
@@ -481,7 +509,7 @@
 	  		
 	  		default:
 	  			if(opsize<=2)	/* Avoid silly options looping forever */
-	  				return;
+	  				goto ende;
 	  			switch(opcode)
 	  			{
 	  				case TCPOPT_MSS:
@@ -497,7 +525,7 @@
 	  			length-=opsize;
 	  	}
 	}
-	if (th->syn) 
+ende:	if (th->syn) 
 	{
 		if (! mss_seen)
 		      sk->mtu=min(sk->mtu, 536);  /* default MSS if none sent */
@@ -588,9 +616,10 @@
 		if (!tcp_clearance(saddr)) {
 #endif
 			/* Only let this warning get printed once a minute. */
-			if (jiffies - warning_time > HZ) {
+			if (jiffies - warning_time > HZ*60) {
 				warning_time = jiffies;
-				printk(KERN_INFO "Warning: possible SYN flooding. Sending cookies.\n");
+				printk(KERN_INFO "Warning: possible SYN flood from %d.%d.%d.%d on %d.%d.%d.%d:%d.  Sending cookies.\n", 
+					NIPQUAD(saddr), NIPQUAD(daddr), ntohs(th->dest));
 			}
 #ifdef CONFIG_RST_COOKIES
 			tcp_send_synack_probe(daddr, saddr, th, &tcp_prot,
@@ -598,6 +627,12 @@
 #endif
 #ifdef CONFIG_SYN_COOKIES
 			send_cookie = 1;
+#else
+			/* If we only have RST cookies we should
+			 * not drop through to the rest of the response code.
+			 */
+			kfree_skb(skb, FREE_READ);
+			return;
 #endif
 #ifdef CONFIG_RST_COOKIES
 		} else if (sk->ack_backlog >= 2*sk->max_ack_backlog) {
@@ -634,6 +669,8 @@
 
 	/* Or else we die! -DaveM */
 	newsk->sklist_next = NULL;
+	/* and die again -- erics */
+	newsk->pprev = NULL;
 
 	newsk->opt = NULL;
 	newsk->ip_route_cache  = NULL;
@@ -667,7 +704,7 @@
 	newsk->rtt = 0;
 	newsk->rto = TCP_TIMEOUT_INIT;
 	newsk->mdev = TCP_TIMEOUT_INIT;
-	newsk->max_window = 0;
+	newsk->max_window = 32;	/* It cannot be left at zero. -DaveM */
 	/*
 	 * See draft-stevens-tcpca-spec-01 for discussion of the
 	 * initialization of these values.
@@ -736,8 +773,18 @@
 	newsk->daddr = saddr;
 	newsk->saddr = daddr;
 	newsk->rcv_saddr = daddr;
-	tcp_v4_hash(newsk);
-	add_to_prot_sklist(newsk);
+#ifdef CONFIG_SYN_COOKIES
+	/* Don't actually stuff the socket into the protocol lists
+	 * if we are going to just destroy it anyway. We don't want any
+	 * funnies happening if the next packet arrives before we get
+	 * a chance to clean this one up.
+	 */
+	if (!send_cookie)
+#endif
+	{
+		tcp_v4_hash(newsk);
+		add_to_prot_sklist(newsk);
+	}
 
 	newsk->acked_seq = skb->seq + 1;
 	newsk->copied_seq = skb->seq + 1;
@@ -758,7 +805,8 @@
 	 * 	Note use of sk->user_mss, since user has no direct access to newsk 
 	 */
 
-	rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0);
+	rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0,
+			 sk->bound_device);
 	newsk->ip_route_cache = rt;
 	
 	if(rt!=NULL && (rt->rt_flags&RTF_WINDOW))
@@ -779,6 +827,14 @@
 
 	newsk->mtu = min(newsk->mtu, dev->mtu - sizeof(struct iphdr) - sizeof(struct tcphdr));
 
+	/* Must check it here, just to be absolutely safe.  If we end up
+	 * with a newsk->{max_window,mtu} of zero, we can thus end up with
+	 * a newsk->mss of zero, which causes us to bomb out in
+	 * tcp_do_sendmsg. -DaveM
+	 */
+	if(newsk->mtu < 32)
+		newsk->mtu = 32;
+
 #ifdef CONFIG_SKIP
 	
 	/*
@@ -918,7 +974,7 @@
 	newsk->rtt = 0;
 	newsk->rto = TCP_TIMEOUT_INIT;
 	newsk->mdev = TCP_TIMEOUT_INIT;
-	newsk->max_window = 0;
+	newsk->max_window = 32; /* It cannot be left at zero. -DaveM */
 	/*
 	 * See draft-stevens-tcpca-spec-01 for discussion of the
 	 * initialization of these values.
@@ -1001,7 +1057,8 @@
 	newsk->ip_ttl=sk->ip_ttl;
 	newsk->ip_tos=skb->ip_hdr->tos;
 
-	rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0);
+	rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0,
+			 sk->bound_device);
 	newsk->ip_route_cache = rt;
 	
 	if (rt!=NULL && (rt->rt_flags&RTF_WINDOW))
@@ -1263,8 +1320,12 @@
 			/* We need to be a bit careful to preserve the
 			 * count of packets that are out in the system here.
 			 */
-			sk->ssthresh = max(sk->cong_window >> 1, 2);
+			sk->ssthresh = max(
+				min(sk->cong_window,
+				(sk->window_seq-sk->rcv_ack_seq)/max(sk->mss,1))
+				 >> 1, 2);
 			sk->cong_window = sk->ssthresh+MAX_DUP_ACKS+1;
+			sk->cong_count = 0;
 			tmp = sk->packets_out;
 			tcp_do_retransmit(sk,0);
 			sk->packets_out = tmp;
@@ -1284,6 +1345,7 @@
 		if (sk->rcv_ack_cnt > MAX_DUP_ACKS) {
 			/* Don't allow congestion window to drop to zero. */
 			sk->cong_window = max(sk->ssthresh, 1);
+			sk->cong_count = 0;
 		}
 		sk->window_seq = window_seq;
 		sk->rcv_ack_seq = ack;
@@ -1534,19 +1596,6 @@
 	}
 
 	/*
-	 *	We have nothing queued but space to send. Send any partial
-	 *	packets immediately (end of Nagle rule application).
-	 */
-	 
-	if (sk->packets_out == 0
-	    && sk->partial != NULL
-	    && skb_queue_empty(&sk->write_queue)
-	    && sk->send_head == NULL) 
-	{
-		tcp_send_partial(sk);
-	}
-
-	/*
 	 * In the LAST_ACK case, the other end FIN'd us.  We then FIN'd them, and
 	 * we are now waiting for an acknowledge to our FIN.  The other end is
 	 * already in TIME_WAIT.
@@ -1618,6 +1667,14 @@
 	if (sk->state==TCP_SYN_RECV)
 	{
 		tcp_set_state(sk, TCP_ESTABLISHED);
+
+		/* Must check for peer advertising zero sized window
+		 * or else we get a sk->{mtu,mss} of zero and thus bomb out
+		 * in tcp_do_sendmsg. -DaveM
+		 */
+		if(sk->max_window == 0)
+			sk->max_window = 32;
+
 		tcp_options(sk,th);
 
 #if 0
@@ -1628,11 +1685,7 @@
 		sk->copied_seq = sk->acked_seq;
 		if(!sk->dead)
 			sk->state_change(sk);
-		if(sk->max_window==0)
-		{
-			sk->max_window=32;	/* Sanity check */
-			sk->mss=min(sk->max_window,sk->mtu);
-		}
+
 		/* Reset the RTT estimator to the initial
 		 * state rather than testing to avoid
 		 * updating it on the ACK to the SYN packet.
@@ -1650,11 +1703,20 @@
 	 * If we are retransmitting, and we acked a packet on the retransmit
 	 * queue, and there is still something in the retransmit queue,
 	 * then we can output some retransmission packets.
+	 *
+	 * Note that we need to be a bit careful here about getting the
+	 * correct TIME_WRITE timer set. If we just got an ack of a
+	 * packet we where retransmitting, we will retransmit the next
+	 * packet in the retransmit queue below, and the timeout
+	 * should now start from the time we retransmitted that packet.
+	 * The resetting of the TIME_WRITE timer above will have set it
+	 * relative to the prior transmission time, which would be wrong.
 	 */
 
 	if (sk->send_head != NULL && (flag&2) && sk->retransmits)
 	{
 		tcp_do_retransmit(sk, 1);
+		tcp_reset_xmit_timer(sk, TIME_WRITE, sk->rto);
 	}
 
 	return 1;
@@ -2196,7 +2258,8 @@
 	struct tcphdr *th = (struct tcphdr *)(skb->h.raw + iph->ihl*4);
 	struct sock *sk;
 
-	sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest);
+	sk = tcp_v4_proxy_lookup(th->dest, iph->saddr, th->source, iph->daddr,
+				 0, 0, skb->dev);
 	if (!sk)
 		return 0;
 	/* 0 means accept all LOCAL addresses here, not all the world... */
@@ -2262,7 +2325,13 @@
 #ifdef CONFIG_SYN_COOKIES
 retry_search:
 #endif
-		sk = __tcp_v4_lookup(th, saddr, th->source, daddr, th->dest);
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+		if (skb->redirport)
+			sk = tcp_v4_proxy_lookup(th->dest, saddr, th->source, daddr,
+						 dev->pa_addr, skb->redirport, dev);
+		else
+#endif
+		sk = __tcp_v4_lookup(th, saddr, th->source, daddr, th->dest, dev);
 		if (!sk)
 			goto no_tcp_socket;
 		skb->sk = sk;
@@ -2478,6 +2547,13 @@
 				 */
 				tcp_ack(sk,th,skb->ack_seq,len);
 
+				/* We must check here (before tcp_options) whether
+				 * peer advertised a zero sized window on us, else
+				 * we end up with a zero sk->{mtu,mss} and thus bomb
+				 * out in tcp_do_sendmsg. -DaveM
+				 */
+				if(sk->max_window == 0)
+					sk->max_window = 32;
 
 				/*
 				 *	Ok.. it's good. Set up sequence numbers and
@@ -2501,11 +2577,7 @@
 					sk->state_change(sk);
 					sock_wake_async(sk->socket, 0);
 				}
-				if(sk->max_window==0)
-				{
-					sk->max_window = 32;
-					sk->mss = min(sk->max_window, sk->mtu);
-				}
+
 				/* Reset the RTT estimator to the initial
 				 * state rather than testing to avoid
 				 * updating it on the ACK to the SYN packet.
@@ -2572,7 +2644,7 @@
 			sk->shutdown = SHUTDOWN_MASK;
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
 			sk = tcp_v4_proxy_lookup(th->dest, saddr, th->source, daddr,
-						 dev->pa_addr, skb->redirport);
+						 dev->pa_addr, skb->redirport, dev);
 #else
 			sk = NULL;
 #endif
@@ -2671,6 +2743,19 @@
 	
 	if(tcp_data(skb,sk, saddr, len))
 		kfree_skb(skb, FREE_READ);
+
+	/*
+	 *	If we had a partial packet being help up due to
+	 *	application of Nagle's rule we are now free to send it.
+	 */
+	if (th->ack
+	    && sk->packets_out == 0
+	    && sk->partial != NULL
+	    && skb_queue_empty(&sk->write_queue)
+	    && sk->send_head == NULL) 
+	{
+		tcp_send_partial(sk);
+	}
 
 	/*
 	 *	If our receive queue has grown past its limits,
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- v2.0.30/linux/net/ipv4/tcp_output.c	Tue Apr  8 08:47:47 1997
+++ linux/net/ipv4/tcp_output.c	Fri Aug 15 12:23:23 1997
@@ -32,6 +32,8 @@
  *				  thus the outgoing device does as well, when
  *				  skb's are on the retransmit queue which still
  *				  refer to the old obsolete destination.
+ *              Elliot Poger    : Added support for SO_BINDTODEVICE.
+ *	Juan Jose Ciarlante	: Added sock dynamic source address rewriting
  */
 
 #include <linux/config.h>
@@ -50,22 +52,10 @@
  *  RECV.NEXT + RCV.WIN fixed until:
  *  RCV.BUFF - RCV.USER - RCV.WINDOW >= min(1/2 RCV.BUFF, MSS)"
  *
- * Experiments against BSD and Solaris machines show that following
- * these rules results in the BSD and Solaris machines making very
- * bad guesses about how much data they can have in flight.
- *
- * Instead we follow the BSD lead and offer a window that gives
- * the size of the current free space, truncated to a multiple
- * of 1024 bytes. If the window is smaller than
- * 	min(sk->mss, MAX_WINDOW/2)
- * then we advertise the window as having size 0, unless this
- * would shrink the window we offered last time.
- * This results in as much as double the throughput as the original
- * implementation.
- *
  * We do BSD style SWS avoidance -- note that RFC1122 only says we
  * must do silly window avoidance, it does not require that we use
- * the suggested algorithm.
+ * the suggested algorithm. Following BSD avoids breaking header
+ * prediction.
  *
  * The "rcvbuf" and "rmem_alloc" values are shifted by 1, because
  * they also contain buffer handling overhead etc, so the window
@@ -73,33 +63,41 @@
  */
 int tcp_new_window(struct sock * sk)
 {
-	unsigned long window;
+	unsigned long window = sk->window;
 	unsigned long minwin, maxwin;
+	unsigned long free_space;
 
 	/* Get minimum and maximum window values.. */
 	minwin = sk->mss;
 	if (!minwin)
 		minwin = sk->mtu;
+	if (!minwin) {
+		printk(KERN_DEBUG "tcp_new_window: mss fell to 0.\n");
+		minwin = 1;
+	}
 	maxwin = sk->window_clamp;
 	if (!maxwin)
 		maxwin = MAX_WINDOW;
+
 	if (minwin > maxwin/2)
 		minwin = maxwin/2;
 
 	/* Get current rcvbuf size.. */
-	window = sk->rcvbuf/2;
-	if (window < minwin) {
+	free_space = sk->rcvbuf/2;
+	if (free_space < minwin) {
 		sk->rcvbuf = minwin*2;
-		window = minwin;
+		free_space = minwin;
 	}
 
 	/* Check rcvbuf against used and minimum window */
-	window -= sk->rmem_alloc/2;
-	if ((long)(window - minwin) < 0)		/* SWS avoidance */
-		window = 0;
+	free_space -= sk->rmem_alloc/2;
+	if ((long)(free_space - minwin) < 0)		/* SWS avoidance */
+		return 0;
+
+	/* Try to avoid the divide and multiply if we can */
+        if (window <= free_space - minwin || window > free_space)
+                window = (free_space/minwin)*minwin;
 
-	if (window > 1023)
-		window &= ~1023;
 	if (window > maxwin)
 		window = maxwin;
 	return window;
@@ -166,8 +164,10 @@
 	 * anything for a long time, in which case we have no reason to
 	 * believe that our congestion window is still correct.
 	 */
-	if (sk->send_head == 0 && (jiffies - sk->idletime) > sk->rto)
+	if (sk->send_head == 0 && (jiffies - sk->idletime) > sk->rto) {
 		sk->cong_window = 1;
+		sk->cong_count = 0;
+	}
 
 	/*
 	 *	Actual processing.
@@ -175,7 +175,7 @@
 
 	tcp_statistics.TcpOutSegs++;  
 	skb->seq = ntohl(th->seq);
-	skb->end_seq = skb->seq + size - 4*th->doff;
+	skb->end_seq = skb->seq + size - 4*th->doff + th->fin;
 
 	/*
 	 *	We must queue if
@@ -491,7 +491,8 @@
 			/* ANK: UGLY, but the bug, that was here, should be fixed.
 			 */
 			struct options *  opt = (struct options*)skb->proto_priv;
-			rt = ip_check_route(&sk->ip_route_cache, opt->srr?opt->faddr:iph->daddr, skb->localroute);
+			rt = ip_check_route(&sk->ip_route_cache, opt->srr?opt->faddr:iph->daddr,
+					    skb->localroute, sk->bound_device);
 	        }
 
 		iph->id = htons(ip_id_count++);
@@ -534,6 +535,8 @@
 				struct sk_buff *skb2 = sk->write_queue.next;
 				while (skb2 && skb2->dev == skb->dev) {
 					skb2->raddr=rt->rt_gateway;
+                                        if (sk->state == TCP_SYN_SENT && sysctl_ip_dynaddr)
+                                                ip_rewrite_addrs (sk, skb2, dev);
 					skb2->dev = dev;
 					skb2->arp=1;
 					if (rt->rt_hh)
@@ -557,6 +560,8 @@
 				}
 			}
 			skb->raddr=rt->rt_gateway;
+                        if (skb->dev !=dev && sk->state == TCP_SYN_SENT && sysctl_ip_dynaddr)
+                                ip_rewrite_addrs(sk, skb, dev);
 			skb->dev=dev;
 			skb->arp=1;
 #ifdef CONFIG_FIREWALL
@@ -875,6 +880,8 @@
 			return;
 		}
 	}
+
+	clear_delayed_acks(sk);
 	
 	/*
 	 *	We ought to check if the end of the queue is a buffer and
@@ -987,8 +994,25 @@
 	ptr[2] = ((newsk->mtu) >> 8) & 0xff;
 	ptr[3] =(newsk->mtu) & 0xff;
 	buff->csum = csum_partial(ptr, 4, 0);
+#ifdef CONFIG_SYN_COOKIES
+	/* Don't save buff on the newsk chain if we are going to destroy
+	 * newsk anyway in a second, it just delays getting rid of newsk.
+	 */
+ 	if (destroy) {
+		/* BUFF was charged to NEWSK, _this_ is what we want
+		 * to undo so the SYN cookie can be killed now.  SKB
+		 * is charged to SK, below we will undo that when
+		 * we kfree SKB.
+		 */
+		buff->sk = NULL;
+		atomic_sub(buff->truesize, &newsk->wmem_alloc);
+	}
+#endif
 	tcp_send_check(t1, newsk->saddr, newsk->daddr, sizeof(*t1)+4, buff);
-	newsk->prot->queue_xmit(newsk, ndev, buff, destroy);
+	if (destroy)
+		newsk->prot->queue_xmit(NULL, ndev, buff, 1);
+	else
+		newsk->prot->queue_xmit(newsk, ndev, buff, 0);
 
 
 #ifdef CONFIG_SYN_COOKIES
@@ -1212,6 +1236,9 @@
 		win_size = skb->len - (((unsigned char *) th) - skb->data);
 		win_size -= th->doff * 4;
 
+		/* Don't send more than the offered window! */
+		win_size = min(win_size, sk->window_seq - sk->sent_seq);
+
 		/*
 		 *	Grab the data for a temporary frame
 		 */
@@ -1386,7 +1413,9 @@
 	/* Update our starting seq number */
 	skb->seq = ack;
 	th->seq = htonl(ack);
+
 	iph->tot_len = htons(ntohs(iph->tot_len)-diff);
+	ip_send_check(iph);
 
 	/* Get the partial checksum for the IP options */
 	if (th->doff*4 - sizeof(*th) > 0)
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/tcp_timer.c linux/net/ipv4/tcp_timer.c
--- v2.0.30/linux/net/ipv4/tcp_timer.c	Tue Apr  8 08:47:47 1997
+++ linux/net/ipv4/tcp_timer.c	Tue Aug 12 11:30:35 1997
@@ -138,7 +138,9 @@
 		return;
 	}
 
-	sk->ssthresh = sk->cong_window >> 1; /* remember window where we lost */
+	/* remember window where we lost */
+	sk->ssthresh = min(sk->cong_window,
+			(sk->window_seq-sk->rcv_ack_seq)/max(sk->mss,1)) >> 1;
 	/* sk->ssthresh in theory can be zero.  I guess that's OK */
 	sk->cong_count = 0;
 	sk->cong_window = 1;
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/timer.c linux/net/ipv4/timer.c
--- v2.0.30/linux/net/ipv4/timer.c	Thu Feb 22 06:50:46 1996
+++ linux/net/ipv4/timer.c	Fri Aug 15 12:23:23 1997
@@ -139,12 +139,10 @@
 
 		case TIME_CLOSE:
 			/* We've waited long enough, close the socket. */
-			sk->state = TCP_CLOSE;
-			delete_timer (sk);
+			tcp_set_state(sk, TCP_CLOSE);
 			if (!sk->dead)
 				sk->state_change(sk);
 			sk->shutdown = SHUTDOWN_MASK;
-			reset_timer (sk, TIME_DONE, TCP_DONE_TIME);
 			break;
 
 		default:
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
--- v2.0.30/linux/net/ipv4/udp.c	Tue Apr  8 08:47:47 1997
+++ linux/net/ipv4/udp.c	Tue Aug 12 11:30:35 1997
@@ -52,6 +52,7 @@
  *		David S. Miller	:	New socket lookup architecture for ISS.
  *					Last socket cache retained as it
  *					does have a high hit rate.
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  *
  *
  *		This program is free software; you can redistribute it and/or
@@ -130,6 +131,11 @@
 		if((sk2->num == snum) && (sk2 != sk)) {
 			int sk2_reuse = sk2->reuse;
 
+			/* Two sockets can be bound to the same port if they're
+			 * bound to different interfaces... */
+			if (sk->bound_device != sk2->bound_device)
+				continue;
+
 			if(!sk2->rcv_saddr || !sk->rcv_saddr) {
 				if((!sk2_reuse) || (!sk_reuse)) {
 					retval = 1;
@@ -147,7 +153,7 @@
 	return retval;
 }
 
-static inline int udp_lport_inuse(int num)
+static inline int udp_lport_inuse(u16 num)
 {
 	struct sock *sk = udp_hash[num & (UDP_HTABLE_SIZE - 1)];
 
@@ -161,36 +167,42 @@
 /* Shared by v4/v6 tcp. */
 unsigned short udp_good_socknum(void)
 {
-	static int start = 0;
-	unsigned short base;
-	int i, best = 0, size = 32767; /* a big num. */
 	int result;
-
-	base = PROT_SOCK + (start & 1023) + 1;
+	static int start = 0;
+	int i, best, best_size_so_far;
 
 	SOCKHASH_LOCK();
-	for(i = 0; i < UDP_HTABLE_SIZE; i++) {
-		struct sock *sk = udp_hash[i];
-		if(!sk) {
-			start = (i + 1 + start) & 1023;
-			result = i + base + 1;
+
+	/* Select initial not-so-random "best" */
+	best = PROT_SOCK + 1 + (start & 1023);
+	best_size_so_far = 32767;	/* "big" num */
+	result = best;
+	for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) {
+		struct sock *sk;
+		int size;
+
+		sk = udp_hash[result & (UDP_HTABLE_SIZE - 1)];
+
+		/* No clashes - take it */
+		if (!sk)
 			goto out;
-		} else {
-			int j = 0;
-			do {
-				if(++j >= size)
-					goto next;
-			} while((sk = sk->next));
-			best = i;
-			size = j;
-		}
-	next:
+
+		/* Is this one better than our best so far? */
+		size = 0;
+		do {
+			if(++size >= best_size_so_far)
+				goto next;
+		} while((sk = sk->next) != NULL);
+		best_size_so_far = size;
+		best = result;
+next:
 	}
 
-	while(udp_lport_inuse(base + best + 1))
+	while (udp_lport_inuse(best))
 		best += UDP_HTABLE_SIZE;
-	result = (best + base + 1);
+	result = best;
 out:
+	start = result;
 	SOCKHASH_UNLOCK();
 	return result;
 }
@@ -255,7 +267,8 @@
 /* UDP is nearly always wildcards out the wazoo, it makes no sense to try
  * harder than this. -DaveM
  */
-__inline__ struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport)
+__inline__ struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport,
+				      struct device *dev)
 {
 	struct sock *sk, *result = NULL;
 	unsigned short hnum = ntohs(dport);
@@ -279,7 +292,15 @@
 					continue;
 				score++;
 			}
-			if(score == 3) {
+			/* If this socket is bound to a particular interface,
+			 * did the packet come in on it? */
+			if (sk->bound_device) {
+				if (dev == sk->bound_device)
+					score++;
+				else
+					continue;  /* mismatch--not this sock */
+			}
+			if(score == 4) {
 				result = sk;
 				break;
 			} else if(score > badness) {
@@ -308,7 +329,8 @@
 
 struct sock *udp_v4_proxy_lookup(unsigned short num, unsigned long raddr,
 				 unsigned short rnum, unsigned long laddr,
-				 unsigned long paddr, unsigned short pnum)
+				 unsigned long paddr, unsigned short pnum,
+				 struct device *dev)
 {
 	struct sock *s, *result = NULL;
 	int badness = -1;
@@ -340,7 +362,14 @@
 					continue;
 				score++;
 			}
-			if(score == 3 && s->num == hnum) {
+			/* If this socket is bound to a particular interface,
+			 * did the packet come in on it? */
+			if(s->bound_device) {
+				if (s->bound_device != dev)
+					continue;
+				score++;
+			}
+			if(score == 4 && s->num == hnum) {
 				result = s;
 				break;
 			} else if(score > badness && (s->num == hpnum || s->rcv_saddr)) {
@@ -363,7 +392,8 @@
 					     unsigned short num,
 					     unsigned long raddr,
 					     unsigned short rnum,
-					     unsigned long laddr)
+					     unsigned long laddr,
+					     struct device *dev)
 {
 	struct sock *s = sk;
 	unsigned short hnum = ntohs(num);
@@ -372,6 +402,7 @@
 		    (s->dead && (s->state == TCP_CLOSE))		||
 		    (s->daddr && s->daddr!=raddr)			||
 		    (s->dummy_th.dest != rnum && s->dummy_th.dest != 0) ||
+		    ((s->bound_device) && (s->bound_device!=dev))       ||
 		    (s->rcv_saddr  && s->rcv_saddr != laddr))
 			continue;
 		break;
@@ -408,7 +439,7 @@
 	
 	uh = (struct udphdr *)header;  
    
-	sk = udp_v4_lookup(daddr, uh->dest, saddr, uh->source);
+	sk = udp_v4_lookup(daddr, uh->dest, saddr, uh->source, NULL);
 	if (sk == NULL) 
 	  	return;	/* No socket for error */
   	
@@ -850,7 +881,7 @@
 	if(!sk->broadcast && ip_chk_addr(usin->sin_addr.s_addr)==IS_BROADCAST)
 		return -EACCES;			/* Must turn broadcast on first */
   	
-  	rt=ip_rt_route((__u32)usin->sin_addr.s_addr, sk->localroute);
+  	rt=ip_rt_route((__u32)usin->sin_addr.s_addr, sk->localroute, sk->bound_device);
   	if (rt==NULL)
   		return -ENETUNREACH;
   	if(!sk->saddr)
@@ -869,8 +900,9 @@
 {
 	lock_sock(sk);
 	sk->state = TCP_CLOSE;
-	release_sock(sk);
 	sk->dead = 1;
+	release_sock(sk);
+	udp_v4_unhash(sk);
 	destroy_sock(sk);
 }
 
@@ -918,7 +950,8 @@
 	struct udphdr *uh = (struct udphdr *)(skb->h.raw + iph->ihl*4);
 	struct sock *sk;
 
-	sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest);
+	sk = udp_v4_proxy_lookup(uh->dest, iph->saddr, uh->source, iph->daddr,
+				 0, 0, skb->dev);
 	if (!sk)
 		return 0;
 	/* 0 means accept all LOCAL addresses here, not all the world... */
@@ -940,7 +973,7 @@
 
 	SOCKHASH_LOCK();
 	sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)];
-	sk = udp_v4_mcast_next(sk, uh->dest, saddr, uh->source, daddr);
+	sk = udp_v4_mcast_next(sk, uh->dest, saddr, uh->source, daddr, skb->dev);
 	if(sk) {
 		struct sock *sknext = NULL;
 
@@ -948,7 +981,7 @@
 			struct sk_buff *skb1 = skb;
 
 			sknext = udp_v4_mcast_next(sk->next, uh->dest, saddr,
-						   uh->source, daddr);
+						   uh->source, daddr, skb->dev);
 			if(sknext)
 				skb1 = skb_clone(skb, GFP_ATOMIC);
 
@@ -1066,10 +1099,10 @@
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
 	if(skb->redirport)
 		sk = udp_v4_proxy_lookup(uh->dest, saddr, uh->source,
-					 daddr, dev->pa_addr, skb->redirport);
+					 daddr, dev->pa_addr, skb->redirport, dev);
 	else
 #endif
-	sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest);
+	sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest, dev);
 	
 	if (sk == NULL) 
   	{
diff -u --recursive --new-file v2.0.30/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v2.0.30/linux/net/ipx/af_ipx.c	Tue Nov 26 23:44:21 1996
+++ linux/net/ipx/af_ipx.c	Tue Aug 12 11:30:39 1997
@@ -468,7 +468,60 @@
 	ipx_socket	*sock1 = NULL, *sock2 = NULL;
 	struct sk_buff	*skb1 = NULL, *skb2 = NULL;
 
-	sock1 = ipxitf_find_socket(intrfc, ipx->ipx_dest.sock);
+	if (intrfc == ipx_primary_net
+	  && ntohs(ipx->ipx_dest.sock) == 0x451) 
+	{
+	  /* 
+	   * The packet's target is a NCP connection handler. We want to
+	   * hand it to the correct socket directly within the kernel,
+	   * so that the mars_nwe packet distribution process
+	   * does not have to do it. Here we only care about NCP and
+	   * BURST packets.
+	   * You might call this a hack, but believe me, you do not
+	   * want a complete NCP layer in the kernel, and this is
+	   * VERY fast as well.
+	   */
+	  int connection = 0;
+
+	  if (    *((char*)(ipx+1))   == 0x22
+	      &&  *((char*)(ipx+1)+1) == 0x22) 
+	  {
+	  	/*
+		 * The packet is a NCP request
+		 */
+		 connection = ( ((int) *((char*)(ipx+1)+5)) << 8 )
+		 	       | (int) *((char*)(ipx+1)+3);
+	  } 
+	  else if (    *((char*)(ipx+1))   == 0x77
+		   &&  *((char*)(ipx+1)+1) == 0x77) 
+	  {
+		/*
+		 * The packet is a BURST packet
+		 */
+		 connection = ( ((int) *((char*)(ipx+1)+9)) << 8 )
+		 	       | (int) *((char*)(ipx+1)+8);
+	  }
+
+          if (connection) 
+	  {
+	    /*
+	     * Now we have to look for a special NCP connection handling
+	     * socket. Only these sockets have ipx_ncp_conn != 0, set
+	     * by SIOCIPXNCPCONN.
+	     */
+            for (sock1=intrfc->if_sklist;
+		(sock1 != NULL) &&
+		(sock1->protinfo.af_ipx.ipx_ncp_conn != connection);
+		sock1=sock1->next);;
+          }
+        }
+        if (sock1 == NULL) 
+	{
+		/* No special socket found, forward the packet the
+		 * normal way.
+		 */
+		sock1 = ipxitf_find_socket(intrfc, ipx->ipx_dest.sock);
+	}
 
 	/*
 	 *	We need to check if there is a primary net and if
@@ -1723,6 +1776,7 @@
 	}
 	sk->rcvbuf=SK_RMEM_MAX;
 	sk->sndbuf=SK_WMEM_MAX;
+	sk->allocation=GFP_KERNEL;
 	sk->prot=NULL;	/* So we use default free mechanisms */
 	skb_queue_head_init(&sk->receive_queue);
 	skb_queue_head_init(&sk->write_queue);
@@ -2242,6 +2296,21 @@
 			if(err) return err;
 			return(ipxcfg_get_config_data((void *)arg));
 		}
+
+		case SIOCIPXNCPCONN:
+                {
+		  /*
+		   * This socket wants to take care of the NCP connection
+		   * handed to us in arg.
+		   */
+                  if (!suser()) return(-EPERM);
+                  err = verify_area(VERIFY_READ, (void *)arg,
+                                sizeof(unsigned short));
+                  if (err) return err;
+                  sk->protinfo.af_ipx.ipx_ncp_conn = get_fs_word(arg);
+                  return 0;
+                }
+
 		case SIOCGSTAMP:
 			if (sk)
 			{
diff -u --recursive --new-file v2.0.30/linux/net/netsyms.c linux/net/netsyms.c
--- v2.0.30/linux/net/netsyms.c	Tue Apr  8 08:47:47 1997
+++ linux/net/netsyms.c	Tue Aug 12 11:30:39 1997
@@ -42,7 +42,8 @@
 #if     defined(CONFIG_ULTRA)   ||      defined(CONFIG_WD80x3)          || \
         defined(CONFIG_EL2)     ||      defined(CONFIG_NE2000)          || \
         defined(CONFIG_E2100)   ||      defined(CONFIG_HPLAN_PLUS)      || \
-        defined(CONFIG_HPLAN)   ||      defined(CONFIG_AC3200)
+        defined(CONFIG_HPLAN)   ||      defined(CONFIG_AC3200)		|| \
+	defined(CONFIG_ULTRA32)
 #include "../drivers/net/8390.h"
 #endif
 
@@ -103,6 +104,7 @@
 	X(ip_options_compile),
 	X(ip_rt_put),
 	X(arp_send),
+	X(arp_bind_cache),
 	X(ip_id_count),
 	X(ip_send_check),
 	X(ip_forward),
@@ -111,7 +113,8 @@
 #if	defined(CONFIG_ULTRA)	||	defined(CONFIG_WD80x3)		|| \
 	defined(CONFIG_EL2)	||	defined(CONFIG_NE2000)		|| \
 	defined(CONFIG_E2100)	||	defined(CONFIG_HPLAN_PLUS)	|| \
-	defined(CONFIG_HPLAN)	||	defined(CONFIG_AC3200)
+	defined(CONFIG_HPLAN)	||	defined(CONFIG_AC3200)		|| \
+	defined(CONFIG_ULTRA32)
 	/* If 8390 NIC support is built in, we will need these. */
 	X(ei_open),
 	X(ei_close),
diff -u --recursive --new-file v2.0.30/linux/scripts/Configure linux/scripts/Configure
--- v2.0.30/linux/scripts/Configure	Tue Apr  8 08:47:48 1997
+++ linux/scripts/Configure	Sun Aug  3 15:38:39 1997
@@ -14,7 +14,7 @@
 # Menuconfig or xconfig, please notify the respective authors so that 
 # those utilities can be updated in parallel.
 #
-# Menuconfig:  <roadcapw@cfw.com>
+# Menuconfig:  <roadcapw@titus.org>
 # xconfig:     <apenwarr@foxnet.net>  <eric@aib.com>
 #              ****************************************
 #
@@ -46,6 +46,9 @@
 # 090397 Axel Boldt (boldt@math.ucsb.edu) - avoid ? and + in regular 
 # expressions for GNU expr since version 1.15 and up use \? and \+.
 
+# 040697 Larry Augustin (lma@varesearch.com) - integer expr test
+# fails with GNU expr 1.12. Re-write to work with new and old expr.
+
 #
 # Make sure we're really running bash.
 #
@@ -285,13 +288,20 @@
 # int processes an integer argument
 #
 #	int question define default
-#
+# GNU expr changed handling of ?.  In older versions you need ?,
+# in newer you need \?
+OLD_EXPR=`expr "0" : '0\?'`
+if [ $OLD_EXPR -eq 1 ]; then
+    INT_EXPR='0$\|-\?[1-9][0-9]*$'
+else
+    INT_EXPR='0$\|-?[1-9][0-9]*$'
+fi
 function int () {
 	old=$(eval echo "\${$2}")
 	def=${old:-$3}
 	while :; do
 	  readln "$1 ($2) [$def] " "$def" "$old"
-	  if expr "$ans" : '0$\|\(-[1-9]\|[1-9]\)[0-9]*$' > /dev/null; then
+	  if expr "$ans" : $INT_EXPR > /dev/null; then
 	    define_int "$2" "$ans"
 	    break
 	  else
diff -u --recursive --new-file v2.0.30/linux/scripts/Menuconfig linux/scripts/Menuconfig
--- v2.0.30/linux/scripts/Menuconfig	Tue Oct 29 07:38:17 1996
+++ linux/scripts/Menuconfig	Tue Aug  5 09:01:54 1997
@@ -19,17 +19,10 @@
 # Portions of this script were borrowed from the original Configure
 # script.
 #
-# Please send comments / questions / bug fixes to roadcapw@cfw.com
+# Please send comments / questions / bug fixes to roadcapw@titus.org
 #
 #----------------------------------------------------------------------------
 
-
-#
-# Change this to TRUE if you prefer all kernel options listed
-# in a single menu rather than the standard menu hierarchy.
-#
-single_menu_mode=
-
 #
 # Make sure we're really running bash.
 #
@@ -41,7 +34,12 @@
 set -h +o posix
 
 
-
+#
+# If you prefer all kernel options listed in a single menu rather than
+# the standard menu hierarchy, set SINGLE_MENU_MODE to "TRUE" in your
+# environment.
+#
+single_menu_mode="${SINGLE_MENU_MODE:-FALSE}"
 
 
 #
@@ -400,7 +398,8 @@
 			answer="`cat MCdialog.out`"
 			answer="${answer:-$3}"
 
-			if expr $answer : '0$\|-?[1-9][0-9]*$' >/dev/null
+			# Avoid problems with GNU vs POSIX expr semantics.
+			if expr "$answer" : '0$\|-[1-9][0-9]*$\|[1-9][0-9]*$' >/dev/null
 			then
 				eval $2="$answer"
 			else
@@ -433,7 +432,8 @@
 			answer="${answer:-$3}"
 			answer="${answer##*[x,X]}"
 
-			if expr $answer : '[0-9a-fA-F]+$' >/dev/null
+			# Avoid problems with GNU vs POSIX expr semantics.
+			if expr "$answer" : '[0-9a-fA-F][0-9a-fA-F]*$' >/dev/null
 			then
 				eval $2="$answer"
 			else
@@ -452,7 +452,7 @@
 }
 
 #
-# Handle a on-of-many choice list.
+# Handle a one-of-many choice list.
 #
 function l_choice () {
 	#
@@ -493,7 +493,7 @@
 	done
 
 	#
-	# Now set the boolean value of each option base on
+	# Now set the boolean value of each option based on
 	# the selection made from the radiolist.
 	#
 	set -- $choices
@@ -579,7 +579,7 @@
 		if ($1 ~ /mainmenu_option|endmenu/) {
 			printf("") >>menu
 		} 
-		else if ($0 ~ /^#|$MAKE|mainmenu_name/) {
+		else if ($0 ~ /^#|\$MAKE|mainmenu_name/) {
 			printf("") >>menu
 		}
 		else if ($1 == "source") {
@@ -655,7 +655,7 @@
 Menuconfig has encountered a possible error in one of the kernel's
 configuration files and is unable to continue.
 
-Please report this to the author <roadcapw@cfw.com>.  You may also
+Please report this to the author <roadcapw@titus.org>.  You may also
 send a problem report to linux-kernel@vger.rutgers.edu or post a
 message to the linux.dev.kernel news group.
 
@@ -718,7 +718,7 @@
 "make clean all" command.
 
 If you have verified that your ncurses install is correct, you may email
-the author <roadcapw@cfw.com> or post a message on the linux.dev.kernel
+the author <roadcapw@titus.org> or post a message on the linux.dev.kernel
 news group for additional assistance. 
 
 EOM
@@ -746,7 +746,7 @@
 
 	while true
 	do
-		ALT_CONFIG="${ALT_CONFIG:-$DEFAULTS}"
+		ALT_CONFIG="${ALT_CONFIG:-$_CONFIG}"
 
 		$DIALOG --backtitle "$backtitle" \
 			--inputbox "\
@@ -812,6 +812,7 @@
 			
 	while true
 	do
+
 		$DIALOG --backtitle "$backtitle" \
 			--inputbox "\
 Enter a filename to which this configuration should be saved \
@@ -858,20 +859,19 @@
 	rm -f help.out MCdialog.out
 }
 
+
+#
+# Load config file into the environment converting all
+# "# OPTION is not set" lines to "OPTION=n".
 #
-# Load config options from a file.
-# Converts all "# OPTION is not set" lines to "OPTION=n" lines
+# The $ARCH defaults are loaded first so "new"/previously 
+# unconfigured parameters are assigned the proper defaults.
 #
 function load_config_file () {
-	awk '
-	  /# .* is not set.*/ { printf("%s=n\n", $2) }
-	! /# .* is not set.*/ { print }
-	' $1 >.tmpconfig
-
-	source ./.tmpconfig
-	rm -f .tmpconfig
+	eval "`sed -e 's/# \(.*\) is not set.*/\1=n/' arch/$ARCH/defconfig $1`"
 }
 
+
 #
 # Just what it says.
 #
@@ -1007,8 +1007,8 @@
 		fi
 	}
 
-	DEF_CONFIG="${1:-.config}"
-	DEF_CONFIG_H="include/linux/autoconf.h"
+	DEF_CONFIG="${1:-$_CONFIG}"
+	DEF_CONFIG_H="$AUTOCONF_H"
 
 	CONFIG=.tmpconfig
 	CONFIG_H=.tmpconfig.h
@@ -1025,7 +1025,11 @@
 	
 	if . $CONFIG_IN >>.menuconfig.log 2>&1
 	then
-		if [ "$DEF_CONFIG" = ".config" ]
+		#
+		# Skip these steps if we are saving to an
+		# alternate configuration file.
+		#
+		if [ "$DEF_CONFIG" = "$_CONFIG" ]
 		then
 			#
 			# Create the sound driver's config files for cards
@@ -1055,12 +1059,14 @@
 	fi
 }
 
+
 #
 # Remove temporary files
 #
 cleanup () {
 	cleanup1
 	cleanup2
+	stty $S_TERMIO
 }
 
 cleanup1 () {
@@ -1093,6 +1099,7 @@
 	ROWS=$((ROWS-4))  COLS=$((COLS-5))
 }
 
+S_TERMIO=`stty -g`
 
 set_geometry `stty size 2>/dev/null`
 
@@ -1111,7 +1118,7 @@
 Press <?> for additional information about this option."
 
 inputbox_instructions_int="\
-Please enter a decimal value between 1 and 9999. \
+Please enter a decimal value. \
 Fractions will not be accepted.  \
 Use the <TAB> key to move from the input field to the buttons below it."
 
@@ -1119,7 +1126,7 @@
 Please enter a hexadecimal value. \
 Use the <TAB> key to move from the input field to the buttons below it."
 
-DIALOG="./scripts/lxdialog/lxdialog"
+DIALOG="${DIALOG:-./scripts/lxdialog/lxdialog}"
 
 kernel_version="${VERSION}.${PATCHLEVEL}.${SUBLEVEL}"
 
@@ -1131,29 +1138,28 @@
 #
 # Locate default files.
 #
-CONFIG_IN=./config.in
-if [ "$1" != "" ] ; then
-	CONFIG_IN=$1
-fi
+DEFAULTS="arch/$ARCH/defconfig"
+
+CONFIG_IN="${1:-./config.in}"
 
-DEFAULTS=arch/$ARCH/defconfig
-if [ -f .config ]; then
-  DEFAULTS=.config
+_CONFIG="${2:-.config}"
+
+if [ -f "$_CONFIG" ]; then
+  DEFAULTS=$_CONFIG
 fi
 
+AUTOCONF_H="${3:-include/linux/autoconf.h}"
+
 if [ -f $DEFAULTS ]
 then
-  echo "#"
-  echo "# Using defaults found in" $DEFAULTS
-  echo "#"
+  echo
+  echo "Using defaults found in" $DEFAULTS
   load_config_file $DEFAULTS
 else
-  echo "#"
-  echo "# No defaults found"
-  echo "#"
+  echo
+  echo "No defaults found"
 fi
 
-
 # Fresh new log.
 >.menuconfig.log
 
@@ -1211,8 +1217,9 @@
 	   
 then
 	save_configuration
-
+	stty $S_TERMIO
 	clear
+
 	cat <<EOM
 
 
@@ -1224,6 +1231,7 @@
 EOM
 else
 	clear
+	stty $S_TERMIO
 	echo -e "Your kernel configuration changes were NOT saved.\n"
 fi
 
diff -u --recursive --new-file v2.0.30/linux/scripts/README.Menuconfig linux/scripts/README.Menuconfig
--- v2.0.30/linux/scripts/README.Menuconfig	Thu Apr 18 04:38:54 1996
+++ linux/scripts/README.Menuconfig	Sun Aug  3 15:38:39 1997
@@ -171,10 +171,11 @@
 
 ******** IMPORTANT, OPTIONAL ALTERNATE PERSONALITY AVAILABLE ********
 ********                                                     ********
-If you prefer to have all of the kernel options listed in a single
-menu, rather than the default multimenu hierarchy, you may edit the
-Menuconfig script and change the line "single_menu_mode="  to 
-"single_menu_mode=TRUE".
+If you prefer having all of the kernel options listed in a single
+menu rather than the default multimenu hierarchy, place the following
+line in your login scripts. (ie. .profile, .bashrc, etc...)
+
+export SINGLE_MENU_MODE=TRUE
 
 This mode is not recommended unless you have a fairly fast machine.
 *********************************************************************
@@ -189,6 +190,6 @@
 He will not be able to assist.
 
 Please feel free to send any questions, comments or suggestions to
-William Roadcap <roadcapw@cfw.com>.
+William Roadcap <roadcapw@titus.org>.
 
 <END OF FILE>
diff -u --recursive --new-file v2.0.30/linux/scripts/ksymoops.cc linux/scripts/ksymoops.cc
--- v2.0.30/linux/scripts/ksymoops.cc	Mon Apr  8 21:07:40 1996
+++ linux/scripts/ksymoops.cc	Wed Aug 13 10:02:44 1997
@@ -31,6 +31,7 @@
 // * Only resolves operands of jump and call instructions.
 
 #include <fstream.h>
+#include <strstream.h>
 #include <iomanip.h>
 #include <stdio.h>
 #include <string.h>
@@ -184,9 +185,23 @@
     
     char buf[1024];
     int lines = 0;
+    int eip_seen = 0;
+    long offset;
     while (fgets(buf, sizeof(buf), objdump_FILE)) {
+	if (eip_seen && buf[4] == ':') {
+	    // assume objdump from binutils 2.8..., reformat to old style
+	    offset = strtol(buf, 0, 16);
+	    char newbuf[sizeof(buf)];
+	    memset(newbuf, '\0', sizeof(newbuf));
+	    ostrstream ost(newbuf, sizeof(newbuf));
+	    ost.width(8);
+	    ost << offset;
+	    ost << " <_EIP+" << offset << ">: " << &buf[6] << ends;
+	    strcpy(buf, newbuf);
+	}
 	if (!strnequ(&buf[9], "<_EIP", 5))
 	    continue;
+	eip_seen = 1;
 	if (strstr(buf, " is out of bounds"))
 	    break;
 	lines++;
@@ -195,19 +210,28 @@
 	    cout << buf;
 	    continue;
 	}
-	long offset = strtol(buf, 0, 16);
-	char* bp_0 = strchr(buf, '>') + 2;
+	offset = strtol(buf, 0, 16);
+	char* bp_0 = strchr(buf, '>');
 	KSym* ksym = find(eip_addr + offset);
+	if (bp_0)
+	    bp_0 += 2;
+	else
+	    bp_0 = strchr(buf, ':');
 	if (ksym)
 	    cout << *ksym << ' ';
-	char* bp = bp_0;
+	char *bp_1 = strstr(bp_0, "\t");        // objdump from binutils 2.8...
+	if (bp_1)
+	    ++bp_1;
+	else
+	    bp_1 = bp_0;
+	char *bp = bp_1;
 	while (!isspace(*bp))
 	    bp++;
 	while (isspace(*bp))
 	    bp++;
-	if (*bp != '0') {
+	if (!isxdigit(*bp)) {
 	    cout << bp_0;
-	} else if (*bp_0 == 'j' || strnequ(bp_0, "call", 4)) { // a jump or call insn
+	} else if (*bp_1 == 'j' || strnequ(bp_1, "call", 4)) { // a jump or call insn
 	    long rel_addr = strtol(bp, 0, 16);
 	    ksym = find(eip_addr + rel_addr);
 	    if (ksym) {
diff -u --recursive --new-file v2.0.30/linux/scripts/lxdialog/lxdialog.c linux/scripts/lxdialog/lxdialog.c
--- v2.0.30/linux/scripts/lxdialog/lxdialog.c	Sun Feb 25 01:17:58 1996
+++ linux/scripts/lxdialog/lxdialog.c	Sun Aug  3 15:38:39 1997
@@ -119,7 +119,8 @@
     if (modePtr->argmax && argc - offset > modePtr->argmax)
 	Usage (argv[0]);
 
-
+    if (title && strlen(title) > atoi(argv[offset+4])-4)
+	title[atoi(argv[offset+4])-4] = 0;
 
     init_dialog ();
     retval = (*(modePtr->jumper)) (title, argc - offset, argv + offset);
